apex-grid 3.1.0 → 3.3.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 +101 -0
- package/README.md +201 -53
- package/components/cell.d.ts +14 -1
- package/components/cell.js +48 -6
- package/components/cell.js.map +1 -1
- package/components/filter-row.d.ts +3 -6
- package/components/filter-row.js +79 -109
- package/components/filter-row.js.map +1 -1
- package/components/grid.d.ts +416 -70
- package/components/grid.js +398 -11
- package/components/grid.js.map +1 -1
- package/components/group-header-row.d.ts +34 -0
- package/components/group-header-row.js +125 -0
- package/components/group-header-row.js.map +1 -0
- package/components/header-row.d.ts +13 -2
- package/components/header-row.js +33 -18
- package/components/header-row.js.map +1 -1
- package/components/header.d.ts +11 -0
- package/components/header.js +136 -6
- package/components/header.js.map +1 -1
- package/components/paginator.d.ts +6 -1
- package/components/paginator.js +17 -11
- package/components/paginator.js.map +1 -1
- package/components/row.d.ts +28 -0
- package/components/row.js +100 -3
- package/components/row.js.map +1 -1
- package/components/toolbar.d.ts +8 -1
- package/components/toolbar.js +25 -7
- package/components/toolbar.js.map +1 -1
- package/controllers/data-operation.js +1 -0
- package/controllers/data-operation.js.map +1 -1
- package/controllers/dom.d.ts +24 -6
- package/controllers/dom.js +62 -5
- package/controllers/dom.js.map +1 -1
- package/controllers/editing.d.ts +68 -1
- package/controllers/editing.js +129 -44
- package/controllers/editing.js.map +1 -1
- package/controllers/filter.d.ts +1 -1
- package/controllers/filter.js +16 -5
- package/controllers/filter.js.map +1 -1
- package/controllers/history.d.ts +86 -0
- package/controllers/history.js +118 -0
- package/controllers/history.js.map +1 -0
- package/controllers/navigation.d.ts +15 -0
- package/controllers/navigation.js +53 -0
- package/controllers/navigation.js.map +1 -1
- package/controllers/reorder.js +3 -1
- package/controllers/reorder.js.map +1 -1
- package/controllers/row-pin.d.ts +59 -0
- package/controllers/row-pin.js +75 -0
- package/controllers/row-pin.js.map +1 -0
- package/controllers/row-reorder.d.ts +161 -0
- package/controllers/row-reorder.js +332 -0
- package/controllers/row-reorder.js.map +1 -0
- package/controllers/sort.d.ts +1 -1
- package/controllers/sort.js +11 -3
- package/controllers/sort.js.map +1 -1
- package/controllers/state.d.ts +33 -0
- package/controllers/state.js +37 -2
- package/controllers/state.js.map +1 -1
- package/controllers/tree.d.ts +8 -0
- package/controllers/tree.js +0 -0
- package/controllers/tree.js.map +1 -1
- package/custom-elements.json +4415 -875
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/highlight.css +28 -0
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/functions/custom.html +3 -0
- package/docs/functions/interpolate.html +4 -0
- package/docs/functions/localize.html +8 -0
- package/docs/functions/max.html +3 -0
- package/docs/functions/min.html +3 -0
- package/docs/functions/pattern.html +3 -0
- package/docs/functions/required.html +3 -0
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +185 -55
- package/docs/interfaces/ApexCellValidationFailedEvent.html +13 -0
- package/docs/interfaces/ApexCellValueChangedEvent.html +1 -1
- package/docs/interfaces/ApexCellValueChangingEvent.html +2 -2
- package/docs/interfaces/ApexColumnGroupContext.html +6 -0
- 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 +37 -9
- package/docs/interfaces/ApexHistoryChangedEvent.html +6 -0
- package/docs/interfaces/ApexPageChangedEvent.html +1 -1
- package/docs/interfaces/ApexRowMovingEvent.html +9 -0
- package/docs/interfaces/ApexRowPinningEvent.html +8 -0
- package/docs/interfaces/ApexStateChangedEvent.html +4 -0
- package/docs/interfaces/BaseApexCellContext.html +1 -1
- package/docs/interfaces/BaseApexEditorContext.html +1 -1
- package/docs/interfaces/BaseColumnConfiguration.html +42 -17
- package/docs/interfaces/CSVExportOptions.html +2 -2
- package/docs/interfaces/ColumnGroupConfiguration.html +12 -0
- package/docs/interfaces/ColumnLayoutState.html +2 -0
- package/docs/interfaces/ColumnSchema.html +22 -0
- package/docs/interfaces/DataPipelineConfiguration.html +2 -2
- package/docs/interfaces/EditingHistoryConfiguration.html +10 -0
- package/docs/interfaces/ExportFormat.html +2 -2
- package/docs/interfaces/ExportOptions.html +1 -1
- package/docs/interfaces/FilterStateSnapshot.html +4 -0
- package/docs/interfaces/GetStateOptions.html +4 -0
- package/docs/interfaces/GridCapabilities.html +8 -0
- package/docs/interfaces/GridEditingConfiguration.html +4 -2
- package/docs/interfaces/GridRowPinningConfiguration.html +5 -0
- package/docs/interfaces/GridRowReorderingConfiguration.html +22 -0
- package/docs/interfaces/GridSchema.html +19 -0
- package/docs/interfaces/GridState.html +41 -0
- package/docs/interfaces/GridTreeConfiguration.html +2 -2
- package/docs/interfaces/PaginationConfiguration.html +1 -1
- package/docs/interfaces/PaginationState.html +1 -1
- package/docs/interfaces/SetStateOptions.html +9 -0
- package/docs/interfaces/SetStateResult.html +11 -0
- package/docs/interfaces/SortStateSnapshot.html +2 -0
- package/docs/interfaces/ValidatorContext.html +8 -0
- package/docs/media/_tokens.scss +131 -44
- package/docs/media/package.json +1 -1
- package/docs/media/state-persistence.html +189 -0
- package/docs/modules.html +1 -1
- package/docs/types/DataPipelineParams.html +2 -2
- package/docs/types/EditMode.html +2 -2
- package/docs/types/GridLocaleKey.html +3 -0
- package/docs/types/GridLocaleText.html +5 -0
- package/docs/types/LocaleParams.html +2 -0
- package/docs/types/PaginationMode.html +1 -1
- package/docs/types/RowDropPosition.html +2 -0
- package/docs/types/RowPinPosition.html +2 -0
- package/docs/types/RowRef.html +3 -0
- package/docs/types/Validator.html +4 -0
- package/docs/variables/EN_LOCALE.html +9 -0
- package/docs/variables/esLocale.html +8 -0
- package/i18n/en.d.ts +151 -0
- package/i18n/en.js +125 -0
- package/i18n/en.js.map +1 -0
- package/i18n/es.d.ts +18 -0
- package/i18n/es.js +125 -0
- package/i18n/es.js.map +1 -0
- package/i18n/index.d.ts +3 -0
- package/i18n/index.js +4 -0
- package/i18n/index.js.map +1 -0
- package/i18n/localize.d.ts +23 -0
- package/i18n/localize.js +15 -0
- package/i18n/localize.js.map +1 -0
- package/index.d.ts +8 -2
- package/index.js +2 -0
- package/index.js.map +1 -1
- package/internal/feature-module.d.ts +46 -0
- package/internal/feature-module.js +11 -0
- package/internal/feature-module.js.map +1 -1
- package/internal/flip.js +1 -1
- package/internal/flip.js.map +1 -1
- package/internal/icons.d.ts +9 -7
- package/internal/icons.js +75 -35
- package/internal/icons.js.map +1 -1
- package/internal/index.d.ts +5 -3
- package/internal/index.js +2 -1
- package/internal/index.js.map +1 -1
- package/internal/state-schema.d.ts +85 -0
- package/internal/state-schema.js +27 -0
- package/internal/state-schema.js.map +1 -0
- package/internal/state-snapshot.d.ts +152 -0
- package/internal/state-snapshot.js +115 -0
- package/internal/state-snapshot.js.map +1 -0
- package/internal/tags.d.ts +1 -0
- package/internal/tags.js +1 -0
- package/internal/tags.js.map +1 -1
- package/internal/types.d.ts +162 -2
- package/internal/types.js.map +1 -1
- package/internal/utils.d.ts +12 -0
- package/internal/utils.js +17 -0
- package/internal/utils.js.map +1 -1
- package/internal/validators.d.ts +31 -0
- package/internal/validators.js +44 -0
- package/internal/validators.js.map +1 -0
- package/package.json +1 -1
- 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-row/body-row.css.js +1 -1
- package/styles/body-row/body-row.css.js.map +1 -1
- 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.d.ts +1 -0
- package/styles/group-header-row/group-header-row.css.js +3 -0
- package/styles/group-header-row/group-header-row.css.js.map +1 -0
- 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/docs/classes/ApexGrid.html +0 -393
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/controllers/filter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAG5D,MAAM,OAAO,gBAAgB;IAC3B,YAAsB,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;QAIhC,UAAK,GAAmB,IAAI,WAAW,EAAE,CAAC;QAH/C,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAID,IAAW,SAAS;QAElB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;IAC7B,CAAC;IAED,IAAI,YAAY;QAEd,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;IACnC,CAAC;IAED,mBAAmB,CAAC,UAA+B,EAAE,IAAiC;QACpF,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE;YACtC,MAAM,EAAE;gBACN,GAAG,EAAE,UAAU,CAAC,GAAG;gBACnB,WAAW,EAAE,CAAC,UAAU,CAAC;gBACzB,IAAI;aACL;YACD,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IAED,kBAAkB,CAAC,MAA6B;QAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,CAAC,UAAuD;QAC7D,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAID,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAEM,aAAa,KAAI,CAAC;IAElB,UAAU;QACf,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,CAAC;IAClC,CAAC;IAEM,GAAG,CAAC,GAAY;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAEM,KAAK,CAAC,GAAa;QACxB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACpD,CAAC;IAEM,eAAe,CAAC,MAA+B;
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/controllers/filter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type { ApexFilteredEvent } from '../components/grid.js';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { ColumnConfiguration, GridHost, Keys } from '../internal/types.js';\nimport { asArray, getFilterOperandsFor } from '../internal/utils.js';\nimport { FilterState } from '../operations/filter/state.js';\nimport type { FilterExpression } from '../operations/filter/types.js';\n\nexport class FilterController<T extends object> implements ReactiveController {\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public state: FilterState<T> = new FilterState();\n\n public get filterRow() {\n // @ts-expect-error - protected access\n return this.host.filterRow;\n }\n\n get #virtualizer() {\n // @ts-expect-error - protected access\n return this.host.scrollContainer;\n }\n\n #emitFilteringEvent(expression: FilterExpression<T>, type: 'add' | 'modify' | 'remove') {\n return this.host.emitEvent('filtering', {\n detail: {\n key: expression.key,\n expressions: [expression],\n type,\n },\n cancelable: true,\n });\n }\n\n #emitFilteredEvent(detail?: ApexFilteredEvent<T>) {\n return this.host.emitEvent('filtered', { detail });\n }\n\n #filter(expression: FilterExpression<T> | FilterExpression<T>[]) {\n for (const expr of asArray(expression)) {\n this.state.set(expr);\n }\n\n // HACK: In the case where the scrollTop is a large and amount and a big chunk of data is filtered out\n // HACK: the virtualizer can't recalculate its scroll position correctly. Thus, we reset the scrollTop state.\n this.#virtualizer?.scrollTo({ top: 0 });\n this.host.requestUpdate(PIPELINE);\n }\n\n public hostConnected() {}\n\n public hostUpdate(): void {\n this.filterRow?.requestUpdate();\n }\n\n public get(key: Keys<T>) {\n return this.state.get(key);\n }\n\n public reset(key?: Keys<T>) {\n key ? this.state.delete(key) : this.state.clear();\n }\n\n public setActiveColumn(column?: ColumnConfiguration<T>, triggerRect?: DOMRect) {\n const filterRow = this.filterRow;\n if (!filterRow) return;\n\n if (!column?.filter) {\n filterRow.active = false;\n return;\n }\n\n // Toggle: clicking the same column's filter while the panel is open closes it.\n if (filterRow.active && filterRow.column?.key === column.key) {\n filterRow.active = false;\n return;\n }\n\n filterRow.triggerRect = triggerRect ?? null;\n filterRow.column = column;\n filterRow.expression = this.getDefaultExpression(column);\n filterRow.active = true;\n this.host.requestUpdate();\n }\n\n public getDefaultExpression(column: ColumnConfiguration<T>) {\n const caseSensitive =\n typeof column.filter === 'boolean' ? false : Boolean(column.filter?.caseSensitive);\n const operands = getFilterOperandsFor(column);\n const keys = Object.keys(operands) as Keys<typeof operands>[];\n\n // XXX: Types\n return {\n key: column.key,\n condition: operands[keys[0]],\n caseSensitive,\n } as unknown as FilterExpression<T>;\n }\n\n public async removeAllExpressions(key: Keys<T>) {\n const state = this.get(key)?.all ?? [];\n\n if (\n !this.host.emitEvent('filtering', {\n detail: {\n key,\n expressions: state,\n type: 'remove',\n },\n cancelable: true,\n })\n ) {\n return;\n }\n\n this.reset(key);\n this.#filter([]);\n\n await this.host.updateComplete;\n this.#emitFilteredEvent({ key, state: this.get(key)?.all ?? [] });\n }\n\n public async removeExpression(expression: FilterExpression<T>) {\n const state = this.get(expression.key);\n\n if (!this.#emitFilteringEvent(expression, 'remove')) {\n return;\n }\n\n state?.remove(expression);\n\n if (state?.empty) {\n this.reset(state.key);\n }\n\n this.#filter([]);\n\n await this.host.updateComplete;\n this.#emitFilteredEvent({ key: expression.key, state: state?.all ?? [] });\n }\n\n public async filterWithEvent(expression: FilterExpression<T>, type: 'add' | 'modify' | 'remove') {\n if (!this.#emitFilteringEvent(expression, type)) {\n return;\n }\n\n this.#filter(expression);\n\n await this.host.updateComplete;\n this.#emitFilteredEvent({ key: expression.key, state: this.get(expression.key)?.all ?? [] });\n }\n\n public filter(expression: FilterExpression<T> | FilterExpression<T>[]) {\n this.#filter(\n asArray(expression).map((expr) =>\n Object.assign(this.getDefaultExpression(this.host.getColumn(expr.key)!), expr)\n )\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ReactiveController } from 'lit';
|
|
2
|
+
import type { GridHost, Keys } from '../internal/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A single recorded cell mutation: the record reference, its column, and the
|
|
5
|
+
* before / after values. `rowIndex` is the view-relative index at record time
|
|
6
|
+
* and is used only for the replayed `cellValueChanged` event (the live record
|
|
7
|
+
* reference is authoritative).
|
|
8
|
+
*/
|
|
9
|
+
export interface CellChange<T extends object> {
|
|
10
|
+
record: T;
|
|
11
|
+
key: Keys<T>;
|
|
12
|
+
rowIndex: number;
|
|
13
|
+
oldValue: unknown;
|
|
14
|
+
newValue: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A reversible unit of work on the history stacks.
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* `apply` re-performs the change (redo); `revert` undoes it. Cell-data edits use
|
|
21
|
+
* {@link CellEditCommand}; the interface is deliberately open so later command
|
|
22
|
+
* kinds (e.g. view-state) can join the same stacks.
|
|
23
|
+
*/
|
|
24
|
+
export interface EditCommand {
|
|
25
|
+
apply(): void;
|
|
26
|
+
revert(): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* One or more cell mutations that undo / redo as a single step. A single-cell
|
|
30
|
+
* edit holds one change; a row-mode commit or a bulk paste / fill holds many so
|
|
31
|
+
* one Ctrl+Z reverts the whole batch.
|
|
32
|
+
*/
|
|
33
|
+
export declare class CellEditCommand<T extends object> implements EditCommand {
|
|
34
|
+
#private;
|
|
35
|
+
private host;
|
|
36
|
+
readonly changes: ReadonlyArray<CellChange<T>>;
|
|
37
|
+
constructor(host: GridHost<T>, changes: ReadonlyArray<CellChange<T>>);
|
|
38
|
+
apply(): void;
|
|
39
|
+
revert(): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reactive controller owning the undo / redo stacks for cell-data edits.
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* Recording is driven from the {@link EditingController.applyCellEdit} choke
|
|
46
|
+
* point: every successful write calls {@link record}. Single edits push one
|
|
47
|
+
* command; {@link beginBatch} / {@link endBatch} coalesce a row-mode commit or a
|
|
48
|
+
* bulk paste / fill into one command. Disabled unless
|
|
49
|
+
* `editing.history.enabled` is set, so the community grid pays nothing.
|
|
50
|
+
*/
|
|
51
|
+
export declare class HistoryController<T extends object> implements ReactiveController {
|
|
52
|
+
#private;
|
|
53
|
+
protected host: GridHost<T>;
|
|
54
|
+
constructor(host: GridHost<T>);
|
|
55
|
+
hostConnected(): void;
|
|
56
|
+
/** Whether undo / redo tracking is enabled. */
|
|
57
|
+
get enabled(): boolean;
|
|
58
|
+
/** Maximum number of commands retained on the undo stack. */
|
|
59
|
+
get stackSize(): number;
|
|
60
|
+
/** Whether there is at least one command to undo. */
|
|
61
|
+
get canUndo(): boolean;
|
|
62
|
+
/** Whether there is at least one command to redo. */
|
|
63
|
+
get canRedo(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Records a single cell mutation. Routed to the open batch when one is active,
|
|
66
|
+
* otherwise pushed immediately as a standalone command. No-op when history is
|
|
67
|
+
* disabled.
|
|
68
|
+
*/
|
|
69
|
+
record(change: CellChange<T>): void;
|
|
70
|
+
/**
|
|
71
|
+
* Opens a batch so subsequent {@link record} calls coalesce into one command.
|
|
72
|
+
* Nestable calls are flattened (the outermost {@link endBatch} commits).
|
|
73
|
+
*/
|
|
74
|
+
beginBatch(): void;
|
|
75
|
+
/**
|
|
76
|
+
* Closes the open batch, pushing it as a single command when it captured any
|
|
77
|
+
* changes.
|
|
78
|
+
*/
|
|
79
|
+
endBatch(): void;
|
|
80
|
+
/** Reverts the most recent command. No-op when the undo stack is empty. */
|
|
81
|
+
undo(): void;
|
|
82
|
+
/** Re-applies the most recently undone command. No-op when nothing to redo. */
|
|
83
|
+
redo(): void;
|
|
84
|
+
/** Clears both stacks (e.g. after a data reset). */
|
|
85
|
+
clear(): void;
|
|
86
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { PIPELINE } from '../internal/constants.js';
|
|
2
|
+
const DEFAULT_STACK_SIZE = 100;
|
|
3
|
+
export class CellEditCommand {
|
|
4
|
+
constructor(host, changes) {
|
|
5
|
+
this.host = host;
|
|
6
|
+
this.changes = changes;
|
|
7
|
+
}
|
|
8
|
+
apply() {
|
|
9
|
+
for (const change of this.changes) {
|
|
10
|
+
this.#write(change, change.newValue);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
revert() {
|
|
14
|
+
for (let i = this.changes.length - 1; i >= 0; i -= 1) {
|
|
15
|
+
this.#write(this.changes[i], this.changes[i].oldValue);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
#write(change, value) {
|
|
19
|
+
change.record[change.key] = value;
|
|
20
|
+
const liveIndex = this.host.pageItems.indexOf(change.record);
|
|
21
|
+
this.host.emitEvent('cellValueChanged', {
|
|
22
|
+
detail: {
|
|
23
|
+
key: change.key,
|
|
24
|
+
rowIndex: liveIndex >= 0 ? liveIndex : change.rowIndex,
|
|
25
|
+
data: change.record,
|
|
26
|
+
value,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class HistoryController {
|
|
32
|
+
#undo = [];
|
|
33
|
+
#redo = [];
|
|
34
|
+
#batch = null;
|
|
35
|
+
constructor(host) {
|
|
36
|
+
this.host = host;
|
|
37
|
+
this.host.addController(this);
|
|
38
|
+
}
|
|
39
|
+
hostConnected() { }
|
|
40
|
+
get enabled() {
|
|
41
|
+
return Boolean(this.host.editing?.history?.enabled);
|
|
42
|
+
}
|
|
43
|
+
get stackSize() {
|
|
44
|
+
const size = this.host.editing?.history?.stackSize;
|
|
45
|
+
return typeof size === 'number' && size > 0 ? size : DEFAULT_STACK_SIZE;
|
|
46
|
+
}
|
|
47
|
+
get canUndo() {
|
|
48
|
+
return this.#undo.length > 0;
|
|
49
|
+
}
|
|
50
|
+
get canRedo() {
|
|
51
|
+
return this.#redo.length > 0;
|
|
52
|
+
}
|
|
53
|
+
record(change) {
|
|
54
|
+
if (!this.enabled)
|
|
55
|
+
return;
|
|
56
|
+
if (this.#batch) {
|
|
57
|
+
this.#batch.push(change);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.#push(new CellEditCommand(this.host, [change]));
|
|
61
|
+
}
|
|
62
|
+
beginBatch() {
|
|
63
|
+
if (!this.enabled)
|
|
64
|
+
return;
|
|
65
|
+
this.#batch ??= [];
|
|
66
|
+
}
|
|
67
|
+
endBatch() {
|
|
68
|
+
const batch = this.#batch;
|
|
69
|
+
this.#batch = null;
|
|
70
|
+
if (batch && batch.length > 0) {
|
|
71
|
+
this.#push(new CellEditCommand(this.host, batch));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
undo() {
|
|
75
|
+
const command = this.#undo.pop();
|
|
76
|
+
if (!command)
|
|
77
|
+
return;
|
|
78
|
+
command.revert();
|
|
79
|
+
this.#redo.push(command);
|
|
80
|
+
this.#afterReplay(command, 'Undo');
|
|
81
|
+
}
|
|
82
|
+
redo() {
|
|
83
|
+
const command = this.#redo.pop();
|
|
84
|
+
if (!command)
|
|
85
|
+
return;
|
|
86
|
+
command.apply();
|
|
87
|
+
this.#undo.push(command);
|
|
88
|
+
this.#afterReplay(command, 'Redo');
|
|
89
|
+
}
|
|
90
|
+
clear() {
|
|
91
|
+
if (this.#undo.length === 0 && this.#redo.length === 0)
|
|
92
|
+
return;
|
|
93
|
+
this.#undo = [];
|
|
94
|
+
this.#redo = [];
|
|
95
|
+
this.#batch = null;
|
|
96
|
+
this.#emitChanged();
|
|
97
|
+
}
|
|
98
|
+
#push(command) {
|
|
99
|
+
this.#undo.push(command);
|
|
100
|
+
this.#redo = [];
|
|
101
|
+
while (this.#undo.length > this.stackSize) {
|
|
102
|
+
this.#undo.shift();
|
|
103
|
+
}
|
|
104
|
+
this.#emitChanged();
|
|
105
|
+
}
|
|
106
|
+
#afterReplay(command, label) {
|
|
107
|
+
this.host.requestUpdate(PIPELINE);
|
|
108
|
+
const count = command instanceof CellEditCommand ? command.changes.length : 1;
|
|
109
|
+
this.host.announce(`${label} ${count} cell ${count === 1 ? 'change' : 'changes'}`);
|
|
110
|
+
this.#emitChanged();
|
|
111
|
+
}
|
|
112
|
+
#emitChanged() {
|
|
113
|
+
this.host.emitEvent('historyChanged', {
|
|
114
|
+
detail: { canUndo: this.canUndo, canRedo: this.canRedo },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +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, label: string): void {\n this.host.requestUpdate(PIPELINE);\n const count = command instanceof CellEditCommand ? command.changes.length : 1;\n this.host.announce(`${label} ${count} cell ${count === 1 ? 'change' : 'changes'}`);\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"]}
|
|
@@ -34,5 +34,20 @@ export declare class NavigationController<T extends object> implements ReactiveC
|
|
|
34
34
|
protected toggleSelection(): void;
|
|
35
35
|
hostConnected(): void;
|
|
36
36
|
hostDisconnected(): void;
|
|
37
|
+
protected get rowReorder(): {
|
|
38
|
+
enabled: boolean;
|
|
39
|
+
isGrabbing: boolean;
|
|
40
|
+
grab(rowIndex: number): boolean;
|
|
41
|
+
moveGrabbed(direction: -1 | 1): number;
|
|
42
|
+
drop(): void;
|
|
43
|
+
cancelGrab(): void;
|
|
44
|
+
} | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Keyboard row reorder: Space grabs the active row, arrows move it, Space /
|
|
47
|
+
* Enter drops, Escape cancels. Returns `true` when the event was consumed.
|
|
48
|
+
* Only active when `rowReordering.enabled` (so it never shadows Space-to-select
|
|
49
|
+
* on grids without reordering).
|
|
50
|
+
*/
|
|
51
|
+
protected handleReorderKey(event: KeyboardEvent): boolean;
|
|
37
52
|
navigate(event: KeyboardEvent): void;
|
|
38
53
|
}
|
|
@@ -104,7 +104,60 @@ export class NavigationController {
|
|
|
104
104
|
this.active = SENTINEL_NODE;
|
|
105
105
|
this.state = NAVIGATION_STATE;
|
|
106
106
|
}
|
|
107
|
+
get rowReorder() {
|
|
108
|
+
return this.host.stateController.rowReorder;
|
|
109
|
+
}
|
|
110
|
+
handleReorderKey(event) {
|
|
111
|
+
const reorder = this.rowReorder;
|
|
112
|
+
if (!reorder?.enabled)
|
|
113
|
+
return false;
|
|
114
|
+
if (reorder.isGrabbing) {
|
|
115
|
+
switch (event.key) {
|
|
116
|
+
case 'ArrowUp':
|
|
117
|
+
case 'ArrowDown': {
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
const next = reorder.moveGrabbed(event.key === 'ArrowDown' ? 1 : -1);
|
|
120
|
+
if (next >= 0) {
|
|
121
|
+
this.active = Object.assign(this.nextNode, { row: next });
|
|
122
|
+
this.virtualizer.element(next)?.scrollIntoView({ block: 'nearest' });
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
case ' ':
|
|
127
|
+
case 'Enter':
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
reorder.drop();
|
|
130
|
+
return true;
|
|
131
|
+
case 'Escape':
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
reorder.cancelGrab();
|
|
134
|
+
return true;
|
|
135
|
+
default:
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (event.key === ' ' && this.active.row >= 0) {
|
|
140
|
+
event.preventDefault();
|
|
141
|
+
return reorder.grab(this.active.row);
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
107
145
|
navigate(event) {
|
|
146
|
+
if (this.handleReorderKey(event))
|
|
147
|
+
return;
|
|
148
|
+
if ((event.ctrlKey || event.metaKey) && !event.altKey) {
|
|
149
|
+
const key = event.key.toLowerCase();
|
|
150
|
+
if (key === 'z' && !event.shiftKey) {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
this.host.undo();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if ((key === 'z' && event.shiftKey) || key === 'y') {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
this.host.redo();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
108
161
|
if (this.handlers.has(event.key)) {
|
|
109
162
|
event.preventDefault();
|
|
110
163
|
this.handlers.get(event.key).call(this);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/controllers/navigation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,OAAO,oBAAoB;IAa/B,IAAc,WAAW;QAEvB,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;IACnC,CAAC;IAKD,IAAc,QAAQ;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;QACxC,OAAO,IAAI,KAAK,aAAa;YAC3B,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE;YACtC,CAAC,CAAE,EAAE,GAAG,IAAI,EAAoB,CAAC;IACrC,CAAC;IAED,IAAc,OAAO;QACnB,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAGxC,CAAC;IACL,CAAC;IAED,IAAc,cAAc;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IAED,IAAc,WAAW;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACxC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAK,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAe,CAAY,CAAC;IAC3E,CAAC;IAES,iBAAiB,CAAC,GAAY;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QAC9D,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3C,CAAC;IAES,aAAa,CAAC,GAAY;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QAC9D,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5D,CAAC;IAES,YAAY,CAAC,IAAmB;QACxC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAC1E,CAAC,GAAG,EAAE,EAAE,CAAE,GAAiC,CAAC,KAAK,KAAK,IAAI,CAAC,GAAG,CAClC,CAAC;QAE/B,IAAI,GAAG,EAAE,CAAC;YACR,GAAG,CAAC,KAAK;iBACN,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC;gBAChD,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAwB,CAAC;IACvC,CAAC;IAED,IAAW,MAAM,CAAC,IAAmB;QACnC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;IAC5B,CAAC;IAED,YAAsB,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;QA9E7B,aAAQ,GAAG,IAAI,GAAG,CAC1B,MAAM,CAAC,OAAO,CAAC;YACb,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,eAAe;SAC1B,CAAC,CACH,CAAC;QAOQ,UAAK,GAAG,gBAAgB,CAAC;QACzB,YAAO,GAAG,aAAa,CAAC;QA6DhC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAES,IAAI;QACZ,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAClF,CAAC;IAES,GAAG;QACX,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAClF,CAAC;IAES,SAAS;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAE3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;YAChC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;SAC5D,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IAES,OAAO;QACf,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IAES,SAAS;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAES,UAAU;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAES,eAAe;QAGvB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAkB,CAAC;QACnE,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,KAAK,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAEM,aAAa,KAAI,CAAC;IAElB,gBAAgB;QACrB,IAAI,CAAC,MAAM,GAAG,aAA8B,CAAC;QAC7C,IAAI,CAAC,KAAK,GAAG,gBAAgB,CAAC;IAChC,CAAC;IAEM,QAAQ,CAAC,KAAoB;QAClC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;CACF","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type ApexGridRow from '../components/row.js';\nimport { NAVIGATION_STATE, SENTINEL_NODE } from '../internal/constants.js';\nimport { GRID_ROW_TAG } from '../internal/tags.js';\nimport type { ActiveNode, GridHost, Keys } from '../internal/types.js';\nimport { getDisplayColumns } from '../internal/utils.js';\n\nexport class NavigationController<T extends object> implements ReactiveController {\n protected handlers = new Map(\n Object.entries({\n ArrowDown: this.arrowDown,\n ArrowUp: this.arrowUp,\n ArrowLeft: this.arrowLeft,\n ArrowRight: this.arrowRight,\n Home: this.home,\n End: this.end,\n ' ': this.toggleSelection,\n })\n );\n\n protected get virtualizer() {\n // @ts-expect-error - Protected member access\n return this.host.scrollContainer;\n }\n\n protected state = NAVIGATION_STATE;\n protected _active = SENTINEL_NODE;\n\n protected get nextNode() {\n const node = this.state.get('current')!;\n return node === SENTINEL_NODE\n ? { column: this.firstColumn, row: 0 }\n : ({ ...node } as ActiveNode<T>);\n }\n\n protected get columns(): Array<{ key: Keys<T>; hidden?: boolean }> {\n return getDisplayColumns(this.host.columns) as unknown as Array<{\n key: Keys<T>;\n hidden?: boolean;\n }>;\n }\n\n protected get visibleColumns() {\n return this.columns.filter((column) => !column.hidden);\n }\n\n protected get firstColumn(): Keys<T> {\n const first = this.visibleColumns.at(0);\n return (first?.key ?? (this.host.columns[0]?.key as Keys<T>)) as Keys<T>;\n }\n\n protected getPreviousColumn(key: Keys<T>): Keys<T> {\n const columns = this.visibleColumns;\n const idx = columns.findIndex((column) => column.key === key);\n return columns[Math.max(idx - 1, 0)].key;\n }\n\n protected getNextColumn(key: Keys<T>): Keys<T> {\n const columns = this.visibleColumns;\n const idx = columns.findIndex((column) => column.key === key);\n return columns[Math.min(idx + 1, columns.length - 1)].key;\n }\n\n protected scrollToCell(node: ActiveNode<T>) {\n const row = Array.from(this.virtualizer.querySelectorAll(GRID_ROW_TAG)).find(\n (row) => (row as unknown as ApexGridRow<T>).index === node.row\n ) as unknown as ApexGridRow<T>;\n\n if (row) {\n row.cells\n .find((cell) => cell.column.key === node.column)\n ?.scrollIntoView({ block: 'nearest' });\n }\n }\n\n public get active() {\n return this._active as ActiveNode<T>;\n }\n\n public set active(node: ActiveNode<T>) {\n this._active = node;\n this.state.set('previous', this._active);\n this.state.set('current', node);\n this.host.requestUpdate();\n }\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n protected home() {\n this.active = Object.assign(this.nextNode, { row: 0 });\n this.virtualizer.element(this.active.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected end() {\n this.active = Object.assign(this.nextNode, { row: this.host.pageItems.length - 1 });\n this.virtualizer.element(this.active.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowDown() {\n const next = this.nextNode;\n\n this.active = Object.assign(next, {\n row: Math.min(next.row + 1, this.host.pageItems.length - 1),\n });\n this.virtualizer.element(next.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowUp() {\n const next = this.nextNode;\n this.active = Object.assign(next, { row: Math.max(0, next.row - 1) });\n this.virtualizer.element(next.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowLeft() {\n const next = this.nextNode;\n this.active = Object.assign(next, { column: this.getPreviousColumn(next.column) });\n this.scrollToCell(this.active);\n }\n\n protected arrowRight() {\n const next = this.nextNode;\n this.active = Object.assign(next, { column: this.getNextColumn(next.column) });\n this.scrollToCell(this.active);\n }\n\n protected toggleSelection() {\n // Space on the focused grid toggles selection of the active row.\n // No-op when selection is disabled or there's no active row.\n const data = this.host.pageItems[this.active.row] as T | undefined;\n if (!data) return;\n void this.host.toggleRowSelection(data);\n }\n\n public hostConnected() {}\n\n public hostDisconnected() {\n this.active = SENTINEL_NODE as ActiveNode<T>;\n this.state = NAVIGATION_STATE;\n }\n\n public navigate(event: KeyboardEvent) {\n if (this.handlers.has(event.key)) {\n event.preventDefault();\n this.handlers.get(event.key)!.call(this);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/controllers/navigation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,OAAO,oBAAoB;IAa/B,IAAc,WAAW;QAEvB,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;IACnC,CAAC;IAKD,IAAc,QAAQ;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;QACxC,OAAO,IAAI,KAAK,aAAa;YAC3B,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE;YACtC,CAAC,CAAE,EAAE,GAAG,IAAI,EAAoB,CAAC;IACrC,CAAC;IAED,IAAc,OAAO;QACnB,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAGxC,CAAC;IACL,CAAC;IAED,IAAc,cAAc;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IAED,IAAc,WAAW;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACxC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAK,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAe,CAAY,CAAC;IAC3E,CAAC;IAES,iBAAiB,CAAC,GAAY;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QAC9D,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3C,CAAC;IAES,aAAa,CAAC,GAAY;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QAC9D,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5D,CAAC;IAES,YAAY,CAAC,IAAmB;QACxC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAC1E,CAAC,GAAG,EAAE,EAAE,CAAE,GAAiC,CAAC,KAAK,KAAK,IAAI,CAAC,GAAG,CAClC,CAAC;QAE/B,IAAI,GAAG,EAAE,CAAC;YACR,GAAG,CAAC,KAAK;iBACN,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC;gBAChD,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAwB,CAAC;IACvC,CAAC;IAED,IAAW,MAAM,CAAC,IAAmB;QACnC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;IAC5B,CAAC;IAED,YAAsB,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;QA9E7B,aAAQ,GAAG,IAAI,GAAG,CAC1B,MAAM,CAAC,OAAO,CAAC;YACb,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,eAAe;SAC1B,CAAC,CACH,CAAC;QAOQ,UAAK,GAAG,gBAAgB,CAAC;QACzB,YAAO,GAAG,aAAa,CAAC;QA6DhC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAES,IAAI;QACZ,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAClF,CAAC;IAES,GAAG;QACX,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAClF,CAAC;IAES,SAAS;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAE3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;YAChC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;SAC5D,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IAES,OAAO;QACf,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IAES,SAAS;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAES,UAAU;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAES,eAAe;QAGvB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAkB,CAAC;QACnE,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,KAAK,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAEM,aAAa,KAAI,CAAC;IAElB,gBAAgB;QACrB,IAAI,CAAC,MAAM,GAAG,aAA8B,CAAC;QAC7C,IAAI,CAAC,KAAK,GAAG,gBAAgB,CAAC;IAChC,CAAC;IAED,IAAc,UAAU;QAEtB,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,UASpB,CAAC;IAChB,CAAC;IAQS,gBAAgB,CAAC,KAAoB;QAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC;QAChC,IAAI,CAAC,OAAO,EAAE,OAAO;YAAE,OAAO,KAAK,CAAC;QAEpC,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,QAAQ,KAAK,CAAC,GAAG,EAAE,CAAC;gBAClB,KAAK,SAAS,CAAC;gBACf,KAAK,WAAW,CAAC,CAAC,CAAC;oBACjB,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBACrE,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;wBACd,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;wBAC1D,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;oBACvE,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,KAAK,GAAG,CAAC;gBACT,KAAK,OAAO;oBACV,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,OAAO,CAAC,IAAI,EAAE,CAAC;oBACf,OAAO,IAAI,CAAC;gBACd,KAAK,QAAQ;oBACX,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,OAAO,CAAC,UAAU,EAAE,CAAC;oBACrB,OAAO,IAAI,CAAC;gBACd;oBACE,OAAO,KAAK,CAAC;YACjB,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;YAC9C,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAEM,QAAQ,CAAC,KAAoB;QAClC,IAAI,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC;YAAE,OAAO;QAIzC,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACtD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnC,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;gBACnD,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO;YACT,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;CACF","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type ApexGridRow from '../components/row.js';\nimport { NAVIGATION_STATE, SENTINEL_NODE } from '../internal/constants.js';\nimport { GRID_ROW_TAG } from '../internal/tags.js';\nimport type { ActiveNode, GridHost, Keys } from '../internal/types.js';\nimport { getDisplayColumns } from '../internal/utils.js';\n\nexport class NavigationController<T extends object> implements ReactiveController {\n protected handlers = new Map(\n Object.entries({\n ArrowDown: this.arrowDown,\n ArrowUp: this.arrowUp,\n ArrowLeft: this.arrowLeft,\n ArrowRight: this.arrowRight,\n Home: this.home,\n End: this.end,\n ' ': this.toggleSelection,\n })\n );\n\n protected get virtualizer() {\n // @ts-expect-error - Protected member access\n return this.host.scrollContainer;\n }\n\n protected state = NAVIGATION_STATE;\n protected _active = SENTINEL_NODE;\n\n protected get nextNode() {\n const node = this.state.get('current')!;\n return node === SENTINEL_NODE\n ? { column: this.firstColumn, row: 0 }\n : ({ ...node } as ActiveNode<T>);\n }\n\n protected get columns(): Array<{ key: Keys<T>; hidden?: boolean }> {\n return getDisplayColumns(this.host.columns) as unknown as Array<{\n key: Keys<T>;\n hidden?: boolean;\n }>;\n }\n\n protected get visibleColumns() {\n return this.columns.filter((column) => !column.hidden);\n }\n\n protected get firstColumn(): Keys<T> {\n const first = this.visibleColumns.at(0);\n return (first?.key ?? (this.host.columns[0]?.key as Keys<T>)) as Keys<T>;\n }\n\n protected getPreviousColumn(key: Keys<T>): Keys<T> {\n const columns = this.visibleColumns;\n const idx = columns.findIndex((column) => column.key === key);\n return columns[Math.max(idx - 1, 0)].key;\n }\n\n protected getNextColumn(key: Keys<T>): Keys<T> {\n const columns = this.visibleColumns;\n const idx = columns.findIndex((column) => column.key === key);\n return columns[Math.min(idx + 1, columns.length - 1)].key;\n }\n\n protected scrollToCell(node: ActiveNode<T>) {\n const row = Array.from(this.virtualizer.querySelectorAll(GRID_ROW_TAG)).find(\n (row) => (row as unknown as ApexGridRow<T>).index === node.row\n ) as unknown as ApexGridRow<T>;\n\n if (row) {\n row.cells\n .find((cell) => cell.column.key === node.column)\n ?.scrollIntoView({ block: 'nearest' });\n }\n }\n\n public get active() {\n return this._active as ActiveNode<T>;\n }\n\n public set active(node: ActiveNode<T>) {\n this._active = node;\n this.state.set('previous', this._active);\n this.state.set('current', node);\n this.host.requestUpdate();\n }\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n protected home() {\n this.active = Object.assign(this.nextNode, { row: 0 });\n this.virtualizer.element(this.active.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected end() {\n this.active = Object.assign(this.nextNode, { row: this.host.pageItems.length - 1 });\n this.virtualizer.element(this.active.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowDown() {\n const next = this.nextNode;\n\n this.active = Object.assign(next, {\n row: Math.min(next.row + 1, this.host.pageItems.length - 1),\n });\n this.virtualizer.element(next.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowUp() {\n const next = this.nextNode;\n this.active = Object.assign(next, { row: Math.max(0, next.row - 1) });\n this.virtualizer.element(next.row)?.scrollIntoView({ block: 'nearest' });\n }\n\n protected arrowLeft() {\n const next = this.nextNode;\n this.active = Object.assign(next, { column: this.getPreviousColumn(next.column) });\n this.scrollToCell(this.active);\n }\n\n protected arrowRight() {\n const next = this.nextNode;\n this.active = Object.assign(next, { column: this.getNextColumn(next.column) });\n this.scrollToCell(this.active);\n }\n\n protected toggleSelection() {\n // Space on the focused grid toggles selection of the active row.\n // No-op when selection is disabled or there's no active row.\n const data = this.host.pageItems[this.active.row] as T | undefined;\n if (!data) return;\n void this.host.toggleRowSelection(data);\n }\n\n public hostConnected() {}\n\n public hostDisconnected() {\n this.active = SENTINEL_NODE as ActiveNode<T>;\n this.state = NAVIGATION_STATE;\n }\n\n protected get rowReorder() {\n // @ts-expect-error - protected member access\n return this.host.stateController.rowReorder as\n | {\n enabled: boolean;\n isGrabbing: boolean;\n grab(rowIndex: number): boolean;\n moveGrabbed(direction: -1 | 1): number;\n drop(): void;\n cancelGrab(): void;\n }\n | undefined;\n }\n\n /**\n * Keyboard row reorder: Space grabs the active row, arrows move it, Space /\n * Enter drops, Escape cancels. Returns `true` when the event was consumed.\n * Only active when `rowReordering.enabled` (so it never shadows Space-to-select\n * on grids without reordering).\n */\n protected handleReorderKey(event: KeyboardEvent): boolean {\n const reorder = this.rowReorder;\n if (!reorder?.enabled) return false;\n\n if (reorder.isGrabbing) {\n switch (event.key) {\n case 'ArrowUp':\n case 'ArrowDown': {\n event.preventDefault();\n const next = reorder.moveGrabbed(event.key === 'ArrowDown' ? 1 : -1);\n if (next >= 0) {\n this.active = Object.assign(this.nextNode, { row: next });\n this.virtualizer.element(next)?.scrollIntoView({ block: 'nearest' });\n }\n return true;\n }\n case ' ':\n case 'Enter':\n event.preventDefault();\n reorder.drop();\n return true;\n case 'Escape':\n event.preventDefault();\n reorder.cancelGrab();\n return true;\n default:\n return false;\n }\n }\n\n if (event.key === ' ' && this.active.row >= 0) {\n event.preventDefault();\n return reorder.grab(this.active.row);\n }\n return false;\n }\n\n public navigate(event: KeyboardEvent) {\n if (this.handleReorderKey(event)) return;\n // Undo / redo: Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z, Ctrl/Cmd+Y. Only fires when the\n // grid body (not an open editor) has focus, so a text editor's native undo\n // is never hijacked.\n if ((event.ctrlKey || event.metaKey) && !event.altKey) {\n const key = event.key.toLowerCase();\n if (key === 'z' && !event.shiftKey) {\n event.preventDefault();\n this.host.undo();\n return;\n }\n if ((key === 'z' && event.shiftKey) || key === 'y') {\n event.preventDefault();\n this.host.redo();\n return;\n }\n }\n if (this.handlers.has(event.key)) {\n event.preventDefault();\n this.handlers.get(event.key)!.call(this);\n }\n }\n}\n"]}
|
package/controllers/reorder.js
CHANGED
|
@@ -23,7 +23,9 @@ export class ReorderController {
|
|
|
23
23
|
return false;
|
|
24
24
|
if (!this.isDraggable(source) || !this.isDraggable(target))
|
|
25
25
|
return false;
|
|
26
|
-
|
|
26
|
+
if ((source.pinned ?? null) !== (target.pinned ?? null))
|
|
27
|
+
return false;
|
|
28
|
+
return (source.group ?? null) === (target.group ?? null);
|
|
27
29
|
}
|
|
28
30
|
start(sourceKey, sourceRect, initialClientX, initialClientY, label) {
|
|
29
31
|
this.state = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reorder.js","sourceRoot":"","sources":["../../src/controllers/reorder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { awaitChildUpdates, captureRect, type FlipEntry, playFlip } from '../internal/flip.js';\nimport type { ColumnConfiguration, GridHost, Keys } from '../internal/types.js';\n\n/**\n * Live drag state surfaced to the header row so it can render the floating\n * \"ghost\" element that follows the cursor.\n *\n * @remarks\n * The dragged column itself stays in the columns array — we live-swap it\n * with neighbours as the cursor crosses their midpoints, so the rendered\n * order updates while the user is still dragging. The ghost is purely a\n * visual indicator of what the user is grabbing.\n */\ninterface ReorderState<T> {\n /** The column currently being dragged. */\n sourceKey: Keys<T>;\n /** Ghost position (viewport-relative top-left, in CSS pixels). */\n ghostX: number;\n ghostY: number;\n /** Ghost size — fixed at the source header's initial bounding rect. */\n ghostWidth: number;\n ghostHeight: number;\n /** Cursor offset within the ghost so the grab point stays under the cursor. */\n pointerOffsetX: number;\n pointerOffsetY: number;\n /** Display label for the ghost (header text or column key). */\n label: string;\n}\n\n/**\n * Reactive controller backing pointer-driven column reordering with a\n * floating ghost and live mid-drag swaps.\n *\n * @remarks\n * Headers feed `pointerdown` / `pointermove` / `pointerup` events into the\n * controller. While dragging, the controller updates the ghost position and\n * decides whether the cursor has crossed an adjacent column's midpoint —\n * if so it calls {@link ApexGrid.moveColumn} immediately, so the swap\n * happens during the drag instead of waiting for drop. The same cancellable\n * `columnMoving` / follow-up `columnMoved` event pipeline applies for\n * every swap.\n */\nexport class ReorderController<T extends object> implements ReactiveController {\n public state: ReorderState<T> | null = null;\n\n /** Re-entrancy guard so a slow `moveColumn` can't trigger a second swap. */\n #swapping = false;\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected() {}\n\n protected get headerRow() {\n // @ts-expect-error - protected member access\n return this.host.headerRow as\n | (HTMLElement & { headers?: Array<HTMLElement & { column: ColumnConfiguration<T> }> })\n | undefined;\n }\n\n /**\n * Whether reordering is enabled for the given column.\n */\n public isDraggable(column: ColumnConfiguration<T>): boolean {\n if (!this.host.columnReordering) return false;\n if (column.hidden) return false;\n return column.reorderable !== false;\n }\n\n /**\n * Whether `source` may be dropped onto `target`. Cross-pinning-group moves\n * are blocked so the drag can't escape its pin region.\n */\n public canDrop(source: ColumnConfiguration<T>, target: ColumnConfiguration<T>): boolean {\n if (source.key === target.key) return false;\n if (!this.isDraggable(source) || !this.isDraggable(target)) return false;\n return (source.pinned ?? null) === (target.pinned ?? null);\n }\n\n /**\n * Begins a drag from `sourceKey`. Captures the source's bounding rect and\n * the cursor offset within it so the ghost stays anchored to the grab\n * point as the cursor moves.\n */\n public start(\n sourceKey: Keys<T>,\n sourceRect: DOMRect,\n initialClientX: number,\n initialClientY: number,\n label: string\n ): void {\n this.state = {\n sourceKey,\n ghostX: sourceRect.left,\n ghostY: sourceRect.top,\n ghostWidth: sourceRect.width,\n ghostHeight: sourceRect.height,\n pointerOffsetX: initialClientX - sourceRect.left,\n pointerOffsetY: initialClientY - sourceRect.top,\n label,\n };\n this.host.requestUpdate();\n }\n\n /**\n * Updates the ghost position and decides whether to swap with an adjacent\n * column.\n */\n public move(clientX: number, clientY: number): void {\n if (!this.state) return;\n this.state = {\n ...this.state,\n ghostX: clientX - this.state.pointerOffsetX,\n ghostY: clientY - this.state.pointerOffsetY,\n };\n this.host.requestUpdate();\n void this.#checkSwap(clientX);\n }\n\n /**\n * Ends the drag session. The columns are already in their final order\n * because we live-swapped during the drag, so there's nothing to commit.\n */\n public end(): void {\n if (!this.state) return;\n this.state = null;\n this.host.requestUpdate();\n }\n\n async #checkSwap(clientX: number): Promise<void> {\n if (!this.state || this.#swapping) return;\n const source = this.host.getColumn(this.state.sourceKey);\n if (!source) return;\n const headerRow = this.headerRow;\n if (!headerRow?.headers) return;\n\n // Find the visible header whose box currently contains the cursor X.\n const target = headerRow.headers.find((h) => {\n const rect = h.getBoundingClientRect();\n return clientX >= rect.left && clientX < rect.right;\n });\n if (!target || target.column.key === source.key) return;\n if (!this.canDrop(source, target.column)) return;\n\n // Only swap once the cursor crosses the target's midpoint, so we don't\n // bounce back and forth as soon as the ghost touches the edge.\n const rect = target.getBoundingClientRect();\n const midpoint = rect.left + rect.width / 2;\n const visibleColumns = this.host.columns.filter((c) => !c.hidden);\n const sourceIdx = visibleColumns.findIndex((c) => c.key === source.key);\n const targetIdx = visibleColumns.findIndex((c) => c.key === target.column.key);\n if (sourceIdx === -1 || targetIdx === -1) return;\n\n let position: 'before' | 'after';\n if (sourceIdx < targetIdx) {\n // Dragging right — swap when the cursor crosses past the target's centre.\n if (clientX < midpoint) return;\n position = 'after';\n } else {\n // Dragging left — swap when the cursor crosses before the target's centre.\n if (clientX > midpoint) return;\n position = 'before';\n }\n\n this.#swapping = true;\n try {\n const before = this.#captureCellRects();\n await this.host.moveColumn(source.key, target.column.key, position);\n await this.host.updateComplete;\n // Child rows + header row schedule their own updates one microtask\n // after the grid — wait for them so the cells have actually moved\n // before measuring the \"Last\" rect.\n await awaitChildUpdates([\n this.headerRow as unknown as { updateComplete: Promise<unknown> } | undefined,\n ...this.host.rows,\n ]);\n playFlip(before, 'x');\n } finally {\n this.#swapping = false;\n }\n }\n\n /**\n * Snapshots the bounding rect of every header + body cell before a\n * swap. Used as the \"First\" step of a FLIP — after `moveColumn` resolves,\n * `playFlip` computes the delta against the new rect and animates each\n * element from its old position back to identity.\n */\n #captureCellRects(): FlipEntry[] {\n const entries: FlipEntry[] = [];\n const headerRow = this.headerRow;\n if (headerRow?.headers) {\n for (const header of headerRow.headers) {\n entries.push(captureRect(header));\n }\n }\n for (const row of this.host.rows) {\n for (const cell of row.cells) {\n entries.push(captureRect(cell as unknown as HTMLElement));\n }\n }\n return entries;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"reorder.js","sourceRoot":"","sources":["../../src/controllers/reorder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { awaitChildUpdates, captureRect, type FlipEntry, playFlip } from '../internal/flip.js';\nimport type { ColumnConfiguration, GridHost, Keys } from '../internal/types.js';\n\n/**\n * Live drag state surfaced to the header row so it can render the floating\n * \"ghost\" element that follows the cursor.\n *\n * @remarks\n * The dragged column itself stays in the columns array — we live-swap it\n * with neighbours as the cursor crosses their midpoints, so the rendered\n * order updates while the user is still dragging. The ghost is purely a\n * visual indicator of what the user is grabbing.\n */\ninterface ReorderState<T> {\n /** The column currently being dragged. */\n sourceKey: Keys<T>;\n /** Ghost position (viewport-relative top-left, in CSS pixels). */\n ghostX: number;\n ghostY: number;\n /** Ghost size — fixed at the source header's initial bounding rect. */\n ghostWidth: number;\n ghostHeight: number;\n /** Cursor offset within the ghost so the grab point stays under the cursor. */\n pointerOffsetX: number;\n pointerOffsetY: number;\n /** Display label for the ghost (header text or column key). */\n label: string;\n}\n\n/**\n * Reactive controller backing pointer-driven column reordering with a\n * floating ghost and live mid-drag swaps.\n *\n * @remarks\n * Headers feed `pointerdown` / `pointermove` / `pointerup` events into the\n * controller. While dragging, the controller updates the ghost position and\n * decides whether the cursor has crossed an adjacent column's midpoint —\n * if so it calls {@link ApexGrid.moveColumn} immediately, so the swap\n * happens during the drag instead of waiting for drop. The same cancellable\n * `columnMoving` / follow-up `columnMoved` event pipeline applies for\n * every swap.\n */\nexport class ReorderController<T extends object> implements ReactiveController {\n public state: ReorderState<T> | null = null;\n\n /** Re-entrancy guard so a slow `moveColumn` can't trigger a second swap. */\n #swapping = false;\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected() {}\n\n protected get headerRow() {\n // @ts-expect-error - protected member access\n return this.host.headerRow as\n | (HTMLElement & { headers?: Array<HTMLElement & { column: ColumnConfiguration<T> }> })\n | undefined;\n }\n\n /**\n * Whether reordering is enabled for the given column.\n */\n public isDraggable(column: ColumnConfiguration<T>): boolean {\n if (!this.host.columnReordering) return false;\n if (column.hidden) return false;\n return column.reorderable !== false;\n }\n\n /**\n * Whether `source` may be dropped onto `target`. Cross-pinning-group moves\n * are blocked so the drag can't escape its pin region.\n */\n public canDrop(source: ColumnConfiguration<T>, target: ColumnConfiguration<T>): boolean {\n if (source.key === target.key) return false;\n if (!this.isDraggable(source) || !this.isDraggable(target)) return false;\n if ((source.pinned ?? null) !== (target.pinned ?? null)) return false;\n // Grouped columns reorder only within their own group; ungrouped only among\n // ungrouped. Keeps a group's members contiguous (v1 has no group-level move).\n return (source.group ?? null) === (target.group ?? null);\n }\n\n /**\n * Begins a drag from `sourceKey`. Captures the source's bounding rect and\n * the cursor offset within it so the ghost stays anchored to the grab\n * point as the cursor moves.\n */\n public start(\n sourceKey: Keys<T>,\n sourceRect: DOMRect,\n initialClientX: number,\n initialClientY: number,\n label: string\n ): void {\n this.state = {\n sourceKey,\n ghostX: sourceRect.left,\n ghostY: sourceRect.top,\n ghostWidth: sourceRect.width,\n ghostHeight: sourceRect.height,\n pointerOffsetX: initialClientX - sourceRect.left,\n pointerOffsetY: initialClientY - sourceRect.top,\n label,\n };\n this.host.requestUpdate();\n }\n\n /**\n * Updates the ghost position and decides whether to swap with an adjacent\n * column.\n */\n public move(clientX: number, clientY: number): void {\n if (!this.state) return;\n this.state = {\n ...this.state,\n ghostX: clientX - this.state.pointerOffsetX,\n ghostY: clientY - this.state.pointerOffsetY,\n };\n this.host.requestUpdate();\n void this.#checkSwap(clientX);\n }\n\n /**\n * Ends the drag session. The columns are already in their final order\n * because we live-swapped during the drag, so there's nothing to commit.\n */\n public end(): void {\n if (!this.state) return;\n this.state = null;\n this.host.requestUpdate();\n }\n\n async #checkSwap(clientX: number): Promise<void> {\n if (!this.state || this.#swapping) return;\n const source = this.host.getColumn(this.state.sourceKey);\n if (!source) return;\n const headerRow = this.headerRow;\n if (!headerRow?.headers) return;\n\n // Find the visible header whose box currently contains the cursor X.\n const target = headerRow.headers.find((h) => {\n const rect = h.getBoundingClientRect();\n return clientX >= rect.left && clientX < rect.right;\n });\n if (!target || target.column.key === source.key) return;\n if (!this.canDrop(source, target.column)) return;\n\n // Only swap once the cursor crosses the target's midpoint, so we don't\n // bounce back and forth as soon as the ghost touches the edge.\n const rect = target.getBoundingClientRect();\n const midpoint = rect.left + rect.width / 2;\n const visibleColumns = this.host.columns.filter((c) => !c.hidden);\n const sourceIdx = visibleColumns.findIndex((c) => c.key === source.key);\n const targetIdx = visibleColumns.findIndex((c) => c.key === target.column.key);\n if (sourceIdx === -1 || targetIdx === -1) return;\n\n let position: 'before' | 'after';\n if (sourceIdx < targetIdx) {\n // Dragging right — swap when the cursor crosses past the target's centre.\n if (clientX < midpoint) return;\n position = 'after';\n } else {\n // Dragging left — swap when the cursor crosses before the target's centre.\n if (clientX > midpoint) return;\n position = 'before';\n }\n\n this.#swapping = true;\n try {\n const before = this.#captureCellRects();\n await this.host.moveColumn(source.key, target.column.key, position);\n await this.host.updateComplete;\n // Child rows + header row schedule their own updates one microtask\n // after the grid — wait for them so the cells have actually moved\n // before measuring the \"Last\" rect.\n await awaitChildUpdates([\n this.headerRow as unknown as { updateComplete: Promise<unknown> } | undefined,\n ...this.host.rows,\n ]);\n playFlip(before, 'x');\n } finally {\n this.#swapping = false;\n }\n }\n\n /**\n * Snapshots the bounding rect of every header + body cell before a\n * swap. Used as the \"First\" step of a FLIP — after `moveColumn` resolves,\n * `playFlip` computes the delta against the new rect and animates each\n * element from its old position back to identity.\n */\n #captureCellRects(): FlipEntry[] {\n const entries: FlipEntry[] = [];\n const headerRow = this.headerRow;\n if (headerRow?.headers) {\n for (const header of headerRow.headers) {\n entries.push(captureRect(header));\n }\n }\n for (const row of this.host.rows) {\n for (const cell of row.cells) {\n entries.push(captureRect(cell as unknown as HTMLElement));\n }\n }\n return entries;\n }\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ReactiveController } from 'lit';
|
|
2
|
+
import type { GridHost } from '../internal/types.js';
|
|
3
|
+
/** Where a row is pinned, or `null` when it scrolls with the body. */
|
|
4
|
+
export type RowPinPosition = 'top' | 'bottom';
|
|
5
|
+
/**
|
|
6
|
+
* Reactive controller backing row pinning.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Pinned rows are lifted out of the scrollable body and rendered in sticky
|
|
10
|
+
* bands above (`'top'`) or below (`'bottom'`) the virtualized rows. State is
|
|
11
|
+
* reference-based: the controller holds a `Set` of row data references per band,
|
|
12
|
+
* so pins survive sort / filter / pagination as long as those preserve row
|
|
13
|
+
* identity (the default in-place pipeline does). Replacing {@link ApexGrid.data}
|
|
14
|
+
* wholesale should be followed by re-pinning or {@link clear}.
|
|
15
|
+
*
|
|
16
|
+
* Mutations go through the cancellable `rowPinning` event and emit a follow-up
|
|
17
|
+
* `rowPinned` event, mirroring selection / expansion.
|
|
18
|
+
*/
|
|
19
|
+
export declare class RowPinController<T extends object> implements ReactiveController {
|
|
20
|
+
#private;
|
|
21
|
+
protected host: GridHost<T>;
|
|
22
|
+
constructor(host: GridHost<T>);
|
|
23
|
+
hostConnected(): void;
|
|
24
|
+
/** Whether row pinning is enabled at the grid level. */
|
|
25
|
+
get enabled(): boolean;
|
|
26
|
+
/** Whether any row is currently pinned. */
|
|
27
|
+
get hasPinnedRows(): boolean;
|
|
28
|
+
/** The band `row` is pinned to, or `null` when it is not pinned. */
|
|
29
|
+
pinnedPosition(row: T): RowPinPosition | null;
|
|
30
|
+
/** Whether `row` is pinned to either band. */
|
|
31
|
+
isPinned(row: T): boolean;
|
|
32
|
+
/** Pinned rows per band, in the order they were pinned. */
|
|
33
|
+
get pinnedRows(): {
|
|
34
|
+
top: T[];
|
|
35
|
+
bottom: T[];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Pins `row` to the given band. Moves it between bands when already pinned
|
|
39
|
+
* elsewhere. No-op (returns `true`) when it is already pinned there.
|
|
40
|
+
*
|
|
41
|
+
* @returns `true` when applied, `false` when disabled or cancelled.
|
|
42
|
+
*/
|
|
43
|
+
pinRow(row: T, position: RowPinPosition): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Unpins `row` from whichever band holds it. No-op (returns `true`) when it is
|
|
46
|
+
* not pinned.
|
|
47
|
+
*
|
|
48
|
+
* @returns `true` when applied, `false` when cancelled.
|
|
49
|
+
*/
|
|
50
|
+
unpinRow(row: T): boolean;
|
|
51
|
+
/** Clears every pin in both bands. */
|
|
52
|
+
clear(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Restores pinned rows from explicit per-band reference lists (state restore),
|
|
55
|
+
* replacing any current pins. Silent: emits no `rowPinning` / `rowPinned` and
|
|
56
|
+
* preserves the given order within each band.
|
|
57
|
+
*/
|
|
58
|
+
restore(top: ReadonlyArray<T>, bottom: ReadonlyArray<T>): void;
|
|
59
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export class RowPinController {
|
|
2
|
+
#top = new Set();
|
|
3
|
+
#bottom = new Set();
|
|
4
|
+
constructor(host) {
|
|
5
|
+
this.host = host;
|
|
6
|
+
this.host.addController(this);
|
|
7
|
+
}
|
|
8
|
+
hostConnected() { }
|
|
9
|
+
get enabled() {
|
|
10
|
+
return Boolean(this.host.rowPinning?.enabled);
|
|
11
|
+
}
|
|
12
|
+
get hasPinnedRows() {
|
|
13
|
+
return this.#top.size > 0 || this.#bottom.size > 0;
|
|
14
|
+
}
|
|
15
|
+
pinnedPosition(row) {
|
|
16
|
+
if (this.#top.has(row))
|
|
17
|
+
return 'top';
|
|
18
|
+
if (this.#bottom.has(row))
|
|
19
|
+
return 'bottom';
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
isPinned(row) {
|
|
23
|
+
return this.#top.has(row) || this.#bottom.has(row);
|
|
24
|
+
}
|
|
25
|
+
get pinnedRows() {
|
|
26
|
+
return { top: [...this.#top], bottom: [...this.#bottom] };
|
|
27
|
+
}
|
|
28
|
+
pinRow(row, position) {
|
|
29
|
+
if (!this.enabled)
|
|
30
|
+
return false;
|
|
31
|
+
if (this.pinnedPosition(row) === position)
|
|
32
|
+
return true;
|
|
33
|
+
if (!this.#emitPinning(row, position))
|
|
34
|
+
return false;
|
|
35
|
+
this.#top.delete(row);
|
|
36
|
+
this.#bottom.delete(row);
|
|
37
|
+
(position === 'top' ? this.#top : this.#bottom).add(row);
|
|
38
|
+
this.#commit(row, position, `Row pinned to ${position}`);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
unpinRow(row) {
|
|
42
|
+
if (!this.isPinned(row))
|
|
43
|
+
return true;
|
|
44
|
+
if (!this.#emitPinning(row, null))
|
|
45
|
+
return false;
|
|
46
|
+
this.#top.delete(row);
|
|
47
|
+
this.#bottom.delete(row);
|
|
48
|
+
this.#commit(row, null, 'Row unpinned');
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
clear() {
|
|
52
|
+
if (!this.hasPinnedRows)
|
|
53
|
+
return;
|
|
54
|
+
this.#top.clear();
|
|
55
|
+
this.#bottom.clear();
|
|
56
|
+
this.host.requestUpdate();
|
|
57
|
+
}
|
|
58
|
+
restore(top, bottom) {
|
|
59
|
+
this.#top = new Set(top);
|
|
60
|
+
this.#bottom = new Set(bottom);
|
|
61
|
+
this.host.requestUpdate();
|
|
62
|
+
}
|
|
63
|
+
#emitPinning(row, position) {
|
|
64
|
+
return this.host.emitEvent('rowPinning', {
|
|
65
|
+
detail: { row, position },
|
|
66
|
+
cancelable: true,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
#commit(row, position, message) {
|
|
70
|
+
this.host.requestUpdate();
|
|
71
|
+
this.host.announce(message);
|
|
72
|
+
this.host.emitEvent('rowPinned', { detail: { row, position } });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=row-pin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row-pin.js","sourceRoot":"","sources":["../../src/controllers/row-pin.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport type { GridHost } from '../internal/types.js';\n\n/** Where a row is pinned, or `null` when it scrolls with the body. */\nexport type RowPinPosition = 'top' | 'bottom';\n\n/**\n * Reactive controller backing row pinning.\n *\n * @remarks\n * Pinned rows are lifted out of the scrollable body and rendered in sticky\n * bands above (`'top'`) or below (`'bottom'`) the virtualized rows. State is\n * reference-based: the controller holds a `Set` of row data references per band,\n * so pins survive sort / filter / pagination as long as those preserve row\n * identity (the default in-place pipeline does). Replacing {@link ApexGrid.data}\n * wholesale should be followed by re-pinning or {@link clear}.\n *\n * Mutations go through the cancellable `rowPinning` event and emit a follow-up\n * `rowPinned` event, mirroring selection / expansion.\n */\nexport class RowPinController<T extends object> implements ReactiveController {\n #top: Set<T> = new Set();\n #bottom: Set<T> = new Set();\n\n constructor(protected host: GridHost<T>) {\n this.host.addController(this);\n }\n\n public hostConnected() {}\n\n /** Whether row pinning is enabled at the grid level. */\n public get enabled(): boolean {\n return Boolean(this.host.rowPinning?.enabled);\n }\n\n /** Whether any row is currently pinned. */\n public get hasPinnedRows(): boolean {\n return this.#top.size > 0 || this.#bottom.size > 0;\n }\n\n /** The band `row` is pinned to, or `null` when it is not pinned. */\n public pinnedPosition(row: T): RowPinPosition | null {\n if (this.#top.has(row)) return 'top';\n if (this.#bottom.has(row)) return 'bottom';\n return null;\n }\n\n /** Whether `row` is pinned to either band. */\n public isPinned(row: T): boolean {\n return this.#top.has(row) || this.#bottom.has(row);\n }\n\n /** Pinned rows per band, in the order they were pinned. */\n public get pinnedRows(): { top: T[]; bottom: T[] } {\n return { top: [...this.#top], bottom: [...this.#bottom] };\n }\n\n /**\n * Pins `row` to the given band. Moves it between bands when already pinned\n * elsewhere. No-op (returns `true`) when it is already pinned there.\n *\n * @returns `true` when applied, `false` when disabled or cancelled.\n */\n public pinRow(row: T, position: RowPinPosition): boolean {\n if (!this.enabled) return false;\n if (this.pinnedPosition(row) === position) return true;\n if (!this.#emitPinning(row, position)) return false;\n\n this.#top.delete(row);\n this.#bottom.delete(row);\n (position === 'top' ? this.#top : this.#bottom).add(row);\n this.#commit(row, position, `Row pinned to ${position}`);\n return true;\n }\n\n /**\n * Unpins `row` from whichever band holds it. No-op (returns `true`) when it is\n * not pinned.\n *\n * @returns `true` when applied, `false` when cancelled.\n */\n public unpinRow(row: T): boolean {\n if (!this.isPinned(row)) return true;\n if (!this.#emitPinning(row, null)) return false;\n\n this.#top.delete(row);\n this.#bottom.delete(row);\n this.#commit(row, null, 'Row unpinned');\n return true;\n }\n\n /** Clears every pin in both bands. */\n public clear(): void {\n if (!this.hasPinnedRows) return;\n this.#top.clear();\n this.#bottom.clear();\n this.host.requestUpdate();\n }\n\n /**\n * Restores pinned rows from explicit per-band reference lists (state restore),\n * replacing any current pins. Silent: emits no `rowPinning` / `rowPinned` and\n * preserves the given order within each band.\n */\n public restore(top: ReadonlyArray<T>, bottom: ReadonlyArray<T>): void {\n this.#top = new Set(top);\n this.#bottom = new Set(bottom);\n this.host.requestUpdate();\n }\n\n #emitPinning(row: T, position: RowPinPosition | null): boolean {\n return this.host.emitEvent('rowPinning', {\n detail: { row, position },\n cancelable: true,\n });\n }\n\n #commit(row: T, position: RowPinPosition | null, message: string): void {\n this.host.requestUpdate();\n this.host.announce(message);\n this.host.emitEvent('rowPinned', { detail: { row, position } });\n }\n}\n"]}
|