@toolbox-web/grid 1.21.1 → 1.22.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/README.md +6 -8
- package/all.js +2 -6850
- package/all.js.map +1 -1
- package/index.js +1 -4352
- package/index.js.map +1 -1
- package/lib/core/grid.d.ts +75 -8
- package/lib/core/grid.d.ts.map +1 -1
- package/lib/core/internal/rows.d.ts.map +1 -1
- package/lib/core/plugin/base-plugin.d.ts +8 -2
- package/lib/core/plugin/base-plugin.d.ts.map +1 -1
- package/lib/core/styles/index.d.ts.map +1 -1
- package/lib/core/types.d.ts +84 -2
- package/lib/core/types.d.ts.map +1 -1
- package/lib/plugins/clipboard/index.js +1 -733
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/clipboard/types.d.ts +5 -0
- package/lib/plugins/clipboard/types.d.ts.map +1 -1
- package/lib/plugins/column-virtualization/index.js +1 -560
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/column-virtualization/types.d.ts +5 -0
- package/lib/plugins/column-virtualization/types.d.ts.map +1 -1
- package/lib/plugins/context-menu/index.js +1 -754
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/context-menu/types.d.ts +5 -0
- package/lib/plugins/context-menu/types.d.ts.map +1 -1
- package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
- package/lib/plugins/editing/index.d.ts +1 -1
- package/lib/plugins/editing/index.d.ts.map +1 -1
- package/lib/plugins/editing/index.js +1 -1539
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/editing/types.d.ts +49 -0
- package/lib/plugins/editing/types.d.ts.map +1 -1
- package/lib/plugins/export/index.js +1 -589
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/export/types.d.ts +5 -0
- package/lib/plugins/export/types.d.ts.map +1 -1
- package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
- package/lib/plugins/filtering/filter-model.d.ts.map +1 -1
- package/lib/plugins/filtering/index.js +1 -1283
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/filtering/types.d.ts +7 -2
- package/lib/plugins/filtering/types.d.ts.map +1 -1
- package/lib/plugins/grouping-columns/index.js +1 -726
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-columns/types.d.ts +3 -0
- package/lib/plugins/grouping-columns/types.d.ts.map +1 -1
- package/lib/plugins/grouping-rows/index.js +2 -905
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/grouping-rows/types.d.ts +5 -0
- package/lib/plugins/grouping-rows/types.d.ts.map +1 -1
- package/lib/plugins/master-detail/index.js +1 -950
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/master-detail/types.d.ts +5 -0
- package/lib/plugins/master-detail/types.d.ts.map +1 -1
- package/lib/plugins/multi-sort/index.js +1 -553
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/multi-sort/types.d.ts +5 -0
- package/lib/plugins/multi-sort/types.d.ts.map +1 -1
- package/lib/plugins/pinned-columns/index.js +1 -688
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-columns/types.d.ts +3 -0
- package/lib/plugins/pinned-columns/types.d.ts.map +1 -1
- package/lib/plugins/pinned-rows/index.js +1 -704
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pinned-rows/types.d.ts +5 -0
- package/lib/plugins/pinned-rows/types.d.ts.map +1 -1
- package/lib/plugins/pivot/index.js +1 -1191
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/pivot/types.d.ts +5 -0
- package/lib/plugins/pivot/types.d.ts.map +1 -1
- package/lib/plugins/print/index.js +1 -691
- package/lib/plugins/print/index.js.map +1 -1
- package/lib/plugins/print/types.d.ts +3 -0
- package/lib/plugins/print/types.d.ts.map +1 -1
- package/lib/plugins/reorder/index.js +1 -703
- package/lib/plugins/reorder/index.js.map +1 -1
- package/lib/plugins/reorder/types.d.ts +5 -0
- package/lib/plugins/reorder/types.d.ts.map +1 -1
- package/lib/plugins/responsive/index.js +1 -971
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/responsive/types.d.ts +5 -0
- package/lib/plugins/responsive/types.d.ts.map +1 -1
- package/lib/plugins/row-reorder/index.js +1 -728
- package/lib/plugins/row-reorder/index.js.map +1 -1
- package/lib/plugins/row-reorder/types.d.ts +5 -0
- package/lib/plugins/row-reorder/types.d.ts.map +1 -1
- package/lib/plugins/selection/index.js +1 -1071
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/selection/types.d.ts +3 -0
- package/lib/plugins/selection/types.d.ts.map +1 -1
- package/lib/plugins/server-side/index.js +1 -521
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/server-side/types.d.ts +5 -0
- package/lib/plugins/server-side/types.d.ts.map +1 -1
- package/lib/plugins/tree/index.js +1 -686
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/tree/types.d.ts +5 -0
- package/lib/plugins/tree/types.d.ts.map +1 -1
- package/lib/plugins/undo-redo/index.js +1 -584
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/undo-redo/types.d.ts +5 -0
- package/lib/plugins/undo-redo/types.d.ts.map +1 -1
- package/lib/plugins/visibility/index.js +1 -792
- package/lib/plugins/visibility/index.js.map +1 -1
- package/lib/plugins/visibility/types.d.ts +5 -0
- package/lib/plugins/visibility/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/umd/grid.all.umd.js +1 -186
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +1 -90
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/clipboard.umd.js +1 -6
- package/umd/plugins/clipboard.umd.js.map +1 -1
- package/umd/plugins/column-virtualization.umd.js +1 -1
- package/umd/plugins/column-virtualization.umd.js.map +1 -1
- package/umd/plugins/context-menu.umd.js +1 -1
- package/umd/plugins/context-menu.umd.js.map +1 -1
- package/umd/plugins/editing.umd.js +1 -1
- package/umd/plugins/editing.umd.js.map +1 -1
- package/umd/plugins/export.umd.js +1 -13
- package/umd/plugins/export.umd.js.map +1 -1
- package/umd/plugins/filtering.umd.js +1 -1
- package/umd/plugins/filtering.umd.js.map +1 -1
- package/umd/plugins/grouping-columns.umd.js +1 -1
- package/umd/plugins/grouping-columns.umd.js.map +1 -1
- package/umd/plugins/grouping-rows.umd.js +1 -4
- package/umd/plugins/grouping-rows.umd.js.map +1 -1
- package/umd/plugins/master-detail.umd.js +1 -1
- package/umd/plugins/master-detail.umd.js.map +1 -1
- package/umd/plugins/multi-sort.umd.js +1 -1
- package/umd/plugins/multi-sort.umd.js.map +1 -1
- package/umd/plugins/pinned-columns.umd.js +1 -1
- package/umd/plugins/pinned-columns.umd.js.map +1 -1
- package/umd/plugins/pinned-rows.umd.js +1 -1
- package/umd/plugins/pinned-rows.umd.js.map +1 -1
- package/umd/plugins/pivot.umd.js +1 -1
- package/umd/plugins/pivot.umd.js.map +1 -1
- package/umd/plugins/print.umd.js +1 -75
- package/umd/plugins/print.umd.js.map +1 -1
- package/umd/plugins/reorder.umd.js +1 -1
- package/umd/plugins/reorder.umd.js.map +1 -1
- package/umd/plugins/responsive.umd.js +1 -1
- package/umd/plugins/responsive.umd.js.map +1 -1
- package/umd/plugins/row-reorder.umd.js +1 -1
- package/umd/plugins/row-reorder.umd.js.map +1 -1
- package/umd/plugins/selection.umd.js +1 -3
- package/umd/plugins/selection.umd.js.map +1 -1
- package/umd/plugins/server-side.umd.js +1 -1
- package/umd/plugins/server-side.umd.js.map +1 -1
- package/umd/plugins/tree.umd.js +1 -1
- package/umd/plugins/tree.umd.js.map +1 -1
- package/umd/plugins/undo-redo.umd.js +1 -1
- package/umd/plugins/undo-redo.umd.js.map +1 -1
- package/umd/plugins/visibility.umd.js +1 -1
- package/umd/plugins/visibility.umd.js.map +1 -1
|
@@ -1,906 +1,3 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
collapse: "▼",
|
|
4
|
-
sortAsc: "▲",
|
|
5
|
-
sortDesc: "▼",
|
|
6
|
-
sortNone: "⇅",
|
|
7
|
-
submenuArrow: "▶",
|
|
8
|
-
dragHandle: "⋮⋮",
|
|
9
|
-
toolPanel: "☰",
|
|
10
|
-
filter: x,
|
|
11
|
-
filterActive: x,
|
|
12
|
-
print: "🖨️"
|
|
13
|
-
};
|
|
14
|
-
class C {
|
|
15
|
-
/**
|
|
16
|
-
* Plugin dependencies - declare other plugins this one requires.
|
|
17
|
-
*
|
|
18
|
-
* Dependencies are validated when the plugin is attached.
|
|
19
|
-
* Required dependencies throw an error if missing.
|
|
20
|
-
* Optional dependencies log an info message if missing.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```typescript
|
|
24
|
-
* static readonly dependencies: PluginDependency[] = [
|
|
25
|
-
* { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },
|
|
26
|
-
* { name: 'selection', required: false, reason: 'Enables selection-based undo' },
|
|
27
|
-
* ];
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
static dependencies;
|
|
31
|
-
/**
|
|
32
|
-
* Plugin manifest - declares owned properties, config rules, and hook priorities.
|
|
33
|
-
*
|
|
34
|
-
* This is read by the configuration validator to:
|
|
35
|
-
* - Validate that required plugins are loaded when their properties are used
|
|
36
|
-
* - Execute configRules to detect invalid/conflicting settings
|
|
37
|
-
* - Order hook execution based on priority
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```typescript
|
|
41
|
-
* static override readonly manifest: PluginManifest<MyConfig> = {
|
|
42
|
-
* ownedProperties: [
|
|
43
|
-
* { property: 'myProp', level: 'column', description: 'the "myProp" column property' },
|
|
44
|
-
* ],
|
|
45
|
-
* configRules: [
|
|
46
|
-
* { id: 'myPlugin/conflict', severity: 'warn', message: '...', check: (c) => c.a && c.b },
|
|
47
|
-
* ],
|
|
48
|
-
* };
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
-
static manifest;
|
|
53
|
-
/**
|
|
54
|
-
* Plugin version - defaults to grid version for built-in plugins.
|
|
55
|
-
* Third-party plugins can override with their own semver.
|
|
56
|
-
*/
|
|
57
|
-
version = typeof __GRID_VERSION__ < "u" ? __GRID_VERSION__ : "dev";
|
|
58
|
-
/** CSS styles to inject into the grid's shadow DOM */
|
|
59
|
-
styles;
|
|
60
|
-
/** Custom cell renderers keyed by type name */
|
|
61
|
-
cellRenderers;
|
|
62
|
-
/** Custom header renderers keyed by type name */
|
|
63
|
-
headerRenderers;
|
|
64
|
-
/** Custom cell editors keyed by type name */
|
|
65
|
-
cellEditors;
|
|
66
|
-
/** The grid instance this plugin is attached to */
|
|
67
|
-
grid;
|
|
68
|
-
/** Plugin configuration - merged with defaults in attach() */
|
|
69
|
-
config;
|
|
70
|
-
/** User-provided configuration from constructor */
|
|
71
|
-
userConfig;
|
|
72
|
-
/**
|
|
73
|
-
* Plugin-level AbortController for event listener cleanup.
|
|
74
|
-
* Created fresh in attach(), aborted in detach().
|
|
75
|
-
* This ensures event listeners are properly cleaned up when plugins are re-attached.
|
|
76
|
-
*/
|
|
77
|
-
#e;
|
|
78
|
-
/**
|
|
79
|
-
* Default configuration - subclasses should override this getter.
|
|
80
|
-
* Note: This must be a getter (not property initializer) for proper inheritance
|
|
81
|
-
* since property initializers run after parent constructor.
|
|
82
|
-
*/
|
|
83
|
-
get defaultConfig() {
|
|
84
|
-
return {};
|
|
85
|
-
}
|
|
86
|
-
constructor(e = {}) {
|
|
87
|
-
this.userConfig = e;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Called when the plugin is attached to a grid.
|
|
91
|
-
* Override to set up event listeners, initialize state, etc.
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* ```ts
|
|
95
|
-
* attach(grid: GridElement): void {
|
|
96
|
-
* super.attach(grid);
|
|
97
|
-
* // Set up document-level listeners with auto-cleanup
|
|
98
|
-
* document.addEventListener('keydown', this.handleEscape, {
|
|
99
|
-
* signal: this.disconnectSignal
|
|
100
|
-
* });
|
|
101
|
-
* }
|
|
102
|
-
* ```
|
|
103
|
-
*/
|
|
104
|
-
attach(e) {
|
|
105
|
-
this.#e?.abort(), this.#e = new AbortController(), this.grid = e, this.config = { ...this.defaultConfig, ...this.userConfig };
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Called when the plugin is detached from a grid.
|
|
109
|
-
* Override to clean up event listeners, timers, etc.
|
|
110
|
-
*
|
|
111
|
-
* @example
|
|
112
|
-
* ```ts
|
|
113
|
-
* detach(): void {
|
|
114
|
-
* // Clean up any state not handled by disconnectSignal
|
|
115
|
-
* this.selectedRows.clear();
|
|
116
|
-
* this.cache = null;
|
|
117
|
-
* }
|
|
118
|
-
* ```
|
|
119
|
-
*/
|
|
120
|
-
detach() {
|
|
121
|
-
this.#e?.abort(), this.#e = void 0;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Get another plugin instance from the same grid.
|
|
125
|
-
* Use for inter-plugin communication.
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```ts
|
|
129
|
-
* const selection = this.getPlugin(SelectionPlugin);
|
|
130
|
-
* if (selection) {
|
|
131
|
-
* const selectedRows = selection.getSelectedRows();
|
|
132
|
-
* }
|
|
133
|
-
* ```
|
|
134
|
-
*/
|
|
135
|
-
getPlugin(e) {
|
|
136
|
-
return this.grid?.getPlugin(e);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Emit a custom event from the grid.
|
|
140
|
-
*/
|
|
141
|
-
emit(e, t) {
|
|
142
|
-
this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: t, bubbles: !0 }));
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Emit a cancelable custom event from the grid.
|
|
146
|
-
* @returns `true` if the event was cancelled (preventDefault called), `false` otherwise
|
|
147
|
-
*/
|
|
148
|
-
emitCancelable(e, t) {
|
|
149
|
-
const r = new CustomEvent(e, { detail: t, bubbles: !0, cancelable: !0 });
|
|
150
|
-
return this.grid?.dispatchEvent?.(r), r.defaultPrevented;
|
|
151
|
-
}
|
|
152
|
-
// =========================================================================
|
|
153
|
-
// Event Bus - Plugin-to-Plugin Communication
|
|
154
|
-
// =========================================================================
|
|
155
|
-
/**
|
|
156
|
-
* Subscribe to an event from another plugin.
|
|
157
|
-
* The subscription is automatically cleaned up when this plugin is detached.
|
|
158
|
-
*
|
|
159
|
-
* @category Plugin Development
|
|
160
|
-
* @param eventType - The event type to listen for (e.g., 'filter-change')
|
|
161
|
-
* @param callback - The callback to invoke when the event is emitted
|
|
162
|
-
*
|
|
163
|
-
* @example
|
|
164
|
-
* ```typescript
|
|
165
|
-
* // In attach() or other initialization
|
|
166
|
-
* this.on('filter-change', (detail) => {
|
|
167
|
-
* console.log('Filter changed:', detail);
|
|
168
|
-
* });
|
|
169
|
-
* ```
|
|
170
|
-
*/
|
|
171
|
-
on(e, t) {
|
|
172
|
-
this.grid?._pluginManager?.subscribe(this, e, t);
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Unsubscribe from a plugin event.
|
|
176
|
-
*
|
|
177
|
-
* @category Plugin Development
|
|
178
|
-
* @param eventType - The event type to stop listening for
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* ```typescript
|
|
182
|
-
* this.off('filter-change');
|
|
183
|
-
* ```
|
|
184
|
-
*/
|
|
185
|
-
off(e) {
|
|
186
|
-
this.grid?._pluginManager?.unsubscribe(this, e);
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Emit an event to other plugins via the Event Bus.
|
|
190
|
-
* This is for inter-plugin communication only; it does NOT dispatch DOM events.
|
|
191
|
-
* Use `emit()` to dispatch DOM events that external consumers can listen to.
|
|
192
|
-
*
|
|
193
|
-
* @category Plugin Development
|
|
194
|
-
* @param eventType - The event type to emit (should be declared in manifest.events)
|
|
195
|
-
* @param detail - The event payload
|
|
196
|
-
*
|
|
197
|
-
* @example
|
|
198
|
-
* ```typescript
|
|
199
|
-
* // Emit to other plugins (not DOM)
|
|
200
|
-
* this.emitPluginEvent('filter-change', { field: 'name', value: 'Alice' });
|
|
201
|
-
*
|
|
202
|
-
* // For DOM events that consumers can addEventListener to:
|
|
203
|
-
* this.emit('filter-change', { field: 'name', value: 'Alice' });
|
|
204
|
-
* ```
|
|
205
|
-
*/
|
|
206
|
-
emitPluginEvent(e, t) {
|
|
207
|
-
this.grid?._pluginManager?.emitPluginEvent(e, t);
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Request a re-render of the grid.
|
|
211
|
-
* Uses ROWS phase - does NOT trigger processColumns hooks.
|
|
212
|
-
*/
|
|
213
|
-
requestRender() {
|
|
214
|
-
this.grid?.requestRender?.();
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Request a columns re-render of the grid.
|
|
218
|
-
* Uses COLUMNS phase - triggers processColumns hooks.
|
|
219
|
-
* Use this when your plugin needs to reprocess column configuration.
|
|
220
|
-
*/
|
|
221
|
-
requestColumnsRender() {
|
|
222
|
-
this.grid?.requestColumnsRender?.();
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Request a re-render and restore focus styling afterward.
|
|
226
|
-
* Use this when a plugin action (like expand/collapse) triggers a render
|
|
227
|
-
* but needs to maintain keyboard navigation focus.
|
|
228
|
-
*/
|
|
229
|
-
requestRenderWithFocus() {
|
|
230
|
-
this.grid?.requestRenderWithFocus?.();
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Request a lightweight style update without rebuilding DOM.
|
|
234
|
-
* Use this instead of requestRender() when only CSS classes need updating.
|
|
235
|
-
*/
|
|
236
|
-
requestAfterRender() {
|
|
237
|
-
this.grid?.requestAfterRender?.();
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Get the current rows from the grid.
|
|
241
|
-
*/
|
|
242
|
-
get rows() {
|
|
243
|
-
return this.grid?.rows ?? [];
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Get the original unfiltered/unprocessed rows from the grid.
|
|
247
|
-
* Use this when you need all source data regardless of active filters.
|
|
248
|
-
*/
|
|
249
|
-
get sourceRows() {
|
|
250
|
-
return this.grid?.sourceRows ?? [];
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Get the current columns from the grid.
|
|
254
|
-
*/
|
|
255
|
-
get columns() {
|
|
256
|
-
return this.grid?.columns ?? [];
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Get only visible columns from the grid (excludes hidden).
|
|
260
|
-
* Use this for rendering that needs to match the grid template.
|
|
261
|
-
*/
|
|
262
|
-
get visibleColumns() {
|
|
263
|
-
return this.grid?._visibleColumns ?? [];
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Get the grid as an HTMLElement for direct DOM operations.
|
|
267
|
-
* Use sparingly - prefer the typed GridElementRef API when possible.
|
|
268
|
-
*
|
|
269
|
-
* @example
|
|
270
|
-
* ```ts
|
|
271
|
-
* const width = this.gridElement.clientWidth;
|
|
272
|
-
* this.gridElement.classList.add('my-plugin-active');
|
|
273
|
-
* ```
|
|
274
|
-
*/
|
|
275
|
-
get gridElement() {
|
|
276
|
-
return this.grid;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Get the disconnect signal for event listener cleanup.
|
|
280
|
-
* This signal is aborted when the grid disconnects from the DOM.
|
|
281
|
-
* Use this when adding event listeners that should be cleaned up automatically.
|
|
282
|
-
*
|
|
283
|
-
* Best for:
|
|
284
|
-
* - Document/window-level listeners added in attach()
|
|
285
|
-
* - Listeners on the grid element itself
|
|
286
|
-
* - Any listener that should persist across renders
|
|
287
|
-
*
|
|
288
|
-
* Not needed for:
|
|
289
|
-
* - Listeners on elements created in afterRender() (removed with element)
|
|
290
|
-
*
|
|
291
|
-
* @example
|
|
292
|
-
* element.addEventListener('click', handler, { signal: this.disconnectSignal });
|
|
293
|
-
* document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
|
|
294
|
-
*/
|
|
295
|
-
get disconnectSignal() {
|
|
296
|
-
return this.#e?.signal ?? this.grid?.disconnectSignal;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Get the grid-level icons configuration.
|
|
300
|
-
* Returns merged icons (user config + defaults).
|
|
301
|
-
*/
|
|
302
|
-
get gridIcons() {
|
|
303
|
-
const e = this.grid?.gridConfig?.icons ?? {};
|
|
304
|
-
return { ...R, ...e };
|
|
305
|
-
}
|
|
306
|
-
// #region Animation Helpers
|
|
307
|
-
/**
|
|
308
|
-
* Check if animations are enabled at the grid level.
|
|
309
|
-
* Respects gridConfig.animation.mode and the CSS variable set by the grid.
|
|
310
|
-
*
|
|
311
|
-
* Plugins should use this to skip animations when:
|
|
312
|
-
* - Animation mode is 'off' or `false`
|
|
313
|
-
* - User prefers reduced motion and mode is 'reduced-motion' (default)
|
|
314
|
-
*
|
|
315
|
-
* @example
|
|
316
|
-
* ```ts
|
|
317
|
-
* private get animationStyle(): 'slide' | 'fade' | false {
|
|
318
|
-
* if (!this.isAnimationEnabled) return false;
|
|
319
|
-
* return this.config.animation ?? 'slide';
|
|
320
|
-
* }
|
|
321
|
-
* ```
|
|
322
|
-
*/
|
|
323
|
-
get isAnimationEnabled() {
|
|
324
|
-
const e = this.grid?.effectiveConfig?.animation?.mode ?? "reduced-motion";
|
|
325
|
-
if (e === !1 || e === "off") return !1;
|
|
326
|
-
if (e === !0 || e === "on") return !0;
|
|
327
|
-
const t = this.gridElement;
|
|
328
|
-
return t ? getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim() !== "0" : !0;
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Get the animation duration in milliseconds from CSS variable.
|
|
332
|
-
* Falls back to 200ms if not set.
|
|
333
|
-
*
|
|
334
|
-
* Plugins can use this for their animation timing to stay consistent
|
|
335
|
-
* with the grid-level animation.duration setting.
|
|
336
|
-
*
|
|
337
|
-
* @example
|
|
338
|
-
* ```ts
|
|
339
|
-
* element.animate(keyframes, { duration: this.animationDuration });
|
|
340
|
-
* ```
|
|
341
|
-
*/
|
|
342
|
-
get animationDuration() {
|
|
343
|
-
const e = this.gridElement;
|
|
344
|
-
if (e) {
|
|
345
|
-
const t = getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(), r = parseInt(t, 10);
|
|
346
|
-
if (!isNaN(r)) return r;
|
|
347
|
-
}
|
|
348
|
-
return 200;
|
|
349
|
-
}
|
|
350
|
-
// #endregion
|
|
351
|
-
/**
|
|
352
|
-
* Resolve an icon value to string or HTMLElement.
|
|
353
|
-
* Checks plugin config first, then grid-level icons, then defaults.
|
|
354
|
-
*
|
|
355
|
-
* @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')
|
|
356
|
-
* @param pluginOverride - Optional plugin-level override
|
|
357
|
-
* @returns The resolved icon value
|
|
358
|
-
*/
|
|
359
|
-
resolveIcon(e, t) {
|
|
360
|
-
return t !== void 0 ? t : this.gridIcons[e];
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Set an icon value on an element.
|
|
364
|
-
* Handles both string (text/HTML) and HTMLElement values.
|
|
365
|
-
*
|
|
366
|
-
* @param element - The element to set the icon on
|
|
367
|
-
* @param icon - The icon value (string or HTMLElement)
|
|
368
|
-
*/
|
|
369
|
-
setIcon(e, t) {
|
|
370
|
-
typeof t == "string" ? e.innerHTML = t : t instanceof HTMLElement && (e.innerHTML = "", e.appendChild(t.cloneNode(!0)));
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Log a warning message.
|
|
374
|
-
*/
|
|
375
|
-
warn(e) {
|
|
376
|
-
console.warn(`[tbw-grid:${this.name}] ${e}`);
|
|
377
|
-
}
|
|
378
|
-
// #endregion
|
|
379
|
-
}
|
|
380
|
-
const E = "__tbw_expander";
|
|
381
|
-
function A(n) {
|
|
382
|
-
return n.field === E;
|
|
383
|
-
}
|
|
384
|
-
const y = {
|
|
385
|
-
sum: (n, e) => n.reduce((t, r) => t + (Number(r[e]) || 0), 0),
|
|
386
|
-
avg: (n, e) => {
|
|
387
|
-
const t = n.reduce((r, i) => r + (Number(i[e]) || 0), 0);
|
|
388
|
-
return n.length ? t / n.length : 0;
|
|
389
|
-
},
|
|
390
|
-
count: (n) => n.length,
|
|
391
|
-
min: (n, e) => n.length ? Math.min(...n.map((t) => Number(t[e]) || 1 / 0)) : 0,
|
|
392
|
-
max: (n, e) => n.length ? Math.max(...n.map((t) => Number(t[e]) || -1 / 0)) : 0,
|
|
393
|
-
first: (n, e) => n[0]?.[e],
|
|
394
|
-
last: (n, e) => n[n.length - 1]?.[e]
|
|
395
|
-
}, b = /* @__PURE__ */ new Map(), w = {
|
|
396
|
-
/**
|
|
397
|
-
* Register a custom aggregator function.
|
|
398
|
-
*/
|
|
399
|
-
register(n, e) {
|
|
400
|
-
b.set(n, e);
|
|
401
|
-
},
|
|
402
|
-
/**
|
|
403
|
-
* Unregister a custom aggregator function.
|
|
404
|
-
*/
|
|
405
|
-
unregister(n) {
|
|
406
|
-
b.delete(n);
|
|
407
|
-
},
|
|
408
|
-
/**
|
|
409
|
-
* Get an aggregator function by reference.
|
|
410
|
-
*/
|
|
411
|
-
get(n) {
|
|
412
|
-
if (n !== void 0)
|
|
413
|
-
return typeof n == "function" ? n : b.get(n) ?? y[n];
|
|
414
|
-
},
|
|
415
|
-
/**
|
|
416
|
-
* Run an aggregator on a set of rows.
|
|
417
|
-
*/
|
|
418
|
-
run(n, e, t, r) {
|
|
419
|
-
const i = this.get(n);
|
|
420
|
-
return i ? i(e, t, r) : void 0;
|
|
421
|
-
},
|
|
422
|
-
/**
|
|
423
|
-
* Check if an aggregator exists.
|
|
424
|
-
*/
|
|
425
|
-
has(n) {
|
|
426
|
-
return b.has(n) || n in y;
|
|
427
|
-
},
|
|
428
|
-
/**
|
|
429
|
-
* List all available aggregator names.
|
|
430
|
-
*/
|
|
431
|
-
list() {
|
|
432
|
-
return [...Object.keys(y), ...b.keys()];
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
|
-
w.register.bind(w);
|
|
436
|
-
w.unregister.bind(w);
|
|
437
|
-
w.get.bind(w);
|
|
438
|
-
const _ = w.run.bind(w);
|
|
439
|
-
w.list.bind(w);
|
|
440
|
-
function v({ rows: n, config: e, expanded: t, initialExpanded: r }) {
|
|
441
|
-
const i = e.groupOn;
|
|
442
|
-
if (typeof i != "function")
|
|
443
|
-
return [];
|
|
444
|
-
const o = { key: "__root__", value: null, depth: -1, rows: [], children: /* @__PURE__ */ new Map() };
|
|
445
|
-
if (n.forEach((d) => {
|
|
446
|
-
let u = i(d);
|
|
447
|
-
u == null || u === !1 ? u = ["__ungrouped__"] : Array.isArray(u) || (u = [u]);
|
|
448
|
-
let a = o;
|
|
449
|
-
u.forEach((m, p) => {
|
|
450
|
-
const c = m == null ? "∅" : String(m), h = a.key === "__root__" ? c : a.key + "||" + c;
|
|
451
|
-
let f = a.children.get(c);
|
|
452
|
-
f || (f = { key: h, value: m, depth: p, rows: [], children: /* @__PURE__ */ new Map(), parent: a }, a.children.set(c, f)), a = f;
|
|
453
|
-
}), a.rows.push(d);
|
|
454
|
-
}), o.children.size === 1 && o.children.has("__ungrouped__") && o.children.get("__ungrouped__").rows.length === n.length)
|
|
455
|
-
return [];
|
|
456
|
-
const g = /* @__PURE__ */ new Set([...t, ...r ?? []]), s = [], l = (d) => {
|
|
457
|
-
if (d === o) {
|
|
458
|
-
d.children.forEach((a) => l(a));
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
const u = g.has(d.key);
|
|
462
|
-
s.push({
|
|
463
|
-
kind: "group",
|
|
464
|
-
key: d.key,
|
|
465
|
-
value: d.value,
|
|
466
|
-
depth: d.depth,
|
|
467
|
-
rows: d.rows,
|
|
468
|
-
expanded: u
|
|
469
|
-
}), u && (d.children.size ? d.children.forEach((a) => l(a)) : d.rows.forEach((a) => s.push({ kind: "data", row: a, rowIndex: n.indexOf(a) })));
|
|
470
|
-
};
|
|
471
|
-
return l(o), s;
|
|
472
|
-
}
|
|
473
|
-
function K(n, e) {
|
|
474
|
-
const t = new Set(n);
|
|
475
|
-
return t.has(e) ? t.delete(e) : t.add(e), t;
|
|
476
|
-
}
|
|
477
|
-
function S(n) {
|
|
478
|
-
const e = /* @__PURE__ */ new Set();
|
|
479
|
-
for (const t of n)
|
|
480
|
-
t.kind === "group" && e.add(t.key);
|
|
481
|
-
return e;
|
|
482
|
-
}
|
|
483
|
-
function k() {
|
|
484
|
-
return /* @__PURE__ */ new Set();
|
|
485
|
-
}
|
|
486
|
-
function G(n, e) {
|
|
487
|
-
if (n === !0)
|
|
488
|
-
return new Set(e);
|
|
489
|
-
if (n === !1 || n == null)
|
|
490
|
-
return /* @__PURE__ */ new Set();
|
|
491
|
-
if (typeof n == "number") {
|
|
492
|
-
const t = e[n];
|
|
493
|
-
return t ? /* @__PURE__ */ new Set([t]) : /* @__PURE__ */ new Set();
|
|
494
|
-
}
|
|
495
|
-
return typeof n == "string" ? /* @__PURE__ */ new Set([n]) : Array.isArray(n) ? new Set(n) : /* @__PURE__ */ new Set();
|
|
496
|
-
}
|
|
497
|
-
function T(n) {
|
|
498
|
-
return n.filter((e) => e.kind === "group").map((e) => e.key);
|
|
499
|
-
}
|
|
500
|
-
function I(n) {
|
|
501
|
-
return n.kind !== "group" ? 0 : n.rows.length;
|
|
502
|
-
}
|
|
503
|
-
const N = "@layer tbw-plugins{.group-row{display:grid;grid-template-columns:var(--tbw-column-template);background:var(--tbw-grouping-rows-bg, var(--tbw-color-panel-bg));font-weight:500;border-bottom:var(--tbw-row-divider);min-height:var(--tbw-row-height)}.group-row .cell{display:flex;align-items:center;padding:var(--tbw-cell-padding, .125rem .5rem)}.group-row:hover{background:var(--tbw-grouping-rows-bg-hover, var(--tbw-color-row-hover))}.group-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;display:inline-flex;align-items:center;justify-content:center;width:var(--tbw-toggle-size, 1.25rem);height:var(--tbw-toggle-size, 1.25rem);margin-right:.25rem;background:none;border:0;font:inherit}.group-toggle:hover{background:var(--tbw-grouping-rows-toggle-hover, var(--tbw-color-row-hover));border-radius:var(--tbw-border-radius, .125rem)}.group-label{display:inline-flex;align-items:center;gap:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem))}.group-count{color:var(--tbw-grouping-rows-count-color, var(--tbw-color-fg-muted));font-size:var(--tbw-font-size-xs, .85em);font-weight:400}.group-aggregates{display:inline-flex;align-items:center;gap:var(--tbw-spacing-lg, 1rem);margin-left:var(--tbw-spacing-lg, 1rem);font-weight:400;font-size:var(--tbw-font-size-sm, .875em);color:var(--tbw-grouping-rows-aggregate-color, var(--tbw-color-fg-muted))}.group-aggregate{white-space:nowrap}.group-row{padding-left:calc(var(--tbw-group-depth, 0) * var(--tbw-group-indent-width, 1.25em))}.data-grid-row.tbw-group-slide-in{animation:tbw-group-slide-in var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-group-slide-in{0%{opacity:0;transform:translate(-8px)}to{opacity:1;transform:translate(0)}}.data-grid-row.tbw-group-fade-in{animation:tbw-group-fade-in var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-group-fade-in{0%{opacity:0}to{opacity:1}}}";
|
|
504
|
-
class D extends C {
|
|
505
|
-
/**
|
|
506
|
-
* Plugin manifest - declares configuration validation rules and events.
|
|
507
|
-
* @internal
|
|
508
|
-
*/
|
|
509
|
-
static manifest = {
|
|
510
|
-
events: [
|
|
511
|
-
{
|
|
512
|
-
type: "grouping-state-change",
|
|
513
|
-
description: "Emitted when groups are expanded/collapsed. Subscribers can react to row visibility changes."
|
|
514
|
-
}
|
|
515
|
-
],
|
|
516
|
-
queries: [
|
|
517
|
-
{
|
|
518
|
-
type: "canMoveRow",
|
|
519
|
-
description: "Returns false for group header rows (cannot be reordered)"
|
|
520
|
-
}
|
|
521
|
-
],
|
|
522
|
-
configRules: [
|
|
523
|
-
{
|
|
524
|
-
id: "groupingRows/accordion-defaultExpanded",
|
|
525
|
-
severity: "warn",
|
|
526
|
-
message: `"accordion: true" and "defaultExpanded" (non-false) are used together.
|
|
527
|
-
→ In accordion mode, only one group can be open at a time.
|
|
528
|
-
→ Using defaultExpanded with multiple groups will collapse to one on first toggle.
|
|
529
|
-
→ Consider using "defaultExpanded: false" or a single group key/index with accordion mode.`,
|
|
530
|
-
check: (e) => e.accordion === !0 && e.defaultExpanded !== !1 && e.defaultExpanded !== void 0 && typeof e.defaultExpanded != "number" && typeof e.defaultExpanded != "string" && // Warn if true or array with multiple items
|
|
531
|
-
(e.defaultExpanded === !0 || Array.isArray(e.defaultExpanded) && e.defaultExpanded.length > 1)
|
|
532
|
-
}
|
|
533
|
-
]
|
|
534
|
-
};
|
|
535
|
-
/** @internal */
|
|
536
|
-
name = "groupingRows";
|
|
537
|
-
/** @internal */
|
|
538
|
-
styles = N;
|
|
539
|
-
/** @internal */
|
|
540
|
-
get defaultConfig() {
|
|
541
|
-
return {
|
|
542
|
-
defaultExpanded: !1,
|
|
543
|
-
showRowCount: !0,
|
|
544
|
-
indentWidth: 20,
|
|
545
|
-
aggregators: {},
|
|
546
|
-
animation: "slide",
|
|
547
|
-
accordion: !1
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
// #region Internal State
|
|
551
|
-
expandedKeys = /* @__PURE__ */ new Set();
|
|
552
|
-
flattenedRows = [];
|
|
553
|
-
isActive = !1;
|
|
554
|
-
previousVisibleKeys = /* @__PURE__ */ new Set();
|
|
555
|
-
keysToAnimate = /* @__PURE__ */ new Set();
|
|
556
|
-
/** Track if initial defaultExpanded has been applied */
|
|
557
|
-
hasAppliedDefaultExpanded = !1;
|
|
558
|
-
// #endregion
|
|
559
|
-
// #region Animation
|
|
560
|
-
/**
|
|
561
|
-
* Get expand/collapse animation style from plugin config.
|
|
562
|
-
* Uses base class isAnimationEnabled to respect grid-level settings.
|
|
563
|
-
*/
|
|
564
|
-
get animationStyle() {
|
|
565
|
-
return this.isAnimationEnabled ? this.config.animation ?? "slide" : !1;
|
|
566
|
-
}
|
|
567
|
-
// #endregion
|
|
568
|
-
// #region Lifecycle
|
|
569
|
-
/** @internal */
|
|
570
|
-
detach() {
|
|
571
|
-
this.expandedKeys.clear(), this.flattenedRows = [], this.isActive = !1, this.previousVisibleKeys.clear(), this.keysToAnimate.clear(), this.hasAppliedDefaultExpanded = !1;
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Provide row height for group header rows.
|
|
575
|
-
*
|
|
576
|
-
* If `groupRowHeight` is configured, returns that value for group rows.
|
|
577
|
-
* This allows the variable row height system to use known heights for
|
|
578
|
-
* group headers without needing to measure them from the DOM.
|
|
579
|
-
*
|
|
580
|
-
* @param row - The row object (may be a group row)
|
|
581
|
-
* @param _index - Index in the processed rows array (unused)
|
|
582
|
-
* @returns Height in pixels for group rows, undefined for data rows
|
|
583
|
-
*
|
|
584
|
-
* @internal Plugin hook for variable row height support
|
|
585
|
-
*/
|
|
586
|
-
getRowHeight(e, t) {
|
|
587
|
-
if (this.config.groupRowHeight != null && e.__isGroupRow === !0)
|
|
588
|
-
return this.config.groupRowHeight;
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* Handle plugin queries.
|
|
592
|
-
* @internal
|
|
593
|
-
*/
|
|
594
|
-
handleQuery(e) {
|
|
595
|
-
if (e.type === "canMoveRow" && e.context?.__isGroupRow === !0)
|
|
596
|
-
return !1;
|
|
597
|
-
}
|
|
598
|
-
// #endregion
|
|
599
|
-
// #region Hooks
|
|
600
|
-
/**
|
|
601
|
-
* Auto-detect grouping configuration from grid config.
|
|
602
|
-
* Called by plugin system to determine if plugin should activate.
|
|
603
|
-
*/
|
|
604
|
-
static detect(e, t) {
|
|
605
|
-
return typeof t?.groupOn == "function" || typeof t?.enableRowGrouping == "boolean";
|
|
606
|
-
}
|
|
607
|
-
/** @internal */
|
|
608
|
-
processRows(e) {
|
|
609
|
-
const t = this.config;
|
|
610
|
-
if (typeof t.groupOn != "function")
|
|
611
|
-
return this.isActive = !1, this.flattenedRows = [], [...e];
|
|
612
|
-
const r = v({
|
|
613
|
-
rows: [...e],
|
|
614
|
-
config: t,
|
|
615
|
-
expanded: /* @__PURE__ */ new Set()
|
|
616
|
-
// Empty to get all root groups
|
|
617
|
-
});
|
|
618
|
-
if (r.length === 0)
|
|
619
|
-
return this.isActive = !1, this.flattenedRows = [], [...e];
|
|
620
|
-
let i;
|
|
621
|
-
if (!this.hasAppliedDefaultExpanded && this.expandedKeys.size === 0 && t.defaultExpanded !== !1) {
|
|
622
|
-
const s = T(r);
|
|
623
|
-
i = G(t.defaultExpanded ?? !1, s), i.size > 0 && (this.expandedKeys = new Set(i), this.hasAppliedDefaultExpanded = !0);
|
|
624
|
-
}
|
|
625
|
-
const o = v({
|
|
626
|
-
rows: [...e],
|
|
627
|
-
config: t,
|
|
628
|
-
expanded: this.expandedKeys,
|
|
629
|
-
initialExpanded: i
|
|
630
|
-
});
|
|
631
|
-
this.isActive = !0, this.flattenedRows = o, this.keysToAnimate.clear();
|
|
632
|
-
const g = /* @__PURE__ */ new Set();
|
|
633
|
-
return o.forEach((s, l) => {
|
|
634
|
-
if (s.kind === "data") {
|
|
635
|
-
const d = `data-${l}`;
|
|
636
|
-
g.add(d), this.previousVisibleKeys.has(d) || this.keysToAnimate.add(d);
|
|
637
|
-
}
|
|
638
|
-
}), this.previousVisibleKeys = g, o.map((s) => s.kind === "group" ? {
|
|
639
|
-
__isGroupRow: !0,
|
|
640
|
-
__groupKey: s.key,
|
|
641
|
-
__groupValue: s.value,
|
|
642
|
-
__groupDepth: s.depth,
|
|
643
|
-
__groupRows: s.rows,
|
|
644
|
-
__groupExpanded: s.expanded,
|
|
645
|
-
__groupRowCount: I(s),
|
|
646
|
-
// Cache key for variable row height support - survives expand/collapse
|
|
647
|
-
__rowCacheKey: `group:${s.key}`
|
|
648
|
-
} : s.row);
|
|
649
|
-
}
|
|
650
|
-
/** @internal */
|
|
651
|
-
onCellClick(e) {
|
|
652
|
-
const t = e.row;
|
|
653
|
-
if (t?.__isGroupRow && e.originalEvent.target?.closest(".group-toggle"))
|
|
654
|
-
return this.toggle(t.__groupKey), !0;
|
|
655
|
-
}
|
|
656
|
-
/** @internal */
|
|
657
|
-
onKeyDown(e) {
|
|
658
|
-
if (e.key !== " ") return;
|
|
659
|
-
const t = this.grid._focusRow, r = this.rows[t];
|
|
660
|
-
if (r?.__isGroupRow)
|
|
661
|
-
return e.preventDefault(), this.toggle(r.__groupKey), this.requestRenderWithFocus(), !0;
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* Render a row. Returns true if we handled the row (group row), false otherwise.
|
|
665
|
-
* @internal
|
|
666
|
-
*/
|
|
667
|
-
renderRow(e, t, r) {
|
|
668
|
-
if (!e?.__isGroupRow)
|
|
669
|
-
return !1;
|
|
670
|
-
const i = this.config;
|
|
671
|
-
if (i.groupRowRenderer) {
|
|
672
|
-
const s = () => {
|
|
673
|
-
this.toggle(e.__groupKey);
|
|
674
|
-
}, l = i.groupRowRenderer({
|
|
675
|
-
key: e.__groupKey,
|
|
676
|
-
value: e.__groupValue,
|
|
677
|
-
depth: e.__groupDepth,
|
|
678
|
-
rows: e.__groupRows,
|
|
679
|
-
expanded: e.__groupExpanded,
|
|
680
|
-
toggleExpand: s
|
|
681
|
-
});
|
|
682
|
-
if (l)
|
|
683
|
-
return t.className = "data-grid-row group-row", t.__isCustomRow = !0, t.setAttribute("data-group-depth", String(e.__groupDepth)), typeof l == "string" ? t.innerHTML = l : (t.innerHTML = "", t.appendChild(l)), !0;
|
|
684
|
-
}
|
|
685
|
-
const o = () => {
|
|
686
|
-
this.toggle(e.__groupKey);
|
|
687
|
-
};
|
|
688
|
-
return t.className = "data-grid-row group-row", t.__isCustomRow = !0, t.setAttribute("data-group-depth", String(e.__groupDepth)), t.setAttribute("role", "row"), t.setAttribute("aria-expanded", String(e.__groupExpanded)), t.style.setProperty("--tbw-group-depth", String(e.__groupDepth || 0)), i.indentWidth !== void 0 && t.style.setProperty("--tbw-group-indent-width", `${i.indentWidth}px`), t.style.height = "", t.innerHTML = "", i.fullWidth !== !1 ? this.renderFullWidthGroupRow(e, t, o) : this.renderPerColumnGroupRow(e, t, o), !0;
|
|
689
|
-
}
|
|
690
|
-
/** @internal */
|
|
691
|
-
afterRender() {
|
|
692
|
-
const e = this.animationStyle;
|
|
693
|
-
if (e === !1 || this.keysToAnimate.size === 0) return;
|
|
694
|
-
const t = this.gridElement?.querySelector(".rows");
|
|
695
|
-
if (!t) return;
|
|
696
|
-
const r = e === "fade" ? "tbw-group-fade-in" : "tbw-group-slide-in";
|
|
697
|
-
for (const i of t.querySelectorAll(".data-grid-row:not(.group-row)")) {
|
|
698
|
-
const o = i.querySelector(".cell[data-row]"), g = o ? parseInt(o.getAttribute("data-row") ?? "-1", 10) : -1, l = this.flattenedRows[g]?.kind === "data" ? `data-${g}` : void 0;
|
|
699
|
-
l && this.keysToAnimate.has(l) && (i.classList.add(r), i.addEventListener("animationend", () => i.classList.remove(r), { once: !0 }));
|
|
700
|
-
}
|
|
701
|
-
this.keysToAnimate.clear();
|
|
702
|
-
}
|
|
703
|
-
// #endregion
|
|
704
|
-
// #region Private Rendering Helpers
|
|
705
|
-
/**
|
|
706
|
-
* Create a toggle button for expanding/collapsing a group.
|
|
707
|
-
*/
|
|
708
|
-
createToggleButton(e, t) {
|
|
709
|
-
const r = document.createElement("button");
|
|
710
|
-
return r.type = "button", r.className = `group-toggle${e ? " expanded" : ""}`, r.setAttribute("aria-label", e ? "Collapse group" : "Expand group"), this.setIcon(r, this.resolveIcon(e ? "collapse" : "expand")), r.addEventListener("click", (i) => {
|
|
711
|
-
i.stopPropagation(), t();
|
|
712
|
-
}), r;
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* Get the formatted label text for a group.
|
|
716
|
-
*/
|
|
717
|
-
getGroupLabelText(e, t, r) {
|
|
718
|
-
const i = this.config;
|
|
719
|
-
return i.formatLabel ? i.formatLabel(e, t, r) : String(e);
|
|
720
|
-
}
|
|
721
|
-
renderFullWidthGroupRow(e, t, r) {
|
|
722
|
-
const i = this.config, o = i.aggregators ?? {}, g = e.__groupRows ?? [], s = document.createElement("div");
|
|
723
|
-
s.className = "cell group-full", s.style.gridColumn = "1 / -1", s.setAttribute("role", "gridcell"), s.setAttribute("data-col", "0"), s.appendChild(this.createToggleButton(e.__groupExpanded, r));
|
|
724
|
-
const l = document.createElement("span");
|
|
725
|
-
if (l.className = "group-label", l.textContent = this.getGroupLabelText(e.__groupValue, e.__groupDepth || 0, e.__groupKey), s.appendChild(l), i.showRowCount !== !1) {
|
|
726
|
-
const u = document.createElement("span");
|
|
727
|
-
u.className = "group-count", u.textContent = `(${e.__groupRowCount ?? e.__groupRows?.length ?? 0})`, s.appendChild(u);
|
|
728
|
-
}
|
|
729
|
-
const d = Object.entries(o);
|
|
730
|
-
if (d.length > 0) {
|
|
731
|
-
const u = document.createElement("span");
|
|
732
|
-
u.className = "group-aggregates";
|
|
733
|
-
for (const [a, m] of d) {
|
|
734
|
-
const p = this.columns.find((h) => h.field === a), c = _(m, g, a, p);
|
|
735
|
-
if (c != null) {
|
|
736
|
-
const h = document.createElement("span");
|
|
737
|
-
h.className = "group-aggregate", h.setAttribute("data-field", a);
|
|
738
|
-
const f = p?.header ?? a;
|
|
739
|
-
h.textContent = `${f}: ${c}`, u.appendChild(h);
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
u.children.length > 0 && s.appendChild(u);
|
|
743
|
-
}
|
|
744
|
-
t.appendChild(s);
|
|
745
|
-
}
|
|
746
|
-
renderPerColumnGroupRow(e, t, r) {
|
|
747
|
-
const i = this.config, o = i.aggregators ?? {}, g = this.columns, s = e.__groupRows ?? [], d = this.gridElement?.querySelector(".body")?.style.gridTemplateColumns || "";
|
|
748
|
-
d && (t.style.display = "grid", t.style.gridTemplateColumns = d);
|
|
749
|
-
let u = !1;
|
|
750
|
-
g.forEach((a, m) => {
|
|
751
|
-
const p = document.createElement("div");
|
|
752
|
-
if (p.className = "cell group-cell", p.setAttribute("data-col", String(m)), p.setAttribute("role", "gridcell"), A(a)) {
|
|
753
|
-
p.setAttribute("data-field", a.field), t.appendChild(p);
|
|
754
|
-
return;
|
|
755
|
-
}
|
|
756
|
-
if (u) {
|
|
757
|
-
const c = o[a.field];
|
|
758
|
-
if (c) {
|
|
759
|
-
const h = _(c, s, a.field, a);
|
|
760
|
-
p.textContent = h != null ? String(h) : "";
|
|
761
|
-
} else
|
|
762
|
-
p.textContent = "";
|
|
763
|
-
} else {
|
|
764
|
-
u = !0, p.appendChild(this.createToggleButton(e.__groupExpanded, r));
|
|
765
|
-
const c = document.createElement("span"), h = o[a.field];
|
|
766
|
-
if (h) {
|
|
767
|
-
const f = _(h, s, a.field, a);
|
|
768
|
-
c.textContent = f != null ? String(f) : String(e.__groupValue);
|
|
769
|
-
} else
|
|
770
|
-
c.textContent = this.getGroupLabelText(e.__groupValue, e.__groupDepth || 0, e.__groupKey);
|
|
771
|
-
if (p.appendChild(c), i.showRowCount !== !1) {
|
|
772
|
-
const f = document.createElement("span");
|
|
773
|
-
f.className = "group-count", f.textContent = ` (${s.length})`, p.appendChild(f);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
t.appendChild(p);
|
|
777
|
-
});
|
|
778
|
-
}
|
|
779
|
-
// #endregion
|
|
780
|
-
// #region Public API
|
|
781
|
-
/**
|
|
782
|
-
* Expand all groups.
|
|
783
|
-
*/
|
|
784
|
-
expandAll() {
|
|
785
|
-
this.expandedKeys = S(this.flattenedRows), this.emitPluginEvent("grouping-state-change", { expandedKeys: [...this.expandedKeys] }), this.requestRender();
|
|
786
|
-
}
|
|
787
|
-
/**
|
|
788
|
-
* Collapse all groups.
|
|
789
|
-
*/
|
|
790
|
-
collapseAll() {
|
|
791
|
-
this.expandedKeys = k(), this.emitPluginEvent("grouping-state-change", { expandedKeys: [...this.expandedKeys] }), this.requestRender();
|
|
792
|
-
}
|
|
793
|
-
/**
|
|
794
|
-
* Toggle expansion of a specific group.
|
|
795
|
-
* In accordion mode, expanding a group will collapse all sibling groups.
|
|
796
|
-
* @param key - The group key to toggle
|
|
797
|
-
*/
|
|
798
|
-
toggle(e) {
|
|
799
|
-
const t = !this.expandedKeys.has(e), r = this.config, i = this.flattenedRows.find((o) => o.kind === "group" && o.key === e);
|
|
800
|
-
if (r.accordion && t && i) {
|
|
801
|
-
const o = /* @__PURE__ */ new Set();
|
|
802
|
-
for (const g of this.expandedKeys)
|
|
803
|
-
if (e.startsWith(g + "||") || g.startsWith(e + "||"))
|
|
804
|
-
e.startsWith(g + "||") && o.add(g);
|
|
805
|
-
else {
|
|
806
|
-
const s = this.flattenedRows.find((l) => l.kind === "group" && l.key === g);
|
|
807
|
-
s && s.depth !== i.depth && o.add(g);
|
|
808
|
-
}
|
|
809
|
-
o.add(e), this.expandedKeys = o;
|
|
810
|
-
} else
|
|
811
|
-
this.expandedKeys = K(this.expandedKeys, e);
|
|
812
|
-
this.emit("group-toggle", {
|
|
813
|
-
key: e,
|
|
814
|
-
expanded: this.expandedKeys.has(e),
|
|
815
|
-
value: i?.value,
|
|
816
|
-
depth: i?.depth ?? 0
|
|
817
|
-
}), this.emitPluginEvent("grouping-state-change", {
|
|
818
|
-
expandedKeys: [...this.expandedKeys]
|
|
819
|
-
}), this.requestRender();
|
|
820
|
-
}
|
|
821
|
-
/**
|
|
822
|
-
* Check if a specific group is expanded.
|
|
823
|
-
* @param key - The group key to check
|
|
824
|
-
* @returns Whether the group is expanded
|
|
825
|
-
*/
|
|
826
|
-
isExpanded(e) {
|
|
827
|
-
return this.expandedKeys.has(e);
|
|
828
|
-
}
|
|
829
|
-
/**
|
|
830
|
-
* Expand a specific group.
|
|
831
|
-
* @param key - The group key to expand
|
|
832
|
-
*/
|
|
833
|
-
expand(e) {
|
|
834
|
-
this.expandedKeys.has(e) || (this.expandedKeys = /* @__PURE__ */ new Set([...this.expandedKeys, e]), this.requestRender());
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* Collapse a specific group.
|
|
838
|
-
* @param key - The group key to collapse
|
|
839
|
-
*/
|
|
840
|
-
collapse(e) {
|
|
841
|
-
if (this.expandedKeys.has(e)) {
|
|
842
|
-
const t = new Set(this.expandedKeys);
|
|
843
|
-
t.delete(e), this.expandedKeys = t, this.requestRender();
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
|
-
* Get the current group state.
|
|
848
|
-
* @returns Group state information
|
|
849
|
-
*/
|
|
850
|
-
getGroupState() {
|
|
851
|
-
const e = this.flattenedRows.filter((t) => t.kind === "group");
|
|
852
|
-
return {
|
|
853
|
-
isActive: this.isActive,
|
|
854
|
-
expandedCount: this.expandedKeys.size,
|
|
855
|
-
totalGroups: e.length,
|
|
856
|
-
expandedKeys: [...this.expandedKeys]
|
|
857
|
-
};
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* Get the total count of visible rows (including group headers).
|
|
861
|
-
* @returns Number of visible rows
|
|
862
|
-
*/
|
|
863
|
-
getRowCount() {
|
|
864
|
-
return this.flattenedRows.length;
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Refresh the grouped row model.
|
|
868
|
-
* Call this after modifying groupOn or other config options.
|
|
869
|
-
*/
|
|
870
|
-
refreshGroups() {
|
|
871
|
-
this.requestRender();
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Get current expanded group keys.
|
|
875
|
-
* @returns Array of expanded group keys
|
|
876
|
-
*/
|
|
877
|
-
getExpandedGroups() {
|
|
878
|
-
return [...this.expandedKeys];
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* Get the flattened row model.
|
|
882
|
-
* @returns Array of render rows (groups + data rows)
|
|
883
|
-
*/
|
|
884
|
-
getFlattenedRows() {
|
|
885
|
-
return this.flattenedRows;
|
|
886
|
-
}
|
|
887
|
-
/**
|
|
888
|
-
* Check if grouping is currently active.
|
|
889
|
-
* @returns Whether grouping is active
|
|
890
|
-
*/
|
|
891
|
-
isGroupingActive() {
|
|
892
|
-
return this.isActive;
|
|
893
|
-
}
|
|
894
|
-
/**
|
|
895
|
-
* Set the groupOn function dynamically.
|
|
896
|
-
* @param fn - The groupOn function or undefined to disable
|
|
897
|
-
*/
|
|
898
|
-
setGroupOn(e) {
|
|
899
|
-
this.config.groupOn = e, this.requestRender();
|
|
900
|
-
}
|
|
901
|
-
// #endregion
|
|
902
|
-
}
|
|
903
|
-
export {
|
|
904
|
-
D as GroupingRowsPlugin
|
|
905
|
-
};
|
|
1
|
+
const e='<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>',t={expand:"▶",collapse:"▼",sortAsc:"▲",sortDesc:"▼",sortNone:"⇅",submenuArrow:"▶",dragHandle:"⋮⋮",toolPanel:"☰",filter:e,filterActive:e,print:"🖨️"};class n{static dependencies;static manifest;version="undefined"!=typeof __GRID_VERSION__?__GRID_VERSION__:"dev";styles;cellRenderers;headerRenderers;cellEditors;grid;config;userConfig;#e;get defaultConfig(){return{}}constructor(e={}){this.userConfig=e}attach(e){this.#e?.abort(),this.#e=new AbortController,this.grid=e,this.config={...this.defaultConfig,...this.userConfig}}detach(){this.#e?.abort(),this.#e=void 0}getPlugin(e){return this.grid?.getPlugin(e)}emit(e,t){this.grid?.dispatchEvent?.(new CustomEvent(e,{detail:t,bubbles:!0}))}emitCancelable(e,t){const n=new CustomEvent(e,{detail:t,bubbles:!0,cancelable:!0});return this.grid?.dispatchEvent?.(n),n.defaultPrevented}on(e,t){this.grid?._pluginManager?.subscribe(this,e,t)}off(e){this.grid?._pluginManager?.unsubscribe(this,e)}emitPluginEvent(e,t){this.grid?._pluginManager?.emitPluginEvent(e,t)}requestRender(){this.grid?.requestRender?.()}requestColumnsRender(){this.grid?.requestColumnsRender?.()}requestRenderWithFocus(){this.grid?.requestRenderWithFocus?.()}requestAfterRender(){this.grid?.requestAfterRender?.()}get rows(){return this.grid?.rows??[]}get sourceRows(){return this.grid?.sourceRows??[]}get columns(){return this.grid?.columns??[]}get visibleColumns(){return this.grid?._visibleColumns??[]}get gridElement(){return this.grid}get disconnectSignal(){return this.#e?.signal??this.grid?.disconnectSignal}get gridIcons(){const e=this.grid?.gridConfig?.icons??{};return{...t,...e}}get isAnimationEnabled(){const e=this.grid?.effectiveConfig?.animation?.mode??"reduced-motion";if(!1===e||"off"===e)return!1;if(!0===e||"on"===e)return!0;const t=this.gridElement;if(t){return"0"!==getComputedStyle(t).getPropertyValue("--tbw-animation-enabled").trim()}return!0}get animationDuration(){const e=this.gridElement;if(e){const t=getComputedStyle(e).getPropertyValue("--tbw-animation-duration").trim(),n=parseInt(t,10);if(!isNaN(n))return n}return 200}resolveIcon(e,t){return void 0!==t?t:this.gridIcons[e]}setIcon(e,t){"string"==typeof t?e.innerHTML=t:t instanceof HTMLElement&&(e.innerHTML="",e.appendChild(t.cloneNode(!0)))}warn(e){console.warn(`[tbw-grid:${this.name}] ${e}`)}}const r={sum:(e,t)=>e.reduce((e,n)=>e+(Number(n[t])||0),0),avg:(e,t)=>{const n=e.reduce((e,n)=>e+(Number(n[t])||0),0);return e.length?n/e.length:0},count:e=>e.length,min:(e,t)=>e.length?Math.min(...e.map(e=>Number(e[t])||1/0)):0,max:(e,t)=>e.length?Math.max(...e.map(e=>Number(e[t])||-1/0)):0,first:(e,t)=>e[0]?.[t],last:(e,t)=>e[e.length-1]?.[t]},o=/* @__PURE__ */new Map,i={register(e,t){o.set(e,t)},unregister(e){o.delete(e)},get(e){if(void 0!==e)return"function"==typeof e?e:o.get(e)??r[e]},run(e,t,n,r){const o=this.get(e);return o?o(t,n,r):void 0},has:e=>o.has(e)||e in r,list:()=>[...Object.keys(r),...o.keys()]};i.register.bind(i),i.unregister.bind(i),i.get.bind(i);const s=i.run.bind(i);function a({rows:e,config:t,expanded:n,initialExpanded:r}){const o=t.groupOn;if("function"!=typeof o)return[];const i={key:"__root__",value:null,depth:-1,rows:[],children:/* @__PURE__ */new Map};if(e.forEach(e=>{let t=o(e);null==t||!1===t?t=["__ungrouped__"]:Array.isArray(t)||(t=[t]);let n=i;t.forEach((e,t)=>{const r=null==e?"∅":String(e),o="__root__"===n.key?r:n.key+"||"+r;let i=n.children.get(r);i||(i={key:o,value:e,depth:t,rows:[],children:/* @__PURE__ */new Map,parent:n},n.children.set(r,i)),n=i}),n.rows.push(e)}),1===i.children.size&&i.children.has("__ungrouped__")){if(i.children.get("__ungrouped__").rows.length===e.length)return[]}const s=/* @__PURE__ */new Set([...n,...r??[]]),a=[],d=t=>{if(t===i)return void t.children.forEach(e=>d(e));const n=s.has(t.key);a.push({kind:"group",key:t.key,value:t.value,depth:t.depth,rows:t.rows,expanded:n}),n&&(t.children.size?t.children.forEach(e=>d(e)):t.rows.forEach(t=>a.push({kind:"data",row:t,rowIndex:e.indexOf(t)})))};return d(i),a}i.list.bind(i);class d extends n{static manifest={events:[{type:"grouping-state-change",description:"Emitted when groups are expanded/collapsed. Subscribers can react to row visibility changes."}],queries:[{type:"canMoveRow",description:"Returns false for group header rows (cannot be reordered)"}],configRules:[{id:"groupingRows/accordion-defaultExpanded",severity:"warn",message:'"accordion: true" and "defaultExpanded" (non-false) are used together.\n → In accordion mode, only one group can be open at a time.\n → Using defaultExpanded with multiple groups will collapse to one on first toggle.\n → Consider using "defaultExpanded: false" or a single group key/index with accordion mode.',check:e=>!0===e.accordion&&!1!==e.defaultExpanded&&void 0!==e.defaultExpanded&&!("number"==typeof e.defaultExpanded)&&!("string"==typeof e.defaultExpanded)&&(!0===e.defaultExpanded||Array.isArray(e.defaultExpanded)&&e.defaultExpanded.length>1)}]};name="groupingRows";styles="@layer tbw-plugins{.group-row{display:grid;grid-template-columns:var(--tbw-column-template);background:var(--tbw-grouping-rows-bg, var(--tbw-color-panel-bg));font-weight:500;border-bottom:var(--tbw-row-divider);min-height:var(--tbw-row-height)}.group-row .cell{display:flex;align-items:center;padding:var(--tbw-cell-padding, .125rem .5rem)}.group-row:hover{background:var(--tbw-grouping-rows-bg-hover, var(--tbw-color-row-hover))}.group-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;display:inline-flex;align-items:center;justify-content:center;width:var(--tbw-toggle-size, 1.25rem);height:var(--tbw-toggle-size, 1.25rem);margin-right:.25rem;background:none;border:0;font:inherit}.group-toggle:hover{background:var(--tbw-grouping-rows-toggle-hover, var(--tbw-color-row-hover));border-radius:var(--tbw-border-radius, .125rem)}.group-label{display:inline-flex;align-items:center;gap:var(--tbw-panel-gap, var(--tbw-spacing-md, .5rem))}.group-count{color:var(--tbw-grouping-rows-count-color, var(--tbw-color-fg-muted));font-size:var(--tbw-font-size-xs, .85em);font-weight:400}.group-aggregates{display:inline-flex;align-items:center;gap:var(--tbw-spacing-lg, 1rem);margin-left:var(--tbw-spacing-lg, 1rem);font-weight:400;font-size:var(--tbw-font-size-sm, .875em);color:var(--tbw-grouping-rows-aggregate-color, var(--tbw-color-fg-muted))}.group-aggregate{white-space:nowrap}.group-row{padding-left:calc(var(--tbw-group-depth, 0) * var(--tbw-group-indent-width, 1.25em))}.data-grid-row.tbw-group-slide-in{animation:tbw-group-slide-in var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-group-slide-in{0%{opacity:0;transform:translate(-8px)}to{opacity:1;transform:translate(0)}}.data-grid-row.tbw-group-fade-in{animation:tbw-group-fade-in var(--tbw-animation-duration, .2s) var(--tbw-animation-easing, ease-out) forwards}@keyframes tbw-group-fade-in{0%{opacity:0}to{opacity:1}}}";get defaultConfig(){return{defaultExpanded:!1,showRowCount:!0,indentWidth:20,aggregators:{},animation:"slide",accordion:!1}}expandedKeys=/* @__PURE__ */new Set;flattenedRows=[];isActive=!1;previousVisibleKeys=/* @__PURE__ */new Set;keysToAnimate=/* @__PURE__ */new Set;hasAppliedDefaultExpanded=!1;get animationStyle(){return!!this.isAnimationEnabled&&(this.config.animation??"slide")}detach(){this.expandedKeys.clear(),this.flattenedRows=[],this.isActive=!1,this.previousVisibleKeys.clear(),this.keysToAnimate.clear(),this.hasAppliedDefaultExpanded=!1}getRowHeight(e,t){if(null!=this.config.groupRowHeight)return!0===e.__isGroupRow?this.config.groupRowHeight:void 0}handleQuery(e){if("canMoveRow"===e.type){const t=e.context;if(!0===t?.__isGroupRow)return!1}}static detect(e,t){return"function"==typeof t?.groupOn||"boolean"==typeof t?.enableRowGrouping}processRows(e){const t=this.config;if("function"!=typeof t.groupOn)return this.isActive=!1,this.flattenedRows=[],[...e];const n=a({rows:[...e],config:t,expanded:/* @__PURE__ */new Set});if(0===n.length)return this.isActive=!1,this.flattenedRows=[],[...e];let r;if(!this.hasAppliedDefaultExpanded&&0===this.expandedKeys.size&&!1!==t.defaultExpanded){const e=function(e){return e.filter(e=>"group"===e.kind).map(e=>e.key)}(n);r=function(e,t){if(!0===e)return new Set(t);if(!1===e||null==e)/* @__PURE__ */
|
|
2
|
+
return new Set;if("number"==typeof e){const n=t[e];return n?/* @__PURE__ */new Set([n]):/* @__PURE__ */new Set}return"string"==typeof e?/* @__PURE__ */new Set([e]):Array.isArray(e)?new Set(e):/* @__PURE__ */new Set}(t.defaultExpanded??!1,e),r.size>0&&(this.expandedKeys=new Set(r),this.hasAppliedDefaultExpanded=!0)}const o=a({rows:[...e],config:t,expanded:this.expandedKeys,initialExpanded:r});this.isActive=!0,this.flattenedRows=o,this.keysToAnimate.clear();const i=/* @__PURE__ */new Set;return o.forEach((e,t)=>{if("data"===e.kind){const e=`data-${t}`;i.add(e),this.previousVisibleKeys.has(e)||this.keysToAnimate.add(e)}}),this.previousVisibleKeys=i,o.map(e=>{return"group"===e.kind?{__isGroupRow:!0,__groupKey:e.key,__groupValue:e.value,__groupDepth:e.depth,__groupRows:e.rows,__groupExpanded:e.expanded,__groupRowCount:(t=e,"group"!==t.kind?0:t.rows.length),__rowCacheKey:`group:${e.key}`}:e.row;var t})}onCellClick(e){const t=e.row;if(t?.__isGroupRow){const n=e.originalEvent.target;if(n?.closest(".group-toggle"))return this.toggle(t.__groupKey),!0}}onKeyDown(e){if(" "!==e.key)return;const t=this.grid._focusRow,n=this.rows[t];return n?.__isGroupRow?(e.preventDefault(),this.toggle(n.__groupKey),this.requestRenderWithFocus(),!0):void 0}renderRow(e,t,n){if(!e?.__isGroupRow)return!1;const r=this.config;if(r.groupRowRenderer){const n=()=>{this.toggle(e.__groupKey)},o=r.groupRowRenderer({key:e.__groupKey,value:e.__groupValue,depth:e.__groupDepth,rows:e.__groupRows,expanded:e.__groupExpanded,toggleExpand:n});if(o)return t.className="data-grid-row group-row",t.__isCustomRow=!0,t.setAttribute("data-group-depth",String(e.__groupDepth)),"string"==typeof o?t.innerHTML=o:(t.innerHTML="",t.appendChild(o)),!0}const o=()=>{this.toggle(e.__groupKey)};t.className="data-grid-row group-row",t.__isCustomRow=!0,t.setAttribute("data-group-depth",String(e.__groupDepth)),t.setAttribute("role","row"),t.setAttribute("aria-expanded",String(e.__groupExpanded)),t.style.setProperty("--tbw-group-depth",String(e.__groupDepth||0)),void 0!==r.indentWidth&&t.style.setProperty("--tbw-group-indent-width",`${r.indentWidth}px`),t.style.height="",t.innerHTML="";return!1!==r.fullWidth?this.renderFullWidthGroupRow(e,t,o):this.renderPerColumnGroupRow(e,t,o),!0}afterRender(){const e=this.animationStyle;if(!1===e||0===this.keysToAnimate.size)return;const t=this.gridElement?.querySelector(".rows");if(!t)return;const n="fade"===e?"tbw-group-fade-in":"tbw-group-slide-in";for(const r of t.querySelectorAll(".data-grid-row:not(.group-row)")){const e=r.querySelector(".cell[data-row]"),t=e?parseInt(e.getAttribute("data-row")??"-1",10):-1,o=this.flattenedRows[t],i="data"===o?.kind?`data-${t}`:void 0;i&&this.keysToAnimate.has(i)&&(r.classList.add(n),r.addEventListener("animationend",()=>r.classList.remove(n),{once:!0}))}this.keysToAnimate.clear()}createToggleButton(e,t){const n=document.createElement("button");return n.type="button",n.className="group-toggle"+(e?" expanded":""),n.setAttribute("aria-label",e?"Collapse group":"Expand group"),this.setIcon(n,this.resolveIcon(e?"collapse":"expand")),n.addEventListener("click",e=>{e.stopPropagation(),t()}),n}getGroupLabelText(e,t,n){const r=this.config;return r.formatLabel?r.formatLabel(e,t,n):String(e)}renderFullWidthGroupRow(e,t,n){const r=this.config,o=r.aggregators??{},i=e.__groupRows??[],a=document.createElement("div");a.className="cell group-full",a.style.gridColumn="1 / -1",a.setAttribute("role","gridcell"),a.setAttribute("data-col","0"),a.appendChild(this.createToggleButton(e.__groupExpanded,n));const d=document.createElement("span");if(d.className="group-label",d.textContent=this.getGroupLabelText(e.__groupValue,e.__groupDepth||0,e.__groupKey),a.appendChild(d),!1!==r.showRowCount){const t=document.createElement("span");t.className="group-count",t.textContent=`(${e.__groupRowCount??e.__groupRows?.length??0})`,a.appendChild(t)}const l=Object.entries(o);if(l.length>0){const e=document.createElement("span");e.className="group-aggregates";for(const[t,n]of l){const r=this.columns.find(e=>e.field===t),o=s(n,i,t,r);if(null!=o){const n=document.createElement("span");n.className="group-aggregate",n.setAttribute("data-field",t);const i=r?.header??t;n.textContent=`${i}: ${o}`,e.appendChild(n)}}e.children.length>0&&a.appendChild(e)}t.appendChild(a)}renderPerColumnGroupRow(e,t,n){const r=this.config,o=r.aggregators??{},i=this.columns,a=e.__groupRows??[],d=this.gridElement?.querySelector(".body"),l=d?.style.gridTemplateColumns||"";l&&(t.style.display="grid",t.style.gridTemplateColumns=l);let u=!1;i.forEach((i,d)=>{const l=document.createElement("div");if(l.className="cell group-cell",l.setAttribute("data-col",String(d)),l.setAttribute("role","gridcell"),"__tbw_expander"===i.field)return l.setAttribute("data-field",i.field),void t.appendChild(l);if(u){const e=o[i.field];if(e){const t=s(e,a,i.field,i);l.textContent=null!=t?String(t):""}else l.textContent=""}else{u=!0,l.appendChild(this.createToggleButton(e.__groupExpanded,n));const t=document.createElement("span"),d=o[i.field];if(d){const n=s(d,a,i.field,i);t.textContent=String(null!=n?n:e.__groupValue)}else t.textContent=this.getGroupLabelText(e.__groupValue,e.__groupDepth||0,e.__groupKey);if(l.appendChild(t),!1!==r.showRowCount){const e=document.createElement("span");e.className="group-count",e.textContent=` (${a.length})`,l.appendChild(e)}}t.appendChild(l)})}expandAll(){this.expandedKeys=function(e){const t=/* @__PURE__ */new Set;for(const n of e)"group"===n.kind&&t.add(n.key);return t}(this.flattenedRows),this.emitPluginEvent("grouping-state-change",{expandedKeys:[...this.expandedKeys]}),this.requestRender()}collapseAll(){this.expandedKeys=/* @__PURE__ */new Set,this.emitPluginEvent("grouping-state-change",{expandedKeys:[...this.expandedKeys]}),this.requestRender()}toggle(e){const t=!this.expandedKeys.has(e),n=this.config,r=this.flattenedRows.find(t=>"group"===t.kind&&t.key===e);if(n.accordion&&t&&r){const t=/* @__PURE__ */new Set;for(const n of this.expandedKeys)if(e.startsWith(n+"||")||n.startsWith(e+"||"))e.startsWith(n+"||")&&t.add(n);else{const e=this.flattenedRows.find(e=>"group"===e.kind&&e.key===n);e&&e.depth!==r.depth&&t.add(n)}t.add(e),this.expandedKeys=t}else this.expandedKeys=function(e,t){const n=new Set(e);return n.has(t)?n.delete(t):n.add(t),n}(this.expandedKeys,e);this.emit("group-toggle",{key:e,expanded:this.expandedKeys.has(e),value:r?.value,depth:r?.depth??0}),this.emitPluginEvent("grouping-state-change",{expandedKeys:[...this.expandedKeys]}),this.requestRender()}isExpanded(e){return this.expandedKeys.has(e)}expand(e){this.expandedKeys.has(e)||(this.expandedKeys=/* @__PURE__ */new Set([...this.expandedKeys,e]),this.requestRender())}collapse(e){if(this.expandedKeys.has(e)){const t=new Set(this.expandedKeys);t.delete(e),this.expandedKeys=t,this.requestRender()}}getGroupState(){const e=this.flattenedRows.filter(e=>"group"===e.kind);return{isActive:this.isActive,expandedCount:this.expandedKeys.size,totalGroups:e.length,expandedKeys:[...this.expandedKeys]}}getRowCount(){return this.flattenedRows.length}refreshGroups(){this.requestRender()}getExpandedGroups(){return[...this.expandedKeys]}getFlattenedRows(){return this.flattenedRows}isGroupingActive(){return this.isActive}setGroupOn(e){this.config.groupOn=e,this.requestRender()}}export{d as GroupingRowsPlugin};
|
|
906
3
|
//# sourceMappingURL=index.js.map
|