igniteui-grid-lite 0.10.0 → 0.11.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.
Files changed (90) hide show
  1. package/components/cell.d.ts +5 -17
  2. package/components/cell.js +2 -2
  3. package/components/cell.js.map +1 -1
  4. package/components/filter-row.d.ts +2 -5
  5. package/components/filter-row.js +22 -27
  6. package/components/filter-row.js.map +1 -1
  7. package/components/grid.d.ts +10 -15
  8. package/components/grid.js +33 -36
  9. package/components/grid.js.map +1 -1
  10. package/components/header-row.js +2 -1
  11. package/components/header-row.js.map +1 -1
  12. package/components/header.js +2 -6
  13. package/components/header.js.map +1 -1
  14. package/components/row.d.ts +0 -3
  15. package/components/row.js +3 -3
  16. package/components/row.js.map +1 -1
  17. package/controllers/data-operation.d.ts +1 -3
  18. package/controllers/data-operation.js +0 -2
  19. package/controllers/data-operation.js.map +1 -1
  20. package/controllers/dom.d.ts +4 -16
  21. package/controllers/dom.js +14 -13
  22. package/controllers/dom.js.map +1 -1
  23. package/controllers/filter.d.ts +10 -22
  24. package/controllers/filter.js +14 -8
  25. package/controllers/filter.js.map +1 -1
  26. package/controllers/navigation.d.ts +4 -9
  27. package/controllers/navigation.js +23 -43
  28. package/controllers/navigation.js.map +1 -1
  29. package/controllers/resize.d.ts +3 -9
  30. package/controllers/resize.js +2 -7
  31. package/controllers/resize.js.map +1 -1
  32. package/controllers/sort.d.ts +3 -5
  33. package/controllers/sort.js +19 -14
  34. package/controllers/sort.js.map +1 -1
  35. package/controllers/state.d.ts +2 -13
  36. package/controllers/state.js +12 -10
  37. package/controllers/state.js.map +1 -1
  38. package/custom-elements.json +201 -350
  39. package/docs/igniteui-grid-lite.json +849 -859
  40. package/internal/a11y.d.ts +2 -5
  41. package/internal/a11y.js.map +1 -1
  42. package/internal/body-focus.d.ts +6 -0
  43. package/internal/body-focus.js +46 -0
  44. package/internal/body-focus.js.map +1 -0
  45. package/internal/constants.d.ts +4 -1
  46. package/internal/constants.js +2 -0
  47. package/internal/constants.js.map +1 -1
  48. package/internal/element-from-event-path.d.ts +1 -1
  49. package/internal/element-from-event-path.js +2 -9
  50. package/internal/element-from-event-path.js.map +1 -1
  51. package/internal/tags.d.ts +0 -1
  52. package/internal/tags.js +0 -1
  53. package/internal/tags.js.map +1 -1
  54. package/internal/types.d.ts +6 -7
  55. package/internal/types.js.map +1 -1
  56. package/internal/utils.d.ts +4 -8
  57. package/internal/utils.js +7 -3
  58. package/internal/utils.js.map +1 -1
  59. package/operations/base.js +2 -2
  60. package/operations/base.js.map +1 -1
  61. package/operations/filter/state.d.ts +0 -1
  62. package/operations/filter/state.js +0 -3
  63. package/operations/filter/state.js.map +1 -1
  64. package/operations/filter/types.d.ts +1 -1
  65. package/operations/filter/types.js.map +1 -1
  66. package/operations/filter.js +3 -2
  67. package/operations/filter.js.map +1 -1
  68. package/operations/sort/types.d.ts +1 -1
  69. package/operations/sort/types.js.map +1 -1
  70. package/operations/sort.js +5 -1
  71. package/operations/sort.js.map +1 -1
  72. package/package.json +2 -3
  73. package/styles/header-cell/header-cell.css.js +1 -1
  74. package/styles/header-cell/header-cell.css.js.map +1 -1
  75. package/styles/themes/filtering-row-themes.js +2 -30
  76. package/styles/themes/filtering-row-themes.js.map +1 -1
  77. package/styles/themes/grid-header-themes.js +2 -20
  78. package/styles/themes/grid-header-themes.js.map +1 -1
  79. package/styles/themes/grid-themes.js +10 -31
  80. package/styles/themes/grid-themes.js.map +1 -1
  81. package/styles/themes/grid.base.css.js +1 -1
  82. package/styles/themes/grid.base.css.js.map +1 -1
  83. package/styles/themes/shared/grid.common.css.js +1 -1
  84. package/styles/themes/shared/grid.common.css.js.map +1 -1
  85. package/components/virtualizer.d.ts +0 -14
  86. package/components/virtualizer.js +0 -49
  87. package/components/virtualizer.js.map +0 -1
  88. package/internal/virt-flow-layout.d.ts +0 -4
  89. package/internal/virt-flow-layout.js +0 -15
  90. package/internal/virt-flow-layout.js.map +0 -1
@@ -1,13 +1,12 @@
1
- import type { ReactiveController } from 'lit';
2
1
  import type IgcFilterRow from '../components/filter-row.js';
2
+ import type { IgcFilteringEvent } from '../components/grid.js';
3
3
  import type { ColumnConfiguration, Keys } from '../internal/types.js';
4
4
  import { FilterState } from '../operations/filter/state.js';
5
5
  import type { FilterExpression } from '../operations/filter/types.js';
6
6
  import type { GridDOMController } from './dom.js';
7
7
  import type { StateController } from './state.js';
8
- /** The kind of modification a `filtering` event describes. */
9
- export type FilterEventType = 'add' | 'modify' | 'remove';
10
- export declare class FilterController<T extends object> implements ReactiveController {
8
+ type FilterEventType = IgcFilteringEvent<object>['type'];
9
+ export declare class FilterController<T extends object> {
11
10
  #private;
12
11
  private readonly _stateController;
13
12
  private readonly _dom;
@@ -15,7 +14,6 @@ export declare class FilterController<T extends object> implements ReactiveContr
15
14
  state: FilterState<T>;
16
15
  get host(): import("../internal/types.js").GridHost<T>;
17
16
  get filterRow(): IgcFilterRow<T> | null;
18
- hostConnected(): void;
19
17
  get(key: Keys<T>): import("../operations/filter/tree.js").FilterExpressionTree<T> | undefined;
20
18
  reset(key?: Keys<T>): void;
21
19
  setActiveColumn(column?: ColumnConfiguration<T>): void;
@@ -23,28 +21,18 @@ export declare class FilterController<T extends object> implements ReactiveContr
23
21
  removeAllExpressions(key: Keys<T>): Promise<void>;
24
22
  removeExpression(expression: FilterExpression<T>): Promise<void>;
25
23
  /**
26
- * Emits `filtering` for `expression` and, if it passes, applies it.
24
+ * Emits `filtering` for `expression` and, if not canceled, applies it.
27
25
  *
28
26
  * @remarks
29
- * `target` is the stored expression the change applies to. Callers pass a
30
- * candidate copy as `expression`, so a canceled event does not touch the stored
31
- * state. On commit the candidate is merged back into `target`. This keeps the
32
- * object identity that the expression tree and the chip selection use.
27
+ * `expression` is a candidate copy of the stored `target`, so a canceled event
28
+ * leaves `target` untouched. On commit it merges into `target`, keeping the
29
+ * identity the expression tree and the chip selection use.
33
30
  */
34
31
  filterWithEvent(expression: FilterExpression<T>, type: FilterEventType, target?: FilterExpression<T>): Promise<void>;
35
32
  filter(expression: FilterExpression<T> | FilterExpression<T>[]): void;
36
- /**
37
- * Stores expressions in the filter state without requiring column configuration.
38
- * Used when setting initial filter expressions before columns are available.
39
- *
40
- * @remarks
41
- * Copies are stored. `resolveConditions` later rewrites each stored expression,
42
- * and those writes must not reach the caller's objects.
43
- */
33
+ /** Stores copies before columns exist. `resolveConditions` rewrites them later. */
44
34
  setRaw(expressions: FilterExpression<T>[]): void;
45
- /**
46
- * Resolves any string conditions in the current filter state using available column configuration.
47
- * Called after columns become available to finalize deferred expressions.
48
- */
35
+ /** Resolves string conditions and default case sensitivity once columns exist. */
49
36
  resolveConditions(): void;
50
37
  }
38
+ export {};
@@ -6,7 +6,6 @@ export class FilterController {
6
6
  this.state = new FilterState();
7
7
  this._stateController = state;
8
8
  this._dom = dom;
9
- this._stateController.host.addController(this);
10
9
  }
11
10
  get host() {
12
11
  return this._stateController.host;
@@ -31,20 +30,26 @@ export class FilterController {
31
30
  this._stateController.updateObservers();
32
31
  this.host.requestUpdate(PIPELINE);
33
32
  }
34
- hostConnected() { }
35
33
  get(key) {
36
34
  return this.state.get(key);
37
35
  }
38
36
  reset(key) {
39
- key !== undefined ? this.state.delete(key) : this.state.clear();
37
+ if (key === undefined) {
38
+ this.state.clear();
39
+ }
40
+ else {
41
+ this.state.delete(key);
42
+ }
40
43
  this._stateController.updateObservers();
41
44
  }
42
45
  setActiveColumn(column) {
43
- if (column?.filterable && this.filterRow?.active) {
44
- this.filterRow.column = column;
45
- this.filterRow.expression = this.getDefaultExpression(column);
46
- this._stateController.updateObservers();
46
+ const filterRow = this.filterRow;
47
+ if (!(column?.filterable && filterRow?.active)) {
48
+ return;
47
49
  }
50
+ filterRow.column = column;
51
+ filterRow.expression = this.getDefaultExpression(column);
52
+ this._stateController.updateObservers();
48
53
  }
49
54
  getDefaultExpression(column) {
50
55
  return {
@@ -96,8 +101,9 @@ export class FilterController {
96
101
  resolveConditions() {
97
102
  for (const tree of this.state.values) {
98
103
  const column = this.host.getColumn(tree.key);
99
- if (!column)
104
+ if (!column) {
100
105
  continue;
106
+ }
101
107
  const defaults = this.getDefaultExpression(column);
102
108
  for (const expr of tree.all) {
103
109
  if (isString(expr.condition)) {
@@ -1 +1 @@
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 IgcFilterRow from '../components/filter-row.js';\nimport type { IgcFilteredEvent } from '../components/grid.js';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { ColumnConfiguration, Keys } from '../internal/types.js';\nimport { asArray, getFilterOperandsFor, isString, resolveCondition } from '../internal/utils.js';\nimport { FilterState } from '../operations/filter/state.js';\nimport type { FilterExpression } from '../operations/filter/types.js';\nimport type { GridDOMController } from './dom.js';\nimport type { StateController } from './state.js';\n\n/** The kind of modification a `filtering` event describes. */\nexport type FilterEventType = 'add' | 'modify' | 'remove';\n\nexport class FilterController<T extends object> implements ReactiveController {\n private readonly _stateController: StateController<T>;\n private readonly _dom: GridDOMController<T>;\n\n constructor(state: StateController<T>, dom: GridDOMController<T>) {\n this._stateController = state;\n this._dom = dom;\n this._stateController.host.addController(this);\n }\n\n public state: FilterState<T> = new FilterState();\n\n public get host() {\n return this._stateController.host;\n }\n\n public get filterRow(): IgcFilterRow<T> | null {\n return this._dom.filterRow;\n }\n\n #emitFilteringEvent(key: Keys<T>, expressions: FilterExpression<T>[], type: FilterEventType) {\n return this.host.emitEvent('filtering', {\n detail: { key, expressions, type },\n cancelable: true,\n });\n }\n\n #emitFilteredEvent(detail?: IgcFilteredEvent<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._dom.resetScrollPosition();\n this._stateController.updateObservers();\n this.host.requestUpdate(PIPELINE);\n }\n\n public hostConnected() {}\n\n public get(key: Keys<T>) {\n return this.state.get(key);\n }\n\n public reset(key?: Keys<T>) {\n key !== undefined ? this.state.delete(key) : this.state.clear();\n\n // The filter row renders its chips from this state.\n this._stateController.updateObservers();\n }\n\n public setActiveColumn(column?: ColumnConfiguration<T>) {\n if (column?.filterable && this.filterRow?.active) {\n this.filterRow.column = column;\n this.filterRow.expression = this.getDefaultExpression(column);\n\n // The header row marks the filtered column.\n this._stateController.updateObservers();\n }\n }\n\n public getDefaultExpression(column: ColumnConfiguration<T>) {\n // XXX: Types\n return {\n key: column.field,\n condition: Object.values(getFilterOperandsFor(column))[0],\n caseSensitive: Boolean(column.filteringCaseSensitive),\n } as unknown as FilterExpression<T>;\n }\n\n /**\n * Emits `filtering` and, if it passes, runs `commit`, waits for the pipeline\n * and emits `filtered` with the resulting state of the column.\n */\n async #applyWithEvents(\n key: Keys<T>,\n expressions: FilterExpression<T>[],\n type: FilterEventType,\n commit: () => void\n ) {\n if (!this.#emitFilteringEvent(key, expressions, type)) {\n return;\n }\n\n commit();\n\n await this.host._pipelineComplete;\n this.#emitFilteredEvent({ key, state: this.get(key)?.all ?? [] });\n }\n\n public async removeAllExpressions(key: Keys<T>) {\n await this.#applyWithEvents(key, this.get(key)?.all ?? [], 'remove', () => {\n this.reset(key);\n this.#filter([]);\n });\n }\n\n public async removeExpression(expression: FilterExpression<T>) {\n const state = this.get(expression.key);\n\n await this.#applyWithEvents(expression.key, [expression], 'remove', () => {\n state?.remove(expression);\n\n if (state?.empty) {\n this.reset(state.key);\n }\n\n this.#filter([]);\n });\n }\n\n /**\n * Emits `filtering` for `expression` and, if it passes, applies it.\n *\n * @remarks\n * `target` is the stored expression the change applies to. Callers pass a\n * candidate copy as `expression`, so a canceled event does not touch the stored\n * state. On commit the candidate is merged back into `target`. This keeps the\n * object identity that the expression tree and the chip selection use.\n */\n public async filterWithEvent(\n expression: FilterExpression<T>,\n type: FilterEventType,\n target: FilterExpression<T> = expression\n ) {\n await this.#applyWithEvents(expression.key, [expression], type, () => {\n if (target !== expression) {\n Object.assign(target, expression);\n }\n\n this.#filter(target);\n });\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 /**\n * Stores expressions in the filter state without requiring column configuration.\n * Used when setting initial filter expressions before columns are available.\n *\n * @remarks\n * Copies are stored. `resolveConditions` later rewrites each stored expression,\n * and those writes must not reach the caller's objects.\n */\n public setRaw(expressions: FilterExpression<T>[]) {\n for (const expr of expressions) {\n this.state.set({ ...expr });\n }\n }\n\n /**\n * Resolves any string conditions in the current filter state using available column configuration.\n * Called after columns become available to finalize deferred expressions.\n */\n public resolveConditions() {\n for (const tree of this.state.values) {\n const column = this.host.getColumn(tree.key);\n if (!column) continue;\n const defaults = this.getDefaultExpression(column);\n for (const expr of tree.all) {\n if (isString(expr.condition)) {\n (expr as any).condition = resolveCondition(column, expr.condition);\n }\n if (expr.caseSensitive === undefined) {\n expr.caseSensitive = defaults.caseSensitive;\n }\n }\n }\n }\n}\n"]}
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/controllers/filter.ts"],"names":[],"mappings":"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","sourcesContent":["import type IgcFilterRow from '../components/filter-row.js';\nimport type { IgcFilteredEvent, IgcFilteringEvent } from '../components/grid.js';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { ColumnConfiguration, Keys } from '../internal/types.js';\nimport { asArray, getFilterOperandsFor, isString, resolveCondition } from '../internal/utils.js';\nimport { FilterState } from '../operations/filter/state.js';\nimport type { FilterExpression } from '../operations/filter/types.js';\nimport type { GridDOMController } from './dom.js';\nimport type { StateController } from './state.js';\n\ntype FilterEventType = IgcFilteringEvent<object>['type'];\n\nexport class FilterController<T extends object> {\n private readonly _stateController: StateController<T>;\n private readonly _dom: GridDOMController<T>;\n\n constructor(state: StateController<T>, dom: GridDOMController<T>) {\n this._stateController = state;\n this._dom = dom;\n }\n\n public state: FilterState<T> = new FilterState();\n\n public get host() {\n return this._stateController.host;\n }\n\n public get filterRow(): IgcFilterRow<T> | null {\n return this._dom.filterRow;\n }\n\n #emitFilteringEvent(key: Keys<T>, expressions: FilterExpression<T>[], type: FilterEventType) {\n return this.host.emitEvent('filtering', {\n detail: { key, expressions, type },\n cancelable: true,\n });\n }\n\n #emitFilteredEvent(detail: IgcFilteredEvent<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: a large scrollTop plus a heavy filter broke the scroll position. Reset it.\n this._dom.resetScrollPosition();\n this._stateController.updateObservers();\n this.host.requestUpdate(PIPELINE);\n }\n\n public get(key: Keys<T>) {\n return this.state.get(key);\n }\n\n public reset(key?: Keys<T>) {\n if (key === undefined) {\n this.state.clear();\n } else {\n this.state.delete(key);\n }\n\n // The filter row renders its chips from this state.\n this._stateController.updateObservers();\n }\n\n public setActiveColumn(column?: ColumnConfiguration<T>) {\n const filterRow = this.filterRow;\n\n if (!(column?.filterable && filterRow?.active)) {\n return;\n }\n\n filterRow.column = column;\n filterRow.expression = this.getDefaultExpression(column);\n\n // The header row marks the filtered column.\n this._stateController.updateObservers();\n }\n\n public getDefaultExpression(column: ColumnConfiguration<T>) {\n return {\n key: column.field,\n condition: Object.values(getFilterOperandsFor(column))[0],\n caseSensitive: Boolean(column.filteringCaseSensitive),\n } as unknown as FilterExpression<T>;\n }\n\n /** Emits `filtering`. If not canceled: commits, awaits the pipeline, emits `filtered`. */\n async #applyWithEvents(\n key: Keys<T>,\n expressions: FilterExpression<T>[],\n type: FilterEventType,\n commit: () => void\n ) {\n if (!this.#emitFilteringEvent(key, expressions, type)) {\n return;\n }\n\n commit();\n\n await this.host._pipelineComplete;\n this.#emitFilteredEvent({ key, state: this.get(key)?.all ?? [] });\n }\n\n public async removeAllExpressions(key: Keys<T>) {\n await this.#applyWithEvents(key, this.get(key)?.all ?? [], 'remove', () => {\n this.reset(key);\n this.#filter([]);\n });\n }\n\n public async removeExpression(expression: FilterExpression<T>) {\n const state = this.get(expression.key);\n\n await this.#applyWithEvents(expression.key, [expression], 'remove', () => {\n state?.remove(expression);\n\n if (state?.empty) {\n this.reset(state.key);\n }\n\n this.#filter([]);\n });\n }\n\n /**\n * Emits `filtering` for `expression` and, if not canceled, applies it.\n *\n * @remarks\n * `expression` is a candidate copy of the stored `target`, so a canceled event\n * leaves `target` untouched. On commit it merges into `target`, keeping the\n * identity the expression tree and the chip selection use.\n */\n public async filterWithEvent(\n expression: FilterExpression<T>,\n type: FilterEventType,\n target: FilterExpression<T> = expression\n ) {\n await this.#applyWithEvents(expression.key, [expression], type, () => {\n if (target !== expression) {\n Object.assign(target, expression);\n }\n\n this.#filter(target);\n });\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 /** Stores copies before columns exist. `resolveConditions` rewrites them later. */\n public setRaw(expressions: FilterExpression<T>[]) {\n for (const expr of expressions) {\n this.state.set({ ...expr });\n }\n }\n\n /** Resolves string conditions and default case sensitivity once columns exist. */\n public resolveConditions() {\n for (const tree of this.state.values) {\n const column = this.host.getColumn(tree.key);\n\n if (!column) {\n continue;\n }\n\n const defaults = this.getDefaultExpression(column);\n\n for (const expr of tree.all) {\n if (isString(expr.condition)) {\n (expr as any).condition = resolveCondition(column, expr.condition);\n }\n if (expr.caseSensitive === undefined) {\n expr.caseSensitive = defaults.caseSensitive;\n }\n }\n }\n }\n}\n"]}
@@ -2,15 +2,16 @@ import type { ReactiveController } from 'lit';
2
2
  import type { ActiveNode, NavigateToOptions } from '../internal/types.js';
3
3
  import type { GridDOMController } from './dom.js';
4
4
  import type { StateController } from './state.js';
5
+ /** The active node a row at `index` receives: only the active row sees it. */
6
+ export declare function activeNodeFor<T extends object>(active: ActiveNode<T>, index: number): ActiveNode<T>;
5
7
  export declare class NavigationController<T extends object> implements ReactiveController {
6
8
  #private;
7
9
  protected _state: StateController<T>;
8
10
  protected _dom: GridDOMController<T>;
9
- protected handlers: Map<string, (() => void) | (() => void) | (() => void) | (() => void) | (() => void) | (() => void)>;
10
- protected get _virtualizer(): import("../components/virtualizer.js").default | null;
11
+ protected handlers: Map<string, () => void>;
12
+ protected get _virtualizer(): import("igniteui-webcomponents").IgcVirtualScrollComponent<any> | null;
11
13
  protected _active: ActiveNode<T>;
12
14
  protected get nextNode(): ActiveNode<T>;
13
- /** Rows render only the visible columns, so navigation walks the same sequence. */
14
15
  protected get _columns(): import("../index.js").ColumnConfiguration<T>[];
15
16
  protected get _firstColumn(): import("../index.js").Keys<any>;
16
17
  protected queryRowByIndex(index: number): import("../components/row.js").default<T> | undefined;
@@ -18,12 +19,6 @@ export declare class NavigationController<T extends object> implements ReactiveC
18
19
  get active(): ActiveNode<T>;
19
20
  set active(node: ActiveNode<T>);
20
21
  constructor(_state: StateController<T>, _dom: GridDOMController<T>);
21
- protected home(): void;
22
- protected end(): void;
23
- protected arrowDown(): void;
24
- protected arrowUp(): void;
25
- protected arrowLeft(): void;
26
- protected arrowRight(): void;
27
22
  hostDisconnected(): void;
28
23
  navigate(event: KeyboardEvent): void;
29
24
  navigateTo(row: number, options?: NavigateToOptions<T>): Promise<void>;
@@ -1,4 +1,9 @@
1
- import { SENTINEL_NODE } from '../internal/constants.js';
1
+ import { NO_SCROLL, SENTINEL_NODE } from '../internal/constants.js';
2
+ import { clamp, visibleColumns } from '../internal/utils.js';
3
+ const SCROLL_NEAREST = { block: 'nearest' };
4
+ export function activeNodeFor(active, index) {
5
+ return active.row === index ? active : SENTINEL_NODE;
6
+ }
2
7
  export class NavigationController {
3
8
  get _virtualizer() {
4
9
  return this._dom.virtualizer;
@@ -9,7 +14,7 @@ export class NavigationController {
9
14
  : { ...this._active };
10
15
  }
11
16
  get _columns() {
12
- return this._state.columns.filter((column) => !column.hidden);
17
+ return visibleColumns(this._state.columns);
13
18
  }
14
19
  get _firstColumn() {
15
20
  return this._columns.at(0)?.field ?? SENTINEL_NODE.column;
@@ -21,7 +26,7 @@ export class NavigationController {
21
26
  return this.queryRowByIndex(node.row)?.cells.find((cell) => cell.column.field === node.column);
22
27
  }
23
28
  scrollToCell(node) {
24
- this.#queryCell(node)?.scrollIntoView({ block: 'nearest' });
29
+ this.#queryCell(node)?.scrollIntoView(SCROLL_NEAREST);
25
30
  }
26
31
  get active() {
27
32
  return this._active;
@@ -35,7 +40,7 @@ export class NavigationController {
35
40
  #updateActiveRows(previous) {
36
41
  for (const row of this._dom.rows) {
37
42
  if (row.index === previous.row || row.index === this._active.row) {
38
- row.activeNode = this._active;
43
+ row.activeNode = activeNodeFor(this._active, row.index);
39
44
  }
40
45
  }
41
46
  }
@@ -50,53 +55,35 @@ export class NavigationController {
50
55
  if (node !== this._active) {
51
56
  return;
52
57
  }
53
- this.#queryCell(node)?.focus({ preventScroll: true });
58
+ this.#queryCell(node)?.focus(NO_SCROLL);
54
59
  }
55
60
  constructor(_state, _dom) {
56
61
  this._state = _state;
57
62
  this._dom = _dom;
58
- this.handlers = new Map(Object.entries({
59
- ArrowDown: this.arrowDown,
60
- ArrowUp: this.arrowUp,
61
- ArrowLeft: this.arrowLeft,
62
- ArrowRight: this.arrowRight,
63
- Home: this.home,
64
- End: this.end,
65
- }));
63
+ this.handlers = new Map([
64
+ ['ArrowDown', () => this.#moveToRow(this.nextNode.row + 1)],
65
+ ['ArrowUp', () => this.#moveToRow(this.nextNode.row - 1)],
66
+ ['ArrowLeft', () => this.#moveToColumn(-1)],
67
+ ['ArrowRight', () => this.#moveToColumn(1)],
68
+ ['Home', () => this.#moveToRow(0)],
69
+ ['End', () => this.#moveToRow(this._state.host.totalItems - 1)],
70
+ ]);
66
71
  this._active = SENTINEL_NODE;
67
72
  this._state.host.addController(this);
68
73
  }
69
74
  #moveToRow(row) {
70
- const clamped = Math.min(Math.max(row, 0), this._state.host.totalItems - 1);
75
+ const clamped = clamp(row, 0, this._state.host.totalItems - 1);
71
76
  this.active = Object.assign(this.nextNode, { row: clamped });
72
- this._virtualizer?.element(clamped)?.scrollIntoView({ block: 'nearest' });
77
+ this._virtualizer?.scrollToIndex(clamped, SCROLL_NEAREST);
73
78
  }
74
79
  #moveToColumn(offset) {
75
80
  const next = this.nextNode;
76
81
  const columns = this._columns;
77
82
  const index = columns.findIndex((column) => column.field === next.column);
78
- const target = Math.min(Math.max(index + offset, 0), columns.length - 1);
83
+ const target = clamp(index + offset, 0, columns.length - 1);
79
84
  this.active = Object.assign(next, { column: columns[target].field });
80
85
  this.scrollToCell(this.active);
81
86
  }
82
- home() {
83
- this.#moveToRow(0);
84
- }
85
- end() {
86
- this.#moveToRow(this._state.host.totalItems - 1);
87
- }
88
- arrowDown() {
89
- this.#moveToRow(this.nextNode.row + 1);
90
- }
91
- arrowUp() {
92
- this.#moveToRow(this.nextNode.row - 1);
93
- }
94
- arrowLeft() {
95
- this.#moveToColumn(-1);
96
- }
97
- arrowRight() {
98
- this.#moveToColumn(1);
99
- }
100
87
  hostDisconnected() {
101
88
  this.active = SENTINEL_NODE;
102
89
  }
@@ -106,7 +93,7 @@ export class NavigationController {
106
93
  return;
107
94
  }
108
95
  event.preventDefault();
109
- handler.call(this);
96
+ handler();
110
97
  this.#focusActiveCell();
111
98
  }
112
99
  async navigateTo(row, options) {
@@ -114,14 +101,7 @@ export class NavigationController {
114
101
  if (activate) {
115
102
  this.active = Object.assign(this.nextNode, column ? { row, column } : { row });
116
103
  }
117
- let item = this.queryRowByIndex(row);
118
- let completePromise;
119
- if (!item) {
120
- item = this._virtualizer?.element(row);
121
- completePromise = item && this._virtualizer?.layoutComplete;
122
- }
123
- item?.scrollIntoView({ block: 'nearest' });
124
- await completePromise;
104
+ await this._virtualizer?.scrollToIndex(row, SCROLL_NEAREST);
125
105
  if (column) {
126
106
  this.scrollToCell({ row, column });
127
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/controllers/navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { SENTINEL_NODE } from '../internal/constants.js';\nimport type { ActiveNode, NavigateToOptions } from '../internal/types.js';\nimport type { GridDOMController } from './dom.js';\nimport type { StateController } from './state.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 })\n );\n\n protected get _virtualizer() {\n return this._dom.virtualizer;\n }\n\n protected _active = SENTINEL_NODE as ActiveNode<T>;\n\n protected get nextNode() {\n return this._active === SENTINEL_NODE\n ? ({ column: this._firstColumn, row: 0 } as ActiveNode<T>)\n : ({ ...this._active } as ActiveNode<T>);\n }\n\n /** Rows render only the visible columns, so navigation walks the same sequence. */\n protected get _columns() {\n return this._state.columns.filter((column) => !column.hidden);\n }\n\n protected get _firstColumn() {\n return this._columns.at(0)?.field ?? SENTINEL_NODE.column;\n }\n\n protected queryRowByIndex(index: number) {\n return this._dom.rows.find((row) => row.index === index);\n }\n\n /** The rendered cell at `node`, if its row and column are in the DOM. */\n #queryCell(node: ActiveNode<T>) {\n return this.queryRowByIndex(node.row)?.cells.find((cell) => cell.column.field === node.column);\n }\n\n protected scrollToCell(node: ActiveNode<T>) {\n this.#queryCell(node)?.scrollIntoView({ block: 'nearest' });\n }\n\n public get active(): ActiveNode<T> {\n return this._active;\n }\n\n public set active(node: ActiveNode<T>) {\n const previous = this._active;\n\n this._active = node ?? SENTINEL_NODE;\n this.#updateActiveRows(previous);\n this._state.host.requestUpdate();\n }\n\n /**\n * Sends the new active node only to the rows that enter or leave the active\n * state. The other rendered rows have no changes and are not re-rendered.\n */\n #updateActiveRows(previous: ActiveNode<T>): void {\n for (const row of this._dom.rows) {\n if (row.index === previous.row || row.index === this._active.row) {\n row.activeNode = this._active;\n }\n }\n }\n\n /** Moves DOM focus onto the active cell when it is rendered (roving focus). */\n async #focusActiveCell(): Promise<void> {\n const node = this._active;\n let row = this.queryRowByIndex(node.row);\n\n // The target row is not rendered yet. Wait for the virtualizer layout pass.\n if (!row) {\n await this._virtualizer?.layoutComplete;\n row = this.queryRowByIndex(node.row);\n }\n\n await row?.updateComplete;\n\n // A newer navigation replaced this one during the render wait.\n if (node !== this._active) {\n return;\n }\n\n this.#queryCell(node)?.focus({ preventScroll: true });\n }\n\n constructor(\n protected _state: StateController<T>,\n protected _dom: GridDOMController<T>\n ) {\n this._state.host.addController(this);\n }\n\n /** Activates `row` (clamped to the data range) and scrolls it into view. */\n #moveToRow(row: number) {\n const clamped = Math.min(Math.max(row, 0), this._state.host.totalItems - 1);\n\n this.active = Object.assign(this.nextNode, { row: clamped });\n this._virtualizer?.element(clamped)?.scrollIntoView({ block: 'nearest' });\n }\n\n /**\n * Steps the active column by `offset`, clamping at the visible ends. A hidden\n * or missing key (index -1) lands on the first visible column.\n */\n #moveToColumn(offset: -1 | 1) {\n const next = this.nextNode;\n const columns = this._columns;\n\n const index = columns.findIndex((column) => column.field === next.column);\n const target = Math.min(Math.max(index + offset, 0), columns.length - 1);\n\n this.active = Object.assign(next, { column: columns[target].field });\n this.scrollToCell(this.active);\n }\n\n protected home() {\n this.#moveToRow(0);\n }\n\n protected end() {\n this.#moveToRow(this._state.host.totalItems - 1);\n }\n\n protected arrowDown() {\n this.#moveToRow(this.nextNode.row + 1);\n }\n\n protected arrowUp() {\n this.#moveToRow(this.nextNode.row - 1);\n }\n\n protected arrowLeft() {\n this.#moveToColumn(-1);\n }\n\n protected arrowRight() {\n this.#moveToColumn(1);\n }\n\n public hostDisconnected() {\n this.active = SENTINEL_NODE as ActiveNode<T>;\n }\n\n public navigate(event: KeyboardEvent) {\n const handler = this.handlers.get(event.key);\n\n // Nothing to move to without a visible column or a data row.\n if (!handler || this._columns.length === 0 || this._state.host.totalItems === 0) {\n return;\n }\n\n event.preventDefault();\n handler.call(this);\n this.#focusActiveCell();\n }\n\n public async navigateTo(row: number, options?: NavigateToOptions<T>) {\n const { column, activate } = options ?? {};\n\n if (activate) {\n // Without an explicit column, activation keeps the one from `nextNode`.\n this.active = Object.assign(this.nextNode, column ? { row, column } : { row });\n }\n\n // Resolve the row in the DOM first. A missing row means a layout pass is necessary.\n let item: Pick<HTMLElement, 'scrollIntoView'> | undefined = this.queryRowByIndex(row);\n let completePromise: Promise<void> | undefined;\n\n if (!item) {\n item = this._virtualizer?.element(row);\n completePromise = item && this._virtualizer?.layoutComplete;\n }\n\n item?.scrollIntoView({ block: 'nearest' });\n await completePromise;\n\n if (column) {\n this.scrollToCell({ row, column });\n }\n }\n}\n"]}
1
+ {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/controllers/navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { NO_SCROLL, SENTINEL_NODE } from '../internal/constants.js';\nimport type { ActiveNode, NavigateToOptions } from '../internal/types.js';\nimport { clamp, visibleColumns } from '../internal/utils.js';\nimport type { GridDOMController } from './dom.js';\nimport type { StateController } from './state.js';\n\n/** Minimal scroll that shows the target. */\nconst SCROLL_NEAREST: ScrollIntoViewOptions = { block: 'nearest' };\n\n/** The active node a row at `index` receives: only the active row sees it. */\nexport function activeNodeFor<T extends object>(active: ActiveNode<T>, index: number) {\n return active.row === index ? active : (SENTINEL_NODE as ActiveNode<T>);\n}\n\nexport class NavigationController<T extends object> implements ReactiveController {\n protected handlers = new Map<string, () => void>([\n ['ArrowDown', () => this.#moveToRow(this.nextNode.row + 1)],\n ['ArrowUp', () => this.#moveToRow(this.nextNode.row - 1)],\n ['ArrowLeft', () => this.#moveToColumn(-1)],\n ['ArrowRight', () => this.#moveToColumn(1)],\n ['Home', () => this.#moveToRow(0)],\n ['End', () => this.#moveToRow(this._state.host.totalItems - 1)],\n ]);\n\n protected get _virtualizer() {\n return this._dom.virtualizer;\n }\n\n protected _active = SENTINEL_NODE as ActiveNode<T>;\n\n protected get nextNode() {\n return this._active === SENTINEL_NODE\n ? ({ column: this._firstColumn, row: 0 } as ActiveNode<T>)\n : ({ ...this._active } as ActiveNode<T>);\n }\n\n protected get _columns() {\n return visibleColumns(this._state.columns);\n }\n\n protected get _firstColumn() {\n return this._columns.at(0)?.field ?? SENTINEL_NODE.column;\n }\n\n protected queryRowByIndex(index: number) {\n return this._dom.rows.find((row) => row.index === index);\n }\n\n /** The rendered cell at `node`, if its row and column are in the DOM. */\n #queryCell(node: ActiveNode<T>) {\n return this.queryRowByIndex(node.row)?.cells.find((cell) => cell.column.field === node.column);\n }\n\n protected scrollToCell(node: ActiveNode<T>) {\n this.#queryCell(node)?.scrollIntoView(SCROLL_NEAREST);\n }\n\n public get active(): ActiveNode<T> {\n return this._active;\n }\n\n public set active(node: ActiveNode<T>) {\n const previous = this._active;\n\n this._active = node ?? SENTINEL_NODE;\n this.#updateActiveRows(previous);\n this._state.host.requestUpdate();\n }\n\n /** Updates only the rows entering or leaving the active state. */\n #updateActiveRows(previous: ActiveNode<T>): void {\n for (const row of this._dom.rows) {\n if (row.index === previous.row || row.index === this._active.row) {\n row.activeNode = activeNodeFor(this._active, row.index);\n }\n }\n }\n\n /** Moves DOM focus onto the active cell when it is rendered (roving focus). */\n async #focusActiveCell(): Promise<void> {\n const node = this._active;\n let row = this.queryRowByIndex(node.row);\n\n // Row not rendered yet: wait for layout.\n if (!row) {\n await this._virtualizer?.layoutComplete;\n row = this.queryRowByIndex(node.row);\n }\n\n await row?.updateComplete;\n\n // A newer navigation replaced this one during the render wait.\n if (node !== this._active) {\n return;\n }\n\n this.#queryCell(node)?.focus(NO_SCROLL);\n }\n\n constructor(\n protected _state: StateController<T>,\n protected _dom: GridDOMController<T>\n ) {\n this._state.host.addController(this);\n }\n\n /** Activates `row` (clamped to the data range) and scrolls it into view. */\n #moveToRow(row: number) {\n const clamped = clamp(row, 0, this._state.host.totalItems - 1);\n\n this.active = Object.assign(this.nextNode, { row: clamped });\n this._virtualizer?.scrollToIndex(clamped, SCROLL_NEAREST);\n }\n\n /**\n * Steps the active column by `offset`, clamping at the visible ends. A hidden\n * or missing key (index -1) lands on the first visible column.\n */\n #moveToColumn(offset: -1 | 1) {\n const next = this.nextNode;\n const columns = this._columns;\n\n const index = columns.findIndex((column) => column.field === next.column);\n const target = clamp(index + offset, 0, columns.length - 1);\n\n this.active = Object.assign(next, { column: columns[target].field });\n this.scrollToCell(this.active);\n }\n\n public hostDisconnected() {\n this.active = SENTINEL_NODE as ActiveNode<T>;\n }\n\n public navigate(event: KeyboardEvent) {\n const handler = this.handlers.get(event.key);\n\n // Nothing to move to without a visible column or a data row.\n if (!handler || this._columns.length === 0 || this._state.host.totalItems === 0) {\n return;\n }\n\n event.preventDefault();\n handler();\n this.#focusActiveCell();\n }\n\n public async navigateTo(row: number, options?: NavigateToOptions<T>) {\n const { column, activate } = options ?? {};\n\n if (activate) {\n // Without an explicit column, activation keeps the one from `nextNode`.\n this.active = Object.assign(this.nextNode, column ? { row, column } : { row });\n }\n\n await this._virtualizer?.scrollToIndex(row, SCROLL_NEAREST);\n\n if (column) {\n this.scrollToCell({ row, column });\n }\n }\n}\n"]}
@@ -1,9 +1,9 @@
1
- import { nothing, type ReactiveController } from 'lit';
1
+ import { nothing } from 'lit';
2
2
  import type IgcGridLiteHeader from '../components/header.js';
3
3
  import type { ColumnConfiguration } from '../internal/types.js';
4
4
  import type { GridDOMController } from './dom.js';
5
5
  import type { StateController } from './state.js';
6
- export declare class ResizeController<T extends object> implements ReactiveController {
6
+ export declare class ResizeController<T extends object> {
7
7
  #private;
8
8
  protected _state: StateController<T>;
9
9
  protected _dom: GridDOMController<T>;
@@ -13,15 +13,9 @@ export declare class ResizeController<T extends object> implements ReactiveContr
13
13
  protected get host(): import("../internal/types.js").GridHost<T>;
14
14
  /** Starts a column resize: shows the indicator and aligns it with the header. */
15
15
  start(header: IgcGridLiteHeader<T>): void;
16
- /**
17
- * Stops and resets the resizing state.
18
- */
16
+ /** Hides the indicator. */
19
17
  stop(): void;
20
18
  resize(column: ColumnConfiguration<T>, width: number, sizerOffset?: number): void;
21
19
  autosize(column: ColumnConfiguration<T>, header: IgcGridLiteHeader<T>): Promise<void>;
22
- hostConnected(): void;
23
- /**
24
- * Renders the resize indicator in the grid.
25
- */
26
20
  renderIndicator(): import("lit-html").TemplateResult<1> | typeof nothing;
27
21
  }
@@ -7,17 +7,13 @@ export class ResizeController {
7
7
  this._dom = _dom;
8
8
  this.indicatorActive = false;
9
9
  this.indicatorOffset = 0;
10
- this.host.addController(this);
11
10
  }
12
11
  get host() {
13
12
  return this._state.host;
14
13
  }
15
14
  #maxSize(key, headerWidth) {
16
- const max = this._dom.rows.reduce((prev, row) => {
17
- const cell = row.cells.find((cell) => cell.column.field === key);
18
- return cell && cell.offsetWidth > prev ? cell.offsetWidth : prev;
19
- }, 0);
20
- return Math.max(MIN_COL_RESIZE_WIDTH, max, headerWidth);
15
+ const widths = this._dom.rows.map((row) => row.cells.find((cell) => cell.column.field === key)?.offsetWidth ?? 0);
16
+ return Math.max(MIN_COL_RESIZE_WIDTH, headerWidth, ...widths);
21
17
  }
22
18
  #setWidth(column, width) {
23
19
  this._state.setColumnWidth(column.field, width);
@@ -46,7 +42,6 @@ export class ResizeController {
46
42
  await this.host.updateComplete;
47
43
  this.#setWidth(column, `${this.#maxSize(column.field, header.offsetWidth)}px`);
48
44
  }
49
- hostConnected() { }
50
45
  renderIndicator() {
51
46
  return this.indicatorActive
52
47
  ? html `<div
@@ -1 +1 @@
1
- {"version":3,"file":"resize.js","sourceRoot":"","sources":["../../src/controllers/resize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAA2B,MAAM,KAAK,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAKhE,MAAM,OAAO,gBAAgB;IAC3B,YACY,MAA0B,EAC1B,IAA0B;QAD1B,WAAM,GAAN,MAAM,CAAoB;QAC1B,SAAI,GAAJ,IAAI,CAAsB;QAK/B,oBAAe,GAAG,KAAK,CAAC;QACxB,oBAAe,GAAG,CAAC,CAAC;QAJzB,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAKD,IAAc,IAAI;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,QAAQ,CAAC,GAAY,EAAE,WAAmB;QAExC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC;YACjE,OAAO,IAAI,IAAI,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;QACnE,CAAC,EAAE,CAAC,CAAC,CAAC;QAEN,OAAO,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;IAC1D,CAAC;IAGD,SAAS,CAAC,MAA8B,EAAE,KAAa;QACrD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;IAGD,iBAAiB;QACf,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;IAChC,CAAC;IAGM,KAAK,CAAC,MAA4B;QACvC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;QAC9D,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAKM,IAAI;QACT,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAEM,MAAM,CAAC,MAA8B,EAAE,KAAa,EAAE,WAAoB;QAC/E,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC;QACrC,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,MAA8B,EAAE,MAA4B;QAEhF,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACtC,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;QAE/B,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjF,CAAC;IAEM,aAAa,KAAI,CAAC;IAKlB,eAAe;QACpB,OAAO,IAAI,CAAC,eAAe;YACzB,CAAC,CAAC,IAAI,CAAA;;kBAEM,QAAQ,CAAC;gBACf,SAAS,EAAE,cAAc,IAAI,CAAC,eAAe,KAAK;aACnD,CAAC;gBACI;YACV,CAAC,CAAC,OAAO,CAAC;IACd,CAAC;CACF","sourcesContent":["import { html, nothing, type ReactiveController } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport type IgcGridLiteHeader from '../components/header.js';\nimport { MIN_COL_RESIZE_WIDTH } from '../internal/constants.js';\nimport type { ColumnConfiguration, Keys } from '../internal/types.js';\nimport type { GridDOMController } from './dom.js';\nimport type { StateController } from './state.js';\n\nexport class ResizeController<T extends object> implements ReactiveController {\n constructor(\n protected _state: StateController<T>,\n protected _dom: GridDOMController<T>\n ) {\n this.host.addController(this);\n }\n\n public indicatorActive = false;\n public indicatorOffset = 0;\n\n protected get host() {\n return this._state.host;\n }\n\n #maxSize(key: Keys<T>, headerWidth: number) {\n // A rendered row may not carry a cell for the column (e.g. it was just hidden).\n const max = this._dom.rows.reduce((prev, row) => {\n const cell = row.cells.find((cell) => cell.column.field === key);\n return cell && cell.offsetWidth > prev ? cell.offsetWidth : prev;\n }, 0);\n\n return Math.max(MIN_COL_RESIZE_WIDTH, max, headerWidth);\n }\n\n /** Column objects are immutable. Widths are set through the state. */\n #setWidth(column: ColumnConfiguration<T>, width: string) {\n this._state.setColumnWidth(column.field, width);\n }\n\n /** The grid renders the indicator. Each header renders its `resizing` part. */\n #indicatorChanged() {\n this.host.requestUpdate();\n this._state.updateObservers();\n }\n\n /** Starts a column resize: shows the indicator and aligns it with the header. */\n public start(header: IgcGridLiteHeader<T>) {\n this.indicatorActive = true;\n this.indicatorOffset = header.offsetLeft + header.offsetWidth;\n this.#indicatorChanged();\n }\n\n /**\n * Stops and resets the resizing state.\n */\n public stop() {\n this.indicatorActive = false;\n this.#indicatorChanged();\n }\n\n public resize(column: ColumnConfiguration<T>, width: number, sizerOffset?: number) {\n if (sizerOffset) {\n this.indicatorOffset = sizerOffset;\n }\n\n this.#setWidth(column, `${width}px`);\n }\n\n public async autosize(column: ColumnConfiguration<T>, header: IgcGridLiteHeader<T>) {\n // Measure the column at its natural size first, then pin the result in pixels.\n this.#setWidth(column, 'max-content');\n await this.host.updateComplete;\n\n this.#setWidth(column, `${this.#maxSize(column.field, header.offsetWidth)}px`);\n }\n\n public hostConnected() {}\n\n /**\n * Renders the resize indicator in the grid.\n */\n public renderIndicator() {\n return this.indicatorActive\n ? html`<div\n part=\"resize-indicator\"\n style=${styleMap({\n transform: `translateX(${this.indicatorOffset}px)`,\n })}\n ></div>`\n : nothing;\n }\n}\n"]}
1
+ {"version":3,"file":"resize.js","sourceRoot":"","sources":["../../src/controllers/resize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAKhE,MAAM,OAAO,gBAAgB;IAC3B,YACY,MAA0B,EAC1B,IAA0B;QAD1B,WAAM,GAAN,MAAM,CAAoB;QAC1B,SAAI,GAAJ,IAAI,CAAsB;QAG/B,oBAAe,GAAG,KAAK,CAAC;QACxB,oBAAe,GAAG,CAAC,CAAC;IAHxB,CAAC;IAKJ,IAAc,IAAI;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,QAAQ,CAAC,GAAY,EAAE,WAAmB;QAExC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC/B,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,GAAG,CAAC,EAAE,WAAW,IAAI,CAAC,CAC/E,CAAC;QAEF,OAAO,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,CAAC;IAChE,CAAC;IAED,SAAS,CAAC,MAA8B,EAAE,KAAa;QACrD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;IAGD,iBAAiB;QACf,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;IAChC,CAAC;IAGM,KAAK,CAAC,MAA4B;QACvC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;QAC9D,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAGM,IAAI;QACT,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAEM,MAAM,CAAC,MAA8B,EAAE,KAAa,EAAE,WAAoB;QAC/E,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC;QACrC,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,QAAQ,CAAC,MAA8B,EAAE,MAA4B;QAEhF,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACtC,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;QAE/B,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjF,CAAC;IAEM,eAAe;QACpB,OAAO,IAAI,CAAC,eAAe;YACzB,CAAC,CAAC,IAAI,CAAA;;kBAEM,QAAQ,CAAC;gBACf,SAAS,EAAE,cAAc,IAAI,CAAC,eAAe,KAAK;aACnD,CAAC;gBACI;YACV,CAAC,CAAC,OAAO,CAAC;IACd,CAAC;CACF","sourcesContent":["import { html, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport type IgcGridLiteHeader from '../components/header.js';\nimport { MIN_COL_RESIZE_WIDTH } from '../internal/constants.js';\nimport type { ColumnConfiguration, Keys } from '../internal/types.js';\nimport type { GridDOMController } from './dom.js';\nimport type { StateController } from './state.js';\n\nexport class ResizeController<T extends object> {\n constructor(\n protected _state: StateController<T>,\n protected _dom: GridDOMController<T>\n ) {}\n\n public indicatorActive = false;\n public indicatorOffset = 0;\n\n protected get host() {\n return this._state.host;\n }\n\n #maxSize(key: Keys<T>, headerWidth: number) {\n // A rendered row may lack the cell, e.g. for a just hidden column.\n const widths = this._dom.rows.map(\n (row) => row.cells.find((cell) => cell.column.field === key)?.offsetWidth ?? 0\n );\n\n return Math.max(MIN_COL_RESIZE_WIDTH, headerWidth, ...widths);\n }\n\n #setWidth(column: ColumnConfiguration<T>, width: string) {\n this._state.setColumnWidth(column.field, width);\n }\n\n /** The grid renders the indicator. Each header renders its `resizing` part. */\n #indicatorChanged() {\n this.host.requestUpdate();\n this._state.updateObservers();\n }\n\n /** Starts a column resize: shows the indicator and aligns it with the header. */\n public start(header: IgcGridLiteHeader<T>) {\n this.indicatorActive = true;\n this.indicatorOffset = header.offsetLeft + header.offsetWidth;\n this.#indicatorChanged();\n }\n\n /** Hides the indicator. */\n public stop() {\n this.indicatorActive = false;\n this.#indicatorChanged();\n }\n\n public resize(column: ColumnConfiguration<T>, width: number, sizerOffset?: number) {\n if (sizerOffset) {\n this.indicatorOffset = sizerOffset;\n }\n\n this.#setWidth(column, `${width}px`);\n }\n\n public async autosize(column: ColumnConfiguration<T>, header: IgcGridLiteHeader<T>) {\n // Measure the column at its natural size first, then pin the result in pixels.\n this.#setWidth(column, 'max-content');\n await this.host.updateComplete;\n\n this.#setWidth(column, `${this.#maxSize(column.field, header.offsetWidth)}px`);\n }\n\n public renderIndicator() {\n return this.indicatorActive\n ? html`<div\n part=\"resize-indicator\"\n style=${styleMap({\n transform: `translateX(${this.indicatorOffset}px)`,\n })}\n ></div>`\n : nothing;\n }\n}\n"]}
@@ -1,8 +1,7 @@
1
- import type { ReactiveController } from 'lit';
2
1
  import type { ColumnConfiguration, Keys } from '../internal/types.js';
3
2
  import type { SortingExpression, SortState } from '../operations/sort/types.js';
4
3
  import type { StateController } from './state.js';
5
- export declare class SortController<T extends object> implements ReactiveController {
4
+ export declare class SortController<T extends object> {
6
5
  #private;
7
6
  protected _state: StateController<T>;
8
7
  constructor(_state: StateController<T>);
@@ -13,12 +12,11 @@ export declare class SortController<T extends object> implements ReactiveControl
13
12
  * Returns the expression the next sort operation would apply for `column`.
14
13
  *
15
14
  * @remarks
16
- * The result is a candidate copy - the stored state is only updated once the
17
- * operation is committed through {@link SortController._sort}.
15
+ * A candidate copy. The stored state changes only on commit through
16
+ * {@link SortController._sort}.
18
17
  */
19
18
  prepareExpression(column: ColumnConfiguration<T>): SortingExpression<T>;
20
19
  reset(key?: Keys<T>): void;
21
20
  protected _sort(expressions: SortingExpression<T> | SortingExpression<T>[]): void;
22
21
  sort(expressions: SortingExpression<T> | SortingExpression<T>[]): void;
23
- hostConnected(): void;
24
22
  }
@@ -9,7 +9,6 @@ export class SortController {
9
9
  constructor(_state) {
10
10
  this._state = _state;
11
11
  this.state = new Map();
12
- this.host.addController(this);
13
12
  }
14
13
  get host() {
15
14
  return this._state.host;
@@ -38,9 +37,11 @@ export class SortController {
38
37
  return this.host.emitEvent('sorted', { detail });
39
38
  }
40
39
  #setExpression(expression) {
41
- expression.direction === 'none'
42
- ? this.reset(expression.key)
43
- : this.state.set(expression.key, { ...expression });
40
+ if (expression.direction === 'none') {
41
+ this.reset(expression.key);
42
+ return;
43
+ }
44
+ this.state.set(expression.key, { ...expression });
44
45
  }
45
46
  async sortFromHeaderClick(column) {
46
47
  const expression = this.prepareExpression(column);
@@ -55,18 +56,23 @@ export class SortController {
55
56
  this.#emitSortedEvent(expression);
56
57
  }
57
58
  prepareExpression(column) {
58
- if (this.state.has(column.field)) {
59
- const expr = this.state.get(column.field);
60
- return {
61
- ...expr,
62
- direction: NEXT_DIRECTION[expr.direction],
63
- ...this.#resolveSortOptions(column),
64
- };
59
+ const expr = this.state.get(column.field);
60
+ if (!expr) {
61
+ return this.#createDefaultExpression(column.field);
65
62
  }
66
- return this.#createDefaultExpression(column.field);
63
+ return {
64
+ ...expr,
65
+ direction: NEXT_DIRECTION[expr.direction],
66
+ ...this.#resolveSortOptions(column),
67
+ };
67
68
  }
68
69
  reset(key) {
69
- key !== undefined ? this.state.delete(key) : this.state.clear();
70
+ if (key === undefined) {
71
+ this.state.clear();
72
+ }
73
+ else {
74
+ this.state.delete(key);
75
+ }
70
76
  this._state.updateObservers();
71
77
  }
72
78
  _sort(expressions) {
@@ -82,6 +88,5 @@ export class SortController {
82
88
  ...expr,
83
89
  })));
84
90
  }
85
- hostConnected() { }
86
91
  }
87
92
  //# sourceMappingURL=sort.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sort.js","sourceRoot":"","sources":["../../src/controllers/sort.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController } from 'lit';\nimport { PIPELINE } from '../internal/constants.js';\nimport type { ColumnConfiguration, Keys } from '../internal/types.js';\nimport { asArray } from '../internal/utils.js';\nimport type { SortingDirection, SortingExpression, SortState } from '../operations/sort/types.js';\nimport type { StateController } from './state.js';\n\n/** Tri-state cycle: ascending -> descending -> none -> ascending. */\nconst NEXT_DIRECTION: Record<SortingDirection, SortingDirection> = {\n ascending: 'descending',\n descending: 'none',\n none: 'ascending',\n};\n\nexport class SortController<T extends object> implements ReactiveController {\n constructor(protected _state: StateController<T>) {\n this.host.addController(this);\n }\n\n public state: SortState<T> = new Map();\n\n protected get host() {\n return this._state.host;\n }\n\n get #isMultipleSort() {\n return this.host.sortingOptions.mode === 'multiple';\n }\n\n #resolveSortOptions(column?: ColumnConfiguration<T>): Partial<SortingExpression<T>> {\n return {\n caseSensitive: column?.sortingCaseSensitive ?? false,\n comparer: column?.sortConfiguration?.comparer,\n } as Partial<SortingExpression<T>>;\n }\n\n #createDefaultExpression(key: Keys<T>) {\n const column = this.host.getColumn(key);\n\n return {\n key,\n direction: 'ascending',\n ...this.#resolveSortOptions(column),\n } as SortingExpression<T>;\n }\n\n #emitSortingEvent(detail: SortingExpression<T>) {\n return this.host.emitEvent('sorting', { detail, cancelable: true });\n }\n\n #emitSortedEvent(detail: SortingExpression<T>) {\n return this.host.emitEvent('sorted', { detail });\n }\n\n #setExpression(expression: SortingExpression<T>) {\n expression.direction === 'none'\n ? this.reset(expression.key)\n : this.state.set(expression.key, { ...expression });\n }\n\n public async sortFromHeaderClick(column: ColumnConfiguration<T>) {\n const expression = this.prepareExpression(column);\n\n if (!this.#emitSortingEvent(expression)) {\n return;\n }\n\n if (!this.#isMultipleSort) {\n this.reset();\n }\n\n this._sort(expression);\n\n await this.host._pipelineComplete;\n this.#emitSortedEvent(expression);\n }\n\n /**\n * Returns the expression the next sort operation would apply for `column`.\n *\n * @remarks\n * The result is a candidate copy - the stored state is only updated once the\n * operation is committed through {@link SortController._sort}.\n */\n public prepareExpression(column: ColumnConfiguration<T>): SortingExpression<T> {\n if (this.state.has(column.field)) {\n const expr = this.state.get(column.field)!;\n\n return {\n ...expr,\n direction: NEXT_DIRECTION[expr.direction],\n ...this.#resolveSortOptions(column),\n };\n }\n\n // Initial state\n return this.#createDefaultExpression(column.field);\n }\n\n public reset(key?: Keys<T>) {\n key !== undefined ? this.state.delete(key) : this.state.clear();\n\n // Headers render the sort indicator from this state.\n this._state.updateObservers();\n }\n\n protected _sort(expressions: SortingExpression<T> | SortingExpression<T>[]) {\n for (const expr of asArray(expressions)) {\n this.#setExpression(expr);\n }\n\n this._state.updateObservers();\n this.host.requestUpdate(PIPELINE);\n }\n\n public sort(expressions: SortingExpression<T> | SortingExpression<T>[]) {\n // Merge into copies. Only `#setExpression` replaces the stored expression.\n this._sort(\n asArray(expressions).map((expr) => ({\n ...(this.state.get(expr.key) ?? this.#createDefaultExpression(expr.key)),\n ...expr,\n }))\n );\n }\n\n public hostConnected() {}\n}\n"]}
1
+ {"version":3,"file":"sort.js","sourceRoot":"","sources":["../../src/controllers/sort.ts"],"names":[],"mappings":"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","sourcesContent":["import { PIPELINE } from '../internal/constants.js';\nimport type { ColumnConfiguration, Keys } from '../internal/types.js';\nimport { asArray } from '../internal/utils.js';\nimport type { SortingDirection, SortingExpression, SortState } from '../operations/sort/types.js';\nimport type { StateController } from './state.js';\n\n/** Tri-state cycle: ascending -> descending -> none -> ascending. */\nconst NEXT_DIRECTION: Record<SortingDirection, SortingDirection> = {\n ascending: 'descending',\n descending: 'none',\n none: 'ascending',\n};\n\nexport class SortController<T extends object> {\n constructor(protected _state: StateController<T>) {}\n\n public state: SortState<T> = new Map();\n\n protected get host() {\n return this._state.host;\n }\n\n get #isMultipleSort() {\n return this.host.sortingOptions.mode === 'multiple';\n }\n\n #resolveSortOptions(column?: ColumnConfiguration<T>): Partial<SortingExpression<T>> {\n return {\n caseSensitive: column?.sortingCaseSensitive ?? false,\n comparer: column?.sortConfiguration?.comparer,\n } as Partial<SortingExpression<T>>;\n }\n\n #createDefaultExpression(key: Keys<T>) {\n const column = this.host.getColumn(key);\n\n return {\n key,\n direction: 'ascending',\n ...this.#resolveSortOptions(column),\n } as SortingExpression<T>;\n }\n\n #emitSortingEvent(detail: SortingExpression<T>) {\n return this.host.emitEvent('sorting', { detail, cancelable: true });\n }\n\n #emitSortedEvent(detail: SortingExpression<T>) {\n return this.host.emitEvent('sorted', { detail });\n }\n\n #setExpression(expression: SortingExpression<T>) {\n if (expression.direction === 'none') {\n this.reset(expression.key);\n return;\n }\n\n this.state.set(expression.key, { ...expression });\n }\n\n public async sortFromHeaderClick(column: ColumnConfiguration<T>) {\n const expression = this.prepareExpression(column);\n\n if (!this.#emitSortingEvent(expression)) {\n return;\n }\n\n if (!this.#isMultipleSort) {\n this.reset();\n }\n\n this._sort(expression);\n\n await this.host._pipelineComplete;\n this.#emitSortedEvent(expression);\n }\n\n /**\n * Returns the expression the next sort operation would apply for `column`.\n *\n * @remarks\n * A candidate copy. The stored state changes only on commit through\n * {@link SortController._sort}.\n */\n public prepareExpression(column: ColumnConfiguration<T>): SortingExpression<T> {\n const expr = this.state.get(column.field);\n\n if (!expr) {\n return this.#createDefaultExpression(column.field);\n }\n\n return {\n ...expr,\n direction: NEXT_DIRECTION[expr.direction],\n ...this.#resolveSortOptions(column),\n };\n }\n\n public reset(key?: Keys<T>) {\n if (key === undefined) {\n this.state.clear();\n } else {\n this.state.delete(key);\n }\n\n // Headers render the sort indicator from this state.\n this._state.updateObservers();\n }\n\n protected _sort(expressions: SortingExpression<T> | SortingExpression<T>[]) {\n for (const expr of asArray(expressions)) {\n this.#setExpression(expr);\n }\n\n this._state.updateObservers();\n this.host.requestUpdate(PIPELINE);\n }\n\n public sort(expressions: SortingExpression<T> | SortingExpression<T>[]) {\n // Merge into copies. Only `#setExpression` replaces the stored expression.\n this._sort(\n asArray(expressions).map((expr) => ({\n ...(this.state.get(expr.key) ?? this.#createDefaultExpression(expr.key)),\n ...expr,\n }))\n );\n }\n}\n"]}
@@ -16,7 +16,6 @@ import { SortController } from './sort.js';
16
16
  declare class StateController<T extends object> {
17
17
  private _columns;
18
18
  private readonly _observersCallback;
19
- /** The grid host element. */
20
19
  readonly host: GridHost<T>;
21
20
  readonly sorting: SortController<T>;
22
21
  readonly filtering: FilterController<T>;
@@ -28,22 +27,12 @@ declare class StateController<T extends object> {
28
27
  /** Sets the currently active node in the grid. */
29
28
  set active(node: ActiveNode<T>);
30
29
  constructor(host: GridHost<T>, dom: GridDOMController<T>, observersCallback: () => void);
31
- /**
32
- * Notifies the state context consumers (headers, header row, filter row) that
33
- * their render inputs changed. Called at the mutation points only, never on
34
- * each host update.
35
- */
30
+ /** Pings the state consumers (headers, header row, filter row). Call at mutation points only. */
36
31
  updateObservers(): void;
37
32
  setColumnConfiguration(columns: ColumnConfiguration<T>[]): void;
38
33
  setAutoColumnConfiguration(): void;
39
34
  updateColumnsConfiguration(config: ColumnConfiguration<T>[]): void;
40
- /**
41
- * Replaces the width of a single column with a new configuration object.
42
- *
43
- * @remarks
44
- * Width has no effect on the data pipeline, so no pipeline run is scheduled.
45
- * Resize drags call this on each pointer move.
46
- */
35
+ /** Replaces one column's width. Skips the pipeline: resize drags call this per pointer move. */
47
36
  setColumnWidth(field: Keys<T>, width: string): void;
48
37
  }
49
38
  declare function createStateController<T extends object>(host: GridHost<T>, dom: GridDOMController<T>, observersCallback: () => void): StateController<T>;