@devicechain/widgets 0.21.1-rc.1 → 0.22.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 +28 -7
- package/dist/aggregate-feed.d.ts +14 -0
- package/dist/aggregate-fold.d.ts +11 -0
- package/dist/aggregate-reading.d.ts +19 -0
- package/dist/aggregate-strings.d.ts +16 -0
- package/dist/binding-policy.d.ts +3 -1
- package/dist/builtin-descriptors.d.ts +1210 -8
- package/dist/builtin-registry.d.ts +1197 -8
- package/dist/category-geometry.d.ts +19 -0
- package/dist/category-items.d.ts +10 -0
- package/dist/chunks/aggregate-feed-6ZYKOQOB.js +130 -0
- package/dist/chunks/aggregate-feed-6ZYKOQOB.js.map +7 -0
- package/dist/chunks/aggregate-widgets-WVXAW3UL.js +895 -0
- package/dist/chunks/aggregate-widgets-WVXAW3UL.js.map +7 -0
- package/dist/chunks/chunk-2D7JNKFE.js +65 -0
- package/dist/chunks/chunk-2D7JNKFE.js.map +7 -0
- package/dist/chunks/chunk-S2OMBJLI.js +200 -0
- package/dist/chunks/chunk-S2OMBJLI.js.map +7 -0
- package/dist/chunks/chunk-XQQQNTBX.js +76 -0
- package/dist/chunks/chunk-XQQQNTBX.js.map +7 -0
- package/dist/chunks/chunk-YPFB6DAL.js +157 -0
- package/dist/chunks/chunk-YPFB6DAL.js.map +7 -0
- package/dist/chunks/chunk-ZR74ORIT.js +35 -0
- package/dist/chunks/chunk-ZR74ORIT.js.map +7 -0
- package/dist/chunks/control-widgets-UDS4ET3T.js +775 -0
- package/dist/chunks/control-widgets-UDS4ET3T.js.map +7 -0
- package/dist/chunks/fleet-feed-FK4ZMKIK.js +252 -0
- package/dist/chunks/fleet-feed-FK4ZMKIK.js.map +7 -0
- package/dist/chunks/level-widgets-MJB7TOLU.js +833 -0
- package/dist/chunks/level-widgets-MJB7TOLU.js.map +7 -0
- package/dist/command-control-core.d.ts +68 -0
- package/dist/command-control-strings.d.ts +32 -0
- package/dist/command-control.d.ts +31 -0
- package/dist/dashboard-renderer.d.ts +15 -4
- package/dist/fleet-feed.d.ts +21 -0
- package/dist/fleet-model.d.ts +43 -0
- package/dist/fleet-seed.d.ts +7 -0
- package/dist/fleet-strings.d.ts +25 -0
- package/dist/heading.d.ts +3 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1774 -676
- package/dist/index.js.map +4 -4
- package/dist/level-reading.d.ts +29 -0
- package/dist/level-readout.d.ts +25 -0
- package/dist/level-strings.d.ts +13 -0
- package/dist/options.d.ts +1 -1
- package/dist/time-range-bar.d.ts +28 -0
- package/dist/use-held.d.ts +1 -0
- package/dist/value-scale.d.ts +30 -0
- package/dist/widgets/aggregate-lazy.d.ts +305 -0
- package/dist/widgets/aggregate-widgets.d.ts +4 -0
- package/dist/widgets/alarm-count.d.ts +1 -0
- package/dist/widgets/alarm-table.d.ts +4 -1
- package/dist/widgets/bar-chart.d.ts +5 -0
- package/dist/widgets/battery.d.ts +7 -0
- package/dist/widgets/command-slider.d.ts +2 -0
- package/dist/widgets/command-toggle.d.ts +4 -0
- package/dist/widgets/compass.d.ts +2 -0
- package/dist/widgets/control-lazy.d.ts +92 -0
- package/dist/widgets/control-widgets.d.ts +2 -0
- package/dist/widgets/fleet-table.d.ts +13 -0
- package/dist/widgets/gauge.d.ts +52 -0
- package/dist/widgets/label.d.ts +1 -1
- package/dist/widgets/latest-card.d.ts +44 -1
- package/dist/widgets/lazy-chunk.d.ts +8 -0
- package/dist/widgets/level-gauge.d.ts +2 -0
- package/dist/widgets/level-lazy.d.ts +647 -0
- package/dist/widgets/level-widgets.d.ts +7 -0
- package/dist/widgets/linear-gauge.d.ts +2 -0
- package/dist/widgets/pie-chart.d.ts +2 -0
- package/dist/widgets/progress-ring.d.ts +3 -0
- package/dist/widgets/severity.d.ts +2 -1
- package/dist/widgets/signal-bars.d.ts +3 -0
- package/dist/widgets/stat-card.d.ts +3 -0
- package/dist/widgets/status-light.d.ts +7 -0
- package/dist/widgets/table.d.ts +9 -1
- package/dist/widgets/time-series-chart.d.ts +62 -4
- package/package.json +8 -8
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/widgets/bar-chart.tsx", "../../src/aggregate-strings.ts", "../../src/category-items.ts", "../../src/category-geometry.ts", "../../src/widgets/fleet-table.tsx", "../../src/widgets/pie-chart.tsx", "../../src/widgets/stat-card.tsx", "../../src/aggregate-reading.ts"],
|
|
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 // 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 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 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// The arithmetic of the bar and pie charts, apart from the drawing: a value axis that rounds out\n// to tidy ticks, and the angles of a pie's slices. Pure, so it is tested without a DOM.\n\nexport interface NiceDomain {\n min: number;\n max: number;\n step: number;\n ticks: number[];\n}\n\n// niceDomain returns an axis that holds every value and zero (a bar starts at zero, so the axis\n// always includes it), rounded out to a 1, 2, 2.5 or 5 step so the ticks read as round numbers. All\n// zeros give 0 to 1, so an empty chart still has an axis. Non-finite values are ignored.\nexport function niceDomain(values: readonly number[], intervals = 4): NiceDomain {\n const finite = values.filter(Number.isFinite);\n let lo = Math.min(0, ...finite);\n let hi = Math.max(0, ...finite);\n if (lo === hi) hi = lo + 1;\n const raw = (hi - lo) / Math.max(1, intervals);\n const magnitude = 10 ** Math.floor(Math.log10(raw));\n const norm = raw / magnitude;\n const step = (norm <= 1 ? 1 : norm <= 2 ? 2 : norm <= 2.5 ? 2.5 : norm <= 5 ? 5 : 10) * magnitude;\n lo = Math.floor(lo / step + 1e-9) * step;\n hi = Math.ceil(hi / step - 1e-9) * step;\n // Rounded to the step's own decimals so 0.1 + 0.2 does not become a tick label.\n const decimals = Math.max(0, -Math.floor(Math.log10(step)) + 1);\n const round = (v: number) => Number(v.toFixed(decimals));\n const ticks: number[] = [];\n for (let i = 0; lo + i * step <= hi + step * 1e-9; i++) ticks.push(round(lo + i * step));\n return { min: round(lo), max: round(hi), step, ticks };\n}\n\nexport interface Slice {\n index: number;\n // The share of the whole, 0 to 1.\n fraction: number;\n // Clockwise from twelve o'clock, in turns (0 to 1).\n from: number;\n to: number;\n}\n\n// pieSlices lays the values out clockwise from the top, in the order given. A value that is null,\n// zero or negative takes no angle (it keeps its place in the list with a fraction of 0), so the\n// angles of the rest are shares of the sum of the positive ones.\nexport function pieSlices(values: readonly (number | null)[]): Slice[] {\n const positive = values.map((v) => (v !== null && v > 0 ? v : 0));\n const total = positive.reduce((a, v) => a + v, 0);\n let at = 0;\n return positive.map((v, index) => {\n const fraction = total > 0 ? v / total : 0;\n const slice = { index, fraction, from: at, to: at + fraction };\n at += fraction;\n return slice;\n });\n}\n\n// The point on a circle of radius r about (cx, cy), `turns` clockwise from twelve o'clock.\nexport function polar(cx: number, cy: number, r: number, turns: number): { x: number; y: number } {\n const angle = turns * 2 * Math.PI;\n return { x: cx + r * Math.sin(angle), y: cy - r * Math.cos(angle) };\n}\n\n// The SVG path of one wedge. A slice that fills the circle is a circle (an arc from a point to\n// itself draws nothing), and an empty one has no path at all.\nexport function wedgePath(cx: number, cy: number, r: number, from: number, to: number): string | undefined {\n const span = to - from;\n if (span <= 0) return undefined;\n if (span >= 1 - 1e-9) {\n return `M${cx},${cy - r}A${r},${r} 0 1 1 ${cx},${cy + r}A${r},${r} 0 1 1 ${cx},${cy - r}Z`;\n }\n const a = polar(cx, cy, r, from);\n const b = polar(cx, cy, r, to);\n return `M${cx},${cy}L${a.x},${a.y}A${r},${r} 0 ${span > 0.5 ? 1 : 0} 1 ${b.x},${b.y}Z`;\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 it sent other measurements but not this one;\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// 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 { spanLabel, type AggregateStreamState } from '@devicechain/dashboards';\nimport {\n bandAt,\n css,\n FIT_MIN_FONT_PX,\n fontVar,\n formatValue,\n levelColor,\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';\n\n// The room the caption under the figure needs, before the density scale.\nconst CAPTION_ROOM_PX = 22;\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\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 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) };\n return (\n <div\n data-stat={reading.stat}\n data-stat-span={reading.spanSeconds}\n data-stat-layout={beside ? 'beside' : 'under'}\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 <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"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["useMemo", "css", "formatValue", "optNumber", "optString", "WidgetFrame", "jsx", "jsxs", "css", "optString", "WidgetFrame", "useMemo", "optNumber", "formatValue", "useMemo", "css", "formatValue", "optNumber", "optString", "fontVar", "spaceVar", "WidgetFrame", "jsx", "jsxs", "optString", "useMemo", "optNumber", "WidgetFrame", "formatValue", "spaceVar", "css", "fontVar", "useMemo", "bandAt", "css", "fontVar", "formatValue", "levelColor", "optNumber", "optString", "spaceVar", "useSeverityColors", "WidgetFrame", "name", "jsx", "jsxs", "useSeverityColors", "optString", "useMemo", "optNumber", "WidgetFrame", "bandAt", "spaceVar", "formatValue", "levelColor", "fontVar", "css"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/binding-policy.ts
|
|
2
|
+
import {
|
|
3
|
+
isValidSpanSeconds
|
|
4
|
+
} from "@devicechain/dashboards";
|
|
5
|
+
import { optNumber, optString } from "@devicechain/widget-sdk";
|
|
6
|
+
function numberValue(binding, options) {
|
|
7
|
+
if (!binding) return void 0;
|
|
8
|
+
let value;
|
|
9
|
+
if (typeof binding.const === "number") value = binding.const;
|
|
10
|
+
else if (binding.option !== void 0) value = optNumber(options, binding.option) ?? binding.default;
|
|
11
|
+
if (value === void 0) return void 0;
|
|
12
|
+
return binding.min !== void 0 ? Math.max(binding.min, value) : value;
|
|
13
|
+
}
|
|
14
|
+
function stringValue(binding, options) {
|
|
15
|
+
if (!binding || binding.option === void 0) return void 0;
|
|
16
|
+
return optString(options, binding.option) ?? binding.default;
|
|
17
|
+
}
|
|
18
|
+
function policy(descriptor) {
|
|
19
|
+
return descriptor.binding;
|
|
20
|
+
}
|
|
21
|
+
function measurementWindow(descriptor, widget) {
|
|
22
|
+
return numberValue(policy(descriptor).window, widget.options);
|
|
23
|
+
}
|
|
24
|
+
function measurementSpan(descriptor, widget, boardSpan) {
|
|
25
|
+
const binding = policy(descriptor).windowSeconds;
|
|
26
|
+
if (!binding) return void 0;
|
|
27
|
+
const pinned = numberValue({ ...binding, min: void 0 }, widget.options);
|
|
28
|
+
return isValidSpanSeconds(pinned) ? pinned : boardSpan;
|
|
29
|
+
}
|
|
30
|
+
function membersMode(descriptor, widget) {
|
|
31
|
+
const members = policy(descriptor).members;
|
|
32
|
+
if (!members) return false;
|
|
33
|
+
if (members.const === true) return true;
|
|
34
|
+
return members.option !== void 0 && optString(widget.options, members.option) === members.equals;
|
|
35
|
+
}
|
|
36
|
+
function parseAcknowledged(value) {
|
|
37
|
+
if (value === "true") return true;
|
|
38
|
+
if (value === "false") return false;
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
function alarmSubscription(descriptor, widget) {
|
|
42
|
+
const p = policy(descriptor);
|
|
43
|
+
return {
|
|
44
|
+
datasource: widget.datasource,
|
|
45
|
+
state: stringValue(p.state, widget.options),
|
|
46
|
+
severity: stringValue(p.severity, widget.options),
|
|
47
|
+
acknowledged: parseAcknowledged(stringValue(p.acknowledged, widget.options)),
|
|
48
|
+
pageSize: numberValue(p.pageSize, widget.options) ?? 1
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function commandSubscription(descriptor, widget) {
|
|
52
|
+
return {
|
|
53
|
+
datasource: widget.datasource,
|
|
54
|
+
pageSize: numberValue(policy(descriptor).pageSize, widget.options) ?? 1
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
measurementWindow,
|
|
60
|
+
measurementSpan,
|
|
61
|
+
membersMode,
|
|
62
|
+
alarmSubscription,
|
|
63
|
+
commandSubscription
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=chunk-2D7JNKFE.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/binding-policy.ts"],
|
|
4
|
+
"sourcesContent": ["// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The channel adapters' half of a widget's declarative binding policy: turn the descriptor's\n// `binding` (typed constants and references to declared option keys) plus the widget's stored\n// options into the subscription arguments a hub channel takes. This is the ONLY place that\n// reads a policy-referenced option, so it reads them by the NAME the descriptor declares \u2014\n// no widget id is consulted, and an extension that declares `{ option: 'rows' }` is honored\n// exactly as a built-in declaring `{ option: 'maxRows' }` is.\n\nimport {\n isValidSpanSeconds,\n type AlarmSubscription,\n type CommandSubscription,\n type WidgetDescriptor,\n type WidgetInstance,\n} from '@devicechain/dashboards';\n\nimport { optNumber, optString } from '@devicechain/widget-sdk';\n\n// The shapes a policy value takes at runtime (the descriptor is validated at registration,\n// so a ref names a declared option of a compatible kind; the value stored in a widget may\n// still be absent or the wrong type, in which case the ref's default applies).\ninterface NumberBinding {\n const?: number;\n option?: string;\n default?: number;\n min?: number;\n}\ninterface StringBinding {\n option?: string;\n default?: string;\n}\n\nfunction numberValue(binding: NumberBinding | undefined, options: WidgetInstance['options']): number | undefined {\n if (!binding) return undefined;\n let value: number | undefined;\n if (typeof binding.const === 'number') value = binding.const;\n else if (binding.option !== undefined) value = optNumber(options, binding.option) ?? binding.default;\n if (value === undefined) return undefined;\n return binding.min !== undefined ? Math.max(binding.min, value) : value;\n}\n\nfunction stringValue(binding: StringBinding | undefined, options: WidgetInstance['options']): string | undefined {\n if (!binding || binding.option === undefined) return undefined;\n return optString(options, binding.option) ?? binding.default;\n}\n\nfunction policy(descriptor: WidgetDescriptor): Record<string, unknown> {\n return descriptor.binding as Record<string, unknown>;\n}\n\n// The rolling-window size a measurement widget asked for, or undefined for the hook default.\nexport function measurementWindow(descriptor: WidgetDescriptor, widget: WidgetInstance): number | undefined {\n return numberValue(policy(descriptor).window as NumberBinding | undefined, widget.options);\n}\n\n// The span (seconds) a TIME-RANGED measurement widget draws, or undefined for one that is not.\n// A widget is time-ranged by declaring `binding.windowSeconds`. Its option pins the span when set\n// to a usable one; otherwise the widget follows the board's range (`boardSpan`, which a viewer\n// can change). An option that is set but not a usable span (under a minute, over a week, not a\n// whole number) is ignored rather than clamped, for the reason the parser gives for the board's\n// own default: clamping turns a typo into a window nobody wrote.\nexport function measurementSpan(\n descriptor: WidgetDescriptor,\n widget: WidgetInstance,\n boardSpan: number | undefined,\n): number | undefined {\n const binding = policy(descriptor).windowSeconds as NumberBinding | undefined;\n if (!binding) return undefined;\n const pinned = numberValue({ ...binding, min: undefined }, widget.options);\n return isValidSpanSeconds(pinned) ? pinned : boardSpan;\n}\n\n// Whether a measurement widget reads its datasource MEMBER BY MEMBER (a fleet widget: a row or a\n// bar per device of an anchor) rather than as one stream keyed by measurement name. The descriptor\n// says so with `binding.members`: always (`const`), or when a declared option holds a given value\n// (a bar chart set to one bar per device).\nexport function membersMode(descriptor: WidgetDescriptor, widget: WidgetInstance): boolean {\n const members = policy(descriptor).members as { const?: boolean; option?: string; equals?: string } | undefined;\n if (!members) return false;\n if (members.const === true) return true;\n return members.option !== undefined && optString(widget.options, members.option) === members.equals;\n}\n\n// The acknowledged filter is stored as 'true' / 'false' (or absent = any).\nfunction parseAcknowledged(value: string | undefined): boolean | undefined {\n if (value === 'true') return true;\n if (value === 'false') return false;\n return undefined;\n}\n\n// An alarm widget's scope + filters. The datasource (undefined = tenant-wide) is the\n// renderer's; the page size and filters come from the policy.\nexport function alarmSubscription(descriptor: WidgetDescriptor, widget: WidgetInstance): AlarmSubscription {\n const p = policy(descriptor);\n return {\n datasource: widget.datasource,\n state: stringValue(p.state as StringBinding | undefined, widget.options),\n severity: stringValue(p.severity as StringBinding | undefined, widget.options),\n acknowledged: parseAcknowledged(stringValue(p.acknowledged as StringBinding | undefined, widget.options)),\n pageSize: numberValue(p.pageSize as NumberBinding | undefined, widget.options) ?? 1,\n };\n}\n\n// A command widget's target scope and how many recent commands its history shows.\nexport function commandSubscription(descriptor: WidgetDescriptor, widget: WidgetInstance): CommandSubscription {\n return {\n datasource: widget.datasource,\n pageSize: numberValue(policy(descriptor).pageSize as NumberBinding | undefined, widget.options) ?? 1,\n };\n}\n"],
|
|
5
|
+
"mappings": ";AAUA;AAAA,EACE;AAAA,OAKK;AAEP,SAAS,WAAW,iBAAiB;AAgBrC,SAAS,YAAY,SAAoC,SAAwD;AAC/G,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACJ,MAAI,OAAO,QAAQ,UAAU,SAAU,SAAQ,QAAQ;AAAA,WAC9C,QAAQ,WAAW,OAAW,SAAQ,UAAU,SAAS,QAAQ,MAAM,KAAK,QAAQ;AAC7F,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO,QAAQ,QAAQ,SAAY,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI;AACpE;AAEA,SAAS,YAAY,SAAoC,SAAwD;AAC/G,MAAI,CAAC,WAAW,QAAQ,WAAW,OAAW,QAAO;AACrD,SAAO,UAAU,SAAS,QAAQ,MAAM,KAAK,QAAQ;AACvD;AAEA,SAAS,OAAO,YAAuD;AACrE,SAAO,WAAW;AACpB;AAGO,SAAS,kBAAkB,YAA8B,QAA4C;AAC1G,SAAO,YAAY,OAAO,UAAU,EAAE,QAAqC,OAAO,OAAO;AAC3F;AAQO,SAAS,gBACd,YACA,QACA,WACoB;AACpB,QAAM,UAAU,OAAO,UAAU,EAAE;AACnC,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,SAAS,YAAY,EAAE,GAAG,SAAS,KAAK,OAAU,GAAG,OAAO,OAAO;AACzE,SAAO,mBAAmB,MAAM,IAAI,SAAS;AAC/C;AAMO,SAAS,YAAY,YAA8B,QAAiC;AACzF,QAAM,UAAU,OAAO,UAAU,EAAE;AACnC,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,QAAQ,UAAU,KAAM,QAAO;AACnC,SAAO,QAAQ,WAAW,UAAa,UAAU,OAAO,SAAS,QAAQ,MAAM,MAAM,QAAQ;AAC/F;AAGA,SAAS,kBAAkB,OAAgD;AACzE,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,UAAU,QAAS,QAAO;AAC9B,SAAO;AACT;AAIO,SAAS,kBAAkB,YAA8B,QAA2C;AACzG,QAAM,IAAI,OAAO,UAAU;AAC3B,SAAO;AAAA,IACL,YAAY,OAAO;AAAA,IACnB,OAAO,YAAY,EAAE,OAAoC,OAAO,OAAO;AAAA,IACvE,UAAU,YAAY,EAAE,UAAuC,OAAO,OAAO;AAAA,IAC7E,cAAc,kBAAkB,YAAY,EAAE,cAA2C,OAAO,OAAO,CAAC;AAAA,IACxG,UAAU,YAAY,EAAE,UAAuC,OAAO,OAAO,KAAK;AAAA,EACpF;AACF;AAGO,SAAS,oBAAoB,YAA8B,QAA6C;AAC7G,SAAO;AAAA,IACL,YAAY,OAAO;AAAA,IACnB,UAAU,YAAY,OAAO,UAAU,EAAE,UAAuC,OAAO,OAAO,KAAK;AAAA,EACrG;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// src/fleet-model.ts
|
|
2
|
+
import {
|
|
3
|
+
bucketSecondsFor
|
|
4
|
+
} from "@devicechain/dashboards";
|
|
5
|
+
var MINUTE_MS = 6e4;
|
|
6
|
+
function seedWindow(spanSeconds, at) {
|
|
7
|
+
const end = Math.floor(at / MINUTE_MS) * MINUTE_MS;
|
|
8
|
+
const start = Math.ceil((end - spanSeconds * 1e3) / MINUTE_MS) * MINUTE_MS;
|
|
9
|
+
return {
|
|
10
|
+
startTime: new Date(start).toISOString(),
|
|
11
|
+
endTime: new Date(end - 1).toISOString(),
|
|
12
|
+
intervalSeconds: bucketSecondsFor(Math.max(60, (end - start) / 1e3))
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function timeOf(sample) {
|
|
16
|
+
if (!sample?.occurredTime) return Number.NaN;
|
|
17
|
+
return Date.parse(sample.occurredTime);
|
|
18
|
+
}
|
|
19
|
+
function inRange(sample, since) {
|
|
20
|
+
const at = timeOf(sample);
|
|
21
|
+
return Number.isNaN(at) || at >= since;
|
|
22
|
+
}
|
|
23
|
+
function newer(a, b) {
|
|
24
|
+
if (!a) return b;
|
|
25
|
+
if (!b) return a;
|
|
26
|
+
const ta = timeOf(a);
|
|
27
|
+
const tb = timeOf(b);
|
|
28
|
+
if (Number.isNaN(ta)) return a;
|
|
29
|
+
if (Number.isNaN(tb)) return b;
|
|
30
|
+
return tb >= ta ? b : a;
|
|
31
|
+
}
|
|
32
|
+
function buildFleet(i) {
|
|
33
|
+
if (i.forbidden) return { status: "forbidden", members: [], total: 0, truncated: false };
|
|
34
|
+
if (!i.roster) return { status: "loading", members: [], total: 0, truncated: false };
|
|
35
|
+
if (i.roster.unbound) return { status: "unbound", members: [], total: 0, truncated: false };
|
|
36
|
+
const seedSince = Math.floor(i.since / MINUTE_MS) * MINUTE_MS;
|
|
37
|
+
const members = i.roster.members.map((ref) => {
|
|
38
|
+
const seed = i.seeds.get(ref.token);
|
|
39
|
+
const seeded = seed && seed !== "failed" ? seed : void 0;
|
|
40
|
+
const live = i.live.get(ref.token);
|
|
41
|
+
const latest = {};
|
|
42
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(seeded?.latest ?? {}), ...live?.keys() ?? []]);
|
|
43
|
+
for (const name of names) {
|
|
44
|
+
const best = newer(seeded?.latest[name], live?.get(name));
|
|
45
|
+
if (best && inRange(best, best === seeded?.latest[name] ? seedSince : i.since)) latest[name] = best;
|
|
46
|
+
}
|
|
47
|
+
const observed = new Set(seeded?.observed ?? []);
|
|
48
|
+
for (const name of Object.keys(latest)) observed.add(name);
|
|
49
|
+
return {
|
|
50
|
+
token: ref.token,
|
|
51
|
+
name: ref.name,
|
|
52
|
+
latest,
|
|
53
|
+
seed: seed === void 0 ? "pending" : seed === "failed" ? "failed" : "ready",
|
|
54
|
+
observed: [...observed],
|
|
55
|
+
// A live reading of an unread name is its value: cellFor looks at `latest` first.
|
|
56
|
+
...seeded?.unread?.length ? { unread: seeded.unread } : {}
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
status: "ready",
|
|
61
|
+
members,
|
|
62
|
+
total: i.roster.total,
|
|
63
|
+
truncated: i.roster.total > members.length
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function newestMemberTime(fleet) {
|
|
67
|
+
let newest;
|
|
68
|
+
for (const m of fleet.members) {
|
|
69
|
+
for (const sample of Object.values(m.latest)) {
|
|
70
|
+
const at = timeOf(sample);
|
|
71
|
+
if (!Number.isNaN(at) && (newest === void 0 || at > newest)) newest = at;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return newest;
|
|
75
|
+
}
|
|
76
|
+
function cellFor(member, measurement) {
|
|
77
|
+
const sample = member.latest[measurement];
|
|
78
|
+
if (sample && typeof sample.value === "number" && Number.isFinite(sample.value)) {
|
|
79
|
+
return { kind: "value", sample, value: sample.value };
|
|
80
|
+
}
|
|
81
|
+
if (member.seed === "pending") return { kind: "loading" };
|
|
82
|
+
if (member.seed === "failed") return { kind: "failed" };
|
|
83
|
+
if (member.unread?.includes(measurement)) return { kind: "unread" };
|
|
84
|
+
return member.observed.length > 0 && !member.observed.includes(measurement) ? { kind: "not-reported" } : { kind: "no-data" };
|
|
85
|
+
}
|
|
86
|
+
function columnNames(named, members) {
|
|
87
|
+
if (named.length > 0) return [...new Set(named)];
|
|
88
|
+
const seen = /* @__PURE__ */ new Set();
|
|
89
|
+
for (const m of members) for (const name of m.observed) seen.add(name);
|
|
90
|
+
return [...seen].sort((a, b) => a.localeCompare(b));
|
|
91
|
+
}
|
|
92
|
+
function deviceLabel(member) {
|
|
93
|
+
return member.name && member.name !== "" ? member.name : member.token;
|
|
94
|
+
}
|
|
95
|
+
var collator = new Intl.Collator(void 0, { numeric: true, sensitivity: "base" });
|
|
96
|
+
function sortMembers(members, key) {
|
|
97
|
+
const byDevice = (a, b) => collator.compare(deviceLabel(a), deviceLabel(b)) || (a.token < b.token ? -1 : a.token > b.token ? 1 : 0);
|
|
98
|
+
const sign = key.descending ? -1 : 1;
|
|
99
|
+
return [...members].sort((a, b) => {
|
|
100
|
+
if (key.by === null) return sign * byDevice(a, b);
|
|
101
|
+
const ca = cellFor(a, key.by);
|
|
102
|
+
const cb = cellFor(b, key.by);
|
|
103
|
+
if (ca.kind !== "value" || cb.kind !== "value") {
|
|
104
|
+
if (ca.kind === cb.kind) return byDevice(a, b);
|
|
105
|
+
return ca.kind === "value" ? -1 : cb.kind === "value" ? 1 : byDevice(a, b);
|
|
106
|
+
}
|
|
107
|
+
return sign * (ca.value - cb.value) || byDevice(a, b);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
var DEFAULT_PAGE_SIZE = 20;
|
|
111
|
+
function pageCount(rows, pageSize) {
|
|
112
|
+
return Math.max(1, Math.ceil(rows / Math.max(1, pageSize)));
|
|
113
|
+
}
|
|
114
|
+
function clampPage(page, rows, pageSize) {
|
|
115
|
+
return Math.min(Math.max(0, page), pageCount(rows, pageSize) - 1);
|
|
116
|
+
}
|
|
117
|
+
function deviceBars(members, measurement, sort) {
|
|
118
|
+
const bars = members.map((m) => {
|
|
119
|
+
const cell = cellFor(m, measurement);
|
|
120
|
+
return { label: deviceLabel(m), token: m.token, value: cell.kind === "value" ? cell.value : null };
|
|
121
|
+
});
|
|
122
|
+
if (sort === "none") return bars;
|
|
123
|
+
const sign = sort === "ascending" ? 1 : -1;
|
|
124
|
+
return bars.map((bar, i) => ({ bar, i })).sort((a, b) => {
|
|
125
|
+
if (a.bar.value === null || b.bar.value === null) {
|
|
126
|
+
return a.bar.value === b.bar.value ? a.i - b.i : a.bar.value === null ? 1 : -1;
|
|
127
|
+
}
|
|
128
|
+
return sign * (a.bar.value - b.bar.value) || a.i - b.i;
|
|
129
|
+
}).map((x) => x.bar);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/fleet-strings.ts
|
|
133
|
+
import { viewerLocale } from "@devicechain/widget-sdk";
|
|
134
|
+
var FLEET_STRINGS = {
|
|
135
|
+
en: {
|
|
136
|
+
device: "Device",
|
|
137
|
+
loading: "Loading",
|
|
138
|
+
notReported: "does not report this",
|
|
139
|
+
unread: "not loaded",
|
|
140
|
+
noData: "no data in range",
|
|
141
|
+
failed: "could not be read",
|
|
142
|
+
unbound: "Choose a device group for this widget",
|
|
143
|
+
forbidden: "You do not have access to this data",
|
|
144
|
+
unsupported: "This data source cannot list a fleet",
|
|
145
|
+
noMembers: "No devices in this group",
|
|
146
|
+
noMeasurement: "Choose the measurement to compare",
|
|
147
|
+
oneDevice: "This widget shows one device, and it is bound to a group of devices whose readings it would mix together.",
|
|
148
|
+
useFleetTable: "Bind it to one device, or show the group in a fleet table.",
|
|
149
|
+
usePerDevice: "Set it to one bar per device to compare them, or bind it to one device.",
|
|
150
|
+
stale: "stale",
|
|
151
|
+
truncated: (shown, total) => `Showing ${shown} of ${total} devices`,
|
|
152
|
+
range: (from, to, of) => `${from}\u2013${to} of ${of}`,
|
|
153
|
+
previous: "Previous page",
|
|
154
|
+
next: "Next page",
|
|
155
|
+
sortBy: (column) => `Sort by ${column}`,
|
|
156
|
+
drill: (device) => `Show ${device} in the linked widgets`
|
|
157
|
+
},
|
|
158
|
+
es: {
|
|
159
|
+
device: "Dispositivo",
|
|
160
|
+
loading: "Cargando",
|
|
161
|
+
notReported: "no informa esta medici\xF3n",
|
|
162
|
+
unread: "no cargado",
|
|
163
|
+
noData: "sin datos en el rango",
|
|
164
|
+
failed: "no se pudo leer",
|
|
165
|
+
unbound: "Elija un grupo de dispositivos para este widget",
|
|
166
|
+
forbidden: "No tiene acceso a estos datos",
|
|
167
|
+
unsupported: "Esta fuente de datos no puede listar una flota",
|
|
168
|
+
noMembers: "No hay dispositivos en este grupo",
|
|
169
|
+
noMeasurement: "Elija la medici\xF3n que comparar",
|
|
170
|
+
oneDevice: "Este widget muestra un solo dispositivo y est\xE1 vinculado a un grupo de dispositivos cuyas lecturas mezclar\xEDa.",
|
|
171
|
+
useFleetTable: "Vinc\xFAlelo a un dispositivo o muestre el grupo en una tabla de flota.",
|
|
172
|
+
usePerDevice: "Config\xFArelo con una barra por dispositivo para compararlos, o vinc\xFAlelo a un dispositivo.",
|
|
173
|
+
stale: "obsoleto",
|
|
174
|
+
truncated: (shown, total) => `Mostrando ${shown} de ${total} dispositivos`,
|
|
175
|
+
range: (from, to, of) => `${from}\u2013${to} de ${of}`,
|
|
176
|
+
previous: "P\xE1gina anterior",
|
|
177
|
+
next: "P\xE1gina siguiente",
|
|
178
|
+
sortBy: (column) => `Ordenar por ${column}`,
|
|
179
|
+
drill: (device) => `Mostrar ${device} en los widgets enlazados`
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
function fleetStrings(locale = viewerLocale()) {
|
|
183
|
+
return locale?.toLowerCase().split("-")[0] === "es" ? FLEET_STRINGS.es : FLEET_STRINGS.en;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
seedWindow,
|
|
188
|
+
buildFleet,
|
|
189
|
+
newestMemberTime,
|
|
190
|
+
cellFor,
|
|
191
|
+
columnNames,
|
|
192
|
+
deviceLabel,
|
|
193
|
+
sortMembers,
|
|
194
|
+
DEFAULT_PAGE_SIZE,
|
|
195
|
+
pageCount,
|
|
196
|
+
clampPage,
|
|
197
|
+
deviceBars,
|
|
198
|
+
fleetStrings
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=chunk-S2OMBJLI.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/fleet-model.ts", "../../src/fleet-strings.ts"],
|
|
4
|
+
"sourcesContent": ["// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The fleet widgets' state, decided apart from the drawing and from the hub: which reading a cell\n// shows, why one is empty, how the rows are ordered and paged. Pure, so each rule is a test.\n//\n// Everything is keyed by DEVICE and measurement together. The measurement channel keys its latest\n// values by name alone, so two thermostats reporting \"temperature\" overwrite each other there;\n// nothing in this file ever looks a value up by name without the device beside it.\n\nimport {\n bucketSecondsFor,\n type FleetMember,\n type FleetState,\n type HistoryWindow,\n type MeasurementSample,\n type MemberRoster,\n type MemberSeed,\n} from '@devicechain/dashboards';\n\nconst MINUTE_MS = 60_000;\n\n// The window a fleet's seed reads: whole minutes ending at the last minute boundary at or before\n// `at`, in buckets of bucketSecondsFor(span) (a multiple of 60, so the store answers from its\n// one-minute summaries). Whole minutes are what let widgets reading the same span on the same\n// board share one request per device (the hub keys in-flight reads by window), and what keep the\n// window from holding a reading from before the span. The end is the instant just before the\n// boundary, because the store's end is inclusive.\nexport function seedWindow(spanSeconds: number, at: number): HistoryWindow {\n const end = Math.floor(at / MINUTE_MS) * MINUTE_MS;\n const start = Math.ceil((end - spanSeconds * 1000) / MINUTE_MS) * MINUTE_MS;\n return {\n startTime: new Date(start).toISOString(),\n endTime: new Date(end - 1).toISOString(),\n intervalSeconds: bucketSecondsFor(Math.max(60, (end - start) / 1000)),\n };\n}\n\n// ---- Building the state ------------------------------------------------------\n\n// What the feed has gathered. `seeds` holds a settled seed per device (`'failed'` for a read that\n// did not come back); a device absent from it is still loading. `live` is every device's newest\n// sample per measurement name since the board opened.\nexport interface FleetInputs {\n roster: MemberRoster | null;\n seeds: ReadonlyMap<string, MemberSeed | 'failed'>;\n live: ReadonlyMap<string, ReadonlyMap<string, MeasurementSample>>;\n // The start of the span (epoch ms): a reading older than this is not \"in range\".\n since: number;\n forbidden: boolean;\n}\n\nfunction timeOf(sample: MeasurementSample | undefined): number {\n if (!sample?.occurredTime) return Number.NaN;\n return Date.parse(sample.occurredTime);\n}\n\n// A sample with no readable time is taken as current: only a live sample can lack one, and it just\n// arrived.\nfunction inRange(sample: MeasurementSample, since: number): boolean {\n const at = timeOf(sample);\n return Number.isNaN(at) || at >= since;\n}\n\nfunction newer(a: MeasurementSample | undefined, b: MeasurementSample | undefined): MeasurementSample | undefined {\n if (!a) return b;\n if (!b) return a;\n const ta = timeOf(a);\n const tb = timeOf(b);\n if (Number.isNaN(ta)) return a;\n if (Number.isNaN(tb)) return b;\n return tb >= ta ? b : a;\n}\n\nexport function buildFleet(i: FleetInputs): FleetState {\n if (i.forbidden) return { status: 'forbidden', members: [], total: 0, truncated: false };\n if (!i.roster) return { status: 'loading', members: [], total: 0, truncated: false };\n if (i.roster.unbound) return { status: 'unbound', members: [], total: 0, truncated: false };\n\n // A seeded reading is stamped at the start of its minute (fleet-seed.ts, \"Ages\") and happened\n // somewhere in that minute, so it is in range while its minute is. Held to the exact instant, a\n // reading 59 minutes 45 seconds old on an hour's board was dropped as out of range.\n const seedSince = Math.floor(i.since / MINUTE_MS) * MINUTE_MS;\n const members = i.roster.members.map((ref): FleetMember => {\n const seed = i.seeds.get(ref.token);\n const seeded = seed && seed !== 'failed' ? seed : undefined;\n const live = i.live.get(ref.token);\n const latest: Record<string, MeasurementSample> = {};\n const names = new Set<string>([...Object.keys(seeded?.latest ?? {}), ...(live?.keys() ?? [])]);\n for (const name of names) {\n const best = newer(seeded?.latest[name], live?.get(name));\n if (best && inRange(best, best === seeded?.latest[name] ? seedSince : i.since)) latest[name] = best;\n }\n const observed = new Set<string>(seeded?.observed ?? []);\n for (const name of Object.keys(latest)) observed.add(name);\n return {\n token: ref.token,\n name: ref.name,\n latest,\n seed: seed === undefined ? 'pending' : seed === 'failed' ? 'failed' : 'ready',\n observed: [...observed],\n // A live reading of an unread name is its value: cellFor looks at `latest` first.\n ...(seeded?.unread?.length ? { unread: seeded.unread } : {}),\n };\n });\n return {\n status: 'ready',\n members,\n total: i.roster.total,\n truncated: i.roster.total > members.length,\n };\n}\n\n// The newest reading anywhere in the fleet (epoch ms), or undefined when none carries a time.\nexport function newestMemberTime(fleet: FleetState): number | undefined {\n let newest: number | undefined;\n for (const m of fleet.members) {\n for (const sample of Object.values(m.latest)) {\n const at = timeOf(sample);\n if (!Number.isNaN(at) && (newest === undefined || at > newest)) newest = at;\n }\n }\n return newest;\n}\n\n// ---- Cells -------------------------------------------------------------------\n\n// What one (device, measurement) cell holds, and when it holds nothing, why.\nexport type Cell =\n | { kind: 'value'; sample: MeasurementSample; value: number }\n | { kind: 'loading' }\n // The device's history read failed and no live reading has arrived since.\n | { kind: 'failed' }\n // The device reported other measurements in the span, but never this one.\n | { kind: 'not-reported' }\n // The device reported this in the span, but its newest reading was not loaded (MemberSeed.unread).\n | { kind: 'unread' }\n // The device reported nothing at all in the span.\n | { kind: 'no-data' };\n\nexport function cellFor(member: FleetMember, measurement: string): Cell {\n const sample = member.latest[measurement];\n if (sample && typeof sample.value === 'number' && Number.isFinite(sample.value)) {\n return { kind: 'value', sample, value: sample.value };\n }\n if (member.seed === 'pending') return { kind: 'loading' };\n if (member.seed === 'failed') return { kind: 'failed' };\n if (member.unread?.includes(measurement)) return { kind: 'unread' };\n return member.observed.length > 0 && !member.observed.includes(measurement) ? { kind: 'not-reported' } : { kind: 'no-data' };\n}\n\n// The columns: the measurements the datasource names, in that order; with none named, every\n// measurement any member reported, alphabetically (stable across renders however samples arrive).\nexport function columnNames(named: readonly string[], members: readonly FleetMember[]): string[] {\n if (named.length > 0) return [...new Set(named)];\n const seen = new Set<string>();\n for (const m of members) for (const name of m.observed) seen.add(name);\n return [...seen].sort((a, b) => a.localeCompare(b));\n}\n\n// ---- Order and pages ------------------------------------------------------------\n\nexport interface SortKey {\n // A measurement name, or null for the device-name column.\n by: string | null;\n descending: boolean;\n}\n\nexport function deviceLabel(member: Pick<FleetMember, 'name' | 'token'>): string {\n return member.name && member.name !== '' ? member.name : member.token;\n}\n\nconst collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });\n\n// Ordered by the key, ties broken by device name then token, so the order never depends on the\n// order samples arrived in. A device with no value for the sorted measurement goes LAST in either\n// direction: \"largest first\" must not open with the devices that reported nothing.\nexport function sortMembers(members: readonly FleetMember[], key: SortKey): FleetMember[] {\n const byDevice = (a: FleetMember, b: FleetMember): number =>\n collator.compare(deviceLabel(a), deviceLabel(b)) || (a.token < b.token ? -1 : a.token > b.token ? 1 : 0);\n const sign = key.descending ? -1 : 1;\n return [...members].sort((a, b) => {\n if (key.by === null) return sign * byDevice(a, b);\n const ca = cellFor(a, key.by);\n const cb = cellFor(b, key.by);\n if (ca.kind !== 'value' || cb.kind !== 'value') {\n if (ca.kind === cb.kind) return byDevice(a, b);\n return ca.kind === 'value' ? -1 : cb.kind === 'value' ? 1 : byDevice(a, b);\n }\n return sign * (ca.value - cb.value) || byDevice(a, b);\n });\n}\n\nexport const DEFAULT_PAGE_SIZE = 20;\n\nexport function pageCount(rows: number, pageSize: number): number {\n return Math.max(1, Math.ceil(rows / Math.max(1, pageSize)));\n}\n\n// The page asked for, held inside what exists (a fleet that shrinks must not leave the viewer on a\n// page past the end).\nexport function clampPage(page: number, rows: number, pageSize: number): number {\n return Math.min(Math.max(0, page), pageCount(rows, pageSize) - 1);\n}\n\n// ---- Bars ---------------------------------------------------------------------------\n\n// One bar per member for one measurement, in the shape the bar chart already draws (category\n// items), ordered like the categorical charts order theirs. A device with no number keeps its\n// label with no bar and sorts after those that have one.\nexport interface DeviceBar {\n label: string;\n token: string;\n value: number | null;\n}\n\nexport function deviceBars(members: readonly FleetMember[], measurement: string, sort: 'none' | 'ascending' | 'descending'): DeviceBar[] {\n const bars = members.map((m): DeviceBar => {\n const cell = cellFor(m, measurement);\n return { label: deviceLabel(m), token: m.token, value: cell.kind === 'value' ? cell.value : null };\n });\n if (sort === 'none') return bars;\n const sign = sort === 'ascending' ? 1 : -1;\n return bars\n .map((bar, i) => ({ bar, i }))\n .sort((a, b) => {\n if (a.bar.value === null || b.bar.value === null) {\n return a.bar.value === b.bar.value ? a.i - b.i : a.bar.value === null ? 1 : -1;\n }\n return sign * (a.bar.value - b.bar.value) || a.i - b.i;\n })\n .map((x) => x.bar);\n}\n", "// Copyright The DeviceChain Authors\n// SPDX-License-Identifier: Apache-2.0\n\n// The fixed words the fleet table and the per-device bar chart render themselves: the device\n// column, the reason a cell holds no number, the truncation and paging captions. The widget\n// packages carry no i18n runtime, so this is the one small per-language table (modelled on\n// aggregate-strings.ts); option labels are translated in the console's catalogs, not here.\n\nimport { viewerLocale } from '@devicechain/widget-sdk';\n\nexport interface FleetStrings {\n device: string;\n loading: string;\n // The reason a cell holds no number (D-5): each is a different fact about the device.\n notReported: string;\n // It reported this, but its newest reading was not loaded: there IS data.\n unread: string;\n noData: string;\n failed: string;\n // Whole-widget states.\n unbound: string;\n forbidden: string;\n unsupported: string;\n noMembers: string;\n noMeasurement: string;\n // A widget that shows one device, bound to a group: refused, with where the group can be shown.\n oneDevice: string;\n useFleetTable: string;\n usePerDevice: string;\n stale: string;\n // \"Showing 500 of 812 devices\": a fleet past what is read must say so.\n truncated: (shown: number, total: number) => string;\n range: (from: number, to: number, of: number) => string;\n previous: string;\n next: string;\n sortBy: (column: string) => string;\n drill: (device: string) => string;\n}\n\nexport const FLEET_STRINGS: Record<'en' | 'es', FleetStrings> = {\n en: {\n device: 'Device',\n loading: 'Loading',\n notReported: 'does not report this',\n unread: 'not loaded',\n noData: 'no data in range',\n failed: 'could not be read',\n unbound: 'Choose a device group for this widget',\n forbidden: 'You do not have access to this data',\n unsupported: 'This data source cannot list a fleet',\n noMembers: 'No devices in this group',\n noMeasurement: 'Choose the measurement to compare',\n oneDevice: 'This widget shows one device, and it is bound to a group of devices whose readings it would mix together.',\n useFleetTable: 'Bind it to one device, or show the group in a fleet table.',\n usePerDevice: 'Set it to one bar per device to compare them, or bind it to one device.',\n stale: 'stale',\n truncated: (shown, total) => `Showing ${shown} of ${total} devices`,\n range: (from, to, of) => `${from}\u2013${to} of ${of}`,\n previous: 'Previous page',\n next: 'Next page',\n sortBy: (column) => `Sort by ${column}`,\n drill: (device) => `Show ${device} in the linked widgets`,\n },\n es: {\n device: 'Dispositivo',\n loading: 'Cargando',\n notReported: 'no informa esta medici\u00F3n',\n unread: 'no cargado',\n noData: 'sin datos en el rango',\n failed: 'no se pudo leer',\n unbound: 'Elija un grupo de dispositivos para este widget',\n forbidden: 'No tiene acceso a estos datos',\n unsupported: 'Esta fuente de datos no puede listar una flota',\n noMembers: 'No hay dispositivos en este grupo',\n noMeasurement: 'Elija la medici\u00F3n que comparar',\n oneDevice: 'Este widget muestra un solo dispositivo y est\u00E1 vinculado a un grupo de dispositivos cuyas lecturas mezclar\u00EDa.',\n useFleetTable: 'Vinc\u00FAlelo a un dispositivo o muestre el grupo en una tabla de flota.',\n usePerDevice: 'Config\u00FArelo con una barra por dispositivo para compararlos, o vinc\u00FAlelo a un dispositivo.',\n stale: 'obsoleto',\n truncated: (shown, total) => `Mostrando ${shown} de ${total} dispositivos`,\n range: (from, to, of) => `${from}\u2013${to} de ${of}`,\n previous: 'P\u00E1gina anterior',\n next: 'P\u00E1gina siguiente',\n sortBy: (column) => `Ordenar por ${column}`,\n drill: (device) => `Mostrar ${device} en los widgets enlazados`,\n },\n};\n\nexport function fleetStrings(locale: string | undefined = viewerLocale()): FleetStrings {\n return locale?.toLowerCase().split('-')[0] === 'es' ? FLEET_STRINGS.es : FLEET_STRINGS.en;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|