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