@toolbox-web/grid 3.10.1 → 3.10.2
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/all.js +2 -1
- package/all.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/lib/features/registry.js +1 -1
- package/lib/plugins/editing/editing-plugin.d.ts +11 -5
- package/lib/plugins/editing/index.js +2 -1
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/editing/internal/editor-injection.d.ts +9 -2
- package/lib/plugins/hierarchy/hierarchy.d.ts +5 -1
- package/lib/plugins/hierarchy/index.js +1 -1
- package/lib/plugins/hierarchy/index.js.map +1 -1
- package/package.json +1 -1
- package/umd/grid.all.umd.js +1 -1
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +1 -1
- package/umd/grid.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/grouping-rows.umd.js +1 -1
- package/umd/plugins/grouping-rows.umd.js.map +1 -1
- package/umd/plugins/hierarchy.umd.js +1 -1
- package/umd/plugins/hierarchy.umd.js.map +1 -1
- package/umd/plugins/tree.umd.js +1 -1
- package/umd/plugins/tree.umd.js.map +1 -1
|
@@ -17,9 +17,16 @@ export interface EditorInjectionDeps<T> {
|
|
|
17
17
|
editorValueCallbacks: Map<string, (newValue: unknown) => void>;
|
|
18
18
|
/** Returns `true` when an edit session is active (#activeEditRow !== -1). */
|
|
19
19
|
isEditSessionActive: () => boolean;
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Commit a single cell value change. `rowIndex` is honoured in grid mode only — in
|
|
22
|
+
* row mode the plugin substitutes the active edit row, because editors outlive
|
|
23
|
+
* their row moving to another index and this index can be stale (#572).
|
|
24
|
+
*/
|
|
21
25
|
commitCellValue: (rowIndex: number, column: ColumnConfig<T>, newValue: unknown, rowData: T) => void;
|
|
22
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Exit editing for a row (commit or revert). `rowIndex` is honoured in grid mode
|
|
28
|
+
* only — row mode always exits the active edit row (#572).
|
|
29
|
+
*/
|
|
23
30
|
exitRowEdit: (rowIndex: number, revert: boolean) => void;
|
|
24
31
|
}
|
|
25
32
|
/**
|
|
@@ -22,7 +22,11 @@ import { HierarchyContributor, HierarchyRowMeta } from './types';
|
|
|
22
22
|
* @param rows - The root rows.
|
|
23
23
|
* @param contributors - Contributors to run, in `processRows` hook order.
|
|
24
24
|
* @param meta - Receives each emitted row's position. Pass a fresh map per rebuild.
|
|
25
|
+
* @param substitute - Optional. Maps a row to the object to use in its place, at
|
|
26
|
+
* every nesting level, before any contributor sees it. Return the row itself to
|
|
27
|
+
* keep it. `HierarchyPlugin` uses this to keep a row being edited when the data
|
|
28
|
+
* is replaced with new objects. Added in 3.10.2.
|
|
25
29
|
* @returns The flattened rows, in render order.
|
|
26
30
|
* @since 3.9.0
|
|
27
31
|
*/
|
|
28
|
-
export declare function buildHierarchy(rows: readonly unknown[], contributors: readonly HierarchyContributor[], meta?: WeakMap<object, HierarchyRowMeta
|
|
32
|
+
export declare function buildHierarchy(rows: readonly unknown[], contributors: readonly HierarchyContributor[], meta?: WeakMap<object, HierarchyRowMeta>, substitute?: (row: unknown) => unknown): unknown[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{BaseGridPlugin as t}from"@toolbox-web/grid";function e(t,e,r=/* @__PURE__ */new WeakMap){const o=[];for(const
|
|
1
|
+
import{BaseGridPlugin as t}from"@toolbox-web/grid";function e(t,e,r=/* @__PURE__ */new WeakMap,o){const n=o?function(t,e){let r;for(let o=0;o<t.length;o++){const n=e(t[o]);n!==t[o]&&(r??=t.slice(),r[o]=n)}return r??t}(t,o):t,i=[];for(const h of["structure","transform"])for(const t of e)t.hierarchyStage===h&&t.beginHierarchy(n)&&i.push(t);if(0===i.length)return[...n];const s=[],l=(t,e,r)=>{o&&e&&(t=function(t,e){let r;for(let o=0;o<t.length;o++){const n=e(t[o].row);n!==t[o].row&&(r??=t.slice(),r[o]={...t[o],row:n})}return r??t}(t,o));const n={parent:e?e.row:null,parentKey:e?.key??null,depth:r};for(const o of i)t=o.processSiblings(t,n);return t},u=(t,e)=>{const o=t.length;for(let n=0;n<o;n++){const i=t[n];s.push(i.row),"object"==typeof i.row&&null!==i.row&&r.set(i.row,{level:e+1,setSize:o,posInSet:n+1});const a=i.children;i.expanded&&a&&u(i.shaped?a:l(a,i,e+1),e+1)}},a=new Array(n.length);for(let h=0;h<n.length;h++)a[h]={row:n[h]};u(l(a,null,0),0);const c=t=>"object"==typeof t&&null!==t?r.get(t):void 0;let f=!1;for(const h of i)h.endHierarchy(s,c)&&(f=!0);if(!f)for(const h of s)"object"==typeof h&&null!==h&&r.delete(h);return s}function r(t,e,r){const o=String(r);t.getAttribute(e)!==o&&t.setAttribute(e,o)}function o(t){return"function"==typeof t.processSiblings}function n(t){return t.constructor.manifest?.hookPriority?.processRows??0}var i=class extends t{static manifest={modifiesRowStructure:!0};name="hierarchy";#t=/* @__PURE__ */new WeakMap;detach(){this.gridElement?.querySelector(".rows-body")?.setAttribute("role","grid"),this.#t=/* @__PURE__ */new WeakMap}process(t,r){const o=this.#e();return o.includes(r)?o[o.length-1]!==r?t:(this.#t=/* @__PURE__ */new WeakMap,e(t,o,this.#t,this.#r())):(this.#t=/* @__PURE__ */new WeakMap,e(t,[r],this.#t,this.#r()))}#r(){const t=this.grid?.query?.("getRowSubstitutes",null)??[];let e;for(const o of t)if(o?.size){e??=/* @__PURE__ */new Map;for(const[t,r]of o)e.set(t,r)}if(!e)return;const r=this.grid;return t=>{if("object"!=typeof t||null===t)return t;let o;try{o=r.getRowId(t)}catch{return t}return e.has(o)?e.get(o):t}}getRowMeta(t){return"object"==typeof t&&null!==t?this.#t.get(t):void 0}afterRender(){this.#o()}onScrollRender(){this.#o()}#e(){return(this.grid?._pluginManager?.getPlugins?.()??[]).filter(o).sort((t,e)=>n(t)-n(e))}#o(){if(0===this.#e().length)return;const t=this.gridElement?.querySelector(".rows");if(!t)return;let e=!1;for(const i of t.querySelectorAll(".data-grid-row")){const t=this.getRowMeta(i.__rowDataRef);t?(e=!0,r(i,"aria-level",t.level),r(i,"aria-setsize",t.setSize),r(i,"aria-posinset",t.posInSet)):(i.removeAttribute("aria-level"),i.removeAttribute("aria-setsize"),i.removeAttribute("aria-posinset"))}const o=e?"treegrid":"grid",n=this.gridElement?.querySelector(".rows-body");n&&n.getAttribute("role")!==o&&n.setAttribute("role",o)}};export{i as HierarchyPlugin,e as buildHierarchy};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["BaseGridPlugin","buildHierarchy","rows","contributors","meta","WeakMap","active","stage","c","hierarchyStage","beginHierarchy","push","length","out","process","nodes","parent","depth","context","row","parentKey","key","processSiblings","emit","setSize","i","node","set","level","posInSet","children","expanded","shaped","roots","Array","positionOf","get","endHierarchy","delete","setAttrIfChanged","el","name","value","next","String","getAttribute","setAttribute","isContributor","plugin","processRowsPriority","constructor","manifest","hookPriority","processRows","HierarchyPlugin","static","modifiesRowStructure","detach","this","gridElement","querySelector","caller","includes","getRowMeta","afterRender","applyAria","onScrollRender","grid","_pluginManager","getPlugins","filter","sort","a","b","body","hierarchical","rowEl","querySelectorAll","__rowDataRef","removeAttribute","role","rowsBody"],"sources":["../../../../../../libs/grid/src/lib/plugins/hierarchy/hierarchy.ts","../../../../../../libs/grid/src/lib/plugins/hierarchy/hierarchy-plugin.ts"],"sourcesContent":["/**\n * Hierarchy Engine\n *\n * Pure row-model builder shared by the HierarchyPlugin and its contributors.\n */\n\nimport type { HierarchyContributor, HierarchyNode, HierarchyRowMeta } from './types';\n\n/**\n * Build the flat row list for `rows` from every contributor's sibling-list\n * transforms, recording each emitted row's ARIA position in `meta`.\n *\n * Contributors whose `beginHierarchy` returns `false` sit out the rebuild.\n * With no active contributor the rows pass through unchanged. When every\n * active contributor's `endHierarchy` returns `false`, no position is kept in `meta`.\n *\n * This is what `HierarchyPlugin.process()` runs for all attached contributors.\n * Call it directly to run a contributor without a grid — e.g. as the\n * `processRows` fallback when no HierarchyPlugin is attached, or in unit tests.\n *\n * @example\n * ```ts\n * override processRows(rows: readonly unknown[]): unknown[] {\n * const hierarchy = this.grid?.getPluginByName('hierarchy');\n * return hierarchy ? hierarchy.process(rows, this) : buildHierarchy(rows, [this]);\n * }\n * ```\n *\n * @param rows - The root rows.\n * @param contributors - Contributors to run, in `processRows` hook order.\n * @param meta - Receives each emitted row's position. Pass a fresh map per rebuild.\n * @returns The flattened rows, in render order.\n * @since 3.9.0\n */\nexport function buildHierarchy(\n rows: readonly unknown[],\n contributors: readonly HierarchyContributor[],\n meta: WeakMap<object, HierarchyRowMeta> = new WeakMap(),\n): unknown[] {\n const active: HierarchyContributor[] = [];\n for (const stage of ['structure', 'transform'] as const) {\n for (const c of contributors) {\n if (c.hierarchyStage === stage && c.beginHierarchy(rows)) active.push(c);\n }\n }\n if (active.length === 0) return [...rows];\n\n const out: unknown[] = [];\n const process = (nodes: HierarchyNode[], parent: HierarchyNode | null, depth: number): HierarchyNode[] => {\n const context = { parent: parent ? parent.row : null, parentKey: parent?.key ?? null, depth };\n for (const c of active) nodes = c.processSiblings(nodes, context);\n return nodes;\n };\n const emit = (nodes: HierarchyNode[], depth: number): void => {\n const setSize = nodes.length;\n for (let i = 0; i < setSize; i++) {\n const node = nodes[i];\n out.push(node.row);\n if (typeof node.row === 'object' && node.row !== null) {\n meta.set(node.row, { level: depth + 1, setSize, posInSet: i + 1 });\n }\n const children = node.children;\n if (node.expanded && children) {\n emit(node.shaped ? children : process(children, node, depth + 1), depth + 1);\n }\n }\n };\n\n const roots: HierarchyNode[] = new Array(rows.length);\n for (let i = 0; i < rows.length; i++) roots[i] = { row: rows[i] };\n emit(process(roots, null, 0), 0);\n\n const positionOf = (row: unknown) => (typeof row === 'object' && row !== null ? meta.get(row) : undefined);\n let shaped = false;\n for (const c of active) if (c.endHierarchy(out, positionOf)) shaped = true;\n if (!shaped) {\n for (const row of out) if (typeof row === 'object' && row !== null) meta.delete(row);\n }\n return out;\n}\n","/**\n * Hierarchy Plugin\n *\n * Owns the hierarchical row model shared by structure-contributing plugins.\n */\n\nimport type { PluginManifest } from '../../core/plugin/base-plugin';\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { RowElementInternal } from '../../core/types';\nimport { buildHierarchy } from './hierarchy';\nimport type { HierarchyContributor, HierarchyRowMeta } from './types';\n\n/** Attribute write that skips the DOM mutation when the value is unchanged (per-frame hot path). */\nfunction setAttrIfChanged(el: Element, name: string, value: number): void {\n const next = String(value);\n if (el.getAttribute(name) !== next) el.setAttribute(name, next);\n}\n\nfunction isContributor(plugin: unknown): plugin is HierarchyContributor & BaseGridPlugin {\n return typeof (plugin as Partial<HierarchyContributor>).processSiblings === 'function';\n}\n\nfunction processRowsPriority(plugin: BaseGridPlugin): number {\n return (plugin.constructor as typeof BaseGridPlugin).manifest?.hookPriority?.processRows ?? 0;\n}\n\n/**\n * Hierarchy Plugin for tbw-grid\n *\n * Builds one hierarchical row model from every attached structure contributor\n * — {@link https://toolboxjs.com/grid/plugins/tree/ | TreePlugin} discovers\n * child rows, {@link https://toolboxjs.com/grid/plugins/grouping-rows/ | GroupingRowsPlugin}\n * groups sibling lists — and announces each row's position with the WAI-ARIA\n * treegrid pattern (`aria-level` / `aria-setsize` / `aria-posinset`).\n *\n * Contributors know nothing about each other, so they compose: a grid with\n * both Tree and GroupingRows groups the children of every tree level.\n *\n * You never need to add this plugin yourself — TreePlugin and\n * GroupingRowsPlugin declare it as a dependency and the grid attaches it\n * automatically. It has no configuration.\n *\n * @example Group the children of every tree node\n * ```ts\n * import { TreePlugin } from '@toolbox-web/grid/plugins/tree';\n * import { GroupingRowsPlugin } from '@toolbox-web/grid/plugins/grouping-rows';\n *\n * grid.gridConfig = {\n * plugins: [\n * new TreePlugin({ childrenField: 'children' }),\n * new GroupingRowsPlugin({\n * // Leave the root level ungrouped, group every child level by kind\n * groupOn: (row, { depth }) => (depth > 0 ? row.kind : null),\n * }),\n * ],\n * };\n * ```\n *\n * @since 3.9.0\n */\nexport class HierarchyPlugin extends BaseGridPlugin {\n /** @internal */\n static override readonly manifest: PluginManifest = {\n modifiesRowStructure: true,\n };\n\n /** @internal */\n readonly name = 'hierarchy';\n\n #meta = new WeakMap<object, HierarchyRowMeta>();\n\n /** @internal */\n override detach(): void {\n // Restore the default `role=\"grid\"` (template default in `core/internal/dom-builder.ts`).\n this.gridElement?.querySelector('.rows-body')?.setAttribute('role', 'grid');\n this.#meta = new WeakMap();\n }\n\n /**\n * Build the row model for a contributor's `processRows` hook.\n *\n * Every contributor forwards its hook here; only the one that runs last in\n * the `processRows` pipeline builds the model (for all contributors at\n * once) — the earlier ones pass their rows through untouched.\n *\n * @example\n * ```ts\n * override processRows(rows: readonly unknown[]): unknown[] {\n * return this.grid.getPluginByName('hierarchy')?.process(rows, this) ?? [...rows];\n * }\n * ```\n *\n * @since 3.9.0\n */\n process(rows: readonly unknown[], caller: HierarchyContributor): unknown[] {\n const contributors = this.#contributors();\n if (!contributors.includes(caller as HierarchyContributor & BaseGridPlugin)) {\n this.#meta = new WeakMap();\n return buildHierarchy(rows, [caller], this.#meta);\n }\n if (contributors[contributors.length - 1] !== caller) return rows as unknown[];\n this.#meta = new WeakMap();\n return buildHierarchy(rows, contributors, this.#meta);\n }\n\n /**\n * Hierarchy position of a row in the current row model, or `undefined` when\n * the row is not rendered (e.g. collapsed under a parent).\n *\n * @example\n * ```ts\n * const meta = grid.getPluginByName('hierarchy')?.getRowMeta(grid.rows[0]);\n * console.log(meta?.level, meta?.posInSet, meta?.setSize);\n * ```\n *\n * @since 3.9.0\n */\n getRowMeta(row: unknown): HierarchyRowMeta | undefined {\n return typeof row === 'object' && row !== null ? this.#meta.get(row) : undefined;\n }\n\n /** @internal */\n override afterRender(): void {\n this.#applyAria();\n }\n\n /** @internal */\n override onScrollRender(): void {\n this.#applyAria();\n }\n\n /** Attached contributors, in `processRows` hook order. */\n #contributors(): Array<HierarchyContributor & BaseGridPlugin> {\n const plugins = (this.grid?._pluginManager?.getPlugins?.() ?? []) as readonly BaseGridPlugin[];\n return plugins.filter(isContributor).sort((a, b) => processRowsPriority(a) - processRowsPriority(b));\n }\n\n #applyAria(): void {\n if (this.#contributors().length === 0) return;\n const body = this.gridElement?.querySelector('.rows');\n if (!body) return;\n\n let hierarchical = false;\n for (const rowEl of body.querySelectorAll('.data-grid-row')) {\n const meta = this.getRowMeta((rowEl as RowElementInternal).__rowDataRef);\n if (!meta) {\n // Pooled row elements may carry a position from a previous row model.\n rowEl.removeAttribute('aria-level');\n rowEl.removeAttribute('aria-setsize');\n rowEl.removeAttribute('aria-posinset');\n continue;\n }\n hierarchical = true;\n setAttrIfChanged(rowEl, 'aria-level', meta.level);\n setAttrIfChanged(rowEl, 'aria-setsize', meta.setSize);\n setAttrIfChanged(rowEl, 'aria-posinset', meta.posInSet);\n }\n\n // `treegrid` only while the model has hierarchy, so per-row level/setsize/posinset are valid in context.\n const role = hierarchical ? 'treegrid' : 'grid';\n const rowsBody = this.gridElement?.querySelector('.rows-body');\n if (rowsBody && rowsBody.getAttribute('role') !== role) rowsBody.setAttribute('role', role);\n }\n}\n"],"mappings":"yB,0BAkCA,SAAgBC,EACdC,EACAC,EACAC,iBAA0C,IAAIC,SAE9C,MAAMC,EAAiC,GACvC,IAAK,MAAMC,IAAS,CAAC,YAAa,aAChC,IAAK,MAAMC,KAAKL,EACVK,EAAEC,iBAAmBF,GAASC,EAAEE,eAAeR,IAAOI,EAAOK,KAAKH,GAG1E,GAAsB,IAAlBF,EAAOM,OAAc,MAAO,IAAIV,GAEpC,MAAMW,EAAiB,GACjBC,EAAA,CAAWC,EAAwBC,EAA8BC,KACrE,MAAMC,EAAU,CAAEF,OAAQA,EAASA,EAAOG,IAAM,KAAMC,UAAWJ,GAAQK,KAAO,KAAMJ,SACtF,IAAK,MAAMT,KAAKF,EAAQS,EAAQP,EAAEc,gBAAgBP,EAAOG,GACzD,OAAOH,GAEHQ,EAAA,CAAQR,EAAwBE,KACpC,MAAMO,EAAUT,EAAMH,OACtB,IAAK,IAAIa,EAAI,EAAGA,EAAID,EAASC,IAAK,CAChC,MAAMC,EAAOX,EAAMU,GACnBZ,EAAIF,KAAKe,EAAKP,KACU,iBAAbO,EAAKP,KAAiC,OAAbO,EAAKP,KACvCf,EAAKuB,IAAID,EAAKP,IAAK,CAAES,MAAOX,EAAQ,EAAGO,UAASK,SAAUJ,EAAI,IAEhE,MAAMK,EAAWJ,EAAKI,SAClBJ,EAAKK,UAAYD,GACnBP,EAAKG,EAAKM,OAASF,EAAWhB,EAAQgB,EAAUJ,EAAMT,EAAQ,GAAIA,EAAQ,EAE9E,GAGIgB,EAAyB,IAAIC,MAAMhC,EAAKU,QAC9C,IAAK,IAAIa,EAAI,EAAGA,EAAIvB,EAAKU,OAAQa,IAAKQ,EAAMR,GAAK,CAAEN,IAAKjB,EAAKuB,IAC7DF,EAAKT,EAAQmB,EAAO,KAAM,GAAI,GAE9B,MAAME,EAAchB,GAAiC,iBAARA,GAA4B,OAARA,EAAef,EAAKgC,IAAIjB,QAAO,EAChG,IAAIa,GAAS,EACb,IAAK,MAAMxB,KAAKF,EAAYE,EAAE6B,aAAaxB,EAAKsB,KAAaH,GAAS,GACtE,IAAKA,EACE,IAAA,MAAMb,KAAON,EAAwB,iBAARM,GAA4B,OAARA,GAAcf,EAAKkC,OAAOnB,GAElF,OAAON,CACT,CClEA,SAAS0B,EAAiBC,EAAaC,EAAcC,GACnD,MAAMC,EAAOC,OAAOF,GAChBF,EAAGK,aAAaJ,KAAUE,GAAMH,EAAGM,aAAaL,EAAME,EAC5D,CAEA,SAASI,EAAcC,GACrB,MAA4E,mBAA7DA,EAAyC1B,eAC1D,CAEA,SAAS2B,EAAoBD,GAC3B,OAAQA,EAAOE,YAAsCC,UAAUC,cAAcC,aAAe,CAC9F,CAoCA,IAAaC,EAAb,cAAqCtD,EAEnCuD,gBAAoD,CAClDC,sBAAsB,GAIxBf,KAAgB,YAEhBrC,kBAAQ,IAAIC,QAGZ,MAAAoD,GAEEC,KAAKC,aAAaC,cAAc,eAAed,aAAa,OAAQ,QACpEY,MAAKtD,iBAAQ,IAAIC,OACnB,CAkBA,OAAAS,CAAQZ,EAA0B2D,GAChC,MAAM1D,EAAeuD,MAAKvD,IAC1B,OAAKA,EAAa2D,SAASD,GAIvB1D,EAAaA,EAAaS,OAAS,KAAOiD,EAAe3D,GAC7DwD,MAAKtD,iBAAQ,IAAIC,QACVJ,EAAeC,EAAMC,EAAcuD,MAAKtD,KAL7CsD,MAAKtD,iBAAQ,IAAIC,QACVJ,EAAeC,EAAM,CAAC2D,GAASH,MAAKtD,GAK/C,CAcA,UAAA2D,CAAW5C,GACT,MAAsB,iBAARA,GAA4B,OAARA,EAAeuC,MAAKtD,EAAMgC,IAAIjB,QAAO,CACzE,CAGA,WAAA6C,GACEN,MAAKO,GACP,CAGA,cAAAC,GACER,MAAKO,GACP,CAGA,EAAA9D,GAEE,OADiBuD,KAAKS,MAAMC,gBAAgBC,gBAAkB,IAC/CC,OAAOvB,GAAewB,KAAA,CAAMC,EAAGC,IAAMxB,EAAoBuB,GAAKvB,EAAoBwB,GACnG,CAEA,EAAAR,GACE,GAAoC,IAAhCP,MAAKvD,IAAgBS,OAAc,OACvC,MAAM8D,EAAOhB,KAAKC,aAAaC,cAAc,SAC7C,IAAKc,EAAM,OAEX,IAAIC,GAAe,EACnB,IAAK,MAAMC,KAASF,EAAKG,iBAAiB,kBAAmB,CAC3D,MAAMzE,EAAOsD,KAAKK,WAAYa,EAA6BE,cACtD1E,GAOLuE,GAAe,EACfpC,EAAiBqC,EAAO,aAAcxE,EAAKwB,OAC3CW,EAAiBqC,EAAO,eAAgBxE,EAAKoB,SAC7Ce,EAAiBqC,EAAO,gBAAiBxE,EAAKyB,YAR5C+C,EAAMG,gBAAgB,cACtBH,EAAMG,gBAAgB,gBACtBH,EAAMG,gBAAgB,iBAO1B,CAGA,MAAMC,EAAOL,EAAe,WAAa,OACnCM,EAAWvB,KAAKC,aAAaC,cAAc,cAC7CqB,GAAYA,EAASpC,aAAa,UAAYmC,GAAMC,EAASnC,aAAa,OAAQkC,EACxF,U"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["BaseGridPlugin","buildHierarchy","rows","contributors","meta","WeakMap","substitute","roots","out","i","length","row","slice","substituteValues","active","stage","c","hierarchyStage","beginHierarchy","push","process","nodes","parent","depth","substituteRows","context","parentKey","key","processSiblings","emit","setSize","node","set","level","posInSet","children","expanded","shaped","rootNodes","Array","positionOf","get","endHierarchy","delete","setAttrIfChanged","el","name","value","next","String","getAttribute","setAttribute","isContributor","plugin","processRowsPriority","constructor","manifest","hookPriority","processRows","HierarchyPlugin","static","modifiesRowStructure","detach","this","gridElement","querySelector","caller","includes","rowSubstitute","responses","grid","query","pinned","map","size","Map","id","getRowId","has","getRowMeta","afterRender","applyAria","onScrollRender","_pluginManager","getPlugins","filter","sort","a","b","body","hierarchical","rowEl","querySelectorAll","__rowDataRef","removeAttribute","role","rowsBody"],"sources":["../../../../../../libs/grid/src/lib/plugins/hierarchy/hierarchy.ts","../../../../../../libs/grid/src/lib/plugins/hierarchy/hierarchy-plugin.ts"],"sourcesContent":["/**\n * Hierarchy Engine\n *\n * Pure row-model builder shared by the HierarchyPlugin and its contributors.\n */\n\nimport type { HierarchyContributor, HierarchyNode, HierarchyRowMeta } from './types';\n\n/** Copy-on-write: contributors may hold on to node objects, so a substituted node is a new object. */\nfunction substituteRows(nodes: HierarchyNode[], substitute: (row: unknown) => unknown): HierarchyNode[] {\n let out: HierarchyNode[] | undefined;\n for (let i = 0; i < nodes.length; i++) {\n const row = substitute(nodes[i].row);\n if (row === nodes[i].row) continue;\n out ??= nodes.slice();\n out[i] = { ...nodes[i], row };\n }\n return out ?? nodes;\n}\n\n/** Copy-on-write substitution of a plain row list (the roots, before any node exists). */\nfunction substituteValues(rows: readonly unknown[], substitute: (row: unknown) => unknown): readonly unknown[] {\n let out: unknown[] | undefined;\n for (let i = 0; i < rows.length; i++) {\n const row = substitute(rows[i]);\n if (row === rows[i]) continue;\n out ??= rows.slice();\n out[i] = row;\n }\n return out ?? rows;\n}\n\n/**\n * Build the flat row list for `rows` from every contributor's sibling-list\n * transforms, recording each emitted row's ARIA position in `meta`.\n *\n * Contributors whose `beginHierarchy` returns `false` sit out the rebuild.\n * With no active contributor the rows pass through unchanged. When every\n * active contributor's `endHierarchy` returns `false`, no position is kept in `meta`.\n *\n * This is what `HierarchyPlugin.process()` runs for all attached contributors.\n * Call it directly to run a contributor without a grid — e.g. as the\n * `processRows` fallback when no HierarchyPlugin is attached, or in unit tests.\n *\n * @example\n * ```ts\n * override processRows(rows: readonly unknown[]): unknown[] {\n * const hierarchy = this.grid?.getPluginByName('hierarchy');\n * return hierarchy ? hierarchy.process(rows, this) : buildHierarchy(rows, [this]);\n * }\n * ```\n *\n * @param rows - The root rows.\n * @param contributors - Contributors to run, in `processRows` hook order.\n * @param meta - Receives each emitted row's position. Pass a fresh map per rebuild.\n * @param substitute - Optional. Maps a row to the object to use in its place, at\n * every nesting level, before any contributor sees it. Return the row itself to\n * keep it. `HierarchyPlugin` uses this to keep a row being edited when the data\n * is replaced with new objects. Added in 3.10.2.\n * @returns The flattened rows, in render order.\n * @since 3.9.0\n */\nexport function buildHierarchy(\n rows: readonly unknown[],\n contributors: readonly HierarchyContributor[],\n meta: WeakMap<object, HierarchyRowMeta> = new WeakMap(),\n substitute?: (row: unknown) => unknown,\n): unknown[] {\n // Roots are substituted BEFORE activation: `beginHierarchy` decides from them whether\n // there is any structure (a pinned parent may be the only row with children).\n const roots = substitute ? substituteValues(rows, substitute) : rows;\n const active: HierarchyContributor[] = [];\n for (const stage of ['structure', 'transform'] as const) {\n for (const c of contributors) {\n if (c.hierarchyStage === stage && c.beginHierarchy(roots)) active.push(c);\n }\n }\n if (active.length === 0) return [...roots];\n\n const out: unknown[] = [];\n const process = (nodes: HierarchyNode[], parent: HierarchyNode | null, depth: number): HierarchyNode[] => {\n if (substitute && parent) nodes = substituteRows(nodes, substitute);\n const context = { parent: parent ? parent.row : null, parentKey: parent?.key ?? null, depth };\n for (const c of active) nodes = c.processSiblings(nodes, context);\n return nodes;\n };\n const emit = (nodes: HierarchyNode[], depth: number): void => {\n const setSize = nodes.length;\n for (let i = 0; i < setSize; i++) {\n const node = nodes[i];\n out.push(node.row);\n if (typeof node.row === 'object' && node.row !== null) {\n meta.set(node.row, { level: depth + 1, setSize, posInSet: i + 1 });\n }\n const children = node.children;\n if (node.expanded && children) {\n emit(node.shaped ? children : process(children, node, depth + 1), depth + 1);\n }\n }\n };\n\n const rootNodes: HierarchyNode[] = new Array(roots.length);\n for (let i = 0; i < roots.length; i++) rootNodes[i] = { row: roots[i] };\n emit(process(rootNodes, null, 0), 0);\n\n const positionOf = (row: unknown) => (typeof row === 'object' && row !== null ? meta.get(row) : undefined);\n let shaped = false;\n for (const c of active) if (c.endHierarchy(out, positionOf)) shaped = true;\n if (!shaped) {\n for (const row of out) if (typeof row === 'object' && row !== null) meta.delete(row);\n }\n return out;\n}\n","/**\n * Hierarchy Plugin\n *\n * Owns the hierarchical row model shared by structure-contributing plugins.\n */\n\nimport type { PluginManifest } from '../../core/plugin/base-plugin';\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { RowElementInternal } from '../../core/types';\nimport { buildHierarchy } from './hierarchy';\nimport type { HierarchyContributor, HierarchyRowMeta } from './types';\n\n/** Attribute write that skips the DOM mutation when the value is unchanged (per-frame hot path). */\nfunction setAttrIfChanged(el: Element, name: string, value: number): void {\n const next = String(value);\n if (el.getAttribute(name) !== next) el.setAttribute(name, next);\n}\n\nfunction isContributor(plugin: unknown): plugin is HierarchyContributor & BaseGridPlugin {\n return typeof (plugin as Partial<HierarchyContributor>).processSiblings === 'function';\n}\n\nfunction processRowsPriority(plugin: BaseGridPlugin): number {\n return (plugin.constructor as typeof BaseGridPlugin).manifest?.hookPriority?.processRows ?? 0;\n}\n\n/**\n * Hierarchy Plugin for tbw-grid\n *\n * Builds one hierarchical row model from every attached structure contributor\n * — {@link https://toolboxjs.com/grid/plugins/tree/ | TreePlugin} discovers\n * child rows, {@link https://toolboxjs.com/grid/plugins/grouping-rows/ | GroupingRowsPlugin}\n * groups sibling lists — and announces each row's position with the WAI-ARIA\n * treegrid pattern (`aria-level` / `aria-setsize` / `aria-posinset`).\n *\n * Contributors know nothing about each other, so they compose: a grid with\n * both Tree and GroupingRows groups the children of every tree level.\n *\n * You never need to add this plugin yourself — TreePlugin and\n * GroupingRowsPlugin declare it as a dependency and the grid attaches it\n * automatically. It has no configuration.\n *\n * @example Group the children of every tree node\n * ```ts\n * import { TreePlugin } from '@toolbox-web/grid/plugins/tree';\n * import { GroupingRowsPlugin } from '@toolbox-web/grid/plugins/grouping-rows';\n *\n * grid.gridConfig = {\n * plugins: [\n * new TreePlugin({ childrenField: 'children' }),\n * new GroupingRowsPlugin({\n * // Leave the root level ungrouped, group every child level by kind\n * groupOn: (row, { depth }) => (depth > 0 ? row.kind : null),\n * }),\n * ],\n * };\n * ```\n *\n * @since 3.9.0\n */\nexport class HierarchyPlugin extends BaseGridPlugin {\n /** @internal */\n static override readonly manifest: PluginManifest = {\n modifiesRowStructure: true,\n };\n\n /** @internal */\n readonly name = 'hierarchy';\n\n #meta = new WeakMap<object, HierarchyRowMeta>();\n\n /** @internal */\n override detach(): void {\n // Restore the default `role=\"grid\"` (template default in `core/internal/dom-builder.ts`).\n this.gridElement?.querySelector('.rows-body')?.setAttribute('role', 'grid');\n this.#meta = new WeakMap();\n }\n\n /**\n * Build the row model for a contributor's `processRows` hook.\n *\n * Every contributor forwards its hook here; only the one that runs last in\n * the `processRows` pipeline builds the model (for all contributors at\n * once) — the earlier ones pass their rows through untouched.\n *\n * @example\n * ```ts\n * override processRows(rows: readonly unknown[]): unknown[] {\n * return this.grid.getPluginByName('hierarchy')?.process(rows, this) ?? [...rows];\n * }\n * ```\n *\n * @since 3.9.0\n */\n process(rows: readonly unknown[], caller: HierarchyContributor): unknown[] {\n const contributors = this.#contributors();\n if (!contributors.includes(caller as HierarchyContributor & BaseGridPlugin)) {\n this.#meta = new WeakMap();\n return buildHierarchy(rows, [caller], this.#meta, this.#rowSubstitute());\n }\n if (contributors[contributors.length - 1] !== caller) return rows as unknown[];\n this.#meta = new WeakMap();\n return buildHierarchy(rows, contributors, this.#meta, this.#rowSubstitute());\n }\n\n /**\n * Rows other plugins pin by ID for this rebuild (`getRowSubstitutes` query) — e.g.\n * EditingPlugin's in-progress row, so an edit survives `rows` being replaced with\n * new objects even when the row is nested (#570). `undefined` when nothing is pinned.\n */\n #rowSubstitute(): ((row: unknown) => unknown) | undefined {\n const responses = this.grid?.query?.<ReadonlyMap<string, unknown> | undefined>('getRowSubstitutes', null) ?? [];\n let pinned: Map<string, unknown> | undefined;\n for (const map of responses) {\n if (!map?.size) continue;\n pinned ??= new Map();\n for (const [id, row] of map) pinned.set(id, row);\n }\n if (!pinned) return undefined;\n const grid = this.grid;\n return (row) => {\n if (typeof row !== 'object' || row === null) return row;\n let id: string | undefined;\n try {\n id = grid.getRowId(row);\n } catch {\n return row;\n }\n return pinned.has(id) ? pinned.get(id) : row;\n };\n }\n\n /**\n * Hierarchy position of a row in the current row model, or `undefined` when\n * the row is not rendered (e.g. collapsed under a parent).\n *\n * @example\n * ```ts\n * const meta = grid.getPluginByName('hierarchy')?.getRowMeta(grid.rows[0]);\n * console.log(meta?.level, meta?.posInSet, meta?.setSize);\n * ```\n *\n * @since 3.9.0\n */\n getRowMeta(row: unknown): HierarchyRowMeta | undefined {\n return typeof row === 'object' && row !== null ? this.#meta.get(row) : undefined;\n }\n\n /** @internal */\n override afterRender(): void {\n this.#applyAria();\n }\n\n /** @internal */\n override onScrollRender(): void {\n this.#applyAria();\n }\n\n /** Attached contributors, in `processRows` hook order. */\n #contributors(): Array<HierarchyContributor & BaseGridPlugin> {\n const plugins = (this.grid?._pluginManager?.getPlugins?.() ?? []) as readonly BaseGridPlugin[];\n return plugins.filter(isContributor).sort((a, b) => processRowsPriority(a) - processRowsPriority(b));\n }\n\n #applyAria(): void {\n if (this.#contributors().length === 0) return;\n const body = this.gridElement?.querySelector('.rows');\n if (!body) return;\n\n let hierarchical = false;\n for (const rowEl of body.querySelectorAll('.data-grid-row')) {\n const meta = this.getRowMeta((rowEl as RowElementInternal).__rowDataRef);\n if (!meta) {\n // Pooled row elements may carry a position from a previous row model.\n rowEl.removeAttribute('aria-level');\n rowEl.removeAttribute('aria-setsize');\n rowEl.removeAttribute('aria-posinset');\n continue;\n }\n hierarchical = true;\n setAttrIfChanged(rowEl, 'aria-level', meta.level);\n setAttrIfChanged(rowEl, 'aria-setsize', meta.setSize);\n setAttrIfChanged(rowEl, 'aria-posinset', meta.posInSet);\n }\n\n // `treegrid` only while the model has hierarchy, so per-row level/setsize/posinset are valid in context.\n const role = hierarchical ? 'treegrid' : 'grid';\n const rowsBody = this.gridElement?.querySelector('.rows-body');\n if (rowsBody && rowsBody.getAttribute('role') !== role) rowsBody.setAttribute('role', role);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toolbox-web/grid",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.2",
|
|
4
4
|
"description": "Zero-dependency, framework-agnostic data grid web component with virtualization, sorting, filtering, editing, and 20+ plugins. Works in vanilla JS, React, Vue, Angular, and any framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|