@platform/ui.datagrid
Version:
Isolated tabular DataGrid.
594 lines (593 loc) • 26.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Grid = void 0;
var tslib_1 = require("tslib");
var rxjs_1 = require("rxjs");
var operators_1 = require("rxjs/operators");
var commands_1 = require("../../commands");
var common_1 = require("../../common");
var constants_1 = require("../../common/constants");
var keyboard_1 = require("../../keyboard");
var Cell_1 = require("../Cell");
var Grid_calc_1 = require("./Grid.calc");
var Grid = (function () {
function Grid(args) {
var _this = this;
this._ = {
id: '',
totalColumns: -1,
totalRows: -1,
defaults: undefined,
keyBindings: undefined,
cache: common_1.MemoryCache.create(),
table: undefined,
dispose$: new rxjs_1.Subject(),
events$: new rxjs_1.Subject(),
redraw$: new rxjs_1.Subject(),
isReady: false,
isEditing: false,
ns: constants_1.DEFAULT.NS,
cells: {},
columns: {},
rows: {},
lastSelection: undefined,
calc: undefined,
refs: undefined,
};
this.dispose$ = this._.dispose$.pipe((0, operators_1.share)());
this.events$ = this._.events$.pipe((0, operators_1.takeUntil)(this.dispose$), (0, operators_1.share)());
this.keyboard$ = this._.events$.pipe((0, operators_1.filter)(function (e) { return e.type === 'GRID/keydown'; }), (0, operators_1.map)(function (e) { return e.payload; }), (0, operators_1.share)());
this.getValueSync = function (key) {
var cell = _this.data.cells[key];
return cell && typeof cell.value === 'string' ? cell.value : undefined;
};
this.refsTable = common_1.coord.refs.table({
getKeys: function () { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { return (0, tslib_1.__generator)(this, function (_a) {
return [2, Object.keys(this.data.cells)];
}); }); },
getValue: function (key) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { return (0, tslib_1.__generator)(this, function (_a) {
return [2, this.getValueSync(key)];
}); }); },
});
this.command = function (args) {
var payload = {
command: args.command,
grid: _this,
selection: _this.selection,
props: args.props || {},
isCancelled: false,
cancel: function () {
payload.isCancelled = true;
if (args.cancel) {
args.cancel();
}
},
};
_this.fire({ type: 'GRID/command', payload: payload });
return _this;
};
this.fire = function (e) {
_this._.events$.next(e);
return _this;
};
this._init(args);
}
Grid.create = function (args) {
return new Grid(args);
};
Grid.defaults = function (input) {
var partial = input || {};
return {
ns: (0, common_1.defaultValue)(partial.ns, constants_1.DEFAULT.NS),
totalColumns: (0, common_1.defaultValue)(partial.totalColumns, constants_1.DEFAULT.TOTAL_COLUMNS),
totalRows: (0, common_1.defaultValue)(partial.totalRows, constants_1.DEFAULT.TOTAL_ROWS),
columnWidth: (0, common_1.defaultValue)(partial.columnWidth, constants_1.DEFAULT.COLUMN.WIDTH),
columnWidthMin: (0, common_1.defaultValue)(partial.columnWidthMin, constants_1.DEFAULT.COLUMN.WIDTH_MIN),
rowHeight: (0, common_1.defaultValue)(partial.rowHeight, constants_1.DEFAULT.ROW.HEIGHT),
rowHeightMin: (0, common_1.defaultValue)(partial.rowHeightMin, constants_1.DEFAULT.ROW.HEIGHT_MIN),
};
};
Grid.toDataArray = function (args) {
return Array.from({ length: args.totalRows }).map(function (v, row) {
return Array.from({ length: args.totalColumns }).map(function (v, column) {
return args.cells[Cell_1.Cell.toKey({ row: row, column: column })];
});
});
};
Grid.toNs = function (input) {
var ns = input === undefined ? constants_1.DEFAULT.NS : typeof input === 'string' ? { id: input } : input;
ns.id = ns.id.trim().replace(/^ns\:/, '');
return ns;
};
Grid.isDefaultValue = function (args) {
var kind = args.kind, value = args.value;
var defaults = Grid.defaults(args.defaults);
return common_1.util.isDefaultGridValue({ defaults: defaults, kind: kind, value: value });
};
Grid.prototype._init = function (args) {
var _this = this;
var defaults = (this._.defaults = Grid.defaults(args.defaults));
this._.totalColumns = (0, common_1.defaultValue)(args.totalColumns, defaults.totalColumns);
this._.totalRows = (0, common_1.defaultValue)(args.totalRows, defaults.totalRows);
this._.ns = Grid.toNs(args.ns);
this._.cells = args.cells || {};
this._.columns = args.columns || {};
this._.rows = args.rows || {};
this._.calc = (0, Grid_calc_1.calc)({ grid: this, getFunc: args.getFunc });
this.events$
.pipe((0, operators_1.filter)(function (e) { return e.type === 'GRID/ready'; }))
.subscribe(function () { return (_this._.isReady = true); });
this._.keyBindings = common_1.R.uniqBy(common_1.R.prop('command'), (0, tslib_1.__spreadArray)((0, tslib_1.__spreadArray)([], (args.keyBindings || []), true), constants_1.DEFAULT.KEY_BINDINGS, true));
var table = args.table;
if (table) {
this.initialize({ table: table });
}
};
Grid.prototype.initialize = function (args) {
var _this = this;
var table = args.table;
this._.table = table;
this._.id = "grid/" + table.guid.replace(/^ht_/, '');
keyboard_1.keyboard.init({ grid: this });
commands_1.commands.init({ grid: this, fire: this.fire });
this._.redraw$
.pipe((0, operators_1.takeUntil)(this.dispose$), (0, operators_1.debounceTime)(0))
.subscribe(function (e) { return _this.fire({ type: 'GRID/redraw', payload: { redraw: true } }); });
this.events$
.pipe((0, operators_1.filter)(function (e) { return e.type === 'GRID/EDITOR/begin'; }))
.subscribe(function () { return (_this._.isEditing = true); });
var editEnd$ = this.events$.pipe((0, operators_1.filter)(function (e) { return e.type === 'GRID/EDITOR/end'; }), (0, operators_1.map)(function (e) { return e.payload; }));
editEnd$.subscribe(function () { return (_this._.isEditing = false); });
editEnd$.pipe((0, operators_1.filter)(function (e) { return e.isChanged; })).subscribe(function (e) {
var _a;
var key = e.cell.key;
var value = e.value.to;
var props = (0, tslib_1.__assign)((0, tslib_1.__assign)({}, _this.cell(key).data.props), { value: undefined });
var cell = { value: value, props: props };
_this.changeCells((_a = {}, _a[key] = cell, _a), { source: 'EDIT' });
});
editEnd$
.pipe((0, operators_1.filter)(function (e) { return !e.isCancelled; }), (0, operators_1.filter)(function (e) { return e.cell.key === _this.selection.cell; }))
.subscribe(function (e) {
var below = e.cell.siblings.bottom;
if (below) {
_this.select({ cell: below });
}
});
var selection$ = this.events$.pipe((0, operators_1.filter)(function (e) { return e.type === 'GRID/selection'; }), (0, operators_1.map)(function (e) { return e.payload; }));
selection$
.pipe((0, operators_1.filter)(function (e) { return Boolean(e.to.cell); }))
.subscribe(function (e) { return (_this._.lastSelection = e.to); });
selection$
.pipe((0, operators_1.debounceTime)(0), (0, operators_1.filter)(function (e) { return !Boolean(e.from.cell) && Boolean(e.to.cell); }))
.subscribe(function (e) { return _this.fire({ type: 'GRID/focus', payload: { grid: _this } }); });
selection$
.pipe((0, operators_1.debounceTime)(0), (0, operators_1.filter)(function (e) { return Boolean(e.from.cell) && !Boolean(e.to.cell); }))
.subscribe(function (e) { return _this.fire({ type: 'GRID/blur', payload: { grid: _this } }); });
this.events$
.pipe((0, operators_1.filter)(function (e) { return (_this._.isReady = true); }), (0, operators_1.filter)(function (e) { return e.type === 'GRID/cells/change'; }), (0, operators_1.map)(function (e) { return e.payload; }))
.subscribe(function (e) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () {
var cells;
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
cells = e.changes.map(function (change) { return change.cell.key; });
return [4, this.calc.update({ cells: cells })];
case 1:
_a.sent();
return [2];
}
});
}); });
return this;
};
Object.defineProperty(Grid.prototype, "id", {
get: function () {
return this._.id;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "totalColumns", {
get: function () {
return this._.totalColumns;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "totalRows", {
get: function () {
return this._.totalRows;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "defaults", {
get: function () {
return this._.defaults;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "keyBindings", {
get: function () {
return this._.keyBindings;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "isInitialized", {
get: function () {
return Boolean(this._.table);
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "isDisposed", {
get: function () {
return this._.table.isDestroyed || this._.dispose$.isStopped;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "isReady", {
get: function () {
return this.isDisposed ? false : this._.isReady;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "isEditing", {
get: function () {
return this._.isEditing;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "data", {
get: function () {
var _a = this._, ns = _a.ns, cells = _a.cells, columns = _a.columns, rows = _a.rows;
var files = {};
return { ns: ns, cells: cells, columns: columns, rows: rows, files: files };
},
enumerable: false,
configurable: true
});
Grid.prototype.setCells = function (cells) {
cells = (0, tslib_1.__assign)({}, cells);
var totalColumns = this.totalColumns;
var totalRows = this.totalRows;
var data = Grid.toDataArray({ cells: cells, totalColumns: totalColumns, totalRows: totalRows });
this._.cells = cells;
this._.table.loadData(data);
};
Object.defineProperty(Grid.prototype, "calc", {
get: function () {
return this._.calc;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "selection", {
get: function () {
var toKey = function (coord) {
return Cell_1.Cell.toKey({ row: coord.row, column: coord.col });
};
var last = this._.table.getSelectedRangeLast();
var cell = last ? toKey(last.highlight) : undefined;
var selectedRanges = this._.table.getSelectedRange() || [];
var ranges = selectedRanges.map(function (item) { return toKey(item.from) + ":" + toKey(item.to); });
ranges = ranges.length === 1 && ranges[0] === cell + ":" + cell ? [] : ranges;
var all = false;
if (ranges.length > 0) {
var min_1 = { row: -1, col: -1 };
var max_1 = { row: -1, col: -1 };
selectedRanges.forEach(function (range) {
var from = range.from, to = range.to;
min_1.row = min_1.row === -1 || from.row < min_1.row ? from.row : min_1.row;
min_1.col = min_1.col === -1 || from.col < min_1.col ? from.col : min_1.col;
max_1.row = max_1.row === -1 || to.row > max_1.row ? to.row : max_1.row;
max_1.col = max_1.col === -1 || to.col > max_1.col ? to.col : max_1.col;
});
if (min_1.row === 0 &&
min_1.col === 0 &&
max_1.row === this.totalRows - 1 &&
max_1.col === this.totalColumns - 1) {
all = true;
}
}
if (ranges.length > 0) {
var totalColumns = this.totalColumns;
var totalRows = this.totalRows;
var union = common_1.coord.range.union(ranges).formated({ totalColumns: totalColumns, totalRows: totalRows });
ranges = union.ranges.map(function (range) { return range.key; });
if (cell) {
ranges = ranges.filter(function (range) { return range !== cell + ":" + cell; });
}
ranges = common_1.R.uniq(ranges);
}
var result = { cell: cell, ranges: ranges };
result = all ? (0, tslib_1.__assign)((0, tslib_1.__assign)({}, result), { all: all }) : result;
return result;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Grid.prototype, "selectionValues", {
get: function () {
var cells = this.data.cells;
var selection = this.selection;
return (0, common_1.toSelectionValues)({ cells: cells, selection: selection });
},
enumerable: false,
configurable: true
});
Grid.prototype.dispose = function () {
var _a = this._, table = _a.table, dispose$ = _a.dispose$;
if (!table.isDestroyed) {
table.destroy();
}
dispose$.next();
dispose$.complete();
};
Grid.prototype.mergeCells = function (args) {
var cells = args.cells;
var list = args.init
? []
: this._.table.getSettings().mergeCells || [];
var map = list.reduce(function (acc, next) {
var key = common_1.coord.cell.toKey(next.col, next.row);
acc[key] = next;
return acc;
}, {});
Object.keys(cells).map(function (key) {
var item = cells[key];
if (item) {
var props = item.props || {};
var merge = props.merge;
if (merge) {
var rowspan = Math.max(1, (0, common_1.defaultValue)(merge.rowspan, 1));
var colspan = Math.max(1, (0, common_1.defaultValue)(merge.colspan, 1));
var _a = common_1.coord.cell.fromKey(key), row = _a.row, col = _a.column;
map[key] = { row: row, col: col, rowspan: rowspan, colspan: colspan };
}
}
});
var mergeCells = Object.keys(map).map(function (key) { return map[key]; });
this._.table.updateSettings({ mergeCells: mergeCells }, false);
return this;
};
Grid.prototype.changeCells = function (cells, options) {
var _this = this;
if (options === void 0) { options = {}; }
var done = function () { return _this; };
var format = function (key, to) {
if (Cell_1.Cell.isEmpty(to)) {
return undefined;
}
if (to) {
to = (0, tslib_1.__assign)((0, tslib_1.__assign)({}, to), { hash: common_1.util.gridCellHash(_this, key, to) });
if (Cell_1.Cell.isEmptyProps(to.props)) {
delete to.props;
}
}
return to;
};
if (options.init && cells && Object.keys(cells).length === 0) {
this.setCells({});
return done();
}
if (cells) {
var current_1 = (0, tslib_1.__assign)({}, (options.init ? {} : this.data.cells));
cells = (0, tslib_1.__assign)({}, cells);
Object.keys(cells)
.filter(function (key) { return !common_1.coord.cell.isCell(key); })
.forEach(function (key) { return delete cells[key]; });
var formatted_1 = Object.keys(cells).reduce(function (acc, key) {
acc[key] = {
from: format(key, current_1[key]),
to: format(key, cells[key]),
};
return acc;
}, {});
var changes_1 = Object.keys(cells).map(function (key) {
var cell = _this.cell(key);
var _a = formatted_1[key], from = _a.from, to = _a.to;
return Cell_1.Cell.changeEvent({ cell: cell, from: from, to: to });
});
var isChanged = changes_1.some(function (e) { return e.isChanged; });
if (!isChanged) {
return done();
}
if (!options.silent) {
var payload = {
source: (0, common_1.defaultValue)(options.source, 'EDIT'),
changes: changes_1,
get isCancelled() {
return changes_1.some(function (change) { return change.isCancelled; });
},
cancel: function () {
changes_1.forEach(function (change) { return change.cancel(); });
},
};
this.fire({ type: 'GRID/cells/change', payload: payload });
changes_1
.filter(function (change) { return change.isModified; })
.forEach(function (change) { return (cells[change.cell.key] = change.value.to); });
changes_1
.filter(function (change) { return change.isCancelled; })
.forEach(function (change) { return (cells[change.cell.key] = change.value.from); });
}
var mergeChanges_1 = {};
var updates_1 = (0, tslib_1.__assign)((0, tslib_1.__assign)({}, current_1), Object.keys(formatted_1).reduce(function (acc, key) {
acc[key] = formatted_1[key].to;
return acc;
}, {}));
Object.keys(formatted_1).forEach(function (key) {
var _a = formatted_1[key], from = _a.from, to = _a.to;
if (Cell_1.Cell.isEmpty(to)) {
delete updates_1[key];
return;
}
var isMergeChanged = !common_1.R.equals(((from || {}).props || {}).merge, ((to || {}).props || {}).merge);
if (isMergeChanged) {
mergeChanges_1[key] = to;
}
});
if (Object.keys(mergeChanges_1).length > 0) {
this.mergeCells({ cells: mergeChanges_1 });
}
this.setCells(updates_1);
}
return done();
};
Grid.prototype.changeColumns = function (columns, options) {
var _this = this;
if (options === void 0) { options = {}; }
var _a = options.source, source = _a === void 0 ? 'UPDATE' : _a;
var from = (0, tslib_1.__assign)({}, this._.columns);
var to = (0, tslib_1.__assign)({}, from);
var changes = [];
Object.keys(columns).forEach(function (key) {
var prev = from[key] || { props: { grid: { width: -1 } } };
var next = columns[key] || { props: { grid: { width: _this.defaults.columnWidth } } };
var nextProps = next.props || {};
var isDefault = nextProps.grid && nextProps.grid.width === _this.defaults.columnWidth;
if (isDefault) {
delete to[key];
}
else {
to[key] = next;
}
if (!common_1.R.equals(prev, next)) {
changes = (0, tslib_1.__spreadArray)((0, tslib_1.__spreadArray)([], changes, true), [{ column: key, source: source, from: prev, to: next }], false);
}
});
this._.columns = to;
if (!common_1.R.equals(from, to)) {
this.fire({ type: 'GRID/columns/change', payload: { from: from, to: to, changes: changes } });
}
return this;
};
Grid.prototype.changeRows = function (rows, options) {
var _this = this;
if (options === void 0) { options = {}; }
var _a = options.source, source = _a === void 0 ? 'UPDATE' : _a;
var from = (0, tslib_1.__assign)({}, this._.rows);
var to = (0, tslib_1.__assign)({}, from);
var changes = [];
Object.keys(rows).forEach(function (key) {
var prev = from[key] || { props: { grid: { height: -1 } } };
var next = rows[key] || { props: { grid: { height: _this.defaults.rowHeight } } };
var nextProps = next.props || {};
var isDefault = nextProps.grid && nextProps.grid.height === _this.defaults.rowHeight;
if (isDefault) {
delete to[key];
}
else {
to[key] = next;
}
if (!common_1.R.equals(prev, next)) {
var row = common_1.coord.cell.fromKey(key).row;
changes = (0, tslib_1.__spreadArray)((0, tslib_1.__spreadArray)([], changes, true), [{ row: row, source: source, from: prev, to: next }], false);
}
});
this._.rows = to;
if (!common_1.R.equals(from, to)) {
this.fire({
type: 'GRID/rows/change',
payload: { from: from, to: to, changes: changes },
});
}
return this;
};
Grid.prototype.cell = function (key) {
var _this = this;
var args = typeof key === 'string' ? Cell_1.Cell.fromKey(key) : key;
var row = args.row, column = args.column;
if (row < 0 || column < 0) {
var msg = "Cell does not exist at row:" + row + ", column:" + column + ".";
msg = typeof key === 'string' ? msg + " key: \"" + key + "\"" : msg;
throw new Error(msg);
}
var ns = this.data.ns.id;
var cacheKey = ns + ":cell/" + column + ":" + row;
return this._.cache.get(cacheKey, function () {
var table = _this._.table;
return Cell_1.Cell.create({ ns: ns, table: table, row: row, column: column });
});
};
Grid.prototype.scrollTo = function (args) {
var _a = this.toPosition(args.cell), row = _a.row, column = _a.column;
var _b = args.snapToBottom, snapToBottom = _b === void 0 ? false : _b, _c = args.snapToRight, snapToRight = _c === void 0 ? false : _c;
this._.table.scrollViewportTo(row, column, snapToBottom, snapToRight);
return this;
};
Grid.prototype.select = function (args) {
var totalColumns = this.totalColumns;
var totalRows = this.totalRows;
var table = this._.table;
var scrollToCell = common_1.value.defaultValue(args.scrollToCell, true);
var ranges = (args.ranges || [])
.map(function (range) { return Cell_1.Cell.toRangePositions({ range: range, totalColumns: totalColumns, totalRows: totalRows }); })
.map(function (_a) {
var start = _a.start, end = _a.end;
return [start.row, start.column, end.row, end.column];
});
var pos = this.toPosition(args.cell);
var current = [pos.row, pos.column, pos.row, pos.column];
var selection = (0, tslib_1.__spreadArray)((0, tslib_1.__spreadArray)([], ranges, true), [current], false);
table.selectCells(selection, scrollToCell);
return this;
};
Grid.prototype.deselect = function () {
this._.table.deselectCell();
return this;
};
Grid.prototype.focus = function () {
var last = this._.lastSelection;
var cell = (last && last.cell) || 'A1';
var ranges = (last && last.ranges) || [];
this.select({ cell: cell, ranges: ranges });
return this;
};
Grid.prototype.blur = function () {
this.fire({ type: 'GRID/blur', payload: { grid: this } });
this.deselect();
return this;
};
Grid.prototype.redraw = function () {
this._.redraw$.next();
return this;
};
Grid.prototype.toPosition = function (ref) {
var pos = Cell_1.Cell.toPosition(ref);
var row = common_1.R.clamp(0, this.totalRows - 1, pos.row);
var column = common_1.R.clamp(0, this.totalColumns - 1, pos.column);
return { row: row, column: column };
};
Grid.prototype.updateHashes = function (options) {
var _this = this;
if (options === void 0) { options = {}; }
var data = this.data;
var cells = (0, tslib_1.__assign)({}, data.cells);
var isChanged = false;
Object.keys(cells).forEach(function (key) {
var cell = cells[key];
if (cell) {
var hash = cell.hash;
if (!hash || options.force) {
hash = common_1.util.gridCellHash(_this, key, cell);
cells[key] = (0, tslib_1.__assign)((0, tslib_1.__assign)({}, cell), { hash: hash });
isChanged = true;
}
}
});
if (isChanged) {
this.setCells(cells);
}
return this;
};
return Grid;
}());
exports.Grid = Grid;