UNPKG

@mhmdaljefri/revogrid

Version:

Virtual reactive data grid component - RevoGrid.

724 lines (723 loc) 21.5 kB
import { Component, Event, h, Host, Listen, Prop, Element, Watch } from '@stencil/core'; import ColumnService from '../data/columnService'; import SelectionStoreService from '../../store/selection/selection.store.service'; import { codesLetter } from '../../utils/keyCodes'; import { SELECTION_BORDER_CLASS } from '../../utils/consts'; import { isRangeSingleCell } from '../../store/selection/selection.helpers'; import { getCurrentCell, getElStyle } from './selection.utils'; import { isEditInput } from './editors/edit.utils'; import { KeyboardService } from './keyboard.service'; import { AutoFillService } from './autofill.service'; import { ClipboardService } from './clipboard.service'; export class OverlaySelection { constructor() { this.keyboardService = null; this.autoFillService = null; this.clipboardService = null; } // -------------------------------------------------------------------------- // // Listeners // // -------------------------------------------------------------------------- onMouseMove(e) { if (this.selectionStoreService.focused) { this.autoFillService.selectionMouseMove(e); } } /** Pointer left document, clear any active operation */ onMouseOut() { this.autoFillService.clearAutoFillSelection(); } /** Action finished inside of the document */ onMouseUp() { this.autoFillService.clearAutoFillSelection(); } /** Row drag started */ onCellDrag(e) { var _a; (_a = this.orderEditor) === null || _a === void 0 ? void 0 : _a.dragStart(e.detail); } /** Recived keyboard down from element */ onKeyUp(e) { var _a; (_a = this.keyboardService) === null || _a === void 0 ? void 0 : _a.keyUp(e); } /** Recived keyboard down from element */ onKeyDown(e) { var _a; (_a = this.keyboardService) === null || _a === void 0 ? void 0 : _a.keyDown(e, this.range); } /** Create selection store */ selectionServiceSet(s) { this.selectionStoreService = new SelectionStoreService(s, { changeRange: range => { var _a; return !((_a = this.setRange.emit(range)) === null || _a === void 0 ? void 0 : _a.defaultPrevented); }, focus: (focus, end) => { var _a; const focused = { focus, end }; const { defaultPrevented } = this.internalFocusCell.emit(this.columnService.getSaveData(focus.y, focus.x)); if (defaultPrevented) { return false; } return !((_a = this.focusCell.emit(focused)) === null || _a === void 0 ? void 0 : _a.defaultPrevented); }, }); this.keyboardService = new KeyboardService({ selectionStoreService: this.selectionStoreService, selectionStore: s, doEdit: (v, c) => this.doEdit(v, c), clearCell: () => this.clearCell(), getData: () => this.getData(), internalPaste: () => this.internalPaste.emit(), }); this.createAutoFillService(); this.createClipboardService(); } createAutoFillService() { this.autoFillService = new AutoFillService({ selectionStoreService: this.selectionStoreService, dimensionRow: this.dimensionRow, dimensionCol: this.dimensionCol, columnService: this.columnService, dataStore: this.dataStore, setTempRange: e => this.setTempRange.emit(e), internalSelectionChanged: e => this.internalSelectionChanged.emit(e), internalRangeDataApply: e => this.internalRangeDataApply.emit(e), setRange: e => this.setRange.emit(e), getData: () => this.getData(), }); } columnServiceSet() { var _a; (_a = this.columnService) === null || _a === void 0 ? void 0 : _a.destroy(); this.columnService = new ColumnService(this.dataStore, this.colData); this.createAutoFillService(); this.createClipboardService(); } createClipboardService() { this.clipboardService = new ClipboardService({ selectionStoreService: this.selectionStoreService, columnService: this.columnService, dataStore: this.dataStore, onRangeApply: (d, r) => this.autoFillService.onRangeApply(d, r), internalCopy: () => this.internalCopy.emit(), }); } connectedCallback() { this.columnServiceSet(); this.selectionServiceSet(this.selectionStore); } disconnectedCallback() { var _a; (_a = this.columnService) === null || _a === void 0 ? void 0 : _a.destroy(); } renderRange(range) { const style = getElStyle(range, this.dimensionRow.state, this.dimensionCol.state); return [h("div", { class: SELECTION_BORDER_CLASS, style: style })]; } renderEditCell() { // if can edit const editCell = this.selectionStore.get('edit'); if (this.readonly || !editCell) { return; } const val = editCell.val || this.columnService.getCellData(editCell.y, editCell.x); const editable = Object.assign(Object.assign({}, editCell), this.columnService.getSaveData(editCell.y, editCell.x, val)); const style = getElStyle(Object.assign(Object.assign({}, editCell), { x1: editCell.x, y1: editCell.y }), this.dimensionRow.state, this.dimensionCol.state); return (h("revogr-edit", { onCellEdit: e => this.onCellEdit(e.detail), onCloseEdit: e => this.closeEdit(e), editCell: editable, column: this.columnService.columns[editCell.x], editor: this.columnService.getCellEditor(editCell.y, editCell.x, this.editors), style: style })); } render() { const range = this.selectionStoreService.ranged; const selectionFocus = this.selectionStoreService.focused; const els = []; if ((range || selectionFocus) && this.useClipboard) { els.push(this.clipboardService.renderClipboard()); } if (range) { els.push(...this.renderRange(range)); } const editCell = this.renderEditCell(); if (editCell) { els.push(editCell); } if (selectionFocus && !this.readonly && !editCell && this.range) { els.push(this.autoFillService.renderAutofill(range, selectionFocus)); } if (this.canDrag) { els.push(h("revogr-order-editor", { ref: e => (this.orderEditor = e), dataStore: this.dataStore, dimensionRow: this.dimensionRow, dimensionCol: this.dimensionCol, parent: this.element, onInternalRowDragStart: e => this.onRowDragStart(e) })); } return (h(Host, { onDblClick: () => this.doEdit(), onMouseDown: (e) => this.onElementMouseDown(e) }, els, h("slot", { name: "data" }))); } onElementMouseDown(e) { // Ignore focus if clicked input if (isEditInput(e.target)) { return; } const data = this.getData(); if (e.defaultPrevented) { return; } // Regular cell click const focusCell = getCurrentCell({ x: e.x, y: e.y }, data); this.selectionStoreService.focus(focusCell, this.range && e.shiftKey); // Initiate autofill selection if (this.range) { this.autoFillService.selectionStart(e, data); } } doEdit(val = '', isCancel = false) { var _a; if (this.canEdit()) { const editCell = this.selectionStore.get('focus'); const data = this.columnService.getSaveData(editCell.y, editCell.x); (_a = this.setEdit) === null || _a === void 0 ? void 0 : _a.emit(Object.assign(Object.assign({}, data), { isCancel, val })); } } closeEdit(e) { this.doEdit(undefined, true); if (e === null || e === void 0 ? void 0 : e.detail) { this.focusNext(); } } async focusNext() { const canFocus = await this.keyboardService.keyChangeSelection(new KeyboardEvent('keydown', { code: codesLetter.ARROW_DOWN, }), this.range); if (!canFocus) { this.closeEdit(); } } clearCell() { if (this.selectionStoreService.ranged && !isRangeSingleCell(this.selectionStoreService.ranged)) { const data = this.columnService.getRangeStaticData(this.selectionStoreService.ranged, ''); this.autoFillService.onRangeApply(data, this.selectionStoreService.ranged); } else if (this.canEdit()) { const focused = this.selectionStoreService.focused; this.onCellEdit({ rgRow: focused.y, rgCol: focused.x, val: '' }, true); } } /** Edit finished, close cell and save */ onCellEdit(e, clear = false) { const dataToSave = this.columnService.getSaveData(e.rgRow, e.rgCol, e.val); this.internalCellEdit.emit(dataToSave); // if not clear navigate to next cell after edit if (!clear && !e.preventFocus) { this.focusNext(); } } onRowDragStart({ detail }) { detail.text = this.columnService.getCellData(detail.cell.y, detail.cell.x); } /** Check if edit possible */ canEdit() { var _a; if (this.readonly) { return false; } const editCell = this.selectionStoreService.focused; return editCell && !((_a = this.columnService) === null || _a === void 0 ? void 0 : _a.isReadOnly(editCell.y, editCell.x)); } /** Collect data from element */ getData() { return { el: this.element, rows: this.dimensionRow.state, cols: this.dimensionCol.state, lastCell: this.lastCell, }; } static get is() { return "revogr-overlay-selection"; } static get originalStyleUrls() { return { "$": ["revogr-overlay-style.scss"] }; } static get styleUrls() { return { "$": ["revogr-overlay-style.css"] }; } static get properties() { return { "readonly": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "readonly", "reflect": false }, "range": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "range", "reflect": false }, "canDrag": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "can-drag", "reflect": false }, "useClipboard": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "use-clipboard", "reflect": false }, "selectionStore": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<Selection.SelectionStoreState>", "resolved": "ObservableMap<SelectionStoreState>", "references": { "Observable": { "location": "import", "path": "../../interfaces" }, "Selection": { "location": "import", "path": "../../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Dynamic stores" } }, "dimensionRow": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<RevoGrid.DimensionSettingsState>", "resolved": "ObservableMap<DimensionSettingsState>", "references": { "Observable": { "location": "import", "path": "../../interfaces" }, "RevoGrid": { "location": "import", "path": "../../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" } }, "dimensionCol": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<RevoGrid.DimensionSettingsState>", "resolved": "ObservableMap<DimensionSettingsState>", "references": { "Observable": { "location": "import", "path": "../../interfaces" }, "RevoGrid": { "location": "import", "path": "../../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" } }, "dataStore": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<DataSourceState<RevoGrid.DataType, RevoGrid.DimensionRows>>", "resolved": "ObservableMap<DataSourceState<DataType, DimensionRows>>", "references": { "Observable": { "location": "import", "path": "../../interfaces" }, "DataSourceState": { "location": "import", "path": "../../store/dataSource/data.store" }, "RevoGrid": { "location": "import", "path": "../../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Static stores, not expected to change during component lifetime" } }, "colData": { "type": "unknown", "mutable": false, "complexType": { "original": "Observable<DataSourceState<RevoGrid.ColumnRegular, RevoGrid.DimensionCols>>", "resolved": "ObservableMap<DataSourceState<ColumnRegular, DimensionCols>>", "references": { "Observable": { "location": "import", "path": "../../interfaces" }, "DataSourceState": { "location": "import", "path": "../../store/dataSource/data.store" }, "RevoGrid": { "location": "import", "path": "../../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "" } }, "lastCell": { "type": "unknown", "mutable": false, "complexType": { "original": "Selection.Cell", "resolved": "Cell", "references": { "Selection": { "location": "import", "path": "../../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Last cell position" } }, "editors": { "type": "unknown", "mutable": false, "complexType": { "original": "Edition.Editors", "resolved": "{ [name: string]: EditorCtr; }", "references": { "Edition": { "location": "import", "path": "../../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Custom editors register" } } }; } static get events() { return [{ "method": "internalCopy", "name": "internalCopy", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "any", "resolved": "any", "references": {} } }, { "method": "internalPaste", "name": "internalPaste", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "any", "resolved": "any", "references": {} } }, { "method": "internalCellEdit", "name": "internalCellEdit", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "Edition.BeforeSaveDataDetails", "resolved": "{ prop: ColumnProp; model: DataType; val?: string; rowIndex: number; type: DimensionRows; }", "references": { "Edition": { "location": "import", "path": "../../interfaces" } } } }, { "method": "internalFocusCell", "name": "internalFocusCell", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "Edition.BeforeSaveDataDetails", "resolved": "{ prop: ColumnProp; model: DataType; val?: string; rowIndex: number; type: DimensionRows; }", "references": { "Edition": { "location": "import", "path": "../../interfaces" } } } }, { "method": "setEdit", "name": "setEdit", "bubbles": false, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "Edition.BeforeEdit", "resolved": "{ isCancel: boolean; } & BeforeSaveDataDetails", "references": { "Edition": { "location": "import", "path": "../../interfaces" } } } }, { "method": "setRange", "name": "setRange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "Selection.RangeArea", "resolved": "{ x: number; y: number; x1: number; y1: number; }", "references": { "Selection": { "location": "import", "path": "../../interfaces" } } } }, { "method": "setTempRange", "name": "setTempRange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "Selection.TempRange | null", "resolved": "{ type: string; area: RangeArea; }", "references": { "Selection": { "location": "import", "path": "../../interfaces" } } } }, { "method": "focusCell", "name": "focusCell", "bubbles": false, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "" }, "complexType": { "original": "Selection.FocusedCells", "resolved": "{ focus: Cell; end: Cell; }", "references": { "Selection": { "location": "import", "path": "../../interfaces" } } } }, { "method": "internalSelectionChanged", "name": "internalSelectionChanged", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Selection range changed" }, "complexType": { "original": "Selection.ChangedRange", "resolved": "{ type: DimensionRows; newRange: RangeArea; oldRange: RangeArea; newProps: ColumnProp[]; oldProps: ColumnProp[]; newData: { [key: number]: DataType; }; }", "references": { "Selection": { "location": "import", "path": "../../interfaces" } } } }, { "method": "internalRangeDataApply", "name": "internalRangeDataApply", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Range data apply" }, "complexType": { "original": "Edition.BeforeRangeSaveDataDetails", "resolved": "{ data: DataLookup; models: { [rowIndex: number]: DataType; }; type: DimensionRows; }", "references": { "Edition": { "location": "import", "path": "../../interfaces" } } } }]; } static get elementRef() { return "element"; } static get watchers() { return [{ "propName": "selectionStore", "methodName": "selectionServiceSet" }, { "propName": "dimensionRow", "methodName": "createAutoFillService" }, { "propName": "dimensionCol", "methodName": "createAutoFillService" }, { "propName": "dataStore", "methodName": "columnServiceSet" }, { "propName": "colData", "methodName": "columnServiceSet" }]; } static get listeners() { return [{ "name": "mousemove", "method": "onMouseMove", "target": "document", "capture": false, "passive": true }, { "name": "mouseleave", "method": "onMouseOut", "target": "document", "capture": false, "passive": true }, { "name": "mouseup", "method": "onMouseUp", "target": "document", "capture": false, "passive": true }, { "name": "dragStartCell", "method": "onCellDrag", "target": undefined, "capture": false, "passive": false }, { "name": "keyup", "method": "onKeyUp", "target": "document", "capture": false, "passive": false }, { "name": "keydown", "method": "onKeyDown", "target": "document", "capture": false, "passive": false }]; } }