@toolbox-web/grid 2.0.0-alpha → 2.0.0-alpha.1

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.
@@ -30,6 +30,16 @@ export interface ExportParams {
30
30
  format: ExportFormat;
31
31
  /** File name for the export (without extension) */
32
32
  fileName?: string;
33
+ /**
34
+ * Override the file extension for Excel export.
35
+ *
36
+ * Defaults to `'.xls'` so the file opens in Excel on most operating systems.
37
+ * Set to `'.xml'` to avoid Excel's "format mismatch" warning — but note
38
+ * that `.xml` files open in a browser by default on most systems.
39
+ *
40
+ * Only applies to Excel format exports.
41
+ */
42
+ fileExtension?: string;
33
43
  /** Specific column fields to export */
34
44
  columns?: string[];
35
45
  /** Specific row indices to export */
@@ -40,6 +50,64 @@ export interface ExportParams {
40
50
  processCell?: (value: any, field: string, row: any) => any;
41
51
  /** Custom header processor */
42
52
  processHeader?: (header: string, field: string) => string;
53
+ /** Excel style configuration (only applies to Excel export) */
54
+ excelStyles?: ExcelStyleConfig;
55
+ }
56
+ /**
57
+ * Configuration for styled Excel export.
58
+ *
59
+ * Controls header styles, per-column and per-cell formatting,
60
+ * column widths, and auto-fit behavior in Excel XML output.
61
+ */
62
+ export interface ExcelStyleConfig {
63
+ /** Style applied to all header cells */
64
+ headerStyle?: ExcelCellStyle;
65
+ /** Default style for all data cells */
66
+ defaultStyle?: ExcelCellStyle;
67
+ /** Per-column style overrides (keyed by field name) */
68
+ columnStyles?: Record<string, ExcelCellStyle>;
69
+ /** Callback for per-cell dynamic styling */
70
+ cellStyle?: (value: unknown, field: string, row: unknown) => ExcelCellStyle | undefined;
71
+ /** Column width overrides in characters (keyed by field name) */
72
+ columnWidths?: Record<string, number>;
73
+ /** Auto-fit column widths based on content (default: false) */
74
+ autoFitColumns?: boolean;
75
+ }
76
+ /** Style definition for an Excel cell. */
77
+ export interface ExcelCellStyle {
78
+ /** Font configuration */
79
+ font?: {
80
+ name?: string;
81
+ size?: number;
82
+ bold?: boolean;
83
+ italic?: boolean;
84
+ color?: string;
85
+ };
86
+ /** Fill / background color */
87
+ fill?: {
88
+ color: string;
89
+ pattern?: 'Solid' | 'None';
90
+ };
91
+ /** Number format string (Excel format codes) */
92
+ numberFormat?: string;
93
+ /** Text alignment */
94
+ alignment?: {
95
+ horizontal?: 'Left' | 'Center' | 'Right';
96
+ vertical?: 'Top' | 'Center' | 'Bottom';
97
+ wrapText?: boolean;
98
+ };
99
+ /** Cell borders */
100
+ borders?: {
101
+ top?: ExcelBorder;
102
+ bottom?: ExcelBorder;
103
+ left?: ExcelBorder;
104
+ right?: ExcelBorder;
105
+ };
106
+ }
107
+ /** Border definition for an Excel cell edge. */
108
+ export interface ExcelBorder {
109
+ style: 'Thin' | 'Medium' | 'Thick';
110
+ color?: string;
43
111
  }
44
112
  /** Internal state managed by the export plugin */
45
113
  export interface ExportState {
@@ -1,5 +1,5 @@
1
1
  import { BaseGridPlugin, CellClickEvent, PluginManifest, PluginQuery } from '../../core/plugin/base-plugin';
2
- import { GroupingRowsConfig, RenderRow } from './types';
2
+ import { GroupDefinition, GroupingRowsConfig, RenderRow } from './types';
3
3
  /**
4
4
  * Group state information returned by getGroupState()
5
5
  */
@@ -90,6 +90,16 @@ export declare class GroupingRowsPlugin extends BaseGridPlugin<GroupingRowsConfi
90
90
  private keysToAnimate;
91
91
  /** Track if initial defaultExpanded has been applied */
92
92
  private hasAppliedDefaultExpanded;
93
+ /** Pre-defined group definitions (server-side mode) */
94
+ private preDefinedGroups;
95
+ /** Lazily loaded row data keyed by group key */
96
+ private groupRowsMap;
97
+ /** Groups currently in a loading state */
98
+ private loadingGroups;
99
+ /** Whether an async groups fetch is currently in progress */
100
+ private groupsFetchInFlight;
101
+ /** Group keys with an in-flight rows fetch */
102
+ private rowsFetchInFlight;
93
103
  /**
94
104
  * Get expand/collapse animation style from plugin config.
95
105
  * Uses base class isAnimationEnabled to respect grid-level settings.
@@ -128,12 +138,39 @@ export declare class GroupingRowsPlugin extends BaseGridPlugin<GroupingRowsConfi
128
138
  /** @internal */
129
139
  onKeyDown(event: KeyboardEvent): boolean | void;
130
140
  /**
131
- * Render a row. Returns true if we handled the row (group row), false otherwise.
141
+ * Render a row. Returns true if we handled the row (group row or loading placeholder), false otherwise.
132
142
  * @internal
133
143
  */
134
144
  renderRow(row: any, rowEl: HTMLElement, _rowIndex: number): boolean;
135
145
  /** @internal */
136
146
  afterRender(): void;
147
+ /**
148
+ * Build the row model from pre-defined group definitions.
149
+ * Used when `groups` config or `setGroups()` provides external group structure.
150
+ */
151
+ private processPreDefinedGroups;
152
+ /**
153
+ * Fetch group definitions from an async callback.
154
+ * Sets `preDefinedGroups` when resolved and triggers re-render.
155
+ */
156
+ private fetchGroupsAsync;
157
+ /**
158
+ * Fetch rows for a group using the `rows` callback and update state.
159
+ * Manages loading indicator automatically. Guards against duplicate in-flight fetches.
160
+ */
161
+ private fetchGroupRowsAsync;
162
+ /**
163
+ * Collect all group keys from a pre-defined group tree.
164
+ */
165
+ private collectGroupKeys;
166
+ /**
167
+ * Get the active group definitions (pre-defined or empty).
168
+ */
169
+ private getActiveGroups;
170
+ /**
171
+ * Find a group definition by key in the pre-defined group tree.
172
+ */
173
+ private findGroupDefinition;
137
174
  /**
138
175
  * Create a toggle button for expanding/collapsing a group.
139
176
  */
@@ -209,4 +246,56 @@ export declare class GroupingRowsPlugin extends BaseGridPlugin<GroupingRowsConfi
209
246
  * @param fn - The groupOn function or undefined to disable
210
247
  */
211
248
  setGroupOn(fn: ((row: any) => any[] | any | null | false) | undefined): void;
249
+ /**
250
+ * Replace auto-detected groups with an externally provided group structure.
251
+ *
252
+ * When groups are set, the plugin switches to pre-defined mode — `groupOn`
253
+ * is ignored and the plugin renders the provided group headers instead.
254
+ * Row data for each group must be populated via {@link setGroupRows}.
255
+ *
256
+ * @param groups - Array of group definitions, or empty array to clear
257
+ */
258
+ setGroups(groups: GroupDefinition[]): void;
259
+ /**
260
+ * Get the current pre-defined group structure.
261
+ *
262
+ * Returns the groups set via {@link setGroups} or the resolved `groups` config.
263
+ * Returns an empty array when using `groupOn`-based grouping or while
264
+ * an async `groups` callback is still in flight.
265
+ *
266
+ * @returns Current group definitions
267
+ */
268
+ getGroups(): GroupDefinition[];
269
+ /**
270
+ * Populate row data for an expanded group.
271
+ *
272
+ * Call this in response to a `group-expand` event after fetching rows
273
+ * from the server. The plugin will re-render to show the rows.
274
+ *
275
+ * If the {@link GroupingRowsConfig.rows | rows} callback is configured,
276
+ * this is called automatically — you only need this for the imperative API.
277
+ *
278
+ * @param groupKey - The group key to populate
279
+ * @param rows - The row data for this group
280
+ */
281
+ setGroupRows(groupKey: string, rows: unknown[]): void;
282
+ /**
283
+ * Toggle loading indicator for a group.
284
+ *
285
+ * When loading is true, the group shows a loading spinner instead of row data.
286
+ * Call with `false` after rows are loaded (also cleared by {@link setGroupRows}).
287
+ *
288
+ * @param groupKey - The group key
289
+ * @param loading - Whether the group is loading
290
+ */
291
+ setGroupLoading(groupKey: string, loading: boolean): void;
292
+ /**
293
+ * Clear cached row data for one or all groups.
294
+ *
295
+ * Use when the server data has changed and groups need to be re-fetched
296
+ * on next expand.
297
+ *
298
+ * @param groupKey - Specific group key to clear, or omit to clear all
299
+ */
300
+ clearGroupRows(groupKey?: string): void;
212
301
  }
@@ -1,4 +1,4 @@
1
- import { DefaultExpandedValue, GroupingRowsConfig, RenderRow } from './types';
1
+ import { DefaultExpandedValue, GroupDefinition, GroupingRowsConfig, RenderRow } from './types';
2
2
  interface BuildGroupingArgs {
3
3
  rows: any[];
4
4
  config: GroupingRowsConfig;
@@ -58,4 +58,32 @@ export declare function getGroupKeys(rows: RenderRow[]): string[];
58
58
  * @returns Total row count
59
59
  */
60
60
  export declare function getGroupRowCount(groupRow: RenderRow): number;
61
+ interface PreDefinedGroupModelArgs {
62
+ groups: GroupDefinition[];
63
+ expanded: Set<string>;
64
+ groupRows: Map<string, unknown[]>;
65
+ loadingGroups: Set<string>;
66
+ parentPath?: string[];
67
+ }
68
+ /**
69
+ * Build a flattened render model from pre-defined group definitions.
70
+ *
71
+ * Unlike `buildGroupedRowModel`, this does not analyze row data — groups
72
+ * are provided externally (e.g. from a server). Row data for each group
73
+ * is populated lazily via the `groupRows` map.
74
+ *
75
+ * @param args - Pre-defined grouping arguments
76
+ * @returns Flattened array of render rows (groups + data rows)
77
+ */
78
+ export declare function buildPreDefinedGroupModel({ groups, expanded, groupRows, loadingGroups, parentPath, }: PreDefinedGroupModelArgs): RenderRow[];
79
+ /**
80
+ * Compute the group path (array of ancestor keys) for a given group key
81
+ * within a pre-defined group structure.
82
+ *
83
+ * @param groups - The group definitions to search
84
+ * @param targetKey - The key to find
85
+ * @param parentPath - Accumulated path (used for recursion)
86
+ * @returns Array of group keys from root to target, or empty array if not found
87
+ */
88
+ export declare function getGroupPath(groups: GroupDefinition[], targetKey: string, parentPath?: string[]): string[];
61
89
  export {};
@@ -6,4 +6,4 @@
6
6
  */
7
7
  export { GroupingRowsPlugin } from './GroupingRowsPlugin';
8
8
  export type { GroupState } from './GroupingRowsPlugin';
9
- export type { AggregatorMap, DataRowModelItem, DefaultExpandedValue, GroupRowModelItem, GroupRowRenderParams, GroupToggleDetail, GroupingRowsConfig, RenderRow, } from './types';
9
+ export type { AggregatorMap, DataRowModelItem, DefaultExpandedValue, GroupCollapseDetail, GroupDefinition, GroupExpandDetail, GroupingRowsConfig, GroupRowModelItem, GroupRowRenderParams, GroupToggleDetail, RenderRow, } from './types';
@@ -1,3 +1,3 @@
1
- const e="expanded",t="group-toggle",r="group-label",n="group-count",o={sum:(e,t)=>{let r=0;for(let n=0;n<e.length;n++)r+=Number(e[n][t])||0;return r},avg:(e,t)=>{if(!e.length)return 0;let r=0;for(let n=0;n<e.length;n++)r+=Number(e[n][t])||0;return r/e.length},count:e=>e.length,min:(e,t)=>{if(!e.length)return 0;let r=1/0;for(let n=0;n<e.length;n++){const o=Number(e[n][t]);o<r&&(r=o)}return r},max:(e,t)=>{if(!e.length)return 0;let r=-1/0;for(let n=0;n<e.length;n++){const o=Number(e[n][t]);o>r&&(r=o)}return r},first:(e,t)=>e[0]?.[t],last:(e,t)=>e[e.length-1]?.[t]},i=/* @__PURE__ */new Map,s={register(e,t){i.set(e,t)},unregister(e){i.delete(e)},get(e){if(void 0!==e)return"function"==typeof e?e:i.get(e)??o[e]},run(e,t,r,n){const o=this.get(e);return o?o(t,r,n):void 0},has:e=>i.has(e)||e in o,list:()=>[...Object.keys(o),...i.keys()]},a={sortApplied:(e,t)=>`Sorted by ${e}, ${t}`,sortCleared:()=>"Sort cleared",filterApplied:e=>`Filter applied on ${e}`,filterCleared:e=>`Filter cleared from ${e}`,allFiltersCleared:()=>"All filters cleared",groupExpanded:(e,t)=>`Group ${e} expanded, ${t} rows`,groupCollapsed:e=>`Group ${e} collapsed`,selectionChanged:e=>`${e} rows selected`,editingStarted:e=>`Editing row ${e+1}`,editingCommitted:e=>`Row ${e+1} saved`,dataLoaded:e=>`${e} rows loaded`},d='<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>',l={expand:"▶",collapse:"▼",sortAsc:"▲",sortDesc:"▼",sortNone:"⇅",submenuArrow:"▶",dragHandle:"⋮⋮",toolPanel:"☰",filter:d,filterActive:d,print:"🖨️"};function u(e,t){if(!e)return;const r="effectiveConfig"in e?e.effectiveConfig:void 0;if(!1===r?.a11y?.announcements)return;const n=e.querySelector?.(".tbw-sr-only");n&&(n.textContent="",requestAnimationFrame(()=>{n.textContent=t}))}function g(e,t,...r){const n=e&&"effectiveConfig"in e?e.effectiveConfig:void 0,o=n?.a11y?.messages?.[t];return o?o(...r):a[t](...r)}function p(e,t){return`[tbw-grid${e?`#${e}`:""}${t?`:${t}`:""}]`}function c(e,t,r,n){return`${p(r,n)} ${e}: ${t}\n\n → More info: ${function(e){return`https://toolboxjs.com/grid/errors#${e.toLowerCase()}`}(e)}`}["__otorp__","__retteGenifed__","__retteSenifed__","rotcurtsnoc","wodniw","sihTlabolg","labolg","ssecorp","noitcnuF","tropmi","lave","tcelfeR","yxorP","rorrE","stnemugra","tnemucod","noitacol","eikooc","egarotSlacol","egarotSnoisses","BDdexedni","hctef","tseuqeRpttHLMX","tekcoSbeW","rekroW","rekroWderahS","rekroWecivreS","renepo","tnerap","pot","semarf","fles"].map(e=>e.split("").reverse().join(""));const h=/* @__PURE__ */new Set(["script","iframe","object","embed","form","input","button","textarea","select","link","meta","base","style","template","slot","portal","frame","frameset","applet","noscript","noembed","plaintext","xmp","listing"]),f=/^on\w+$/i,w=/* @__PURE__ */new Set(["href","src","action","formaction","data","srcdoc","xlink:href","poster","srcset"]),m=/^\s*(javascript|vbscript|data|blob):/i;function b(e){if(!e||"string"!=typeof e)return"";if(-1===e.indexOf("<"))return e;const t=document.createElement("template");return t.innerHTML=e,function(e){const t=[],r=e.querySelectorAll("*");for(const n of r){const e=n.tagName.toLowerCase();if(h.has(e)){t.push(n);continue}if("svg"===e||"http://www.w3.org/2000/svg"===n.namespaceURI){if(Array.from(n.attributes).some(e=>f.test(e.name)||"href"===e.name||"xlink:href"===e.name)){t.push(n);continue}}const r=[];for(const t of n.attributes){const e=t.name.toLowerCase();f.test(e)?r.push(t.name):(w.has(e)&&m.test(t.value)||"style"===e&&/expression\s*\(|javascript:|behavior\s*:/i.test(t.value))&&r.push(t.name)}r.forEach(e=>n.removeAttribute(e))}t.forEach(e=>e.remove())}(t.content),t.innerHTML}class y{static dependencies;static manifest;aliases;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 r=new CustomEvent(e,{detail:t,bubbles:!0,cancelable:!0});return this.grid?.dispatchEvent?.(r),r.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?.()}requestVirtualRefresh(){this.grid?.requestVirtualRefresh?.()}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?._hostElement}get disconnectSignal(){return this.#e?.signal??this.grid?.disconnectSignal}get gridIcons(){const e=this.grid?.gridConfig?.icons??{};return{...l,...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(),r=parseInt(t,10);if(!isNaN(r))return r}return 200}setIcon(e,t,r){e.dataset.icon=t.replace(/([A-Z])/g,"-$1").toLowerCase(),"collapse"===t?e.dataset.expanded="":"expand"===t&&delete e.dataset.expanded;const n=this.#t(t,r);void 0!==n?"string"==typeof n?e.innerHTML=b(n):n instanceof HTMLElement&&(e.innerHTML="",e.appendChild(n.cloneNode(!0))):e.innerHTML=""}#t(e,t){return void 0!==t?t:this.grid?.gridConfig?.icons?.[e]}updateSortIndicator(e,t){e.querySelector('[part~="sort-indicator"], .sort-indicator')?.remove();const r=document.createElement("span");r.setAttribute("part","sort-indicator"),r.className="sort-indicator",t?(e.setAttribute("aria-sort","asc"===t?"ascending":"descending"),e.setAttribute("data-sort",t),this.setIcon(r,"asc"===t?"sortAsc":"sortDesc")):(e.setAttribute("aria-sort","none"),e.removeAttribute("data-sort"),this.setIcon(r,"sortNone"));const n=e.querySelector(".tbw-filter-btn")??e.querySelector(".resize-handle");return n?e.insertBefore(r,n):e.appendChild(r),r}warn(e,t){void 0!==t?console.warn(c(e,t,this.gridElement.id,this.name)):console.warn(`${p(this.gridElement.id,this.name)} ${e}`)}throwDiagnostic(e,t){throw new Error(c(e,t,this.gridElement.id,this.name))}}function v({rows:e,config:t,expanded:r,initialExpanded:n}){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 r=i;t.forEach((t,n)=>{const o=null==t?"∅":String(t),i="__root__"===r.key?o:r.key+"||"+o;let s=r.children.get(o);s||(s={key:i,value:t,depth:n,rows:[],children:/* @__PURE__ */new Map,parent:r},r.children.set(o,s)),s.rows.push(e),r=s})}),1===i.children.size&&i.children.has("__ungrouped__")){if(i.children.get("__ungrouped__").rows.length===e.length)return[]}const s=/* @__PURE__ */new Map;for(let u=0;u<e.length;u++)s.set(e[u],u);const a=/* @__PURE__ */new Set([...r,...n??[]]),d=[],l=e=>{if(e===i)return void e.children.forEach(e=>l(e));const t=a.has(e.key);d.push({kind:"group",key:e.key,value:e.value,depth:e.depth,rows:e.rows,expanded:t}),t&&(e.children.size?e.children.forEach(e=>l(e)):e.rows.forEach(e=>d.push({kind:"data",row:e,rowIndex:s.get(e)??-1})))};return l(i),d}class _ extends y{static manifest={incompatibleWith:[{name:"tree",reason:"Both plugins transform the entire row model. TreePlugin flattens nested hierarchies while GroupingRowsPlugin groups flat rows with synthetic headers. Use one approach per grid."},{name:"pivot",reason:"PivotPlugin creates its own aggregated row and column structure. Row grouping cannot be applied on top of pivot-generated rows."},{name:"serverSide",reason:"Row grouping requires the full dataset to compute group boundaries. ServerSidePlugin lazy-loads rows in blocks, so groups cannot be built client-side."}],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)}@media(hover:hover){.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 r=v({rows:[...e],config:t,expanded:/* @__PURE__ */new Set});if(0===r.length)return this.isActive=!1,this.flattenedRows=[],[...e];let n;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)}(r);n=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 r=t[e];return r?/* @__PURE__ */new Set([r]):/* @__PURE__ */new Set}return"string"==typeof e?/* @__PURE__ */new Set([e]):Array.isArray(e)?new Set(e):/* @__PURE__ */new Set}(t.defaultExpanded??!1,e),n.size>0&&(this.expandedKeys=new Set(n),this.hasAppliedDefaultExpanded=!0)}const o=v({rows:[...e],config:t,expanded:this.expandedKeys,initialExpanded:n});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 r=e.row;if(r?.__isGroupRow){const n=e.originalEvent.target;if(n?.closest(`.${t}`))return this.toggle(r.__groupKey),!0}}onKeyDown(e){if(" "!==e.key)return;const t=this.grid._focusRow,r=this.rows[t];return r?.__isGroupRow?(e.preventDefault(),this.toggle(r.__groupKey),this.requestRenderWithFocus(),!0):void 0}renderRow(e,t,r){if(!e?.__isGroupRow)return!1;const n=this.config;if(n.groupRowRenderer){const r=()=>{this.toggle(e.__groupKey)},o=n.groupRowRenderer({key:e.__groupKey,value:e.__groupValue,depth:e.__groupDepth,rows:e.__groupRows,expanded:e.__groupExpanded,toggleExpand:r});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!==n.indentWidth&&t.style.setProperty("--tbw-group-indent-width",`${n.indentWidth}px`),t.style.height="",t.innerHTML="";return!1!==n.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 r="fade"===e?"tbw-group-fade-in":"tbw-group-slide-in";for(const n of t.querySelectorAll(".data-grid-row:not(.group-row)")){const e=n.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)&&(n.classList.add(r),n.addEventListener("animationend",()=>n.classList.remove(r),{once:!0}))}this.keysToAnimate.clear()}createToggleButton(r,n){const o=document.createElement("button");return o.type="button",o.className=`${t}${r?` ${e}`:""}`,o.setAttribute("aria-label",r?"Collapse group":"Expand group"),this.setIcon(o,r?"collapse":"expand"),o.addEventListener("click",e=>{e.stopPropagation(),n()}),o}getGroupLabelText(e,t,r){const n=this.config;return n.formatLabel?n.formatLabel(e,t,r):String(e)}renderFullWidthGroupRow(e,t,o){const i=this.config,a=i.aggregators??{},d=e.__groupRows??[],l=document.createElement("div");l.className="cell group-full",l.style.gridColumn="1 / -1",l.setAttribute("role","gridcell"),l.setAttribute("data-col","0"),l.appendChild(this.createToggleButton(e.__groupExpanded,o));const u=document.createElement("span");if(u.className=r,u.textContent=this.getGroupLabelText(e.__groupValue,e.__groupDepth||0,e.__groupKey),l.appendChild(u),!1!==i.showRowCount){const t=document.createElement("span");t.className=n,t.textContent=`(${e.__groupRowCount??e.__groupRows?.length??0})`,l.appendChild(t)}const g=Object.entries(a);if(g.length>0){const e=document.createElement("span");e.className="group-aggregates";for(const[t,r]of g){const n=this.columns.find(e=>e.field===t),o=s.run(r,d,t,n);if(null!=o){const r=document.createElement("span");r.className="group-aggregate",r.setAttribute("data-field",t);const i=n?.header??t;r.textContent=`${i}: ${o}`,e.appendChild(r)}}e.children.length>0&&l.appendChild(e)}t.appendChild(l)}renderPerColumnGroupRow(e,t,r){const o=this.config,i=o.aggregators??{},a=this.columns,d=e.__groupRows??[],l=this.gridElement?.querySelector(".body"),u=l?.style.gridTemplateColumns||"";u&&(t.style.display="grid",t.style.gridTemplateColumns=u);let g=!1;a.forEach((a,l)=>{const u=document.createElement("div");if(u.className="cell group-cell",u.setAttribute("data-col",String(l)),u.setAttribute("role","gridcell"),"__tbw_expander"===a.field)return u.setAttribute("data-field",a.field),void t.appendChild(u);if(g){const e=i[a.field];if(e){const t=s.run(e,d,a.field,a);u.textContent=null!=t?String(t):""}else u.textContent=""}else{g=!0,u.appendChild(this.createToggleButton(e.__groupExpanded,r));const t=document.createElement("span"),l=i[a.field];if(l){const r=s.run(l,d,a.field,a);t.textContent=String(null!=r?r:e.__groupValue)}else t.textContent=this.getGroupLabelText(e.__groupValue,e.__groupDepth||0,e.__groupKey);if(u.appendChild(t),!1!==o.showRowCount){const e=document.createElement("span");e.className=n,e.textContent=` (${d.length})`,u.appendChild(e)}}t.appendChild(u)})}expandAll(){this.expandedKeys=function(e){const t=/* @__PURE__ */new Set;for(const r of e)"group"===r.kind&&t.add(r.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),r=this.config,n=this.flattenedRows.find(t=>"group"===t.kind&&t.key===e);if(r.accordion&&t&&n){const t=/* @__PURE__ */new Set;for(const r of this.expandedKeys)if(e.startsWith(r+"||")||r.startsWith(e+"||"))e.startsWith(r+"||")&&t.add(r);else{const e=this.flattenedRows.find(e=>"group"===e.kind&&e.key===r);e&&e.depth!==n.depth&&t.add(r)}t.add(e),this.expandedKeys=t}else this.expandedKeys=function(e,t){const r=new Set(e);return r.has(t)?r.delete(t):r.add(t),r}(this.expandedKeys,e);this.emit("group-toggle",{key:e,expanded:this.expandedKeys.has(e),value:n?.value,depth:n?.depth??0});const o=this.expandedKeys.has(e),i=null!=n?.value?String(n.value):e;if(o){const e=n?.rows?.length??0;u(this.gridElement,g(this.gridElement,"groupExpanded",i,e))}else u(this.gridElement,g(this.gridElement,"groupCollapsed",i));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{_ as GroupingRowsPlugin};
1
+ const e="expanded",t="group-toggle",r="group-label",n="group-count",o={sum:(e,t)=>{let r=0;for(let n=0;n<e.length;n++)r+=Number(e[n][t])||0;return r},avg:(e,t)=>{if(!e.length)return 0;let r=0;for(let n=0;n<e.length;n++)r+=Number(e[n][t])||0;return r/e.length},count:e=>e.length,min:(e,t)=>{if(!e.length)return 0;let r=1/0;for(let n=0;n<e.length;n++){const o=Number(e[n][t]);o<r&&(r=o)}return r},max:(e,t)=>{if(!e.length)return 0;let r=-1/0;for(let n=0;n<e.length;n++){const o=Number(e[n][t]);o>r&&(r=o)}return r},first:(e,t)=>e[0]?.[t],last:(e,t)=>e[e.length-1]?.[t]},i=/* @__PURE__ */new Map,s={register(e,t){i.set(e,t)},unregister(e){i.delete(e)},get(e){if(void 0!==e)return"function"==typeof e?e:i.get(e)??o[e]},run(e,t,r,n){const o=this.get(e);return o?o(t,r,n):void 0},has:e=>i.has(e)||e in o,list:()=>[...Object.keys(o),...i.keys()]},a={sortApplied:(e,t)=>`Sorted by ${e}, ${t}`,sortCleared:()=>"Sort cleared",filterApplied:e=>`Filter applied on ${e}`,filterCleared:e=>`Filter cleared from ${e}`,allFiltersCleared:()=>"All filters cleared",groupExpanded:(e,t)=>`Group ${e} expanded, ${t} rows`,groupCollapsed:e=>`Group ${e} collapsed`,selectionChanged:e=>`${e} rows selected`,editingStarted:e=>`Editing row ${e+1}`,editingCommitted:e=>`Row ${e+1} saved`,dataLoaded:e=>`${e} rows loaded`},d='<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>',l={expand:"▶",collapse:"▼",sortAsc:"▲",sortDesc:"▼",sortNone:"⇅",submenuArrow:"▶",dragHandle:"⋮⋮",toolPanel:"☰",filter:d,filterActive:d,print:"🖨️"};function u(e,t){if(!e)return;const r="effectiveConfig"in e?e.effectiveConfig:void 0;if(!1===r?.a11y?.announcements)return;const n=e.querySelector?.(".tbw-sr-only");n&&(n.textContent="",requestAnimationFrame(()=>{n.textContent=t}))}function p(e,t,...r){const n=e&&"effectiveConfig"in e?e.effectiveConfig:void 0,o=n?.a11y?.messages?.[t];return o?o(...r):a[t](...r)}function g(e,t){return`[tbw-grid${e?`#${e}`:""}${t?`:${t}`:""}]`}function h(e,t,r,n){return`${g(r,n)} ${e}: ${t}\n\n → More info: ${function(e){return`https://toolboxjs.com/grid/errors#${e.toLowerCase()}`}(e)}`}["__otorp__","__retteGenifed__","__retteSenifed__","rotcurtsnoc","wodniw","sihTlabolg","labolg","ssecorp","noitcnuF","tropmi","lave","tcelfeR","yxorP","rorrE","stnemugra","tnemucod","noitacol","eikooc","egarotSlacol","egarotSnoisses","BDdexedni","hctef","tseuqeRpttHLMX","tekcoSbeW","rekroW","rekroWderahS","rekroWecivreS","renepo","tnerap","pot","semarf","fles"].map(e=>e.split("").reverse().join(""));const c=/* @__PURE__ */new Set(["script","iframe","object","embed","form","input","button","textarea","select","link","meta","base","style","template","slot","portal","frame","frameset","applet","noscript","noembed","plaintext","xmp","listing"]),f=/^on\w+$/i,w=/* @__PURE__ */new Set(["href","src","action","formaction","data","srcdoc","xlink:href","poster","srcset"]),m=/^\s*(javascript|vbscript|data|blob):/i;function y(e){if(!e||"string"!=typeof e)return"";if(-1===e.indexOf("<"))return e;const t=document.createElement("template");return t.innerHTML=e,function(e){const t=[],r=e.querySelectorAll("*");for(const n of r){const e=n.tagName.toLowerCase();if(c.has(e)){t.push(n);continue}if("svg"===e||"http://www.w3.org/2000/svg"===n.namespaceURI){if(Array.from(n.attributes).some(e=>f.test(e.name)||"href"===e.name||"xlink:href"===e.name)){t.push(n);continue}}const r=[];for(const t of n.attributes){const e=t.name.toLowerCase();f.test(e)?r.push(t.name):(w.has(e)&&m.test(t.value)||"style"===e&&/expression\s*\(|javascript:|behavior\s*:/i.test(t.value))&&r.push(t.name)}r.forEach(e=>n.removeAttribute(e))}t.forEach(e=>e.remove())}(t.content),t.innerHTML}class b{static dependencies;static manifest;aliases;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 r=new CustomEvent(e,{detail:t,bubbles:!0,cancelable:!0});return this.grid?.dispatchEvent?.(r),r.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?.()}requestVirtualRefresh(){this.grid?.requestVirtualRefresh?.()}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?._hostElement}get disconnectSignal(){return this.#e?.signal??this.grid?.disconnectSignal}get gridIcons(){const e=this.grid?.gridConfig?.icons??{};return{...l,...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(),r=parseInt(t,10);if(!isNaN(r))return r}return 200}setIcon(e,t,r){e.dataset.icon=t.replace(/([A-Z])/g,"-$1").toLowerCase(),"collapse"===t?e.dataset.expanded="":"expand"===t&&delete e.dataset.expanded;const n=this.#t(t,r);void 0!==n?"string"==typeof n?e.innerHTML=y(n):n instanceof HTMLElement&&(e.innerHTML="",e.appendChild(n.cloneNode(!0))):e.innerHTML=""}#t(e,t){return void 0!==t?t:this.grid?.gridConfig?.icons?.[e]}updateSortIndicator(e,t){e.querySelector('[part~="sort-indicator"], .sort-indicator')?.remove();const r=document.createElement("span");r.setAttribute("part","sort-indicator"),r.className="sort-indicator",t?(e.setAttribute("aria-sort","asc"===t?"ascending":"descending"),e.setAttribute("data-sort",t),this.setIcon(r,"asc"===t?"sortAsc":"sortDesc")):(e.setAttribute("aria-sort","none"),e.removeAttribute("data-sort"),this.setIcon(r,"sortNone"));const n=e.querySelector(".tbw-filter-btn")??e.querySelector(".resize-handle");return n?e.insertBefore(r,n):e.appendChild(r),r}warn(e,t){void 0!==t?console.warn(h(e,t,this.gridElement.id,this.name)):console.warn(`${g(this.gridElement.id,this.name)} ${e}`)}throwDiagnostic(e,t){throw new Error(h(e,t,this.gridElement.id,this.name))}}function _({rows:e,config:t,expanded:r,initialExpanded:n}){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 r=i;t.forEach((t,n)=>{const o=null==t?"∅":String(t),i="__root__"===r.key?o:r.key+"||"+o;let s=r.children.get(o);s||(s={key:i,value:t,depth:n,rows:[],children:/* @__PURE__ */new Map,parent:r},r.children.set(o,s)),s.rows.push(e),r=s})}),1===i.children.size&&i.children.has("__ungrouped__")){if(i.children.get("__ungrouped__").rows.length===e.length)return[]}const s=/* @__PURE__ */new Map;for(let u=0;u<e.length;u++)s.set(e[u],u);const a=/* @__PURE__ */new Set([...r,...n??[]]),d=[],l=e=>{if(e===i)return void e.children.forEach(e=>l(e));const t=a.has(e.key);d.push({kind:"group",key:e.key,value:e.value,depth:e.depth,rows:e.rows,expanded:t}),t&&(e.children.size?e.children.forEach(e=>l(e)):e.rows.forEach(e=>d.push({kind:"data",row:e,rowIndex:s.get(e)??-1})))};return l(i),d}function v(e,t){if(!0===e)return new Set(t);if(!1===e||null==e)/* @__PURE__ */
2
+ return new Set;if("number"==typeof e){const r=t[e];return r?/* @__PURE__ */new Set([r]):/* @__PURE__ */new Set}return"string"==typeof e?/* @__PURE__ */new Set([e]):Array.isArray(e)?new Set(e):/* @__PURE__ */new Set}function x({groups:e,expanded:t,groupRows:r,loadingGroups:n,parentPath:o=[]}){const i=[],s=o.length;for(const a of e){const e=[...o,a.key],d=t.has(a.key),l=r.get(a.key)??[],u=n.has(a.key);if(i.push({kind:"group",key:a.key,value:a.value,depth:s,rows:l,expanded:d}),d)if(a.children?.length){const o=x({groups:a.children,expanded:t,groupRows:r,loadingGroups:n,parentPath:e});i.push(...o)}else u?i.push({kind:"data",row:{__loading:!0,__groupKey:a.key},rowIndex:-1}):l.forEach((e,t)=>{i.push({kind:"data",row:e,rowIndex:t})})}return i}function R(e,t,r=[]){for(const n of e){const e=[...r,n.key];if(n.key===t)return e;if(n.children?.length){const r=R(n.children,t,e);if(r.length>0)return r}}return[]}class E extends b{static manifest={incompatibleWith:[{name:"tree",reason:"Both plugins transform the entire row model. TreePlugin flattens nested hierarchies while GroupingRowsPlugin groups flat rows with synthetic headers. Use one approach per grid."},{name:"pivot",reason:"PivotPlugin creates its own aggregated row and column structure. Row grouping cannot be applied on top of pivot-generated rows."}],events:[{type:"grouping-state-change",description:"Emitted when groups are expanded/collapsed. Subscribers can react to row visibility changes."},{type:"group-expand",description:"Emitted when a pre-defined group is expanded. Use to lazily load group row data."},{type:"group-collapse",description:"Emitted when a pre-defined group is collapsed."}],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)}@media(hover:hover){.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;preDefinedGroups=[];groupRowsMap=/* @__PURE__ */new Map;loadingGroups=/* @__PURE__ */new Set;groupsFetchInFlight=!1;rowsFetchInFlight=/* @__PURE__ */new Set;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,this.preDefinedGroups=[],this.groupRowsMap.clear(),this.loadingGroups.clear(),this.groupsFetchInFlight=!1,this.rowsFetchInFlight.clear()}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||Array.isArray(t?.groups)||"function"==typeof t?.groups}processRows(e){if(this.preDefinedGroups.length>0||null!=this.config.groups)return"function"!=typeof this.config.groups||0!==this.preDefinedGroups.length||this.groupsFetchInFlight?this.preDefinedGroups.length>0||Array.isArray(this.config.groups)&&this.config.groups.length>0?this.processPreDefinedGroups():(this.isActive="function"==typeof this.config.groups,this.flattenedRows=[],[]):(this.fetchGroupsAsync(this.config.groups),this.isActive=!0,this.flattenedRows=[],[]);const t=this.config;if("function"!=typeof t.groupOn)return this.isActive=!1,this.flattenedRows=[],[...e];const r=_({rows:[...e],config:t,expanded:/* @__PURE__ */new Set});if(0===r.length)return this.isActive=!1,this.flattenedRows=[],[...e];let n;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)}(r);n=v(t.defaultExpanded??!1,e),n.size>0&&(this.expandedKeys=new Set(n),this.hasAppliedDefaultExpanded=!0)}const o=_({rows:[...e],config:t,expanded:this.expandedKeys,initialExpanded:n});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 r=e.row;if(r?.__isGroupRow){const n=e.originalEvent.target;if(n?.closest(`.${t}`))return this.toggle(r.__groupKey),!0}}onKeyDown(e){if(" "!==e.key)return;const t=this.grid._focusRow,r=this.rows[t];return r?.__isGroupRow?(e.preventDefault(),this.toggle(r.__groupKey),this.requestRenderWithFocus(),!0):void 0}renderRow(e,t,r){if(!0===e?.__loading&&e?.__groupKey){t.className="data-grid-row",t.__isCustomRow=!0,t.innerHTML="";const e=document.createElement("div");return e.className="cell",e.style.gridColumn="1 / -1",e.setAttribute("role","gridcell"),e.textContent=" ",t.appendChild(e),function(e){if(e.classList.add("tbw-row-loading"),e.setAttribute("aria-busy","true"),!e.querySelector(".tbw-row-loading-overlay")){const t=document.createElement("div");t.className="tbw-row-loading-overlay",t.setAttribute("aria-hidden","true");const r=document.createElement("div");r.className="tbw-row-loading-spinner",t.appendChild(r),e.appendChild(t)}}(t),!0}if(!e?.__isGroupRow)return!1;const n=this.config;if(n.groupRowRenderer){const r=()=>{this.toggle(e.__groupKey)},o=n.groupRowRenderer({key:e.__groupKey,value:e.__groupValue,depth:e.__groupDepth,rows:e.__groupRows,expanded:e.__groupExpanded,toggleExpand:r});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!==n.indentWidth&&t.style.setProperty("--tbw-group-indent-width",`${n.indentWidth}px`),t.style.height="",t.innerHTML="";return!1!==n.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 r="fade"===e?"tbw-group-fade-in":"tbw-group-slide-in";for(const n of t.querySelectorAll(".data-grid-row:not(.group-row)")){const e=n.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)&&(n.classList.add(r),n.addEventListener("animationend",()=>n.classList.remove(r),{once:!0}))}this.keysToAnimate.clear()}processPreDefinedGroups(){const e=this.preDefinedGroups.length>0?this.preDefinedGroups:Array.isArray(this.config.groups)?this.config.groups:[];if(0===e.length)return this.isActive=!1,this.flattenedRows=[],[];if(!this.hasAppliedDefaultExpanded&&0===this.expandedKeys.size&&!1!==this.config.defaultExpanded){const t=this.collectGroupKeys(e),r=v(this.config.defaultExpanded??!1,t);r.size>0&&(this.expandedKeys=new Set(r),this.hasAppliedDefaultExpanded=!0)}const t=x({groups:e,expanded:this.expandedKeys,groupRows:this.groupRowsMap,loadingGroups:this.loadingGroups});this.isActive=!0,this.flattenedRows=t,this.keysToAnimate.clear();const r=/* @__PURE__ */new Set;return t.forEach((e,t)=>{if("data"===e.kind){const e=`data-${t}`;r.add(e),this.previousVisibleKeys.has(e)||this.keysToAnimate.add(e)}}),this.previousVisibleKeys=r,t.map(t=>{if("group"===t.kind){const r=this.findGroupDefinition(e,t.key),n=r?.rowCount??t.rows.length;return{__isGroupRow:!0,__groupKey:t.key,__groupValue:t.value,__groupDepth:t.depth,__groupRows:t.rows,__groupExpanded:t.expanded,__groupRowCount:n,__rowCacheKey:`group:${t.key}`}}return t.row})}fetchGroupsAsync(e){this.groupsFetchInFlight=!0,e().then(e=>{this.groupsFetchInFlight=!1,this.preDefinedGroups=e,this.requestRender()},()=>{this.groupsFetchInFlight=!1})}fetchGroupRowsAsync(e,t){const r=this.config.rows;r&&!this.rowsFetchInFlight.has(e)&&(this.rowsFetchInFlight.add(e),this.loadingGroups.add(e),this.requestRender(),r(t).then(t=>{this.rowsFetchInFlight.delete(e),this.groupRowsMap.set(e,t),this.loadingGroups.delete(e),this.requestRender()},()=>{this.rowsFetchInFlight.delete(e),this.loadingGroups.delete(e),this.requestRender()}))}collectGroupKeys(e){const t=[];for(const r of e)t.push(r.key),r.children?.length&&t.push(...this.collectGroupKeys(r.children));return t}getActiveGroups(){return this.preDefinedGroups.length>0?this.preDefinedGroups:Array.isArray(this.config.groups)?this.config.groups:[]}findGroupDefinition(e,t){for(const r of e){if(r.key===t)return r;if(r.children?.length){const e=this.findGroupDefinition(r.children,t);if(e)return e}}}createToggleButton(r,n){const o=document.createElement("button");return o.type="button",o.className=`${t}${r?` ${e}`:""}`,o.setAttribute("aria-label",r?"Collapse group":"Expand group"),this.setIcon(o,r?"collapse":"expand"),o.addEventListener("click",e=>{e.stopPropagation(),n()}),o}getGroupLabelText(e,t,r){const n=this.config;return n.formatLabel?n.formatLabel(e,t,r):String(e)}renderFullWidthGroupRow(e,t,o){const i=this.config,a=i.aggregators??{},d=e.__groupRows??[],l=document.createElement("div");l.className="cell group-full",l.style.gridColumn="1 / -1",l.setAttribute("role","gridcell"),l.setAttribute("data-col","0"),l.appendChild(this.createToggleButton(e.__groupExpanded,o));const u=document.createElement("span");if(u.className=r,u.textContent=this.getGroupLabelText(e.__groupValue,e.__groupDepth||0,e.__groupKey),l.appendChild(u),!1!==i.showRowCount){const t=document.createElement("span");t.className=n,t.textContent=`(${e.__groupRowCount??e.__groupRows?.length??0})`,l.appendChild(t)}const p=Object.entries(a);if(p.length>0){const e=document.createElement("span");e.className="group-aggregates";for(const[t,r]of p){const n=this.columns.find(e=>e.field===t),o=s.run(r,d,t,n);if(null!=o){const r=document.createElement("span");r.className="group-aggregate",r.setAttribute("data-field",t);const i=n?.header??t;r.textContent=`${i}: ${o}`,e.appendChild(r)}}e.children.length>0&&l.appendChild(e)}t.appendChild(l)}renderPerColumnGroupRow(e,t,r){const o=this.config,i=o.aggregators??{},a=this.columns,d=e.__groupRows??[],l=this.gridElement?.querySelector(".body"),u=l?.style.gridTemplateColumns||"";u&&(t.style.display="grid",t.style.gridTemplateColumns=u);let p=!1;a.forEach((a,l)=>{const u=document.createElement("div");if(u.className="cell group-cell",u.setAttribute("data-col",String(l)),u.setAttribute("role","gridcell"),"__tbw_expander"===a.field)return u.setAttribute("data-field",a.field),void t.appendChild(u);if(p){const e=i[a.field];if(e){const t=s.run(e,d,a.field,a);u.textContent=null!=t?String(t):""}else u.textContent=""}else{p=!0,u.appendChild(this.createToggleButton(e.__groupExpanded,r));const t=document.createElement("span"),l=i[a.field];if(l){const r=s.run(l,d,a.field,a);t.textContent=String(null!=r?r:e.__groupValue)}else t.textContent=this.getGroupLabelText(e.__groupValue,e.__groupDepth||0,e.__groupKey);if(u.appendChild(t),!1!==o.showRowCount){const e=document.createElement("span");e.className=n,e.textContent=` (${d.length})`,u.appendChild(e)}}t.appendChild(u)})}expandAll(){this.expandedKeys=function(e){const t=/* @__PURE__ */new Set;for(const r of e)"group"===r.kind&&t.add(r.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),r=this.config,n=this.flattenedRows.find(t=>"group"===t.kind&&t.key===e);if(r.accordion&&t&&n){const t=/* @__PURE__ */new Set;for(const r of this.expandedKeys)if(e.startsWith(r+"||")||r.startsWith(e+"||"))e.startsWith(r+"||")&&t.add(r);else{const e=this.flattenedRows.find(e=>"group"===e.kind&&e.key===r);e&&e.depth!==n.depth&&t.add(r)}t.add(e),this.expandedKeys=t}else this.expandedKeys=function(e,t){const r=new Set(e);return r.has(t)?r.delete(t):r.add(t),r}(this.expandedKeys,e);this.emit("group-toggle",{key:e,expanded:this.expandedKeys.has(e),value:n?.value,depth:n?.depth??0});const o=this.getActiveGroups();if(o.length>0){const n=R(o,e);if(t){if(this.emit("group-expand",{groupKey:e,groupPath:n}),r.rows&&!this.groupRowsMap.has(e)){const t=this.findGroupDefinition(o,e);t&&this.fetchGroupRowsAsync(e,t)}}else this.emit("group-collapse",{groupKey:e,groupPath:n})}const i=this.expandedKeys.has(e),s=null!=n?.value?String(n.value):e;if(i){const e=n?.rows?.length??0;u(this.gridElement,p(this.gridElement,"groupExpanded",s,e))}else u(this.gridElement,p(this.gridElement,"groupCollapsed",s));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()}setGroups(e){this.preDefinedGroups=e,this.groupRowsMap.clear(),this.loadingGroups.clear(),this.rowsFetchInFlight.clear(),this.expandedKeys.clear(),this.hasAppliedDefaultExpanded=!1,this.requestRender()}getGroups(){return this.preDefinedGroups.length>0?[...this.preDefinedGroups]:Array.isArray(this.config.groups)?[...this.config.groups]:[]}setGroupRows(e,t){this.groupRowsMap.set(e,t),this.loadingGroups.delete(e),this.requestRender()}setGroupLoading(e,t){t?this.loadingGroups.add(e):this.loadingGroups.delete(e),this.requestRender()}clearGroupRows(e){null!=e?(this.groupRowsMap.delete(e),this.rowsFetchInFlight.delete(e)):(this.groupRowsMap.clear(),this.rowsFetchInFlight.clear()),this.requestRender()}}export{E as GroupingRowsPlugin};
3
3
  //# sourceMappingURL=index.js.map