Skip to content

Commit

Permalink
Merge pull request #1321 from finos/regular-table-021
Browse files Browse the repository at this point in the history
Update regular-table to 0.2.1
  • Loading branch information
texodus authored Feb 10, 2021
2 parents eeb8b1e + 330ca7d commit b895d24
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 15 deletions.
4 changes: 2 additions & 2 deletions packages/perspective-viewer-datagrid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
"dependencies": {
"@finos/perspective": "^0.6.0",
"@finos/perspective-viewer": "^0.6.0",
"regular-table": "=0.1.5"
"regular-table": "=0.2.1"
},
"devDependencies": {
"@finos/perspective-test": "^0.6.0"
}
}
}
24 changes: 15 additions & 9 deletions packages/perspective-viewer-datagrid/src/js/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import {registerPlugin} from "@finos/perspective-viewer/dist/esm/utils.js";

import "regular-table";
import {createModel, configureRegularTable, formatters} from "regular-table/dist/examples/perspective.js";
import {createModel, configureRegularTable, formatters} from "./regular_table_handlers.js";
import MATERIAL_STYLE from "../less/regular_table.less";

import {configureRowSelectable, deselect} from "./row_selection.js";
Expand All @@ -32,8 +32,6 @@ function lock(body) {
try {
lock = new Promise(x => (resolve = x));
await body.apply(this, args);
} catch (e) {
throw e;
} finally {
lock = undefined;
resolve();
Expand All @@ -59,7 +57,7 @@ const datagridPlugin = lock(async function(regular, viewer, view) {
}

try {
const draw = regular.draw({swap: true});
const draw = regular.draw({invalid_columns: true});
if (!model._preserve_focus_state) {
regular.scrollTop = 0;
regular.scrollLeft = 0;
Expand All @@ -71,7 +69,9 @@ const datagridPlugin = lock(async function(regular, viewer, view) {

await draw;
} catch (e) {
console.error(e);
if (e.message !== "View is not initialized") {
throw e;
}
}
});

Expand Down Expand Up @@ -122,16 +122,20 @@ class DatagridPlugin {
model._num_rows = await model._view.num_rows();
await datagrid.draw();
} catch (e) {
return;
if (e.message !== "View is not initialized") {
throw e;
}
}
}

static async create(div, view) {
const datagrid = get_or_create_datagrid(this, div);
try {
const datagrid = get_or_create_datagrid(this, div);
await datagridPlugin(datagrid, this, view);
} catch (e) {
return;
if (e.message !== "View is not initialized") {
throw e;
}
}
}

Expand All @@ -141,7 +145,9 @@ class DatagridPlugin {
try {
await datagrid.draw();
} catch (e) {
return;
if (e.message !== "View is not initialized") {
throw e;
}
}
}
}
Expand Down
261 changes: 261 additions & 0 deletions packages/perspective-viewer-datagrid/src/js/regular_table_handlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
/******************************************************************************
*
* Copyright (c) 2017, the Perspective Authors.
*
* This file is part of the Perspective library, distributed under the terms of
* the Apache License 2.0. The full license can be found in the LICENSE file.
*
*/

import {get_type_config} from "@finos/perspective/dist/esm/config/index.js";

function styleListener(regularTable) {
const header_depth = regularTable._view_cache.config.row_pivots.length - 1;
let group_headers = Array.from(regularTable.children[0].children[0].children);
let [col_headers] = group_headers.splice(group_headers.length - 1, 1);
for (const td of col_headers?.children) {
const metadata = regularTable.getMeta(td);
const sort = this._config.sort.find(x => x[0] === metadata.column_header[metadata.column_header.length - 1]);
let needs_border = metadata.row_header_x === header_depth;
needs_border = needs_border || (metadata.x + 1) % this._config.columns.length === 0;
td.classList.toggle("psp-header-border", needs_border);
td.classList.toggle("psp-header-group", false);
td.classList.toggle("psp-header-leaf", true);
td.classList.toggle("psp-header-corner", typeof metadata.x === "undefined");
td.classList.toggle("psp-header-sort-asc", !!sort && sort[1] === "asc");
td.classList.toggle("psp-header-sort-desc", !!sort && sort[1] === "desc");
td.classList.toggle("psp-header-sort-col-asc", !!sort && sort[1] === "col asc");
td.classList.toggle("psp-header-sort-col-desc", !!sort && sort[1] === "col desc");

let type = get_psp_type.call(this, metadata);
const is_numeric = type === "integer" || type === "float";
const float_val = is_numeric && parseFloat(metadata.value);
td.classList.toggle("psp-align-right", is_numeric);
td.classList.toggle("psp-align-left", !is_numeric);
td.classList.toggle("psp-positive", float_val > 0);
td.classList.toggle("psp-negative", float_val < 0);
}

for (const tr of group_headers) {
for (const td of tr.children) {
const metadata = regularTable.getMeta(td);
let needs_border = metadata.row_header_x === header_depth || metadata.x >= 0;
td.classList.toggle("psp-header-group", true);
td.classList.toggle("psp-header-leaf", false);
td.classList.toggle("psp-header-border", needs_border);
}
}

for (const tr of regularTable.children[0].children[1].children) {
for (const td of tr.children) {
const metadata = regularTable.getMeta(td);
if (td.tagName === "TH") {
const is_not_empty = !!metadata.value && metadata.value.toString().trim().length > 0;
const is_leaf = metadata.row_header_x >= this._config.row_pivots.length;
const next = regularTable.getMeta({dx: 0, dy: metadata.y - metadata.y0 + 1});
const is_collapse = next && next.row_header && typeof next.row_header[metadata.row_header_x + 1] !== "undefined";
td.classList.toggle("psp-tree-label", is_not_empty && !is_leaf);
td.classList.toggle("psp-tree-label-expand", is_not_empty && !is_leaf && !is_collapse);
td.classList.toggle("psp-tree-label-collapse", is_not_empty && !is_leaf && is_collapse);
td.classList.toggle("psp-tree-leaf", is_not_empty && is_leaf);
}

let type = get_psp_type.call(this, metadata);
const is_numeric = type === "integer" || type === "float";
const float_val = is_numeric && parseFloat(metadata.value);
td.classList.toggle("psp-align-right", is_numeric);
td.classList.toggle("psp-align-left", !is_numeric);
td.classList.toggle("psp-positive", float_val > 0);
td.classList.toggle("psp-negative", float_val < 0);
}
}
}

function get_psp_type(metadata) {
if (metadata.x >= 0) {
const column_path = this._column_paths[metadata.x];
const column_path_parts = column_path.split("|");
return this._schema[column_path_parts[column_path_parts.length - 1]];
} else {
const column_path = this._config.row_pivots[metadata.row_header_x - 1];
return this._table_schema[column_path];
}
}

async function sortHandler(regularTable, event) {
const meta = regularTable.getMeta(event.target);
const column_name = meta.column_header[meta.column_header.length - 1];
const sort_method = event.shiftKey ? append_sort : override_sort;
const sort = sort_method.call(this, column_name);
regularTable.dispatchEvent(new CustomEvent("regular-table-psp-sort", {detail: {sort}}));
}

function append_sort(column_name) {
const sort = [];
let found = false;
for (const sort_term of this._config.sort) {
const [_column_name, _sort_dir] = sort_term;
if (_column_name === column_name) {
found = true;
const term = create_sort.call(this, column_name, _sort_dir);
if (term) {
sort.push(term);
}
} else {
sort.push(sort_term);
}
}
if (!found) {
sort.push([column_name, "desc"]);
}
return sort;
}
function override_sort(column_name) {
for (const [_column_name, _sort_dir] of this._config.sort) {
if (_column_name === column_name) {
const sort = create_sort.call(this, column_name, _sort_dir);
return sort ? [sort] : [];
}
}
return [[column_name, "desc"]];
}
function create_sort(column_name, sort_dir) {
const is_col_sortable = this._config.column_pivots.length > 0;
const order = is_col_sortable ? ROW_COL_SORT_ORDER : ROW_SORT_ORDER;
const inc_sort_dir = sort_dir ? order[sort_dir] : "desc";
if (inc_sort_dir) {
return [column_name, inc_sort_dir];
}
}

const ROW_SORT_ORDER = {desc: "asc", asc: undefined};
const ROW_COL_SORT_ORDER = {desc: "asc", asc: "col desc", "col desc": "col asc", "col asc": undefined};

async function expandCollapseHandler(regularTable, event) {
const meta = regularTable.getMeta(event.target);
const is_collapse = event.target.classList.contains("psp-tree-label-collapse");
if (event.shiftKey && is_collapse) {
this._view.set_depth(meta.row_header.filter(x => x !== undefined).length - 2);
} else if (event.shiftKey) {
this._view.set_depth(meta.row_header.filter(x => x !== undefined).length - 1);
} else if (is_collapse) {
this._view.collapse(meta.y);
} else {
this._view.expand(meta.y);
}
this._num_rows = await this._view.num_rows();
this._num_columns = await this._view.num_columns();
regularTable.draw();
}

function mousedownListener(regularTable, event) {
if (event.target.classList.contains("psp-tree-label") && event.offsetX < 26) {
expandCollapseHandler.call(this, regularTable, event);
event.handled = true;
} else if (event.target.classList.contains("psp-header-leaf") && !event.target.classList.contains("psp-header-corner")) {
sortHandler.call(this, regularTable, event);
event.handled = true;
}
}

const FORMATTERS = {};

const FORMATTER_CONS = {
datetime: Intl.DateTimeFormat,
date: Intl.DateTimeFormat,
integer: Intl.NumberFormat,
float: Intl.NumberFormat
};

export const formatters = FORMATTERS;

function _format(parts, val, use_table_schema = false) {
if (val === null) {
return "-";
}
const title = parts[parts.length - 1];
const type = (use_table_schema && this._table_schema[title]) || this._schema[title] || "string";
if (FORMATTERS[type] === undefined) {
const type_config = get_type_config(type);
if (FORMATTER_CONS[type] && type_config.format) {
FORMATTERS[type] = new FORMATTER_CONS[type]("en-us", type_config.format);
} else {
FORMATTERS[type] = false;
}
}

return FORMATTERS[type] ? FORMATTERS[type].format(val) : val;
}

function* _tree_header(paths = [], row_headers) {
for (let path of paths) {
path = ["TOTAL", ...path];
const last = path[path.length - 1];
path = path.slice(0, path.length - 1).fill("");
const formatted = _format.call(this, [row_headers[path.length - 1]], last, true);
path = path.concat({toString: () => formatted});
path.length = row_headers.length + 1;
yield path;
}
}

async function dataListener(x0, y0, x1, y1) {
let columns = {};
if (x1 - x0 > 0 && y1 - y0 > 0) {
columns = await this._view.to_columns({
start_row: y0,
start_col: x0,
end_row: y1,
end_col: x1,
id: true
});
this._ids = columns.__ID__;
}
const data = [];
const column_headers = [];
for (const path of this._column_paths.slice(x0, x1)) {
const path_parts = path.split("|");
const column = columns[path] || new Array(y1 - y0).fill(null);
data.push(column.map(x => _format.call(this, path_parts, x)));
column_headers.push(path_parts);
}
return {
num_rows: this._num_rows,
num_columns: this._column_paths.length,
row_headers: Array.from(_tree_header.call(this, columns.__ROW_PATH__, this._config.row_pivots)),
column_headers,
data
};
}

export async function createModel(regular, table, view, extend = {}) {
const config = await view.get_config();
const [table_schema, table_computed_schema, num_rows, schema, computed_schema, column_paths] = await Promise.all([
table.schema(),
table.computed_schema(config.computed_columns),
view.num_rows(),
view.schema(),
view.computed_schema(),
view.column_paths()
]);
const model = Object.assign(extend, {
_view: view,
_table: table,
_table_schema: {...table_schema, ...table_computed_schema},
_config: config,
_num_rows: num_rows,
_schema: {...schema, ...computed_schema},
_ids: [],
_column_paths: column_paths.filter(path => {
return path !== "__ROW_PATH__" && path !== "__ID__";
})
});
regular.setDataListener(dataListener.bind(model));
return model;
}

export async function configureRegularTable(regular, model) {
regular.addStyleListener(styleListener.bind(model, regular));
regular.addEventListener("mousedown", mousedownListener.bind(model, regular));
await regular.draw();
}
5 changes: 5 additions & 0 deletions packages/perspective/src/config/common.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ function common({no_minify, inline} = {}) {
plugins: plugins,
module: {
rules: [
// {
// test: /\.js$/,
// enforce: "pre",
// use: ["source-map-loader"]
// },
{
test: /\.less$/,
exclude: /node_modules\/(?!regular-table)/,
Expand Down
9 changes: 5 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2480,6 +2480,7 @@
write-file-atomic "^2.3.0"

"@lumino/algorithm@^1.2.0", "@lumino/algorithm@^1.3.3", "@lumino/messaging@^1.2.1", "@lumino/messaging@^1.3.3":
name "@lumino/algorithm"
version "1.3.3"
resolved "https://registry.yarnpkg.com/@lumino/algorithm/-/algorithm-1.3.3.tgz#fdf4daa407a1ce6f233e173add6a2dda0c99eef4"
integrity sha512-I2BkssbOSLq3rDjgAC3fzf/zAIwkRUnAh60MO0lYcaFdSGyI15w4K3gwZHGIO0p9cKEiNHLXKEODGmOjMLOQ3g==
Expand Down Expand Up @@ -13990,10 +13991,10 @@ regjsparser@^0.6.4:
dependencies:
jsesc "~0.5.0"

regular-table@=0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/regular-table/-/regular-table-0.1.5.tgz#11e4ba9fd799800006d27a9f5415bf33c0cd0246"
integrity sha512-cNBVD8Qw/o9rqs3h4bS/4cnDSyH5egAcBT2bJw7ihKQQFIz6MtJoE9iIC/kozTPlIK//EiFw5C4VKKkckElwmQ==
regular-table@=0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/regular-table/-/regular-table-0.2.1.tgz#af3d1efeeebe7d8071eda4e0dda689b4683ae156"
integrity sha512-24cKRtO/Gkj1p16mvqvkqWCTJSngAtFhYea7bax9XzNjTOy2DAF/507bQE+5zjq/qydfFXOUpTjnAjyIC1E5ug==

[email protected], relateurl@^0.2.7:
version "0.2.7"
Expand Down

0 comments on commit b895d24

Please sign in to comment.