@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,1004 @@
1
+ import { formatValue, buildChartRendererModel } from './chunk-OMPR32AR.js';
2
+ import { buildPivotRendererModel } from './chunk-LHD445PJ.js';
3
+ import { buildTabulatorRendererModel, buildTableRendererModel } from './chunk-QDQ5VVTC.js';
4
+ import { stringValue, displayBlock, numberValue, isRecord, rendererFrame, clearElement, record, resolveRendererTheme, createMessage, applyWidgetTheme, setStyles, createLabel, getClickInteraction } from './chunk-75CRTQK7.js';
5
+
6
+ // ../dashboard-core/src/filter-text.ts
7
+ var TOKEN = /\{\{\s*filter:([A-Za-z0-9_.-]+)\s*\}\}/g;
8
+ function isPeriod(value) {
9
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10
+ }
11
+ function formatFilterValue(value, empty = "All") {
12
+ if (value === void 0 || value === null || value === "") return empty;
13
+ if (Array.isArray(value)) {
14
+ const parts = value.filter((entry) => entry !== null && entry !== void 0 && entry !== "").map((entry) => String(entry));
15
+ return parts.length > 0 ? parts.join(", ") : empty;
16
+ }
17
+ if (isPeriod(value)) {
18
+ const period = value;
19
+ if (period.start && period.end && period.start !== period.end) {
20
+ return `${period.start} to ${period.end}`;
21
+ }
22
+ return period.start ?? period.end ?? empty;
23
+ }
24
+ return String(value);
25
+ }
26
+ function fillFilterTokens(content2, filters, labels = {}) {
27
+ return content2.replace(TOKEN, (whole, id) => {
28
+ if (labels[id]) return labels[id];
29
+ if (!(id in filters)) return whole;
30
+ return formatFilterValue(filters[id]);
31
+ });
32
+ }
33
+ function mentionsFilter(content2) {
34
+ TOKEN.lastIndex = 0;
35
+ return TOKEN.test(content2);
36
+ }
37
+
38
+ // ../dashboard-renderers/src/filter-model.ts
39
+ function record2(value) {
40
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
41
+ }
42
+ function text(value) {
43
+ return typeof value === "string" && value !== "" ? value : void 0;
44
+ }
45
+ function filterOptions(widget, result) {
46
+ const fromResult = Array.isArray(result.options) ? result.options : null;
47
+ const configured = record2(widget.spec.data).options;
48
+ const fromSpec = Array.isArray(configured) ? configured : [];
49
+ return (fromResult ?? fromSpec).map((entry) => record2(entry)).filter(
50
+ (entry) => typeof entry.label === "string"
51
+ ).map((entry) => ({ value: entry.value, label: entry.label }));
52
+ }
53
+ function describeFilter(value, options) {
54
+ const name = (entry) => options.find((option) => String(option.value) === String(entry))?.label ?? String(entry);
55
+ if (Array.isArray(value)) {
56
+ return value.length === 0 ? "All" : value.map(name).join(", ");
57
+ }
58
+ if (typeof value === "object" && value !== null) {
59
+ const range = record2(value);
60
+ const start = text(range.start) ?? "Start";
61
+ const end = text(range.end) ?? "End";
62
+ return `${start} to ${end}`;
63
+ }
64
+ return value === "" || value === null || value === void 0 ? "All" : name(value);
65
+ }
66
+ function resolveControl(display) {
67
+ const control = text(display.control);
68
+ if (control === "radio") return "radio";
69
+ if (control === "date_range") return "date_range";
70
+ if (control === "date" || control === "calendar") return "date";
71
+ if (control === "input" || control === "custom") return "input";
72
+ return display.multi === true ? "multiselect" : "select";
73
+ }
74
+ function isRelativeDateExpression(value) {
75
+ const normalized = value.trim().toLowerCase();
76
+ const relative = /^(today|yesterday|tomorrow|\d+\s+(days?|weeks?|months?|years?)\s+ago\s+for\s+\d+\s+(days?|weeks?|months?|years?)|\d+\s+(days?|weeks?|months?|years?)\s+(ago|from now)|(last|past|previous|trailing)?\s*\d+\s+(days?|weeks?|months?|years?)|this\s+(week|month|quarter|year)|last\s+(week|month|quarter|year)|next\s+(week|month|quarter|year))$/;
77
+ return relative.test(normalized) || normalized.startsWith("before ") || normalized.startsWith("after ");
78
+ }
79
+ function isIsoDate(value) {
80
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
81
+ return !Number.isNaN(Date.parse(value));
82
+ }
83
+ function buildFilterModel(widget, result = {}) {
84
+ const component = widget.spec.component === "select" ? "select" : "filter";
85
+ const display = record2(record2(widget.spec.display)[component]);
86
+ const options = filterOptions(widget, result);
87
+ const header = record2(widget.metadata.header);
88
+ const presets = Array.isArray(display.presets) ? display.presets.filter(
89
+ (entry) => typeof entry === "string" && entry !== ""
90
+ ) : [];
91
+ return {
92
+ labelPosition: record2(display.label).position === "bottom" ? "bottom" : "top",
93
+ showObjectHeader: widget.metadata.header !== void 0 && header.visible !== false,
94
+ radioOrientation: display.radio_orientation === "horizontal" ? "horizontal" : "vertical",
95
+ presets,
96
+ showIcon: display.show_icon !== false,
97
+ widgetId: widget.metadata.id,
98
+ label: text(record2(display.label).text) ?? text(display.label) ?? widget.metadata.name,
99
+ control: resolveControl(display),
100
+ options,
101
+ ...text(display.placeholder) === void 0 ? {} : { placeholder: text(display.placeholder) },
102
+ defaultValue: display.default_value ?? record2(widget.spec.data).default_value ?? "",
103
+ panelled: display.panel === true || display.panelled === true
104
+ };
105
+ }
106
+
107
+ // ../dashboard-renderers/src/state.ts
108
+ var DATA_COMPONENTS = /* @__PURE__ */ new Set([
109
+ "kpi",
110
+ "echarts",
111
+ "table",
112
+ "tabulator",
113
+ "pivot_table"
114
+ ]);
115
+ function loadingState(component) {
116
+ return {
117
+ kind: "loading",
118
+ skeleton: component === "kpi" ? "kpi" : component === "echarts" ? "chart" : "content",
119
+ message: "Loading widget"
120
+ };
121
+ }
122
+ function missingChartColumns(widget, result) {
123
+ const rows = Array.isArray(result.rows) ? result.rows : [];
124
+ const returned = new Set(
125
+ Array.isArray(result.columns) ? result.columns.filter(
126
+ (column) => typeof column === "string"
127
+ ) : Object.keys(record(rows[0]))
128
+ );
129
+ const mappings = record(widget.spec.mappings);
130
+ const named = [mappings.x, mappings.category, mappings.value].filter(
131
+ (field) => typeof field === "string" && field.length > 0
132
+ );
133
+ const display = record(record(widget.spec.display).echarts);
134
+ const hasSeries = Array.isArray(display.series);
135
+ const missing = named.filter((field) => !returned.has(field));
136
+ if (hasSeries || named.length === 0 || missing.length !== named.length) {
137
+ return void 0;
138
+ }
139
+ return {
140
+ kind: "notice",
141
+ message: `Nothing to draw: this chart reads ${missing.join(", ")}, and the query returned ${[...returned].join(", ") || "no columns"}.`
142
+ };
143
+ }
144
+ function missingKpiValue(widget, result) {
145
+ const mapped = stringValue(record(widget.spec.mappings).value);
146
+ const returned = Array.isArray(result.columns) ? result.columns.filter(
147
+ (column) => typeof column === "string"
148
+ ) : [];
149
+ const arrived = mapped !== void 0 && returned.includes(mapped);
150
+ return {
151
+ kind: "notice",
152
+ message: mapped === void 0 ? "No number to show: pick the column the number comes from." : arrived ? `No number to show: ${mapped} came back empty for the current filters, so there is nothing to add up.` : `No number to show: this card reads ${mapped}, which the query did not return${returned.length > 0 ? `. It returned ${returned.join(", ")}` : ""}.`
153
+ };
154
+ }
155
+ function resolveWidgetRenderState(widget, result = {}, options = {}) {
156
+ const component = widget.spec.component ?? "";
157
+ const isDataComponent = DATA_COMPONENTS.has(component);
158
+ const loading = options.loading === true;
159
+ const hasAnswered = isDataComponent && !loading && Object.keys(result).length > 0;
160
+ const answeredOnce = options.answeredOnce === true || hasAnswered;
161
+ if (loading && isDataComponent && options.answeredOnce !== true) {
162
+ return { state: loadingState(component), answeredOnce };
163
+ }
164
+ if (component !== "filter" && component !== "select" && typeof result.error === "string") {
165
+ const state = {
166
+ kind: "error",
167
+ message: result.error,
168
+ retryable: options.retryable === true
169
+ };
170
+ const hint = stringValue(result.error_hint);
171
+ if (hint !== void 0) state.hint = hint;
172
+ return { state, answeredOnce };
173
+ }
174
+ if (isDataComponent && Object.keys(result).length === 0) {
175
+ return {
176
+ state: {
177
+ kind: "notice",
178
+ message: "Not loaded yet. Save the layout and this widget will be queried with the rest."
179
+ },
180
+ answeredOnce
181
+ };
182
+ }
183
+ if ((component === "echarts" || component === "table" || component === "tabulator" || component === "pivot_table") && Array.isArray(result.rows) && result.rows.length === 0) {
184
+ return {
185
+ state: {
186
+ kind: "notice",
187
+ message: "The query returned no rows for the current filters."
188
+ },
189
+ answeredOnce
190
+ };
191
+ }
192
+ if (component === "echarts" && Array.isArray(result.rows)) {
193
+ const missing = missingChartColumns(widget, result);
194
+ if (missing !== void 0) return { state: missing, answeredOnce };
195
+ }
196
+ if (component === "kpi" && (result.value === void 0 || result.value === null)) {
197
+ return {
198
+ state: missingKpiValue(widget, result),
199
+ answeredOnce
200
+ };
201
+ }
202
+ return {
203
+ state: {
204
+ kind: "ready",
205
+ refreshing: loading && isDataComponent
206
+ },
207
+ answeredOnce
208
+ };
209
+ }
210
+
211
+ // ../dashboard-renderers/src/models.ts
212
+ function deltaTone(value) {
213
+ if (value > 0) return "up";
214
+ if (value < 0) return "down";
215
+ return "flat";
216
+ }
217
+ function deltaIcon(value, tone) {
218
+ if (value === "up" || value === "down" || value === "neutral" || value === "none") {
219
+ return value;
220
+ }
221
+ return tone === "flat" ? "neutral" : tone;
222
+ }
223
+ function tooltipIcon(value) {
224
+ return value === "info" || value === "check" || value === "help" || value === "warning" ? value : void 0;
225
+ }
226
+ function kpiDelta(result, display, valueFormat, valuePrefix) {
227
+ const delta = numberValue(result.delta);
228
+ if (delta === void 0) return void 0;
229
+ const configuration = record(display.delta);
230
+ const tone = deltaTone(delta);
231
+ const format = stringValue(configuration.format) ?? stringValue(result.delta_format) ?? "number";
232
+ const prefix = stringValue(configuration.prefix) ?? "";
233
+ const comparedWith = typeof result.delta_label === "string" ? `vs ${result.delta_label}` : void 0;
234
+ const model = {
235
+ value: delta,
236
+ formattedValue: format === "percent" ? new Intl.NumberFormat("en-US", {
237
+ style: "percent",
238
+ maximumFractionDigits: 1
239
+ }).format(delta) : formatValue(delta, {
240
+ format,
241
+ prefix,
242
+ compact: configuration.compact === true
243
+ }),
244
+ tone,
245
+ icon: deltaIcon(configuration.icon, tone)
246
+ };
247
+ if (comparedWith !== void 0) model.comparedWith = comparedWith;
248
+ const tooltip = stringValue(configuration.tooltip) ?? comparedWith;
249
+ if (tooltip !== void 0) model.tooltip = tooltip;
250
+ const icon = tooltipIcon(configuration.tooltip_icon);
251
+ if (icon !== void 0) model.tooltipIcon = icon;
252
+ const color = stringValue(configuration.color);
253
+ if (color !== void 0) model.color = color;
254
+ const reference = numberValue(result.delta_ref);
255
+ if (reference !== void 0) {
256
+ model.reference = {
257
+ value: reference,
258
+ formattedValue: formatValue(reference, {
259
+ format: valueFormat,
260
+ prefix: valuePrefix
261
+ })
262
+ };
263
+ }
264
+ return model;
265
+ }
266
+ function kpiFrame(widget, result, context) {
267
+ const display = displayBlock(widget, "kpi");
268
+ const frame = rendererFrame(
269
+ widget,
270
+ "kpi",
271
+ context.widgetDefaults,
272
+ context.framed
273
+ );
274
+ if (!frame.showObjectHeader && frame.label === void 0) {
275
+ const label = stringValue(result.label) ?? widget.metadata.name;
276
+ frame.label = {
277
+ text: label,
278
+ position: display.position === "bottom" ? "bottom" : "top"
279
+ };
280
+ }
281
+ return frame;
282
+ }
283
+ function buildKpiRendererModel(widget, result, context = {}) {
284
+ const display = displayBlock(widget, "kpi");
285
+ const value = Number(result.value);
286
+ const format = stringValue(display.format) ?? stringValue(result.format) ?? "number";
287
+ const prefix = stringValue(display.prefix) ?? "";
288
+ const suffix = stringValue(display.suffix) ?? "";
289
+ const decimals = numberValue(display.decimals);
290
+ const options = {
291
+ format,
292
+ prefix,
293
+ suffix,
294
+ compact: display.compact === true,
295
+ ...decimals === void 0 ? {} : { decimals }
296
+ };
297
+ const formattedValue = formatValue(value, options);
298
+ const model = {
299
+ kind: "kpi",
300
+ frame: kpiFrame(widget, result, context),
301
+ value,
302
+ formattedValue,
303
+ settledValue: formattedValue,
304
+ valueFormat: options
305
+ };
306
+ const delta = kpiDelta(result, display, format, prefix);
307
+ if (delta !== void 0) model.delta = delta;
308
+ const note = stringValue(result.delta_note);
309
+ if (note !== void 0) model.note = note;
310
+ return model;
311
+ }
312
+ function content(raw, context) {
313
+ return fillFilterTokens(
314
+ stringValue(raw) ?? "",
315
+ context.filters ?? {},
316
+ context.filterLabels ?? {}
317
+ );
318
+ }
319
+ function buildTextRendererModel(widget, context = {}) {
320
+ const display = displayBlock(widget, "text");
321
+ const custom = isRecord(display.style) ? Object.fromEntries(
322
+ Object.entries(display.style).filter(
323
+ (entry) => typeof entry[1] === "string" || typeof entry[1] === "number"
324
+ )
325
+ ) : {};
326
+ const model = {
327
+ kind: "text",
328
+ frame: rendererFrame(
329
+ widget,
330
+ "text",
331
+ context.widgetDefaults,
332
+ context.framed
333
+ ),
334
+ content: content(display.content, context),
335
+ contentFormat: display.markdown === true ? "markdown" : "text",
336
+ style: {
337
+ textAlign: display.text_align === "center" || display.text_align === "right" || display.text_align === "justify" ? display.text_align : "left",
338
+ verticalAlign: display.vertical_align === "middle" || display.vertical_align === "bottom" ? display.vertical_align : "top",
339
+ truncate: display.truncate === true,
340
+ custom
341
+ }
342
+ };
343
+ const style = model.style;
344
+ const fontSize = display.font_size;
345
+ if (typeof fontSize === "string" || typeof fontSize === "number") {
346
+ style.fontSize = fontSize;
347
+ }
348
+ const fontWeight = display.font_weight;
349
+ if (typeof fontWeight === "string" || typeof fontWeight === "number") {
350
+ style.fontWeight = fontWeight;
351
+ }
352
+ const color = stringValue(display.color);
353
+ if (color !== void 0) style.color = color;
354
+ const lineHeight = display.line_height;
355
+ if (typeof lineHeight === "string" || typeof lineHeight === "number") {
356
+ style.lineHeight = lineHeight;
357
+ }
358
+ const letterSpacing = display.letter_spacing;
359
+ if (typeof letterSpacing === "string" || typeof letterSpacing === "number") {
360
+ style.letterSpacing = letterSpacing;
361
+ }
362
+ if (display.white_space === "normal" || display.white_space === "nowrap" || display.white_space === "pre" || display.white_space === "pre-wrap" || display.white_space === "pre-line") {
363
+ style.whiteSpace = display.white_space;
364
+ }
365
+ return model;
366
+ }
367
+ function buildMarkdownRendererModel(widget, context = {}) {
368
+ const display = displayBlock(widget, "markdown");
369
+ return {
370
+ kind: "markdown",
371
+ frame: rendererFrame(
372
+ widget,
373
+ "markdown",
374
+ context.widgetDefaults,
375
+ context.framed
376
+ ),
377
+ content: content(display.content, context)
378
+ };
379
+ }
380
+ function buildImageRendererModel(widget, context = {}) {
381
+ const display = displayBlock(widget, "image");
382
+ return {
383
+ kind: "image",
384
+ frame: rendererFrame(
385
+ widget,
386
+ "image",
387
+ context.widgetDefaults,
388
+ context.framed
389
+ ),
390
+ source: stringValue(display.url) ?? null,
391
+ alt: stringValue(display.alt) ?? widget.metadata.name,
392
+ objectFit: display.object_fit === "cover" ? "cover" : "contain"
393
+ };
394
+ }
395
+ function extensionName(widget) {
396
+ const component = widget.spec.component;
397
+ if (component !== "kpi" && component !== "echarts") return void 0;
398
+ return stringValue(displayBlock(widget, component).customRenderer);
399
+ }
400
+ function extensionModel(name, widget, result, context) {
401
+ const renderer = context.registry?.get(name);
402
+ if (renderer === void 0) return void 0;
403
+ const component = widget.spec.component === "kpi" ? "kpi" : "echarts";
404
+ const frame = rendererFrame(
405
+ widget,
406
+ component,
407
+ context.widgetDefaults,
408
+ context.framed
409
+ );
410
+ return {
411
+ kind: "extension",
412
+ frame,
413
+ name,
414
+ content: renderer({ widget, result, frame, context })
415
+ };
416
+ }
417
+ function buildBasicRendererModel(widget, result = {}, context = {}) {
418
+ const extension = extensionName(widget);
419
+ if (extension !== void 0) {
420
+ const model = extensionModel(extension, widget, result, context);
421
+ if (model !== void 0) return model;
422
+ }
423
+ switch (widget.spec.component) {
424
+ case "kpi":
425
+ return buildKpiRendererModel(widget, result, context);
426
+ case "echarts":
427
+ return buildChartRendererModel(widget, result, context);
428
+ case "table":
429
+ return buildTableRendererModel(widget, result, context);
430
+ case "tabulator":
431
+ return buildTabulatorRendererModel(widget, result, context);
432
+ case "pivot_table":
433
+ return buildPivotRendererModel(widget, result, context);
434
+ case "text":
435
+ return buildTextRendererModel(widget, context);
436
+ case "markdown":
437
+ return buildMarkdownRendererModel(widget, context);
438
+ case "image":
439
+ return buildImageRendererModel(widget, context);
440
+ default:
441
+ return {
442
+ kind: "unknown",
443
+ frame: {
444
+ widgetId: widget.metadata.id,
445
+ component: widget.spec.component ?? "",
446
+ theme: {},
447
+ showObjectHeader: false,
448
+ interactive: false
449
+ },
450
+ message: "Unknown widget type"
451
+ };
452
+ }
453
+ }
454
+ function buildWidgetRendererModel(widget, result = {}, context = {}) {
455
+ const stateOptions = {
456
+ ...context.loading === void 0 ? {} : { loading: context.loading },
457
+ ...context.answeredOnce === void 0 ? {} : { answeredOnce: context.answeredOnce },
458
+ ...context.retryable === void 0 ? {} : { retryable: context.retryable }
459
+ };
460
+ const resolution = resolveWidgetRenderState(widget, result, stateOptions);
461
+ if (resolution.state.kind !== "ready") return resolution;
462
+ const extension = extensionName(widget);
463
+ if (extension !== void 0 && context.registry?.get(extension) === void 0) {
464
+ return {
465
+ ...resolution,
466
+ state: { kind: "notice", message: `Unknown renderer: ${extension}` }
467
+ };
468
+ }
469
+ return {
470
+ ...resolution,
471
+ model: buildBasicRendererModel(widget, result, context)
472
+ };
473
+ }
474
+
475
+ // ../dashboard-renderer-web/src/markdown.ts
476
+ function safeLink(value) {
477
+ if (value.startsWith("#") || value.startsWith("/") || /^https?:\/\//u.test(value) || /^mailto:/u.test(value)) {
478
+ return value;
479
+ }
480
+ return null;
481
+ }
482
+ function renderInlineMarkdown(document, parent, content2) {
483
+ const pattern = /(\*\*[^*]+\*\*|~~[^~]+~~|`[^`]+`|\[[^\]]+\]\([^)]+\)|\*[^*]+\*)/gu;
484
+ let offset = 0;
485
+ for (const match of content2.matchAll(pattern)) {
486
+ const index = match.index;
487
+ if (index > offset) {
488
+ parent.append(document.createTextNode(content2.slice(offset, index)));
489
+ }
490
+ const token = match[0];
491
+ if (token.startsWith("**")) {
492
+ const strong = document.createElement("strong");
493
+ strong.textContent = token.slice(2, -2);
494
+ parent.append(strong);
495
+ } else if (token.startsWith("~~")) {
496
+ const strike = document.createElement("del");
497
+ strike.textContent = token.slice(2, -2);
498
+ parent.append(strike);
499
+ } else if (token.startsWith("`")) {
500
+ const code = document.createElement("code");
501
+ code.textContent = token.slice(1, -1);
502
+ parent.append(code);
503
+ } else if (token.startsWith("[")) {
504
+ const parts = /^\[([^\]]+)\]\(([^)]+)\)$/u.exec(token);
505
+ const href = parts === null ? null : safeLink(parts[2] ?? "");
506
+ if (parts === null || href === null) {
507
+ parent.append(document.createTextNode(token));
508
+ } else {
509
+ const link = document.createElement("a");
510
+ link.textContent = parts[1] ?? "";
511
+ link.href = href;
512
+ if (/^https?:\/\//u.test(href)) {
513
+ link.rel = "noreferrer";
514
+ link.target = "_blank";
515
+ }
516
+ parent.append(link);
517
+ }
518
+ } else {
519
+ const emphasis = document.createElement("em");
520
+ emphasis.textContent = token.slice(1, -1);
521
+ parent.append(emphasis);
522
+ }
523
+ offset = index + token.length;
524
+ }
525
+ if (offset < content2.length) {
526
+ parent.append(document.createTextNode(content2.slice(offset)));
527
+ }
528
+ }
529
+ function renderMarkdown(document, parent, markdown) {
530
+ const lines = markdown.replaceAll("\r\n", "\n").split("\n");
531
+ let list = null;
532
+ let paragraph = null;
533
+ let fence = null;
534
+ const closeBlocks = () => {
535
+ list = null;
536
+ paragraph = null;
537
+ };
538
+ for (const line of lines) {
539
+ if (fence !== null) {
540
+ if (/^\s*```/u.test(line)) {
541
+ fence = null;
542
+ } else {
543
+ fence.textContent = `${fence.textContent ?? ""}${line}
544
+ `;
545
+ }
546
+ continue;
547
+ }
548
+ if (/^\s*```/u.test(line)) {
549
+ closeBlocks();
550
+ const pre = document.createElement("pre");
551
+ fence = document.createElement("code");
552
+ pre.append(fence);
553
+ parent.append(pre);
554
+ continue;
555
+ }
556
+ const heading = /^(#{1,6})\s+(.+)$/u.exec(line);
557
+ const bullet = /^\s*[-*+]\s+(.+)$/u.exec(line);
558
+ const numbered = /^\s*\d+[.)]\s+(.+)$/u.exec(line);
559
+ const quote = /^\s*>\s?(.*)$/u.exec(line);
560
+ const item = bullet ?? numbered;
561
+ if (item !== null) {
562
+ const tag = bullet === null ? "ol" : "ul";
563
+ if (list === null || list.tagName.toLowerCase() !== tag) {
564
+ list = document.createElement(tag);
565
+ parent.append(list);
566
+ }
567
+ paragraph = null;
568
+ const entry = document.createElement("li");
569
+ renderInlineMarkdown(document, entry, item[1] ?? "");
570
+ list.append(entry);
571
+ continue;
572
+ }
573
+ if (/^\s*([-*_])(\s*\1){2,}\s*$/u.test(line)) {
574
+ closeBlocks();
575
+ parent.append(document.createElement("hr"));
576
+ continue;
577
+ }
578
+ if (line.trim().length === 0) {
579
+ closeBlocks();
580
+ continue;
581
+ }
582
+ if (heading !== null) {
583
+ closeBlocks();
584
+ const block = document.createElement(
585
+ `h${heading[1]?.length ?? 1}`
586
+ );
587
+ renderInlineMarkdown(document, block, heading[2] ?? "");
588
+ parent.append(block);
589
+ continue;
590
+ }
591
+ if (quote !== null) {
592
+ closeBlocks();
593
+ const block = document.createElement("blockquote");
594
+ renderInlineMarkdown(document, block, quote[1] ?? "");
595
+ parent.append(block);
596
+ continue;
597
+ }
598
+ list = null;
599
+ if (paragraph === null) {
600
+ paragraph = document.createElement("p");
601
+ parent.append(paragraph);
602
+ } else {
603
+ paragraph.append(document.createTextNode(" "));
604
+ }
605
+ renderInlineMarkdown(document, paragraph, line.trim());
606
+ }
607
+ }
608
+
609
+ // ../dashboard-renderer-web/src/native-renderer.ts
610
+ function record3(value) {
611
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
612
+ }
613
+ function stringValue2(value) {
614
+ return typeof value === "string" ? value : void 0;
615
+ }
616
+ function frameContainer(element, model, tokens) {
617
+ applyWidgetTheme(element, model.frame, tokens);
618
+ setStyles(element, {
619
+ display: "flex",
620
+ "flex-direction": "column",
621
+ gap: "8px",
622
+ overflow: "auto"
623
+ });
624
+ const document = element.ownerDocument;
625
+ const body = document.createElement("div");
626
+ body.dataset.bideuxPart = "body";
627
+ setStyles(body, { flex: "1", "min-height": "0" });
628
+ if (model.frame.label?.position === "top" && model.frame.label.text !== void 0) {
629
+ element.append(createLabel(document, model.frame.label.text, tokens));
630
+ }
631
+ element.append(body);
632
+ if (model.frame.label?.position === "bottom" && model.frame.label.text !== void 0) {
633
+ element.append(createLabel(document, model.frame.label.text, tokens));
634
+ }
635
+ return body;
636
+ }
637
+ function bindInteraction(element, widget, callbacks) {
638
+ const interaction = getClickInteraction(widget);
639
+ if (interaction === void 0 || callbacks.onInteraction === void 0) {
640
+ return;
641
+ }
642
+ const context = {
643
+ widgetId: widget.metadata.id
644
+ };
645
+ const activate = () => callbacks.onInteraction?.(interaction, context);
646
+ element.tabIndex = 0;
647
+ element.setAttribute("role", "button");
648
+ element.addEventListener("click", activate);
649
+ element.addEventListener("keydown", (event) => {
650
+ if (event.key === "Enter" || event.key === " ") {
651
+ event.preventDefault();
652
+ activate();
653
+ }
654
+ });
655
+ }
656
+ function renderKpi(body, model, tokens) {
657
+ setStyles(body, {
658
+ display: "flex",
659
+ "flex-direction": "column",
660
+ "justify-content": "center",
661
+ gap: "8px"
662
+ });
663
+ const value = body.ownerDocument.createElement("strong");
664
+ value.dataset.bideuxPart = "kpi-value";
665
+ value.textContent = model.formattedValue;
666
+ setStyles(value, {
667
+ color: tokens.foreground,
668
+ "font-size": "clamp(2rem, 5vw, 3rem)",
669
+ "letter-spacing": "-0.04em"
670
+ });
671
+ body.append(value);
672
+ if (model.delta !== void 0) {
673
+ const delta = body.ownerDocument.createElement("span");
674
+ delta.dataset.tone = model.delta.tone;
675
+ delta.textContent = `${model.delta.icon === "up" ? "\u2191 " : model.delta.icon === "down" ? "\u2193 " : ""}${model.delta.formattedValue}${model.delta.comparedWith === void 0 ? "" : ` ${model.delta.comparedWith}`}`;
676
+ setStyles(delta, {
677
+ color: model.delta.color ?? (model.delta.tone === "down" ? tokens.danger : tokens.primary),
678
+ "font-weight": "700"
679
+ });
680
+ body.append(delta);
681
+ }
682
+ if (model.note !== void 0) {
683
+ const note = body.ownerDocument.createElement("small");
684
+ note.textContent = model.note;
685
+ note.style.color = tokens.mutedForeground;
686
+ body.append(note);
687
+ }
688
+ }
689
+ function renderText(body, model) {
690
+ const content2 = body.ownerDocument.createElement("div");
691
+ content2.textContent = model.content;
692
+ setStyles(content2, {
693
+ display: "flex",
694
+ height: "100%",
695
+ "align-items": model.style.verticalAlign === "middle" ? "center" : model.style.verticalAlign === "bottom" ? "flex-end" : "flex-start",
696
+ "text-align": model.style.textAlign,
697
+ "font-size": typeof model.style.fontSize === "number" ? `${model.style.fontSize}px` : model.style.fontSize,
698
+ "font-weight": model.style.fontWeight === void 0 ? void 0 : String(model.style.fontWeight),
699
+ color: model.style.color,
700
+ "line-height": model.style.lineHeight === void 0 ? void 0 : String(model.style.lineHeight),
701
+ "letter-spacing": model.style.letterSpacing === void 0 ? void 0 : String(model.style.letterSpacing),
702
+ "white-space": model.style.whiteSpace,
703
+ overflow: model.style.truncate ? "hidden" : void 0,
704
+ "text-overflow": model.style.truncate ? "ellipsis" : void 0
705
+ });
706
+ for (const [property, value] of Object.entries(model.style.custom)) {
707
+ content2.style.setProperty(property, String(value));
708
+ }
709
+ if (model.contentFormat === "markdown") {
710
+ content2.textContent = "";
711
+ renderMarkdown(body.ownerDocument, content2, model.content);
712
+ }
713
+ body.append(content2);
714
+ }
715
+ function renderMarkdownModel(body, model) {
716
+ body.dataset.bideuxPart = "markdown";
717
+ renderMarkdown(body.ownerDocument, body, model.content);
718
+ }
719
+ function renderImage(body, model) {
720
+ if (model.source === null) {
721
+ body.append(
722
+ createMessage(
723
+ body.ownerDocument,
724
+ "No image source",
725
+ "notice",
726
+ resolveRendererTheme()
727
+ )
728
+ );
729
+ return;
730
+ }
731
+ const image = body.ownerDocument.createElement("img");
732
+ image.src = model.source;
733
+ image.alt = model.alt;
734
+ setStyles(image, {
735
+ width: "100%",
736
+ height: "100%",
737
+ "object-fit": model.objectFit
738
+ });
739
+ body.append(image);
740
+ }
741
+ function renderFilter(input, tokens) {
742
+ const { element, widget, callbacks = {} } = input;
743
+ const result = input.result ?? {};
744
+ const component = widget.spec.component === "select" ? "select" : "filter";
745
+ const display = record3(record3(widget.spec.display)[component]);
746
+ const options = filterOptions(widget, result);
747
+ const defaultValue = display.default_value ?? record3(widget.spec.data).default_value ?? "";
748
+ const value = input.filterValue ?? defaultValue;
749
+ const frame = {
750
+ widgetId: widget.metadata.id,
751
+ component,
752
+ theme: record3(display.theme),
753
+ showObjectHeader: false,
754
+ interactive: true
755
+ };
756
+ applyWidgetTheme(element, frame, tokens);
757
+ setStyles(element, {
758
+ display: "flex",
759
+ "flex-direction": "column",
760
+ "justify-content": "center",
761
+ gap: "6px"
762
+ });
763
+ const document = element.ownerDocument;
764
+ const label = document.createElement("label");
765
+ label.textContent = stringValue2(record3(display.label).text) ?? stringValue2(display.label) ?? widget.metadata.name;
766
+ label.htmlFor = `${widget.metadata.id}-control`;
767
+ setStyles(label, {
768
+ color: tokens.mutedForeground,
769
+ "font-size": "0.78rem",
770
+ "font-weight": "700"
771
+ });
772
+ element.append(label);
773
+ const control = stringValue2(display.control) ?? "select";
774
+ if (control === "radio") {
775
+ renderRadios(document, element, input, options, value, tokens);
776
+ } else if (control === "date_range") {
777
+ renderDateRange(document, element, input, value, tokens);
778
+ } else if (control === "date" || control === "calendar") {
779
+ const date = document.createElement("input");
780
+ date.id = `${widget.metadata.id}-control`;
781
+ date.type = "date";
782
+ date.value = typeof value === "string" ? value : "";
783
+ styleControl(date, tokens);
784
+ date.addEventListener("change", () => {
785
+ callbacks.onFilterChange?.(widget.metadata.id, date.value);
786
+ callbacks.onFilterLabelChange?.(widget.metadata.id, date.value || "All");
787
+ });
788
+ element.append(date);
789
+ } else if (control === "input" || control === "custom") {
790
+ const text2 = document.createElement("input");
791
+ text2.id = `${widget.metadata.id}-control`;
792
+ text2.type = "text";
793
+ text2.value = value === null || value === void 0 ? "" : String(value);
794
+ text2.placeholder = stringValue2(display.placeholder) ?? "";
795
+ styleControl(text2, tokens);
796
+ text2.addEventListener("change", () => {
797
+ callbacks.onFilterChange?.(widget.metadata.id, text2.value);
798
+ callbacks.onFilterLabelChange?.(widget.metadata.id, text2.value || "All");
799
+ });
800
+ element.append(text2);
801
+ } else {
802
+ renderSelect(document, element, input, options, value, tokens);
803
+ }
804
+ callbacks.onFilterLabelChange?.(
805
+ widget.metadata.id,
806
+ describeFilter(value, options)
807
+ );
808
+ }
809
+ function styleControl(element, tokens) {
810
+ setStyles(element, {
811
+ width: "100%",
812
+ padding: "9px 10px",
813
+ border: `1px solid ${tokens.border}`,
814
+ "border-radius": "8px",
815
+ color: tokens.foreground,
816
+ background: tokens.surface,
817
+ "font-family": tokens.fontFamily
818
+ });
819
+ }
820
+ function renderSelect(document, element, input, options, value, tokens) {
821
+ const select = document.createElement("select");
822
+ select.id = `${input.widget.metadata.id}-control`;
823
+ select.multiple = record3(
824
+ record3(input.widget.spec.display)[input.widget.spec.component === "select" ? "select" : "filter"]
825
+ ).multi === true;
826
+ const selected = Array.isArray(value) ? value.map(String) : [value === null || value === void 0 ? "" : String(value)];
827
+ for (const option of options) {
828
+ const entry = document.createElement("option");
829
+ entry.value = String(option.value);
830
+ entry.textContent = option.label;
831
+ entry.selected = selected.includes(entry.value);
832
+ select.append(entry);
833
+ }
834
+ styleControl(select, tokens);
835
+ select.addEventListener("change", () => {
836
+ const next = select.multiple ? Array.from(select.selectedOptions).map(
837
+ (entry) => options.find((option) => String(option.value) === entry.value)?.value ?? entry.value
838
+ ) : options.find((option) => String(option.value) === select.value)?.value ?? select.value;
839
+ input.callbacks?.onFilterChange?.(input.widget.metadata.id, next);
840
+ input.callbacks?.onFilterLabelChange?.(
841
+ input.widget.metadata.id,
842
+ describeFilter(next, options)
843
+ );
844
+ });
845
+ element.append(select);
846
+ }
847
+ function renderRadios(document, element, input, options, value, tokens) {
848
+ const group = document.createElement("div");
849
+ group.setAttribute("role", "radiogroup");
850
+ group.id = `${input.widget.metadata.id}-control`;
851
+ for (const option of options) {
852
+ const label = document.createElement("label");
853
+ const radio = document.createElement("input");
854
+ radio.type = "radio";
855
+ radio.name = `${input.widget.metadata.id}-radio`;
856
+ radio.value = String(option.value);
857
+ radio.checked = String(option.value) === String(value);
858
+ radio.addEventListener("change", () => {
859
+ input.callbacks?.onFilterChange?.(input.widget.metadata.id, option.value);
860
+ input.callbacks?.onFilterLabelChange?.(
861
+ input.widget.metadata.id,
862
+ option.label
863
+ );
864
+ });
865
+ label.append(radio, document.createTextNode(option.label));
866
+ setStyles(label, {
867
+ display: "flex",
868
+ gap: "6px",
869
+ color: tokens.foreground
870
+ });
871
+ group.append(label);
872
+ }
873
+ element.append(group);
874
+ }
875
+ function renderDateRange(document, element, input, value, tokens) {
876
+ const range = record3(value);
877
+ const container = document.createElement("div");
878
+ container.id = `${input.widget.metadata.id}-control`;
879
+ setStyles(container, {
880
+ display: "grid",
881
+ "grid-template-columns": "1fr 1fr",
882
+ gap: "8px"
883
+ });
884
+ const start = document.createElement("input");
885
+ const end = document.createElement("input");
886
+ start.type = "date";
887
+ end.type = "date";
888
+ start.value = stringValue2(range.start) ?? "";
889
+ end.value = stringValue2(range.end) ?? "";
890
+ styleControl(start, tokens);
891
+ styleControl(end, tokens);
892
+ const update = () => {
893
+ const next = { start: start.value, end: end.value };
894
+ input.callbacks?.onFilterChange?.(input.widget.metadata.id, next);
895
+ input.callbacks?.onFilterLabelChange?.(
896
+ input.widget.metadata.id,
897
+ `${start.value || "Start"} to ${end.value || "End"}`
898
+ );
899
+ };
900
+ start.addEventListener("change", update);
901
+ end.addEventListener("change", update);
902
+ container.append(start, end);
903
+ element.append(container);
904
+ }
905
+ function renderReadyModel(input, model, tokens) {
906
+ const { element, widget, callbacks = {} } = input;
907
+ const body = frameContainer(element, model, tokens);
908
+ bindInteraction(element, widget, callbacks);
909
+ switch (model.kind) {
910
+ case "kpi":
911
+ renderKpi(body, model, tokens);
912
+ break;
913
+ case "text":
914
+ renderText(body, model);
915
+ break;
916
+ case "markdown":
917
+ renderMarkdownModel(body, model);
918
+ break;
919
+ case "image":
920
+ renderImage(body, model);
921
+ break;
922
+ case "extension":
923
+ return callbacks.renderExtension?.({ element: body, model });
924
+ case "unknown":
925
+ body.append(
926
+ createMessage(body.ownerDocument, model.message, "notice", tokens)
927
+ );
928
+ break;
929
+ default:
930
+ body.append(
931
+ createMessage(
932
+ body.ownerDocument,
933
+ `Use the ${model.kind} renderer package for this widget.`,
934
+ "notice",
935
+ tokens
936
+ )
937
+ );
938
+ }
939
+ }
940
+ function render(input) {
941
+ const { element, widget, callbacks = {} } = input;
942
+ const tokens = resolveRendererTheme(input.theme);
943
+ clearElement(element);
944
+ if (widget.spec.component === "filter" || widget.spec.component === "select") {
945
+ renderFilter(input, tokens);
946
+ return;
947
+ }
948
+ const resolution = buildWidgetRendererModel(
949
+ widget,
950
+ input.result ?? {},
951
+ input.context ?? {}
952
+ );
953
+ if (resolution.state.kind !== "ready") {
954
+ const state = resolution.state;
955
+ element.append(
956
+ createMessage(element.ownerDocument, state.message, state.kind, tokens)
957
+ );
958
+ if (state.kind === "error" && state.retryable && callbacks.onRetry !== void 0) {
959
+ const retry = element.ownerDocument.createElement("button");
960
+ retry.type = "button";
961
+ retry.textContent = "Try again";
962
+ retry.addEventListener("click", callbacks.onRetry);
963
+ element.append(retry);
964
+ }
965
+ return;
966
+ }
967
+ if (resolution.model !== void 0) {
968
+ return renderReadyModel(input, resolution.model, tokens);
969
+ }
970
+ }
971
+ function createNativeWidgetRenderer(input) {
972
+ let current = input;
973
+ let extension;
974
+ try {
975
+ extension = render(current);
976
+ } catch (error) {
977
+ const normalized = error instanceof Error ? error : new Error("Native rendering failed");
978
+ input.callbacks?.onError?.(normalized);
979
+ throw normalized;
980
+ }
981
+ return {
982
+ update(next) {
983
+ if (next.element !== current.element) {
984
+ throw new Error(
985
+ "Native renderers cannot move between elements. Create a new renderer."
986
+ );
987
+ }
988
+ extension?.destroy();
989
+ current = next;
990
+ extension = render(current);
991
+ },
992
+ resize() {
993
+ extension?.resize();
994
+ },
995
+ destroy() {
996
+ extension?.destroy();
997
+ clearElement(current.element);
998
+ }
999
+ };
1000
+ }
1001
+
1002
+ export { buildBasicRendererModel, buildFilterModel, buildImageRendererModel, buildKpiRendererModel, buildMarkdownRendererModel, buildTextRendererModel, buildWidgetRendererModel, createNativeWidgetRenderer, describeFilter, fillFilterTokens, filterOptions, formatFilterValue, isIsoDate, isRelativeDateExpression, mentionsFilter, renderInlineMarkdown, renderMarkdown, resolveWidgetRenderState };
1003
+ //# sourceMappingURL=chunk-RRWZKNDZ.js.map
1004
+ //# sourceMappingURL=chunk-RRWZKNDZ.js.map