Skip to content

Commit

Permalink
Merge pull request finos#2618 from finos/datagrid-row-col-region-select
Browse files Browse the repository at this point in the history
Add region, column and row selection modes to Perspective Datagrid
  • Loading branch information
texodus authored May 29, 2024
2 parents 01d59df + 18d90b7 commit abed771
Show file tree
Hide file tree
Showing 49 changed files with 1,003 additions and 200 deletions.
2 changes: 1 addition & 1 deletion examples/blocks/src/editable/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
const viewer = document.getElementsByTagName("perspective-viewer")[0];
const table = worker.table(arrow);
viewer.load(table);
viewer.restore({ settings: true, plugin_config: { editable: true } });
viewer.restore({ settings: true, plugin_config: { edit_mode: "EDIT" } });
</script>

<style>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"webpack-dev-server": "^4.11.1"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
"postinstall": "npm-run-all postinstall:*",
"postinstall:emsdk": "node tools/perspective-scripts/install_emsdk.mjs",
"postinstall:playwright": "npx playwright install --with-deps",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ export class HTMLPerspectiveViewerDatagridToolbarElement extends HTMLElement {
${TOOLBAR_STYLE}
</style>
<div id="toolbar">
<span id="scroll_lock" class="button">
<span></span>
<span class="hover-target">
<span id="scroll_lock" class="button">
<span></span>
</span>
</span>
<span class="hover-target">
<span id="edit_mode" class="button" data-edit-mode="READ_ONLY">
<span></span>
</span>
</span>
<span id="edit_mode" class="button"><span></span></span>
</div>
`;

Expand All @@ -47,8 +53,8 @@ export class HTMLPerspectiveViewerDatagridToolbarElement extends HTMLElement {
toggle_scroll_lock.call(plugin)
);

plugin._edit_mode = this.shadowRoot.querySelector("#edit_mode");
plugin._edit_mode.addEventListener("click", () => {
plugin._edit_button = this.shadowRoot.querySelector("#edit_mode");
plugin._edit_button.addEventListener("click", () => {
toggle_edit_mode.call(plugin);
plugin.regular_table.draw();
viewer.dispatchEvent(new Event("perspective-config-update"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import * as edit_click from "./click/edit_click.js";
import * as edit_keydown from "./keydown/edit_keydown.js";

export function is_editable(viewer, allowed = false) {
const has_pivots =
this._config.group_by.length === 0 &&
this._config.split_by.length === 0;
const selectable = viewer.hasAttribute("selectable");
const editable = allowed || !!viewer.children[0]._is_edit_mode;
return has_pivots && !selectable && editable;
}

export function keydownListener(table, viewer, selected_position_map, event) {
if (this._edit_mode === "EDIT") {
if (!is_editable.call(this, viewer)) {
return;
}

edit_keydown.keydownListener.call(
this,
table,
viewer,
selected_position_map,
event
);
} else {
console.log(
`Mode ${this._edit_mode} for "keydown" event not yet implemented`
);
}
}

export function clickListener(table, viewer, event) {
if (this._edit_mode === "EDIT") {
if (!is_editable.call(this, viewer)) {
return;
}

edit_click.clickListener.call(this, table, viewer, event);
} else if (this._edit_mode === "READ_ONLY") {
} else if (this._edit_mode === "SELECT_COLUMN") {
} else if (this._edit_mode === "SELECT_ROW") {
} else if (this._edit_mode === "SELECT_REGION") {
} else {
console.log(
`Mode ${this._edit_mode} for "click" event not yet implemented`
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

export function write_cell(table, model, active_cell) {
const meta = table.getMeta(active_cell);
const type = model._schema[model._column_paths[meta.x]];
if (meta) {
let text = active_cell.textContent;
const id = model._ids[meta.y - meta.y0][0];
if (type === "float" || type === "integer") {
text = parseFloat(text.replace(/,/g, ""));
if (isNaN(text)) {
return false;
}
} else if (type === "date" || type === "datetime") {
text = Date.parse(text);
if (isNaN(text)) {
return false;
}
} else if (type === "boolean") {
text = text === "true" ? false : text === "false" ? true : null;
}

const msg = {
__INDEX__: id,
[model._column_paths[meta.x]]: text,
};

model._table.update([msg], { port_id: model._edit_port });
return true;
}
}

export function clickListener(table, _viewer, event) {
const meta = table.getMeta(event.target);
if (typeof meta?.x !== "undefined") {
const is_editable2 = this._is_editable[meta.x];
const is_bool = this.get_psp_type(meta) === "boolean";
const is_null = event.target.textContent === "-";
if (is_editable2 && is_bool && !is_null) {
write_cell(table, this, event.target);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import { is_editable, write_cell } from "./edit_click.js";
import { is_editable } from "./click.js";
import { write_cell } from "./click/edit_click.js";

export function focusoutListener(table, viewer, selected_position_map, event) {
if (is_editable.call(this, viewer) && selected_position_map.has(table)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export async function mousedown_listener(regularTable, viewer, event) {
}
}

// TODO I hav eno idea what this does ...

export function click_listener(regularTable, event) {
if (event.which !== 1) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import { focus_style_listener } from "../style_handlers/focus.js";
import { focus_style_listener } from "../../style_handlers/focus.js";

function lock(body) {
let lock;
Expand Down Expand Up @@ -39,45 +39,6 @@ function getPos() {
}
}

export function write_cell(table, model, active_cell) {
const meta = table.getMeta(active_cell);
const type = model._schema[model._column_paths[meta.x]];
if (meta) {
let text = active_cell.textContent;
const id = model._ids[meta.y - meta.y0];
if (type === "float" || type === "integer") {
text = parseFloat(text.replace(/,/g, ""));
if (isNaN(text)) {
return false;
}
} else if (type === "date" || type === "datetime") {
text = Date.parse(text);
if (isNaN(text)) {
return false;
}
} else if (type === "boolean") {
text = text === "true" ? false : text === "false" ? true : null;
}

const msg = {
__INDEX__: id[0],
[model._column_paths[meta.x]]: text,
};

model._table.update([msg], { port_id: model._edit_port });
return true;
}
}

export function is_editable(viewer, allowed = false) {
const has_pivots =
this._config.group_by.length === 0 &&
this._config.split_by.length === 0;
const selectable = viewer.hasAttribute("selectable");
const editable = allowed || !!viewer.children[0]._is_edit_mode;
return has_pivots && !selectable && editable;
}

const moveSelection = lock(async function (
table,
selected_position_map,
Expand Down Expand Up @@ -124,9 +85,6 @@ const moveSelection = lock(async function (
// Events

export function keydownListener(table, viewer, selected_position_map, event) {
if (!is_editable.call(this, viewer)) {
return;
}
const target = table.getRootNode().activeElement;
event.target.classList.remove("psp-error");
switch (event.key) {
Expand Down Expand Up @@ -203,16 +161,3 @@ export function keydownListener(table, viewer, selected_position_map, event) {
default:
}
}

export function clickListener(table, _viewer, event) {
const meta = table.getMeta(event.target);
if (typeof meta?.x !== "undefined") {
const is_all_editable = is_editable.call(this, _viewer);
const is_editable2 = this._is_editable[meta.x];
const is_bool = this.get_psp_type(meta) === "boolean";
const is_null = event.target.textContent === "-";
if (is_all_editable && is_editable2 && is_bool && !is_null) {
write_cell(table, this, event.target);
}
}
}
Loading

0 comments on commit abed771

Please sign in to comment.