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.
- package/components/cell.d.ts +5 -17
- package/components/cell.js +2 -2
- package/components/cell.js.map +1 -1
- package/components/filter-row.d.ts +2 -5
- package/components/filter-row.js +22 -27
- package/components/filter-row.js.map +1 -1
- package/components/grid.d.ts +10 -15
- package/components/grid.js +33 -36
- package/components/grid.js.map +1 -1
- package/components/header-row.js +2 -1
- package/components/header-row.js.map +1 -1
- package/components/header.js +2 -6
- package/components/header.js.map +1 -1
- package/components/row.d.ts +0 -3
- package/components/row.js +3 -3
- package/components/row.js.map +1 -1
- package/controllers/data-operation.d.ts +1 -3
- package/controllers/data-operation.js +0 -2
- package/controllers/data-operation.js.map +1 -1
- package/controllers/dom.d.ts +4 -16
- package/controllers/dom.js +14 -13
- package/controllers/dom.js.map +1 -1
- package/controllers/filter.d.ts +10 -22
- package/controllers/filter.js +14 -8
- package/controllers/filter.js.map +1 -1
- package/controllers/navigation.d.ts +4 -9
- package/controllers/navigation.js +23 -43
- package/controllers/navigation.js.map +1 -1
- package/controllers/resize.d.ts +3 -9
- package/controllers/resize.js +2 -7
- package/controllers/resize.js.map +1 -1
- package/controllers/sort.d.ts +3 -5
- package/controllers/sort.js +19 -14
- package/controllers/sort.js.map +1 -1
- package/controllers/state.d.ts +2 -13
- package/controllers/state.js +12 -10
- package/controllers/state.js.map +1 -1
- package/custom-elements.json +201 -350
- package/docs/igniteui-grid-lite.json +849 -859
- package/internal/a11y.d.ts +2 -5
- package/internal/a11y.js.map +1 -1
- package/internal/body-focus.d.ts +6 -0
- package/internal/body-focus.js +46 -0
- package/internal/body-focus.js.map +1 -0
- package/internal/constants.d.ts +4 -1
- package/internal/constants.js +2 -0
- package/internal/constants.js.map +1 -1
- package/internal/element-from-event-path.d.ts +1 -1
- package/internal/element-from-event-path.js +2 -9
- package/internal/element-from-event-path.js.map +1 -1
- package/internal/tags.d.ts +0 -1
- package/internal/tags.js +0 -1
- package/internal/tags.js.map +1 -1
- package/internal/types.d.ts +6 -7
- package/internal/types.js.map +1 -1
- package/internal/utils.d.ts +4 -8
- package/internal/utils.js +7 -3
- package/internal/utils.js.map +1 -1
- package/operations/base.js +2 -2
- package/operations/base.js.map +1 -1
- package/operations/filter/state.d.ts +0 -1
- package/operations/filter/state.js +0 -3
- package/operations/filter/state.js.map +1 -1
- package/operations/filter/types.d.ts +1 -1
- package/operations/filter/types.js.map +1 -1
- package/operations/filter.js +3 -2
- package/operations/filter.js.map +1 -1
- package/operations/sort/types.d.ts +1 -1
- package/operations/sort/types.js.map +1 -1
- package/operations/sort.js +5 -1
- package/operations/sort.js.map +1 -1
- package/package.json +2 -3
- package/styles/header-cell/header-cell.css.js +1 -1
- package/styles/header-cell/header-cell.css.js.map +1 -1
- package/styles/themes/filtering-row-themes.js +2 -30
- package/styles/themes/filtering-row-themes.js.map +1 -1
- package/styles/themes/grid-header-themes.js +2 -20
- package/styles/themes/grid-header-themes.js.map +1 -1
- package/styles/themes/grid-themes.js +10 -31
- package/styles/themes/grid-themes.js.map +1 -1
- package/styles/themes/grid.base.css.js +1 -1
- package/styles/themes/grid.base.css.js.map +1 -1
- package/styles/themes/shared/grid.common.css.js +1 -1
- package/styles/themes/shared/grid.common.css.js.map +1 -1
- package/components/virtualizer.d.ts +0 -14
- package/components/virtualizer.js +0 -49
- package/components/virtualizer.js.map +0 -1
- package/internal/virt-flow-layout.d.ts +0 -4
- package/internal/virt-flow-layout.js +0 -15
- package/internal/virt-flow-layout.js.map +0 -1
package/controllers/filter.d.ts
CHANGED
|
@@ -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
|
-
|
|
9
|
-
export
|
|
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
|
|
24
|
+
* Emits `filtering` for `expression` and, if not canceled, applies it.
|
|
27
25
|
*
|
|
28
26
|
* @remarks
|
|
29
|
-
* `
|
|
30
|
-
*
|
|
31
|
-
*
|
|
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 {};
|
package/controllers/filter.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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, (
|
|
10
|
-
protected get _virtualizer(): import("
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
59
|
-
ArrowDown
|
|
60
|
-
ArrowUp
|
|
61
|
-
ArrowLeft
|
|
62
|
-
ArrowRight
|
|
63
|
-
Home
|
|
64
|
-
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 =
|
|
75
|
+
const clamped = clamp(row, 0, this._state.host.totalItems - 1);
|
|
71
76
|
this.active = Object.assign(this.nextNode, { row: clamped });
|
|
72
|
-
this._virtualizer?.
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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"]}
|
package/controllers/resize.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { nothing
|
|
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>
|
|
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
|
}
|
package/controllers/resize.js
CHANGED
|
@@ -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
|
|
17
|
-
|
|
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,
|
|
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"]}
|
package/controllers/sort.d.ts
CHANGED
|
@@ -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>
|
|
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
|
-
*
|
|
17
|
-
*
|
|
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
|
}
|
package/controllers/sort.js
CHANGED
|
@@ -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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
|
63
|
+
return {
|
|
64
|
+
...expr,
|
|
65
|
+
direction: NEXT_DIRECTION[expr.direction],
|
|
66
|
+
...this.#resolveSortOptions(column),
|
|
67
|
+
};
|
|
67
68
|
}
|
|
68
69
|
reset(key) {
|
|
69
|
-
key
|
|
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
|
package/controllers/sort.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sort.js","sourceRoot":"","sources":["../../src/controllers/sort.ts"],"names":[],"mappings":"
|
|
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"]}
|
package/controllers/state.d.ts
CHANGED
|
@@ -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>;
|