@devicechain/widgets 0.23.0-rc.1 → 0.24.0-rc.1
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/README.md +8 -3
- package/dist/aggregate-fold.d.ts +3 -2
- package/dist/aggregate-strings.d.ts +1 -0
- package/dist/alarm-strings.d.ts +18 -0
- package/dist/binding-policy.d.ts +4 -0
- package/dist/bound-devices-notice.d.ts +30 -0
- package/dist/builtin-descriptors.d.ts +290 -249
- package/dist/builtin-option-docs.d.ts +2 -0
- package/dist/builtin-registry.d.ts +249 -226
- package/dist/category-geometry.d.ts +1 -0
- package/dist/chunks/{aggregate-feed-BD3CHNFU.js → aggregate-feed-4FSBQYJL.js} +39 -11
- package/dist/chunks/aggregate-feed-4FSBQYJL.js.map +7 -0
- package/dist/chunks/{aggregate-widgets-HZQI5HTX.js → aggregate-widgets-2ZQBW3YF.js} +376 -182
- package/dist/chunks/aggregate-widgets-2ZQBW3YF.js.map +7 -0
- package/dist/chunks/alarm-widgets-X4PX5MYJ.js +72 -0
- package/dist/chunks/alarm-widgets-X4PX5MYJ.js.map +7 -0
- package/dist/chunks/chunk-546TK2QG.js +722 -0
- package/dist/chunks/chunk-546TK2QG.js.map +7 -0
- package/dist/chunks/{chunk-WBKHYZU2.js → chunk-62CHYCHI.js} +6 -62
- package/dist/chunks/chunk-62CHYCHI.js.map +7 -0
- package/dist/chunks/chunk-BMFQOTYG.js +41 -0
- package/dist/chunks/chunk-BMFQOTYG.js.map +7 -0
- package/dist/chunks/{chunk-2D7JNKFE.js → chunk-JFH4QMLA.js} +28 -4
- package/dist/chunks/chunk-JFH4QMLA.js.map +7 -0
- package/dist/chunks/chunk-QMGKB6J3.js +84 -0
- package/dist/chunks/chunk-QMGKB6J3.js.map +7 -0
- package/dist/chunks/chunk-XNEBAY4J.js +19 -0
- package/dist/chunks/chunk-XNEBAY4J.js.map +7 -0
- package/dist/chunks/chunk-YYJV54BV.js +58 -0
- package/dist/chunks/chunk-YYJV54BV.js.map +7 -0
- package/dist/chunks/compare-feed-AY3CVKUZ.js +346 -0
- package/dist/chunks/compare-feed-AY3CVKUZ.js.map +7 -0
- package/dist/chunks/{control-widgets-3V2LSSRF.js → control-widgets-UCTJJJ2D.js} +267 -78
- package/dist/chunks/control-widgets-UCTJJJ2D.js.map +7 -0
- package/dist/chunks/event-markers-feed-GEGTIHXT.js +373 -0
- package/dist/chunks/event-markers-feed-GEGTIHXT.js.map +7 -0
- package/dist/chunks/{fleet-feed-5QGDLOV5.js → fleet-feed-2SKDR6ZQ.js} +47 -8
- package/dist/chunks/fleet-feed-2SKDR6ZQ.js.map +7 -0
- package/dist/chunks/presence-widgets-2LIUKN4Y.js +350 -0
- package/dist/chunks/presence-widgets-2LIUKN4Y.js.map +7 -0
- package/dist/command-control-strings.d.ts +5 -0
- package/dist/compare-feed.d.ts +13 -0
- package/dist/compare-model.d.ts +38 -0
- package/dist/compare-strings.d.ts +13 -0
- package/dist/documented-descriptors.d.ts +4826 -0
- package/dist/event-markers-feed.d.ts +11 -0
- package/dist/event-markers-model.d.ts +38 -0
- package/dist/event-markers-strings.d.ts +20 -0
- package/dist/event-markers-types.d.ts +7 -0
- package/dist/fleet-count-model.d.ts +13 -0
- package/dist/fleet-feed.d.ts +1 -0
- package/dist/fleet-model.d.ts +1 -0
- package/dist/fleet-strings.d.ts +11 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +946 -398
- package/dist/index.js.map +4 -4
- package/dist/presence-feed.d.ts +24 -0
- package/dist/presence-model.d.ts +13 -0
- package/dist/presence-strings.d.ts +19 -0
- package/dist/sparkline.d.ts +23 -0
- package/dist/use-bound-devices.d.ts +26 -0
- package/dist/widgets/aggregate-lazy.d.ts +81 -46
- package/dist/widgets/aggregate-widgets.d.ts +1 -0
- package/dist/widgets/alarm-grouped.d.ts +17 -0
- package/dist/widgets/alarm-lazy.d.ts +63 -16
- package/dist/widgets/alarm-severity.d.ts +8 -0
- package/dist/widgets/alarm-table.d.ts +2 -0
- package/dist/widgets/alarm-type-tabs.d.ts +10 -0
- package/dist/widgets/alarm-widgets.d.ts +2 -0
- package/dist/widgets/command-stepper.d.ts +22 -0
- package/dist/widgets/control-lazy.d.ts +16 -24
- package/dist/widgets/device-status.d.ts +4 -0
- package/dist/widgets/fleet-count.d.ts +2 -0
- package/dist/widgets/gauge.d.ts +2 -15
- package/dist/widgets/image.d.ts +2 -6
- package/dist/widgets/label.d.ts +2 -6
- package/dist/widgets/latest-card.d.ts +2 -13
- package/dist/widgets/lazy-chunk.d.ts +7 -2
- package/dist/widgets/level-lazy.d.ts +14 -96
- package/dist/widgets/map.d.ts +8 -5
- package/dist/widgets/online-doughnut.d.ts +2 -0
- package/dist/widgets/presence-lazy.d.ts +27 -0
- package/dist/widgets/presence-widgets.d.ts +3 -0
- package/dist/widgets/selection-lazy.d.ts +2 -4
- package/dist/widgets/table-lazy.d.ts +2 -7
- package/dist/widgets/time-series-chart.d.ts +62 -12
- package/package.json +13 -13
- package/dist/chunks/aggregate-feed-BD3CHNFU.js.map +0 -7
- package/dist/chunks/aggregate-widgets-HZQI5HTX.js.map +0 -7
- package/dist/chunks/alarm-widgets-7UY6MIYZ.js +0 -9
- package/dist/chunks/alarm-widgets-7UY6MIYZ.js.map +0 -7
- package/dist/chunks/chunk-2D7JNKFE.js.map +0 -7
- package/dist/chunks/chunk-6OGI4UFJ.js +0 -367
- package/dist/chunks/chunk-6OGI4UFJ.js.map +0 -7
- package/dist/chunks/chunk-WBKHYZU2.js.map +0 -7
- package/dist/chunks/control-widgets-3V2LSSRF.js.map +0 -7
- package/dist/chunks/fleet-feed-5QGDLOV5.js.map +0 -7
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/widgets/bar-chart.tsx", "../../src/aggregate-strings.ts", "../../src/category-items.ts", "../../src/widgets/fleet-count.tsx", "../../src/fleet-count-model.ts", "../../src/widgets/fleet-table.tsx", "../../src/widgets/pie-chart.tsx", "../../src/widgets/stat-card.tsx", "../../src/aggregate-reading.ts", "../../src/sparkline.tsx"],
|
|
4
|
+
"sourcesContent": ["// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// bar-chart -- one bar per measurement, each as tall as that measurement's latest reading. The\n// categories are the measurements the datasource names (in that order, or sorted by value); one\n// that has not reported yet keeps its label with no bar rather than vanishing or reading zero.\n//\n// Drawn as SVG with its text as DOM text nodes, not on an ECharts canvas: no chart type to\n// register (a bar or pie type costs ~7 KB gzip each, and a second lazily loaded ECharts module\n// makes the app bundler split the whole chart engine out of the entry chunk, which cost more than\n// the types themselves), and the readability gate can measure the labels.\n\nimport { useMemo } from 'react';\n\nimport {\n css,\n formatValue,\n optNumber,\n optString,\n WidgetFrame,\n type WidgetProps,\n type WidgetScale,\n} from '@devicechain/widget-sdk';\n\nimport { aggregateStrings } from '../aggregate-strings';\nimport { categoryItems, parseSort } from '../category-items';\nimport { deviceBars } from '../fleet-model';\nimport { fleetStrings } from '../fleet-strings';\nimport { niceDomain } from '../category-geometry';\nimport { LevelEmpty } from '../level-readout';\n\n// The width of a character of the chart font, as a share of its size: an estimate for the room a\n// label needs. Wide enough for digits and capitals, so a label is trimmed rather than overlapped.\nconst CHAR_EM = 0.6;\n// Past this many categories the value labels on the bars would collide.\nexport const MAX_LABELLED_BARS = 12;\n// The thickest a bar is drawn, in px at density 1: a single bar does not fill the chart.\nconst MAX_BAR_PX = 56;\n\n// A chart of one bar per device cannot label hundreds of bars; the first MAX_DEVICE_BARS in the\n// chosen order are drawn, and the chart SAYS how many devices there are (see deviceItems).\nexport const MAX_DEVICE_BARS = 100;\n\nexport function BarChart({ widget, data }: WidgetProps) {\n const sort = parseSort(optString(widget.options, 'sort'));\n const perDevice = optString(widget.options, 'groupBy') === 'device';\n const measurement = optString(widget.options, 'measurement') ?? widget.datasource?.measurements[0];\n const items = useMemo(\n () => (perDevice ? [] : categoryItems(widget, data, sort)),\n [widget, data, sort, perDevice],\n );\n const unit = optString(widget.options, 'unit');\n const precision = optNumber(widget.options, 'precision');\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n {(ws) => {\n if (perDevice) {\n return <DeviceBars data={data} measurement={measurement} sort={sort} unit={unit} precision={precision} ws={ws} />;\n }\n return items.length === 0 ? (\n <LevelEmpty message={aggregateStrings().noCategories} />\n ) : (\n <Bars items={items} unit={unit} precision={precision} ws={ws} />\n );\n }}\n </WidgetFrame>\n );\n}\n\n// One bar per device of the anchor, for one measurement: the same chart, with the fleet's members as\n// its categories. Every way the fleet can fail to be a chart is a sentence, and a fleet larger than\n// what is drawn is counted in the chart itself, where it cannot be dropped with a heading.\nfunction DeviceBars({\n data,\n measurement,\n sort,\n unit,\n precision,\n ws,\n}: {\n data: WidgetProps['data'];\n measurement: string | undefined;\n sort: ReturnType<typeof parseSort>;\n unit: string | undefined;\n precision: number | undefined;\n ws: WidgetScale;\n}) {\n const strings = fleetStrings();\n const fleet = data.members;\n const bars = useMemo(\n () => (fleet && measurement ? deviceBars(fleet.members, measurement, sort) : []),\n [fleet, measurement, sort],\n );\n if (!measurement) return <LevelEmpty message={strings.noMeasurement} />;\n if (!fleet || fleet.status === 'loading') return <LevelEmpty message={strings.loading} />;\n if (fleet.status === 'unbound') return <LevelEmpty message={strings.unbound} />;\n if (fleet.status === 'forbidden') return <LevelEmpty message={strings.forbidden} />;\n if (fleet.status === 'unsupported') return <LevelEmpty message={strings.unsupported} />;\n if (bars.length === 0) return <LevelEmpty message={strings.noMembers} />;\n const drawn = bars.slice(0, MAX_DEVICE_BARS);\n // Counted against the anchor, not only against this chart's own cap: a fleet the resolver read\n // only a page of is just as cut off as one this chart trimmed.\n const note = fleet.truncated || drawn.length < bars.length ? strings.truncated(drawn.length, Math.max(fleet.total, bars.length)) : undefined;\n return (\n <Bars\n items={drawn.map((b) => ({ label: b.label, key: b.token, value: b.value }))}\n unit={unit}\n precision={precision}\n ws={ws}\n note={note}\n />\n );\n}\n\n// \"abcdefgh\" fitted to `chars` characters: cut and ended with an ellipsis. The full text stays in\n// the element's <title>, so nothing is lost to the trim.\nexport function fitLabel(label: string, chars: number): string {\n if (chars < 2) return '';\n return label.length <= chars ? label : `${label.slice(0, Math.max(1, chars - 1))}\u2026`;\n}\n\nfunction Bars({\n items,\n unit,\n precision,\n ws,\n note,\n}: {\n items: { label: string; value: number | null; key?: string }[];\n unit: string | undefined;\n precision: number | undefined;\n ws: WidgetScale;\n // A line along the bottom of the chart: how much of the fleet the bars are.\n note?: string;\n}) {\n const W = ws.size.width > 0 ? ws.size.width : 240;\n const fullH = ws.size.height > 0 ? ws.size.height : 160;\n const noteH = note ? ws.px('font-xs') * 1.8 : 0;\n const H = Math.max(1, fullH - noteH);\n // Columns in a wide chart, rows in a tall one: rows leave a long measurement name its full width.\n const horizontal = H > W;\n const font = ws.px('font-xs');\n const pad = ws.px('space-1');\n const charPx = font * CHAR_EM;\n const fmt = (v: number) => formatValue(v, precision);\n const withUnit = (v: number) => (unit ? `${fmt(v)} ${unit}` : fmt(v));\n const domain = niceDomain(items.flatMap((i) => (i.value === null ? [] : [i.value])));\n const span = domain.max - domain.min;\n const few = items.length <= MAX_LABELLED_BARS;\n const widest = (texts: string[]) => texts.reduce((n, t) => Math.max(n, t.length), 0) * charPx;\n const tickText = domain.ticks.map(fmt);\n\n // The plot box. Vertical: value ticks down the left, category labels along the bottom, room\n // above for the unit and the tallest bar's value. Horizontal: category labels down the left,\n // value ticks along the bottom, room to the right for the longest bar's value.\n const catLabelH = font * 1.7;\n const topH = font * 1.7;\n let x0: number, x1: number, y0: number, y1: number;\n if (horizontal) {\n x0 = Math.min(W * 0.4, widest(items.map((i) => i.label)) + pad * 2);\n x1 = W - pad - (few ? widest(items.map((i) => (i.value === null ? '' : withUnit(i.value)))) + pad : pad);\n y0 = pad;\n y1 = H - catLabelH;\n } else {\n x0 = widest(tickText) + pad * 2;\n x1 = W - pad;\n y0 = topH;\n y1 = H - catLabelH;\n }\n const length = horizontal ? Math.max(0, x1 - x0) : Math.max(0, y1 - y0);\n // A value's distance along the value axis from the plot's low end (left, or bottom).\n const along = (v: number) => ((v - domain.min) / span) * length;\n const bandPx = (horizontal ? y1 - y0 : x1 - x0) / items.length;\n // Each bar's value is printed beside it when the labels fit: no more than MAX_LABELLED_BARS, and,\n // in columns, the widest value no wider than a column (side by side they would run together).\n // Otherwise every value is still in the chart's accessible description and on each bar's title.\n const labelled =\n few &&\n (horizontal || widest(items.map((i) => (i.value === null ? '' : fmt(i.value)))) <= bandPx);\n const thick = Math.min(bandPx * 0.62, MAX_BAR_PX * ws.scale);\n const muted = { fill: css('muted-foreground') };\n const text = { fontSize: font, fontVariantNumeric: 'tabular-nums' } as const;\n const zero = along(0);\n\n return (\n <svg\n data-bar-chart={horizontal ? 'horizontal' : 'vertical'}\n width={W}\n height={H}\n viewBox={`0 0 ${W} ${H}`}\n role=\"img\"\n aria-label={items.map((i) => `${i.label}: ${i.value === null ? '\u2014' : withUnit(i.value)}`).join(', ')}\n style={{ display: 'block' }}\n >\n {note ? (\n <text data-bar-note=\"\" x={pad} y={fullH - font * 0.4} style={{ ...text, ...muted }}>\n {note}\n </text>\n ) : null}\n {unit ? (\n <text\n x={horizontal ? W - pad : pad}\n y={horizontal ? H - pad : font}\n textAnchor={horizontal ? 'end' : 'start'}\n style={{ ...text, ...muted }}\n >\n {unit}\n </text>\n ) : null}\n {domain.ticks.map((t, i) => {\n const d = along(t);\n return horizontal ? (\n <g key={t}>\n <line x1={x0 + d} x2={x0 + d} y1={y0} y2={y1} style={{ stroke: css('border') }} />\n <text x={x0 + d} y={y1 + font * 1.2} textAnchor=\"middle\" style={{ ...text, ...muted }}>\n {tickText[i]}\n </text>\n </g>\n ) : (\n <g key={t}>\n <line x1={x0} x2={x1} y1={y1 - d} y2={y1 - d} style={{ stroke: css('border') }} />\n <text x={x0 - pad} y={y1 - d + font * 0.35} textAnchor=\"end\" style={{ ...text, ...muted }}>\n {tickText[i]}\n </text>\n </g>\n );\n })}\n {items.map((item, i) => {\n const centre = (horizontal ? y0 : x0) + bandPx * (i + 0.5);\n const label = fitLabel(item.label, Math.floor((horizontal ? x0 - pad : bandPx) / charPx));\n const v = item.value;\n const end = v === null ? zero : along(v);\n const lo = Math.min(zero, end);\n const hi = Math.max(zero, end);\n return (\n <g key={item.key ?? item.label} data-bar={item.label} data-value={v ?? undefined}>\n <title>{`${item.label}: ${v === null ? '\u2014' : withUnit(v)}`}</title>\n {v === null ? null : horizontal ? (\n <rect x={x0 + lo} y={centre - thick / 2} width={Math.max(1, hi - lo)} height={thick} style={{ fill: css('chart-1') }} />\n ) : (\n <rect x={centre - thick / 2} y={y1 - hi} width={thick} height={Math.max(1, hi - lo)} style={{ fill: css('chart-1') }} />\n )}\n {labelled && v !== null ? (\n horizontal ? (\n <text x={x0 + hi + pad} y={centre + font * 0.35} style={{ ...text, fill: css('foreground') }}>\n {withUnit(v)}\n </text>\n ) : (\n <text x={centre} y={y1 - hi - pad} textAnchor=\"middle\" style={{ ...text, fill: css('foreground') }}>\n {fmt(v)}\n </text>\n )\n ) : null}\n <text\n x={horizontal ? x0 - pad : centre}\n y={horizontal ? centre + font * 0.35 : y1 + font * 1.2}\n textAnchor={horizontal ? 'end' : 'middle'}\n style={{ ...text, ...muted }}\n >\n {label}\n </text>\n </g>\n );\n })}\n </svg>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The few fixed words the aggregate and categorical widgets render themselves: the name of each\n// statistic, and the reasons they give instead of a figure. The widget packages carry no i18n\n// runtime, so this is the one small per-language table (modelled on level-strings.ts): a host that\n// adds a runtime later replaces exactly this file. Option labels are translated in the console's\n// catalogs, not here.\n\nimport { viewerLocale } from '@devicechain/widget-sdk';\n\nexport const AGGREGATE_STATS = ['avg', 'min', 'max', 'sum', 'count', 'last'] as const;\nexport type AggregateStat = (typeof AGGREGATE_STATS)[number];\n\nexport interface AggregateStrings {\n stats: Record<AggregateStat, string>;\n loading: string;\n // The data source cannot compute aggregates (a replayed recording, say).\n unsupported: string;\n failed: (message: string | undefined) => string;\n // Nothing was read in the span.\n noReadings: (span: string) => string;\n noData: string;\n // The widget's slot is bound to no device.\n unbound: string;\n // The accessible name of a card's trend strip: what it draws and over how long.\n trend: (stat: string, span: string) => string;\n // Categorical charts.\n noCategories: string;\n pieNegative: string;\n pieNoShare: string;\n}\n\nexport const AGGREGATE_STRINGS: Record<'en' | 'es', AggregateStrings> = {\n en: {\n stats: { avg: 'Average', min: 'Minimum', max: 'Maximum', sum: 'Sum', count: 'Readings', last: 'Latest' },\n loading: 'Loading',\n unsupported: 'This data source cannot compute aggregates',\n failed: (m) => (m ? `Aggregates unavailable: ${m}` : 'Aggregates unavailable'),\n noReadings: (span) => `No readings in the last ${span}`,\n noData: 'No data',\n unbound: 'Choose a device for this widget',\n trend: (stat, span) => `${stat} trend over the last ${span}`,\n noCategories: 'Select the measurements to show',\n pieNegative: 'A pie needs values of zero or more',\n pieNoShare: 'Every value is zero, so there is no share to show',\n },\n es: {\n stats: { avg: 'Promedio', min: 'M\u00EDnimo', max: 'M\u00E1ximo', sum: 'Suma', count: 'Lecturas', last: '\u00DAltimo' },\n loading: 'Cargando',\n unsupported: 'Esta fuente de datos no puede calcular agregados',\n failed: (m) => (m ? `Agregados no disponibles: ${m}` : 'Agregados no disponibles'),\n noReadings: (span) => `Sin lecturas en los \u00FAltimos ${span}`,\n noData: 'Sin datos',\n unbound: 'Elija un dispositivo para este widget',\n trend: (stat, span) => `Tendencia de ${stat.toLowerCase()} en los \u00FAltimos ${span}`,\n noCategories: 'Seleccione las mediciones que mostrar',\n pieNegative: 'Un gr\u00E1fico circular necesita valores de cero o m\u00E1s',\n pieNoShare: 'Todos los valores son cero, no hay proporci\u00F3n que mostrar',\n },\n};\n\nexport function aggregateStrings(locale: string | undefined = viewerLocale()): AggregateStrings {\n return locale?.toLowerCase().split('-')[0] === 'es' ? AGGREGATE_STRINGS.es : AGGREGATE_STRINGS.en;\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The categories of a bar or pie chart, decided apart from the drawing: which measurements, in\n// what order, with which value. One category per measurement, valued by its latest reading.\n\nimport type { MeasurementStreamState, WidgetInstance } from '@devicechain/dashboards';\n\n// One category: its label and its value (null = no reading yet).\nexport interface CategoryItem {\n label: string;\n value: number | null;\n}\n\nexport const CATEGORY_SORTS = ['none', 'ascending', 'descending'] as const;\nexport type CategorySort = (typeof CATEGORY_SORTS)[number];\n\n// Unset or unknown keeps the datasource's order. (The widget reads its own option and passes it here.)\nexport function parseSort(v: unknown): CategorySort {\n return CATEGORY_SORTS.find((s) => s === v) ?? 'none';\n}\n\n// The measurements the datasource names, in the order it names them; with none named, every\n// measurement that has reported, in the order they were first seen. A category with no reading\n// yet is KEPT with a null value (a bar chart shows the label with no bar), so the chart does not\n// quietly drop a measurement that has not reported -- but it sorts after those that have.\nexport function categoryItems(\n widget: WidgetInstance,\n data: Pick<MeasurementStreamState, 'latest'>,\n sort: CategorySort,\n): CategoryItem[] {\n const named = widget.datasource?.measurements ?? [];\n const names = named.length > 0 ? [...new Set(named)] : Object.keys(data.latest);\n // A value that is not a finite number (NaN, an infinity) is no reading: it has no length and no\n // share, and drawing it as either would be a guess.\n const items = names.map((label) => {\n const v = data.latest[label]?.value;\n return { label, value: typeof v === 'number' && Number.isFinite(v) ? v : null };\n });\n if (sort === 'none') return items;\n const sign = sort === 'ascending' ? 1 : -1;\n return items\n .map((item, i) => ({ item, i }))\n .sort((a, b) => {\n if (a.item.value === null || b.item.value === null) {\n return a.item.value === b.item.value ? a.i - b.i : a.item.value === null ? 1 : -1;\n }\n return sign * (a.item.value - b.item.value) || a.i - b.i;\n })\n .map((x) => x.item);\n}\n\n// Why a pie cannot be drawn from these items, or undefined when it can. A pie is shares of a\n// whole: a negative value has no share, and all zeros have nothing to divide.\n// With no reading at all there is nothing to divide either, but that is `noData`, not a refusal.\nexport function pieProblem(items: readonly CategoryItem[]): 'negative' | 'noShare' | 'noData' | undefined {\n const values = items.map((i) => i.value).filter((v): v is number => v !== null);\n if (values.length === 0) return 'noData';\n if (values.some((v) => v < 0)) return 'negative';\n if (values.every((v) => v === 0)) return 'noShare';\n return undefined;\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// fleet-count -- the figure above a fleet: how many of a group's devices need attention (\"3 of 21\"),\n// or simply how many devices the group holds. It reads the same member feed as the fleet table, so\n// a board that already shows the table asks the platform for nothing more.\n//\n// - ATTENTION counts the devices whose newest reading of one measurement is in an abnormal band\n// of the card's thresholds (fleet-count-model.ts). The figure takes the colour of the worst\n// level among them; with none flagged (0 of N, or a fleet in its normal band) it keeps the\n// foreground, so a healthy fleet is calm;\n// - a device with no reading is counted apart (\"2 not reporting\", or \"1 could not be read\" when\n// its read failed), never as healthy; while any member is still loading there is no figure;\n// - a device whose profile does not declare the measurement is not in the \"of N\" at all, and is\n// noted apart (\"1 does not report this\"): a plant in a fuel count is outside the question,\n// which \"not reporting\" would misstate. A declared measurement the device is silent on (a dead\n// probe, a live heartbeat) stays in N as not reporting; so does a device whose profile could\n// not be read;\n// - a group larger than the feed reads says so (\"Showing 500 of 812 devices\") instead of\n// presenting the subset as the fleet;\n// - DEVICES shows the group's size, taken from the roster rather than from the readings.\n//\n// DOM text, so the readability gate sees it.\n\nimport { useMemo } from 'react';\n\nimport {\n css,\n FIT_MIN_FONT_PX,\n fontVar,\n levelColor,\n optString,\n optThresholds,\n primaryMeasurementName,\n spaceVar,\n useSeverityColors,\n WidgetFrame,\n type WidgetProps,\n} from '@devicechain/widget-sdk';\n\nimport { attentionCount } from '../fleet-count-model';\nimport { fleetStrings } from '../fleet-strings';\nimport { LevelEmpty, LevelReadout } from '../level-readout';\n\n// The room the caption lines need, before the density scale.\nconst CAPTION_ROOM_PX = 22;\n\nexport function FleetCount({ widget, data }: WidgetProps) {\n const strings = fleetStrings();\n const severity = useSeverityColors();\n const fleet = data.members;\n const showDevices = optString(widget.options, 'show') === 'devices';\n const measurement = optString(widget.options, 'measurement') ?? primaryMeasurementName(widget);\n const thresholds = useMemo(() => optThresholds(widget.options, 'thresholds'), [widget.options]);\n const title = optString(widget.options, 'title');\n\n const empty = (message: string) => (\n <WidgetFrame title={title}>\n <LevelEmpty message={message} />\n </WidgetFrame>\n );\n if (!fleet || fleet.status === 'loading') return empty(strings.loading);\n if (fleet.status === 'unbound') return empty(strings.unbound);\n if (fleet.status === 'forbidden') return empty(strings.forbidden);\n if (fleet.status === 'unsupported') return empty(strings.unsupported);\n if (!showDevices) {\n if (measurement === undefined) return empty(strings.noMeasurementCount);\n if (thresholds.length === 0) return empty(strings.noBands);\n }\n\n const counted = showDevices ? undefined : attentionCount(fleet.members, measurement ?? '', thresholds);\n // Zero devices is a count, and the answer to \"how many\": 0. Attention over no devices has no\n // \"N\" to be a share of, so it says there are none, as the fleet table does.\n if (!showDevices && fleet.members.length === 0) return empty(strings.noMembers);\n // A count over the members read so far would be an undercount that looks like an answer.\n if (counted && counted.pending > 0) return empty(strings.loading);\n // No member's profile declares this measurement: there is no \"N\" here either. Over a\n // truncated group only the members read were asked, so the message says so rather than speaking\n // for devices past the page.\n if (counted && counted.members === 0) {\n return empty(fleet.truncated ? strings.noneMeasuredShown(fleet.members.length, fleet.total) : strings.noneMeasured);\n }\n\n const figure = showDevices ? String(fleet.total) : String(counted!.flagged);\n const unit = showDevices ? undefined : strings.countOf(counted!.members);\n const color = counted ? levelColor(severity, counted.worst) : undefined;\n const caption = showDevices ? strings.devices : counted!.flagged > 0 ? (counted!.label ?? strings.needAttention) : strings.needAttention;\n const notes: string[] = [];\n if (counted) {\n const silent = counted.members - counted.reporting - counted.unread;\n if (silent > 0) notes.push(strings.notReporting(silent));\n if (counted.unread > 0) notes.push(strings.notRead(counted.unread));\n if (counted.notMeasured > 0) notes.push(strings.notMeasured(counted.notMeasured));\n // The device count is the roster's own total, exact past the page; only the attention count\n // is taken from the members read, so only it says it covers part of the fleet.\n if (fleet.truncated) notes.push(strings.truncated(fleet.members.length, fleet.total));\n }\n\n return (\n <WidgetFrame title={title}>\n {({ size, scale }) => {\n const room = Math.round(CAPTION_ROOM_PX * scale) * (1 + notes.length);\n const fit = { width: size.width, height: Math.max(Math.round(FIT_MIN_FONT_PX * scale), size.height - room) };\n return (\n <div\n data-fleet-count={showDevices ? 'devices' : 'attention'}\n data-flagged={counted?.flagged}\n data-of={counted?.members}\n data-threshold-level={counted?.worst}\n style={{ display: 'flex', flexDirection: 'column', justifyContent: 'safe center', height: '100%', overflow: 'hidden' }}\n >\n <div style={{ flex: '1 1 0', minHeight: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>\n <LevelReadout text={figure} unit={unit} color={color} size={fit} scale={scale} />\n </div>\n <div\n data-fleet-count-caption=\"\"\n style={{\n flex: '0 0 auto',\n marginTop: spaceVar(1),\n textAlign: 'center',\n fontSize: fontVar('sm'),\n color: css('muted-foreground'),\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {caption}\n </div>\n {notes.map((note) => (\n <div\n key={note}\n data-fleet-count-note=\"\"\n style={{ textAlign: 'center', fontSize: fontVar('xs'), color: css('muted-foreground'), overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}\n >\n {note}\n </div>\n ))}\n </div>\n );\n }}\n </WidgetFrame>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// What the fleet count card says, decided apart from the drawing: how many of a fleet's devices\n// are in an abnormal band of one measurement. Pure, so each rule is a test.\n//\n// - a device NEEDS ATTENTION when its newest reading of the measurement falls in a band of a\n// level that is not `normal` (below the lowest threshold is also not a band). The normal band\n// is the healthy state, so a fleet in it counts zero and the card stays neutral (ISA-101);\n// - a device with no reading of the measurement is neither healthy nor flagged: it is counted\n// apart, never folded into either figure -- as NOT REPORTING when its read settled and found\n// nothing (it sent nothing in the span, or sent others but not this one), and as NOT READ when the\n// read failed or did not load its newest reading (the device may well be reporting);\n// - a device whose profile does not DECLARE the measurement (a plant counted among the machines\n// in a fuel count) is outside the question, not silent: it is left out of the \"of N\" and\n// counted apart again (NOT MEASURED), so the card neither calls it \"not reporting\" nor drops it\n// unseen. That is decided from the declaration, never from what the device has sent: a fuel\n// sensor with a dead probe and a live heartbeat declares fuel and stays in N, as not reporting.\n// A device whose declaration is not known (not looked up yet, or the lookup failed) is taken to\n// declare it -- it is never left out on a guess;\n// - while any member's read is still loading there is no count yet: a figure over the members\n// read so far would undercount, and nothing on the card would say so;\n// - the card names the band when every flagged device is in the same labelled one (\"Low\"), and\n// says \"need attention\" otherwise.\n\nimport type { FleetMember } from '@devicechain/dashboards';\nimport { bandAt, SEVERITY_LEVELS, isAlarmLevel, type SeverityLevel, type Threshold } from '@devicechain/widget-sdk';\n\nimport { cellFor } from './fleet-model';\n\nexport interface AttentionCount {\n // The members the question covers: those the feed delivered (fewer than the fleet when it is\n // truncated), less the ones that do not report the measurement at all.\n members: number;\n // Members whose profile does not declare the measurement.\n notMeasured: number;\n flagged: number;\n // Members with a reading of the measurement.\n reporting: number;\n // Members whose read is still loading: while any is, there is no figure yet.\n pending: number;\n // Members whose read failed, or did not load their newest reading: not known to be silent.\n unread: number;\n // The worst level among the flagged, or undefined when none is.\n worst: SeverityLevel | undefined;\n // The shared band label of the flagged devices, when they all sit in one labelled band.\n label: string | undefined;\n}\n\nexport function attentionCount(members: readonly FleetMember[], measurement: string, thresholds: readonly Threshold[]): AttentionCount {\n let flagged = 0;\n let reporting = 0;\n let pending = 0;\n let unread = 0;\n let notMeasured = 0;\n let worst: SeverityLevel | undefined;\n let band: Threshold | undefined;\n let mixed = false;\n for (const m of members) {\n const cell = cellFor(m, measurement);\n if (cell.kind === 'loading') pending++;\n else if (cell.kind === 'failed' || cell.kind === 'unread') unread++;\n else if (cell.kind === 'not-reported') notMeasured++;\n if (cell.kind !== 'value') continue;\n reporting++;\n const hit = bandAt(thresholds, cell.value);\n if (!hit || !isAlarmLevel(hit.level)) continue;\n flagged++;\n if (worst === undefined || SEVERITY_LEVELS.indexOf(hit.level) > SEVERITY_LEVELS.indexOf(worst)) worst = hit.level;\n if (band === undefined) band = hit;\n else if (band !== hit) mixed = true;\n }\n return { members: members.length - notMeasured, notMeasured, flagged, reporting, pending, unread, worst, label: !mixed ? band?.label : undefined };\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// fleet-table -- one row per device of an anchor, one column per measurement. Each cell is that\n// DEVICE's own latest reading (the fleet feed keeps the device beside the value; the measurement\n// channel's name-only `latest` would collapse ten thermostats into one \"temperature\").\n//\n// - a band from the column's thresholds colours the value; the normal band keeps the table's own\n// colour, as everywhere else, so a table of healthy devices is calm;\n// - each cell shows the reading's age, and is called stale only against the configured\n// `staleAfterSeconds` (the freshness convention: no threshold, no stale mark). With\n// `ageLagSeconds` a cell shows its age only when its reading lags the NEWEST reading in the\n// table by at least that much (or is stale): on a fleet that reports every few seconds every\n// age reads \"a few seconds ago\", which is noise that doubles each row, and the frame's footer\n// already carries the fleet's own age. The lag is measured between readings, not against the\n// viewer's clock, so a played-back run (whose readings are all days old) shows the machine\n// that fell silent and no other;\n// - a cell with no number says WHY: still loading, the read failed, the device sent nothing in\n// the range, or its profile does not declare this measurement;\n// - columns sort from their headings (real buttons, so the keyboard works), the device column\n// drills like the alarm table's originator, and a long fleet is paged. A fleet beyond what the\n// resolver reads says \"Showing N of M devices\" rather than pass a subset off as the fleet;\n// - on a narrow board the table scrolls sideways inside its widget and keeps the device column\n// in view.\n\nimport type { FleetMember } from '@devicechain/dashboards';\nimport { useEffect, useMemo, useState, type CSSProperties, type ReactNode } from 'react';\n\nimport {\n ageSeconds,\n bandAt,\n css,\n fontVar,\n formatAge,\n formatValue,\n isStale,\n levelColor,\n optNumber,\n optBoolean,\n optString,\n parseThresholds,\n resolveStaleAfterSeconds,\n spaceVar,\n useSeverityColors,\n useWidgetScale,\n useWidgetSelect,\n useWidgetTimeRange,\n WidgetFrame,\n type Threshold,\n type WidgetProps,\n} from '@devicechain/widget-sdk';\n\nimport {\n DEFAULT_PAGE_SIZE,\n cellFor,\n clampPage,\n columnNames,\n deviceLabel,\n pageCount,\n sortMembers,\n type Cell,\n type SortKey,\n} from '../fleet-model';\nimport { fleetStrings, type FleetStrings } from '../fleet-strings';\nimport { LevelEmpty } from '../level-readout';\n\n// How often the ages are recomputed. Five seconds reads as live for \"12 sec. ago\" and costs a\n// page of cells one render per tick, not per second.\nconst AGE_TICK_MS = 5_000;\n\ninterface ColumnFormat {\n label?: string;\n unit?: string;\n precision?: number;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n// The per-column formatting, by measurement name. Read defensively: a hand-edited definition may\n// carry anything, and an entry that cannot be read drops out rather than take the table down.\nexport function columnFormats(raw: unknown): Map<string, ColumnFormat> {\n const out = new Map<string, ColumnFormat>();\n if (!Array.isArray(raw)) return out;\n for (const item of raw) {\n if (!isRecord(item) || typeof item.measurement !== 'string' || item.measurement === '') continue;\n const f: ColumnFormat = {};\n if (typeof item.label === 'string' && item.label !== '') f.label = item.label;\n if (typeof item.unit === 'string' && item.unit !== '') f.unit = item.unit;\n if (typeof item.precision === 'number' && Number.isInteger(item.precision) && item.precision >= 0 && item.precision <= 100) {\n f.precision = item.precision;\n }\n out.set(item.measurement, f);\n }\n return out;\n}\n\n// The bands of each column: the thresholds list, grouped by the measurement each entry names.\nexport function columnBands(raw: unknown): Map<string, Threshold[]> {\n const grouped = new Map<string, unknown[]>();\n if (Array.isArray(raw)) {\n for (const item of raw) {\n if (!isRecord(item) || typeof item.measurement !== 'string') continue;\n const list = grouped.get(item.measurement) ?? [];\n list.push(item);\n grouped.set(item.measurement, list);\n }\n }\n const out = new Map<string, Threshold[]>();\n for (const [name, items] of grouped) out.set(name, parseThresholds(items));\n return out;\n}\n\nconst cellBox: CSSProperties = { padding: `${spaceVar(1)} ${spaceVar(3)}`, textAlign: 'left', whiteSpace: 'nowrap', verticalAlign: 'top' };\nconst headBox: CSSProperties = {\n ...cellBox,\n fontSize: fontVar('xs'),\n fontWeight: 600,\n letterSpacing: '0.04em',\n color: css('muted-foreground'),\n borderBottom: `1px solid ${css('border')}`,\n background: css('card'),\n position: 'sticky',\n top: 0,\n zIndex: 1,\n};\n// The device column stays in view while the measurement columns scroll under it.\nconst stickyFirst: CSSProperties = { position: 'sticky', left: 0, background: css('card') };\nconst plainButton: CSSProperties = {\n padding: 0,\n border: 'none',\n background: 'none',\n font: 'inherit',\n color: 'inherit',\n cursor: 'pointer',\n textAlign: 'left',\n};\n\nexport function FleetTable({ widget, data }: WidgetProps) {\n const strings = fleetStrings();\n const fleet = data.members;\n const title = optString(widget.options, 'title');\n\n if (!fleet || fleet.status === 'loading') {\n return <Frame title={title}><LevelEmpty message={strings.loading} /></Frame>;\n }\n if (fleet.status === 'unbound') return <Frame title={title}><LevelEmpty message={strings.unbound} /></Frame>;\n if (fleet.status === 'forbidden') return <Frame title={title}><LevelEmpty message={strings.forbidden} /></Frame>;\n if (fleet.status === 'unsupported') return <Frame title={title}><LevelEmpty message={strings.unsupported} /></Frame>;\n if (fleet.members.length === 0) return <Frame title={title}><LevelEmpty message={strings.noMembers} /></Frame>;\n return <Table widget={widget} fleet={fleet} strings={strings} />;\n}\n\n// Below this width (px of the widget body) the table becomes a card per device.\nexport const STACK_BELOW_PX = 480;\n\nfunction Responsive({ wide, narrow }: { wide: ReactNode; narrow: ReactNode }) {\n const { width } = useWidgetScale().size;\n // An unmeasured body (width 0: before the first layout, or under a test renderer) takes the table.\n return width > 0 && width < STACK_BELOW_PX ? narrow : wide;\n}\n\nfunction Frame({ title, children }: { title: string | undefined; children: ReactNode }) {\n return <WidgetFrame title={title} flush>{children}</WidgetFrame>;\n}\n\nfunction Table({\n widget,\n fleet,\n strings,\n}: {\n widget: WidgetProps['widget'];\n fleet: NonNullable<WidgetProps['data']['members']>;\n strings: FleetStrings;\n}) {\n const colors = useSeverityColors();\n const range = useWidgetTimeRange();\n const select = useWidgetSelect();\n const selectionTarget = optString(widget.options, 'selectionTarget');\n const drillTo = select && selectionTarget ? { select, slot: selectionTarget } : undefined;\n\n const columns = useMemo(() => columnNames(widget.datasource?.measurements ?? [], fleet.members), [widget.datasource, fleet.members]);\n const formats = useMemo(() => columnFormats(widget.options?.columns), [widget.options?.columns]);\n const bands = useMemo(() => columnBands(widget.options?.thresholds), [widget.options?.thresholds]);\n const staleAfter = resolveStaleAfterSeconds(widget.options);\n const ageLag = positiveSeconds(optNumber(widget.options, 'ageLagSeconds'));\n // The newest reading among the cells the table shows: what a cell's lag is measured from.\n const newest = useMemo(() => (ageLag === undefined ? undefined : newestShown(fleet.members, columns)), [ageLag, fleet.members, columns]);\n const pageSize = optNumber(widget.options, 'pageSize') ?? DEFAULT_PAGE_SIZE;\n\n // The author's order is the starting point; a click on a heading overrides it for this viewer.\n const authored: SortKey = { by: optString(widget.options, 'sortBy') || null, descending: optBoolean(widget.options, 'sortDescending') === true };\n const [picked, setPicked] = useState<SortKey | undefined>(undefined);\n const sort = picked ?? authored;\n const [page, setPage] = useState(0);\n\n // Ages move with the clock, and stand still at the freeze instant while the board is paused.\n const [tick, setTick] = useState(() => Date.now());\n useEffect(() => {\n const id = setInterval(() => setTick(Date.now()), AGE_TICK_MS);\n return () => clearInterval(id);\n }, []);\n const now = range?.paused ? range.pausedAt ?? tick : tick;\n\n const sorted = useMemo(() => sortMembers(fleet.members, sort), [fleet.members, sort.by, sort.descending]); // eslint-disable-line react-hooks/exhaustive-deps\n const shownPage = clampPage(page, sorted.length, pageSize);\n const rows = sorted.slice(shownPage * pageSize, (shownPage + 1) * pageSize);\n const pages = pageCount(sorted.length, pageSize);\n\n const sortOn = (by: string | null): void => {\n setPicked({ by, descending: sort.by === by ? !sort.descending : false });\n setPage(0);\n };\n const ariaSort = (by: string | null): 'ascending' | 'descending' | 'none' =>\n sort.by === by ? (sort.descending ? 'descending' : 'ascending') : 'none';\n const arrow = (by: string | null): string => (sort.by === by ? (sort.descending ? ' \u25BC' : ' \u25B2') : '');\n\n const wide = (\n <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: fontVar('md'), color: css('foreground') }}>\n <thead>\n <tr>\n <th scope=\"col\" aria-sort={ariaSort(null)} style={{ ...headBox, ...stickyFirst, zIndex: 2 }}>\n <button type=\"button\" style={plainButton} onClick={() => sortOn(null)} title={strings.sortBy(strings.device)}>\n {strings.device}\n {arrow(null)}\n </button>\n </th>\n {columns.map((name) => {\n const f = formats.get(name);\n const heading = (f?.label ?? name) + (f?.unit ? ` (${f.unit})` : '');\n return (\n <th key={name} scope=\"col\" aria-sort={ariaSort(name)} style={headBox}>\n <button type=\"button\" style={plainButton} onClick={() => sortOn(name)} title={strings.sortBy(heading)}>\n {heading}\n {arrow(name)}\n </button>\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody>\n {rows.map((member) => {\n const label = deviceLabel(member);\n return (\n <tr key={member.token} data-device={member.token} style={{ borderBottom: `1px solid ${css('border')}` }}>\n <th scope=\"row\" style={{ ...cellBox, ...stickyFirst, fontWeight: 500 }}>\n {drillTo ? (\n <button\n type=\"button\"\n style={{ ...plainButton, color: css('primary'), textDecoration: 'underline', textUnderlineOffset: 2 }}\n title={strings.drill(label)}\n onClick={() => drillTo.select({ slot: drillTo.slot, binding: { kind: 'device', deviceToken: member.token } })}\n >\n {label}\n </button>\n ) : (\n label\n )}\n </th>\n {columns.map((name) => (\n <td key={name} data-measurement={name} style={cellBox}>\n <CellView\n cell={cellFor(member, name)}\n format={formats.get(name)}\n bands={bands.get(name)}\n colors={colors}\n staleAfter={staleAfter}\n ageLag={ageLag}\n newest={newest}\n now={now}\n strings={strings}\n />\n </td>\n ))}\n </tr>\n );\n })}\n </tbody>\n </table>\n );\n\n // Narrow, the table gives way to a card per device: a column cut off at the right edge is unread\n // on a wall display, where nobody scrolls sideways, so the narrow layout never overflows.\n const narrow = (\n <div data-fleet-stacked=\"\" style={{ color: css('foreground'), fontSize: fontVar('md') }}>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: spaceVar(2), padding: `${spaceVar(2)} ${spaceVar(3)}` }}>\n {[null, ...columns].map((by) => {\n const f = by === null ? undefined : formats.get(by);\n const heading = by === null ? strings.device : (f?.label ?? by);\n return (\n <button\n key={by ?? ''}\n type=\"button\"\n aria-pressed={sort.by === by}\n style={{ ...plainButton, fontSize: fontVar('xs'), fontWeight: 600, color: css('muted-foreground'), overflowWrap: 'anywhere' }}\n onClick={() => sortOn(by)}\n title={strings.sortBy(heading)}\n >\n {heading}\n {arrow(by)}\n </button>\n );\n })}\n </div>\n {rows.map((member) => {\n const label = deviceLabel(member);\n return (\n <div key={member.token} data-device={member.token} style={{ padding: `${spaceVar(2)} ${spaceVar(3)}`, borderTop: `1px solid ${css('border')}` }}>\n <div style={{ fontWeight: 500, overflowWrap: 'anywhere' }}>\n {drillTo ? (\n <button\n type=\"button\"\n style={{ ...plainButton, color: css('primary'), textDecoration: 'underline', textUnderlineOffset: 2, overflowWrap: 'anywhere' }}\n title={strings.drill(label)}\n onClick={() => drillTo.select({ slot: drillTo.slot, binding: { kind: 'device', deviceToken: member.token } })}\n >\n {label}\n </button>\n ) : (\n label\n )}\n </div>\n {columns.map((name) => {\n const f = formats.get(name);\n return (\n <div key={name} data-measurement={name} style={{ display: 'flex', justifyContent: 'space-between', gap: spaceVar(3), alignItems: 'baseline' }}>\n <span style={{ color: css('muted-foreground'), fontSize: fontVar('sm'), overflowWrap: 'anywhere' }}>{f?.label ?? name}</span>\n <span style={{ textAlign: 'right', overflowWrap: 'anywhere' }}>\n <CellView\n cell={cellFor(member, name)}\n format={f}\n bands={bands.get(name)}\n colors={colors}\n staleAfter={staleAfter}\n ageLag={ageLag}\n newest={newest}\n now={now}\n strings={strings}\n wrap\n />\n </span>\n </div>\n );\n })}\n </div>\n );\n })}\n </div>\n );\n\n return (\n <Frame title={optString(widget.options, 'title')}>\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>\n {/* A focusable scroll region: a keyboard user can scroll a wide table sideways. */}\n <div\n role=\"region\"\n aria-label={optString(widget.options, 'title') ?? strings.device}\n tabIndex={0}\n data-fleet-scroll=\"\"\n style={{ flex: 1, minHeight: 0, overflow: 'auto' }}\n >\n <Responsive wide={wide} narrow={narrow} />\n </div>\n {(fleet.truncated || pages > 1) && (\n <div\n data-fleet-footer=\"\"\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: spaceVar(2),\n padding: `${spaceVar(1)} ${spaceVar(3)}`,\n fontSize: fontVar('sm'),\n color: css('muted-foreground'),\n borderTop: `1px solid ${css('border')}`,\n }}\n >\n <span data-fleet-truncated={fleet.truncated ? '' : undefined}>\n {fleet.truncated ? strings.truncated(fleet.members.length, fleet.total) : ''}\n </span>\n {pages > 1 && (\n <span style={{ display: 'inline-flex', alignItems: 'center', gap: spaceVar(2) }}>\n <button type=\"button\" style={pagerButton} disabled={shownPage === 0} onClick={() => setPage(shownPage - 1)} aria-label={strings.previous}>\n \u2039\n </button>\n <span>{strings.range(shownPage * pageSize + 1, shownPage * pageSize + rows.length, sorted.length)}</span>\n <button type=\"button\" style={pagerButton} disabled={shownPage >= pages - 1} onClick={() => setPage(shownPage + 1)} aria-label={strings.next}>\n \u203A\n </button>\n </span>\n )}\n </div>\n )}\n </div>\n </Frame>\n );\n}\n\n// A seconds option that means something: a positive finite number, else unset.\nfunction positiveSeconds(v: number | undefined): number | undefined {\n return v !== undefined && Number.isFinite(v) && v > 0 ? v : undefined;\n}\n\n// The newest reading time (epoch ms) among the shown columns of every member, or undefined.\nexport function newestShown(members: readonly FleetMember[], columns: readonly string[]): number | undefined {\n let newest: number | undefined;\n for (const m of members) {\n for (const name of columns) {\n const t = m.latest[name]?.occurredTime;\n const at = t ? Date.parse(t) : Number.NaN;\n if (!Number.isNaN(at) && (newest === undefined || at > newest)) newest = at;\n }\n }\n return newest;\n}\n\nconst pagerButton: CSSProperties = {\n minWidth: '2em',\n padding: `0 ${spaceVar(2)}`,\n border: `1px solid ${css('border')}`,\n borderRadius: 4,\n background: 'none',\n color: 'inherit',\n font: 'inherit',\n cursor: 'pointer',\n};\n\nfunction CellView({\n cell,\n format,\n bands,\n colors,\n staleAfter,\n ageLag,\n newest,\n now,\n strings,\n wrap,\n}: {\n cell: Cell;\n format: ColumnFormat | undefined;\n bands: Threshold[] | undefined;\n colors: ReturnType<typeof useSeverityColors>;\n staleAfter: number | undefined;\n // Show the age only past this lag behind `newest` (seconds), or when stale; undefined = always.\n ageLag: number | undefined;\n newest: number | undefined;\n now: number;\n strings: FleetStrings;\n // Let the text break (the narrow card) instead of holding one line (the table cell).\n wrap?: boolean;\n}) {\n const muted: CSSProperties = { color: css('muted-foreground'), fontSize: fontVar('sm'), ...(wrap ? { overflowWrap: 'anywhere' as const } : {}) };\n switch (cell.kind) {\n case 'loading':\n return <span style={muted} title={strings.loading} aria-label={strings.loading}>\u2026</span>;\n case 'failed':\n return <span style={muted}>{strings.failed}</span>;\n case 'not-reported':\n return <span style={muted} data-cell-empty=\"not-reported\">{strings.notReported}</span>;\n case 'unread':\n return <span style={muted} data-cell-empty=\"unread\">{strings.unread}</span>;\n case 'no-data':\n return <span style={muted} data-cell-empty=\"no-data\">{strings.noData}</span>;\n case 'value': {\n const band = bands ? bandAt(bands, cell.value) : undefined;\n // The normal band has no colour: the cell keeps the table's own.\n const color = levelColor(colors, band?.level);\n const at = cell.sample.occurredTime ? Date.parse(cell.sample.occurredTime) : Number.NaN;\n const age = Number.isNaN(at) ? undefined : ageSeconds(at, now);\n const stale = age !== undefined && isStale(age, staleAfter);\n const showAge = age !== undefined && (ageLag === undefined || stale || newest === undefined || newest - at >= ageLag * 1000);\n return (\n <span data-stale={stale ? '' : undefined} style={{ display: 'inline-flex', flexDirection: 'column', opacity: stale ? 0.6 : 1 }}>\n <span data-threshold-level={band?.level} style={{ fontVariantNumeric: 'tabular-nums', color }}>\n {formatValue(cell.value, format?.precision)}\n {format?.unit ? ` ${format.unit}` : ''}\n </span>\n {age !== undefined && showAge && (\n <span style={muted} data-cell-age=\"\">\n {formatAge(age)}\n {stale ? ` \u00B7 ${strings.stale}` : ''}\n </span>\n )}\n </span>\n );\n }\n }\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// pie-chart -- each measurement's share of the total of their latest readings. A pie is shares of\n// one whole, so it is only drawn for values of zero or more that add to something: a negative\n// value, or all zeros, is an explained state, never a pie that quietly means something else.\n//\n// Drawn as SVG wedges with a DOM legend that names each category, its value and its share (so\n// every figure is on screen with no pointer to hover, and a category with no reading reads \"\u2014\",\n// not 0 %): no\n// chart type to register (see bar-chart.tsx), and the labels are text the readability gate sees.\n\nimport { useMemo } from 'react';\n\nimport { css, formatValue, optNumber, optString, fontVar, spaceVar, WidgetFrame, type WidgetProps } from '@devicechain/widget-sdk';\n\nimport { aggregateStrings } from '../aggregate-strings';\nimport { categoryItems, parseSort, pieProblem } from '../category-items';\nimport { pieSlices, wedgePath } from '../category-geometry';\nimport { LevelEmpty } from '../level-readout';\n\n// The chart ramp the wedges are drawn from, in order, repeating past five categories (the legend\n// names each one, so a repeated colour is still readable).\nconst RAMP = ['chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5'] as const;\n\nexport function PieChart({ widget, data }: WidgetProps) {\n const sort = parseSort(optString(widget.options, 'sort'));\n const items = useMemo(() => categoryItems(widget, data, sort), [widget, data, sort]);\n const unit = optString(widget.options, 'unit');\n const precision = optNumber(widget.options, 'precision');\n const strings = aggregateStrings();\n const problem = items.length === 0 ? undefined : pieProblem(items);\n return (\n <WidgetFrame title={optString(widget.options, 'title')}>\n {items.length === 0 ? (\n <LevelEmpty message={strings.noCategories} />\n ) : problem ? (\n <LevelEmpty message={problem === 'negative' ? strings.pieNegative : problem === 'noShare' ? strings.pieNoShare : strings.noData} />\n ) : (\n <Pie items={items} unit={unit} precision={precision} />\n )}\n </WidgetFrame>\n );\n}\n\nfunction Pie({\n items,\n unit,\n precision,\n}: {\n items: { label: string; value: number | null }[];\n unit: string | undefined;\n precision: number | undefined;\n}) {\n const slices = pieSlices(items.map((i) => i.value));\n const fmt = (v: number) => `${formatValue(v, precision)}${unit ? ` ${unit}` : ''}`;\n const percent = (fraction: number) => `${Math.round(fraction * 100)}%`;\n return (\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%', gap: spaceVar(1) }}>\n <svg\n data-pie=\"\"\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"xMidYMid meet\"\n role=\"img\"\n aria-label={items.map((i, n) => `${i.label}: ${i.value === null ? '\u2014' : fmt(i.value)} (${percent(slices[n].fraction)})`).join(', ')}\n style={{ flex: '1 1 0', minHeight: 0, width: '100%', display: 'block' }}\n >\n {slices.map((s) => {\n const d = wedgePath(50, 50, 48, s.from, s.to);\n const item = items[s.index];\n return d ? (\n <path\n key={item.label}\n data-slice={item.label}\n data-fraction={s.fraction}\n d={d}\n style={{ fill: css(RAMP[s.index % RAMP.length]), stroke: css('card'), strokeWidth: 1 }}\n >\n <title>{`${item.label}: ${item.value === null ? '\u2014' : fmt(item.value)} (${percent(s.fraction)})`}</title>\n </path>\n ) : null;\n })}\n </svg>\n <ul\n data-pie-legend=\"\"\n style={{\n flex: '0 1 auto',\n maxHeight: '45%',\n overflow: 'hidden',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n display: 'flex',\n flexWrap: 'wrap',\n justifyContent: 'center',\n gap: `0 ${spaceVar(3)}`,\n fontSize: fontVar('xs'),\n color: css('muted-foreground'),\n }}\n >\n {items.map((item, i) => (\n <li key={item.label} title={`${item.label}: ${item.value === null ? '\u2014' : fmt(item.value)}`} style={{ whiteSpace: 'nowrap' }}>\n <span\n aria-hidden=\"true\"\n style={{\n display: 'inline-block',\n width: '0.7em',\n height: '0.7em',\n marginRight: '0.4em',\n borderRadius: 2,\n background: css(RAMP[i % RAMP.length]),\n }}\n />\n {item.label} {item.value === null ? '\u2014' : `${fmt(item.value)} \u00B7 ${percent(slices[i].fraction)}`}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// stat-card -- one statistic of one measurement over a span: its minimum, maximum, average, sum,\n// the number of readings, or its latest reading. The figure is the server's exact aggregate over\n// the span the board is set to (or the span this card is pinned to), and the card says which span\n// beside it. The figure takes the colour of the threshold band it falls in and keeps the\n// foreground in a `normal` band or below the lowest. DOM text, so the readability gate sees it.\n//\n// With `sparkline` set the card also draws the measurement's trend under the figure (sparkline.tsx):\n// neutral, one point per bucket of the span's chart, and left out where it would crowd the figure.\n//\n// Nothing here is derived from averages: a maximum is the largest reading, not the largest bucket\n// average. When the figure cannot be computed the card says why (aggregate-reading.ts).\n\nimport { useMemo } from 'react';\n\nimport { bucketSecondsFor, spanLabel, type AggregateStreamState } from '@devicechain/dashboards';\nimport {\n bandAt,\n css,\n FIT_MIN_FONT_PX,\n fontVar,\n formatValue,\n levelColor,\n optBoolean,\n optNumber,\n optString,\n optThresholds,\n spaceVar,\n useSeverityColors,\n WidgetFrame,\n WidgetFreshnessProvider,\n type WidgetFreshness,\n type WidgetProps,\n} from '@devicechain/widget-sdk';\n\nimport { parseStat, statReading, type StatReading } from '../aggregate-reading';\nimport { aggregateStrings } from '../aggregate-strings';\nimport { LevelEmpty, LevelReadout } from '../level-readout';\nimport { Sparkline, trendPoints } from '../sparkline';\n\n// The room the caption under the figure needs, before the density scale.\nconst CAPTION_ROOM_PX = 22;\n// The strip a card's trend (`sparkline`) takes under the figure, before the density scale.\nconst SPARK_ROOM_PX = 28;\n// The share of the width the figure is fitted to when the caption sits beside it.\nconst BESIDE_FIGURE_SHARE = 0.6;\n\n// The latest reading is aged on screen like every other single reading (\"12 sec. ago\"), and with\n// no threshold of its own it is never called stale -- only its age is shown.\nconst AGE_ONLY: WidgetFreshness = { at: undefined, staleAfterSeconds: undefined };\n\nexport function StatCard({ widget, data }: WidgetProps<AggregateStreamState>) {\n const severity = useSeverityColors();\n const strings = aggregateStrings();\n const stat = parseStat(optString(widget.options, 'stat'));\n const reading = statReading(widget, data, stat);\n const thresholds = useMemo(() => optThresholds(widget.options, 'thresholds'), [widget.options]);\n const unit = optString(widget.options, 'unit');\n const precision = optNumber(widget.options, 'precision');\n const title = optString(widget.options, 'title') ?? reading.name;\n const wantsTrend = optBoolean(widget.options, 'sparkline');\n\n const card = (\n <WidgetFrame title={title} sampleTime={stat === 'last' ? (reading.kind === 'value' ? reading.time ?? null : null) : undefined}>\n {({ size, scale }) => {\n if (reading.kind === 'empty') {\n return <LevelEmpty message={emptyMessage(reading, strings)} />;\n }\n // A count is a number of readings: it has no unit, and no decimals.\n const isCount = reading.stat === 'count';\n const band = bandAt(thresholds, reading.value);\n const room = Math.round(CAPTION_ROOM_PX * scale);\n // The trend strip, when asked for and when it fits. It takes room from the figure only while\n // that leaves the figure two lines of the smallest readable size; otherwise it is left out\n // and the card is exactly the card without it.\n const floor2 = 2 * Math.round(FIT_MIN_FONT_PX * scale);\n const sparkHeight = Math.round(SPARK_ROOM_PX * scale);\n const points = wantsTrend && data.aggregates.status === 'ready' ? trendPoints(data.aggregates.series?.[reading.name ?? ''], reading.stat) : [];\n const showTrend = points.length > 0 && size.height - room - sparkHeight >= floor2;\n // The caption goes UNDER the figure while that leaves the figure two lines of the smallest\n // readable size; below that (the 2-row minimum with a subject line and a freshness footer,\n // at the smallest cell) it goes BESIDE it, so the declared minimum holds the whole card.\n const beside = size.height > 0 && size.height - room < 2 * Math.round(FIT_MIN_FONT_PX * scale);\n const figure = beside\n ? { width: Math.round(size.width * BESIDE_FIGURE_SHARE), height: size.height }\n : { width: size.width, height: Math.max(0, size.height - room - (showTrend ? sparkHeight : 0)) };\n return (\n <div\n data-stat={reading.stat}\n data-stat-span={reading.spanSeconds}\n data-stat-layout={beside ? 'beside' : 'under'}\n data-stat-trend={showTrend ? points.length : undefined}\n data-threshold-level={band?.level}\n title={band?.label}\n style={{\n display: 'flex',\n flexDirection: beside ? 'row' : 'column',\n alignItems: beside ? 'center' : undefined,\n justifyContent: 'safe center',\n gap: beside ? spaceVar(2) : undefined,\n height: '100%',\n overflow: 'hidden',\n }}\n >\n <div\n style={\n beside\n ? { flex: '0 1 auto', minWidth: 0, display: 'flex', alignItems: 'center' }\n : { flex: '1 1 0', minHeight: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }\n }\n >\n <LevelReadout\n text={formatValue(reading.value, isCount ? 0 : precision)}\n unit={isCount ? undefined : unit}\n color={levelColor(severity, band?.level)}\n size={figure}\n scale={scale}\n />\n </div>\n {showTrend && data.aggregates.status === 'ready' ? (\n <Sparkline\n points={points}\n height={sparkHeight}\n bucketSeconds={bucketSecondsFor(data.aggregates.spanSeconds)}\n // The latest reading has no per-bucket counterpart; its strip is the bucket averages.\n label={strings.trend(strings.stats[reading.stat === 'last' ? 'avg' : reading.stat], spanLabel(data.aggregates.spanSeconds))}\n />\n ) : null}\n <div\n data-stat-caption=\"\"\n style={{\n flex: beside ? '0 1 auto' : '0 0 auto',\n minWidth: 0,\n marginTop: beside ? undefined : spaceVar(1),\n textAlign: 'center',\n fontSize: fontVar('sm'),\n color: css('muted-foreground'),\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {caption(reading, strings.stats)}\n </div>\n </div>\n );\n }}\n </WidgetFrame>\n );\n return stat === 'last' ? <WidgetFreshnessProvider freshness={AGE_ONLY}>{card}</WidgetFreshnessProvider> : card;\n}\n\n// \"Maximum \u00B7 1h\": the statistic and the span it covers. The latest reading covers no span.\nfunction caption(r: Extract<StatReading, { kind: 'value' }>, stats: Record<string, string>): string {\n const name = stats[r.stat];\n return r.spanSeconds === undefined ? name : `${name} \u00B7 ${spanLabel(r.spanSeconds)}`;\n}\n\nfunction emptyMessage(r: Extract<StatReading, { kind: 'empty' }>, strings: ReturnType<typeof aggregateStrings>): string {\n switch (r.reason) {\n case 'loading':\n return strings.loading;\n case 'unsupported':\n return strings.unsupported;\n case 'failed':\n return strings.failed(r.message);\n case 'noReadings':\n return strings.noReadings(r.spanSeconds === undefined ? '' : spanLabel(r.spanSeconds));\n case 'noData':\n return strings.noData;\n case 'unbound':\n return strings.unbound;\n }\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// What a stat widget shows, decided once and apart from the drawing: one figure, or the reason\n// there is none. Every path that cannot produce a real figure says so -- nothing here falls back\n// to a neighbouring statistic or to a number it was not given.\n\nimport type { AggregateStreamState, WidgetInstance } from '@devicechain/dashboards';\nimport { pickSample, primaryMeasurementName } from '@devicechain/widget-sdk';\n\nimport { AGGREGATE_STATS, type AggregateStat } from './aggregate-strings';\n\nexport type EmptyReason = 'loading' | 'unsupported' | 'failed' | 'noReadings' | 'noData' | 'unbound';\n\nexport type StatReading =\n | {\n kind: 'value';\n stat: AggregateStat;\n name: string | undefined;\n value: number;\n // The span the figure covers, in seconds; undefined for the latest reading, which has none.\n spanSeconds: number | undefined;\n // The latest reading's own time, which the card ages on screen; undefined for an aggregate.\n time?: string | null;\n }\n | { kind: 'empty'; reason: EmptyReason; name: string | undefined; message?: string; spanSeconds?: number };\n\n// The statistic a widget is set to. Unset or unknown is the average, the one a card is added with.\n// (The widget reads its own option and passes it here: option reads live in the widget's file.)\nexport function parseStat(v: unknown): AggregateStat {\n return AGGREGATE_STATS.find((s) => s === v) ?? 'avg';\n}\n\nexport function statReading(widget: WidgetInstance, data: AggregateStreamState, stat: AggregateStat): StatReading {\n const { aggregates } = data;\n const pinned = primaryMeasurementName(widget);\n // A slot nothing is bound to names no device, so it has no figure -- not even a count of zero,\n // which would say a device was asked and sent nothing. It says so, as the fleet widgets do.\n if (widget.datasource?.kind === 'unbound') return { kind: 'empty', reason: 'unbound', name: pinned };\n\n // The latest reading is a reading, not an aggregate: it needs no aggregate read, and works\n // where the source cannot compute one. When the read did land, a reading older than the span is\n // not \"the latest in the span\", so it is refused rather than shown.\n if (stat === 'last') {\n const sample = pickSample(data.latest, pinned);\n const name = pinned ?? sample?.name;\n if (sample?.value == null) return { kind: 'empty', reason: 'noData', name };\n if (aggregates.status === 'ready' && sample.occurredTime && Date.parse(sample.occurredTime) < Date.parse(aggregates.startTime)) {\n return { kind: 'empty', reason: 'noReadings', name, spanSeconds: aggregates.spanSeconds };\n }\n return { kind: 'value', stat, name, value: sample.value, spanSeconds: undefined, time: sample.occurredTime };\n }\n\n if (aggregates.status === 'loading') return { kind: 'empty', reason: 'loading', name: pinned };\n if (aggregates.status === 'unavailable') {\n return { kind: 'empty', reason: aggregates.reason, name: pinned, message: aggregates.message };\n }\n const name = pinned ?? Object.keys(aggregates.byName)[0];\n const a = name === undefined ? undefined : aggregates.byName[name];\n if (!a) {\n // The number of readings in a span that holds none is a number: zero, which is the figure a\n // \"no readings in the last hour\" tile exists to show. It needs a measurement the card is set\n // to and a device it was read from (an unbound slot was answered above); every other statistic\n // of no readings is undefined.\n if (stat === 'count' && pinned !== undefined && widget.datasource) {\n return { kind: 'value', stat, name: pinned, value: 0, spanSeconds: aggregates.spanSeconds };\n }\n return { kind: 'empty', reason: 'noReadings', name, spanSeconds: aggregates.spanSeconds };\n }\n return { kind: 'value', stat, name, value: a[stat], spanSeconds: aggregates.spanSeconds };\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The trend strip under a stat card's figure: the card's own buckets drawn as one line, so \"is it\n// rising?\" is answered on the card itself. A few lines of SVG, no chart engine, so it adds\n// nothing to the lazy chunk it lives in beyond itself.\n//\n// - it is NEUTRAL: the stroke is the muted foreground, never a threshold colour. The figure\n// carries the band; a trend that turned red as well would say the same thing twice, and a\n// healthy card stays calm (ISA-101);\n// - the x axis is time, not index, and the line breaks where a stretch of buckets is missing, so a\n// device that went silent shows a gap rather than a line drawn across it;\n// - a lone point is a dot, and no point draws nothing (the figure beside it says why);\n// - each point is the bucket's own aggregate for the statistic the card shows. The latest\n// reading has no bucket aggregate, so its strip is the bucket averages: a trend, never a reading.\n\nimport type { AggregateTrendPoint } from '@devicechain/dashboards';\nimport { css } from '@devicechain/widget-sdk';\n\nimport type { AggregateStat } from './aggregate-strings';\n\nexport interface TrendPoint {\n at: number;\n value: number;\n}\n\n// The value a bucket contributes for the statistic the card shows.\nexport function trendValue(point: AggregateTrendPoint, stat: AggregateStat): number {\n switch (stat) {\n case 'min':\n return point.min;\n case 'max':\n return point.max;\n case 'sum':\n return point.sum;\n case 'count':\n return point.count;\n case 'avg':\n case 'last':\n return point.avg;\n }\n}\n\nexport function trendPoints(series: readonly AggregateTrendPoint[] | undefined, stat: AggregateStat): TrendPoint[] {\n if (!series) return [];\n const out: TrendPoint[] = [];\n for (const p of series) {\n const at = Date.parse(p.bucketStart);\n const value = trendValue(p, stat);\n if (Number.isFinite(at) && Number.isFinite(value)) out.push({ at, value });\n }\n return out.sort((a, b) => a.at - b.at);\n}\n\nconst VIEW_WIDTH = 100;\nconst PAD = 3;\n\nexport interface SparklineGeometry {\n // One \"M x y L x y ...\" sub-path per run of consecutive buckets.\n d: string;\n // The newest point as a percentage of the strip, for the end dot.\n end: { left: number; top: number } | undefined;\n points: number;\n}\n\n// sparklineGeometry maps the points into a VIEW_WIDTH x height box. A gap -- a stretch several times\n// longer than the series' usual step -- starts a new run.\nexport function sparklineGeometry(points: readonly TrendPoint[], height: number, bucketSeconds: number): SparklineGeometry {\n if (points.length === 0) return { d: '', end: undefined, points: 0 };\n const t0 = points[0].at;\n const t1 = points[points.length - 1].at;\n let lo = Infinity;\n let hi = -Infinity;\n for (const p of points) {\n lo = Math.min(lo, p.value);\n hi = Math.max(hi, p.value);\n }\n const x = (at: number): number => (t1 === t0 ? VIEW_WIDTH / 2 : PAD + ((at - t0) / (t1 - t0)) * (VIEW_WIDTH - 2 * PAD));\n // A flat series sits mid-strip: there is nothing to scale, and the bottom edge would read as \"low\".\n const y = (v: number): number => (hi === lo ? height / 2 : PAD + (1 - (v - lo) / (hi - lo)) * (height - 2 * PAD));\n // A device that reports every five minutes into one-minute buckets has a point every five\n // buckets, and that is a line, not five gaps. A gap is a stretch several times longer than the\n // series' own usual step (and never under two buckets).\n const steps = points.slice(1).map((p, i) => p.at - points[i].at).sort((a, b) => a - b);\n const usual = steps.length > 0 ? steps[Math.floor(steps.length / 2)] : 0;\n const gap = Math.max(bucketSeconds * 2000, usual * 3);\n let d = '';\n let prev: number | undefined;\n for (const p of points) {\n const cmd = prev === undefined || p.at - prev > gap ? 'M' : 'L';\n d += `${d ? ' ' : ''}${cmd}${x(p.at).toFixed(2)} ${y(p.value).toFixed(2)}`;\n prev = p.at;\n }\n const last = points[points.length - 1];\n return { d, end: { left: (x(last.at) / VIEW_WIDTH) * 100, top: (y(last.value) / height) * 100 }, points: points.length };\n}\n\nexport function Sparkline({\n points,\n height,\n bucketSeconds,\n label,\n}: {\n points: readonly TrendPoint[];\n height: number;\n bucketSeconds: number;\n label: string;\n}) {\n const g = sparklineGeometry(points, height, bucketSeconds);\n if (g.points === 0) return null;\n return (\n <div data-sparkline={g.points} role=\"img\" aria-label={label} title={label} style={{ position: 'relative', height, width: '100%', flex: '0 0 auto' }}>\n <svg viewBox={`0 0 ${VIEW_WIDTH} ${height}`} preserveAspectRatio=\"none\" width=\"100%\" height={height} style={{ display: 'block', overflow: 'visible' }} aria-hidden=\"true\">\n {g.points > 1 ? (\n <path d={g.d} fill=\"none\" stroke={css('muted-foreground')} strokeWidth={1.5} strokeLinejoin=\"round\" strokeLinecap=\"round\" vectorEffect=\"non-scaling-stroke\" />\n ) : null}\n </svg>\n {g.end ? (\n <span\n data-sparkline-end=\"\"\n style={{\n position: 'absolute',\n left: `${g.end.left}%`,\n top: `${g.end.top}%`,\n width: 6,\n height: 6,\n marginLeft: -3,\n marginTop: -3,\n borderRadius: '50%',\n background: css('muted-foreground'),\n }}\n />\n ) : null}\n </div>\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["useMemo", "css", "optString", "WidgetFrame", "jsx", "jsxs", "optString", "useMemo", "WidgetFrame", "caption", "css", "useMemo", "bandAt", "css", "fontVar", "formatValue", "levelColor", "optNumber", "optString", "spaceVar", "useSeverityColors", "WidgetFrame", "jsx", "jsxs", "spaceVar", "fontVar", "css", "optString", "WidgetFrame", "useSeverityColors", "useMemo", "optNumber", "bandAt", "levelColor", "formatValue", "useMemo", "css", "formatValue", "optNumber", "optString", "fontVar", "spaceVar", "WidgetFrame", "jsx", "jsxs", "optString", "useMemo", "optNumber", "WidgetFrame", "formatValue", "spaceVar", "css", "fontVar", "useMemo", "bandAt", "css", "FIT_MIN_FONT_PX", "fontVar", "formatValue", "levelColor", "optBoolean", "optNumber", "optString", "optThresholds", "spaceVar", "useSeverityColors", "WidgetFrame", "primaryMeasurementName", "primaryMeasurementName", "name", "css", "jsx", "jsxs", "jsx", "jsxs", "CAPTION_ROOM_PX", "useSeverityColors", "optString", "useMemo", "optThresholds", "optNumber", "optBoolean", "WidgetFrame", "bandAt", "FIT_MIN_FONT_PX", "spaceVar", "formatValue", "levelColor", "fontVar", "css"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AlarmCount,
|
|
3
|
+
AlarmSeverity,
|
|
4
|
+
AlarmTable,
|
|
5
|
+
severityRank
|
|
6
|
+
} from "./chunk-546TK2QG.js";
|
|
7
|
+
|
|
8
|
+
// src/widgets/alarm-grouped.tsx
|
|
9
|
+
import { useAlarmStream, useWidgetTimeRange } from "@devicechain/widget-sdk";
|
|
10
|
+
import { useMemo, useRef, useState } from "react";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
function foldTypeTabs(counts, selected, names) {
|
|
13
|
+
const byType = /* @__PURE__ */ new Map();
|
|
14
|
+
for (const c of counts) {
|
|
15
|
+
const type = c.deviceType;
|
|
16
|
+
if (c.count <= 0) continue;
|
|
17
|
+
names.set(type.token, type.name);
|
|
18
|
+
const tab = byType.get(type.token) ?? { token: type.token, name: type.name, active: 0, worstSeverity: null };
|
|
19
|
+
tab.active += c.count;
|
|
20
|
+
if (tab.worstSeverity === null || severityRank(c.severity) < severityRank(tab.worstSeverity)) tab.worstSeverity = c.severity;
|
|
21
|
+
byType.set(type.token, tab);
|
|
22
|
+
}
|
|
23
|
+
if (selected !== null && !byType.has(selected)) {
|
|
24
|
+
byType.set(selected, { token: selected, name: names.get(selected) ?? null, active: 0, worstSeverity: null });
|
|
25
|
+
}
|
|
26
|
+
return [...byType.values()].sort(
|
|
27
|
+
(x, y) => severityRank(x.worstSeverity ?? "") - severityRank(y.worstSeverity ?? "") || y.active - x.active || (x.name ?? x.token).localeCompare(y.name ?? y.token)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function GroupedAlarm({ widget, hub, base, useHeld, Component, actions, ErrorFrame }) {
|
|
31
|
+
const range = useWidgetTimeRange();
|
|
32
|
+
const held = range?.paused === true;
|
|
33
|
+
const [selected, setSelected] = useState(null);
|
|
34
|
+
const ready = (s) => !s.loading;
|
|
35
|
+
const summary = useHeld(
|
|
36
|
+
useAlarmStream(hub, {
|
|
37
|
+
datasource: base.datasource,
|
|
38
|
+
severity: base.severity,
|
|
39
|
+
acknowledged: base.acknowledged,
|
|
40
|
+
state: "ACTIVE",
|
|
41
|
+
// No rows are read in count mode; a page of one keeps a source that ignores the flag cheap.
|
|
42
|
+
pageSize: 1,
|
|
43
|
+
countByDeviceType: true
|
|
44
|
+
}),
|
|
45
|
+
held,
|
|
46
|
+
ready
|
|
47
|
+
);
|
|
48
|
+
const names = useRef(/* @__PURE__ */ new Map());
|
|
49
|
+
const counts = summary.typeCounts;
|
|
50
|
+
const tabs = useMemo(() => counts ? foldTypeTabs(counts, selected, names.current) : null, [counts, selected]);
|
|
51
|
+
const filter = tabs ? selected : null;
|
|
52
|
+
const rows = useHeld(
|
|
53
|
+
useAlarmStream(hub, { ...base, enrichDeviceType: true, ...filter !== null ? { deviceType: filter } : {} }),
|
|
54
|
+
held,
|
|
55
|
+
ready
|
|
56
|
+
);
|
|
57
|
+
const typeTabs = useMemo(
|
|
58
|
+
() => tabs ? { tabs, selected, select: setSelected, loading: summary.loading } : void 0,
|
|
59
|
+
[tabs, selected, summary.loading]
|
|
60
|
+
);
|
|
61
|
+
if (rows.error && rows.alarms.length === 0 && rows.total === 0) {
|
|
62
|
+
return /* @__PURE__ */ jsx(ErrorFrame, { widget, error: rows.error });
|
|
63
|
+
}
|
|
64
|
+
return /* @__PURE__ */ jsx(Component, { widget, data: { ...rows, typeTabs }, actions });
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
AlarmCount,
|
|
68
|
+
AlarmSeverity,
|
|
69
|
+
AlarmTable,
|
|
70
|
+
GroupedAlarm
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=alarm-widgets-X4PX5MYJ.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/widgets/alarm-grouped.tsx"],
|
|
4
|
+
"sourcesContent": ["// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The data side of an alarm widget grouped by device type: a tab strip around the alarm table.\n// It lives in the alarm chunk (alarm-widgets.ts) so a board without one never downloads it.\n//\n// TWO streams, never one per tab. A summary (the ACTIVE alarms in scope counted per device type and\n// severity by device-management, in one grouped read) feeds the tab strip's types, counts and worst\n// severities; the rows of the SELECTED tab are a second stream, filtered by type on the server, that\n// re-subscribes only when the selection changes, so a tab's rows are not read until it is chosen.\n// The two classify alarms by the same server-side device read, so a badge and its tab's rows agree.\n// The selection is state here, above both streams, so neither stream's refresh can reset it.\n//\n// A source that cannot count by type (a recording) fails the summary or leaves its counts unset;\n// the strip is then not drawn, and the table shows every alarm in scope, as an ungrouped one does.\n\nimport type {\n AlarmSubscription,\n AlarmTypeCount,\n AlarmTypeTab,\n AlarmTypeTabs,\n WidgetActions,\n WidgetDataSource,\n WidgetInstance,\n} from '@devicechain/dashboards';\nimport { useAlarmStream, useWidgetTimeRange } from '@devicechain/widget-sdk';\nimport { useMemo, useRef, useState, type ComponentType } from 'react';\n\nimport type { useHeld as UseHeld } from '../use-held';\nimport { severityRank } from './severity';\n\n// \uD83D\uDD34 NOTHING IS IMPORTED FROM THE EAGER GRAPH HERE, ONLY TYPES. The bundler gives modules reachable\n// from the same entries one shared chunk, and this chunk's modules are reachable from the package\n// entry (AlarmTable is a public export): a value import of something the connected widget also uses\n// (the pause hold, the subscription policy) would join them to that chunk, which a host's first\n// download then carries whole. So the connected widget hands those in.\nexport interface GroupedAlarmProps {\n hub: WidgetDataSource;\n widget: WidgetInstance;\n // The subscription the widget's descriptor derives (scope, filters, page size).\n base: AlarmSubscription;\n // Holds a stream's snapshot while the board is paused (use-held.ts).\n useHeld: typeof UseHeld;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Component: ComponentType<any>;\n actions: WidgetActions | undefined;\n ErrorFrame: ComponentType<{ widget: WidgetInstance; error: unknown }>;\n}\n\n// foldTypeTabs: one tab per device type among the active-alarm counts, most alarming first (worst\n// severity, then count, then name) so the stream does not reorder as counts move within a severity.\n// An alarm with no type (a non-device originator, or a device the viewer cannot read) is counted by\n// no type and belongs to All only. The SELECTED type stays in the strip even when its last active\n// alarm has cleared, at a zero count.\nexport function foldTypeTabs(\n counts: readonly AlarmTypeCount[],\n selected: string | null,\n names: Map<string, string | null>,\n): AlarmTypeTab[] {\n const byType = new Map<string, AlarmTypeTab>();\n for (const c of counts) {\n const type = c.deviceType;\n if (c.count <= 0) continue;\n names.set(type.token, type.name);\n const tab = byType.get(type.token) ?? { token: type.token, name: type.name, active: 0, worstSeverity: null };\n tab.active += c.count;\n if (tab.worstSeverity === null || severityRank(c.severity) < severityRank(tab.worstSeverity)) tab.worstSeverity = c.severity;\n byType.set(type.token, tab);\n }\n if (selected !== null && !byType.has(selected)) {\n byType.set(selected, { token: selected, name: names.get(selected) ?? null, active: 0, worstSeverity: null });\n }\n return [...byType.values()].sort(\n (x, y) =>\n severityRank(x.worstSeverity ?? '') - severityRank(y.worstSeverity ?? '') ||\n y.active - x.active ||\n (x.name ?? x.token).localeCompare(y.name ?? y.token),\n );\n}\n\nexport function GroupedAlarm({ widget, hub, base, useHeld, Component, actions, ErrorFrame }: GroupedAlarmProps) {\n const range = useWidgetTimeRange();\n const held = range?.paused === true;\n const [selected, setSelected] = useState<string | null>(null);\n const ready = (s: { loading: boolean }) => !s.loading;\n const summary = useHeld(\n useAlarmStream(hub, {\n datasource: base.datasource,\n severity: base.severity,\n acknowledged: base.acknowledged,\n state: 'ACTIVE',\n // No rows are read in count mode; a page of one keeps a source that ignores the flag cheap.\n pageSize: 1,\n countByDeviceType: true,\n }),\n held,\n ready,\n );\n // A type's name is remembered once seen, so a selected tab keeps its label after its last active\n // alarm clears.\n const names = useRef(new Map<string, string | null>());\n const counts = summary.typeCounts;\n const tabs = useMemo(() => (counts ? foldTypeTabs(counts, selected, names.current) : null), [counts, selected]);\n // No counts (a source that cannot make them, a failed first read, or one still loading): no strip,\n // and no type filter either, so the table is every alarm in scope and never a tab nobody can see.\n const filter = tabs ? selected : null;\n const rows = useHeld(\n useAlarmStream(hub, { ...base, enrichDeviceType: true, ...(filter !== null ? { deviceType: filter } : {}) }),\n held,\n ready,\n );\n const typeTabs = useMemo<AlarmTypeTabs | undefined>(\n () => (tabs ? { tabs, selected, select: setSelected, loading: summary.loading } : undefined),\n [tabs, selected, summary.loading],\n );\n\n // Same rule as the plain alarm channel: the error pane only when there is nothing to show.\n if (rows.error && rows.alarms.length === 0 && rows.total === 0) {\n return <ErrorFrame widget={widget} error={rows.error} />;\n }\n return <Component widget={widget} data={{ ...rows, typeTabs }} actions={actions} />;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;AAyBA,SAAS,gBAAgB,0BAA0B;AACnD,SAAS,SAAS,QAAQ,gBAAoC;AA4FnD;AAhEJ,SAAS,aACd,QACA,UACA,OACgB;AAChB,QAAM,SAAS,oBAAI,IAA0B;AAC7C,aAAW,KAAK,QAAQ;AACtB,UAAM,OAAO,EAAE;AACf,QAAI,EAAE,SAAS,EAAG;AAClB,UAAM,IAAI,KAAK,OAAO,KAAK,IAAI;AAC/B,UAAM,MAAM,OAAO,IAAI,KAAK,KAAK,KAAK,EAAE,OAAO,KAAK,OAAO,MAAM,KAAK,MAAM,QAAQ,GAAG,eAAe,KAAK;AAC3G,QAAI,UAAU,EAAE;AAChB,QAAI,IAAI,kBAAkB,QAAQ,aAAa,EAAE,QAAQ,IAAI,aAAa,IAAI,aAAa,EAAG,KAAI,gBAAgB,EAAE;AACpH,WAAO,IAAI,KAAK,OAAO,GAAG;AAAA,EAC5B;AACA,MAAI,aAAa,QAAQ,CAAC,OAAO,IAAI,QAAQ,GAAG;AAC9C,WAAO,IAAI,UAAU,EAAE,OAAO,UAAU,MAAM,MAAM,IAAI,QAAQ,KAAK,MAAM,QAAQ,GAAG,eAAe,KAAK,CAAC;AAAA,EAC7G;AACA,SAAO,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE;AAAA,IAC1B,CAAC,GAAG,MACF,aAAa,EAAE,iBAAiB,EAAE,IAAI,aAAa,EAAE,iBAAiB,EAAE,KACxE,EAAE,SAAS,EAAE,WACZ,EAAE,QAAQ,EAAE,OAAO,cAAc,EAAE,QAAQ,EAAE,KAAK;AAAA,EACvD;AACF;AAEO,SAAS,aAAa,EAAE,QAAQ,KAAK,MAAM,SAAS,WAAW,SAAS,WAAW,GAAsB;AAC9G,QAAM,QAAQ,mBAAmB;AACjC,QAAM,OAAO,OAAO,WAAW;AAC/B,QAAM,CAAC,UAAU,WAAW,IAAI,SAAwB,IAAI;AAC5D,QAAM,QAAQ,CAAC,MAA4B,CAAC,EAAE;AAC9C,QAAM,UAAU;AAAA,IACd,eAAe,KAAK;AAAA,MAClB,YAAY,KAAK;AAAA,MACjB,UAAU,KAAK;AAAA,MACf,cAAc,KAAK;AAAA,MACnB,OAAO;AAAA;AAAA,MAEP,UAAU;AAAA,MACV,mBAAmB;AAAA,IACrB,CAAC;AAAA,IACD;AAAA,IACA;AAAA,EACF;AAGA,QAAM,QAAQ,OAAO,oBAAI,IAA2B,CAAC;AACrD,QAAM,SAAS,QAAQ;AACvB,QAAM,OAAO,QAAQ,MAAO,SAAS,aAAa,QAAQ,UAAU,MAAM,OAAO,IAAI,MAAO,CAAC,QAAQ,QAAQ,CAAC;AAG9G,QAAM,SAAS,OAAO,WAAW;AACjC,QAAM,OAAO;AAAA,IACX,eAAe,KAAK,EAAE,GAAG,MAAM,kBAAkB,MAAM,GAAI,WAAW,OAAO,EAAE,YAAY,OAAO,IAAI,CAAC,EAAG,CAAC;AAAA,IAC3G;AAAA,IACA;AAAA,EACF;AACA,QAAM,WAAW;AAAA,IACf,MAAO,OAAO,EAAE,MAAM,UAAU,QAAQ,aAAa,SAAS,QAAQ,QAAQ,IAAI;AAAA,IAClF,CAAC,MAAM,UAAU,QAAQ,OAAO;AAAA,EAClC;AAGA,MAAI,KAAK,SAAS,KAAK,OAAO,WAAW,KAAK,KAAK,UAAU,GAAG;AAC9D,WAAO,oBAAC,cAAW,QAAgB,OAAO,KAAK,OAAO;AAAA,EACxD;AACA,SAAO,oBAAC,aAAU,QAAgB,MAAM,EAAE,GAAG,MAAM,SAAS,GAAG,SAAkB;AACnF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|