@dlh.io/bideux 0.1.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 (90) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +90 -0
  3. package/dist/all.d.ts +14 -0
  4. package/dist/all.js +15 -0
  5. package/dist/all.js.map +1 -0
  6. package/dist/chunk-62IQGXLW.js +11470 -0
  7. package/dist/chunk-62IQGXLW.js.map +1 -0
  8. package/dist/chunk-75CRTQK7.js +315 -0
  9. package/dist/chunk-75CRTQK7.js.map +1 -0
  10. package/dist/chunk-FVO6E22Z.js +225 -0
  11. package/dist/chunk-FVO6E22Z.js.map +1 -0
  12. package/dist/chunk-GRITOB4M.js +337 -0
  13. package/dist/chunk-GRITOB4M.js.map +1 -0
  14. package/dist/chunk-HPFHIZWO.js +40 -0
  15. package/dist/chunk-HPFHIZWO.js.map +1 -0
  16. package/dist/chunk-IMNUQGHE.js +2864 -0
  17. package/dist/chunk-IMNUQGHE.js.map +1 -0
  18. package/dist/chunk-LHD445PJ.js +571 -0
  19. package/dist/chunk-LHD445PJ.js.map +1 -0
  20. package/dist/chunk-LPLBBHOW.js +386 -0
  21. package/dist/chunk-LPLBBHOW.js.map +1 -0
  22. package/dist/chunk-NGM5R3LW.js +881 -0
  23. package/dist/chunk-NGM5R3LW.js.map +1 -0
  24. package/dist/chunk-OMPR32AR.js +1320 -0
  25. package/dist/chunk-OMPR32AR.js.map +1 -0
  26. package/dist/chunk-QDQ5VVTC.js +255 -0
  27. package/dist/chunk-QDQ5VVTC.js.map +1 -0
  28. package/dist/chunk-RRWZKNDZ.js +1004 -0
  29. package/dist/chunk-RRWZKNDZ.js.map +1 -0
  30. package/dist/chunk-SRWIBMEB.js +178 -0
  31. package/dist/chunk-SRWIBMEB.js.map +1 -0
  32. package/dist/element/chunk-AOKCBPMV.js +1646 -0
  33. package/dist/element/chunk-AOKCBPMV.js.map +1 -0
  34. package/dist/element/chunk-B67SYRQP.js +281 -0
  35. package/dist/element/chunk-B67SYRQP.js.map +1 -0
  36. package/dist/element/chunk-FNRVFA2C.js +55 -0
  37. package/dist/element/chunk-FNRVFA2C.js.map +1 -0
  38. package/dist/element/chunk-G3C722QP.js +4707 -0
  39. package/dist/element/chunk-G3C722QP.js.map +1 -0
  40. package/dist/element/chunk-H5IFOSQP.js +346 -0
  41. package/dist/element/chunk-H5IFOSQP.js.map +1 -0
  42. package/dist/element/chunk-LXR3CLCN.js +30 -0
  43. package/dist/element/chunk-LXR3CLCN.js.map +1 -0
  44. package/dist/element/chunk-TFV4CJR4.js +183 -0
  45. package/dist/element/chunk-TFV4CJR4.js.map +1 -0
  46. package/dist/element/chunk-TXSK3CIN.js +859 -0
  47. package/dist/element/chunk-TXSK3CIN.js.map +1 -0
  48. package/dist/element/chunk-WDTTBY3S.js +45 -0
  49. package/dist/element/chunk-WDTTBY3S.js.map +1 -0
  50. package/dist/element/chunk-ZI3RYCAR.js +233 -0
  51. package/dist/element/chunk-ZI3RYCAR.js.map +1 -0
  52. package/dist/element/element.d.ts +3044 -0
  53. package/dist/element/element.js +146554 -0
  54. package/dist/element/element.js.map +1 -0
  55. package/dist/element/revo-grid.entry-7FHEBN3O.js +4817 -0
  56. package/dist/element/revo-grid.entry-7FHEBN3O.js.map +1 -0
  57. package/dist/element/revogr-attribution_7.entry-LSOFVZRA.js +1328 -0
  58. package/dist/element/revogr-attribution_7.entry-LSOFVZRA.js.map +1 -0
  59. package/dist/element/revogr-clipboard_3.entry-V74UIBPJ.js +494 -0
  60. package/dist/element/revogr-clipboard_3.entry-V74UIBPJ.js.map +1 -0
  61. package/dist/element/revogr-data_4.entry-2UBOKTFN.js +1041 -0
  62. package/dist/element/revogr-data_4.entry-2UBOKTFN.js.map +1 -0
  63. package/dist/element/revogr-filter-panel.entry-B24PLQU4.js +552 -0
  64. package/dist/element/revogr-filter-panel.entry-B24PLQU4.js.map +1 -0
  65. package/dist/index.d.ts +1041 -0
  66. package/dist/index.js +10 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/react.d.ts +1915 -0
  69. package/dist/react.js +14 -0
  70. package/dist/react.js.map +1 -0
  71. package/dist/renderers/echarts.d.ts +44 -0
  72. package/dist/renderers/echarts.js +6 -0
  73. package/dist/renderers/echarts.js.map +1 -0
  74. package/dist/renderers/index.d.ts +48 -0
  75. package/dist/renderers/index.js +12 -0
  76. package/dist/renderers/index.js.map +1 -0
  77. package/dist/renderers/revogrid.d.ts +76 -0
  78. package/dist/renderers/revogrid.js +6 -0
  79. package/dist/renderers/revogrid.js.map +1 -0
  80. package/dist/renderers/tabulator.d.ts +37 -0
  81. package/dist/renderers/tabulator.js +6 -0
  82. package/dist/renderers/tabulator.js.map +1 -0
  83. package/dist/result-table-CPls-277.d.ts +1885 -0
  84. package/dist/schema/dashboard.schema.json +4374 -0
  85. package/dist/schema/semantic-model.schema.json +1200 -0
  86. package/dist/schema/widget.schema.json +1793 -0
  87. package/dist/styles.css +156 -0
  88. package/dist/theme.css +1789 -0
  89. package/dist/types-CHFSsU_X.d.ts +1007 -0
  90. package/package.json +119 -0
@@ -0,0 +1,881 @@
1
+ import { pivotVisibleRows, pivotOpenColumnLevels, pivotOpenRowLevels, pivotFilterCount, pivotGroupKeys, pivotValueColumns, buildPivotTotalRow, buildPivotRows, buildPivotColumns, initialPivotViewState, PIVOT_META_PROP, pivotCellAction, togglePivotGroup, nextPivotSort, buildPivotRendererModel, isPivotColumnGroup, pivotRowMeta, pivotCellPresentation, setPivotColumnFilter, pivotSortDirection } from './chunk-LHD445PJ.js';
2
+ import { resolveRendererTheme, clearElement, applyWidgetTheme, setStyles, createLabel, createMessage } from './chunk-75CRTQK7.js';
3
+ import { defineCustomElements } from '@revolist/revogrid/loader';
4
+
5
+ // ../dashboard-renderer-revogrid/src/model.ts
6
+ var ROW_INDENT = 14;
7
+ var ROW_HEADER_COLUMN_SIZE = 180;
8
+ var VALUE_COLUMN_SIZE = 130;
9
+ var COLLAPSED_MARK = "\u25B8";
10
+ var EXPANDED_MARK = "\u25BE";
11
+ var SORT_MARK = { asc: "\u2191", desc: "\u2193" };
12
+ var FUNNEL_PATH = "M22 3H2l8 9.46V19l4 2v-8.54L22 3z";
13
+ function hexToRgb(color) {
14
+ const hex = /^#([0-9a-f]{6})$/i.exec(color.trim());
15
+ if (hex === null || hex[1] === void 0) return null;
16
+ const value = Number.parseInt(hex[1], 16);
17
+ return `${value >> 16 & 255}, ${value >> 8 & 255}, ${value & 255}`;
18
+ }
19
+ function headerName(model, column) {
20
+ const direction = pivotSortDirection(model.view, model.payload, column.prop);
21
+ return direction === null ? column.name : `${column.name} ${SORT_MARK[direction]}`;
22
+ }
23
+ function rowHeaderColumn(model, column, tokens) {
24
+ const level = model.payload.row_fields.findIndex(
25
+ (field) => field.key === column.prop
26
+ );
27
+ return {
28
+ prop: column.prop,
29
+ name: headerName(model, column),
30
+ readonly: true,
31
+ sortable: false,
32
+ pin: "colPinStart",
33
+ size: ROW_HEADER_COLUMN_SIZE,
34
+ cellTemplate: (createElement, props) => {
35
+ const meta = pivotRowMeta(props.model);
36
+ const text = pivotCellPresentation(model, props.model, column).text;
37
+ if (meta === null || meta.kind !== "group" || meta.depth !== level) {
38
+ return createElement("span", null, text);
39
+ }
40
+ const mark = model.view.collapsed.has(meta.key) ? COLLAPSED_MARK : EXPANDED_MARK;
41
+ return createElement("span", null, `${mark} ${text}`);
42
+ },
43
+ cellProperties: ({ model: row }) => {
44
+ const meta = pivotRowMeta(row);
45
+ const props = { style: { color: tokens.foreground } };
46
+ if (meta === null) return props;
47
+ props.style = {
48
+ ...props.style,
49
+ paddingLeft: `${8 + meta.depth * ROW_INDENT}px`
50
+ };
51
+ if (meta.kind !== "leaf") {
52
+ props.style = { ...props.style, fontWeight: "600" };
53
+ }
54
+ return props;
55
+ }
56
+ };
57
+ }
58
+ function filterableHeader(model, column, tokens, controls) {
59
+ const name = headerName(model, column);
60
+ const active = model.view.filters[column.prop] !== void 0;
61
+ return (createElement) => createElement(
62
+ "span",
63
+ {
64
+ style: {
65
+ display: "inline-flex",
66
+ alignItems: "center",
67
+ gap: "4px",
68
+ width: "100%"
69
+ }
70
+ },
71
+ [
72
+ createElement(
73
+ "span",
74
+ {
75
+ style: { flex: "1", overflow: "hidden", textOverflow: "ellipsis" }
76
+ },
77
+ name
78
+ ),
79
+ createElement(
80
+ "button",
81
+ {
82
+ type: "button",
83
+ "aria-label": `Filter ${column.name}`,
84
+ title: `Filter ${column.name}`,
85
+ style: {
86
+ display: "inline-flex",
87
+ alignItems: "center",
88
+ border: "none",
89
+ background: "transparent",
90
+ cursor: "pointer",
91
+ padding: "0",
92
+ color: active ? tokens.secondary : "inherit",
93
+ opacity: active ? "1" : "0.55"
94
+ },
95
+ onMouseDown: (event) => {
96
+ event.stopPropagation();
97
+ },
98
+ onClick: (event) => {
99
+ event.stopPropagation();
100
+ const anchor = event.currentTarget.getBoundingClientRect();
101
+ controls.openFilter(column.prop, {
102
+ left: anchor.left,
103
+ top: anchor.bottom + 4
104
+ });
105
+ }
106
+ },
107
+ createElement(
108
+ "svg",
109
+ {
110
+ viewBox: "0 0 24 24",
111
+ width: "11",
112
+ height: "11",
113
+ fill: active ? "currentColor" : "none",
114
+ stroke: "currentColor",
115
+ "stroke-width": "2",
116
+ "stroke-linecap": "round",
117
+ "stroke-linejoin": "round"
118
+ },
119
+ createElement("path", { d: FUNNEL_PATH })
120
+ )
121
+ )
122
+ ]
123
+ );
124
+ }
125
+ function measureColumn(model, column, tokens, controls) {
126
+ const heatmapRgb = hexToRgb(tokens.primary) ?? "130, 204, 92";
127
+ const definition = {
128
+ prop: column.prop,
129
+ name: headerName(model, column),
130
+ readonly: true,
131
+ sortable: false,
132
+ size: VALUE_COLUMN_SIZE,
133
+ cellTemplate: (createElement, props) => createElement(
134
+ "span",
135
+ null,
136
+ pivotCellPresentation(model, props.model, column).text
137
+ ),
138
+ cellProperties: ({ model: row }) => {
139
+ const presentation = pivotCellPresentation(model, row, column);
140
+ const props = {
141
+ style: { textAlign: "right", color: tokens.foreground }
142
+ };
143
+ if (presentation.emphasized) {
144
+ props.style = { ...props.style, fontWeight: "600" };
145
+ }
146
+ if (presentation.heatmapIntensity !== null) {
147
+ props.style = {
148
+ ...props.style,
149
+ background: `rgba(${heatmapRgb}, ${(0.08 + presentation.heatmapIntensity * 0.42).toFixed(3)})`
150
+ };
151
+ }
152
+ return props;
153
+ }
154
+ };
155
+ if (controls !== null && !column.isTotal) {
156
+ definition.columnTemplate = filterableHeader(
157
+ model,
158
+ column,
159
+ tokens,
160
+ controls
161
+ );
162
+ }
163
+ if (column.pinStart) definition.pin = "colPinStart";
164
+ return definition;
165
+ }
166
+ function groupColumn(model, group, tokens, controls) {
167
+ const children = group.children.map(
168
+ (child) => gridColumn(model, child, tokens, controls)
169
+ );
170
+ if (controls === null || !group.foldable) {
171
+ return { name: group.name, children };
172
+ }
173
+ const mark = group.folded ? COLLAPSED_MARK : EXPANDED_MARK;
174
+ const count = group.leafCount > 1 ? ` (${group.leafCount})` : "";
175
+ return {
176
+ name: group.name,
177
+ children,
178
+ columnTemplate: (createElement) => createElement(
179
+ "span",
180
+ {
181
+ style: {
182
+ cursor: "pointer",
183
+ display: "inline-flex",
184
+ alignItems: "center",
185
+ gap: "4px"
186
+ },
187
+ title: group.folded ? `Open ${group.name}` : `Fold ${group.name}`,
188
+ onMouseDown: (event) => {
189
+ event.stopPropagation();
190
+ },
191
+ onClick: (event) => {
192
+ event.stopPropagation();
193
+ controls.toggleColumnGroup(group.key);
194
+ }
195
+ },
196
+ `${mark} ${group.name}${count}`
197
+ )
198
+ };
199
+ }
200
+ function gridColumn(model, column, tokens, controls) {
201
+ if (isPivotColumnGroup(column)) {
202
+ return groupColumn(model, column, tokens, controls);
203
+ }
204
+ return column.isRowField ? rowHeaderColumn(model, column, tokens) : measureColumn(model, column, tokens, controls);
205
+ }
206
+ function revoGridColumns(model, tokens, controls = null) {
207
+ return model.columns.map(
208
+ (column) => gridColumn(model, column, tokens, controls)
209
+ );
210
+ }
211
+ function projectPivotView(model, view) {
212
+ const visibleRows = pivotVisibleRows(model.payload, view);
213
+ return {
214
+ ...model,
215
+ view,
216
+ columns: buildPivotColumns(model.payload, view.collapsedColumns),
217
+ rows: buildPivotRows(model.payload, visibleRows),
218
+ totalRow: buildPivotTotalRow(model.payload),
219
+ valueColumns: pivotValueColumns(model.payload, view.collapsedColumns),
220
+ controls: {
221
+ ...model.controls,
222
+ groupCount: pivotGroupKeys(model.payload).length,
223
+ filterCount: pivotFilterCount(view),
224
+ reordered: view.sort !== null
225
+ },
226
+ openLevels: {
227
+ rows: pivotOpenRowLevels(model.payload, view),
228
+ columns: pivotOpenColumnLevels(model.payload, view)
229
+ }
230
+ };
231
+ }
232
+ function resetPivotView(model) {
233
+ return projectPivotView(model, initialPivotViewState(model.payload, false));
234
+ }
235
+
236
+ // ../dashboard-renderer-revogrid/src/toolbar.ts
237
+ var FILTER_OPS = [
238
+ { op: "gte", label: "At least" },
239
+ { op: "gt", label: "Greater than" },
240
+ { op: "eq", label: "Equals" },
241
+ { op: "lte", label: "At most" },
242
+ { op: "lt", label: "Less than" }
243
+ ];
244
+ var ICON_PATHS = {
245
+ search: ["M21 21l-4.34-4.34", "M11 3a8 8 0 1 0 0 16 8 8 0 1 0 0-16z"],
246
+ expand: ["m7 15 5 5 5-5", "m7 9 5-5 5 5"],
247
+ collapse: ["m7 20 5-5 5 5", "m7 4 5 5 5-5"],
248
+ reset: ["M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8", "M3 3v5h5"],
249
+ filter: ["M22 3H2l8 9.46V19l4 2v-8.54L22 3z"],
250
+ download: [
251
+ "M12 15V3",
252
+ "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",
253
+ "m7 10 5 5 5-5"
254
+ ],
255
+ spreadsheet: [
256
+ "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z",
257
+ "M14 2v4a2 2 0 0 0 2 2h4",
258
+ "M8 13h2",
259
+ "M14 13h2",
260
+ "M8 17h2",
261
+ "M14 17h2"
262
+ ]
263
+ };
264
+ function icon(document, name, size) {
265
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
266
+ svg.setAttribute("viewBox", "0 0 24 24");
267
+ svg.setAttribute("width", String(size));
268
+ svg.setAttribute("height", String(size));
269
+ svg.setAttribute("fill", "none");
270
+ svg.setAttribute("stroke", "currentColor");
271
+ svg.setAttribute("stroke-width", "2");
272
+ svg.setAttribute("stroke-linecap", "round");
273
+ svg.setAttribute("stroke-linejoin", "round");
274
+ svg.setAttribute("aria-hidden", "true");
275
+ for (const d of ICON_PATHS[name]) {
276
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
277
+ path.setAttribute("d", d);
278
+ svg.append(path);
279
+ }
280
+ svg.style.flexShrink = "0";
281
+ return svg;
282
+ }
283
+ function ghostButton(document, tokens, label, glyph, onClick) {
284
+ const button = document.createElement("button");
285
+ button.type = "button";
286
+ button.append(icon(document, glyph, 12), document.createTextNode(label));
287
+ setStyles(button, {
288
+ display: "inline-flex",
289
+ "align-items": "center",
290
+ gap: "4px",
291
+ height: "28px",
292
+ padding: "0 8px",
293
+ border: "none",
294
+ "border-radius": "6px",
295
+ background: "transparent",
296
+ color: "inherit",
297
+ font: "inherit",
298
+ "font-size": "12px",
299
+ "line-height": "16px",
300
+ cursor: "pointer",
301
+ "white-space": "nowrap"
302
+ });
303
+ button.addEventListener("mouseenter", () => {
304
+ if (!button.disabled) button.style.background = tokens.border;
305
+ });
306
+ button.addEventListener("mouseleave", () => {
307
+ button.style.background = "transparent";
308
+ });
309
+ button.addEventListener("click", onClick);
310
+ return button;
311
+ }
312
+ function fieldStyles(tokens) {
313
+ return {
314
+ height: "28px",
315
+ "box-sizing": "border-box",
316
+ border: `1px solid ${tokens.border}`,
317
+ "border-radius": "6px",
318
+ background: "transparent",
319
+ color: "inherit",
320
+ font: "inherit",
321
+ "font-size": "12px",
322
+ "line-height": "16px",
323
+ padding: "0 8px",
324
+ outline: "none"
325
+ };
326
+ }
327
+ function setDisabled(button, disabled) {
328
+ button.disabled = disabled;
329
+ button.style.opacity = disabled ? "0.5" : "1";
330
+ button.style.cursor = disabled ? "default" : "pointer";
331
+ }
332
+ function createPivotToolbar(input, model, setView, tokens, parent) {
333
+ const document = parent.ownerDocument;
334
+ const toolbar = document.createElement("div");
335
+ toolbar.dataset.bideuxPart = "pivot-actions";
336
+ setStyles(toolbar, {
337
+ display: "flex",
338
+ "align-items": "center",
339
+ gap: "8px",
340
+ "flex-wrap": "wrap",
341
+ flex: "0 0 auto"
342
+ });
343
+ const searchWrap = document.createElement("div");
344
+ setStyles(searchWrap, { position: "relative", display: "inline-flex" });
345
+ const glass = document.createElement("span");
346
+ glass.append(icon(document, "search", 12));
347
+ setStyles(glass, {
348
+ position: "absolute",
349
+ left: "8px",
350
+ top: "50%",
351
+ transform: "translateY(-50%)",
352
+ display: "inline-flex",
353
+ "pointer-events": "none",
354
+ color: tokens.mutedForeground
355
+ });
356
+ const search = document.createElement("input");
357
+ search.type = "search";
358
+ search.placeholder = model().controls.searchPlaceholder;
359
+ search.value = model().view.search;
360
+ search.setAttribute("aria-label", "Find a row");
361
+ setStyles(search, {
362
+ ...fieldStyles(tokens),
363
+ width: "160px",
364
+ "padding-left": "28px"
365
+ });
366
+ search.addEventListener("focus", () => {
367
+ search.style.borderColor = tokens.primary;
368
+ });
369
+ search.addEventListener("blur", () => {
370
+ search.style.borderColor = tokens.border;
371
+ });
372
+ search.addEventListener("input", () => {
373
+ setView({ ...model().view, search: search.value });
374
+ });
375
+ searchWrap.append(glass, search);
376
+ toolbar.append(searchWrap);
377
+ if (model().controls.expandable) {
378
+ const groupCount = pivotGroupKeys(model().payload).length;
379
+ const expand = ghostButton(document, tokens, "Expand all", "expand", () => {
380
+ setView({ ...model().view, collapsed: /* @__PURE__ */ new Set() });
381
+ });
382
+ setDisabled(expand, model().view.collapsed.size === 0);
383
+ const collapse = ghostButton(
384
+ document,
385
+ tokens,
386
+ "Collapse all",
387
+ "collapse",
388
+ () => {
389
+ setView({
390
+ ...model().view,
391
+ collapsed: new Set(pivotGroupKeys(model().payload))
392
+ });
393
+ }
394
+ );
395
+ setDisabled(collapse, model().view.collapsed.size === groupCount);
396
+ toolbar.append(expand, collapse);
397
+ }
398
+ if (model().controls.reordered) {
399
+ toolbar.append(
400
+ ghostButton(document, tokens, "Reset order", "reset", () => {
401
+ setView({ ...model().view, sort: null });
402
+ })
403
+ );
404
+ }
405
+ const filterCount = model().controls.filterCount;
406
+ if (filterCount > 0) {
407
+ toolbar.append(
408
+ ghostButton(
409
+ document,
410
+ tokens,
411
+ `Clear filters (${filterCount})`,
412
+ "filter",
413
+ () => {
414
+ setView({ ...model().view, filters: {} });
415
+ }
416
+ )
417
+ );
418
+ }
419
+ const exports = document.createElement("div");
420
+ setStyles(exports, {
421
+ display: "inline-flex",
422
+ "align-items": "center",
423
+ gap: "8px",
424
+ "margin-left": "auto"
425
+ });
426
+ for (const format of ["csv", "excel"]) {
427
+ if (!model().exports[format]) continue;
428
+ exports.append(
429
+ ghostButton(
430
+ document,
431
+ tokens,
432
+ format === "csv" ? "CSV" : "Excel",
433
+ format === "csv" ? "download" : "spreadsheet",
434
+ () => {
435
+ const request = {
436
+ widgetId: model().frame.widgetId,
437
+ format,
438
+ filename: model().exports.filename
439
+ };
440
+ void Promise.resolve().then(() => input().callbacks?.onExport?.(request)).catch((error) => {
441
+ input().callbacks?.onError?.(
442
+ error instanceof Error ? error : new Error("Pivot export failed")
443
+ );
444
+ });
445
+ }
446
+ )
447
+ );
448
+ }
449
+ if (exports.childElementCount > 0) toolbar.append(exports);
450
+ parent.append(toolbar);
451
+ return toolbar;
452
+ }
453
+ function readThreshold(text) {
454
+ const trimmed = text.trim();
455
+ if (trimmed === "") return null;
456
+ const value = Number(trimmed);
457
+ return Number.isFinite(value) ? value : null;
458
+ }
459
+ function openPivotFilterPanel(document, tokens, prop, label, anchor, current, filters, apply, onClose) {
460
+ const backdrop = document.createElement("div");
461
+ backdrop.setAttribute("aria-hidden", "true");
462
+ setStyles(backdrop, { position: "fixed", inset: "0", "z-index": "40" });
463
+ const panel = document.createElement("div");
464
+ panel.setAttribute("role", "dialog");
465
+ panel.setAttribute("aria-label", `Filter ${label}`);
466
+ panel.dataset.bideuxPart = "pivot-filter";
467
+ setStyles(panel, {
468
+ position: "fixed",
469
+ left: `${anchor.left}px`,
470
+ top: `${anchor.top}px`,
471
+ "z-index": "50",
472
+ width: "224px",
473
+ "box-sizing": "border-box",
474
+ padding: "12px",
475
+ border: `1px solid ${tokens.border}`,
476
+ "border-radius": "6px",
477
+ background: tokens.surface,
478
+ color: tokens.surfaceForeground,
479
+ "box-shadow": "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
480
+ "font-family": tokens.fontFamily
481
+ });
482
+ const title = document.createElement("div");
483
+ title.textContent = label;
484
+ setStyles(title, {
485
+ "font-size": "12px",
486
+ "line-height": "16px",
487
+ "font-weight": "500",
488
+ overflow: "hidden",
489
+ "text-overflow": "ellipsis",
490
+ "white-space": "nowrap"
491
+ });
492
+ const fields = document.createElement("div");
493
+ setStyles(fields, {
494
+ display: "flex",
495
+ "flex-direction": "column",
496
+ gap: "8px",
497
+ "margin-top": "8px"
498
+ });
499
+ const select = document.createElement("select");
500
+ select.setAttribute("aria-label", "Comparison");
501
+ for (const option of FILTER_OPS) {
502
+ const entry = document.createElement("option");
503
+ entry.value = option.op;
504
+ entry.textContent = option.label;
505
+ select.append(entry);
506
+ }
507
+ select.value = current?.op ?? "gte";
508
+ setStyles(select, fieldStyles(tokens));
509
+ const value = document.createElement("input");
510
+ value.type = "number";
511
+ value.placeholder = "Value";
512
+ value.value = current === void 0 ? "" : String(current.value);
513
+ value.setAttribute("aria-label", `Keep rows where ${label} matches`);
514
+ setStyles(value, { ...fieldStyles(tokens), width: "100%" });
515
+ fields.append(select, value);
516
+ const footer = document.createElement("div");
517
+ setStyles(footer, {
518
+ display: "flex",
519
+ "align-items": "center",
520
+ "justify-content": "space-between",
521
+ "margin-top": "8px"
522
+ });
523
+ const close = () => {
524
+ backdrop.remove();
525
+ panel.remove();
526
+ onClose();
527
+ };
528
+ const commit = () => {
529
+ const threshold = readThreshold(value.value);
530
+ const op = FILTER_OPS.find((entry) => entry.op === select.value)?.op;
531
+ if (op === void 0) return;
532
+ apply(
533
+ setPivotColumnFilter(
534
+ filters(),
535
+ prop,
536
+ threshold === null ? null : { op, value: threshold }
537
+ )
538
+ );
539
+ };
540
+ const clear = ghostButton(document, tokens, "Clear", "filter", () => {
541
+ apply(setPivotColumnFilter(filters(), prop, null));
542
+ close();
543
+ });
544
+ clear.replaceChildren(document.createTextNode("Clear"));
545
+ const done = document.createElement("button");
546
+ done.type = "button";
547
+ done.textContent = "Done";
548
+ setStyles(done, {
549
+ height: "28px",
550
+ padding: "0 8px",
551
+ border: `1px solid ${tokens.border}`,
552
+ "border-radius": "6px",
553
+ background: "transparent",
554
+ color: "inherit",
555
+ font: "inherit",
556
+ "font-size": "12px",
557
+ cursor: "pointer"
558
+ });
559
+ done.addEventListener("click", close);
560
+ footer.append(clear, done);
561
+ select.addEventListener("change", commit);
562
+ value.addEventListener("input", commit);
563
+ value.addEventListener("keydown", (event) => {
564
+ if (event.key === "Enter" || event.key === "Escape") close();
565
+ });
566
+ backdrop.addEventListener("mousedown", close);
567
+ panel.append(title, fields, footer);
568
+ document.body.append(backdrop, panel);
569
+ value.focus();
570
+ return { prop, close };
571
+ }
572
+
573
+ // ../dashboard-renderer-revogrid/src/renderer.ts
574
+ var defaultRuntime = {
575
+ define(targetWindow) {
576
+ defineCustomElements(targetWindow);
577
+ },
578
+ create(document) {
579
+ return document.createElement("revo-grid");
580
+ }
581
+ };
582
+ function findColumn(columns, prop) {
583
+ for (const column of columns) {
584
+ if (isPivotColumnGroup(column)) {
585
+ const nested = findColumn(column.children, prop);
586
+ if (nested !== null) return nested;
587
+ } else if (column.prop === prop) {
588
+ return column;
589
+ }
590
+ }
591
+ return null;
592
+ }
593
+ function createGrid(input, runtime, model, controls) {
594
+ const document = input.element.ownerDocument;
595
+ const targetWindow = document.defaultView;
596
+ if (targetWindow === null) {
597
+ throw new Error("RevoGrid requires a browser window");
598
+ }
599
+ runtime.define(targetWindow);
600
+ const grid = runtime.create(document);
601
+ grid.dataset.bideuxPart = "revogrid";
602
+ grid.setAttribute("aria-label", `${model.frame.widgetId} pivot table`);
603
+ grid.source = model.rows;
604
+ grid.pinnedBottomSource = model.totalRow === null ? [] : [model.totalRow];
605
+ grid.columns = revoGridColumns(
606
+ model,
607
+ resolveRendererTheme(input.theme),
608
+ controls
609
+ );
610
+ grid.theme = "compact";
611
+ grid.readonly = true;
612
+ grid.resize = true;
613
+ grid.range = false;
614
+ grid.canFocus = true;
615
+ setStyles(grid, { flex: "1", "min-height": "0", width: "100%" });
616
+ return grid;
617
+ }
618
+ async function enableLicensedCapability(input, grid, model) {
619
+ if (input.licensePort === void 0 || input.licensedCapability === void 0) {
620
+ return;
621
+ }
622
+ const license = await input.licensePort.getLicense({
623
+ renderer: "revogrid",
624
+ capability: "pivot-pro"
625
+ });
626
+ if (license === null || license.length === 0) return;
627
+ return input.licensedCapability.enable({ element: grid, license, model });
628
+ }
629
+ async function createRevoGridRenderer(input, runtime = defaultRuntime) {
630
+ let current = input;
631
+ let model = input.model;
632
+ let grid;
633
+ let licensed;
634
+ let filterPanel = null;
635
+ const tokens = resolveRendererTheme(input.theme);
636
+ clearElement(input.element);
637
+ applyWidgetTheme(input.element, model.frame, tokens);
638
+ setStyles(input.element, {
639
+ display: "flex",
640
+ "flex-direction": "column",
641
+ gap: "4px",
642
+ overflow: "hidden"
643
+ });
644
+ if (model.frame.label?.position === "top" && model.frame.label.text !== void 0) {
645
+ input.element.append(
646
+ createLabel(input.element.ownerDocument, model.frame.label.text, tokens)
647
+ );
648
+ }
649
+ const updateGrid = (notifyHost = false) => {
650
+ grid.source = model.rows;
651
+ grid.pinnedBottomSource = model.totalRow === null ? [] : [model.totalRow];
652
+ grid.columns = revoGridColumns(
653
+ model,
654
+ resolveRendererTheme(current.theme),
655
+ headerControls
656
+ );
657
+ if (notifyHost) {
658
+ current.callbacks?.onPivotViewChange?.(model.frame.widgetId, model.view);
659
+ }
660
+ };
661
+ const renderToolbar = () => {
662
+ const previousSearch = toolbar.querySelector("input[type=search]");
663
+ const searchFocused = previousSearch !== null && previousSearch.ownerDocument.activeElement === previousSearch;
664
+ const caret = previousSearch?.selectionStart ?? null;
665
+ toolbar.remove();
666
+ toolbar = createPivotToolbar(
667
+ () => current,
668
+ () => model,
669
+ setView,
670
+ resolveRendererTheme(current.theme),
671
+ current.element
672
+ );
673
+ current.element.insertBefore(toolbar, grid);
674
+ if (searchFocused) {
675
+ const search = toolbar.querySelector("input[type=search]");
676
+ if (search !== null) {
677
+ search.focus();
678
+ if (caret !== null) search.setSelectionRange(caret, caret);
679
+ }
680
+ }
681
+ };
682
+ const setView = (view) => {
683
+ model = projectPivotView(model, view);
684
+ updateGrid(true);
685
+ renderToolbar();
686
+ licensed?.updateModel(model);
687
+ };
688
+ const headerControls = {
689
+ toggleColumnGroup: (key) => {
690
+ const collapsedColumns = new Set(model.view.collapsedColumns);
691
+ if (!collapsedColumns.delete(key)) collapsedColumns.add(key);
692
+ setView({ ...model.view, collapsedColumns });
693
+ },
694
+ openFilter: (prop, anchor) => {
695
+ filterPanel?.close();
696
+ const label = model.valueColumns.find((column) => column.key === prop)?.label ?? prop;
697
+ filterPanel = openPivotFilterPanel(
698
+ current.element.ownerDocument,
699
+ resolveRendererTheme(current.theme),
700
+ prop,
701
+ label,
702
+ anchor,
703
+ model.view.filters[prop],
704
+ () => model.view.filters,
705
+ (filters) => {
706
+ setView({ ...model.view, filters });
707
+ },
708
+ () => {
709
+ filterPanel = null;
710
+ }
711
+ );
712
+ }
713
+ };
714
+ let toolbar = createPivotToolbar(
715
+ () => current,
716
+ () => model,
717
+ setView,
718
+ tokens,
719
+ input.element
720
+ );
721
+ try {
722
+ grid = createGrid(input, runtime, model, headerControls);
723
+ input.element.append(grid);
724
+ } catch (error) {
725
+ const normalized = error instanceof Error ? error : new Error("RevoGrid rendering failed");
726
+ input.callbacks?.onError?.(normalized);
727
+ clearElement(input.element);
728
+ input.element.append(
729
+ createMessage(
730
+ input.element.ownerDocument,
731
+ normalized.message,
732
+ "error",
733
+ tokens
734
+ )
735
+ );
736
+ throw normalized;
737
+ }
738
+ try {
739
+ licensed = await enableLicensedCapability(input, grid, model);
740
+ } catch (error) {
741
+ input.callbacks?.onError?.(
742
+ error instanceof Error ? error : new Error("RevoGrid licensed capability failed")
743
+ );
744
+ }
745
+ const handleFocus = (event) => {
746
+ if (current.widget === void 0 || !(event instanceof CustomEvent)) {
747
+ return;
748
+ }
749
+ const detail = event.detail;
750
+ const original = detail.originalEvent;
751
+ if (original === void 0) return;
752
+ if (original instanceof KeyboardEvent && original.key !== "Enter" && original.key !== " ") {
753
+ return;
754
+ }
755
+ const prop = String(detail.prop);
756
+ const column = findColumn(model.columns, prop);
757
+ const meta = detail.model[PIVOT_META_PROP];
758
+ if (column === null || meta === void 0) return;
759
+ const action = pivotCellAction(
760
+ model,
761
+ current.widget,
762
+ meta,
763
+ column,
764
+ detail.value,
765
+ current.callbacks?.onPivotDetail !== void 0
766
+ );
767
+ if (action?.kind === "toggle-row-group") {
768
+ setView({
769
+ ...model.view,
770
+ collapsed: togglePivotGroup(model.view.collapsed, action.key)
771
+ });
772
+ } else if (action?.kind === "interaction") {
773
+ current.callbacks?.onInteraction?.(action.interaction, action.context);
774
+ } else if (action?.kind === "detail") {
775
+ const request = {
776
+ widgetId: model.frame.widgetId,
777
+ filters: action.filters,
778
+ label: action.label
779
+ };
780
+ void Promise.resolve().then(() => current.callbacks?.onPivotDetail?.(request)).catch((error) => {
781
+ current.callbacks?.onError?.(
782
+ error instanceof Error ? error : new Error("Pivot detail request failed")
783
+ );
784
+ });
785
+ }
786
+ };
787
+ const handleHeaderClick = (event) => {
788
+ if (!(event instanceof CustomEvent)) return;
789
+ const detail = event.detail;
790
+ if (detail.prop === void 0) return;
791
+ const prop = String(detail.prop);
792
+ if (findColumn(model.columns, prop) === null) return;
793
+ setView({
794
+ ...model.view,
795
+ sort: nextPivotSort(model.view, model.payload, prop)
796
+ });
797
+ };
798
+ grid.addEventListener("beforecellfocus", handleFocus);
799
+ grid.addEventListener("headerclick", handleHeaderClick);
800
+ if (model.truncation !== null) {
801
+ const notice = createMessage(
802
+ input.element.ownerDocument,
803
+ model.truncation,
804
+ "notice",
805
+ tokens
806
+ );
807
+ setStyles(notice, { height: "auto", flex: "0 0 auto" });
808
+ input.element.append(notice);
809
+ }
810
+ if (model.frame.label?.position === "bottom" && model.frame.label.text !== void 0) {
811
+ input.element.append(
812
+ createLabel(input.element.ownerDocument, model.frame.label.text, tokens)
813
+ );
814
+ }
815
+ return {
816
+ update(next) {
817
+ if (next.element !== current.element) {
818
+ throw new Error(
819
+ "RevoGrid renderers cannot move between elements. Create a new renderer."
820
+ );
821
+ }
822
+ current = next;
823
+ model = next.model;
824
+ renderToolbar();
825
+ applyWidgetTheme(
826
+ next.element,
827
+ model.frame,
828
+ resolveRendererTheme(next.theme)
829
+ );
830
+ updateGrid();
831
+ licensed?.updateModel(model);
832
+ },
833
+ resize() {
834
+ grid.dispatchEvent(new Event("resize"));
835
+ licensed?.resize();
836
+ },
837
+ destroy() {
838
+ filterPanel?.close();
839
+ grid.removeEventListener("beforecellfocus", handleFocus);
840
+ grid.removeEventListener("headerclick", handleHeaderClick);
841
+ licensed?.destroy();
842
+ clearElement(current.element);
843
+ }
844
+ };
845
+ }
846
+
847
+ // ../dashboard-renderer-revogrid/src/widget-factory.ts
848
+ function createRevoGridWidgetFactory(runtime) {
849
+ const pivotInput = (input) => {
850
+ const model = buildPivotRendererModel(
851
+ input.widget,
852
+ input.result ?? {},
853
+ input.context ?? {}
854
+ );
855
+ if (model.status !== "ready") {
856
+ throw new Error(model.message);
857
+ }
858
+ return {
859
+ element: input.element,
860
+ model,
861
+ widget: input.widget,
862
+ ...input.callbacks === void 0 ? {} : { callbacks: input.callbacks },
863
+ ...input.theme === void 0 ? {} : { theme: input.theme }
864
+ };
865
+ };
866
+ return async (input) => {
867
+ const first = pivotInput(input);
868
+ const renderer = runtime === void 0 ? await createRevoGridRenderer(first) : await createRevoGridRenderer(first, runtime);
869
+ return {
870
+ update: (next) => {
871
+ renderer.update(pivotInput(next));
872
+ },
873
+ resize: () => renderer.resize(),
874
+ destroy: () => renderer.destroy()
875
+ };
876
+ };
877
+ }
878
+
879
+ export { COLLAPSED_MARK, EXPANDED_MARK, ROW_HEADER_COLUMN_SIZE, ROW_INDENT, VALUE_COLUMN_SIZE, createRevoGridRenderer, createRevoGridWidgetFactory, projectPivotView, resetPivotView, revoGridColumns };
880
+ //# sourceMappingURL=chunk-NGM5R3LW.js.map
881
+ //# sourceMappingURL=chunk-NGM5R3LW.js.map