@platform/ui.datagrid
Version:
Isolated tabular DataGrid.
156 lines (155 loc) • 5.61 kB
JavaScript
import { filter, map } from 'rxjs/operators';
import { coord, toClipboard } from '../common';
const CLIPBOARD_COMMANDS = ['CUT', 'COPY', 'PASTE'];
export function init(args) {
const { grid, command$, fire } = args;
const clipboard$ = command$.pipe(filter((e) => CLIPBOARD_COMMANDS.includes(e.command)), filter((e) => !e.isCancelled), map((e) => e.command));
clipboard$.pipe(filter((e) => e === 'CUT')).subscribe((e) => read({ grid, fire, action: 'CUT' }));
clipboard$
.pipe(filter((e) => e === 'COPY'))
.subscribe((e) => read({ grid, fire, action: 'COPY' }));
clipboard$.pipe(filter((e) => e === 'PASTE')).subscribe((e) => write({ grid, fire }));
}
async function read(args) {
const { grid, action } = args;
const wait = [];
args.fire({
type: 'GRID/clipboard/before/read',
payload: {
action,
wait: (promise) => wait.push(promise),
},
});
if (wait.length > 0) {
await Promise.all(wait);
}
const payload = toClipboard({ grid, action });
await navigator.clipboard.writeText(payload.text);
grid.clipboard = Object.assign(Object.assign({}, payload), { pasted: 0 });
args.fire({ type: 'GRID/clipboard', payload });
}
async function write(args) {
const { grid } = args;
const selection = grid.selection;
const targetCell = selection.cell;
if (!targetCell) {
return;
}
let redraw = false;
const text = await navigator.clipboard.readText();
let pending = grid.clipboard;
const before = {
text,
pending,
isModified: false,
modify: (change) => {
before.isModified = true;
pending = Object.assign(Object.assign({}, (change || {})), { pasted: 0 });
},
};
args.fire({ type: 'GRID/clipboard/before/paste', payload: before });
const isPendingCurrent = pending ? pending.text === text : false;
if (!isPendingCurrent) {
grid.clipboard = undefined;
pending = undefined;
}
if (pending && pending.action === 'CUT') {
const empty = Object.keys(pending.cells).reduce((acc, key) => {
acc[key] = { value: undefined };
return acc;
}, {});
grid.changeCells(empty, { source: 'CLIPBOARD/cut' });
}
const cellsFromString = () => coord.table
.fromString({ key: targetCell, text })
.map(({ key, value }) => ({ key, value: { value } }));
const items = pending && pending.selection.cell
? shiftCells({
sourceCell: pending.selection.cell,
targetCell,
data: pending.cells,
})
: cellsFromString();
if (items.length === 0) {
return;
}
const changedCells = items.reduce((acc, next) => {
acc[next.key] = next.value;
return acc;
}, {});
grid.changeCells(changedCells, { source: 'CLIPBOARD/paste' });
const shiftedAxisData = (axis, data) => {
if (!pending || Object.keys(data).length === 0) {
return {};
}
const source = firstAxisRange(axis, pending.selection);
const target = firstAxisRange(axis, selection);
if (!source || !target) {
return {};
}
return shiftAxisData({ axis, source, target, data });
};
const columns = pending ? shiftedAxisData('COLUMN', pending.columns) : {};
const rows = pending ? shiftedAxisData('ROW', pending.rows) : {};
if (Object.keys(columns).length > 0) {
grid.changeColumns(columns, { source: 'CLIPBOARD/paste' });
redraw = true;
}
if (Object.keys(rows).length > 0) {
grid.changeRows(rows, { source: 'CLIPBOARD/paste' });
redraw = true;
}
const square = coord.range.square(items.map((item) => item.key));
grid.select({ cell: square.left.key, ranges: [square.key] });
if (grid.clipboard) {
grid.clipboard = Object.assign(Object.assign({}, grid.clipboard), { pasted: grid.clipboard.pasted + 1 });
}
const cells = items.reduce((acc, next) => {
acc[next.key] = next.value;
return acc;
}, {});
args.fire({
type: 'GRID/clipboard',
payload: { action: 'PASTE', selection, text, cells, columns, rows },
});
if (redraw) {
grid.redraw();
}
}
function shiftCells(args) {
const pos = {
start: coord.cell.fromKey(args.sourceCell),
end: coord.cell.fromKey(args.targetCell),
};
const diff = {
row: pos.end.row - pos.start.row,
column: pos.end.column - pos.start.column,
};
const data = Object.assign({}, args.data);
return Object.keys(data).map((cell) => {
const pos = coord.cell.fromKey(cell);
const key = coord.cell.toKey(pos.column + diff.column, pos.row + diff.row);
return { key, value: data[cell] };
});
}
function shiftAxisData(args) {
const { axis } = args;
const pos = {
start: args.source.left,
end: args.target.left,
};
const diff = coord.cell.toAxisIndex(axis, pos.end) - coord.cell.toAxisIndex(axis, pos.start);
const data = Object.assign({}, args.data);
return Object.keys(data).reduce((acc, next) => {
const index = coord.cell.toAxisIndex(axis, next) + diff;
const key = coord.cell.toAxisKey(axis, index);
if (key) {
acc[key] = data[next];
}
return acc;
}, {});
}
function firstAxisRange(axis, selection) {
const key = selection.ranges.find((key) => coord.cell.isAxisRangeKey(key, axis));
return key ? coord.range.fromKey(key) : undefined;
}