@toclocoinc/lattice-grid 1.66.0 → 1.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +1 -1
  2. package/angular/package.json +1 -1
  3. package/docs/API.html +87 -29
  4. package/docs/api-detail.html +40 -5
  5. package/lattice-grid.d.ts +20 -7
  6. package/lattice-grid.esm.min.js +35 -11
  7. package/lattice-grid.min.cjs +35 -11
  8. package/lattice-grid.min.js +35 -11
  9. package/modules/ai.d.ts +1 -1
  10. package/modules/ai.esm.min.js +3 -3
  11. package/modules/ai.min.cjs +3 -3
  12. package/modules/ai.min.js +3 -3
  13. package/modules/angular.d.ts +1 -1
  14. package/modules/angular.esm.min.js +3 -3
  15. package/modules/angular.min.cjs +3 -3
  16. package/modules/angular.min.js +3 -3
  17. package/modules/chart-alluvial.d.ts +1 -1
  18. package/modules/chart-alluvial.esm.min.js +1 -1
  19. package/modules/chart-alluvial.min.cjs +1 -1
  20. package/modules/chart-alluvial.min.js +1 -1
  21. package/modules/chart-arc.d.ts +1 -1
  22. package/modules/chart-arc.esm.min.js +1 -1
  23. package/modules/chart-arc.min.cjs +1 -1
  24. package/modules/chart-arc.min.js +1 -1
  25. package/modules/chart-bubblemap.d.ts +1 -1
  26. package/modules/chart-bubblemap.esm.min.js +1 -1
  27. package/modules/chart-bubblemap.min.cjs +1 -1
  28. package/modules/chart-bubblemap.min.js +1 -1
  29. package/modules/chart-bump.d.ts +1 -1
  30. package/modules/chart-bump.esm.min.js +1 -1
  31. package/modules/chart-bump.min.cjs +1 -1
  32. package/modules/chart-bump.min.js +1 -1
  33. package/modules/chart-calendar.d.ts +1 -1
  34. package/modules/chart-calendar.esm.min.js +1 -1
  35. package/modules/chart-calendar.min.cjs +1 -1
  36. package/modules/chart-calendar.min.js +1 -1
  37. package/modules/chart-decomposition.d.ts +1 -1
  38. package/modules/chart-decomposition.esm.min.js +1 -1
  39. package/modules/chart-decomposition.min.cjs +1 -1
  40. package/modules/chart-decomposition.min.js +1 -1
  41. package/modules/chart-diverging.d.ts +1 -1
  42. package/modules/chart-diverging.esm.min.js +1 -1
  43. package/modules/chart-diverging.min.cjs +1 -1
  44. package/modules/chart-diverging.min.js +1 -1
  45. package/modules/chart-dumbbell.d.ts +1 -1
  46. package/modules/chart-dumbbell.esm.min.js +1 -1
  47. package/modules/chart-dumbbell.min.cjs +1 -1
  48. package/modules/chart-dumbbell.min.js +1 -1
  49. package/modules/chart-fan.d.ts +1 -1
  50. package/modules/chart-fan.esm.min.js +1 -1
  51. package/modules/chart-fan.min.cjs +1 -1
  52. package/modules/chart-fan.min.js +1 -1
  53. package/modules/chart-hexbin.d.ts +1 -1
  54. package/modules/chart-hexbin.esm.min.js +1 -1
  55. package/modules/chart-hexbin.min.cjs +1 -1
  56. package/modules/chart-hexbin.min.js +1 -1
  57. package/modules/chart-hexmap.d.ts +1 -1
  58. package/modules/chart-hexmap.esm.min.js +1 -1
  59. package/modules/chart-hexmap.min.cjs +1 -1
  60. package/modules/chart-hexmap.min.js +1 -1
  61. package/modules/chart-icicle.d.ts +1 -1
  62. package/modules/chart-icicle.esm.min.js +1 -1
  63. package/modules/chart-icicle.min.cjs +1 -1
  64. package/modules/chart-icicle.min.js +1 -1
  65. package/modules/chart-markermap.d.ts +1 -1
  66. package/modules/chart-markermap.esm.min.js +1 -1
  67. package/modules/chart-markermap.min.cjs +1 -1
  68. package/modules/chart-markermap.min.js +1 -1
  69. package/modules/chart-parallel.d.ts +1 -1
  70. package/modules/chart-parallel.esm.min.js +1 -1
  71. package/modules/chart-parallel.min.cjs +1 -1
  72. package/modules/chart-parallel.min.js +1 -1
  73. package/modules/chart-ridgeline.d.ts +1 -1
  74. package/modules/chart-ridgeline.esm.min.js +1 -1
  75. package/modules/chart-ridgeline.min.cjs +1 -1
  76. package/modules/chart-ridgeline.min.js +1 -1
  77. package/modules/chart-roc.d.ts +1 -1
  78. package/modules/chart-roc.esm.min.js +1 -1
  79. package/modules/chart-roc.min.cjs +1 -1
  80. package/modules/chart-roc.min.js +1 -1
  81. package/modules/chart-slope.d.ts +1 -1
  82. package/modules/chart-slope.esm.min.js +1 -1
  83. package/modules/chart-slope.min.cjs +1 -1
  84. package/modules/chart-slope.min.js +1 -1
  85. package/modules/chart-splom.d.ts +1 -1
  86. package/modules/chart-splom.esm.min.js +1 -1
  87. package/modules/chart-splom.min.cjs +1 -1
  88. package/modules/chart-splom.min.js +1 -1
  89. package/modules/chart-waffle.d.ts +1 -1
  90. package/modules/chart-waffle.esm.min.js +1 -1
  91. package/modules/chart-waffle.min.cjs +1 -1
  92. package/modules/chart-waffle.min.js +1 -1
  93. package/modules/charts.d.ts +1 -1
  94. package/modules/charts.esm.min.js +1376 -1249
  95. package/modules/charts.min.cjs +1376 -1249
  96. package/modules/charts.min.js +1376 -1249
  97. package/modules/data-router.d.ts +8 -3
  98. package/modules/data-router.esm.min.js +229 -11
  99. package/modules/data-router.min.cjs +229 -11
  100. package/modules/data-router.min.js +229 -11
  101. package/modules/devtools.d.ts +1 -1
  102. package/modules/devtools.esm.min.js +1 -1
  103. package/modules/devtools.min.cjs +1 -1
  104. package/modules/devtools.min.js +1 -1
  105. package/modules/dhtmlx-compat.d.ts +1 -1
  106. package/modules/dhtmlx-compat.esm.min.js +3 -3
  107. package/modules/dhtmlx-compat.min.cjs +3 -3
  108. package/modules/dhtmlx-compat.min.js +3 -3
  109. package/modules/gantt.d.ts +8 -3
  110. package/modules/gantt.esm.min.js +56 -8
  111. package/modules/gantt.min.cjs +56 -8
  112. package/modules/gantt.min.js +56 -8
  113. package/modules/geo-europe-nuts.d.ts +1 -1
  114. package/modules/geo-europe-nuts.esm.min.js +1 -1
  115. package/modules/geo-uk.d.ts +1 -1
  116. package/modules/geo-uk.esm.min.js +1 -1
  117. package/modules/geo-us-states.d.ts +1 -1
  118. package/modules/geo-us-states.esm.min.js +1 -1
  119. package/modules/geo-world-110m.d.ts +1 -1
  120. package/modules/geo-world-110m.esm.min.js +1 -1
  121. package/modules/geo-world-50m.d.ts +1 -1
  122. package/modules/geo-world-50m.esm.min.js +1 -1
  123. package/modules/htmx.d.ts +1 -1
  124. package/modules/htmx.esm.min.js +35 -11
  125. package/modules/htmx.min.cjs +35 -11
  126. package/modules/htmx.min.js +35 -11
  127. package/modules/kanban.d.ts +10 -3
  128. package/modules/kanban.esm.min.js +24 -4
  129. package/modules/kanban.min.cjs +24 -4
  130. package/modules/kanban.min.js +24 -4
  131. package/modules/kpi.d.ts +35 -10
  132. package/modules/kpi.esm.min.js +55 -7
  133. package/modules/kpi.min.cjs +55 -7
  134. package/modules/kpi.min.js +55 -7
  135. package/modules/layout.d.ts +1 -1
  136. package/modules/layout.esm.min.js +3 -3
  137. package/modules/layout.min.cjs +3 -3
  138. package/modules/layout.min.js +3 -3
  139. package/modules/mock-socket.d.ts +1 -1
  140. package/modules/mock-socket.esm.min.js +1 -1
  141. package/modules/mock-socket.min.cjs +1 -1
  142. package/modules/mock-socket.min.js +1 -1
  143. package/modules/react.d.ts +1 -1
  144. package/modules/react.esm.min.js +3 -3
  145. package/modules/react.min.cjs +3 -3
  146. package/modules/react.min.js +3 -3
  147. package/modules/svelte.d.ts +1 -1
  148. package/modules/svelte.esm.min.js +3 -3
  149. package/modules/svelte.min.cjs +3 -3
  150. package/modules/svelte.min.js +3 -3
  151. package/modules/tabs.d.ts +1 -1
  152. package/modules/tabs.esm.min.js +13 -14
  153. package/modules/tabs.min.cjs +13 -14
  154. package/modules/tabs.min.js +13 -14
  155. package/modules/vue.d.ts +1 -1
  156. package/modules/vue.esm.min.js +3 -3
  157. package/modules/vue.min.cjs +3 -3
  158. package/modules/vue.min.js +3 -3
  159. package/modules/webcomponent.d.ts +1 -1
  160. package/modules/webcomponent.esm.min.js +35 -11
  161. package/modules/webcomponent.min.cjs +35 -11
  162. package/modules/webcomponent.min.js +35 -11
  163. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.66.0, kanban module type declarations
2
+ * Lattice Grid 1.68.0, kanban module type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -215,7 +215,13 @@ interface KanbanSla {
215
215
  interface KanbanConfig {
216
216
  rows?: KanbanRow[];
217
217
  grid?: unknown;
218
- rowKey?: string | ((row: KanbanRow) => unknown);
218
+ /**
219
+ * Card identity (a field or fn, returning a string or number); default
220
+ * 'id'. Composite (`string[]`) keys are core-grid-only: a board keys its
221
+ * own card identity from one value, so there is nothing for an array to
222
+ * join into here.
223
+ */
224
+ rowKey?: string | ((row: KanbanRow) => string | number);
219
225
  columnProperty?: string;
220
226
  columns?: KanbanColumnDef[];
221
227
  columnOrder?: string[];
@@ -376,7 +382,8 @@ interface KanbanFilters {
376
382
  */
377
383
  interface Kanban {
378
384
  readonly el: unknown | null;
379
- readonly rowKey: string | ((row: KanbanRow) => unknown);
385
+ /** The resolved card identity; see `KanbanConfig.rowKey`. */
386
+ readonly rowKey: string | ((row: KanbanRow) => string | number);
380
387
  rows: KanbanRows;
381
388
  /** The card-aging / SLA monitor, present only when a `sla` config was supplied. */
382
389
  sla?: KanbanSla;
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.66.0, kanban module
2
+ * Lattice Grid 1.68.0, kanban module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -52,12 +52,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
52
52
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
53
53
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
54
54
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
55
- const STAMPED_VERSION="1.66.0";
55
+ const STAMPED_VERSION="1.68.0";
56
56
  async function resolveVersion(){
57
57
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
58
58
  return STAMPED_VERSION;
59
59
  }
60
- const VERSION="1.66.0";
60
+ const VERSION="1.68.0";
61
61
  const warned=new Set();
62
62
  const WARNED_LIMIT=2000;
63
63
  function rememberWarned(key){
@@ -444,8 +444,28 @@ if(isFunction(accessor))return accessor;
444
444
  if(typeof accessor==='string')return(row)=>getPath(row,accessor);
445
445
  return()=>undefined;
446
446
  }
447
+ function rowKeyTypeName(v){
448
+ if(Array.isArray(v))return'array';
449
+ if(v===null)return'null';
450
+ return typeof v;
451
+ }
447
452
  function keyFn(rowKey){
448
- if(isFunction(rowKey))return rowKey;
453
+ if(rowKey!==undefined&&typeof rowKey!=='function'
454
+ &&!(typeof rowKey==='string'&&rowKey.length>0)){
455
+ throw new Error(
456
+ 'lattice-kanban: rowKey must be a property name or a function; composite keys are not supported on boards.',
457
+ );
458
+ }
459
+ if(isFunction(rowKey)){
460
+ return(row)=>{
461
+ const key=rowKey(row);
462
+ if(typeof key==='string')return key;
463
+ if(typeof key==='number'&&Number.isFinite(key))return key;
464
+ throw new Error(
465
+ `lattice-kanban: rowKey function must return a string or number; got ${rowKeyTypeName(key)}.`,
466
+ );
467
+ };
468
+ }
449
469
  const path=typeof rowKey==='string'?rowKey:'id';
450
470
  return(row)=>getPath(row,path);
451
471
  }
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.66.0, kanban module
2
+ * Lattice Grid 1.68.0, kanban module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -54,12 +54,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
54
54
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
55
55
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
56
56
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
57
- const STAMPED_VERSION="1.66.0";
57
+ const STAMPED_VERSION="1.68.0";
58
58
  async function resolveVersion(){
59
59
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
60
60
  return STAMPED_VERSION;
61
61
  }
62
- const VERSION="1.66.0";
62
+ const VERSION="1.68.0";
63
63
  const warned=new Set();
64
64
  const WARNED_LIMIT=2000;
65
65
  function rememberWarned(key){
@@ -446,8 +446,28 @@ if(isFunction(accessor))return accessor;
446
446
  if(typeof accessor==='string')return(row)=>getPath(row,accessor);
447
447
  return()=>undefined;
448
448
  }
449
+ function rowKeyTypeName(v){
450
+ if(Array.isArray(v))return'array';
451
+ if(v===null)return'null';
452
+ return typeof v;
453
+ }
449
454
  function keyFn(rowKey){
450
- if(isFunction(rowKey))return rowKey;
455
+ if(rowKey!==undefined&&typeof rowKey!=='function'
456
+ &&!(typeof rowKey==='string'&&rowKey.length>0)){
457
+ throw new Error(
458
+ 'lattice-kanban: rowKey must be a property name or a function; composite keys are not supported on boards.',
459
+ );
460
+ }
461
+ if(isFunction(rowKey)){
462
+ return(row)=>{
463
+ const key=rowKey(row);
464
+ if(typeof key==='string')return key;
465
+ if(typeof key==='number'&&Number.isFinite(key))return key;
466
+ throw new Error(
467
+ `lattice-kanban: rowKey function must return a string or number; got ${rowKeyTypeName(key)}.`,
468
+ );
469
+ };
470
+ }
451
471
  const path=typeof rowKey==='string'?rowKey:'id';
452
472
  return(row)=>getPath(row,path);
453
473
  }
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.66.0, kanban module
2
+ * Lattice Grid 1.68.0, kanban module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -54,12 +54,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
54
54
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
55
55
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
56
56
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
57
- const STAMPED_VERSION="1.66.0";
57
+ const STAMPED_VERSION="1.68.0";
58
58
  async function resolveVersion(){
59
59
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
60
60
  return STAMPED_VERSION;
61
61
  }
62
- const VERSION="1.66.0";
62
+ const VERSION="1.68.0";
63
63
  const warned=new Set();
64
64
  const WARNED_LIMIT=2000;
65
65
  function rememberWarned(key){
@@ -446,8 +446,28 @@ if(isFunction(accessor))return accessor;
446
446
  if(typeof accessor==='string')return(row)=>getPath(row,accessor);
447
447
  return()=>undefined;
448
448
  }
449
+ function rowKeyTypeName(v){
450
+ if(Array.isArray(v))return'array';
451
+ if(v===null)return'null';
452
+ return typeof v;
453
+ }
449
454
  function keyFn(rowKey){
450
- if(isFunction(rowKey))return rowKey;
455
+ if(rowKey!==undefined&&typeof rowKey!=='function'
456
+ &&!(typeof rowKey==='string'&&rowKey.length>0)){
457
+ throw new Error(
458
+ 'lattice-kanban: rowKey must be a property name or a function; composite keys are not supported on boards.',
459
+ );
460
+ }
461
+ if(isFunction(rowKey)){
462
+ return(row)=>{
463
+ const key=rowKey(row);
464
+ if(typeof key==='string')return key;
465
+ if(typeof key==='number'&&Number.isFinite(key))return key;
466
+ throw new Error(
467
+ `lattice-kanban: rowKey function must return a string or number; got ${rowKeyTypeName(key)}.`,
468
+ );
469
+ };
470
+ }
451
471
  const path=typeof rowKey==='string'?rowKey:'id';
452
472
  return(row)=>getPath(row,path);
453
473
  }
package/modules/kpi.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.66.0, kpi module type declarations
2
+ * Lattice Grid 1.68.0, kpi module type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -60,6 +60,15 @@ interface KPIStatTile {
60
60
  target?: number;
61
61
  /** A baseline the tile's delta is measured against. */
62
62
  baseline?: number;
63
+ /**
64
+ * What the movement line prints against `baseline` (F-FRED-G): `'absolute'` the difference alone, `'relative'` the
65
+ * percentage alone, `'both'` (the default, unchanged) both together. A
66
+ * rate series (4.10 vs 4.30) makes the percentage a percent-of-a-percent
67
+ * and meaningless, so `'absolute'` is how a host keeps the line without
68
+ * it. The arrow and its colour follow the sign of the difference either
69
+ * way.
70
+ */
71
+ delta?: 'absolute' | 'relative' | 'both';
63
72
  /** Threshold bands, either two cut points or an explicit band list. */
64
73
  thresholds?: KPIThresholds;
65
74
  /** Explicit status bands (an alternative to `thresholds`). */
@@ -112,12 +121,15 @@ type KPITile = KPIStatTile | KPIClockTile;
112
121
  * of top-level items that expand to the indicators beneath them, each parent
113
122
  * highlighted with the worst status below it.
114
123
  *
115
- * The shape is declared with `path` or `parentKey` — the same two shapes the
116
- * grid's tree data and the tree-select editor take — over the **tile specs**,
117
- * not the rows. With neither declared, one is derived by splitting the tile
118
- * ids on `separator`, so `system.compute.cpu` files itself under Compute
119
- * under System. A panel whose ids carry no separator stays flat, and `false`
120
- * keeps it flat whatever they look like.
124
+ * `tree` is an opt-in: omitted or `false`, the panel is
125
+ * flat whatever its tile ids look like, and a dotted id seen with `tree`
126
+ * unset is reported once rather than silently turned into a hierarchy. Given
127
+ * any object (`{}` included), the shape is declared with `path` or
128
+ * `parentKey` — the same two shapes the grid's tree data and the tree-select
129
+ * editor take — over the **tile specs**, not the rows. With neither declared,
130
+ * one is derived by splitting the tile ids on `separator`, so
131
+ * `system.compute.cpu` files itself under Compute under System. A panel
132
+ * whose ids carry no separator stays flat even with `tree` set.
121
133
  *
122
134
  * A tile's `field` is never a source: a dot there already means a nested
123
135
  * object property.
@@ -209,6 +221,8 @@ interface KPITileModel {
209
221
  delta: number | null;
210
222
  deltaPercent: number | null;
211
223
  deltaFormatted?: string;
224
+ /** What the movement line prints; see `KPIStatTile.delta`. Always present once `baseline` is. */
225
+ deltaMode?: 'absolute' | 'relative' | 'both';
212
226
  count: number;
213
227
  sparkline: number[] | null;
214
228
  }
@@ -224,7 +238,13 @@ interface KPIEvent {
224
238
  interface KPIConfig {
225
239
  rows?: KPIRow[];
226
240
  grid?: unknown;
227
- rowKey?: string | ((row: KPIRow) => unknown);
241
+ /**
242
+ * Row identity (a field or fn, returning a string or number); default
243
+ * 'id'. Composite (`string[]`) keys are core-grid-only: a panel keys its
244
+ * own tile identity from one value, so there is nothing for an array to
245
+ * join into here.
246
+ */
247
+ rowKey?: string | ((row: KPIRow) => string | number);
228
248
  /**
229
249
  * Extra columns of the bound `grid` to project onto the rows a tile `filter`
230
250
  * sees, beyond the fields the tiles themselves declare. A grid-bound panel
@@ -243,7 +263,11 @@ interface KPIConfig {
243
263
  * on a stat tile, which takes its own `format.locale`.
244
264
  */
245
265
  locale?: string;
246
- /** Arrange the tiles as a hierarchy; `false` keeps the panel flat. */
266
+ /**
267
+ * Arrange the tiles as a hierarchy; unset or `false` keeps the panel flat.
268
+ * Opt-in: a dotted tile id is not a hierarchy until
269
+ * `tree` is set, and warns once while it is not.
270
+ */
247
271
  tree?: KPITreeConfig | false;
248
272
  /**
249
273
  * The catalogue the panel's own text is read from. A panel routinely has no
@@ -274,7 +298,8 @@ interface KPIRows {
274
298
  */
275
299
  interface KPI {
276
300
  readonly el: unknown | null;
277
- readonly rowKey: string | ((row: KPIRow) => unknown);
301
+ /** The resolved row identity; see `KPIConfig.rowKey`. */
302
+ readonly rowKey: string | ((row: KPIRow) => string | number);
278
303
  /** Whether the panel renders as a hierarchy rather than a flat tile grid. */
279
304
  readonly tree: boolean;
280
305
  rows: KPIRows;
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.66.0, kpi module
2
+ * Lattice Grid 1.68.0, kpi module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -52,12 +52,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
52
52
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
53
53
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
54
54
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
55
- const STAMPED_VERSION="1.66.0";
55
+ const STAMPED_VERSION="1.68.0";
56
56
  async function resolveVersion(){
57
57
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
58
58
  return STAMPED_VERSION;
59
59
  }
60
- const VERSION="1.66.0";
60
+ const VERSION="1.68.0";
61
61
  const warned=new Set();
62
62
  const WARNED_LIMIT=2000;
63
63
  function rememberWarned(key){
@@ -825,7 +825,15 @@ const rank=Math.max(SEVERITY[a]||0,SEVERITY[b]||0);
825
825
  return BY_RANK[rank];
826
826
  }
827
827
  function resolveHierarchy(config,tiles){
828
- if(config===false||!tiles.length)return null;
828
+ if(!tiles.length)return null;
829
+ if(config===false)return null;
830
+ if(config===undefined||config===null){
831
+ if(tiles.some((t)=>t.id.includes(DEFAULT_SEPARATOR))){
832
+ warnOnce('kpi:tree:dotted-id',
833
+ 'kpi: tile ids contain dots; set `tree` to render them as a hierarchy.');
834
+ }
835
+ return null;
836
+ }
829
837
  const cfg=isObject(config)?config:{};
830
838
  const order=tiles.map((_,i)=>i);
831
839
  const keyOf=(physical)=>tiles[physical].id;
@@ -1184,14 +1192,20 @@ return node;
1184
1192
  function deltaNode(doc,fig,t){
1185
1193
  const dir=t.delta>0?'up':(t.delta<0?'down':'flat');
1186
1194
  const arrow=t.delta>0?'▲':(t.delta<0?'▼':'■');
1195
+ const abs=t.deltaFormatted??t.delta;
1187
1196
  const pct=t.deltaPercent===null||t.deltaPercent===undefined
1188
- ?'':` (${(t.deltaPercent*100).toFixed(1)}%)`;
1197
+ ?null:`${(t.deltaPercent*100).toFixed(1)}%`;
1198
+ const mode=t.deltaMode||'both';
1199
+ let text;
1200
+ if(mode==='absolute'||pct===null)text=`${arrow} ${abs}`;
1201
+ else if(mode==='relative')text=`${arrow} ${pct}`;
1202
+ else text=`${arrow} ${abs} (${pct})`;
1189
1203
  const node=childByClass(fig,`${NS}__delta`)||div(doc,`${NS}__delta`);
1190
1204
  for(const d of['up','down','flat']){
1191
1205
  if(d!==dir)node.classList.remove(`${NS}__delta--${d}`);
1192
1206
  }
1193
1207
  node.classList.add(`${NS}__delta--${dir}`);
1194
- setText(node,`${arrow} ${t.deltaFormatted??t.delta}${pct}`);
1208
+ setText(node,text);
1195
1209
  return node;
1196
1210
  }
1197
1211
  function sparkNode(doc,fig,t){
@@ -5843,6 +5857,7 @@ const readClock=__m7["readClock"];
5843
5857
  const KPI_EVENTS=Object.freeze([
5844
5858
  'tile:click','tile:dblclick','tile:contextmenu','node:toggle','change',
5845
5859
  ]);
5860
+ const DELTA_MODES=Object.freeze(['absolute','relative','both']);
5846
5861
  const GRID_FOLLOW_EVENTS=Object.freeze([
5847
5862
  'rows:changed','filter:changed','sort:changed','cell:changed','model:changed',
5848
5863
  'stream:chunk','stream:evicted',
@@ -5854,8 +5869,30 @@ onTileContextMenu:'tile:contextmenu',
5854
5869
  onNodeToggle:'node:toggle',
5855
5870
  onChange:'change',
5856
5871
  });
5872
+ function rowKeyTypeName(v){
5873
+ if(Array.isArray(v))return'array';
5874
+ if(v===null)return'null';
5875
+ return typeof v;
5876
+ }
5877
+ function assertRowKeyShape(rowKey){
5878
+ if(rowKey===undefined)return;
5879
+ if(typeof rowKey==='string'&&rowKey.length>0)return;
5880
+ if(typeof rowKey==='function')return;
5881
+ throw new Error(
5882
+ 'lattice-kpi: rowKey must be a property name or a function; composite keys are not supported on tiles.',
5883
+ );
5884
+ }
5857
5885
  function keyFn(rowKey){
5858
- if(isFunction(rowKey))return rowKey;
5886
+ if(isFunction(rowKey)){
5887
+ return(row)=>{
5888
+ const key=rowKey(row);
5889
+ if(typeof key==='string')return key;
5890
+ if(typeof key==='number'&&Number.isFinite(key))return key;
5891
+ throw new Error(
5892
+ `lattice-kpi: rowKey function must return a string or number; got ${rowKeyTypeName(key)}.`,
5893
+ );
5894
+ };
5895
+ }
5859
5896
  const get=pathGetter(typeof rowKey==='string'&&rowKey?rowKey:'id');
5860
5897
  return(row)=>get(row);
5861
5898
  }
@@ -5886,12 +5923,20 @@ format:spec.format,
5886
5923
  target:spec.target,
5887
5924
  barBounds:barBounds(spec),
5888
5925
  baseline:spec.baseline,
5926
+ deltaMode:resolveDeltaMode(spec.delta,id),
5889
5927
  spark:normaliseSpark(spec.sparkline),
5890
5928
  acc,
5891
5929
  members:0,
5892
5930
  noColumn:false,
5893
5931
  };
5894
5932
  }
5933
+ function resolveDeltaMode(raw,id){
5934
+ if(raw===undefined)return'both';
5935
+ if(DELTA_MODES.includes(raw))return raw;
5936
+ warnOnce(`kpi:delta:${id}`,
5937
+ `kpi: tile "${id}" has delta: ${JSON.stringify(raw)}; expected one of ${DELTA_MODES.join(', ')}. Falling back to 'both'.`);
5938
+ return'both';
5939
+ }
5895
5940
  function normaliseSpark(s){
5896
5941
  if(!s)return null;
5897
5942
  if(typeof s==='string')return{x:undefined,y:accessorFor(s)};
@@ -6036,6 +6081,7 @@ return rows;
6036
6081
  }
6037
6082
  function createKPI(el,config={}){
6038
6083
  const cfg=isObject(config)?config:{};
6084
+ assertRowKeyShape(cfg.rowKey);
6039
6085
  const gridBound=!!cfg.grid&&!Array.isArray(cfg.rows);
6040
6086
  const key=gridBound?keyFn('__kpiKey'):keyFn(cfg.rowKey);
6041
6087
  const nullText=typeof cfg.nullText==='string'?cfg.nullText:'—';
@@ -6137,6 +6183,7 @@ baseline:undefined,
6137
6183
  delta:null,
6138
6184
  deltaPercent:null,
6139
6185
  deltaFormatted:undefined,
6186
+ deltaMode:undefined,
6140
6187
  count:0,
6141
6188
  sparkline:null,
6142
6189
  bar:null,
@@ -6174,6 +6221,7 @@ baseline:t.baseline,
6174
6221
  delta,
6175
6222
  deltaPercent,
6176
6223
  deltaFormatted,
6224
+ deltaMode:t.deltaMode,
6177
6225
  count:t.members,
6178
6226
  sparkline:sparkFor(t),
6179
6227
  bar:t.barBounds
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.66.0, kpi module
2
+ * Lattice Grid 1.68.0, kpi module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -54,12 +54,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
54
54
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
55
55
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
56
56
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
57
- const STAMPED_VERSION="1.66.0";
57
+ const STAMPED_VERSION="1.68.0";
58
58
  async function resolveVersion(){
59
59
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
60
60
  return STAMPED_VERSION;
61
61
  }
62
- const VERSION="1.66.0";
62
+ const VERSION="1.68.0";
63
63
  const warned=new Set();
64
64
  const WARNED_LIMIT=2000;
65
65
  function rememberWarned(key){
@@ -827,7 +827,15 @@ const rank=Math.max(SEVERITY[a]||0,SEVERITY[b]||0);
827
827
  return BY_RANK[rank];
828
828
  }
829
829
  function resolveHierarchy(config,tiles){
830
- if(config===false||!tiles.length)return null;
830
+ if(!tiles.length)return null;
831
+ if(config===false)return null;
832
+ if(config===undefined||config===null){
833
+ if(tiles.some((t)=>t.id.includes(DEFAULT_SEPARATOR))){
834
+ warnOnce('kpi:tree:dotted-id',
835
+ 'kpi: tile ids contain dots; set `tree` to render them as a hierarchy.');
836
+ }
837
+ return null;
838
+ }
831
839
  const cfg=isObject(config)?config:{};
832
840
  const order=tiles.map((_,i)=>i);
833
841
  const keyOf=(physical)=>tiles[physical].id;
@@ -1186,14 +1194,20 @@ return node;
1186
1194
  function deltaNode(doc,fig,t){
1187
1195
  const dir=t.delta>0?'up':(t.delta<0?'down':'flat');
1188
1196
  const arrow=t.delta>0?'▲':(t.delta<0?'▼':'■');
1197
+ const abs=t.deltaFormatted??t.delta;
1189
1198
  const pct=t.deltaPercent===null||t.deltaPercent===undefined
1190
- ?'':` (${(t.deltaPercent*100).toFixed(1)}%)`;
1199
+ ?null:`${(t.deltaPercent*100).toFixed(1)}%`;
1200
+ const mode=t.deltaMode||'both';
1201
+ let text;
1202
+ if(mode==='absolute'||pct===null)text=`${arrow} ${abs}`;
1203
+ else if(mode==='relative')text=`${arrow} ${pct}`;
1204
+ else text=`${arrow} ${abs} (${pct})`;
1191
1205
  const node=childByClass(fig,`${NS}__delta`)||div(doc,`${NS}__delta`);
1192
1206
  for(const d of['up','down','flat']){
1193
1207
  if(d!==dir)node.classList.remove(`${NS}__delta--${d}`);
1194
1208
  }
1195
1209
  node.classList.add(`${NS}__delta--${dir}`);
1196
- setText(node,`${arrow} ${t.deltaFormatted??t.delta}${pct}`);
1210
+ setText(node,text);
1197
1211
  return node;
1198
1212
  }
1199
1213
  function sparkNode(doc,fig,t){
@@ -5845,6 +5859,7 @@ const readClock=__m7["readClock"];
5845
5859
  const KPI_EVENTS=Object.freeze([
5846
5860
  'tile:click','tile:dblclick','tile:contextmenu','node:toggle','change',
5847
5861
  ]);
5862
+ const DELTA_MODES=Object.freeze(['absolute','relative','both']);
5848
5863
  const GRID_FOLLOW_EVENTS=Object.freeze([
5849
5864
  'rows:changed','filter:changed','sort:changed','cell:changed','model:changed',
5850
5865
  'stream:chunk','stream:evicted',
@@ -5856,8 +5871,30 @@ onTileContextMenu:'tile:contextmenu',
5856
5871
  onNodeToggle:'node:toggle',
5857
5872
  onChange:'change',
5858
5873
  });
5874
+ function rowKeyTypeName(v){
5875
+ if(Array.isArray(v))return'array';
5876
+ if(v===null)return'null';
5877
+ return typeof v;
5878
+ }
5879
+ function assertRowKeyShape(rowKey){
5880
+ if(rowKey===undefined)return;
5881
+ if(typeof rowKey==='string'&&rowKey.length>0)return;
5882
+ if(typeof rowKey==='function')return;
5883
+ throw new Error(
5884
+ 'lattice-kpi: rowKey must be a property name or a function; composite keys are not supported on tiles.',
5885
+ );
5886
+ }
5859
5887
  function keyFn(rowKey){
5860
- if(isFunction(rowKey))return rowKey;
5888
+ if(isFunction(rowKey)){
5889
+ return(row)=>{
5890
+ const key=rowKey(row);
5891
+ if(typeof key==='string')return key;
5892
+ if(typeof key==='number'&&Number.isFinite(key))return key;
5893
+ throw new Error(
5894
+ `lattice-kpi: rowKey function must return a string or number; got ${rowKeyTypeName(key)}.`,
5895
+ );
5896
+ };
5897
+ }
5861
5898
  const get=pathGetter(typeof rowKey==='string'&&rowKey?rowKey:'id');
5862
5899
  return(row)=>get(row);
5863
5900
  }
@@ -5888,12 +5925,20 @@ format:spec.format,
5888
5925
  target:spec.target,
5889
5926
  barBounds:barBounds(spec),
5890
5927
  baseline:spec.baseline,
5928
+ deltaMode:resolveDeltaMode(spec.delta,id),
5891
5929
  spark:normaliseSpark(spec.sparkline),
5892
5930
  acc,
5893
5931
  members:0,
5894
5932
  noColumn:false,
5895
5933
  };
5896
5934
  }
5935
+ function resolveDeltaMode(raw,id){
5936
+ if(raw===undefined)return'both';
5937
+ if(DELTA_MODES.includes(raw))return raw;
5938
+ warnOnce(`kpi:delta:${id}`,
5939
+ `kpi: tile "${id}" has delta: ${JSON.stringify(raw)}; expected one of ${DELTA_MODES.join(', ')}. Falling back to 'both'.`);
5940
+ return'both';
5941
+ }
5897
5942
  function normaliseSpark(s){
5898
5943
  if(!s)return null;
5899
5944
  if(typeof s==='string')return{x:undefined,y:accessorFor(s)};
@@ -6038,6 +6083,7 @@ return rows;
6038
6083
  }
6039
6084
  function createKPI(el,config={}){
6040
6085
  const cfg=isObject(config)?config:{};
6086
+ assertRowKeyShape(cfg.rowKey);
6041
6087
  const gridBound=!!cfg.grid&&!Array.isArray(cfg.rows);
6042
6088
  const key=gridBound?keyFn('__kpiKey'):keyFn(cfg.rowKey);
6043
6089
  const nullText=typeof cfg.nullText==='string'?cfg.nullText:'—';
@@ -6139,6 +6185,7 @@ baseline:undefined,
6139
6185
  delta:null,
6140
6186
  deltaPercent:null,
6141
6187
  deltaFormatted:undefined,
6188
+ deltaMode:undefined,
6142
6189
  count:0,
6143
6190
  sparkline:null,
6144
6191
  bar:null,
@@ -6176,6 +6223,7 @@ baseline:t.baseline,
6176
6223
  delta,
6177
6224
  deltaPercent,
6178
6225
  deltaFormatted,
6226
+ deltaMode:t.deltaMode,
6179
6227
  count:t.members,
6180
6228
  sparkline:sparkFor(t),
6181
6229
  bar:t.barBounds