@richardmcquiston01/dashboard-widgets-toolkit 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.
package/dist/react.cjs ADDED
@@ -0,0 +1,1273 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/react/card.tsx
7
+
8
+ // src/core/format.ts
9
+ var DEFAULT_CURRENCY = "USD";
10
+ function localeArgument(locale) {
11
+ return locale === void 0 || typeof locale === "string" ? locale : [...locale];
12
+ }
13
+ function numberFormat(locale, options) {
14
+ try {
15
+ return new Intl.NumberFormat(localeArgument(locale), options);
16
+ } catch {
17
+ const { style: _style, currency: _currency, ...rest } = options;
18
+ return new Intl.NumberFormat(void 0, rest);
19
+ }
20
+ }
21
+ function formatValue(value, format, options = {}) {
22
+ if (!Number.isFinite(value)) {
23
+ return "\u2014";
24
+ }
25
+ const compact = options.compact ? { notation: "compact", maximumFractionDigits: 1 } : {};
26
+ switch (format) {
27
+ case "number":
28
+ return numberFormat(options.locale, {
29
+ maximumFractionDigits: 2,
30
+ ...compact
31
+ }).format(value);
32
+ case "currency": {
33
+ const currency = (options.currency ?? DEFAULT_CURRENCY).toUpperCase();
34
+ const formatter = numberFormat(options.locale, {
35
+ style: "currency",
36
+ currency,
37
+ ...compact
38
+ });
39
+ const text = formatter.format(value);
40
+ return formatter.resolvedOptions().style === "currency" ? text : `${text} ${currency}`;
41
+ }
42
+ case "percent":
43
+ return numberFormat(options.locale, {
44
+ style: "percent",
45
+ maximumFractionDigits: 1,
46
+ ...compact
47
+ }).format(value);
48
+ }
49
+ }
50
+ function shareOf(part, total) {
51
+ return total <= 0 ? 0 : Math.round(part / total * 100);
52
+ }
53
+ function percentChange(current, previous) {
54
+ if (previous === null || previous === void 0 || previous === 0 || !Number.isFinite(previous) || !Number.isFinite(current)) {
55
+ return null;
56
+ }
57
+ return (current - previous) / Math.abs(previous) * 100;
58
+ }
59
+ function formatPercentChange(change, locale) {
60
+ const digits = Math.abs(change) < 10 ? 1 : 0;
61
+ const rounded = Number(change.toFixed(digits));
62
+ if (rounded === 0) {
63
+ return "\xB10%";
64
+ }
65
+ return numberFormat(locale, {
66
+ style: "percent",
67
+ maximumFractionDigits: digits,
68
+ signDisplay: "exceptZero"
69
+ }).format(rounded / 100);
70
+ }
71
+ var DEFAULT_COMPARISON_LABEL = "previous period";
72
+ function kpiDelta(current, previous, options = {}) {
73
+ const percent = percentChange(current, previous);
74
+ if (percent === null) {
75
+ return null;
76
+ }
77
+ const text = formatPercentChange(percent, options.locale);
78
+ const direction = text === "\xB10%" ? "flat" : percent > 0 ? "up" : "down";
79
+ const higherIsBetter = options.higherIsBetter ?? true;
80
+ const sentiment = direction === "flat" ? "neutral" : direction === "up" === higherIsBetter ? "good" : "bad";
81
+ return {
82
+ percent,
83
+ direction,
84
+ sentiment,
85
+ text,
86
+ description: `${text} vs ${options.comparisonLabel ?? DEFAULT_COMPARISON_LABEL}`
87
+ };
88
+ }
89
+
90
+ // src/core/scale.ts
91
+ function niceStep(value) {
92
+ if (!(value > 0) || !Number.isFinite(value)) return 1;
93
+ const exponent = Math.floor(Math.log10(value));
94
+ const fraction = value / 10 ** exponent;
95
+ const nice = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;
96
+ return nice * 10 ** exponent;
97
+ }
98
+ function niceDomain(values, tickCount = 4) {
99
+ const finite = values.filter((v) => Number.isFinite(v));
100
+ let low = Math.min(0, ...finite);
101
+ let high = Math.max(0, ...finite);
102
+ if (low === high) {
103
+ high = 1;
104
+ low = 0;
105
+ }
106
+ const step = niceStep((high - low) / Math.max(1, tickCount));
107
+ const min = Math.floor(low / step) * step;
108
+ const max = Math.ceil(high / step) * step;
109
+ const ticks = [];
110
+ const count = Math.round((max - min) / step);
111
+ for (let index = 0; index <= count; index++) {
112
+ ticks.push(Number((min + index * step).toPrecision(12)));
113
+ }
114
+ return { min, max, ticks };
115
+ }
116
+ function labelStride(count, maxLabels = 6) {
117
+ return Math.max(1, Math.ceil(count / Math.max(1, maxLabels)));
118
+ }
119
+
120
+ // src/react/palette.ts
121
+ var DEFAULT_SERIES_COLORS = [
122
+ "#2a78d6",
123
+ "#eb6834",
124
+ "#1baf7a",
125
+ "#eda100",
126
+ "#e87ba4",
127
+ "#008300",
128
+ "#4a3aa7",
129
+ "#e34948"
130
+ ];
131
+ function seriesColor(index) {
132
+ const slot = Math.min(index, DEFAULT_SERIES_COLORS.length - 1);
133
+ return `var(--dwt-series-${slot + 1}, ${DEFAULT_SERIES_COLORS[slot] ?? "#2a78d6"})`;
134
+ }
135
+ var CHART_COLORS = {
136
+ surface: "var(--dwt-surface, #fcfcfb)",
137
+ grid: "var(--dwt-grid, #e1e0d9)",
138
+ baseline: "var(--dwt-baseline, #c3c2b7)",
139
+ textMuted: "var(--dwt-text-muted, #6b6a66)",
140
+ textPrimary: "var(--dwt-text, #0b0b0b)",
141
+ crosshair: "var(--dwt-crosshair, #898781)",
142
+ meterTrack: "var(--dwt-meter-track, #cde2fb)",
143
+ barTrack: "var(--dwt-bar-track, #f0efec)"
144
+ };
145
+ var DEFAULT_LABELS = {
146
+ loading: "Loading\u2026",
147
+ retry: "Retry",
148
+ viewAsTable: "View as table",
149
+ hide: (title) => `Hide ${title}`,
150
+ show: (title) => `Show ${title}`,
151
+ minimize: (title) => `Minimise ${title}`,
152
+ expand: (title) => `Expand ${title}`,
153
+ moveEarlier: (title) => `Move ${title} earlier`,
154
+ moveLater: (title) => `Move ${title} later`,
155
+ hiddenWidgets: "Hidden:",
156
+ allHidden: "Every widget is hidden. Show one from the list above.",
157
+ noWidgets: "No widgets to show.",
158
+ andMore: (count) => `and ${count} more`,
159
+ comparison: "previous period"
160
+ };
161
+ var SettingsContext = react.createContext({
162
+ locale: void 0,
163
+ linkTarget: void 0,
164
+ classNames: {},
165
+ labels: DEFAULT_LABELS
166
+ });
167
+ function WidgetSettingsProvider({
168
+ children,
169
+ ...settings
170
+ }) {
171
+ return /* @__PURE__ */ jsxRuntime.jsx(
172
+ SettingsContext.Provider,
173
+ {
174
+ value: {
175
+ locale: settings.locale,
176
+ linkTarget: settings.linkTarget,
177
+ classNames: settings.classNames ?? {},
178
+ labels: { ...DEFAULT_LABELS, ...settings.labels }
179
+ },
180
+ children
181
+ }
182
+ );
183
+ }
184
+ function useWidgetSettings() {
185
+ return react.useContext(SettingsContext);
186
+ }
187
+ function joinClassNames(...names) {
188
+ return names.filter((name) => typeof name === "string" && name !== "").join(" ");
189
+ }
190
+ function useSlotClassName() {
191
+ const { classNames } = useWidgetSettings();
192
+ return (slot, base, ...extra) => joinClassNames(base, ...extra, classNames[slot]);
193
+ }
194
+ var WIDTH = 640;
195
+ var HEIGHT = 220;
196
+ var MARGIN = { top: 16, right: 20, bottom: 28, left: 56 };
197
+ var INNER_WIDTH = WIDTH - MARGIN.left - MARGIN.right;
198
+ var INNER_HEIGHT = HEIGHT - MARGIN.top - MARGIN.bottom;
199
+ var MAX_BAR_WIDTH = 24;
200
+ var BAR_GAP = 2;
201
+ var BAR_RADIUS = 4;
202
+ var MAX_MARKED_POINTS = 24;
203
+ function barPath(x, y, width, height, roundedEnd) {
204
+ const r = Math.max(0, Math.min(BAR_RADIUS, width / 2, height));
205
+ const right = x + width;
206
+ const bottom = y + height;
207
+ const f = (n) => n.toFixed(2);
208
+ if (roundedEnd === "top") {
209
+ return `M${f(x)},${f(bottom)}V${f(y + r)}Q${f(x)},${f(y)} ${f(x + r)},${f(y)}H${f(right - r)}Q${f(right)},${f(y)} ${f(right)},${f(y + r)}V${f(bottom)}Z`;
210
+ }
211
+ return `M${f(x)},${f(y)}V${f(bottom - r)}Q${f(x)},${f(bottom)} ${f(x + r)},${f(bottom)}H${f(right - r)}Q${f(right)},${f(bottom)} ${f(right)},${f(bottom - r)}V${f(y)}Z`;
212
+ }
213
+ function buildChartModel(data) {
214
+ const count = Math.max(0, ...data.series.map((s) => s.points.length));
215
+ const labels = [];
216
+ for (let index = 0; index < count; index++) {
217
+ const label = data.series.find(
218
+ (s) => s.points[index] !== void 0
219
+ )?.points[index]?.label;
220
+ labels.push(label ?? "");
221
+ }
222
+ const values = data.series.map(
223
+ (s) => labels.map((_, index) => s.points[index]?.value ?? null)
224
+ );
225
+ const all = values.flat().filter((v) => v !== null);
226
+ return { labels, values, domain: niceDomain(all) };
227
+ }
228
+ function yFor(domain, value) {
229
+ const range = domain.max - domain.min || 1;
230
+ return MARGIN.top + INNER_HEIGHT * ((domain.max - value) / range);
231
+ }
232
+ function xFor(chartType, count, index) {
233
+ if (chartType === "bar") {
234
+ const slot = INNER_WIDTH / Math.max(1, count);
235
+ return MARGIN.left + slot * index + slot / 2;
236
+ }
237
+ return count <= 1 ? MARGIN.left + INNER_WIDTH / 2 : MARGIN.left + index / (count - 1) * INNER_WIDTH;
238
+ }
239
+ function hitBand(chartType, count, index) {
240
+ if (chartType === "bar" || count <= 1) {
241
+ const slot = INNER_WIDTH / Math.max(1, count);
242
+ return { x: MARGIN.left + slot * index, width: slot };
243
+ }
244
+ const step = INNER_WIDTH / (count - 1);
245
+ const left = Math.max(
246
+ MARGIN.left,
247
+ xFor("line", count, index) - step / 2
248
+ );
249
+ const right = Math.min(
250
+ MARGIN.left + INNER_WIDTH,
251
+ xFor("line", count, index) + step / 2
252
+ );
253
+ return { x: left, width: right - left };
254
+ }
255
+ function linePath(values, domain, count) {
256
+ let path = "";
257
+ let penDown = false;
258
+ values.forEach((value, index) => {
259
+ if (value === null) {
260
+ penDown = false;
261
+ return;
262
+ }
263
+ path += `${penDown ? "L" : "M"}${xFor("line", count, index).toFixed(2)},${yFor(domain, value).toFixed(2)}`;
264
+ penDown = true;
265
+ });
266
+ return path;
267
+ }
268
+ function areaPath(values, domain, count) {
269
+ if (values.some((value) => value === null) || values.length < 2) {
270
+ return null;
271
+ }
272
+ const baseline = yFor(
273
+ domain,
274
+ Math.max(domain.min, Math.min(0, domain.max))
275
+ );
276
+ const first = xFor("line", count, 0).toFixed(2);
277
+ const last = xFor("line", count, count - 1).toFixed(2);
278
+ return `M${first},${baseline.toFixed(2)}${linePath(values, domain, count).replace(/^M/, "L")}L${last},${baseline.toFixed(2)}Z`;
279
+ }
280
+ function describeChart(data, model, format) {
281
+ const first = model.labels[0] ?? "";
282
+ const last = model.labels[model.labels.length - 1] ?? "";
283
+ const span = model.labels.length <= 1 ? first : `from ${first} to ${last}`;
284
+ const parts = data.series.map((series, s) => {
285
+ const present = (model.values[s] ?? []).filter(
286
+ (v) => v !== null
287
+ );
288
+ if (present.length === 0) return `${series.name}: no values.`;
289
+ const latest = present[present.length - 1] ?? 0;
290
+ return `${series.name}: latest ${format(latest)}, high ${format(Math.max(...present))}, low ${format(Math.min(...present))}.`;
291
+ });
292
+ return `${data.chartType === "bar" ? "Bar" : "Line"} chart, ${model.labels.length} point(s) ${span}. ${parts.join(" ")}`;
293
+ }
294
+ function GraphWidget({
295
+ data,
296
+ title
297
+ }) {
298
+ const slot = useSlotClassName();
299
+ const { locale, labels: text } = useWidgetSettings();
300
+ const id = react.useId();
301
+ const [active, setActive] = react.useState(null);
302
+ const model = buildChartModel(data);
303
+ const count = model.labels.length;
304
+ const currency = data.currency === void 0 ? {} : { currency: data.currency };
305
+ const format = (value) => formatValue(value, data.valueFormat, { locale, ...currency });
306
+ const formatTick = (value) => formatValue(value, data.valueFormat, {
307
+ locale,
308
+ compact: true,
309
+ ...currency
310
+ });
311
+ const chartTitle = title ?? data.series.map((s) => s.name).join(", ");
312
+ const description = describeChart(data, model, format);
313
+ const stride = labelStride(count);
314
+ const zeroY = yFor(
315
+ model.domain,
316
+ Math.max(model.domain.min, Math.min(0, model.domain.max))
317
+ );
318
+ const seriesCount = data.series.length;
319
+ const isBar = data.chartType === "bar";
320
+ const slotWidth = INNER_WIDTH / Math.max(1, count);
321
+ const barWidth = Math.max(
322
+ 1,
323
+ Math.min(
324
+ MAX_BAR_WIDTH,
325
+ (slotWidth * 0.7 - BAR_GAP * (seriesCount - 1)) / Math.max(1, seriesCount)
326
+ )
327
+ );
328
+ const groupWidth = barWidth * seriesCount + BAR_GAP * (seriesCount - 1);
329
+ function handleKeyDown(event) {
330
+ if (count === 0) return;
331
+ let next;
332
+ switch (event.key) {
333
+ case "ArrowRight":
334
+ next = Math.min(count - 1, (active ?? -1) + 1);
335
+ break;
336
+ case "ArrowLeft":
337
+ next = Math.max(0, (active ?? count) - 1);
338
+ break;
339
+ case "Home":
340
+ next = 0;
341
+ break;
342
+ case "End":
343
+ next = count - 1;
344
+ break;
345
+ case "Escape":
346
+ next = null;
347
+ break;
348
+ default:
349
+ return;
350
+ }
351
+ event.preventDefault();
352
+ setActive(next);
353
+ }
354
+ const activeLabel = active === null ? null : model.labels[active] ?? null;
355
+ return /* @__PURE__ */ jsxRuntime.jsxs(
356
+ "figure",
357
+ {
358
+ className: slot("chart", "dwt-chart", `dwt-chart--${data.chartType}`),
359
+ children: [
360
+ seriesCount >= 2 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: slot("legend", "dwt-legend"), children: data.series.map((series, s) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "dwt-legend-item", children: [
361
+ /* @__PURE__ */ jsxRuntime.jsx(
362
+ "svg",
363
+ {
364
+ width: "16",
365
+ height: "10",
366
+ "aria-hidden": "true",
367
+ className: "dwt-legend-key",
368
+ children: isBar ? /* @__PURE__ */ jsxRuntime.jsx(
369
+ "rect",
370
+ {
371
+ x: "3",
372
+ y: "0",
373
+ width: "10",
374
+ height: "10",
375
+ rx: "2",
376
+ fill: seriesColor(s)
377
+ }
378
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
379
+ "line",
380
+ {
381
+ x1: "1",
382
+ x2: "15",
383
+ y1: "5",
384
+ y2: "5",
385
+ stroke: seriesColor(s),
386
+ strokeWidth: "2",
387
+ strokeLinecap: "round"
388
+ }
389
+ )
390
+ }
391
+ ),
392
+ series.name
393
+ ] }, s)) }),
394
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-chart-frame", children: [
395
+ /* @__PURE__ */ jsxRuntime.jsxs(
396
+ "svg",
397
+ {
398
+ viewBox: `0 0 ${WIDTH} ${HEIGHT}`,
399
+ className: "dwt-chart-svg",
400
+ role: "img",
401
+ "aria-labelledby": `${id}-title ${id}-desc`,
402
+ tabIndex: 0,
403
+ onKeyDown: handleKeyDown,
404
+ onBlur: () => setActive(null),
405
+ onPointerLeave: () => setActive(null),
406
+ width: "100%",
407
+ children: [
408
+ /* @__PURE__ */ jsxRuntime.jsx("title", { id: `${id}-title`, children: chartTitle }),
409
+ /* @__PURE__ */ jsxRuntime.jsx("desc", { id: `${id}-desc`, children: description }),
410
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "dwt-chart-grid", "aria-hidden": "true", children: [
411
+ model.domain.ticks.map((tick) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
412
+ /* @__PURE__ */ jsxRuntime.jsx(
413
+ "line",
414
+ {
415
+ x1: MARGIN.left,
416
+ x2: WIDTH - MARGIN.right,
417
+ y1: yFor(model.domain, tick),
418
+ y2: yFor(model.domain, tick),
419
+ stroke: CHART_COLORS.grid,
420
+ strokeWidth: 1
421
+ }
422
+ ),
423
+ /* @__PURE__ */ jsxRuntime.jsx(
424
+ "text",
425
+ {
426
+ x: MARGIN.left - 8,
427
+ y: yFor(model.domain, tick),
428
+ textAnchor: "end",
429
+ dominantBaseline: "middle",
430
+ className: "dwt-axis-label dwt-axis-label--y",
431
+ fill: CHART_COLORS.textMuted,
432
+ fontSize: 11,
433
+ children: formatTick(tick)
434
+ }
435
+ )
436
+ ] }, tick)),
437
+ /* @__PURE__ */ jsxRuntime.jsx(
438
+ "line",
439
+ {
440
+ x1: MARGIN.left,
441
+ x2: WIDTH - MARGIN.right,
442
+ y1: zeroY,
443
+ y2: zeroY,
444
+ stroke: CHART_COLORS.baseline,
445
+ strokeWidth: 1
446
+ }
447
+ ),
448
+ model.labels.map(
449
+ (label, index) => index % stride === 0 || index === count - 1 ? /* @__PURE__ */ jsxRuntime.jsx(
450
+ "text",
451
+ {
452
+ x: xFor(data.chartType, count, index),
453
+ y: HEIGHT - 8,
454
+ textAnchor: !isBar && count > 1 && index === 0 ? "start" : !isBar && count > 1 && index === count - 1 ? "end" : "middle",
455
+ className: "dwt-axis-label dwt-axis-label--x",
456
+ fill: CHART_COLORS.textMuted,
457
+ fontSize: 11,
458
+ children: label
459
+ },
460
+ index
461
+ ) : null
462
+ )
463
+ ] }),
464
+ isBar ? /* @__PURE__ */ jsxRuntime.jsx("g", { className: "dwt-chart-marks", "aria-hidden": "true", children: model.labels.map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(
465
+ "g",
466
+ {
467
+ className: active !== null && active !== index ? "dwt-mark-group dwt-mark-group--dimmed" : "dwt-mark-group",
468
+ opacity: active !== null && active !== index ? 0.55 : 1,
469
+ children: model.values.map((values, s) => {
470
+ const value = values[index] ?? null;
471
+ if (value === null || value === 0) return null;
472
+ const x = xFor("bar", count, index) - groupWidth / 2 + s * (barWidth + BAR_GAP);
473
+ const valueY = yFor(model.domain, value);
474
+ const top = Math.min(valueY, zeroY);
475
+ const height = Math.abs(zeroY - valueY);
476
+ return /* @__PURE__ */ jsxRuntime.jsx(
477
+ "path",
478
+ {
479
+ className: "dwt-bar",
480
+ d: barPath(
481
+ x,
482
+ top,
483
+ barWidth,
484
+ height,
485
+ value >= 0 ? "top" : "bottom"
486
+ ),
487
+ fill: seriesColor(s)
488
+ },
489
+ s
490
+ );
491
+ })
492
+ },
493
+ index
494
+ )) }) : /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "dwt-chart-marks", "aria-hidden": "true", children: [
495
+ seriesCount === 1 && (() => {
496
+ const area = areaPath(
497
+ model.values[0] ?? [],
498
+ model.domain,
499
+ count
500
+ );
501
+ return area === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
502
+ "path",
503
+ {
504
+ d: area,
505
+ fill: seriesColor(0),
506
+ fillOpacity: 0.1,
507
+ stroke: "none"
508
+ }
509
+ );
510
+ })(),
511
+ model.values.map((values, s) => /* @__PURE__ */ jsxRuntime.jsx(
512
+ "path",
513
+ {
514
+ className: "dwt-line",
515
+ d: linePath(values, model.domain, count),
516
+ fill: "none",
517
+ stroke: seriesColor(s),
518
+ strokeWidth: 2,
519
+ strokeLinejoin: "round",
520
+ strokeLinecap: "round"
521
+ },
522
+ s
523
+ )),
524
+ model.values.map(
525
+ (values, s) => values.map((value, index) => {
526
+ const isLast = index === count - 1;
527
+ if (value === null) return null;
528
+ if (count > MAX_MARKED_POINTS && !isLast && index !== active)
529
+ return null;
530
+ return /* @__PURE__ */ jsxRuntime.jsx(
531
+ "circle",
532
+ {
533
+ className: "dwt-marker",
534
+ cx: xFor("line", count, index),
535
+ cy: yFor(model.domain, value),
536
+ r: index === active ? 5 : 4,
537
+ fill: seriesColor(s),
538
+ stroke: CHART_COLORS.surface,
539
+ strokeWidth: 2
540
+ },
541
+ `${s}-${index}`
542
+ );
543
+ })
544
+ ),
545
+ seriesCount === 1 && (() => {
546
+ const last = model.values[0]?.[count - 1] ?? null;
547
+ if (last === null) return null;
548
+ return /* @__PURE__ */ jsxRuntime.jsx(
549
+ "text",
550
+ {
551
+ className: "dwt-end-label",
552
+ x: xFor("line", count, count - 1),
553
+ y: yFor(model.domain, last) - 10,
554
+ textAnchor: "end",
555
+ fill: CHART_COLORS.textPrimary,
556
+ fontSize: 12,
557
+ fontWeight: 600,
558
+ children: format(last)
559
+ }
560
+ );
561
+ })(),
562
+ active !== null && /* @__PURE__ */ jsxRuntime.jsx(
563
+ "line",
564
+ {
565
+ className: "dwt-crosshair",
566
+ x1: xFor("line", count, active),
567
+ x2: xFor("line", count, active),
568
+ y1: MARGIN.top,
569
+ y2: MARGIN.top + INNER_HEIGHT,
570
+ stroke: CHART_COLORS.crosshair,
571
+ strokeWidth: 1
572
+ }
573
+ )
574
+ ] }),
575
+ /* @__PURE__ */ jsxRuntime.jsx("g", { className: "dwt-hit-layer", children: model.labels.map((_, index) => {
576
+ const band = hitBand(data.chartType, count, index);
577
+ return /* @__PURE__ */ jsxRuntime.jsx(
578
+ "rect",
579
+ {
580
+ x: band.x,
581
+ y: MARGIN.top,
582
+ width: Math.max(0, band.width),
583
+ height: INNER_HEIGHT,
584
+ fill: "transparent",
585
+ onPointerEnter: () => setActive(index),
586
+ onPointerMove: () => setActive(index)
587
+ },
588
+ index
589
+ );
590
+ }) })
591
+ ]
592
+ }
593
+ ),
594
+ active !== null && activeLabel !== null && /* @__PURE__ */ jsxRuntime.jsxs(
595
+ "div",
596
+ {
597
+ className: slot("tooltip", "dwt-tooltip"),
598
+ style: {
599
+ left: `${xFor(data.chartType, count, active) / WIDTH * 100}%`
600
+ },
601
+ children: [
602
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "dwt-tooltip-label", children: activeLabel }),
603
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "dwt-tooltip-rows", children: data.series.map((series, s) => {
604
+ const value = model.values[s]?.[active] ?? null;
605
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "dwt-tooltip-row", children: [
606
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(
607
+ "line",
608
+ {
609
+ x1: "0",
610
+ x2: "12",
611
+ y1: "2",
612
+ y2: "2",
613
+ stroke: seriesColor(s),
614
+ strokeWidth: "3",
615
+ strokeLinecap: "round"
616
+ }
617
+ ) }),
618
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "dwt-tooltip-value", children: value === null ? "\u2014" : format(value) }),
619
+ seriesCount >= 2 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dwt-tooltip-series", children: series.name })
620
+ ] }, s);
621
+ }) })
622
+ ]
623
+ }
624
+ )
625
+ ] }),
626
+ /* @__PURE__ */ jsxRuntime.jsxs("details", { className: slot("tableTwin", "dwt-table-twin"), children: [
627
+ /* @__PURE__ */ jsxRuntime.jsx("summary", { children: text.viewAsTable }),
628
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { className: slot("table", "dwt-table"), children: [
629
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
630
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: data.xLabel ?? "Label" }),
631
+ data.series.map((series, s) => /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "dwt-numeric", children: series.name }, s))
632
+ ] }) }),
633
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: model.labels.map((label, index) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
634
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "row", children: label }),
635
+ model.values.map((values, s) => {
636
+ const value = values[index] ?? null;
637
+ return /* @__PURE__ */ jsxRuntime.jsx("td", { className: "dwt-numeric", children: value === null ? "\u2014" : format(value) }, s);
638
+ })
639
+ ] }, index)) })
640
+ ] })
641
+ ] })
642
+ ]
643
+ }
644
+ );
645
+ }
646
+
647
+ // src/core/url.ts
648
+ var SCHEME_PATTERN = /^([a-z][a-z0-9+.-]*):/i;
649
+ var IGNORED_IN_SCHEME = /[\u0000- \u007f]/g;
650
+ function schemeOf(url) {
651
+ const match = SCHEME_PATTERN.exec(
652
+ url.replace(IGNORED_IN_SCHEME, "")
653
+ );
654
+ return match === null ? null : (match[1] ?? "").toLowerCase();
655
+ }
656
+ function isSafeHref(url) {
657
+ if (url.trim() === "") {
658
+ return false;
659
+ }
660
+ const scheme = schemeOf(url);
661
+ return scheme === null || scheme === "http" || scheme === "https" || scheme === "mailto";
662
+ }
663
+ function isSafeImageUrl(url) {
664
+ if (url.trim() === "") {
665
+ return false;
666
+ }
667
+ const scheme = schemeOf(url);
668
+ return scheme === null || scheme === "http" || scheme === "https";
669
+ }
670
+ function WidgetLink({
671
+ href,
672
+ className,
673
+ children
674
+ }) {
675
+ const { linkTarget } = useWidgetSettings();
676
+ if (href === void 0 || !isSafeHref(href)) {
677
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children });
678
+ }
679
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: linkTarget, rel: "noreferrer", className, children });
680
+ }
681
+ function Thumbnail({
682
+ url
683
+ }) {
684
+ if (url === void 0 || !isSafeImageUrl(url)) {
685
+ return /* @__PURE__ */ jsxRuntime.jsx(
686
+ "span",
687
+ {
688
+ className: "dwt-thumbnail dwt-thumbnail--placeholder",
689
+ "aria-hidden": "true"
690
+ }
691
+ );
692
+ }
693
+ return /* @__PURE__ */ jsxRuntime.jsx(
694
+ "img",
695
+ {
696
+ className: "dwt-thumbnail",
697
+ src: url,
698
+ alt: "",
699
+ width: 36,
700
+ height: 36,
701
+ loading: "lazy",
702
+ decoding: "async"
703
+ }
704
+ );
705
+ }
706
+ function TextWidget({
707
+ data
708
+ }) {
709
+ const slot = useSlotClassName();
710
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: slot("text", "dwt-text"), children: [
711
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "dwt-figure", children: data.value }),
712
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "dwt-figure-label", children: data.label })
713
+ ] });
714
+ }
715
+ var DELTA_ARROWS = {
716
+ up: "\u25B2",
717
+ down: "\u25BC",
718
+ flat: "\u25A0"
719
+ };
720
+ function KpiWidget({
721
+ data
722
+ }) {
723
+ const slot = useSlotClassName();
724
+ const { locale, labels } = useWidgetSettings();
725
+ const value = formatValue(data.value, data.format, {
726
+ locale,
727
+ ...data.currency === void 0 ? {} : { currency: data.currency }
728
+ });
729
+ const delta = kpiDelta(data.value, data.previous, {
730
+ locale,
731
+ comparisonLabel: labels.comparison,
732
+ ...data.higherIsBetter === void 0 ? {} : { higherIsBetter: data.higherIsBetter }
733
+ });
734
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: slot("kpi", "dwt-kpi"), children: [
735
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "dwt-figure-label", children: data.label }),
736
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "dwt-figure", children: value }),
737
+ (delta !== null || data.hint !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "dwt-kpi-meta", children: [
738
+ delta !== null && /* @__PURE__ */ jsxRuntime.jsxs(
739
+ "span",
740
+ {
741
+ className: `dwt-delta dwt-delta--${delta.sentiment} dwt-delta--${delta.direction}`,
742
+ children: [
743
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "dwt-delta-arrow", children: DELTA_ARROWS[delta.direction] }),
744
+ " ",
745
+ delta.text,
746
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "dwt-delta-comparison", children: [
747
+ " ",
748
+ "vs ",
749
+ labels.comparison
750
+ ] })
751
+ ]
752
+ }
753
+ ),
754
+ data.hint !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dwt-hint", children: data.hint })
755
+ ] })
756
+ ] });
757
+ }
758
+ function GaugeWidget({
759
+ data
760
+ }) {
761
+ const slot = useSlotClassName();
762
+ const { locale } = useWidgetSettings();
763
+ const max = data.max > 0 ? data.max : 1;
764
+ const percent = Math.max(0, Math.min(100, data.value / max * 100));
765
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: slot("gauge", "dwt-gauge"), children: [
766
+ /* @__PURE__ */ jsxRuntime.jsx(
767
+ "div",
768
+ {
769
+ className: "dwt-meter",
770
+ role: "meter",
771
+ "aria-valuemin": 0,
772
+ "aria-valuemax": max,
773
+ "aria-valuenow": data.value,
774
+ "aria-label": data.label,
775
+ style: { background: CHART_COLORS.meterTrack },
776
+ children: /* @__PURE__ */ jsxRuntime.jsx(
777
+ "div",
778
+ {
779
+ className: "dwt-meter-fill",
780
+ style: { width: `${percent}%`, background: seriesColor(0) }
781
+ }
782
+ )
783
+ }
784
+ ),
785
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "dwt-gauge-caption", children: [
786
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "dwt-gauge-value", children: [
787
+ formatValue(data.value, "number", { locale }),
788
+ " /",
789
+ " ",
790
+ formatValue(max, "number", { locale })
791
+ ] }),
792
+ " ",
793
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dwt-figure-label", children: data.label })
794
+ ] })
795
+ ] });
796
+ }
797
+ function TableWidget({
798
+ data
799
+ }) {
800
+ const slot = useSlotClassName();
801
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-table-wrap", children: [
802
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { className: slot("table", "dwt-table"), children: [
803
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: data.columns.map((column, index) => /* @__PURE__ */ jsxRuntime.jsx(
804
+ "th",
805
+ {
806
+ scope: "col",
807
+ className: column.numeric === true ? "dwt-numeric" : void 0,
808
+ children: column.label
809
+ },
810
+ index
811
+ )) }) }),
812
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: data.rows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: row.map((cell, cellIndex) => /* @__PURE__ */ jsxRuntime.jsx(
813
+ "td",
814
+ {
815
+ className: data.columns[cellIndex]?.numeric === true ? "dwt-numeric" : void 0,
816
+ children: cell.href === void 0 ? cell.text : /* @__PURE__ */ jsxRuntime.jsx(WidgetLink, { href: cell.href, className: "dwt-link", children: cell.text })
817
+ },
818
+ cellIndex
819
+ )) }, rowIndex)) })
820
+ ] }),
821
+ data.footer !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "dwt-note", children: data.footer })
822
+ ] });
823
+ }
824
+ function BarListWidget({
825
+ data
826
+ }) {
827
+ const slot = useSlotClassName();
828
+ const { locale } = useWidgetSettings();
829
+ const denominator = data.total !== void 0 && data.total > 0 ? data.total : Math.max(0, ...data.items.map((item) => item.value));
830
+ return /* @__PURE__ */ jsxRuntime.jsx("ul", { className: slot("barList", "dwt-bar-list"), children: data.items.map((item, index) => {
831
+ const width = denominator > 0 ? Math.max(0, Math.min(100, item.value / denominator * 100)) : 0;
832
+ const display = item.display ?? (data.total !== void 0 ? `${formatValue(item.value, "number", { locale })} (${shareOf(item.value, data.total)}%)` : formatValue(item.value, "number", { locale }));
833
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "dwt-bar-row", children: [
834
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dwt-bar-label", children: item.label }),
835
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dwt-bar-value", children: display }),
836
+ /* @__PURE__ */ jsxRuntime.jsx(
837
+ "span",
838
+ {
839
+ className: "dwt-bar-track",
840
+ "aria-hidden": "true",
841
+ style: { background: CHART_COLORS.barTrack },
842
+ children: /* @__PURE__ */ jsxRuntime.jsx(
843
+ "span",
844
+ {
845
+ className: "dwt-bar-fill",
846
+ style: { width: `${width}%`, background: seriesColor(0) }
847
+ }
848
+ )
849
+ }
850
+ )
851
+ ] }, `${index}-${item.label}`);
852
+ }) });
853
+ }
854
+ function AlertListWidget({
855
+ data
856
+ }) {
857
+ const slot = useSlotClassName();
858
+ const { labels } = useWidgetSettings();
859
+ if (data.items.length === 0) {
860
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: slot("empty", "dwt-empty"), children: data.emptyText });
861
+ }
862
+ const more = data.total - data.items.length;
863
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
864
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: slot("alertList", "dwt-alert-list"), children: data.items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "dwt-alert-row", children: [
865
+ /* @__PURE__ */ jsxRuntime.jsx(Thumbnail, { url: item.thumbnailUrl }),
866
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "dwt-alert-main", children: [
867
+ /* @__PURE__ */ jsxRuntime.jsx(WidgetLink, { href: item.href, className: "dwt-alert-title", children: item.title }),
868
+ item.detail !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dwt-alert-detail", children: item.detail })
869
+ ] }),
870
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dwt-alert-value", children: item.valueLabel })
871
+ ] }, `${index}-${item.title}`)) }),
872
+ more > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "dwt-note", children: labels.andMore(more) })
873
+ ] });
874
+ }
875
+ function WidgetContent({
876
+ data
877
+ }) {
878
+ switch (data.kind) {
879
+ case "TEXT":
880
+ return /* @__PURE__ */ jsxRuntime.jsx(TextWidget, { data });
881
+ case "KPI":
882
+ return /* @__PURE__ */ jsxRuntime.jsx(KpiWidget, { data });
883
+ case "GAUGE":
884
+ return /* @__PURE__ */ jsxRuntime.jsx(GaugeWidget, { data });
885
+ case "TABLE":
886
+ return /* @__PURE__ */ jsxRuntime.jsx(TableWidget, { data });
887
+ case "BAR_LIST":
888
+ return /* @__PURE__ */ jsxRuntime.jsx(BarListWidget, { data });
889
+ case "ALERT_LIST":
890
+ return /* @__PURE__ */ jsxRuntime.jsx(AlertListWidget, { data });
891
+ case "GRAPH":
892
+ return /* @__PURE__ */ jsxRuntime.jsx(GraphWidget, { data });
893
+ }
894
+ }
895
+ function WidgetCard({
896
+ title,
897
+ description,
898
+ actions,
899
+ minimized = false,
900
+ onToggleMinimized,
901
+ status = "ok",
902
+ emptyText,
903
+ error,
904
+ onRetry,
905
+ footer,
906
+ size,
907
+ headingLevel = 2,
908
+ className,
909
+ children
910
+ }) {
911
+ const slot = useSlotClassName();
912
+ const { labels } = useWidgetSettings();
913
+ const id = react.useId();
914
+ const Heading = `h${headingLevel}`;
915
+ const bodyId = `${id}-body`;
916
+ let body;
917
+ switch (status) {
918
+ case "loading":
919
+ body = /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", className: slot("status", "dwt-status"), children: labels.loading });
920
+ break;
921
+ case "empty":
922
+ body = /* @__PURE__ */ jsxRuntime.jsx("p", { className: slot("empty", "dwt-empty"), children: emptyText ?? "" });
923
+ break;
924
+ case "error":
925
+ body = /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: slot("error", "dwt-error"), children: [
926
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "dwt-error-message", children: error ?? "" }),
927
+ onRetry !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
928
+ "button",
929
+ {
930
+ type: "button",
931
+ className: slot("button", "dwt-button"),
932
+ onClick: onRetry,
933
+ children: labels.retry
934
+ }
935
+ )
936
+ ] });
937
+ break;
938
+ case "ok":
939
+ body = children;
940
+ break;
941
+ }
942
+ return /* @__PURE__ */ jsxRuntime.jsxs(
943
+ "section",
944
+ {
945
+ "aria-labelledby": `${id}-title`,
946
+ className: slot(
947
+ "card",
948
+ "dwt-card",
949
+ `dwt-card--${status}`,
950
+ minimized && "dwt-card--minimized",
951
+ size !== void 0 && `dwt-card--size-${size}`,
952
+ className
953
+ ),
954
+ children: [
955
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: slot("cardHeader", "dwt-card-header"), children: [
956
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dwt-card-heading", children: [
957
+ /* @__PURE__ */ jsxRuntime.jsx(
958
+ Heading,
959
+ {
960
+ id: `${id}-title`,
961
+ className: slot("cardTitle", "dwt-card-title"),
962
+ children: title
963
+ }
964
+ ),
965
+ description !== void 0 && !minimized && /* @__PURE__ */ jsxRuntime.jsx("p", { className: slot("cardDescription", "dwt-card-description"), children: description })
966
+ ] }),
967
+ (actions !== void 0 || onToggleMinimized !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: slot("cardActions", "dwt-card-actions"), children: [
968
+ actions,
969
+ onToggleMinimized !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
970
+ "button",
971
+ {
972
+ type: "button",
973
+ className: slot("button", "dwt-button", "dwt-icon-button"),
974
+ "aria-expanded": !minimized,
975
+ "aria-controls": bodyId,
976
+ "aria-label": minimized ? labels.expand(title) : labels.minimize(title),
977
+ title: minimized ? labels.expand(title) : labels.minimize(title),
978
+ onClick: onToggleMinimized,
979
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: minimized ? "\u25B8" : "\u25BE" })
980
+ }
981
+ )
982
+ ] })
983
+ ] }),
984
+ /* @__PURE__ */ jsxRuntime.jsx(
985
+ "div",
986
+ {
987
+ id: bodyId,
988
+ className: slot("cardBody", "dwt-card-body"),
989
+ hidden: minimized,
990
+ children: minimized ? null : body
991
+ }
992
+ ),
993
+ footer !== void 0 && footer !== null && !minimized && /* @__PURE__ */ jsxRuntime.jsx("div", { className: slot("cardFooter", "dwt-card-footer"), children: footer })
994
+ ]
995
+ }
996
+ );
997
+ }
998
+ function widgetTitle(widget) {
999
+ if (widget.status === "ok" && widget.data.kind === "ALERT_LIST" && widget.data.total > 0) {
1000
+ return `${widget.definition.title} (${widget.data.total})`;
1001
+ }
1002
+ return widget.definition.title;
1003
+ }
1004
+ function ResolvedWidgetCard({
1005
+ widget,
1006
+ ...props
1007
+ }) {
1008
+ const { definition } = widget;
1009
+ return /* @__PURE__ */ jsxRuntime.jsx(
1010
+ WidgetCard,
1011
+ {
1012
+ ...props,
1013
+ title: widgetTitle(widget),
1014
+ status: widget.status,
1015
+ ...definition.description === void 0 ? {} : { description: definition.description },
1016
+ ...definition.defaultSize === void 0 ? {} : { size: definition.defaultSize },
1017
+ ...widget.status === "empty" ? { emptyText: widget.emptyText } : {},
1018
+ ...widget.status === "error" ? { error: widget.error } : {},
1019
+ children: widget.status === "ok" ? /* @__PURE__ */ jsxRuntime.jsx(WidgetContent, { data: widget.data }) : null
1020
+ }
1021
+ );
1022
+ }
1023
+
1024
+ // src/core/layout.ts
1025
+ var EMPTY_LAYOUT = Object.freeze({
1026
+ order: Object.freeze([]),
1027
+ hidden: Object.freeze([]),
1028
+ minimized: Object.freeze([])
1029
+ });
1030
+ function bySortOrder(items) {
1031
+ return items.map((item, index) => ({ item, index })).sort(
1032
+ (a, b) => (a.item.sortOrder ?? 0) - (b.item.sortOrder ?? 0) || a.index - b.index
1033
+ ).map(({ item }) => item);
1034
+ }
1035
+ function visibleWidgets(definitions, layout) {
1036
+ const hidden = new Set(layout.hidden);
1037
+ const visible = definitions.filter((d) => !hidden.has(d.key));
1038
+ const byKey = new Map(visible.map((d) => [d.key, d]));
1039
+ const ordered = [];
1040
+ for (const key of layout.order) {
1041
+ const definition = byKey.get(key);
1042
+ if (definition !== void 0) {
1043
+ ordered.push(definition);
1044
+ byKey.delete(key);
1045
+ }
1046
+ }
1047
+ for (const definition of bySortOrder(visible)) {
1048
+ if (byKey.has(definition.key)) ordered.push(definition);
1049
+ }
1050
+ return ordered;
1051
+ }
1052
+ function hiddenWidgets(definitions, layout) {
1053
+ const hidden = new Set(layout.hidden);
1054
+ return bySortOrder(definitions.filter((d) => hidden.has(d.key)));
1055
+ }
1056
+ function moveWidget(definitions, layout, key, toIndex) {
1057
+ const current = visibleWidgets(definitions, layout).map(
1058
+ (d) => d.key
1059
+ );
1060
+ const fromIndex = current.indexOf(key);
1061
+ if (fromIndex === -1 || !Number.isInteger(toIndex) || toIndex < 0 || toIndex >= current.length || toIndex === fromIndex) {
1062
+ return layout;
1063
+ }
1064
+ const next = [...current];
1065
+ next.splice(fromIndex, 1);
1066
+ next.splice(toIndex, 0, key);
1067
+ return { ...layout, order: next };
1068
+ }
1069
+ function moveWidgetBy(definitions, layout, key, offset) {
1070
+ const current = visibleWidgets(definitions, layout).map(
1071
+ (d) => d.key
1072
+ );
1073
+ const fromIndex = current.indexOf(key);
1074
+ if (fromIndex === -1) return layout;
1075
+ const toIndex = Math.max(
1076
+ 0,
1077
+ Math.min(current.length - 1, fromIndex + Math.trunc(offset))
1078
+ );
1079
+ return moveWidget(definitions, layout, key, toIndex);
1080
+ }
1081
+ function isHidden(layout, key) {
1082
+ return layout.hidden.includes(key);
1083
+ }
1084
+ function isMinimized(layout, key) {
1085
+ return layout.minimized.includes(key);
1086
+ }
1087
+ function hideWidget(layout, key) {
1088
+ if (isHidden(layout, key)) return layout;
1089
+ return { ...layout, hidden: [...layout.hidden, key] };
1090
+ }
1091
+ function showWidget(layout, key) {
1092
+ if (!isHidden(layout, key)) return layout;
1093
+ return {
1094
+ ...layout,
1095
+ hidden: layout.hidden.filter((k) => k !== key),
1096
+ minimized: layout.minimized.filter((k) => k !== key)
1097
+ };
1098
+ }
1099
+ function minimizeWidget(layout, key) {
1100
+ if (isMinimized(layout, key)) return layout;
1101
+ return { ...layout, minimized: [...layout.minimized, key] };
1102
+ }
1103
+ function restoreWidget(layout, key) {
1104
+ if (!isMinimized(layout, key)) return layout;
1105
+ return { ...layout, minimized: layout.minimized.filter((k) => k !== key) };
1106
+ }
1107
+ function toggleMinimized(layout, key) {
1108
+ return isMinimized(layout, key) ? restoreWidget(layout, key) : minimizeWidget(layout, key);
1109
+ }
1110
+ function WidgetGrid({
1111
+ widgets,
1112
+ onRetry,
1113
+ headingLevel,
1114
+ className
1115
+ }) {
1116
+ const slot = useSlotClassName();
1117
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: slot("grid", "dwt-grid", className), children: widgets.map((widget) => /* @__PURE__ */ jsxRuntime.jsx(
1118
+ ResolvedWidgetCard,
1119
+ {
1120
+ widget,
1121
+ ...headingLevel === void 0 ? {} : { headingLevel },
1122
+ ...onRetry === void 0 || widget.status !== "error" ? {} : { onRetry: () => onRetry(widget.definition.key) }
1123
+ },
1124
+ widget.definition.key
1125
+ )) });
1126
+ }
1127
+ function DashboardInner({
1128
+ widgets,
1129
+ layout = EMPTY_LAYOUT,
1130
+ onLayoutChange,
1131
+ onRetry,
1132
+ headingLevel,
1133
+ className
1134
+ }) {
1135
+ const slot = useSlotClassName();
1136
+ const { labels } = useWidgetSettings();
1137
+ const definitions = widgets.map((w) => w.definition);
1138
+ const byKey = new Map(
1139
+ widgets.map((w) => [w.definition.key, w])
1140
+ );
1141
+ const visible = visibleWidgets(definitions, layout);
1142
+ const hidden = hiddenWidgets(definitions, layout);
1143
+ const editable = onLayoutChange !== void 0;
1144
+ function change(next) {
1145
+ if (next !== layout) onLayoutChange?.(next);
1146
+ }
1147
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: slot("dashboard", "dwt-dashboard", className), children: [
1148
+ editable && hidden.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: slot("hiddenBar", "dwt-hidden-bar"), children: [
1149
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dwt-hidden-label", children: labels.hiddenWidgets }),
1150
+ hidden.map((definition) => /* @__PURE__ */ jsxRuntime.jsxs(
1151
+ "button",
1152
+ {
1153
+ type: "button",
1154
+ className: slot("button", "dwt-button", "dwt-show-button"),
1155
+ "aria-label": labels.show(definition.title),
1156
+ onClick: () => change(showWidget(layout, definition.key)),
1157
+ children: [
1158
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "+ " }),
1159
+ definition.title
1160
+ ]
1161
+ },
1162
+ definition.key
1163
+ ))
1164
+ ] }),
1165
+ visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: slot("empty", "dwt-empty", "dwt-dashboard-empty"), children: definitions.length === 0 ? labels.noWidgets : labels.allHidden }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: slot("grid", "dwt-grid"), children: visible.map((definition, index) => {
1166
+ const widget = byKey.get(
1167
+ definition.key
1168
+ );
1169
+ if (widget === void 0) return null;
1170
+ const key = definition.key;
1171
+ const title = definition.title;
1172
+ const actions = editable ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1173
+ /* @__PURE__ */ jsxRuntime.jsx(
1174
+ "button",
1175
+ {
1176
+ type: "button",
1177
+ className: slot("button", "dwt-button", "dwt-icon-button"),
1178
+ "aria-label": labels.moveEarlier(title),
1179
+ title: labels.moveEarlier(title),
1180
+ disabled: index === 0,
1181
+ onClick: () => change(moveWidgetBy(definitions, layout, key, -1)),
1182
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2191" })
1183
+ }
1184
+ ),
1185
+ /* @__PURE__ */ jsxRuntime.jsx(
1186
+ "button",
1187
+ {
1188
+ type: "button",
1189
+ className: slot("button", "dwt-button", "dwt-icon-button"),
1190
+ "aria-label": labels.moveLater(title),
1191
+ title: labels.moveLater(title),
1192
+ disabled: index === visible.length - 1,
1193
+ onClick: () => change(moveWidgetBy(definitions, layout, key, 1)),
1194
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2193" })
1195
+ }
1196
+ ),
1197
+ /* @__PURE__ */ jsxRuntime.jsx(
1198
+ "button",
1199
+ {
1200
+ type: "button",
1201
+ className: slot("button", "dwt-button", "dwt-icon-button"),
1202
+ "aria-label": labels.hide(title),
1203
+ title: labels.hide(title),
1204
+ onClick: () => change(hideWidget(layout, key)),
1205
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xD7" })
1206
+ }
1207
+ )
1208
+ ] }) : void 0;
1209
+ return /* @__PURE__ */ jsxRuntime.jsx(
1210
+ ResolvedWidgetCard,
1211
+ {
1212
+ widget,
1213
+ minimized: isMinimized(layout, key),
1214
+ ...actions === void 0 ? {} : { actions },
1215
+ ...editable ? {
1216
+ onToggleMinimized: () => change(toggleMinimized(layout, key))
1217
+ } : {},
1218
+ ...headingLevel === void 0 ? {} : { headingLevel },
1219
+ ...onRetry === void 0 || widget.status !== "error" ? {} : { onRetry: () => onRetry(key) }
1220
+ },
1221
+ key
1222
+ );
1223
+ }) })
1224
+ ] });
1225
+ }
1226
+ function Dashboard({
1227
+ locale,
1228
+ linkTarget,
1229
+ classNames,
1230
+ labels,
1231
+ ...props
1232
+ }) {
1233
+ const parent = useWidgetSettings();
1234
+ const resolvedLocale = locale ?? parent.locale;
1235
+ const resolvedTarget = linkTarget ?? parent.linkTarget;
1236
+ return /* @__PURE__ */ jsxRuntime.jsx(
1237
+ WidgetSettingsProvider,
1238
+ {
1239
+ ...resolvedLocale === void 0 ? {} : { locale: resolvedLocale },
1240
+ ...resolvedTarget === void 0 ? {} : { linkTarget: resolvedTarget },
1241
+ classNames: { ...parent.classNames, ...classNames },
1242
+ labels: { ...parent.labels, ...labels },
1243
+ children: /* @__PURE__ */ jsxRuntime.jsx(DashboardInner, { ...props })
1244
+ }
1245
+ );
1246
+ }
1247
+
1248
+ exports.AlertListWidget = AlertListWidget;
1249
+ exports.BarListWidget = BarListWidget;
1250
+ exports.DEFAULT_LABELS = DEFAULT_LABELS;
1251
+ exports.DEFAULT_SERIES_COLORS = DEFAULT_SERIES_COLORS;
1252
+ exports.Dashboard = Dashboard;
1253
+ exports.GaugeWidget = GaugeWidget;
1254
+ exports.GraphWidget = GraphWidget;
1255
+ exports.KpiWidget = KpiWidget;
1256
+ exports.ResolvedWidgetCard = ResolvedWidgetCard;
1257
+ exports.TableWidget = TableWidget;
1258
+ exports.TextWidget = TextWidget;
1259
+ exports.Thumbnail = Thumbnail;
1260
+ exports.WidgetCard = WidgetCard;
1261
+ exports.WidgetContent = WidgetContent;
1262
+ exports.WidgetGrid = WidgetGrid;
1263
+ exports.WidgetLink = WidgetLink;
1264
+ exports.WidgetSettingsProvider = WidgetSettingsProvider;
1265
+ exports.barPath = barPath;
1266
+ exports.buildChartModel = buildChartModel;
1267
+ exports.describeChart = describeChart;
1268
+ exports.joinClassNames = joinClassNames;
1269
+ exports.seriesColor = seriesColor;
1270
+ exports.useWidgetSettings = useWidgetSettings;
1271
+ exports.widgetTitle = widgetTitle;
1272
+ //# sourceMappingURL=react.cjs.map
1273
+ //# sourceMappingURL=react.cjs.map