apex-grid 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +439 -0
- package/LICENSE.md +125 -0
- package/README.md +107 -14
- package/components/cell.d.ts +30 -4
- package/components/cell.js +199 -53
- package/components/cell.js.map +1 -1
- package/components/filter-row.d.ts +2 -0
- package/components/filter-row.js +82 -28
- package/components/filter-row.js.map +1 -1
- package/components/grid.d.ts +257 -20
- package/components/grid.js +463 -99
- package/components/grid.js.map +1 -1
- package/components/group-header-row.js +28 -25
- package/components/group-header-row.js.map +1 -1
- package/components/header-row.d.ts +7 -5
- package/components/header-row.js +19 -9
- package/components/header-row.js.map +1 -1
- package/components/header.js +36 -22
- package/components/header.js.map +1 -1
- package/components/row.d.ts +19 -3
- package/components/row.js +96 -22
- package/components/row.js.map +1 -1
- package/components/toolbar.js +2 -1
- package/components/toolbar.js.map +1 -1
- package/components/virtualizer.d.ts +45 -1
- package/components/virtualizer.js +184 -3
- package/components/virtualizer.js.map +1 -1
- package/controllers/dom.d.ts +4 -4
- package/controllers/dom.js +40 -22
- package/controllers/dom.js.map +1 -1
- package/controllers/editing.d.ts +104 -37
- package/controllers/editing.js +230 -78
- package/controllers/editing.js.map +1 -1
- package/controllers/expansion.d.ts +14 -5
- package/controllers/expansion.js +24 -1
- package/controllers/expansion.js.map +1 -1
- package/controllers/filter.d.ts +10 -2
- package/controllers/filter.js +62 -17
- package/controllers/filter.js.map +1 -1
- package/controllers/history.d.ts +45 -8
- package/controllers/history.js +58 -20
- package/controllers/history.js.map +1 -1
- package/controllers/navigation.d.ts +68 -13
- package/controllers/navigation.js +196 -51
- package/controllers/navigation.js.map +1 -1
- package/controllers/pagination.d.ts +6 -0
- package/controllers/pagination.js +10 -2
- package/controllers/pagination.js.map +1 -1
- package/controllers/reorder.js +42 -24
- package/controllers/reorder.js.map +1 -1
- package/controllers/resize.d.ts +17 -2
- package/controllers/resize.js +16 -6
- package/controllers/resize.js.map +1 -1
- package/controllers/row-pin.d.ts +20 -2
- package/controllers/row-pin.js +16 -9
- package/controllers/row-pin.js.map +1 -1
- package/controllers/row-reorder.d.ts +31 -11
- package/controllers/row-reorder.js +117 -37
- package/controllers/row-reorder.js.map +1 -1
- package/controllers/selection.d.ts +11 -4
- package/controllers/selection.js +26 -11
- package/controllers/selection.js.map +1 -1
- package/controllers/sort.d.ts +22 -2
- package/controllers/sort.js +29 -23
- package/controllers/sort.js.map +1 -1
- package/controllers/state.d.ts +65 -10
- package/controllers/state.js +86 -6
- package/controllers/state.js.map +1 -1
- package/controllers/tree.d.ts +15 -10
- package/controllers/tree.js +0 -0
- package/controllers/tree.js.map +1 -1
- package/custom-elements.json +3589 -452
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/highlight.css +15 -8
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/ApexGrid.html +660 -0
- package/docs/classes/ApexGridPaginator.html +36 -0
- package/docs/classes/ApexGridToolbar.html +67 -0
- package/docs/functions/localize.html +2 -2
- package/docs/functions/setup.html +1 -2
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +105 -15
- package/docs/interfaces/ApexCellValidationFailedEvent.html +3 -2
- package/docs/interfaces/ApexCellValueChangedEvent.html +4 -2
- package/docs/interfaces/ApexCellValueChangingEvent.html +3 -2
- package/docs/interfaces/ApexColumnMovedEvent.html +2 -2
- package/docs/interfaces/ApexColumnMovingEvent.html +1 -1
- package/docs/interfaces/ApexDetailContext.html +2 -2
- package/docs/interfaces/ApexGridEventMap.html +9 -6
- package/docs/interfaces/ApexRowEditEndedEvent.html +2 -1
- package/docs/interfaces/ApexRowEditStartedEvent.html +2 -1
- package/docs/interfaces/ApexRowMovingEvent.html +9 -3
- package/docs/interfaces/ApexRowPinningEvent.html +1 -1
- package/docs/interfaces/ApexTreeRowExpandedEvent.html +4 -1
- package/docs/interfaces/ApexTreeRowExpandingEvent.html +4 -1
- package/docs/interfaces/BaseApexCellContext.html +1 -1
- package/docs/interfaces/BaseApexEditorContext.html +1 -1
- package/docs/interfaces/BaseColumnConfiguration.html +3 -3
- package/docs/interfaces/BaseFilterExpression.html +2 -2
- package/docs/interfaces/CSVExportOptions.html +13 -5
- package/docs/interfaces/DataPipelineConfiguration.html +2 -2
- package/docs/interfaces/EditingHistoryConfiguration.html +1 -1
- package/docs/interfaces/ExportFormat.html +2 -2
- package/docs/interfaces/ExportOptions.html +5 -3
- package/docs/interfaces/GridExpansionConfiguration.html +6 -2
- package/docs/interfaces/GridRowPinningConfiguration.html +1 -1
- package/docs/interfaces/GridRowReorderingConfiguration.html +2 -2
- package/docs/interfaces/PaginationConfiguration.html +1 -1
- package/docs/interfaces/PaginationState.html +1 -1
- package/docs/interfaces/SetStateResult.html +3 -2
- package/docs/interfaces/ValidatorContext.html +7 -2
- package/docs/media/_token-values.scss +118 -0
- package/docs/media/package.json +5 -5
- package/docs/media/state-persistence.html +0 -10
- package/docs/modules.html +1 -1
- package/docs/types/DataPipelineParams.html +2 -2
- package/docs/types/EditMode.html +2 -2
- package/docs/types/ExportSource.html +6 -4
- package/docs/types/GridLocaleText.html +1 -1
- package/docs/types/OperandKeys.html +9 -0
- package/docs/types/PaginationMode.html +1 -1
- package/docs/variables/EN_LOCALE.html +3 -3
- package/docs/variables/esLocale.html +1 -1
- package/i18n/en.d.ts +19 -0
- package/i18n/en.js +19 -0
- package/i18n/en.js.map +1 -1
- package/i18n/es.js +19 -0
- package/i18n/es.js.map +1 -1
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/index.js.map +1 -1
- package/internal/column-types.d.ts +27 -1
- package/internal/column-types.js +75 -49
- package/internal/column-types.js.map +1 -1
- package/internal/constants.d.ts +1 -2
- package/internal/constants.js +0 -4
- package/internal/constants.js.map +1 -1
- package/internal/document-styles.d.ts +30 -0
- package/internal/document-styles.js +48 -0
- package/internal/document-styles.js.map +1 -0
- package/internal/export.d.ts +39 -7
- package/internal/export.js +35 -34
- package/internal/export.js.map +1 -1
- package/internal/feature-module.d.ts +82 -3
- package/internal/feature-module.js +11 -0
- package/internal/feature-module.js.map +1 -1
- package/internal/index.d.ts +6 -4
- package/internal/index.js +5 -3
- package/internal/index.js.map +1 -1
- package/internal/normalize-case.d.ts +8 -1
- package/internal/normalize-case.js +3 -2
- package/internal/normalize-case.js.map +1 -1
- package/internal/state-snapshot.d.ts +60 -8
- package/internal/state-snapshot.js +87 -19
- package/internal/state-snapshot.js.map +1 -1
- package/internal/types.d.ts +17 -2
- package/internal/types.js.map +1 -1
- package/internal/utils.d.ts +48 -6
- package/internal/utils.js +35 -21
- package/internal/utils.js.map +1 -1
- package/internal/validators.js +1 -0
- package/internal/validators.js.map +1 -1
- package/operations/filter/operands/number.js +9 -6
- package/operations/filter/operands/number.js.map +1 -1
- package/operations/filter/operands/string.js +2 -3
- package/operations/filter/operands/string.js.map +1 -1
- package/operations/filter/types.d.ts +13 -1
- package/operations/filter/types.js.map +1 -1
- package/operations/filter.d.ts +6 -0
- package/operations/filter.js +23 -7
- package/operations/filter.js.map +1 -1
- package/operations/quick-filter.js +2 -5
- package/operations/quick-filter.js.map +1 -1
- package/operations/sort.d.ts +21 -2
- package/operations/sort.js +41 -14
- package/operations/sort.js.map +1 -1
- package/package.json +8 -4
- package/setup.d.ts +23 -11
- package/setup.js +19 -21
- package/setup.js.map +1 -1
- package/styles/_common.css.js +1 -1
- package/styles/_common.css.js.map +1 -1
- package/styles/_token-values.css.d.ts +1 -0
- package/styles/_token-values.css.js +3 -0
- package/styles/_token-values.css.js.map +1 -0
- package/styles/_tokens.css.js +1 -1
- package/styles/_tokens.css.js.map +1 -1
- package/styles/body-cell/body-cell.css.js +1 -1
- package/styles/body-cell/body-cell.css.js.map +1 -1
- package/styles/body-cell/cell-renderers.css.js +1 -1
- package/styles/body-cell/cell-renderers.css.js.map +1 -1
- package/styles/body-row/body-row.css.js +1 -1
- package/styles/body-row/body-row.css.js.map +1 -1
- package/styles/document-tokens.css.d.ts +1 -0
- package/styles/document-tokens.css.js +3 -0
- package/styles/document-tokens.css.js.map +1 -0
- package/styles/filter-row/filter-row.css.js +1 -1
- package/styles/filter-row/filter-row.css.js.map +1 -1
- package/styles/grid/_grid.base.css.js +1 -1
- package/styles/grid/_grid.base.css.js.map +1 -1
- package/styles/grid/grid.css.js +1 -1
- package/styles/grid/grid.css.js.map +1 -1
- package/styles/group-header-row/group-header-row.css.js +1 -1
- package/styles/group-header-row/group-header-row.css.js.map +1 -1
- package/styles/header-cell/header-cell.css.js +1 -1
- package/styles/header-cell/header-cell.css.js.map +1 -1
- package/styles/header-row/header-row.base.css.js +1 -1
- package/styles/header-row/header-row.base.css.js.map +1 -1
- package/styles/paginator/paginator.css.js +1 -1
- package/styles/paginator/paginator.css.js.map +1 -1
- package/styles/toolbar/toolbar.css.js +1 -1
- package/styles/toolbar/toolbar.css.js.map +1 -1
- package/tokens.css +78 -0
- package/LICENSE +0 -80
- package/docs/media/LICENSE +0 -80
- package/docs/media/_tokens.scss +0 -193
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/controllers/history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { GridHost, Keys } from '../internal/types.js';\n\n/** Default number of commands retained on the undo stack. */\nconst DEFAULT_STACK_SIZE = 100;\n\n/**\n * A single recorded cell mutation: the record reference, its column, and the\n * before / after values. `rowIndex` is the view-relative index at record time\n * and is used only for the replayed `cellValueChanged` event (the live record\n * reference is authoritative).\n */\nexport interface CellChange<T extends object> {\n record: T;\n key: Keys<T>;\n rowIndex: number;\n oldValue: unknown;\n newValue: unknown;\n}\n\n/**\n * A reversible unit of work on the history stacks.\n *\n * @remarks\n * `apply` re-performs the change (redo); `revert` undoes it. Cell-data edits use\n * {@link CellEditCommand}; the interface is deliberately open so later command\n * kinds (e.g. view-state) can join the same stacks.\n */\nexport interface EditCommand {\n apply(): void;\n revert(): void;\n}\n\n/**\n * One or more cell mutations that undo / redo as a single step. A single-cell\n * edit holds one change; a row-mode commit or a bulk paste / fill holds many so\n * one Ctrl+Z reverts the whole batch.\n */\nexport class CellEditCommand<T extends object> implements EditCommand {\n constructor(\n private host: GridHost<T>,\n public readonly changes: ReadonlyArray<CellChange<T>>\n ) {}\n\n public apply(): void {\n for (const change of this.changes) {\n this.#write(change, change.newValue);\n }\n }\n\n public revert(): void {\n // Revert in reverse so overlapping writes to the same cell unwind cleanly.\n for (let i = this.changes.length - 1; i >= 0; i -= 1) {\n this.#write(this.changes[i], this.changes[i].oldValue);\n }\n }\n\n #write(change: CellChange<T>, value: unknown): void {\n (change.record as Record<string, unknown>)[change.key as string] = value;\n // Re-emit `cellValueChanged` (never the cancellable `cellValueChanging`) so\n // data-bound consumers stay in sync. Validation and history recording are\n // intentionally skipped on replay. Prefer the record's current view index\n // when it is on the page; fall back to the recorded one.\n const liveIndex = this.host.pageItems.indexOf(change.record);\n this.host.emitEvent('cellValueChanged', {\n detail: {\n key: change.key,\n rowIndex: liveIndex >= 0 ? liveIndex : change.rowIndex,\n data: change.record,\n value,\n },\n });\n }\n}\n\n/**\n * Reactive controller owning the undo / redo stacks for cell-data edits.\n *\n * @remarks\n * Recording is driven from the {@link EditingController.applyCellEdit} choke\n * point: every successful write calls {@link record}. Single edits push one\n * command; {@link beginBatch} / {@link endBatch} coalesce a row-mode commit or a\n * bulk paste / fill into one command. Disabled unless\n * `editing.history.enabled` is set, so the community grid pays nothing.\n */\nexport class HistoryController<T extends object> implements ReactiveController {\n #undo: EditCommand[] = [];\n #redo: EditCommand[] = [];\n #batch: CellChange<T>[] | null = null;\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected(): void {}\n\n /** Whether undo / redo tracking is enabled. */\n public get enabled(): boolean {\n return Boolean(this.host.editing?.history?.enabled);\n }\n\n /** Maximum number of commands retained on the undo stack. */\n public get stackSize(): number {\n const size = this.host.editing?.history?.stackSize;\n return typeof size === 'number' && size > 0 ? size : DEFAULT_STACK_SIZE;\n }\n\n /** Whether there is at least one command to undo. */\n public get canUndo(): boolean {\n return this.#undo.length > 0;\n }\n\n /** Whether there is at least one command to redo. */\n public get canRedo(): boolean {\n return this.#redo.length > 0;\n }\n\n /**\n * Records a single cell mutation. Routed to the open batch when one is active,\n * otherwise pushed immediately as a standalone command. No-op when history is\n * disabled.\n */\n public record(change: CellChange<T>): void {\n if (!this.enabled) return;\n if (this.#batch) {\n this.#batch.push(change);\n return;\n }\n this.#push(new CellEditCommand(this.host, [change]));\n }\n\n /**\n * Opens a batch so subsequent {@link record} calls coalesce into one command.\n * Nestable calls are flattened (the outermost {@link endBatch} commits).\n */\n public beginBatch(): void {\n if (!this.enabled) return;\n this.#batch ??= [];\n }\n\n /**\n * Closes the open batch, pushing it as a single command when it captured any\n * changes.\n */\n public endBatch(): void {\n const batch = this.#batch;\n this.#batch = null;\n if (batch && batch.length > 0) {\n this.#push(new CellEditCommand(this.host, batch));\n }\n }\n\n /** Reverts the most recent command. No-op when the undo stack is empty. */\n public undo(): void {\n const command = this.#undo.pop();\n if (!command) return;\n command.revert();\n this.#redo.push(command);\n this.#afterReplay(command, 'undo');\n }\n\n /** Re-applies the most recently undone command. No-op when nothing to redo. */\n public redo(): void {\n const command = this.#redo.pop();\n if (!command) return;\n command.apply();\n this.#undo.push(command);\n this.#afterReplay(command, 'redo');\n }\n\n /** Clears both stacks (e.g. after a data reset). */\n public clear(): void {\n if (this.#undo.length === 0 && this.#redo.length === 0) return;\n this.#undo = [];\n this.#redo = [];\n this.#batch = null;\n this.#emitChanged();\n }\n\n #push(command: EditCommand): void {\n this.#undo.push(command);\n // A fresh edit invalidates the redo branch.\n this.#redo = [];\n // Evict the oldest commands once the stack exceeds its cap.\n while (this.#undo.length > this.stackSize) {\n this.#undo.shift();\n }\n this.#emitChanged();\n }\n\n #afterReplay(command: EditCommand, kind: 'undo' | 'redo'): void {\n this.host.requestUpdate(PIPELINE);\n const count = command instanceof CellEditCommand ? command.changes.length : 1;\n const key =\n kind === 'undo'\n ? count === 1\n ? 'announce.undoOne'\n : 'announce.undoMany'\n : count === 1\n ? 'announce.redoOne'\n : 'announce.redoMany';\n this.host.announce(this.host.localize(key, { count }));\n this.#emitChanged();\n }\n\n #emitChanged(): void {\n this.host.emitEvent('historyChanged', {\n detail: { canUndo: this.canUndo, canRedo: this.canRedo },\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/controllers/history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { GridHost, Keys } from '../internal/types.js';\n\n/** Default number of commands retained on the undo stack. */\nconst DEFAULT_STACK_SIZE = 100;\n\n/**\n * A single recorded cell mutation: the record reference, its column, and the\n * before / after values. `rowIndex` is the view-relative index at record time;\n * replay reports the record's index at replay time instead (the live record\n * reference is authoritative).\n */\nexport interface CellChange<T extends object> {\n record: T;\n key: Keys<T>;\n rowIndex: number;\n oldValue: unknown;\n newValue: unknown;\n}\n\n/**\n * A reversible unit of work on the history stacks.\n *\n * @remarks\n * `apply` re-performs the change (redo); `revert` undoes it. Cell-data edits use\n * {@link CellEditCommand}; the interface is deliberately open so later command\n * kinds (e.g. view-state) can join the same stacks.\n */\nexport interface EditCommand {\n apply(): void;\n revert(): void;\n /** The record the command acts on: history is cleared once a reload drops it. */\n readonly record?: object;\n /**\n * For a command staged with a row edit: the value to stage for its cell,\n * re-read from the row's staged values just before the row commits, so\n * validation judges what the command will leave there.\n */\n stagedValue?(staged: ReadonlyMap<PropertyKey, unknown>): unknown;\n}\n\n/**\n * One or more cell mutations that undo / redo as a single step. A single-cell\n * edit holds one change; a row-mode commit or a bulk paste / fill holds many so\n * one Ctrl+Z reverts the whole batch. `commands` are other commands pushed in\n * the same batch (a feature's state riding with the edit, such as a formula):\n * they replay after the cell values in both directions, so a value write can\n * never undo the state they restore.\n */\nexport class CellEditCommand<T extends object> implements EditCommand {\n constructor(\n private host: GridHost<T>,\n public readonly changes: ReadonlyArray<CellChange<T>>,\n public readonly commands: ReadonlyArray<EditCommand> = []\n ) {}\n\n public apply(): void {\n const indexOf = viewIndexer(this.host.pageItems, this.changes.length);\n for (const change of this.changes) {\n this.#write(change, change.newValue, indexOf);\n }\n for (const command of this.commands) command.apply();\n }\n\n public revert(): void {\n const indexOf = viewIndexer(this.host.pageItems, this.changes.length);\n // Revert in reverse so overlapping writes to the same cell unwind cleanly.\n for (let i = this.changes.length - 1; i >= 0; i -= 1) {\n this.#write(this.changes[i], this.changes[i].oldValue, indexOf);\n }\n for (let i = this.commands.length - 1; i >= 0; i -= 1) this.commands[i].revert();\n }\n\n #write(change: CellChange<T>, value: unknown, indexOf: (record: T) => number): void {\n (change.record as Record<string, unknown>)[change.key as string] = value;\n // Re-emit `cellValueChanged` (never the cancellable `cellValueChanging`) so\n // data-bound consumers stay in sync. Validation and history recording are\n // intentionally skipped on replay. The index is where the record is now:\n // -1 when it is pinned or on another page.\n this.host.emitEvent('cellValueChanged', {\n detail: { key: change.key, rowIndex: indexOf(change.record), data: change.record, value },\n });\n }\n}\n\n/**\n * Returns a lookup of a record's index in `items` (by reference, `-1` when\n * absent). For more than one lookup it indexes `items` once, so a large batch\n * costs one pass instead of one scan per record.\n */\nexport function viewIndexer<T>(items: ReadonlyArray<T>, lookups: number): (record: T) => number {\n if (lookups < 2) return (record) => items.indexOf(record);\n let index: Map<T, number> | undefined;\n return (record) => {\n if (!index) {\n index = new Map();\n // Backwards, so a record listed twice resolves to its first index.\n for (let i = items.length - 1; i >= 0; i -= 1) index.set(items[i], i);\n }\n return index.get(record) ?? -1;\n };\n}\n\n/**\n * Reactive controller owning the undo / redo stacks for cell-data edits.\n *\n * @remarks\n * Recording is driven from the {@link EditingController.applyCellEdit} choke\n * point: every successful write calls {@link record}. Single edits push one\n * command; {@link beginBatch} / {@link endBatch} coalesce a row-mode commit or a\n * bulk paste / fill into one command. Disabled unless\n * `editing.history.enabled` is set, so the community grid pays nothing.\n */\nexport class HistoryController<T extends object> implements ReactiveController {\n #undo: EditCommand[] = [];\n #redo: EditCommand[] = [];\n /** Changes recorded by the open batch, pushed as one command when it closes. */\n #batch: CellChange<T>[] = [];\n /** Commands {@link push}ed while the batch is open, replayed after its changes. */\n #commands: EditCommand[] = [];\n /** Open {@link beginBatch} calls; only the outermost {@link endBatch} commits. */\n #depth = 0;\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected(): void {}\n\n /** Whether undo / redo tracking is enabled. */\n public get enabled(): boolean {\n return Boolean(this.host.editing?.history?.enabled);\n }\n\n /** Maximum number of commands retained on the undo stack. */\n public get stackSize(): number {\n const size = this.host.editing?.history?.stackSize;\n return typeof size === 'number' && size > 0 ? size : DEFAULT_STACK_SIZE;\n }\n\n /** Whether there is at least one command to undo. */\n public get canUndo(): boolean {\n return this.#undo.length > 0;\n }\n\n /** Whether there is at least one command to redo. */\n public get canRedo(): boolean {\n return this.#redo.length > 0;\n }\n\n /**\n * Records a single cell mutation. Routed to the open batch when one is active,\n * otherwise pushed immediately as a standalone command. No-op when history is\n * disabled.\n */\n public record(change: CellChange<T>): void {\n if (!this.enabled) return;\n if (this.#depth > 0) {\n this.#batch.push(change);\n return;\n }\n this.#push(new CellEditCommand(this.host, [change]));\n }\n\n /**\n * Records a command that is not a plain cell write (its first `apply` has\n * already run). Joins the open batch, replaying after the batch's cell\n * changes, otherwise pushed as its own step. No-op when history is disabled.\n */\n public push(command: EditCommand): void {\n if (!this.enabled) return;\n if (this.#depth > 0) this.#commands.push(command);\n else this.#push(command);\n }\n\n /**\n * Opens a batch so subsequent {@link record} calls coalesce into one command.\n * Nested calls are flattened: a batch opened while another is open (a\n * `cellValueChanged` listener that calls `applyEdits`, say) joins it, and\n * only the outermost {@link endBatch} commits. Counted even while history\n * is disabled, so every begin pairs with its end.\n */\n public beginBatch(): void {\n this.#depth += 1;\n }\n\n /**\n * Closes the innermost open batch. Closing the outermost pushes everything\n * it captured as a single command.\n */\n public endBatch(): void {\n if (this.#depth === 0 || --this.#depth > 0) return;\n const batch = this.#batch;\n const commands = this.#commands;\n this.#batch = [];\n this.#commands = [];\n if (batch.length > 0 || commands.length > 0) {\n this.#push(new CellEditCommand(this.host, batch, commands));\n }\n }\n\n /** Reverts the most recent command. No-op when the undo stack is empty. */\n public undo(): void {\n const command = this.#undo.pop();\n if (!command) return;\n command.revert();\n this.#redo.push(command);\n this.#afterReplay(command, 'undo');\n }\n\n /** Re-applies the most recently undone command. No-op when nothing to redo. */\n public redo(): void {\n const command = this.#redo.pop();\n if (!command) return;\n command.apply();\n this.#undo.push(command);\n this.#afterReplay(command, 'redo');\n }\n\n /** Clears both stacks, and the changes an open batch has captured so far. */\n public clear(): void {\n this.#batch = [];\n this.#commands = [];\n if (this.#undo.length === 0 && this.#redo.length === 0) return;\n this.#undo = [];\n this.#redo = [];\n this.#emitChanged();\n }\n\n /**\n * Clears both stacks when the grid's data was replaced and a recorded change\n * (or a command's {@link EditCommand.record}) names a record that `records`\n * no longer yields (a reload with new objects).\n * Replaying onto such a detached record would change nothing on screen while\n * still reporting the edit. New data that keeps the edited records (an\n * append, a re-sorted copy) keeps its history. `records` is read only while\n * a stack has entries, so the caller may pass an expensive source.\n */\n public reconcile(records: () => Iterable<T>): void {\n if (this.#undo.length === 0 && this.#redo.length === 0) return;\n const live = new Set(records());\n const stale = (command: EditCommand): boolean =>\n (command.record !== undefined && !live.has(command.record as T)) ||\n (command instanceof CellEditCommand &&\n (command.changes.some((change: CellChange<T>) => !live.has(change.record)) ||\n command.commands.some(stale)));\n if (this.#undo.some(stale) || this.#redo.some(stale)) this.clear();\n }\n\n #push(command: EditCommand): void {\n this.#undo.push(command);\n // A fresh edit invalidates the redo branch.\n this.#redo = [];\n // Evict the oldest commands once the stack exceeds its cap.\n while (this.#undo.length > this.stackSize) {\n this.#undo.shift();\n }\n this.#emitChanged();\n }\n\n #afterReplay(command: EditCommand, kind: 'undo' | 'redo'): void {\n this.host.requestUpdate(PIPELINE);\n const count = command instanceof CellEditCommand ? command.changes.length || 1 : 1;\n const key =\n kind === 'undo'\n ? count === 1\n ? 'announce.undoOne'\n : 'announce.undoMany'\n : count === 1\n ? 'announce.redoOne'\n : 'announce.redoMany';\n this.host.announce(this.host.localize(key, { count }));\n this.#emitChanged();\n }\n\n #emitChanged(): void {\n this.host.emitEvent('historyChanged', {\n detail: { canUndo: this.canUndo, canRedo: this.canRedo },\n });\n }\n}\n"]}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { ReactiveController } from 'lit';
|
|
2
|
+
import type ApexGridRow from '../components/row.js';
|
|
3
|
+
import { type ScrollInsets } from '../components/virtualizer.js';
|
|
2
4
|
import type { ActiveNode, ColumnConfiguration, GridHost, Keys } from '../internal/types.js';
|
|
3
5
|
export declare class NavigationController<T extends object> implements ReactiveController {
|
|
6
|
+
#private;
|
|
4
7
|
protected host: GridHost<T>;
|
|
5
8
|
protected handlers: Map<string, (() => void) | (() => void) | (() => void) | (() => void) | (() => void) | (() => void) | (() => void) | (() => void) | (() => void)>;
|
|
6
9
|
protected get virtualizer(): import("../components/virtualizer.js").default;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The active cell of this grid. Instance state on purpose: every grid on a
|
|
12
|
+
* page, including one nested in another grid's detail panel, keeps its own.
|
|
13
|
+
*/
|
|
14
|
+
protected _active: ActiveNode<T>;
|
|
15
|
+
protected get nextNode(): ActiveNode<T>;
|
|
13
16
|
protected get columns(): Array<{
|
|
14
17
|
key: Keys<T>;
|
|
15
18
|
hidden?: boolean;
|
|
@@ -19,21 +22,72 @@ export declare class NavigationController<T extends object> implements ReactiveC
|
|
|
19
22
|
hidden?: boolean;
|
|
20
23
|
}[];
|
|
21
24
|
protected get firstColumn(): Keys<T>;
|
|
25
|
+
/** Whether there is any cell to move to: a body row and a visible column. */
|
|
26
|
+
protected get navigable(): boolean;
|
|
22
27
|
protected getPreviousColumn(key: Keys<T>): Keys<T>;
|
|
23
28
|
protected getNextColumn(key: Keys<T>): Keys<T>;
|
|
29
|
+
/** The rendered body row at `index`, when the virtualizer has it in the DOM. */
|
|
30
|
+
protected findRow(index: number): ApexGridRow<T> | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* The element that stands for `node` in the DOM: the cell, or for a row a
|
|
33
|
+
* feature module renders full width (a group header, a total row) the
|
|
34
|
+
* row's single focus target, whatever the column.
|
|
35
|
+
*/
|
|
36
|
+
protected findCell(node: ActiveNode<T>): HTMLElement | undefined;
|
|
24
37
|
protected scrollToCell(node: ActiveNode<T>): void;
|
|
25
|
-
|
|
38
|
+
/** Scrolls body row `index` into view, rendered or not. */
|
|
39
|
+
protected scrollToRow(index: number): void;
|
|
40
|
+
/**
|
|
41
|
+
* Scrolls the cell at `node` into view clear of the sticky chrome without
|
|
42
|
+
* moving the active cell: the cell itself when it is rendered, else its row
|
|
43
|
+
* first and the cell once the virtualizer has rendered it. For a feature
|
|
44
|
+
* that moves something other than the active cell with the keyboard (range
|
|
45
|
+
* extension), so it scrolls the way arrow navigation does.
|
|
46
|
+
*/
|
|
47
|
+
revealCell(node: ActiveNode<T>): void;
|
|
48
|
+
/**
|
|
49
|
+
* Whether ArrowLeft or ArrowRight steps towards the end of the column
|
|
50
|
+
* order: ArrowRight in a left-to-right grid, ArrowLeft in a right-to-left
|
|
51
|
+
* one. The rule every inline arrow key on this grid follows.
|
|
52
|
+
*/
|
|
53
|
+
towardsInlineEnd(key: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* How much of the scroll container's scrollport the sticky chrome covers
|
|
56
|
+
* (toolbar, headers and the pinned-top band above, the pinned-bottom band
|
|
57
|
+
* and the paginator below, pinned columns on either side). Keyboard scrolls
|
|
58
|
+
* use it as scroll-padding, so a row or cell the keyboard moves to is never
|
|
59
|
+
* parked underneath that chrome. Measured right before each keyboard scroll
|
|
60
|
+
* because any of those bands can change size between key presses. Public
|
|
61
|
+
* so a feature scrolling the body itself (drag auto-scroll) measures the
|
|
62
|
+
* same chrome.
|
|
63
|
+
*/
|
|
64
|
+
scrollInsets(): ScrollInsets;
|
|
26
65
|
/**
|
|
27
66
|
* Moves real DOM focus to the active cell (the grid body's roving tab stop),
|
|
28
67
|
* so screen readers track keyboard navigation. Waits for the host render to
|
|
29
|
-
* commit the cell's `active` state,
|
|
30
|
-
*
|
|
31
|
-
* cell is active (the scroll container keeps focus as the
|
|
68
|
+
* commit the cell's `active` state and, when the cell is not in the DOM yet
|
|
69
|
+
* (a long jump), for the virtualizer to finish laying out the new range.
|
|
70
|
+
* No-op while no cell is active (the scroll container keeps focus as the
|
|
71
|
+
* entry point).
|
|
72
|
+
*
|
|
73
|
+
* @param reveal - also scroll the cell into view once it exists, for jumps
|
|
74
|
+
* whose target row was not rendered when the scroll started.
|
|
32
75
|
*/
|
|
33
|
-
focusActiveCell(): void;
|
|
76
|
+
focusActiveCell(reveal?: boolean): void;
|
|
34
77
|
get active(): ActiveNode<T>;
|
|
35
78
|
set active(node: ActiveNode<T>);
|
|
36
79
|
constructor(host: GridHost<T>);
|
|
80
|
+
/**
|
|
81
|
+
* Keeps the active cell pointing at something that exists. Runs before
|
|
82
|
+
* every grid render, so after a data swap, sort, filter, page change or a
|
|
83
|
+
* column hide or reorder the active node is clamped to the last row and the
|
|
84
|
+
* nearest visible column, or cleared when there is nothing left to point
|
|
85
|
+
* at (no rows, no visible columns). Without it a stale node names a cell
|
|
86
|
+
* that is never rendered, which leaves the body with no tab stop.
|
|
87
|
+
*/
|
|
88
|
+
hostUpdate(): void;
|
|
89
|
+
/** The active row is one a feature module renders full width (no cells). */
|
|
90
|
+
protected isPresentedRow(index: number): boolean;
|
|
37
91
|
/** Home moves to the first cell in the row (ARIA grid pattern). */
|
|
38
92
|
protected home(): void;
|
|
39
93
|
/** End moves to the last cell in the row (ARIA grid pattern). */
|
|
@@ -43,8 +97,9 @@ export declare class NavigationController<T extends object> implements ReactiveC
|
|
|
43
97
|
/** Ctrl/Cmd+End moves to the last cell of the grid. */
|
|
44
98
|
protected ctrlEnd(): void;
|
|
45
99
|
/**
|
|
46
|
-
* Rows per Page Up/Down step
|
|
47
|
-
*
|
|
100
|
+
* Rows per Page Up/Down step: the rows that fit in the part of the
|
|
101
|
+
* scrollport the sticky chrome leaves visible, minus one so the row the
|
|
102
|
+
* user was on stays in view. Falls back to 10 when nothing is measurable.
|
|
48
103
|
*/
|
|
49
104
|
protected pageStep(): number;
|
|
50
105
|
protected pageUp(): void;
|
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { revealWithin } from '../components/virtualizer.js';
|
|
2
|
+
import { SENTINEL_NODE } from '../internal/constants.js';
|
|
2
3
|
import { GRID_ROW_TAG } from '../internal/tags.js';
|
|
3
|
-
import { getDisplayColumns } from '../internal/utils.js';
|
|
4
|
+
import { getDisplayColumns, isRTL } from '../internal/utils.js';
|
|
5
|
+
const RELATIVE_KEYS = new Set([
|
|
6
|
+
'ArrowDown',
|
|
7
|
+
'ArrowUp',
|
|
8
|
+
'ArrowLeft',
|
|
9
|
+
'ArrowRight',
|
|
10
|
+
'Home',
|
|
11
|
+
'End',
|
|
12
|
+
'PageUp',
|
|
13
|
+
'PageDown',
|
|
14
|
+
]);
|
|
4
15
|
export class NavigationController {
|
|
5
16
|
get virtualizer() {
|
|
6
17
|
return this.host.scrollContainer;
|
|
7
18
|
}
|
|
8
19
|
get nextNode() {
|
|
9
|
-
|
|
10
|
-
return node === SENTINEL_NODE
|
|
20
|
+
return this._active === SENTINEL_NODE
|
|
11
21
|
? { column: this.firstColumn, row: 0 }
|
|
12
|
-
: { ...
|
|
22
|
+
: { ...this._active };
|
|
13
23
|
}
|
|
14
24
|
get columns() {
|
|
15
25
|
return getDisplayColumns(this.host.columns);
|
|
@@ -21,40 +31,121 @@ export class NavigationController {
|
|
|
21
31
|
const first = this.visibleColumns.at(0);
|
|
22
32
|
return (first?.key ?? this.host.columns[0]?.key);
|
|
23
33
|
}
|
|
34
|
+
get navigable() {
|
|
35
|
+
return this.host.pageItems.length > 0 && this.visibleColumns.length > 0;
|
|
36
|
+
}
|
|
24
37
|
getPreviousColumn(key) {
|
|
25
38
|
const columns = this.visibleColumns;
|
|
26
39
|
const idx = columns.findIndex((column) => column.key === key);
|
|
27
|
-
return columns[Math.max(idx - 1, 0)]
|
|
40
|
+
return columns[Math.max(idx - 1, 0)]?.key ?? key;
|
|
28
41
|
}
|
|
29
42
|
getNextColumn(key) {
|
|
30
43
|
const columns = this.visibleColumns;
|
|
31
44
|
const idx = columns.findIndex((column) => column.key === key);
|
|
32
|
-
return columns[Math.min(idx + 1, columns.length - 1)]
|
|
45
|
+
return columns[Math.min(idx + 1, columns.length - 1)]?.key ?? key;
|
|
46
|
+
}
|
|
47
|
+
findRow(index) {
|
|
48
|
+
return Array.from(this.virtualizer.querySelectorAll(GRID_ROW_TAG)).find((row) => row.index === index);
|
|
49
|
+
}
|
|
50
|
+
findCell(node) {
|
|
51
|
+
const row = this.findRow(node.row);
|
|
52
|
+
if (!row)
|
|
53
|
+
return undefined;
|
|
54
|
+
return (row.presentedCell ??
|
|
55
|
+
row.cells.find((cell) => cell.column.key === node.column));
|
|
33
56
|
}
|
|
34
57
|
scrollToCell(node) {
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
58
|
+
const cell = this.findCell(node);
|
|
59
|
+
if (cell) {
|
|
60
|
+
revealWithin(this.host, cell, {
|
|
61
|
+
block: 'nearest',
|
|
62
|
+
inline: 'nearest',
|
|
63
|
+
insets: this.scrollInsets(),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
scrollToRow(index) {
|
|
68
|
+
this.virtualizer
|
|
69
|
+
.element(index)
|
|
70
|
+
?.scrollIntoView({ block: 'nearest', insets: this.scrollInsets() });
|
|
71
|
+
}
|
|
72
|
+
#revealing;
|
|
73
|
+
revealCell(node) {
|
|
74
|
+
this.#revealing = node;
|
|
75
|
+
if (this.findCell(node)) {
|
|
76
|
+
this.scrollToCell(node);
|
|
77
|
+
return;
|
|
40
78
|
}
|
|
79
|
+
this.scrollToRow(node.row);
|
|
80
|
+
void this.virtualizer.settled().then(() => {
|
|
81
|
+
if (this.#revealing === node)
|
|
82
|
+
this.scrollToCell(node);
|
|
83
|
+
});
|
|
41
84
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
85
|
+
towardsInlineEnd(key) {
|
|
86
|
+
return isRTL(this.host) === (key === 'ArrowLeft');
|
|
87
|
+
}
|
|
88
|
+
scrollInsets() {
|
|
89
|
+
const host = this.host;
|
|
90
|
+
const root = host.shadowRoot;
|
|
91
|
+
if (!root)
|
|
92
|
+
return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
93
|
+
const box = host.getBoundingClientRect();
|
|
94
|
+
const top = box.top + host.clientTop;
|
|
95
|
+
const left = box.left + host.clientLeft;
|
|
96
|
+
const bottom = top + host.clientHeight;
|
|
97
|
+
const right = left + host.clientWidth;
|
|
98
|
+
let insetTop = 0;
|
|
99
|
+
for (const el of root.querySelectorAll('apex-grid-header-row, [part~="pinned-top"]')) {
|
|
100
|
+
insetTop = Math.max(insetTop, el.getBoundingClientRect().bottom - top);
|
|
101
|
+
}
|
|
102
|
+
let insetBottom = 0;
|
|
103
|
+
for (const el of root.querySelectorAll('apex-grid-paginator, [part~="pinned-bottom"]')) {
|
|
104
|
+
insetBottom = Math.max(insetBottom, bottom - el.getBoundingClientRect().top);
|
|
105
|
+
}
|
|
106
|
+
let insetLeft = 0;
|
|
107
|
+
let insetRight = 0;
|
|
108
|
+
const headerRow = root.querySelector('apex-grid-header-row');
|
|
109
|
+
const pinned = headerRow?.shadowRoot?.querySelectorAll('[data-pinned="start"], [data-pinned="end"]');
|
|
110
|
+
if (pinned?.length) {
|
|
111
|
+
const rtl = isRTL(host);
|
|
112
|
+
for (const el of pinned) {
|
|
113
|
+
const rect = el.getBoundingClientRect();
|
|
114
|
+
const atLeft = (el.getAttribute('data-pinned') === 'start') !== rtl;
|
|
115
|
+
if (atLeft)
|
|
116
|
+
insetLeft = Math.max(insetLeft, rect.right - left);
|
|
117
|
+
else
|
|
118
|
+
insetRight = Math.max(insetRight, right - rect.left);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const clamp = (inset) => Math.max(0, Math.round(inset));
|
|
122
|
+
return {
|
|
123
|
+
top: clamp(insetTop),
|
|
124
|
+
right: clamp(insetRight),
|
|
125
|
+
bottom: clamp(insetBottom),
|
|
126
|
+
left: clamp(insetLeft),
|
|
127
|
+
};
|
|
45
128
|
}
|
|
46
|
-
focusActiveCell() {
|
|
47
|
-
const node = this.
|
|
48
|
-
if (
|
|
129
|
+
focusActiveCell(reveal = false) {
|
|
130
|
+
const node = this._active;
|
|
131
|
+
if (node === SENTINEL_NODE)
|
|
49
132
|
return;
|
|
50
133
|
void (async () => {
|
|
51
134
|
await this.host.updateComplete;
|
|
52
|
-
let cell = this.
|
|
135
|
+
let cell = this.findCell(node);
|
|
136
|
+
if (!cell) {
|
|
137
|
+
await this.virtualizer.settled();
|
|
138
|
+
cell = this.findCell(node);
|
|
139
|
+
}
|
|
53
140
|
if (!cell) {
|
|
54
141
|
await new Promise(requestAnimationFrame);
|
|
55
|
-
cell = this.
|
|
142
|
+
cell = this.findCell(node);
|
|
56
143
|
}
|
|
57
|
-
cell
|
|
144
|
+
if (!cell || this._active !== node)
|
|
145
|
+
return;
|
|
146
|
+
if (reveal)
|
|
147
|
+
this.scrollToCell(node);
|
|
148
|
+
cell.focus({ preventScroll: true });
|
|
58
149
|
})();
|
|
59
150
|
}
|
|
60
151
|
get active() {
|
|
@@ -62,8 +153,6 @@ export class NavigationController {
|
|
|
62
153
|
}
|
|
63
154
|
set active(node) {
|
|
64
155
|
this._active = node;
|
|
65
|
-
this.state.set('previous', this._active);
|
|
66
|
-
this.state.set('current', node);
|
|
67
156
|
this.host.requestUpdate();
|
|
68
157
|
}
|
|
69
158
|
constructor(host) {
|
|
@@ -79,10 +168,46 @@ export class NavigationController {
|
|
|
79
168
|
PageDown: this.pageDown,
|
|
80
169
|
' ': this.toggleSelection,
|
|
81
170
|
}));
|
|
82
|
-
this.state = NAVIGATION_STATE;
|
|
83
171
|
this._active = SENTINEL_NODE;
|
|
172
|
+
this.#revealing = null;
|
|
84
173
|
this.host.addController(this);
|
|
85
174
|
}
|
|
175
|
+
hostUpdate() {
|
|
176
|
+
const node = this._active;
|
|
177
|
+
if (node === SENTINEL_NODE)
|
|
178
|
+
return;
|
|
179
|
+
const count = this.host.pageItems.length;
|
|
180
|
+
if (!count || !this.visibleColumns.length) {
|
|
181
|
+
this._active = SENTINEL_NODE;
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const row = Math.min(node.row, count - 1);
|
|
185
|
+
const column = this.#nearestVisibleColumn(node.column);
|
|
186
|
+
if (row !== node.row || column !== node.column) {
|
|
187
|
+
this._active = { column, row };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
#nearestVisibleColumn(key) {
|
|
191
|
+
const columns = this.columns;
|
|
192
|
+
const index = columns.findIndex((column) => column.key === key);
|
|
193
|
+
if (index < 0)
|
|
194
|
+
return this.firstColumn;
|
|
195
|
+
for (let distance = 0; distance < columns.length; distance++) {
|
|
196
|
+
for (const at of [index + distance, index - distance]) {
|
|
197
|
+
const column = columns[at];
|
|
198
|
+
if (column && !column.hidden)
|
|
199
|
+
return column.key;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return this.firstColumn;
|
|
203
|
+
}
|
|
204
|
+
isPresentedRow(index) {
|
|
205
|
+
const data = this.host.pageItems[index];
|
|
206
|
+
if (!data)
|
|
207
|
+
return false;
|
|
208
|
+
const state = this.host.stateController;
|
|
209
|
+
return state.presentRow(data, { columns: this.host.columns, rowIndex: index }) !== null;
|
|
210
|
+
}
|
|
86
211
|
home() {
|
|
87
212
|
this.active = Object.assign(this.nextNode, { column: this.firstColumn });
|
|
88
213
|
this.scrollToCell(this.active);
|
|
@@ -94,69 +219,75 @@ export class NavigationController {
|
|
|
94
219
|
}
|
|
95
220
|
ctrlHome() {
|
|
96
221
|
this.active = { column: this.firstColumn, row: 0 };
|
|
97
|
-
this.
|
|
98
|
-
this.scrollToCell(this.active);
|
|
222
|
+
this.scrollToRow(0);
|
|
99
223
|
}
|
|
100
224
|
ctrlEnd() {
|
|
101
|
-
const row =
|
|
225
|
+
const row = this.host.pageItems.length - 1;
|
|
102
226
|
const last = this.visibleColumns.at(-1)?.key ?? this.firstColumn;
|
|
103
227
|
this.active = { column: last, row };
|
|
104
|
-
this.
|
|
105
|
-
this.scrollToCell(this.active);
|
|
228
|
+
this.scrollToRow(row);
|
|
106
229
|
}
|
|
107
230
|
pageStep() {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
231
|
+
const host = this.host;
|
|
232
|
+
let rowHeight = 0;
|
|
233
|
+
for (const row of this.virtualizer.querySelectorAll(GRID_ROW_TAG)) {
|
|
234
|
+
const height = row.offsetHeight;
|
|
235
|
+
if (height > 0 && (!rowHeight || height < rowHeight))
|
|
236
|
+
rowHeight = height;
|
|
237
|
+
}
|
|
238
|
+
const { top, bottom } = this.scrollInsets();
|
|
239
|
+
const viewport = host.clientHeight - top - bottom;
|
|
240
|
+
if (!rowHeight || viewport <= 0)
|
|
112
241
|
return 10;
|
|
113
|
-
|
|
114
|
-
return rows > 0 ? rows : 10;
|
|
242
|
+
return Math.max(1, Math.floor(viewport / rowHeight) - 1);
|
|
115
243
|
}
|
|
116
244
|
pageUp() {
|
|
117
245
|
const next = this.nextNode;
|
|
118
246
|
this.active = Object.assign(next, { row: Math.max(0, next.row - this.pageStep()) });
|
|
119
|
-
this.
|
|
247
|
+
this.scrollToRow(this.active.row);
|
|
120
248
|
}
|
|
121
249
|
pageDown() {
|
|
122
250
|
const next = this.nextNode;
|
|
123
251
|
this.active = Object.assign(next, {
|
|
124
252
|
row: Math.min(this.host.pageItems.length - 1, next.row + this.pageStep()),
|
|
125
253
|
});
|
|
126
|
-
this.
|
|
254
|
+
this.scrollToRow(this.active.row);
|
|
127
255
|
}
|
|
128
256
|
arrowDown() {
|
|
129
257
|
const next = this.nextNode;
|
|
130
258
|
this.active = Object.assign(next, {
|
|
131
259
|
row: Math.min(next.row + 1, this.host.pageItems.length - 1),
|
|
132
260
|
});
|
|
133
|
-
this.
|
|
261
|
+
this.scrollToRow(next.row);
|
|
134
262
|
}
|
|
135
263
|
arrowUp() {
|
|
136
264
|
const next = this.nextNode;
|
|
137
265
|
this.active = Object.assign(next, { row: Math.max(0, next.row - 1) });
|
|
138
|
-
this.
|
|
266
|
+
this.scrollToRow(next.row);
|
|
139
267
|
}
|
|
140
268
|
arrowLeft() {
|
|
141
|
-
|
|
142
|
-
this.active = Object.assign(next, { column: this.getPreviousColumn(next.column) });
|
|
143
|
-
this.scrollToCell(this.active);
|
|
269
|
+
this.#moveInline('ArrowLeft');
|
|
144
270
|
}
|
|
145
271
|
arrowRight() {
|
|
272
|
+
this.#moveInline('ArrowRight');
|
|
273
|
+
}
|
|
274
|
+
#moveInline(key) {
|
|
146
275
|
const next = this.nextNode;
|
|
147
|
-
|
|
276
|
+
const column = this.towardsInlineEnd(key)
|
|
277
|
+
? this.getNextColumn(next.column)
|
|
278
|
+
: this.getPreviousColumn(next.column);
|
|
279
|
+
this.active = Object.assign(next, { column });
|
|
148
280
|
this.scrollToCell(this.active);
|
|
149
281
|
}
|
|
150
282
|
toggleSelection() {
|
|
151
|
-
const data = this.host.pageItems[this.
|
|
152
|
-
if (!data)
|
|
283
|
+
const data = this.host.pageItems[this._active.row];
|
|
284
|
+
if (!data || this.isPresentedRow(this._active.row))
|
|
153
285
|
return;
|
|
154
286
|
void this.host.toggleRowSelection(data);
|
|
155
287
|
}
|
|
156
288
|
hostConnected() { }
|
|
157
289
|
hostDisconnected() {
|
|
158
290
|
this.active = SENTINEL_NODE;
|
|
159
|
-
this.state = NAVIGATION_STATE;
|
|
160
291
|
}
|
|
161
292
|
get editing() {
|
|
162
293
|
return this.host.stateController.editing;
|
|
@@ -166,8 +297,7 @@ export class NavigationController {
|
|
|
166
297
|
return false;
|
|
167
298
|
if (event.ctrlKey || event.metaKey || event.altKey)
|
|
168
299
|
return false;
|
|
169
|
-
|
|
170
|
-
if (!node || node === SENTINEL_NODE)
|
|
300
|
+
if (this._active === SENTINEL_NODE || this.isPresentedRow(this._active.row))
|
|
171
301
|
return false;
|
|
172
302
|
const editing = this.editing;
|
|
173
303
|
if (!editing?.enabled)
|
|
@@ -194,7 +324,7 @@ export class NavigationController {
|
|
|
194
324
|
const next = reorder.moveGrabbed(event.key === 'ArrowDown' ? 1 : -1);
|
|
195
325
|
if (next >= 0) {
|
|
196
326
|
this.active = Object.assign(this.nextNode, { row: next });
|
|
197
|
-
this.
|
|
327
|
+
this.scrollToRow(next);
|
|
198
328
|
}
|
|
199
329
|
return true;
|
|
200
330
|
}
|
|
@@ -211,7 +341,7 @@ export class NavigationController {
|
|
|
211
341
|
return false;
|
|
212
342
|
}
|
|
213
343
|
}
|
|
214
|
-
if (event.key === ' ' && this.active.row >= 0) {
|
|
344
|
+
if (event.key === ' ' && this.active.row >= 0 && !this.isPresentedRow(this.active.row)) {
|
|
215
345
|
event.preventDefault();
|
|
216
346
|
return reorder.grab(this.active.row);
|
|
217
347
|
}
|
|
@@ -234,18 +364,33 @@ export class NavigationController {
|
|
|
234
364
|
}
|
|
235
365
|
if (event.key === 'Home' || event.key === 'End') {
|
|
236
366
|
event.preventDefault();
|
|
367
|
+
if (!this.navigable)
|
|
368
|
+
return;
|
|
237
369
|
if (event.key === 'Home') {
|
|
238
370
|
this.ctrlHome();
|
|
239
371
|
}
|
|
240
372
|
else {
|
|
241
373
|
this.ctrlEnd();
|
|
242
374
|
}
|
|
243
|
-
this.focusActiveCell();
|
|
375
|
+
this.focusActiveCell(true);
|
|
244
376
|
return;
|
|
245
377
|
}
|
|
246
378
|
}
|
|
247
379
|
if (this.handleEditKey(event))
|
|
248
380
|
return;
|
|
381
|
+
if (RELATIVE_KEYS.has(event.key)) {
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
if (!this.navigable)
|
|
384
|
+
return;
|
|
385
|
+
if (this._active === SENTINEL_NODE) {
|
|
386
|
+
this.active = { column: this.firstColumn, row: 0 };
|
|
387
|
+
if (event.key !== 'End' && event.key !== 'PageDown') {
|
|
388
|
+
this.scrollToRow(0);
|
|
389
|
+
this.focusActiveCell(true);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
249
394
|
if (this.handlers.has(event.key)) {
|
|
250
395
|
event.preventDefault();
|
|
251
396
|
this.handlers.get(event.key).call(this);
|