UNPKG

@platform/ui.datagrid

Version:

Isolated tabular DataGrid.

594 lines (593 loc) 26.8 kB
"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;