@devicechain/widgets 0.21.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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
package/dist/index.js
CHANGED
|
@@ -1,76 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildPayload,
|
|
3
|
+
commandStatusColor,
|
|
4
|
+
commandStatusLabel,
|
|
5
|
+
defaultValues,
|
|
6
|
+
isScalar,
|
|
7
|
+
isTerminalStatus,
|
|
8
|
+
parseBool,
|
|
9
|
+
parseParameterSchema,
|
|
10
|
+
validateParams
|
|
11
|
+
} from "./chunks/chunk-YPFB6DAL.js";
|
|
12
|
+
import {
|
|
13
|
+
readRange
|
|
14
|
+
} from "./chunks/chunk-ZR74ORIT.js";
|
|
15
|
+
import {
|
|
16
|
+
LevelReadout
|
|
17
|
+
} from "./chunks/chunk-XQQQNTBX.js";
|
|
18
|
+
import {
|
|
19
|
+
alarmSubscription,
|
|
20
|
+
commandSubscription,
|
|
21
|
+
measurementSpan,
|
|
22
|
+
measurementWindow,
|
|
23
|
+
membersMode
|
|
24
|
+
} from "./chunks/chunk-2D7JNKFE.js";
|
|
25
|
+
|
|
1
26
|
// src/dashboard-renderer.tsx
|
|
2
27
|
import {
|
|
3
|
-
|
|
28
|
+
anchorFraction,
|
|
29
|
+
DEFAULT_CANVAS_DENSITY,
|
|
4
30
|
defaultHistoryWindow,
|
|
31
|
+
effectiveLayout,
|
|
32
|
+
historyWindowFor,
|
|
33
|
+
initialTimeRange,
|
|
5
34
|
fetchWidgetHistory,
|
|
6
|
-
|
|
35
|
+
layoutShape,
|
|
36
|
+
resolveFrame,
|
|
37
|
+
resolvePlacement,
|
|
7
38
|
resolveWidgetDatasource,
|
|
39
|
+
spanLimitsOfLayout,
|
|
8
40
|
UNKNOWN_WIDGET_TYPE as UNKNOWN_WIDGET_TYPE2
|
|
9
41
|
} from "@devicechain/dashboards";
|
|
10
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
densityScale,
|
|
44
|
+
tokenStyle,
|
|
45
|
+
WidgetDensityProvider,
|
|
46
|
+
WidgetFrameStyleProvider,
|
|
47
|
+
WidgetTimeRangeProvider,
|
|
48
|
+
WidgetTokenStyles
|
|
49
|
+
} from "@devicechain/widget-sdk";
|
|
50
|
+
import { Fragment as Fragment5, useCallback as useCallback2, useEffect as useEffect5, useLayoutEffect, useMemo as useMemo6, useRef as useRef3, useState as useState8 } from "react";
|
|
11
51
|
|
|
12
52
|
// src/connected-widget.tsx
|
|
13
53
|
import {
|
|
14
54
|
UNKNOWN_WIDGET_TYPE
|
|
15
55
|
} from "@devicechain/dashboards";
|
|
16
|
-
import {
|
|
17
|
-
|
|
56
|
+
import {
|
|
57
|
+
newestSampleTime,
|
|
58
|
+
resolveStaleAfterSeconds,
|
|
59
|
+
WidgetCandidatesProvider,
|
|
60
|
+
WidgetFreshnessProvider,
|
|
61
|
+
WidgetSelectProvider,
|
|
62
|
+
useWidgetTimeRange as useWidgetTimeRange2
|
|
63
|
+
} from "@devicechain/widget-sdk";
|
|
64
|
+
import { useMemo as useMemo5 } from "react";
|
|
18
65
|
|
|
19
|
-
// src/
|
|
20
|
-
import {
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
function policy(descriptor) {
|
|
34
|
-
return descriptor.binding;
|
|
35
|
-
}
|
|
36
|
-
function measurementWindow(descriptor, widget) {
|
|
37
|
-
return numberValue(policy(descriptor).window, widget.options);
|
|
38
|
-
}
|
|
39
|
-
function parseAcknowledged(value) {
|
|
40
|
-
if (value === "true") return true;
|
|
41
|
-
if (value === "false") return false;
|
|
42
|
-
return void 0;
|
|
43
|
-
}
|
|
44
|
-
function alarmSubscription(descriptor, widget) {
|
|
45
|
-
const p = policy(descriptor);
|
|
46
|
-
return {
|
|
47
|
-
datasource: widget.datasource,
|
|
48
|
-
state: stringValue(p.state, widget.options),
|
|
49
|
-
severity: stringValue(p.severity, widget.options),
|
|
50
|
-
acknowledged: parseAcknowledged(stringValue(p.acknowledged, widget.options)),
|
|
51
|
-
pageSize: numberValue(p.pageSize, widget.options) ?? 1
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function commandSubscription(descriptor, widget) {
|
|
55
|
-
return {
|
|
56
|
-
datasource: widget.datasource,
|
|
57
|
-
pageSize: numberValue(policy(descriptor).pageSize, widget.options) ?? 1
|
|
58
|
-
};
|
|
66
|
+
// src/use-held.ts
|
|
67
|
+
import { useState } from "react";
|
|
68
|
+
function useHeld(value, hold, ready) {
|
|
69
|
+
const [held, setHeld] = useState(void 0);
|
|
70
|
+
if (!hold) {
|
|
71
|
+
if (held !== void 0) setHeld(void 0);
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
if (held === void 0 || ready(held.value) !== ready(value)) {
|
|
75
|
+
setHeld({ value });
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
return held.value;
|
|
59
79
|
}
|
|
60
80
|
|
|
61
81
|
// src/builtin-registry.ts
|
|
62
82
|
import { createWidgetRegistry } from "@devicechain/widget-sdk";
|
|
63
83
|
|
|
64
|
-
// src/widgets/
|
|
65
|
-
import {
|
|
66
|
-
css,
|
|
67
|
-
defineWidget,
|
|
68
|
-
optString as optString2,
|
|
69
|
-
useElementSize,
|
|
70
|
-
WidgetFrame
|
|
71
|
-
} from "@devicechain/widget-sdk";
|
|
84
|
+
// src/widgets/aggregate-lazy.tsx
|
|
85
|
+
import { defineWidget } from "@devicechain/widget-sdk";
|
|
72
86
|
|
|
73
87
|
// src/builtin-descriptors.ts
|
|
88
|
+
import { SEVERITY_LEVELS } from "@devicechain/widget-sdk";
|
|
74
89
|
import {
|
|
75
90
|
createWidgetCatalog,
|
|
76
91
|
defineWidgetDescriptor
|
|
@@ -92,7 +107,42 @@ var PRECISION = {
|
|
|
92
107
|
max: 100,
|
|
93
108
|
labelId: "widgetLabelPrecision",
|
|
94
109
|
helpId: "widgetHintPrecision",
|
|
95
|
-
doc: "Decimal places to round displayed values to. Unset
|
|
110
|
+
doc: "Decimal places to round displayed values to. Unset rounds to at most 3 decimals (4 significant digits under 1)."
|
|
111
|
+
};
|
|
112
|
+
var THRESHOLDS = {
|
|
113
|
+
kind: "list",
|
|
114
|
+
maxItems: 12,
|
|
115
|
+
labelId: "widgetLabelThresholds",
|
|
116
|
+
helpId: "widgetHintThresholds",
|
|
117
|
+
doc: "Severity bands over the value. Each entry starts a band at its value that runs to the next entry; below the first, and in a normal band, the widget keeps its own colour.",
|
|
118
|
+
item: {
|
|
119
|
+
value: {
|
|
120
|
+
kind: "number",
|
|
121
|
+
required: true,
|
|
122
|
+
labelId: "widgetLabelThresholdValue",
|
|
123
|
+
doc: "The value at which this band starts."
|
|
124
|
+
},
|
|
125
|
+
level: {
|
|
126
|
+
kind: "enum",
|
|
127
|
+
required: true,
|
|
128
|
+
values: SEVERITY_LEVELS,
|
|
129
|
+
labelId: "widgetLabelThresholdLevel",
|
|
130
|
+
valueLabelIds: {
|
|
131
|
+
normal: "widgetSeverityNormal",
|
|
132
|
+
warning: "widgetSeverityWarning",
|
|
133
|
+
minor: "widgetSeverityMinor",
|
|
134
|
+
major: "widgetSeverityMajor",
|
|
135
|
+
critical: "widgetSeverityCritical"
|
|
136
|
+
},
|
|
137
|
+
placeholderId: "widgetPlaceholderThresholdLevel",
|
|
138
|
+
doc: "The severity of this band. The theme decides the colour; normal has none."
|
|
139
|
+
},
|
|
140
|
+
label: {
|
|
141
|
+
kind: "string",
|
|
142
|
+
labelId: "widgetLabelThresholdLabel",
|
|
143
|
+
doc: "Text shown on the chart line for this threshold. Defaults to its value."
|
|
144
|
+
}
|
|
145
|
+
}
|
|
96
146
|
};
|
|
97
147
|
var FLASH_ON_CHANGE = {
|
|
98
148
|
kind: "boolean",
|
|
@@ -108,11 +158,68 @@ var WINDOW = {
|
|
|
108
158
|
helpId: "widgetHintWindow",
|
|
109
159
|
doc: "Samples retained across ALL of the widget's measurements before the oldest are dropped. Bounds memory; only the chart draws them. Default 300."
|
|
110
160
|
};
|
|
161
|
+
var STALE_AFTER_SECONDS = {
|
|
162
|
+
kind: "number",
|
|
163
|
+
integer: true,
|
|
164
|
+
min: 1,
|
|
165
|
+
labelId: "widgetLabelStaleAfter",
|
|
166
|
+
helpId: "widgetHintStaleAfter",
|
|
167
|
+
doc: "Seconds after which the newest sample is called stale: the value is greyed out and badged. Unset shows the sample age only and never marks the widget stale."
|
|
168
|
+
};
|
|
111
169
|
var UNIT = {
|
|
112
170
|
kind: "string",
|
|
113
171
|
labelId: "widgetLabelUnit",
|
|
114
172
|
doc: "Unit suffix shown beside the value."
|
|
115
173
|
};
|
|
174
|
+
var SCALE_MIN = {
|
|
175
|
+
kind: "number",
|
|
176
|
+
required: true,
|
|
177
|
+
labelId: "widgetLabelMin",
|
|
178
|
+
helpId: "widgetHintScaleMin",
|
|
179
|
+
doc: "Value at the empty end of the scale. Required: the widget does not guess a range."
|
|
180
|
+
};
|
|
181
|
+
var SCALE_MAX = {
|
|
182
|
+
kind: "number",
|
|
183
|
+
required: true,
|
|
184
|
+
labelId: "widgetLabelMax",
|
|
185
|
+
helpId: "widgetHintScaleMax",
|
|
186
|
+
doc: "Value at the full end of the scale. Required, and above min."
|
|
187
|
+
};
|
|
188
|
+
var CONTROL_COMMAND_NAME = {
|
|
189
|
+
kind: "string",
|
|
190
|
+
required: true,
|
|
191
|
+
labelId: "widgetCommandHeading",
|
|
192
|
+
helpId: "widgetCommandFieldHint",
|
|
193
|
+
control: "command-vocabulary",
|
|
194
|
+
doc: "Command key issued to the target device. Without it the control cannot send."
|
|
195
|
+
};
|
|
196
|
+
var CONTROL_COMMAND_LABEL = {
|
|
197
|
+
kind: "string",
|
|
198
|
+
labelId: "widgetCommandHeading",
|
|
199
|
+
helpId: "widgetCommandFieldHint",
|
|
200
|
+
control: "command-vocabulary",
|
|
201
|
+
doc: "Name of the control for assistive technology. Defaults to the command key."
|
|
202
|
+
};
|
|
203
|
+
var CONTROL_PARAMETER_SCHEMA = {
|
|
204
|
+
kind: "string",
|
|
205
|
+
format: "json",
|
|
206
|
+
labelId: "widgetCommandHeading",
|
|
207
|
+
helpId: "widgetCommandFieldHint",
|
|
208
|
+
control: "command-vocabulary",
|
|
209
|
+
doc: "The command's parameter descriptors, baked in as JSON at author time. Gives the control its range and its refusals."
|
|
210
|
+
};
|
|
211
|
+
var CONTROL_PARAMETER = {
|
|
212
|
+
kind: "string",
|
|
213
|
+
labelId: "widgetLabelParameter",
|
|
214
|
+
helpId: "widgetHintControlParameter",
|
|
215
|
+
doc: "Which parameter of the command this control sets. May be left blank when the command has exactly one parameter of the right type."
|
|
216
|
+
};
|
|
217
|
+
var MEASUREMENT = {
|
|
218
|
+
kind: "string",
|
|
219
|
+
labelId: "widgetLabelMeasurement",
|
|
220
|
+
helpId: "widgetHintMeasurement",
|
|
221
|
+
doc: "Which measurement to display. Defaults to the first the datasource selects."
|
|
222
|
+
};
|
|
116
223
|
var ALARM_STATE = {
|
|
117
224
|
kind: "enum",
|
|
118
225
|
values: ["ACTIVE", "CLEARED"],
|
|
@@ -149,28 +256,78 @@ var SELECTION_TARGET = {
|
|
|
149
256
|
};
|
|
150
257
|
var VERSION = "1.0.0";
|
|
151
258
|
var ALL_SELECTORS = ["device", "anchor", "slot"];
|
|
259
|
+
var ONE_DEVICE = ["device", "slot"];
|
|
152
260
|
var NO_DATASOURCE = { mode: "none" };
|
|
153
261
|
var ALARM_FILTERS = {
|
|
154
262
|
state: { option: "state" },
|
|
155
263
|
severity: { option: "severity" },
|
|
156
264
|
acknowledged: { option: "acknowledged" }
|
|
157
265
|
};
|
|
266
|
+
var WINDOW_SECONDS = {
|
|
267
|
+
kind: "number",
|
|
268
|
+
integer: true,
|
|
269
|
+
min: 60,
|
|
270
|
+
max: 604800,
|
|
271
|
+
labelId: "widgetLabelWindowSeconds",
|
|
272
|
+
helpId: "widgetHintWindowSeconds",
|
|
273
|
+
doc: "Seconds of history the widget covers, back from now. Leave unset to follow the board\u2019s time range; set it to pin this widget to a fixed span that the viewer\u2019s range picker does not change. 60 to 604800."
|
|
274
|
+
};
|
|
275
|
+
var CATEGORY_SORT = {
|
|
276
|
+
kind: "enum",
|
|
277
|
+
values: ["none", "ascending", "descending"],
|
|
278
|
+
labelId: "widgetLabelSort",
|
|
279
|
+
valueLabelIds: { none: "widgetSortNone", ascending: "widgetSortAscending", descending: "widgetSortDescending" },
|
|
280
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
281
|
+
doc: "Order of the categories. Default: as the data source lists them."
|
|
282
|
+
};
|
|
283
|
+
var FLEET_MEASUREMENT = {
|
|
284
|
+
kind: "string",
|
|
285
|
+
required: true,
|
|
286
|
+
labelId: "widgetLabelMeasurement",
|
|
287
|
+
doc: "The measurement column this entry applies to."
|
|
288
|
+
};
|
|
289
|
+
var FLEET_COLUMNS = {
|
|
290
|
+
kind: "list",
|
|
291
|
+
maxItems: 24,
|
|
292
|
+
labelId: "widgetLabelColumns",
|
|
293
|
+
helpId: "widgetHintColumns",
|
|
294
|
+
doc: "Heading, unit and precision per measurement column; a column with none shows its name.",
|
|
295
|
+
item: {
|
|
296
|
+
measurement: FLEET_MEASUREMENT,
|
|
297
|
+
label: { kind: "string", labelId: "widgetLabelColumnHeading", doc: "Column heading. Defaults to the measurement name." },
|
|
298
|
+
unit: { ...UNIT },
|
|
299
|
+
precision: { ...PRECISION }
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
var FLEET_THRESHOLDS = {
|
|
303
|
+
...THRESHOLDS,
|
|
304
|
+
item: {
|
|
305
|
+
measurement: FLEET_MEASUREMENT,
|
|
306
|
+
...THRESHOLDS.item
|
|
307
|
+
}
|
|
308
|
+
};
|
|
158
309
|
var builtinWidgetDescriptors = {
|
|
159
310
|
"timeseries-chart": defineWidgetDescriptor({
|
|
160
311
|
id: "timeseries-chart",
|
|
161
312
|
version: VERSION,
|
|
162
313
|
sdkApiVersion: 1,
|
|
163
|
-
optionsVersion:
|
|
314
|
+
optionsVersion: 2,
|
|
164
315
|
display: { title: "Timeseries chart", category: "Telemetry", titleId: "widgetTypeTimeseriesChart", categoryId: "widgetCategoryTelemetry" },
|
|
165
316
|
channel: "measurement",
|
|
166
|
-
datasource: { mode: "required", selectors:
|
|
167
|
-
binding: {
|
|
317
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
318
|
+
binding: { windowSeconds: { option: "windowSeconds" } },
|
|
168
319
|
options: {
|
|
169
320
|
title: TITLE,
|
|
170
|
-
|
|
321
|
+
unit: UNIT,
|
|
322
|
+
precision: PRECISION,
|
|
323
|
+
thresholds: THRESHOLDS,
|
|
324
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
325
|
+
windowSeconds: WINDOW_SECONDS
|
|
171
326
|
},
|
|
172
327
|
defaults: { title: "Timeseries chart" },
|
|
173
|
-
interactions: {}
|
|
328
|
+
interactions: {},
|
|
329
|
+
freshness: true,
|
|
330
|
+
layout: { defaultSpan: { cols: 6, rows: 4 }, minSpan: { cols: 4, rows: 3 } }
|
|
174
331
|
}),
|
|
175
332
|
"latest-card": defineWidgetDescriptor({
|
|
176
333
|
id: "latest-card",
|
|
@@ -179,7 +336,7 @@ var builtinWidgetDescriptors = {
|
|
|
179
336
|
optionsVersion: 1,
|
|
180
337
|
display: { title: "Latest card", category: "Telemetry", titleId: "widgetTypeLatestCard", categoryId: "widgetCategoryTelemetry" },
|
|
181
338
|
channel: "measurement",
|
|
182
|
-
datasource: { mode: "required", selectors:
|
|
339
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
183
340
|
binding: { window: { option: "window", min: 1 } },
|
|
184
341
|
options: {
|
|
185
342
|
title: TITLE_OR_MEASUREMENT,
|
|
@@ -191,11 +348,15 @@ var builtinWidgetDescriptors = {
|
|
|
191
348
|
},
|
|
192
349
|
unit: UNIT,
|
|
193
350
|
precision: PRECISION,
|
|
351
|
+
thresholds: THRESHOLDS,
|
|
194
352
|
flashOnChange: FLASH_ON_CHANGE,
|
|
353
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
195
354
|
window: WINDOW
|
|
196
355
|
},
|
|
197
356
|
defaults: { title: "Latest card" },
|
|
198
|
-
interactions: {}
|
|
357
|
+
interactions: {},
|
|
358
|
+
freshness: true,
|
|
359
|
+
layout: { defaultSpan: { cols: 3, rows: 2 }, minSpan: { cols: 3, rows: 2 }, maxSpan: { cols: 6, rows: 4 } }
|
|
199
360
|
}),
|
|
200
361
|
gauge: defineWidgetDescriptor({
|
|
201
362
|
id: "gauge",
|
|
@@ -204,7 +365,7 @@ var builtinWidgetDescriptors = {
|
|
|
204
365
|
optionsVersion: 1,
|
|
205
366
|
display: { title: "Gauge", category: "Telemetry", titleId: "widgetTypeGauge", categoryId: "widgetCategoryTelemetry" },
|
|
206
367
|
channel: "measurement",
|
|
207
|
-
datasource: { mode: "required", selectors:
|
|
368
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
208
369
|
binding: { window: { option: "window", min: 1 } },
|
|
209
370
|
options: {
|
|
210
371
|
title: TITLE_OR_MEASUREMENT,
|
|
@@ -225,12 +386,17 @@ var builtinWidgetDescriptors = {
|
|
|
225
386
|
doc: "High end of the gauge scale. Default 100."
|
|
226
387
|
},
|
|
227
388
|
unit: UNIT,
|
|
389
|
+
precision: PRECISION,
|
|
390
|
+
thresholds: THRESHOLDS,
|
|
228
391
|
flashOnChange: FLASH_ON_CHANGE,
|
|
392
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
229
393
|
window: WINDOW
|
|
230
394
|
},
|
|
231
395
|
constraints: [{ kind: "greater-than", key: "max", than: "min" }],
|
|
232
396
|
defaults: { title: "Gauge" },
|
|
233
|
-
interactions: {}
|
|
397
|
+
interactions: {},
|
|
398
|
+
freshness: true,
|
|
399
|
+
layout: { defaultSpan: { cols: 3, rows: 3 }, minSpan: { cols: 3, rows: 3 }, maxSpan: { cols: 6, rows: 6 } }
|
|
234
400
|
}),
|
|
235
401
|
table: defineWidgetDescriptor({
|
|
236
402
|
id: "table",
|
|
@@ -239,16 +405,19 @@ var builtinWidgetDescriptors = {
|
|
|
239
405
|
optionsVersion: 1,
|
|
240
406
|
display: { title: "Table", category: "Telemetry", titleId: "widgetTypeTable", categoryId: "widgetCategoryTelemetry" },
|
|
241
407
|
channel: "measurement",
|
|
242
|
-
datasource: { mode: "required", selectors:
|
|
408
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
243
409
|
binding: { window: { option: "window", min: 1 } },
|
|
244
410
|
options: {
|
|
245
411
|
title: TITLE,
|
|
246
412
|
precision: PRECISION,
|
|
247
413
|
flashOnChange: FLASH_ON_CHANGE,
|
|
414
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
248
415
|
window: WINDOW
|
|
249
416
|
},
|
|
250
417
|
defaults: { title: "Table" },
|
|
251
|
-
interactions: {}
|
|
418
|
+
interactions: {},
|
|
419
|
+
freshness: true,
|
|
420
|
+
layout: { defaultSpan: { cols: 6, rows: 3 }, minSpan: { cols: 4, rows: 2 } }
|
|
252
421
|
}),
|
|
253
422
|
// label and image are static: the `none` channel, no datasource, no subscription and no
|
|
254
423
|
// history seed.
|
|
@@ -282,11 +451,12 @@ var builtinWidgetDescriptors = {
|
|
|
282
451
|
min: 1,
|
|
283
452
|
labelId: "widgetLabelFontSize",
|
|
284
453
|
helpId: "widgetHintFontSize",
|
|
285
|
-
doc: "Text size in pixels. Default 16."
|
|
454
|
+
doc: "Text size in pixels at the comfortable density; it scales with the board density. Default 16."
|
|
286
455
|
}
|
|
287
456
|
},
|
|
288
457
|
defaults: { text: "New label" },
|
|
289
|
-
interactions: {}
|
|
458
|
+
interactions: {},
|
|
459
|
+
layout: { defaultSpan: { cols: 3, rows: 1 }, minSpan: { cols: 1, rows: 1 }, maxSpan: { cols: 24, rows: 3 } }
|
|
290
460
|
}),
|
|
291
461
|
image: defineWidgetDescriptor({
|
|
292
462
|
id: "image",
|
|
@@ -320,7 +490,8 @@ var builtinWidgetDescriptors = {
|
|
|
320
490
|
}
|
|
321
491
|
},
|
|
322
492
|
defaults: { title: "Image" },
|
|
323
|
-
interactions: {}
|
|
493
|
+
interactions: {},
|
|
494
|
+
layout: { defaultSpan: { cols: 3, rows: 2 }, minSpan: { cols: 1, rows: 1 } }
|
|
324
495
|
}),
|
|
325
496
|
"alarm-table": defineWidgetDescriptor({
|
|
326
497
|
id: "alarm-table",
|
|
@@ -349,7 +520,8 @@ var builtinWidgetDescriptors = {
|
|
|
349
520
|
selectionTarget: SELECTION_TARGET
|
|
350
521
|
},
|
|
351
522
|
defaults: { title: "Alarm table", state: "ACTIVE" },
|
|
352
|
-
interactions: { alarmActions: true, selection: true }
|
|
523
|
+
interactions: { alarmActions: true, selection: true },
|
|
524
|
+
layout: { defaultSpan: { cols: 6, rows: 4 }, minSpan: { cols: 4, rows: 2 } }
|
|
353
525
|
}),
|
|
354
526
|
// The count shows the SERVER total and reads a page only to sample the worst loaded
|
|
355
527
|
// severity, so its page size is a constant rather than an authorable option.
|
|
@@ -372,7 +544,8 @@ var builtinWidgetDescriptors = {
|
|
|
372
544
|
acknowledged: ALARM_ACKNOWLEDGED
|
|
373
545
|
},
|
|
374
546
|
defaults: { title: "Alarm count", state: "ACTIVE" },
|
|
375
|
-
interactions: {}
|
|
547
|
+
interactions: {},
|
|
548
|
+
layout: { defaultSpan: { cols: 3, rows: 2 }, minSpan: { cols: 2, rows: 2 }, maxSpan: { cols: 6, rows: 4 } }
|
|
376
549
|
}),
|
|
377
550
|
"command-button": defineWidgetDescriptor({
|
|
378
551
|
id: "command-button",
|
|
@@ -381,7 +554,7 @@ var builtinWidgetDescriptors = {
|
|
|
381
554
|
optionsVersion: 1,
|
|
382
555
|
display: { title: "Command button", category: "Control", titleId: "widgetTypeCommandButton", categoryId: "widgetCategoryControl" },
|
|
383
556
|
channel: "control",
|
|
384
|
-
datasource: { mode: "required", selectors:
|
|
557
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
385
558
|
binding: { pageSize: { option: "maxRows", default: 20, min: 1 } },
|
|
386
559
|
options: {
|
|
387
560
|
title: TITLE,
|
|
@@ -418,7 +591,8 @@ var builtinWidgetDescriptors = {
|
|
|
418
591
|
}
|
|
419
592
|
},
|
|
420
593
|
defaults: { title: "Command button" },
|
|
421
|
-
interactions: { commandSend: true }
|
|
594
|
+
interactions: { commandSend: true },
|
|
595
|
+
layout: { defaultSpan: { cols: 3, rows: 3 }, minSpan: { cols: 3, rows: 2 }, maxSpan: { cols: 6, rows: 6 } }
|
|
422
596
|
}),
|
|
423
597
|
"entity-selector": defineWidgetDescriptor({
|
|
424
598
|
id: "entity-selector",
|
|
@@ -434,7 +608,9 @@ var builtinWidgetDescriptors = {
|
|
|
434
608
|
selectionTarget: { ...SELECTION_TARGET, required: true }
|
|
435
609
|
},
|
|
436
610
|
defaults: { title: "Entity selector" },
|
|
437
|
-
interactions: { selection: true, candidates: true }
|
|
611
|
+
interactions: { selection: true, candidates: true },
|
|
612
|
+
// A select control is one line high, so its rows are pinned; it widens to fit long names.
|
|
613
|
+
layout: { defaultSpan: { cols: 3, rows: 1 }, minSpan: { cols: 3, rows: 1 }, maxSpan: { cols: 12, rows: 1 }, resize: "horizontal" }
|
|
438
614
|
}),
|
|
439
615
|
map: defineWidgetDescriptor({
|
|
440
616
|
id: "map",
|
|
@@ -470,23 +646,516 @@ var builtinWidgetDescriptors = {
|
|
|
470
646
|
}
|
|
471
647
|
},
|
|
472
648
|
defaults: { title: "Map" },
|
|
473
|
-
interactions: {}
|
|
649
|
+
interactions: {},
|
|
650
|
+
layout: { defaultSpan: { cols: 6, rows: 4 }, minSpan: { cols: 4, rows: 3 } }
|
|
651
|
+
}),
|
|
652
|
+
// ---- Levels and indicators ------------------------------------------------
|
|
653
|
+
// DOM/SVG single-value widgets on the measurement channel. A threshold band colours the fill
|
|
654
|
+
// or lamp; `normal` keeps the widget's neutral accent.
|
|
655
|
+
"linear-gauge": defineWidgetDescriptor({
|
|
656
|
+
id: "linear-gauge",
|
|
657
|
+
version: VERSION,
|
|
658
|
+
sdkApiVersion: 1,
|
|
659
|
+
optionsVersion: 1,
|
|
660
|
+
display: { title: "Linear gauge", category: "Telemetry", titleId: "widgetTypeLinearGauge", categoryId: "widgetCategoryTelemetry" },
|
|
661
|
+
channel: "measurement",
|
|
662
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
663
|
+
binding: { window: { option: "window", min: 1 } },
|
|
664
|
+
options: {
|
|
665
|
+
title: TITLE_OR_MEASUREMENT,
|
|
666
|
+
measurement: MEASUREMENT,
|
|
667
|
+
min: SCALE_MIN,
|
|
668
|
+
max: SCALE_MAX,
|
|
669
|
+
style: {
|
|
670
|
+
kind: "enum",
|
|
671
|
+
values: ["bar", "thermometer"],
|
|
672
|
+
labelId: "widgetLabelLinearStyle",
|
|
673
|
+
valueLabelIds: { bar: "widgetLinearStyleBar", thermometer: "widgetLinearStyleThermometer" },
|
|
674
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
675
|
+
doc: "A plain bar (default) or a thermometer: a vertical tube with a bulb. A thermometer is always vertical."
|
|
676
|
+
},
|
|
677
|
+
orientation: {
|
|
678
|
+
kind: "enum",
|
|
679
|
+
values: ["horizontal", "vertical"],
|
|
680
|
+
labelId: "widgetLabelOrientation",
|
|
681
|
+
valueLabelIds: { horizontal: "widgetOrientationHorizontal", vertical: "widgetOrientationVertical" },
|
|
682
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
683
|
+
doc: "Direction of a bar: horizontal (default) or vertical. Ignored by a thermometer."
|
|
684
|
+
},
|
|
685
|
+
unit: UNIT,
|
|
686
|
+
precision: PRECISION,
|
|
687
|
+
thresholds: THRESHOLDS,
|
|
688
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
689
|
+
window: WINDOW
|
|
690
|
+
},
|
|
691
|
+
constraints: [{ kind: "greater-than", key: "max", than: "min" }],
|
|
692
|
+
defaults: { title: "Linear gauge" },
|
|
693
|
+
interactions: {},
|
|
694
|
+
freshness: true,
|
|
695
|
+
layout: { defaultSpan: { cols: 4, rows: 3 }, minSpan: { cols: 3, rows: 3 }, maxSpan: { cols: 8, rows: 8 } }
|
|
696
|
+
}),
|
|
697
|
+
"level-gauge": defineWidgetDescriptor({
|
|
698
|
+
id: "level-gauge",
|
|
699
|
+
version: VERSION,
|
|
700
|
+
sdkApiVersion: 1,
|
|
701
|
+
optionsVersion: 1,
|
|
702
|
+
display: { title: "Level gauge", category: "Telemetry", titleId: "widgetTypeLevelGauge", categoryId: "widgetCategoryTelemetry" },
|
|
703
|
+
channel: "measurement",
|
|
704
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
705
|
+
binding: { window: { option: "window", min: 1 } },
|
|
706
|
+
options: {
|
|
707
|
+
title: TITLE_OR_MEASUREMENT,
|
|
708
|
+
measurement: MEASUREMENT,
|
|
709
|
+
min: SCALE_MIN,
|
|
710
|
+
max: SCALE_MAX,
|
|
711
|
+
readout: {
|
|
712
|
+
kind: "enum",
|
|
713
|
+
values: ["value", "percent"],
|
|
714
|
+
labelId: "widgetLabelReadout",
|
|
715
|
+
valueLabelIds: { value: "widgetReadoutValue", percent: "widgetReadoutPercent" },
|
|
716
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
717
|
+
doc: "What the readout shows: the value with its unit (default) or the percent of the scale."
|
|
718
|
+
},
|
|
719
|
+
unit: UNIT,
|
|
720
|
+
precision: PRECISION,
|
|
721
|
+
thresholds: THRESHOLDS,
|
|
722
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
723
|
+
window: WINDOW
|
|
724
|
+
},
|
|
725
|
+
constraints: [{ kind: "greater-than", key: "max", than: "min" }],
|
|
726
|
+
defaults: { title: "Level gauge" },
|
|
727
|
+
interactions: {},
|
|
728
|
+
freshness: true,
|
|
729
|
+
layout: { defaultSpan: { cols: 3, rows: 4 }, minSpan: { cols: 2, rows: 3 }, maxSpan: { cols: 6, rows: 8 } }
|
|
730
|
+
}),
|
|
731
|
+
battery: defineWidgetDescriptor({
|
|
732
|
+
id: "battery",
|
|
733
|
+
version: VERSION,
|
|
734
|
+
sdkApiVersion: 1,
|
|
735
|
+
optionsVersion: 1,
|
|
736
|
+
display: { title: "Battery", category: "Telemetry", titleId: "widgetTypeBattery", categoryId: "widgetCategoryTelemetry" },
|
|
737
|
+
channel: "measurement",
|
|
738
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
739
|
+
binding: { window: { option: "window", min: 1 } },
|
|
740
|
+
options: {
|
|
741
|
+
title: TITLE_OR_MEASUREMENT,
|
|
742
|
+
measurement: MEASUREMENT,
|
|
743
|
+
min: {
|
|
744
|
+
kind: "number",
|
|
745
|
+
labelId: "widgetLabelMin",
|
|
746
|
+
helpId: "widgetHintBatteryRange",
|
|
747
|
+
doc: "Value of an empty battery. Default 0 (the scale is percent unless you set min and max)."
|
|
748
|
+
},
|
|
749
|
+
max: {
|
|
750
|
+
kind: "number",
|
|
751
|
+
labelId: "widgetLabelMax",
|
|
752
|
+
helpId: "widgetHintBatteryRange",
|
|
753
|
+
doc: "Value of a full battery. Default 100 (the scale is percent unless you set min and max)."
|
|
754
|
+
},
|
|
755
|
+
unit: UNIT,
|
|
756
|
+
precision: PRECISION,
|
|
757
|
+
thresholds: THRESHOLDS,
|
|
758
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
759
|
+
window: WINDOW
|
|
760
|
+
},
|
|
761
|
+
constraints: [{ kind: "greater-than", key: "max", than: "min" }],
|
|
762
|
+
defaults: { title: "Battery" },
|
|
763
|
+
interactions: {},
|
|
764
|
+
freshness: true,
|
|
765
|
+
layout: { defaultSpan: { cols: 3, rows: 2 }, minSpan: { cols: 2, rows: 2 }, maxSpan: { cols: 6, rows: 4 } }
|
|
766
|
+
}),
|
|
767
|
+
"status-light": defineWidgetDescriptor({
|
|
768
|
+
id: "status-light",
|
|
769
|
+
version: VERSION,
|
|
770
|
+
sdkApiVersion: 1,
|
|
771
|
+
optionsVersion: 1,
|
|
772
|
+
display: { title: "Status light", category: "Telemetry", titleId: "widgetTypeStatusLight", categoryId: "widgetCategoryTelemetry" },
|
|
773
|
+
channel: "measurement",
|
|
774
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
775
|
+
binding: { window: { option: "window", min: 1 } },
|
|
776
|
+
options: {
|
|
777
|
+
title: TITLE_OR_MEASUREMENT,
|
|
778
|
+
measurement: MEASUREMENT,
|
|
779
|
+
onAt: {
|
|
780
|
+
kind: "number",
|
|
781
|
+
labelId: "widgetLabelOnAt",
|
|
782
|
+
helpId: "widgetHintOnAt",
|
|
783
|
+
doc: "For an on/off signal: the lamp is lit at or above this value and dark below it. Unset keeps the lamp lit while a value is present, coloured by its threshold band."
|
|
784
|
+
},
|
|
785
|
+
unit: UNIT,
|
|
786
|
+
precision: PRECISION,
|
|
787
|
+
thresholds: THRESHOLDS,
|
|
788
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
789
|
+
window: WINDOW
|
|
790
|
+
},
|
|
791
|
+
defaults: { title: "Status light" },
|
|
792
|
+
interactions: {},
|
|
793
|
+
freshness: true,
|
|
794
|
+
layout: { defaultSpan: { cols: 2, rows: 2 }, minSpan: { cols: 2, rows: 2 }, maxSpan: { cols: 4, rows: 4 } }
|
|
795
|
+
}),
|
|
796
|
+
"signal-bars": defineWidgetDescriptor({
|
|
797
|
+
id: "signal-bars",
|
|
798
|
+
version: VERSION,
|
|
799
|
+
sdkApiVersion: 1,
|
|
800
|
+
optionsVersion: 1,
|
|
801
|
+
display: { title: "Signal bars", category: "Telemetry", titleId: "widgetTypeSignalBars", categoryId: "widgetCategoryTelemetry" },
|
|
802
|
+
channel: "measurement",
|
|
803
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
804
|
+
binding: { window: { option: "window", min: 1 } },
|
|
805
|
+
options: {
|
|
806
|
+
title: TITLE_OR_MEASUREMENT,
|
|
807
|
+
measurement: MEASUREMENT,
|
|
808
|
+
unit: UNIT,
|
|
809
|
+
precision: PRECISION,
|
|
810
|
+
// One bar per threshold: the entries ARE the steps, so they are required.
|
|
811
|
+
thresholds: {
|
|
812
|
+
...THRESHOLDS,
|
|
813
|
+
maxItems: 8,
|
|
814
|
+
required: true,
|
|
815
|
+
helpId: "widgetHintSignalThresholds",
|
|
816
|
+
doc: "The steps of the signal, lowest first: one bar per entry, lit once the value reaches it and coloured by its level. At least two, at most eight."
|
|
817
|
+
},
|
|
818
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
819
|
+
window: WINDOW
|
|
820
|
+
},
|
|
821
|
+
defaults: { title: "Signal bars" },
|
|
822
|
+
interactions: {},
|
|
823
|
+
freshness: true,
|
|
824
|
+
layout: { defaultSpan: { cols: 2, rows: 2 }, minSpan: { cols: 2, rows: 2 }, maxSpan: { cols: 4, rows: 4 } }
|
|
825
|
+
}),
|
|
826
|
+
// ---- Radial family ----------------------------------------------------------
|
|
827
|
+
// A heading on a full turn, and a ring filled between a configured min and max. The compass
|
|
828
|
+
// draws on the registered gauge series; the ring is SVG. Both put their number in DOM text.
|
|
829
|
+
compass: defineWidgetDescriptor({
|
|
830
|
+
id: "compass",
|
|
831
|
+
version: VERSION,
|
|
832
|
+
sdkApiVersion: 1,
|
|
833
|
+
optionsVersion: 1,
|
|
834
|
+
display: { title: "Compass", category: "Telemetry", titleId: "widgetTypeCompass", categoryId: "widgetCategoryTelemetry" },
|
|
835
|
+
channel: "measurement",
|
|
836
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
837
|
+
binding: { window: { option: "window", min: 1 } },
|
|
838
|
+
options: {
|
|
839
|
+
title: TITLE_OR_MEASUREMENT,
|
|
840
|
+
measurement: MEASUREMENT,
|
|
841
|
+
precision: {
|
|
842
|
+
...PRECISION,
|
|
843
|
+
doc: "Decimal places of the heading. Default 0 (whole degrees). The heading is rounded first, then folded onto 0 to 360, so it never reads 360."
|
|
844
|
+
},
|
|
845
|
+
thresholds: {
|
|
846
|
+
...THRESHOLDS,
|
|
847
|
+
doc: "Severity bands over the heading in degrees (0 to 360, after folding). Each entry starts a band at its value that runs to the next entry; below the first, and in a normal band, the needle keeps its own colour."
|
|
848
|
+
},
|
|
849
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
850
|
+
window: WINDOW
|
|
851
|
+
},
|
|
852
|
+
defaults: { title: "Compass" },
|
|
853
|
+
interactions: {},
|
|
854
|
+
freshness: true,
|
|
855
|
+
layout: { defaultSpan: { cols: 4, rows: 4 }, minSpan: { cols: 3, rows: 3 }, maxSpan: { cols: 8, rows: 8 } }
|
|
856
|
+
}),
|
|
857
|
+
"progress-ring": defineWidgetDescriptor({
|
|
858
|
+
id: "progress-ring",
|
|
859
|
+
version: VERSION,
|
|
860
|
+
sdkApiVersion: 1,
|
|
861
|
+
optionsVersion: 1,
|
|
862
|
+
display: { title: "Progress ring", category: "Telemetry", titleId: "widgetTypeProgressRing", categoryId: "widgetCategoryTelemetry" },
|
|
863
|
+
channel: "measurement",
|
|
864
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
865
|
+
binding: { window: { option: "window", min: 1 } },
|
|
866
|
+
options: {
|
|
867
|
+
title: TITLE_OR_MEASUREMENT,
|
|
868
|
+
measurement: MEASUREMENT,
|
|
869
|
+
min: SCALE_MIN,
|
|
870
|
+
max: SCALE_MAX,
|
|
871
|
+
readout: {
|
|
872
|
+
kind: "enum",
|
|
873
|
+
values: ["percent", "value"],
|
|
874
|
+
labelId: "widgetLabelReadout",
|
|
875
|
+
valueLabelIds: { percent: "widgetReadoutPercent", value: "widgetReadoutValue" },
|
|
876
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
877
|
+
doc: "What the middle of the ring shows: the percent of the scale (default) or the value with its unit."
|
|
878
|
+
},
|
|
879
|
+
unit: UNIT,
|
|
880
|
+
precision: PRECISION,
|
|
881
|
+
thresholds: THRESHOLDS,
|
|
882
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
883
|
+
window: WINDOW
|
|
884
|
+
},
|
|
885
|
+
constraints: [{ kind: "greater-than", key: "max", than: "min" }],
|
|
886
|
+
defaults: { title: "Progress ring" },
|
|
887
|
+
interactions: {},
|
|
888
|
+
freshness: true,
|
|
889
|
+
layout: { defaultSpan: { cols: 3, rows: 3 }, minSpan: { cols: 2, rows: 2 }, maxSpan: { cols: 6, rows: 6 } }
|
|
890
|
+
}),
|
|
891
|
+
"command-slider": defineWidgetDescriptor({
|
|
892
|
+
id: "command-slider",
|
|
893
|
+
version: VERSION,
|
|
894
|
+
sdkApiVersion: 1,
|
|
895
|
+
optionsVersion: 1,
|
|
896
|
+
display: { title: "Command slider", category: "Control", titleId: "widgetTypeCommandSlider", categoryId: "widgetCategoryControl" },
|
|
897
|
+
channel: "control",
|
|
898
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
899
|
+
// The control reads its last-sent value off this history, so it asks for a long one.
|
|
900
|
+
binding: { pageSize: { const: 50 } },
|
|
901
|
+
options: {
|
|
902
|
+
title: TITLE,
|
|
903
|
+
commandName: CONTROL_COMMAND_NAME,
|
|
904
|
+
commandLabel: CONTROL_COMMAND_LABEL,
|
|
905
|
+
parameterSchema: CONTROL_PARAMETER_SCHEMA,
|
|
906
|
+
parameter: CONTROL_PARAMETER,
|
|
907
|
+
min: {
|
|
908
|
+
kind: "number",
|
|
909
|
+
labelId: "widgetLabelMin",
|
|
910
|
+
helpId: "widgetHintSliderRange",
|
|
911
|
+
doc: "Lowest value, used only when the command's parameter declares no minimum."
|
|
912
|
+
},
|
|
913
|
+
max: {
|
|
914
|
+
kind: "number",
|
|
915
|
+
labelId: "widgetLabelMax",
|
|
916
|
+
helpId: "widgetHintSliderRange",
|
|
917
|
+
doc: "Highest value, used only when the command's parameter declares no maximum."
|
|
918
|
+
},
|
|
919
|
+
step: {
|
|
920
|
+
kind: "number",
|
|
921
|
+
min: 0,
|
|
922
|
+
labelId: "widgetLabelStep",
|
|
923
|
+
helpId: "widgetHintSliderStep",
|
|
924
|
+
doc: "Distance between values. Defaults to 1 for a whole-number parameter, else a hundredth of the range."
|
|
925
|
+
}
|
|
926
|
+
},
|
|
927
|
+
constraints: [{ kind: "greater-than", key: "max", than: "min" }],
|
|
928
|
+
defaults: { title: "Command slider" },
|
|
929
|
+
interactions: { commandSend: true },
|
|
930
|
+
layout: { defaultSpan: { cols: 4, rows: 2 }, minSpan: { cols: 3, rows: 2 }, maxSpan: { cols: 8, rows: 3 } }
|
|
931
|
+
}),
|
|
932
|
+
"command-toggle": defineWidgetDescriptor({
|
|
933
|
+
id: "command-toggle",
|
|
934
|
+
version: VERSION,
|
|
935
|
+
sdkApiVersion: 1,
|
|
936
|
+
optionsVersion: 1,
|
|
937
|
+
display: { title: "Command toggle", category: "Control", titleId: "widgetTypeCommandToggle", categoryId: "widgetCategoryControl" },
|
|
938
|
+
channel: "control",
|
|
939
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
940
|
+
binding: { pageSize: { const: 50 } },
|
|
941
|
+
options: {
|
|
942
|
+
title: TITLE,
|
|
943
|
+
commandName: CONTROL_COMMAND_NAME,
|
|
944
|
+
commandLabel: CONTROL_COMMAND_LABEL,
|
|
945
|
+
parameterSchema: CONTROL_PARAMETER_SCHEMA,
|
|
946
|
+
parameter: CONTROL_PARAMETER
|
|
947
|
+
},
|
|
948
|
+
defaults: { title: "Command toggle" },
|
|
949
|
+
interactions: { commandSend: true },
|
|
950
|
+
layout: { defaultSpan: { cols: 3, rows: 2 }, minSpan: { cols: 2, rows: 2 }, maxSpan: { cols: 6, rows: 3 } }
|
|
951
|
+
}),
|
|
952
|
+
"stat-card": defineWidgetDescriptor({
|
|
953
|
+
id: "stat-card",
|
|
954
|
+
version: VERSION,
|
|
955
|
+
sdkApiVersion: 1,
|
|
956
|
+
optionsVersion: 1,
|
|
957
|
+
display: { title: "Stat card", category: "Telemetry", titleId: "widgetTypeStatCard", categoryId: "widgetCategoryTelemetry" },
|
|
958
|
+
channel: "aggregate",
|
|
959
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
960
|
+
binding: { windowSeconds: { option: "windowSeconds" } },
|
|
961
|
+
options: {
|
|
962
|
+
title: TITLE_OR_MEASUREMENT,
|
|
963
|
+
measurement: MEASUREMENT,
|
|
964
|
+
stat: {
|
|
965
|
+
kind: "enum",
|
|
966
|
+
values: ["avg", "min", "max", "sum", "count", "last"],
|
|
967
|
+
labelId: "widgetLabelStat",
|
|
968
|
+
helpId: "widgetHintStat",
|
|
969
|
+
valueLabelIds: {
|
|
970
|
+
avg: "widgetStatAvg",
|
|
971
|
+
min: "widgetStatMin",
|
|
972
|
+
max: "widgetStatMax",
|
|
973
|
+
sum: "widgetStatSum",
|
|
974
|
+
count: "widgetStatCount",
|
|
975
|
+
last: "widgetStatLast"
|
|
976
|
+
},
|
|
977
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
978
|
+
doc: "Which figure to show. Default average."
|
|
979
|
+
},
|
|
980
|
+
unit: UNIT,
|
|
981
|
+
precision: PRECISION,
|
|
982
|
+
thresholds: THRESHOLDS,
|
|
983
|
+
windowSeconds: WINDOW_SECONDS
|
|
984
|
+
},
|
|
985
|
+
defaults: { title: "Stat card" },
|
|
986
|
+
interactions: {},
|
|
987
|
+
layout: { defaultSpan: { cols: 3, rows: 3 }, minSpan: { cols: 3, rows: 2 }, maxSpan: { cols: 6, rows: 4 } }
|
|
988
|
+
}),
|
|
989
|
+
"bar-chart": defineWidgetDescriptor({
|
|
990
|
+
id: "bar-chart",
|
|
991
|
+
version: VERSION,
|
|
992
|
+
sdkApiVersion: 1,
|
|
993
|
+
optionsVersion: 1,
|
|
994
|
+
display: { title: "Bar chart", category: "Telemetry", titleId: "widgetTypeBarChart", categoryId: "widgetCategoryTelemetry" },
|
|
995
|
+
channel: "measurement",
|
|
996
|
+
datasource: { mode: "required", selectors: ALL_SELECTORS },
|
|
997
|
+
// One bar per measurement of a device, or (groupBy device) one bar per member of an anchor.
|
|
998
|
+
binding: { members: { option: "groupBy", equals: "device" } },
|
|
999
|
+
options: {
|
|
1000
|
+
title: TITLE,
|
|
1001
|
+
groupBy: {
|
|
1002
|
+
kind: "enum",
|
|
1003
|
+
values: ["measurement", "device"],
|
|
1004
|
+
labelId: "widgetLabelGroupBy",
|
|
1005
|
+
helpId: "widgetHintGroupBy",
|
|
1006
|
+
valueLabelIds: { measurement: "widgetGroupByMeasurement", device: "widgetGroupByDevice" },
|
|
1007
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
1008
|
+
doc: "One bar per measurement (the default), or per device of the group for the measurement set below."
|
|
1009
|
+
},
|
|
1010
|
+
measurement: MEASUREMENT,
|
|
1011
|
+
unit: UNIT,
|
|
1012
|
+
precision: PRECISION,
|
|
1013
|
+
sort: CATEGORY_SORT
|
|
1014
|
+
},
|
|
1015
|
+
defaults: { title: "Bar chart" },
|
|
1016
|
+
interactions: {},
|
|
1017
|
+
layout: { defaultSpan: { cols: 6, rows: 4 }, minSpan: { cols: 4, rows: 3 } }
|
|
1018
|
+
}),
|
|
1019
|
+
"pie-chart": defineWidgetDescriptor({
|
|
1020
|
+
id: "pie-chart",
|
|
1021
|
+
version: VERSION,
|
|
1022
|
+
sdkApiVersion: 1,
|
|
1023
|
+
optionsVersion: 1,
|
|
1024
|
+
display: { title: "Pie chart", category: "Telemetry", titleId: "widgetTypePieChart", categoryId: "widgetCategoryTelemetry" },
|
|
1025
|
+
channel: "measurement",
|
|
1026
|
+
datasource: { mode: "required", selectors: ONE_DEVICE },
|
|
1027
|
+
binding: {},
|
|
1028
|
+
options: { title: TITLE, unit: UNIT, precision: PRECISION, sort: CATEGORY_SORT },
|
|
1029
|
+
defaults: { title: "Pie chart" },
|
|
1030
|
+
interactions: {},
|
|
1031
|
+
layout: { defaultSpan: { cols: 4, rows: 4 }, minSpan: { cols: 3, rows: 3 }, maxSpan: { cols: 8, rows: 8 } }
|
|
1032
|
+
}),
|
|
1033
|
+
// A new widget rather than a mode of `table`: that one is one device's measurements as rows, on a
|
|
1034
|
+
// channel that keys its values by name; this one is a fleet, a row per member and a column per
|
|
1035
|
+
// measurement, and reads its data by device.
|
|
1036
|
+
"fleet-table": defineWidgetDescriptor({
|
|
1037
|
+
id: "fleet-table",
|
|
1038
|
+
version: VERSION,
|
|
1039
|
+
sdkApiVersion: 1,
|
|
1040
|
+
optionsVersion: 1,
|
|
1041
|
+
display: { title: "Fleet table", category: "Telemetry", titleId: "widgetTypeFleetTable", categoryId: "widgetCategoryTelemetry" },
|
|
1042
|
+
channel: "measurement",
|
|
1043
|
+
datasource: { mode: "required", selectors: ALL_SELECTORS },
|
|
1044
|
+
binding: { windowSeconds: { option: "windowSeconds" }, members: { const: true } },
|
|
1045
|
+
options: {
|
|
1046
|
+
title: TITLE,
|
|
1047
|
+
columns: FLEET_COLUMNS,
|
|
1048
|
+
thresholds: FLEET_THRESHOLDS,
|
|
1049
|
+
staleAfterSeconds: STALE_AFTER_SECONDS,
|
|
1050
|
+
ageLagSeconds: {
|
|
1051
|
+
kind: "number",
|
|
1052
|
+
integer: true,
|
|
1053
|
+
min: 1,
|
|
1054
|
+
labelId: "widgetLabelAgeLag",
|
|
1055
|
+
helpId: "widgetHintAgeLag",
|
|
1056
|
+
doc: "Show a reading's age under it only when the reading is at least this many seconds older than the newest reading in the table (or stale). Unset shows every reading's age."
|
|
1057
|
+
},
|
|
1058
|
+
sortBy: {
|
|
1059
|
+
kind: "string",
|
|
1060
|
+
labelId: "widgetLabelSortBy",
|
|
1061
|
+
helpId: "widgetHintSortBy",
|
|
1062
|
+
doc: "The measurement the table is ordered by. Blank orders by device name."
|
|
1063
|
+
},
|
|
1064
|
+
sortDescending: { kind: "boolean", labelId: "widgetLabelSortDescending", doc: "Largest first." },
|
|
1065
|
+
pageSize: {
|
|
1066
|
+
kind: "number",
|
|
1067
|
+
integer: true,
|
|
1068
|
+
min: 5,
|
|
1069
|
+
max: 100,
|
|
1070
|
+
labelId: "widgetLabelPageSize",
|
|
1071
|
+
doc: "Devices per page. Default 20."
|
|
1072
|
+
},
|
|
1073
|
+
selectionTarget: SELECTION_TARGET,
|
|
1074
|
+
windowSeconds: WINDOW_SECONDS
|
|
1075
|
+
},
|
|
1076
|
+
defaults: { title: "Fleet table" },
|
|
1077
|
+
// The frame shows the age of the newest reading in the fleet; each cell shows its own.
|
|
1078
|
+
freshness: true,
|
|
1079
|
+
interactions: { selection: true },
|
|
1080
|
+
layout: { defaultSpan: { cols: 12, rows: 6 }, minSpan: { cols: 6, rows: 3 } }
|
|
474
1081
|
})
|
|
475
1082
|
};
|
|
476
1083
|
var builtinWidgetCatalog = createWidgetCatalog(Object.values(builtinWidgetDescriptors));
|
|
477
1084
|
|
|
1085
|
+
// src/widgets/lazy-chunk.tsx
|
|
1086
|
+
import { useEffect, useState as useState2 } from "react";
|
|
1087
|
+
import { jsx } from "react/jsx-runtime";
|
|
1088
|
+
function lazyChunk(importer) {
|
|
1089
|
+
let loaded;
|
|
1090
|
+
let loading;
|
|
1091
|
+
const load = () => loading ?? (loading = importer().then(
|
|
1092
|
+
(m) => loaded = m,
|
|
1093
|
+
(err) => {
|
|
1094
|
+
loading = void 0;
|
|
1095
|
+
throw err;
|
|
1096
|
+
}
|
|
1097
|
+
));
|
|
1098
|
+
function component(name) {
|
|
1099
|
+
function LazyChunkWidget(props) {
|
|
1100
|
+
const [mod, setMod] = useState2(loaded);
|
|
1101
|
+
const [error, setError] = useState2();
|
|
1102
|
+
useEffect(() => {
|
|
1103
|
+
if (!mod) load().then(setMod, setError);
|
|
1104
|
+
}, [mod]);
|
|
1105
|
+
if (error !== void 0) throw error;
|
|
1106
|
+
if (!mod) return null;
|
|
1107
|
+
const Component2 = mod[name];
|
|
1108
|
+
return /* @__PURE__ */ jsx(Component2, { ...props });
|
|
1109
|
+
}
|
|
1110
|
+
LazyChunkWidget.displayName = `Lazy(${name})`;
|
|
1111
|
+
return LazyChunkWidget;
|
|
1112
|
+
}
|
|
1113
|
+
return { load, component };
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// src/widgets/aggregate-lazy.tsx
|
|
1117
|
+
var chunk = lazyChunk(() => import("./chunks/aggregate-widgets-WVXAW3UL.js"));
|
|
1118
|
+
var loadAggregateWidgets = chunk.load;
|
|
1119
|
+
var statCardWidget = defineWidget({
|
|
1120
|
+
descriptor: builtinWidgetDescriptors["stat-card"],
|
|
1121
|
+
implementation: { kind: "trusted-react", component: chunk.component("StatCard") }
|
|
1122
|
+
});
|
|
1123
|
+
var barChartWidget = defineWidget({
|
|
1124
|
+
descriptor: builtinWidgetDescriptors["bar-chart"],
|
|
1125
|
+
implementation: { kind: "trusted-react", component: chunk.component("BarChart") }
|
|
1126
|
+
});
|
|
1127
|
+
var pieChartWidget = defineWidget({
|
|
1128
|
+
descriptor: builtinWidgetDescriptors["pie-chart"],
|
|
1129
|
+
implementation: { kind: "trusted-react", component: chunk.component("PieChart") }
|
|
1130
|
+
});
|
|
1131
|
+
var fleetTableWidget = defineWidget({
|
|
1132
|
+
descriptor: builtinWidgetDescriptors["fleet-table"],
|
|
1133
|
+
implementation: { kind: "trusted-react", component: chunk.component("FleetTable") }
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
// src/widgets/alarm-count.tsx
|
|
1137
|
+
import {
|
|
1138
|
+
css as css2,
|
|
1139
|
+
defineWidget as defineWidget2,
|
|
1140
|
+
fitValueFont,
|
|
1141
|
+
fitValueFontCss,
|
|
1142
|
+
FIT_MIN_FONT_PX,
|
|
1143
|
+
fontVar,
|
|
1144
|
+
optString,
|
|
1145
|
+
spaceVar,
|
|
1146
|
+
useSeverityColors,
|
|
1147
|
+
useStickyChars,
|
|
1148
|
+
useWidgetScale,
|
|
1149
|
+
WidgetFrame
|
|
1150
|
+
} from "@devicechain/widget-sdk";
|
|
1151
|
+
|
|
478
1152
|
// src/widgets/severity.ts
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
MAJOR: "
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
// amber-600 (readable as foreground; console uses amber-400 as a fill)
|
|
486
|
-
WARNING: "#0ea5e9",
|
|
487
|
-
// sky-500 — cool end of the ramp, matching the console
|
|
488
|
-
INDETERMINATE: "#64748b"
|
|
489
|
-
// slate-500
|
|
1153
|
+
import { css } from "@devicechain/widget-sdk";
|
|
1154
|
+
var LEVEL_OF = {
|
|
1155
|
+
CRITICAL: "critical",
|
|
1156
|
+
MAJOR: "major",
|
|
1157
|
+
MINOR: "minor",
|
|
1158
|
+
WARNING: "warning"
|
|
490
1159
|
};
|
|
491
1160
|
var SEVERITY_ORDER = {
|
|
492
1161
|
CRITICAL: 0,
|
|
@@ -495,9 +1164,9 @@ var SEVERITY_ORDER = {
|
|
|
495
1164
|
WARNING: 3,
|
|
496
1165
|
INDETERMINATE: 4
|
|
497
1166
|
};
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
return
|
|
1167
|
+
function severityColor(colors, severity) {
|
|
1168
|
+
const level = LEVEL_OF[severity];
|
|
1169
|
+
return level ? colors[level] : css("muted-foreground");
|
|
501
1170
|
}
|
|
502
1171
|
function severityRank(severity) {
|
|
503
1172
|
return SEVERITY_ORDER[severity] ?? 99;
|
|
@@ -515,86 +1184,133 @@ function highestSeverity(severities) {
|
|
|
515
1184
|
}
|
|
516
1185
|
|
|
517
1186
|
// src/widgets/alarm-count.tsx
|
|
518
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
519
|
-
var
|
|
520
|
-
function
|
|
521
|
-
const
|
|
1187
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1188
|
+
var ALARM_COUNT_CAPTION_ROOM_PX = 20;
|
|
1189
|
+
function AlarmCountBody({ data }) {
|
|
1190
|
+
const { size, scale } = useWidgetScale();
|
|
1191
|
+
const severity = useSeverityColors();
|
|
522
1192
|
const { total, alarms, loading } = data;
|
|
523
1193
|
const worst = highestSeverity(alarms.map((a) => a.severity));
|
|
524
1194
|
const alerting = total > 0 && worst != null;
|
|
525
|
-
const color = alerting ? severityColor(worst) :
|
|
526
|
-
const valueFont = size.height > 0 ? clamp(Math.round(size.height / 2.8), 20, 56) : 40;
|
|
1195
|
+
const color = alerting ? severityColor(severity, worst) : css2("muted-foreground");
|
|
527
1196
|
const display = loading && total === 0 ? "\u2014" : String(total);
|
|
1197
|
+
const chars = useStickyChars(display.length);
|
|
1198
|
+
const floor = Math.round(FIT_MIN_FONT_PX * scale);
|
|
1199
|
+
const measured = size.width > 0 && size.height > 0;
|
|
1200
|
+
const valueFont = measured ? fitValueFont(
|
|
1201
|
+
{ width: size.width, height: Math.max(1, size.height - ALARM_COUNT_CAPTION_ROOM_PX * scale) },
|
|
1202
|
+
chars,
|
|
1203
|
+
{ floor }
|
|
1204
|
+
) : (
|
|
1205
|
+
// First frame: the same curve in cq units, resolved by the browser against the frame's
|
|
1206
|
+
// column (it does not take the caption's room off; the first measure does).
|
|
1207
|
+
fitValueFontCss(chars, { floor })
|
|
1208
|
+
);
|
|
528
1209
|
const unit = total === 1 ? "alarm" : "alarms";
|
|
529
1210
|
const ariaLabel = alerting ? `${total} ${unit}, worst severity ${severityLabel(worst)}` : `${total} ${unit}`;
|
|
530
|
-
return /* @__PURE__ */
|
|
1211
|
+
return /* @__PURE__ */ jsxs(
|
|
531
1212
|
"div",
|
|
532
1213
|
{
|
|
533
|
-
ref: sizeRef,
|
|
534
1214
|
style: {
|
|
535
1215
|
display: "flex",
|
|
536
1216
|
flexDirection: "column",
|
|
537
|
-
justifyContent: "center",
|
|
1217
|
+
justifyContent: "safe center",
|
|
538
1218
|
alignItems: "center",
|
|
539
1219
|
height: "100%",
|
|
540
|
-
|
|
1220
|
+
boxSizing: "border-box",
|
|
1221
|
+
overflow: "hidden",
|
|
541
1222
|
textAlign: "center"
|
|
542
1223
|
},
|
|
543
1224
|
children: [
|
|
544
|
-
/* @__PURE__ */
|
|
1225
|
+
/* @__PURE__ */ jsx2(
|
|
1226
|
+
"div",
|
|
1227
|
+
{
|
|
1228
|
+
"aria-label": ariaLabel,
|
|
1229
|
+
style: {
|
|
1230
|
+
fontSize: valueFont,
|
|
1231
|
+
fontWeight: 700,
|
|
1232
|
+
lineHeight: 1,
|
|
1233
|
+
whiteSpace: "nowrap",
|
|
1234
|
+
fontVariantNumeric: "tabular-nums",
|
|
1235
|
+
color
|
|
1236
|
+
},
|
|
1237
|
+
children: display
|
|
1238
|
+
}
|
|
1239
|
+
),
|
|
545
1240
|
/* @__PURE__ */ jsxs(
|
|
546
1241
|
"div",
|
|
547
1242
|
{
|
|
548
1243
|
style: {
|
|
549
|
-
fontSize:
|
|
550
|
-
color:
|
|
551
|
-
marginTop:
|
|
1244
|
+
fontSize: fontVar("xs"),
|
|
1245
|
+
color: css2("muted-foreground"),
|
|
1246
|
+
marginTop: spaceVar(1),
|
|
552
1247
|
textTransform: "uppercase",
|
|
553
1248
|
letterSpacing: "0.05em"
|
|
554
1249
|
},
|
|
555
1250
|
children: [
|
|
556
|
-
/* @__PURE__ */
|
|
557
|
-
alerting ? /* @__PURE__ */
|
|
1251
|
+
/* @__PURE__ */ jsx2("span", { children: unit }),
|
|
1252
|
+
alerting ? /* @__PURE__ */ jsx2("span", { children: ` \xB7 worst ${severityLabel(worst)}` }) : null
|
|
558
1253
|
]
|
|
559
1254
|
}
|
|
560
1255
|
)
|
|
561
1256
|
]
|
|
562
1257
|
}
|
|
563
|
-
)
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
function AlarmCount({ widget, data }) {
|
|
1261
|
+
return /* @__PURE__ */ jsx2(WidgetFrame, { title: optString(widget.options, "title"), children: /* @__PURE__ */ jsx2(AlarmCountBody, { data }) });
|
|
564
1262
|
}
|
|
565
|
-
var alarmCountWidget =
|
|
1263
|
+
var alarmCountWidget = defineWidget2({
|
|
566
1264
|
descriptor: builtinWidgetDescriptors["alarm-count"],
|
|
567
1265
|
implementation: { kind: "trusted-react", component: AlarmCount }
|
|
568
1266
|
});
|
|
569
1267
|
|
|
570
1268
|
// src/widgets/alarm-table.tsx
|
|
571
|
-
import { useState } from "react";
|
|
1269
|
+
import { Fragment, useState as useState3 } from "react";
|
|
572
1270
|
import {
|
|
573
|
-
css as
|
|
574
|
-
|
|
1271
|
+
css as css3,
|
|
1272
|
+
fontVar as fontVar2,
|
|
1273
|
+
defineWidget as defineWidget3,
|
|
575
1274
|
FlashValue,
|
|
576
1275
|
formatDateTime,
|
|
577
1276
|
optBoolean,
|
|
578
|
-
optNumber
|
|
579
|
-
optString as
|
|
1277
|
+
optNumber,
|
|
1278
|
+
optString as optString2,
|
|
1279
|
+
useElementSize,
|
|
1280
|
+
useSeverityColors as useSeverityColors2,
|
|
580
1281
|
useWidgetSelect,
|
|
581
1282
|
useWidgetTimeZone,
|
|
582
1283
|
WidgetFrame as WidgetFrame2
|
|
583
1284
|
} from "@devicechain/widget-sdk";
|
|
584
|
-
import { Fragment, jsx as
|
|
1285
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
585
1286
|
var cell = { padding: "6px 10px", textAlign: "left", whiteSpace: "nowrap" };
|
|
586
1287
|
var headCell = {
|
|
587
1288
|
...cell,
|
|
588
1289
|
position: "sticky",
|
|
589
1290
|
top: 0,
|
|
590
|
-
background:
|
|
591
|
-
fontSize:
|
|
1291
|
+
background: css3("card"),
|
|
1292
|
+
fontSize: fontVar2("xs"),
|
|
592
1293
|
fontWeight: 600,
|
|
593
1294
|
textTransform: "uppercase",
|
|
594
1295
|
letterSpacing: "0.04em",
|
|
595
|
-
color:
|
|
596
|
-
borderBottom: `1px solid ${
|
|
1296
|
+
color: css3("muted-foreground"),
|
|
1297
|
+
borderBottom: `1px solid ${css3("border")}`
|
|
1298
|
+
};
|
|
1299
|
+
var ALARM_TABLE_NARROW_PX = 480;
|
|
1300
|
+
var narrowCell = {
|
|
1301
|
+
...cell,
|
|
1302
|
+
padding: "6px 6px",
|
|
1303
|
+
whiteSpace: "normal",
|
|
1304
|
+
overflowWrap: "break-word",
|
|
1305
|
+
wordBreak: "normal"
|
|
597
1306
|
};
|
|
1307
|
+
function hyphenBreaks(text) {
|
|
1308
|
+
const parts = text.split("-").map((p, i, all) => i < all.length - 1 ? `${p}-` : p);
|
|
1309
|
+
return parts.map((part, i) => /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1310
|
+
i > 0 ? /* @__PURE__ */ jsx3("wbr", {}) : null,
|
|
1311
|
+
part
|
|
1312
|
+
] }, i));
|
|
1313
|
+
}
|
|
598
1314
|
function statusLabel(alarm) {
|
|
599
1315
|
if (alarm.state === "CLEARED") return "Cleared";
|
|
600
1316
|
return alarm.acknowledged ? "Acknowledged" : "Active";
|
|
@@ -602,41 +1318,45 @@ function statusLabel(alarm) {
|
|
|
602
1318
|
function AlarmTable({ widget, data, actions }) {
|
|
603
1319
|
const { alarms, total, loading } = data;
|
|
604
1320
|
const canAct = actions?.can("alarm:write") ?? false;
|
|
605
|
-
const
|
|
606
|
-
const
|
|
1321
|
+
const [sizeRef, size] = useElementSize();
|
|
1322
|
+
const severity = useSeverityColors2();
|
|
1323
|
+
const narrow = size.width > 0 && size.width < ALARM_TABLE_NARROW_PX;
|
|
1324
|
+
const columns = 5 + (narrow ? 0 : 2) + (canAct ? 1 : 0);
|
|
1325
|
+
const text = narrow ? narrowCell : cell;
|
|
1326
|
+
const precision = optNumber(widget.options, "precision");
|
|
607
1327
|
const flash = optBoolean(widget.options, "flashOnChange");
|
|
608
1328
|
const select = useWidgetSelect();
|
|
609
1329
|
const timeZone = useWidgetTimeZone();
|
|
610
|
-
const selectionTarget =
|
|
1330
|
+
const selectionTarget = optString2(widget.options, "selectionTarget");
|
|
611
1331
|
const drillTo = select && selectionTarget ? { select, slot: selectionTarget } : void 0;
|
|
612
|
-
return /* @__PURE__ */
|
|
613
|
-
/* @__PURE__ */
|
|
1332
|
+
return /* @__PURE__ */ jsx3(WidgetFrame2, { title: optString2(widget.options, "title"), flush: true, children: /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
|
|
1333
|
+
/* @__PURE__ */ jsx3("div", { ref: sizeRef, style: { flex: 1, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ jsxs2(
|
|
614
1334
|
"table",
|
|
615
1335
|
{
|
|
616
1336
|
style: {
|
|
617
1337
|
width: "100%",
|
|
618
1338
|
borderCollapse: "collapse",
|
|
619
|
-
fontSize:
|
|
620
|
-
color:
|
|
1339
|
+
fontSize: fontVar2("md"),
|
|
1340
|
+
color: css3("foreground"),
|
|
621
1341
|
fontVariantNumeric: "tabular-nums"
|
|
622
1342
|
},
|
|
623
1343
|
children: [
|
|
624
|
-
/* @__PURE__ */
|
|
625
|
-
/* @__PURE__ */
|
|
626
|
-
/* @__PURE__ */
|
|
627
|
-
/* @__PURE__ */
|
|
628
|
-
/* @__PURE__ */
|
|
629
|
-
/* @__PURE__ */
|
|
630
|
-
/* @__PURE__ */
|
|
631
|
-
/* @__PURE__ */
|
|
632
|
-
canAct ? /* @__PURE__ */
|
|
1344
|
+
/* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
|
|
1345
|
+
/* @__PURE__ */ jsx3("th", { style: { ...headCell, width: 4, padding: 0 }, "aria-hidden": "true" }),
|
|
1346
|
+
/* @__PURE__ */ jsx3("th", { scope: "col", style: headCell, children: "Severity" }),
|
|
1347
|
+
narrow ? null : /* @__PURE__ */ jsx3("th", { scope: "col", style: headCell, children: "Status" }),
|
|
1348
|
+
/* @__PURE__ */ jsx3("th", { scope: "col", style: headCell, children: "Originator" }),
|
|
1349
|
+
/* @__PURE__ */ jsx3("th", { scope: "col", style: headCell, children: "Alarm" }),
|
|
1350
|
+
/* @__PURE__ */ jsx3("th", { scope: "col", style: { ...headCell, textAlign: "right" }, children: "Value" }),
|
|
1351
|
+
narrow ? null : /* @__PURE__ */ jsx3("th", { scope: "col", style: headCell, children: "Raised" }),
|
|
1352
|
+
canAct ? /* @__PURE__ */ jsx3("th", { scope: "col", style: { ...headCell, textAlign: "right" }, children: "Actions" }) : null
|
|
633
1353
|
] }) }),
|
|
634
|
-
/* @__PURE__ */
|
|
635
|
-
const color = severityColor(alarm.severity);
|
|
636
|
-
return /* @__PURE__ */ jsxs2("tr", { style: { borderBottom: `1px solid ${
|
|
637
|
-
/* @__PURE__ */
|
|
638
|
-
/* @__PURE__ */
|
|
639
|
-
/* @__PURE__ */
|
|
1354
|
+
/* @__PURE__ */ jsx3("tbody", { children: alarms.length === 0 ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { colSpan: columns, style: { ...cell, textAlign: "center", color: css3("muted-foreground") }, children: loading ? "Loading\u2026" : "No alarms" }) }) : alarms.map((alarm) => {
|
|
1355
|
+
const color = severityColor(severity, alarm.severity);
|
|
1356
|
+
return /* @__PURE__ */ jsxs2("tr", { style: { borderBottom: `1px solid ${css3("border")}` }, children: [
|
|
1357
|
+
/* @__PURE__ */ jsx3("td", { "aria-hidden": "true", style: { padding: 0, width: 4, background: color } }),
|
|
1358
|
+
/* @__PURE__ */ jsx3("td", { style: cell, children: /* @__PURE__ */ jsxs2("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
|
|
1359
|
+
/* @__PURE__ */ jsx3(
|
|
640
1360
|
"span",
|
|
641
1361
|
{
|
|
642
1362
|
"aria-hidden": "true",
|
|
@@ -645,10 +1365,10 @@ function AlarmTable({ widget, data, actions }) {
|
|
|
645
1365
|
),
|
|
646
1366
|
severityLabel(alarm.severity)
|
|
647
1367
|
] }) }),
|
|
648
|
-
/* @__PURE__ */
|
|
649
|
-
/* @__PURE__ */
|
|
650
|
-
/* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
1368
|
+
narrow ? null : /* @__PURE__ */ jsx3("td", { style: { ...cell, color: css3("muted-foreground") }, children: statusLabel(alarm) }),
|
|
1369
|
+
/* @__PURE__ */ jsx3("td", { style: text, children: /* @__PURE__ */ jsx3(OriginatorCell, { alarm, drillTo }) }),
|
|
1370
|
+
/* @__PURE__ */ jsx3("td", { style: text, children: hyphenBreaks(alarm.alarmKey) }),
|
|
1371
|
+
/* @__PURE__ */ jsx3("td", { style: { ...cell, textAlign: "right" }, children: /* @__PURE__ */ jsx3(
|
|
652
1372
|
FlashValue,
|
|
653
1373
|
{
|
|
654
1374
|
value: alarm.lastValue,
|
|
@@ -657,8 +1377,8 @@ function AlarmTable({ widget, data, actions }) {
|
|
|
657
1377
|
identity: alarm.token
|
|
658
1378
|
}
|
|
659
1379
|
) }),
|
|
660
|
-
/* @__PURE__ */
|
|
661
|
-
canAct ? /* @__PURE__ */
|
|
1380
|
+
narrow ? null : /* @__PURE__ */ jsx3("td", { style: { ...cell, color: css3("muted-foreground") }, children: formatDateTime(alarm.raisedTime, timeZone) }),
|
|
1381
|
+
canAct ? /* @__PURE__ */ jsx3("td", { style: { ...cell, textAlign: "right" }, children: /* @__PURE__ */ jsx3(AlarmRowActions, { alarm, actions }) }) : null
|
|
662
1382
|
] }, alarm.token);
|
|
663
1383
|
}) })
|
|
664
1384
|
]
|
|
@@ -669,9 +1389,9 @@ function AlarmTable({ widget, data, actions }) {
|
|
|
669
1389
|
{
|
|
670
1390
|
style: {
|
|
671
1391
|
padding: "4px 10px",
|
|
672
|
-
fontSize:
|
|
673
|
-
color:
|
|
674
|
-
borderTop: `1px solid ${
|
|
1392
|
+
fontSize: fontVar2("xs"),
|
|
1393
|
+
color: css3("muted-foreground"),
|
|
1394
|
+
borderTop: `1px solid ${css3("border")}`
|
|
675
1395
|
},
|
|
676
1396
|
children: [
|
|
677
1397
|
"Showing ",
|
|
@@ -689,7 +1409,7 @@ function OriginatorCell({
|
|
|
689
1409
|
}) {
|
|
690
1410
|
if (drillTo && alarm.originatorType === "device" && alarm.originatorToken) {
|
|
691
1411
|
const token = alarm.originatorToken;
|
|
692
|
-
return /* @__PURE__ */
|
|
1412
|
+
return /* @__PURE__ */ jsx3(
|
|
693
1413
|
"button",
|
|
694
1414
|
{
|
|
695
1415
|
type: "button",
|
|
@@ -700,32 +1420,33 @@ function OriginatorCell({
|
|
|
700
1420
|
border: "none",
|
|
701
1421
|
background: "none",
|
|
702
1422
|
font: "inherit",
|
|
703
|
-
color:
|
|
1423
|
+
color: css3("primary"),
|
|
704
1424
|
cursor: "pointer",
|
|
705
1425
|
textDecoration: "underline",
|
|
706
1426
|
textUnderlineOffset: 2
|
|
707
1427
|
},
|
|
708
|
-
children: token
|
|
1428
|
+
children: hyphenBreaks(token)
|
|
709
1429
|
}
|
|
710
1430
|
);
|
|
711
1431
|
}
|
|
712
|
-
return /* @__PURE__ */
|
|
1432
|
+
return /* @__PURE__ */ jsx3(Fragment2, { children: hyphenBreaks(alarm.originatorToken ?? alarm.originatorType) });
|
|
713
1433
|
}
|
|
714
1434
|
var actionButton = {
|
|
715
1435
|
padding: "2px 8px",
|
|
716
|
-
fontSize:
|
|
1436
|
+
fontSize: fontVar2("sm"),
|
|
717
1437
|
lineHeight: 1.4,
|
|
718
1438
|
borderRadius: 4,
|
|
719
|
-
border: `1px solid ${
|
|
720
|
-
background:
|
|
721
|
-
color:
|
|
1439
|
+
border: `1px solid ${css3("border")}`,
|
|
1440
|
+
background: css3("card"),
|
|
1441
|
+
color: css3("foreground"),
|
|
722
1442
|
cursor: "pointer"
|
|
723
1443
|
};
|
|
724
1444
|
function AlarmRowActions({ alarm, actions }) {
|
|
725
|
-
const
|
|
726
|
-
const [
|
|
1445
|
+
const severity = useSeverityColors2();
|
|
1446
|
+
const [pending, setPending] = useState3(null);
|
|
1447
|
+
const [failed, setFailed] = useState3(false);
|
|
727
1448
|
if (alarm.state !== "ACTIVE") {
|
|
728
|
-
return /* @__PURE__ */
|
|
1449
|
+
return /* @__PURE__ */ jsx3("span", { style: { color: css3("muted-foreground") }, children: "\u2014" });
|
|
729
1450
|
}
|
|
730
1451
|
const run = (kind, op) => {
|
|
731
1452
|
setPending(kind);
|
|
@@ -741,17 +1462,17 @@ function AlarmRowActions({ alarm, actions }) {
|
|
|
741
1462
|
promise.catch(() => setFailed(true)).finally(() => setPending(null));
|
|
742
1463
|
};
|
|
743
1464
|
return /* @__PURE__ */ jsxs2("span", { style: { display: "inline-flex", gap: 6, justifyContent: "flex-end", alignItems: "center" }, children: [
|
|
744
|
-
failed ? /* @__PURE__ */
|
|
1465
|
+
failed ? /* @__PURE__ */ jsx3(
|
|
745
1466
|
"span",
|
|
746
1467
|
{
|
|
747
1468
|
role: "img",
|
|
748
1469
|
"aria-label": "Action failed \u2014 retry",
|
|
749
1470
|
title: "Action failed \u2014 retry",
|
|
750
|
-
style: { color: severityColor("CRITICAL"), fontSize:
|
|
1471
|
+
style: { color: severityColor(severity, "CRITICAL"), fontSize: fontVar2("sm") },
|
|
751
1472
|
children: "\u26A0"
|
|
752
1473
|
}
|
|
753
1474
|
) : null,
|
|
754
|
-
!alarm.acknowledged ? /* @__PURE__ */
|
|
1475
|
+
!alarm.acknowledged ? /* @__PURE__ */ jsx3(
|
|
755
1476
|
"button",
|
|
756
1477
|
{
|
|
757
1478
|
type: "button",
|
|
@@ -763,196 +1484,50 @@ function AlarmRowActions({ alarm, actions }) {
|
|
|
763
1484
|
children: pending === "ack" ? "\u2026" : "Ack"
|
|
764
1485
|
}
|
|
765
1486
|
) : null,
|
|
766
|
-
/* @__PURE__ */
|
|
1487
|
+
/* @__PURE__ */ jsx3(
|
|
767
1488
|
"button",
|
|
768
1489
|
{
|
|
769
1490
|
type: "button",
|
|
770
1491
|
style: { ...actionButton, opacity: pending ? 0.6 : 1 },
|
|
771
1492
|
disabled: pending !== null,
|
|
772
|
-
"aria-busy": pending === "clear",
|
|
773
|
-
"aria-label": `Clear alarm ${alarm.alarmKey}`,
|
|
774
|
-
onClick: () => run("clear", () => actions.clearAlarm(alarm.token)),
|
|
775
|
-
children: pending === "clear" ? "\u2026" : "Clear"
|
|
776
|
-
}
|
|
777
|
-
)
|
|
778
|
-
] });
|
|
779
|
-
}
|
|
780
|
-
var alarmTableWidget = defineWidget2({
|
|
781
|
-
descriptor: builtinWidgetDescriptors["alarm-table"],
|
|
782
|
-
implementation: { kind: "trusted-react", component: AlarmTable }
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
// src/widgets/command-button.tsx
|
|
786
|
-
import { useEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
787
|
-
import {
|
|
788
|
-
css as css3,
|
|
789
|
-
defineWidget as defineWidget3,
|
|
790
|
-
formatDateTime as formatDateTime2,
|
|
791
|
-
optString as optString4,
|
|
792
|
-
useWidgetTimeZone as useWidgetTimeZone2,
|
|
793
|
-
WidgetFrame as WidgetFrame3
|
|
794
|
-
} from "@devicechain/widget-sdk";
|
|
795
|
-
|
|
796
|
-
// src/widgets/command-status.ts
|
|
797
|
-
import { isTerminalCommandStatus } from "@devicechain/dashboards";
|
|
798
|
-
var STATUS_COLORS = {
|
|
799
|
-
SUCCESSFUL: "#16a34a",
|
|
800
|
-
// green-600 — acknowledged by the device
|
|
801
|
-
FAILED: "#dc2626",
|
|
802
|
-
// red-600
|
|
803
|
-
TIMEOUT: "#dc2626",
|
|
804
|
-
// red-600 — never acknowledged in time
|
|
805
|
-
EXPIRED: "#64748b",
|
|
806
|
-
// slate-500 — lapsed before it was ever sent
|
|
807
|
-
CANCELLED: "#7c3aed",
|
|
808
|
-
// violet-600 — deliberately called off; a decision, not a failure
|
|
809
|
-
QUEUED: "#0284c7",
|
|
810
|
-
// sky-600 — in flight
|
|
811
|
-
SENT: "#0284c7",
|
|
812
|
-
HELD: "#d97706",
|
|
813
|
-
// amber-600 — withheld: the device is absent, the command still stands
|
|
814
|
-
PARKED: "#f59e0b"
|
|
815
|
-
// amber-500 — published, nobody home; held for the device's return
|
|
816
|
-
};
|
|
817
|
-
var UNKNOWN_COLOR2 = "#64748b";
|
|
818
|
-
function commandStatusColor(status) {
|
|
819
|
-
return STATUS_COLORS[status] ?? UNKNOWN_COLOR2;
|
|
820
|
-
}
|
|
821
|
-
function isTerminalStatus(status) {
|
|
822
|
-
return isTerminalCommandStatus(status);
|
|
823
|
-
}
|
|
824
|
-
function commandStatusLabel(status) {
|
|
825
|
-
if (!status) return "";
|
|
826
|
-
return status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
// src/widgets/command-params.ts
|
|
830
|
-
var DATA_TYPES = /* @__PURE__ */ new Set(["DOUBLE", "INT", "BOOLEAN", "STRING"]);
|
|
831
|
-
function parseParameterSchema(schema) {
|
|
832
|
-
if (!schema) return [];
|
|
833
|
-
let parsed;
|
|
834
|
-
try {
|
|
835
|
-
parsed = JSON.parse(schema);
|
|
836
|
-
} catch {
|
|
837
|
-
return [];
|
|
838
|
-
}
|
|
839
|
-
if (!Array.isArray(parsed)) return [];
|
|
840
|
-
const out = [];
|
|
841
|
-
for (const raw of parsed) {
|
|
842
|
-
const norm = normalizeParam(raw);
|
|
843
|
-
if (norm) out.push(norm);
|
|
844
|
-
}
|
|
845
|
-
return out;
|
|
846
|
-
}
|
|
847
|
-
function normalizeParam(raw) {
|
|
848
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
849
|
-
const r = raw;
|
|
850
|
-
if (typeof r.name !== "string") return null;
|
|
851
|
-
const param = { name: r.name };
|
|
852
|
-
if (typeof r.description === "string") param.description = r.description;
|
|
853
|
-
if (r.kind === "OBJECT") param.kind = "OBJECT";
|
|
854
|
-
else if (r.kind === "SCALAR") param.kind = "SCALAR";
|
|
855
|
-
if (typeof r.dataType === "string" && DATA_TYPES.has(r.dataType)) {
|
|
856
|
-
param.dataType = r.dataType;
|
|
857
|
-
}
|
|
858
|
-
if (typeof r.unit === "string") param.unit = r.unit;
|
|
859
|
-
if (r.required === true) param.required = true;
|
|
860
|
-
if (typeof r.default === "string") param.default = r.default;
|
|
861
|
-
if (typeof r.minValue === "number" && Number.isFinite(r.minValue)) param.minValue = r.minValue;
|
|
862
|
-
if (typeof r.maxValue === "number" && Number.isFinite(r.maxValue)) param.maxValue = r.maxValue;
|
|
863
|
-
if (Array.isArray(r.enum)) {
|
|
864
|
-
const values = r.enum.filter((v) => typeof v === "string");
|
|
865
|
-
if (values.length > 0) param.enum = values;
|
|
866
|
-
}
|
|
867
|
-
return param;
|
|
868
|
-
}
|
|
869
|
-
function isScalar(param) {
|
|
870
|
-
return param.kind !== "OBJECT";
|
|
871
|
-
}
|
|
872
|
-
function parseBool(raw) {
|
|
873
|
-
const s = raw.trim().toLowerCase();
|
|
874
|
-
return s === "true" || s === "1" || s === "t" || s === "yes" || s === "on";
|
|
875
|
-
}
|
|
876
|
-
function defaultValues(params) {
|
|
877
|
-
const values = {};
|
|
878
|
-
for (const p of params) {
|
|
879
|
-
if (!isScalar(p)) continue;
|
|
880
|
-
if (p.dataType === "BOOLEAN") values[p.name] = parseBool(p.default ?? "") ? "true" : "false";
|
|
881
|
-
else if (p.default != null) values[p.name] = p.default;
|
|
882
|
-
}
|
|
883
|
-
return values;
|
|
884
|
-
}
|
|
885
|
-
function validateParams(params, values) {
|
|
886
|
-
const errors = {};
|
|
887
|
-
for (const p of params) {
|
|
888
|
-
if (!isScalar(p)) {
|
|
889
|
-
if (p.required) errors[p.name] = "Structured parameter \u2014 not supported here";
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
if (p.dataType === "BOOLEAN") continue;
|
|
893
|
-
const raw = (values[p.name] ?? "").trim();
|
|
894
|
-
if (raw === "") {
|
|
895
|
-
if (p.required) errors[p.name] = "Required";
|
|
896
|
-
continue;
|
|
897
|
-
}
|
|
898
|
-
if (p.dataType === "INT" || p.dataType === "DOUBLE") {
|
|
899
|
-
const n = Number(raw);
|
|
900
|
-
if (!Number.isFinite(n)) {
|
|
901
|
-
errors[p.name] = "Must be a number";
|
|
902
|
-
} else if (p.dataType === "INT" && !Number.isInteger(n)) {
|
|
903
|
-
errors[p.name] = "Must be a whole number";
|
|
904
|
-
} else if (p.minValue != null && n < p.minValue) {
|
|
905
|
-
errors[p.name] = `Must be \u2265 ${p.minValue}`;
|
|
906
|
-
} else if (p.maxValue != null && n > p.maxValue) {
|
|
907
|
-
errors[p.name] = `Must be \u2264 ${p.maxValue}`;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
return errors;
|
|
912
|
-
}
|
|
913
|
-
function buildPayload(params, values) {
|
|
914
|
-
const payload = {};
|
|
915
|
-
for (const p of params) {
|
|
916
|
-
if (!isScalar(p)) continue;
|
|
917
|
-
const raw = (values[p.name] ?? "").trim();
|
|
918
|
-
if (p.dataType === "BOOLEAN") {
|
|
919
|
-
payload[p.name] = parseBool(raw);
|
|
920
|
-
continue;
|
|
921
|
-
}
|
|
922
|
-
if (raw === "") continue;
|
|
923
|
-
payload[p.name] = coerce(p, raw);
|
|
924
|
-
}
|
|
925
|
-
return Object.keys(payload).length === 0 ? void 0 : JSON.stringify(payload);
|
|
926
|
-
}
|
|
927
|
-
function coerce(param, raw) {
|
|
928
|
-
switch (param.dataType) {
|
|
929
|
-
case "INT":
|
|
930
|
-
case "DOUBLE": {
|
|
931
|
-
const n = Number(raw);
|
|
932
|
-
return Number.isFinite(n) ? n : raw;
|
|
933
|
-
}
|
|
934
|
-
case "BOOLEAN":
|
|
935
|
-
return parseBool(raw);
|
|
936
|
-
default:
|
|
937
|
-
return raw;
|
|
938
|
-
}
|
|
1493
|
+
"aria-busy": pending === "clear",
|
|
1494
|
+
"aria-label": `Clear alarm ${alarm.alarmKey}`,
|
|
1495
|
+
onClick: () => run("clear", () => actions.clearAlarm(alarm.token)),
|
|
1496
|
+
children: pending === "clear" ? "\u2026" : "Clear"
|
|
1497
|
+
}
|
|
1498
|
+
)
|
|
1499
|
+
] });
|
|
939
1500
|
}
|
|
1501
|
+
var alarmTableWidget = defineWidget3({
|
|
1502
|
+
descriptor: builtinWidgetDescriptors["alarm-table"],
|
|
1503
|
+
implementation: { kind: "trusted-react", component: AlarmTable }
|
|
1504
|
+
});
|
|
940
1505
|
|
|
941
1506
|
// src/widgets/command-button.tsx
|
|
942
|
-
import {
|
|
1507
|
+
import { useEffect as useEffect2, useMemo, useRef, useState as useState4 } from "react";
|
|
1508
|
+
import {
|
|
1509
|
+
css as css4,
|
|
1510
|
+
fontVar as fontVar3,
|
|
1511
|
+
defineWidget as defineWidget4,
|
|
1512
|
+
formatDateTime as formatDateTime2,
|
|
1513
|
+
optString as optString3,
|
|
1514
|
+
useWidgetTimeZone as useWidgetTimeZone2,
|
|
1515
|
+
WidgetFrame as WidgetFrame3
|
|
1516
|
+
} from "@devicechain/widget-sdk";
|
|
1517
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
943
1518
|
function CommandButton({ widget, data, actions }) {
|
|
944
|
-
const commandName =
|
|
945
|
-
const commandLabel =
|
|
946
|
-
const parameterSchema =
|
|
1519
|
+
const commandName = optString3(widget.options, "commandName");
|
|
1520
|
+
const commandLabel = optString3(widget.options, "commandLabel") ?? commandName;
|
|
1521
|
+
const parameterSchema = optString3(widget.options, "parameterSchema") ?? null;
|
|
947
1522
|
const params = useMemo(() => parseParameterSchema(parameterSchema), [parameterSchema]);
|
|
948
1523
|
const schemaKey = `${commandName ?? ""}::${parameterSchema ?? ""}`;
|
|
949
|
-
const [values, setValues] =
|
|
950
|
-
const [errors, setErrors] =
|
|
951
|
-
const [pending, setPending] =
|
|
952
|
-
const [sendError, setSendError] =
|
|
953
|
-
const [sentToken, setSentToken] =
|
|
1524
|
+
const [values, setValues] = useState4(() => defaultValues(params));
|
|
1525
|
+
const [errors, setErrors] = useState4({});
|
|
1526
|
+
const [pending, setPending] = useState4(false);
|
|
1527
|
+
const [sendError, setSendError] = useState4(null);
|
|
1528
|
+
const [sentToken, setSentToken] = useState4(null);
|
|
954
1529
|
const sendGen = useRef(0);
|
|
955
|
-
|
|
1530
|
+
useEffect2(() => {
|
|
956
1531
|
sendGen.current += 1;
|
|
957
1532
|
setValues(defaultValues(params));
|
|
958
1533
|
setErrors({});
|
|
@@ -1002,10 +1577,10 @@ function CommandButton({ widget, data, actions }) {
|
|
|
1002
1577
|
if (gen === sendGen.current) setPending(false);
|
|
1003
1578
|
});
|
|
1004
1579
|
};
|
|
1005
|
-
return /* @__PURE__ */
|
|
1006
|
-
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
1580
|
+
return /* @__PURE__ */ jsx4(WidgetFrame3, { title: optString3(widget.options, "title"), children: /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "column", height: "100%", gap: 10, overflowY: "auto" }, children: !commandName ? /* @__PURE__ */ jsx4("div", { style: { ...hint, margin: "auto" }, children: "No command configured." }) : /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1581
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: 8, flexShrink: 0 }, children: [
|
|
1007
1582
|
params.map(
|
|
1008
|
-
(param) => isScalar(param) ? /* @__PURE__ */
|
|
1583
|
+
(param) => isScalar(param) ? /* @__PURE__ */ jsx4(
|
|
1009
1584
|
ParamField,
|
|
1010
1585
|
{
|
|
1011
1586
|
widgetId: widget.id,
|
|
@@ -1019,7 +1594,7 @@ function CommandButton({ widget, data, actions }) {
|
|
|
1019
1594
|
) : (
|
|
1020
1595
|
// A structured (OBJECT) parameter isn't form-editable. A REQUIRED one
|
|
1021
1596
|
// blocks sending (validateParams flags it); flag that in the note.
|
|
1022
|
-
/* @__PURE__ */ jsxs3("div", { style: { ...hint, color: param.required ? commandStatusColor("FAILED") :
|
|
1597
|
+
/* @__PURE__ */ jsxs3("div", { style: { ...hint, color: param.required ? commandStatusColor("FAILED") : css4("muted-foreground") }, children: [
|
|
1023
1598
|
param.name,
|
|
1024
1599
|
": ",
|
|
1025
1600
|
param.required ? "required " : "",
|
|
@@ -1027,7 +1602,7 @@ function CommandButton({ widget, data, actions }) {
|
|
|
1027
1602
|
] }, param.name)
|
|
1028
1603
|
)
|
|
1029
1604
|
),
|
|
1030
|
-
/* @__PURE__ */
|
|
1605
|
+
/* @__PURE__ */ jsx4(
|
|
1031
1606
|
"button",
|
|
1032
1607
|
{
|
|
1033
1608
|
type: "button",
|
|
@@ -1038,10 +1613,10 @@ function CommandButton({ widget, data, actions }) {
|
|
|
1038
1613
|
children: pending ? "Sending\u2026" : `Send ${commandLabel}`
|
|
1039
1614
|
}
|
|
1040
1615
|
),
|
|
1041
|
-
!canWrite ? /* @__PURE__ */
|
|
1042
|
-
sendError ? /* @__PURE__ */
|
|
1616
|
+
!canWrite ? /* @__PURE__ */ jsx4("div", { style: hint, children: "You don\u2019t have permission to issue commands." }) : !hasSeam ? /* @__PURE__ */ jsx4("div", { style: hint, children: "Commands aren\u2019t available in this view." }) : !deviceToken ? /* @__PURE__ */ jsx4("div", { style: hint, children: "No device is selected for this command." }) : null,
|
|
1617
|
+
sendError ? /* @__PURE__ */ jsx4("div", { role: "alert", style: { ...hint, color: commandStatusColor("FAILED") }, children: sendError }) : null
|
|
1043
1618
|
] }),
|
|
1044
|
-
/* @__PURE__ */
|
|
1619
|
+
/* @__PURE__ */ jsx4(CommandHistory, { commands: data.commands, sentToken, loading: data.loading, error: data.error })
|
|
1045
1620
|
] }) }) });
|
|
1046
1621
|
}
|
|
1047
1622
|
function ParamField({
|
|
@@ -1055,13 +1630,13 @@ function ParamField({
|
|
|
1055
1630
|
const labelId = `cmd-${widgetId}-${param.name}`;
|
|
1056
1631
|
const unit = param.unit ? ` (${param.unit})` : "";
|
|
1057
1632
|
return /* @__PURE__ */ jsxs3("label", { htmlFor: labelId, style: { display: "flex", flexDirection: "column", gap: 3 }, children: [
|
|
1058
|
-
/* @__PURE__ */ jsxs3("span", { style: { fontSize:
|
|
1633
|
+
/* @__PURE__ */ jsxs3("span", { style: { fontSize: fontVar3("sm"), color: css4("muted-foreground") }, children: [
|
|
1059
1634
|
param.name,
|
|
1060
1635
|
unit,
|
|
1061
|
-
param.required ? /* @__PURE__ */
|
|
1636
|
+
param.required ? /* @__PURE__ */ jsx4("span", { style: { color: commandStatusColor("FAILED") }, children: " *" }) : null
|
|
1062
1637
|
] }),
|
|
1063
|
-
/* @__PURE__ */
|
|
1064
|
-
error ? /* @__PURE__ */
|
|
1638
|
+
/* @__PURE__ */ jsx4(ParamInput, { id: labelId, param, value, disabled, onChange }),
|
|
1639
|
+
error ? /* @__PURE__ */ jsx4("span", { style: { fontSize: fontVar3("xs"), color: commandStatusColor("FAILED") }, children: error }) : null
|
|
1065
1640
|
] });
|
|
1066
1641
|
}
|
|
1067
1642
|
function ParamInput({
|
|
@@ -1073,12 +1648,12 @@ function ParamInput({
|
|
|
1073
1648
|
}) {
|
|
1074
1649
|
if (param.enum && param.enum.length > 0) {
|
|
1075
1650
|
return /* @__PURE__ */ jsxs3("select", { id, style: inputStyle, value, disabled, onChange: (e) => onChange(e.target.value), children: [
|
|
1076
|
-
!param.required ? /* @__PURE__ */
|
|
1077
|
-
param.enum.map((opt) => /* @__PURE__ */
|
|
1651
|
+
!param.required ? /* @__PURE__ */ jsx4("option", { value: "", children: "\u2014" }) : null,
|
|
1652
|
+
param.enum.map((opt) => /* @__PURE__ */ jsx4("option", { value: opt, children: opt }, opt))
|
|
1078
1653
|
] });
|
|
1079
1654
|
}
|
|
1080
1655
|
if (param.dataType === "BOOLEAN") {
|
|
1081
|
-
return /* @__PURE__ */
|
|
1656
|
+
return /* @__PURE__ */ jsx4(
|
|
1082
1657
|
"input",
|
|
1083
1658
|
{
|
|
1084
1659
|
id,
|
|
@@ -1091,7 +1666,7 @@ function ParamInput({
|
|
|
1091
1666
|
);
|
|
1092
1667
|
}
|
|
1093
1668
|
const numeric = param.dataType === "INT" || param.dataType === "DOUBLE";
|
|
1094
|
-
return /* @__PURE__ */
|
|
1669
|
+
return /* @__PURE__ */ jsx4(
|
|
1095
1670
|
"input",
|
|
1096
1671
|
{
|
|
1097
1672
|
id,
|
|
@@ -1115,31 +1690,31 @@ function CommandHistory({
|
|
|
1115
1690
|
}) {
|
|
1116
1691
|
const empty = commands.length === 0;
|
|
1117
1692
|
const timeZone = useWidgetTimeZone2();
|
|
1118
|
-
return /* @__PURE__ */
|
|
1693
|
+
return /* @__PURE__ */ jsx4(
|
|
1119
1694
|
"div",
|
|
1120
1695
|
{
|
|
1121
1696
|
style: {
|
|
1122
1697
|
flex: 1,
|
|
1123
1698
|
minHeight: 0,
|
|
1124
1699
|
overflow: "auto",
|
|
1125
|
-
borderTop: `1px solid ${
|
|
1700
|
+
borderTop: `1px solid ${css4("border")}`,
|
|
1126
1701
|
paddingTop: 6
|
|
1127
1702
|
},
|
|
1128
|
-
children: empty ? /* @__PURE__ */
|
|
1703
|
+
children: empty ? /* @__PURE__ */ jsx4("div", { style: { ...hint, textAlign: "center", color: error ? commandStatusColor("FAILED") : css4("muted-foreground") }, children: error ? "Couldn\u2019t load command history." : loading ? "Loading\u2026" : "No commands issued yet." }) : /* @__PURE__ */ jsx4("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 4 }, children: commands.map((command) => /* @__PURE__ */ jsxs3(
|
|
1129
1704
|
"li",
|
|
1130
1705
|
{
|
|
1131
1706
|
style: {
|
|
1132
1707
|
display: "flex",
|
|
1133
1708
|
alignItems: "center",
|
|
1134
1709
|
gap: 8,
|
|
1135
|
-
fontSize:
|
|
1710
|
+
fontSize: fontVar3("sm"),
|
|
1136
1711
|
// Highlight the command just issued from this widget.
|
|
1137
1712
|
fontWeight: command.token === sentToken ? 600 : 400
|
|
1138
1713
|
},
|
|
1139
1714
|
children: [
|
|
1140
|
-
/* @__PURE__ */
|
|
1141
|
-
/* @__PURE__ */
|
|
1142
|
-
/* @__PURE__ */
|
|
1715
|
+
/* @__PURE__ */ jsx4(StatusBadge, { status: command.status }),
|
|
1716
|
+
/* @__PURE__ */ jsx4("span", { style: { color: css4("foreground"), whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: command.name }),
|
|
1717
|
+
/* @__PURE__ */ jsx4("span", { style: { marginLeft: "auto", color: css4("muted-foreground"), whiteSpace: "nowrap" }, children: formatDateTime2(command.queuedTime, timeZone) })
|
|
1143
1718
|
]
|
|
1144
1719
|
},
|
|
1145
1720
|
command.token
|
|
@@ -1157,80 +1732,94 @@ function StatusBadge({ status }) {
|
|
|
1157
1732
|
alignItems: "center",
|
|
1158
1733
|
gap: 4,
|
|
1159
1734
|
flexShrink: 0,
|
|
1160
|
-
fontSize:
|
|
1735
|
+
fontSize: fontVar3("xs"),
|
|
1161
1736
|
fontWeight: 600,
|
|
1162
1737
|
color
|
|
1163
1738
|
},
|
|
1164
1739
|
children: [
|
|
1165
|
-
/* @__PURE__ */
|
|
1740
|
+
/* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { width: 7, height: 7, borderRadius: "50%", background: color } }),
|
|
1166
1741
|
commandStatusLabel(status)
|
|
1167
1742
|
]
|
|
1168
1743
|
}
|
|
1169
1744
|
);
|
|
1170
1745
|
}
|
|
1171
|
-
var hint = { fontSize:
|
|
1746
|
+
var hint = { fontSize: fontVar3("xs"), color: css4("muted-foreground") };
|
|
1172
1747
|
var SEND_FAILED = "Couldn\u2019t issue the command. Try again.";
|
|
1173
1748
|
var inputStyle = {
|
|
1174
1749
|
width: "100%",
|
|
1175
1750
|
boxSizing: "border-box",
|
|
1176
1751
|
padding: "4px 8px",
|
|
1177
|
-
fontSize:
|
|
1752
|
+
fontSize: fontVar3("md"),
|
|
1178
1753
|
borderRadius: 4,
|
|
1179
|
-
border: `1px solid ${
|
|
1180
|
-
background:
|
|
1181
|
-
color:
|
|
1754
|
+
border: `1px solid ${css4("border")}`,
|
|
1755
|
+
background: css4("background"),
|
|
1756
|
+
color: css4("foreground")
|
|
1182
1757
|
};
|
|
1183
1758
|
var sendButton = {
|
|
1184
1759
|
padding: "6px 12px",
|
|
1185
|
-
fontSize:
|
|
1760
|
+
fontSize: fontVar3("md"),
|
|
1186
1761
|
fontWeight: 600,
|
|
1187
1762
|
borderRadius: 4,
|
|
1188
1763
|
border: "none",
|
|
1189
|
-
background:
|
|
1190
|
-
color:
|
|
1764
|
+
background: css4("primary"),
|
|
1765
|
+
color: css4("background"),
|
|
1191
1766
|
marginTop: 2
|
|
1192
1767
|
};
|
|
1193
|
-
var commandButtonWidget =
|
|
1768
|
+
var commandButtonWidget = defineWidget4({
|
|
1194
1769
|
descriptor: builtinWidgetDescriptors["command-button"],
|
|
1195
1770
|
implementation: { kind: "trusted-react", component: CommandButton }
|
|
1196
1771
|
});
|
|
1197
1772
|
|
|
1773
|
+
// src/widgets/control-lazy.tsx
|
|
1774
|
+
import { defineWidget as defineWidget5 } from "@devicechain/widget-sdk";
|
|
1775
|
+
var chunk2 = lazyChunk(() => import("./chunks/control-widgets-UDS4ET3T.js"));
|
|
1776
|
+
var loadControlWidgets = chunk2.load;
|
|
1777
|
+
var commandSliderWidget = defineWidget5({
|
|
1778
|
+
descriptor: builtinWidgetDescriptors["command-slider"],
|
|
1779
|
+
implementation: { kind: "trusted-react", component: chunk2.component("CommandSlider") }
|
|
1780
|
+
});
|
|
1781
|
+
var commandToggleWidget = defineWidget5({
|
|
1782
|
+
descriptor: builtinWidgetDescriptors["command-toggle"],
|
|
1783
|
+
implementation: { kind: "trusted-react", component: chunk2.component("CommandToggle") }
|
|
1784
|
+
});
|
|
1785
|
+
|
|
1198
1786
|
// src/widgets/entity-selector.tsx
|
|
1199
|
-
import { useEffect as
|
|
1787
|
+
import { useEffect as useEffect3, useState as useState5 } from "react";
|
|
1200
1788
|
import {
|
|
1201
|
-
css as
|
|
1202
|
-
|
|
1203
|
-
|
|
1789
|
+
css as css5,
|
|
1790
|
+
fontVar as fontVar4,
|
|
1791
|
+
defineWidget as defineWidget6,
|
|
1792
|
+
optString as optString4,
|
|
1204
1793
|
useCandidates,
|
|
1205
1794
|
useWidgetSelect as useWidgetSelect2,
|
|
1206
1795
|
WidgetFrame as WidgetFrame4
|
|
1207
1796
|
} from "@devicechain/widget-sdk";
|
|
1208
|
-
import { Fragment as
|
|
1797
|
+
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1209
1798
|
function tokenOf(b) {
|
|
1210
1799
|
return b.kind === "device" ? b.deviceToken : b.anchor.targetToken;
|
|
1211
1800
|
}
|
|
1212
1801
|
var control = {
|
|
1213
1802
|
width: "100%",
|
|
1214
|
-
fontSize:
|
|
1803
|
+
fontSize: fontVar4("md"),
|
|
1215
1804
|
padding: "8px 10px",
|
|
1216
1805
|
borderRadius: 6,
|
|
1217
|
-
border: `1px solid ${
|
|
1218
|
-
background:
|
|
1219
|
-
color:
|
|
1806
|
+
border: `1px solid ${css5("border")}`,
|
|
1807
|
+
background: css5("background"),
|
|
1808
|
+
color: css5("foreground")
|
|
1220
1809
|
};
|
|
1221
1810
|
var hint2 = {
|
|
1222
|
-
fontSize:
|
|
1223
|
-
color:
|
|
1811
|
+
fontSize: fontVar4("sm"),
|
|
1812
|
+
color: css5("muted-foreground")
|
|
1224
1813
|
};
|
|
1225
1814
|
function EntitySelector({ widget }) {
|
|
1226
1815
|
const select = useWidgetSelect2();
|
|
1227
|
-
const target =
|
|
1816
|
+
const target = optString4(widget.options, "selectionTarget");
|
|
1228
1817
|
const { candidates, loading, error, wired } = useCandidates(target);
|
|
1229
|
-
const title =
|
|
1230
|
-
const [pending, setPending] =
|
|
1818
|
+
const title = optString4(widget.options, "title");
|
|
1819
|
+
const [pending, setPending] = useState5(null);
|
|
1231
1820
|
const selected = candidates.find((c) => c.selected);
|
|
1232
1821
|
const selectedToken = selected ? tokenOf(selected.binding) : "";
|
|
1233
|
-
|
|
1822
|
+
useEffect3(() => {
|
|
1234
1823
|
if (pending !== null && (selectedToken === pending || !candidates.some((c) => tokenOf(c.binding) === pending))) {
|
|
1235
1824
|
setPending(null);
|
|
1236
1825
|
}
|
|
@@ -1246,25 +1835,38 @@ function EntitySelector({ widget }) {
|
|
|
1246
1835
|
select({ slot: target, binding: candidate.binding });
|
|
1247
1836
|
}
|
|
1248
1837
|
};
|
|
1249
|
-
return /* @__PURE__ */
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
{
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
"
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1838
|
+
return /* @__PURE__ */ jsx5(WidgetFrame4, { title, children: /* @__PURE__ */ jsx5(
|
|
1839
|
+
"div",
|
|
1840
|
+
{
|
|
1841
|
+
style: {
|
|
1842
|
+
display: "flex",
|
|
1843
|
+
flexDirection: "column",
|
|
1844
|
+
gap: 8,
|
|
1845
|
+
height: "100%",
|
|
1846
|
+
boxSizing: "border-box",
|
|
1847
|
+
overflow: "auto"
|
|
1848
|
+
},
|
|
1849
|
+
children: inert ? /* @__PURE__ */ jsx5("span", { style: hint2, children: select && target ? "Selection isn\u2019t available here." : select ? "No target slot configured." : "Selection is available on the live dashboard." }) : /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1850
|
+
/* @__PURE__ */ jsxs4(
|
|
1851
|
+
"select",
|
|
1852
|
+
{
|
|
1853
|
+
style: { ...control, opacity: loading ? 0.6 : 1 },
|
|
1854
|
+
value,
|
|
1855
|
+
disabled: candidates.length === 0,
|
|
1856
|
+
onChange,
|
|
1857
|
+
"aria-label": title || `Select ${target}`,
|
|
1858
|
+
children: [
|
|
1859
|
+
/* @__PURE__ */ jsx5("option", { value: "", disabled: true, children: loading ? "Loading\u2026" : candidates.length === 0 ? "No options" : "Select\u2026" }),
|
|
1860
|
+
candidates.map((c) => /* @__PURE__ */ jsx5("option", { value: tokenOf(c.binding), children: c.label }, tokenOf(c.binding)))
|
|
1861
|
+
]
|
|
1862
|
+
}
|
|
1863
|
+
),
|
|
1864
|
+
error ? /* @__PURE__ */ jsx5("span", { style: hint2, children: "Couldn\u2019t load options." }) : null
|
|
1865
|
+
] })
|
|
1866
|
+
}
|
|
1867
|
+
) });
|
|
1266
1868
|
}
|
|
1267
|
-
var entitySelectorWidget =
|
|
1869
|
+
var entitySelectorWidget = defineWidget6({
|
|
1268
1870
|
descriptor: builtinWidgetDescriptors["entity-selector"],
|
|
1269
1871
|
implementation: { kind: "trusted-react", component: EntitySelector }
|
|
1270
1872
|
});
|
|
@@ -1272,58 +1874,105 @@ var entitySelectorWidget = defineWidget4({
|
|
|
1272
1874
|
// src/widgets/gauge.tsx
|
|
1273
1875
|
import { useMemo as useMemo2 } from "react";
|
|
1274
1876
|
import {
|
|
1275
|
-
defineWidget as
|
|
1877
|
+
defineWidget as defineWidget7,
|
|
1276
1878
|
flashBackgroundStyle,
|
|
1879
|
+
formatValue,
|
|
1277
1880
|
optBoolean as optBoolean2,
|
|
1278
|
-
optNumber as
|
|
1279
|
-
optString as
|
|
1881
|
+
optNumber as optNumber2,
|
|
1882
|
+
optString as optString5,
|
|
1883
|
+
optThresholds,
|
|
1280
1884
|
pickSample,
|
|
1281
1885
|
primaryMeasurementName,
|
|
1282
|
-
useElementSize as useElementSize2,
|
|
1283
1886
|
useFlashOnChange,
|
|
1887
|
+
useWidgetScale as useWidgetScale2,
|
|
1284
1888
|
WidgetFrame as WidgetFrame5
|
|
1285
1889
|
} from "@devicechain/widget-sdk";
|
|
1286
|
-
import { buildGaugeOption, EChart, useChartTheme } from "@devicechain/widget-sdk/echarts";
|
|
1287
|
-
import { jsx as
|
|
1890
|
+
import { buildGaugeOption, EChart, gaugeReadoutBox, useChartTheme } from "@devicechain/widget-sdk/echarts";
|
|
1891
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1288
1892
|
function Gauge({ widget, data }) {
|
|
1893
|
+
const name = primaryMeasurementName(widget);
|
|
1894
|
+
const sample = pickSample(data.latest, name);
|
|
1895
|
+
return /* @__PURE__ */ jsx6(WidgetFrame5, { title: optString5(widget.options, "title") ?? name, sampleTime: sample?.occurredTime ?? null, flush: true, children: /* @__PURE__ */ jsx6(GaugeBody, { widget, data }) });
|
|
1896
|
+
}
|
|
1897
|
+
function GaugeBody({ widget, data }) {
|
|
1289
1898
|
const theme = useChartTheme();
|
|
1290
|
-
const
|
|
1899
|
+
const { size, scale } = useWidgetScale2();
|
|
1291
1900
|
const name = primaryMeasurementName(widget);
|
|
1292
1901
|
const sample = pickSample(data.latest, name);
|
|
1293
|
-
const min =
|
|
1294
|
-
const max =
|
|
1295
|
-
const unit =
|
|
1902
|
+
const min = optNumber2(widget.options, "min");
|
|
1903
|
+
const max = optNumber2(widget.options, "max");
|
|
1904
|
+
const unit = optString5(widget.options, "unit");
|
|
1905
|
+
const precision = optNumber2(widget.options, "precision");
|
|
1906
|
+
const thresholds = useMemo2(() => optThresholds(widget.options, "thresholds"), [widget.options]);
|
|
1296
1907
|
const option = useMemo2(
|
|
1297
|
-
() => buildGaugeOption(sample?.value ?? null, theme, { min, max,
|
|
1298
|
-
[sample?.value, theme, min, max,
|
|
1908
|
+
() => buildGaugeOption(sample?.value ?? null, theme, { min, max, thresholds, scale, readout: "none", emptyNeedle: "none" }, size),
|
|
1909
|
+
[sample?.value, theme, min, max, thresholds, scale, size]
|
|
1299
1910
|
);
|
|
1300
1911
|
const flashDirection = useFlashOnChange(
|
|
1301
1912
|
sample?.value,
|
|
1302
1913
|
optBoolean2(widget.options, "flashOnChange"),
|
|
1303
1914
|
sample && `${sample.deviceToken}:${sample.name}`
|
|
1304
1915
|
);
|
|
1305
|
-
|
|
1916
|
+
const box = gaugeReadoutBox(size);
|
|
1917
|
+
const range = readRange(min, max, { min: 0, max: 100 });
|
|
1918
|
+
const value = sample?.value ?? void 0;
|
|
1919
|
+
const meter = range.ok && value !== void 0 && Number.isFinite(value) ? {
|
|
1920
|
+
role: "meter",
|
|
1921
|
+
"aria-valuemin": range.range.min,
|
|
1922
|
+
"aria-valuemax": range.range.max,
|
|
1923
|
+
"aria-valuenow": Math.min(range.range.max, Math.max(range.range.min, value)),
|
|
1924
|
+
"aria-valuetext": `${formatValue(value, precision)}${unit ? ` ${unit}` : ""}`
|
|
1925
|
+
} : {};
|
|
1926
|
+
return /* @__PURE__ */ jsxs5("div", { style: { position: "relative", width: "100%", height: "100%", ...flashBackgroundStyle(flashDirection) }, ...meter, children: [
|
|
1927
|
+
/* @__PURE__ */ jsx6(EChart, { option }),
|
|
1928
|
+
/* @__PURE__ */ jsx6(
|
|
1929
|
+
"div",
|
|
1930
|
+
{
|
|
1931
|
+
style: {
|
|
1932
|
+
position: "absolute",
|
|
1933
|
+
left: box.centerX - box.width / 2,
|
|
1934
|
+
top: box.centerY - box.height / 2,
|
|
1935
|
+
width: box.width,
|
|
1936
|
+
height: box.height,
|
|
1937
|
+
display: "flex",
|
|
1938
|
+
alignItems: "center",
|
|
1939
|
+
justifyContent: "center",
|
|
1940
|
+
pointerEvents: "none"
|
|
1941
|
+
},
|
|
1942
|
+
children: /* @__PURE__ */ jsx6(
|
|
1943
|
+
LevelReadout,
|
|
1944
|
+
{
|
|
1945
|
+
text: formatValue(value, precision),
|
|
1946
|
+
unit: value === void 0 ? void 0 : unit,
|
|
1947
|
+
size: { width: box.width, height: box.height },
|
|
1948
|
+
scale
|
|
1949
|
+
}
|
|
1950
|
+
)
|
|
1951
|
+
}
|
|
1952
|
+
)
|
|
1953
|
+
] });
|
|
1306
1954
|
}
|
|
1307
|
-
var gaugeWidget =
|
|
1955
|
+
var gaugeWidget = defineWidget7({
|
|
1308
1956
|
descriptor: builtinWidgetDescriptors["gauge"],
|
|
1309
1957
|
implementation: { kind: "trusted-react", component: Gauge }
|
|
1310
1958
|
});
|
|
1311
1959
|
|
|
1312
1960
|
// src/widgets/image.tsx
|
|
1313
1961
|
import {
|
|
1314
|
-
css as
|
|
1315
|
-
defineWidget as
|
|
1316
|
-
|
|
1962
|
+
css as css6,
|
|
1963
|
+
defineWidget as defineWidget8,
|
|
1964
|
+
fontVar as fontVar5,
|
|
1965
|
+
optString as optString6,
|
|
1317
1966
|
WidgetFrame as WidgetFrame6
|
|
1318
1967
|
} from "@devicechain/widget-sdk";
|
|
1319
|
-
import { jsx as
|
|
1968
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1320
1969
|
var FITS = /* @__PURE__ */ new Set(["contain", "cover", "fill"]);
|
|
1321
1970
|
function Image({ widget }) {
|
|
1322
|
-
const url =
|
|
1323
|
-
const alt =
|
|
1324
|
-
const fitOption =
|
|
1971
|
+
const url = optString6(widget.options, "url");
|
|
1972
|
+
const alt = optString6(widget.options, "alt") ?? "";
|
|
1973
|
+
const fitOption = optString6(widget.options, "fit");
|
|
1325
1974
|
const objectFit = fitOption && FITS.has(fitOption) ? fitOption : "contain";
|
|
1326
|
-
return /* @__PURE__ */
|
|
1975
|
+
return /* @__PURE__ */ jsx7(WidgetFrame6, { title: optString6(widget.options, "title"), flush: true, children: url ? /* @__PURE__ */ jsx7("img", { src: url, alt, style: { width: "100%", height: "100%", objectFit, display: "block" } }) : /* @__PURE__ */ jsx7(
|
|
1327
1976
|
"div",
|
|
1328
1977
|
{
|
|
1329
1978
|
style: {
|
|
@@ -1331,52 +1980,59 @@ function Image({ widget }) {
|
|
|
1331
1980
|
alignItems: "center",
|
|
1332
1981
|
justifyContent: "center",
|
|
1333
1982
|
height: "100%",
|
|
1334
|
-
color:
|
|
1335
|
-
fontSize:
|
|
1983
|
+
color: css6("muted-foreground"),
|
|
1984
|
+
fontSize: fontVar5("md")
|
|
1336
1985
|
},
|
|
1337
1986
|
children: "No image URL"
|
|
1338
1987
|
}
|
|
1339
1988
|
) });
|
|
1340
1989
|
}
|
|
1341
|
-
var imageWidget =
|
|
1990
|
+
var imageWidget = defineWidget8({
|
|
1342
1991
|
descriptor: builtinWidgetDescriptors["image"],
|
|
1343
1992
|
implementation: { kind: "trusted-react", component: Image }
|
|
1344
1993
|
});
|
|
1345
1994
|
|
|
1346
1995
|
// src/widgets/label.tsx
|
|
1347
1996
|
import {
|
|
1348
|
-
css as
|
|
1349
|
-
defineWidget as
|
|
1350
|
-
|
|
1351
|
-
|
|
1997
|
+
css as css7,
|
|
1998
|
+
defineWidget as defineWidget9,
|
|
1999
|
+
fontVar as fontVar6,
|
|
2000
|
+
spaceVar as spaceVar2,
|
|
2001
|
+
optNumber as optNumber3,
|
|
2002
|
+
optString as optString7,
|
|
1352
2003
|
WidgetFrame as WidgetFrame7
|
|
1353
2004
|
} from "@devicechain/widget-sdk";
|
|
1354
|
-
import { jsx as
|
|
2005
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1355
2006
|
var justify = {
|
|
1356
2007
|
left: "flex-start",
|
|
1357
2008
|
center: "center",
|
|
1358
2009
|
right: "flex-end"
|
|
1359
2010
|
};
|
|
1360
2011
|
function Label({ widget }) {
|
|
1361
|
-
const text =
|
|
1362
|
-
const align =
|
|
1363
|
-
const
|
|
1364
|
-
|
|
2012
|
+
const text = optString7(widget.options, "text") ?? "";
|
|
2013
|
+
const align = optString7(widget.options, "align") ?? "center";
|
|
2014
|
+
const authored = optNumber3(widget.options, "fontSize");
|
|
2015
|
+
const fontSize = authored !== void 0 ? `calc(${authored}px * var(--dc-scale, 1))` : fontVar6("lg");
|
|
2016
|
+
return /* @__PURE__ */ jsx8(WidgetFrame7, { title: optString7(widget.options, "title"), flush: true, children: /* @__PURE__ */ jsx8(
|
|
1365
2017
|
"div",
|
|
1366
2018
|
{
|
|
1367
2019
|
style: {
|
|
1368
2020
|
display: "flex",
|
|
1369
|
-
alignItems: "center",
|
|
2021
|
+
alignItems: "safe center",
|
|
1370
2022
|
justifyContent: justify[align] ?? "center",
|
|
1371
2023
|
height: "100%",
|
|
1372
|
-
|
|
2024
|
+
boxSizing: "border-box",
|
|
2025
|
+
overflow: "hidden",
|
|
2026
|
+
// A label is often one row tall: the frame's uniform inset would leave it a line's
|
|
2027
|
+
// height or less, so it keeps a slim vertical inset and the usual side inset.
|
|
2028
|
+
padding: `${spaceVar2(1)} ${spaceVar2(3)}`
|
|
1373
2029
|
},
|
|
1374
|
-
children: /* @__PURE__ */
|
|
2030
|
+
children: /* @__PURE__ */ jsx8(
|
|
1375
2031
|
"span",
|
|
1376
2032
|
{
|
|
1377
2033
|
style: {
|
|
1378
2034
|
fontSize,
|
|
1379
|
-
color:
|
|
2035
|
+
color: css7("foreground"),
|
|
1380
2036
|
textAlign: align,
|
|
1381
2037
|
whiteSpace: "pre-wrap"
|
|
1382
2038
|
},
|
|
@@ -1386,96 +2042,166 @@ function Label({ widget }) {
|
|
|
1386
2042
|
}
|
|
1387
2043
|
) });
|
|
1388
2044
|
}
|
|
1389
|
-
var labelWidget =
|
|
2045
|
+
var labelWidget = defineWidget9({
|
|
1390
2046
|
descriptor: builtinWidgetDescriptors["label"],
|
|
1391
2047
|
implementation: { kind: "trusted-react", component: Label }
|
|
1392
2048
|
});
|
|
1393
2049
|
|
|
1394
2050
|
// src/widgets/latest-card.tsx
|
|
2051
|
+
import { useMemo as useMemo3 } from "react";
|
|
1395
2052
|
import {
|
|
1396
|
-
|
|
1397
|
-
|
|
2053
|
+
bandAt,
|
|
2054
|
+
css as css8,
|
|
2055
|
+
defineWidget as defineWidget10,
|
|
2056
|
+
fitValueFont as fitValueFont2,
|
|
2057
|
+
fitValueFontCss as fitValueFontCss2,
|
|
2058
|
+
FIT_MIN_FONT_PX as FIT_MIN_FONT_PX2,
|
|
1398
2059
|
flashTextStyle,
|
|
2060
|
+
fontVar as fontVar7,
|
|
1399
2061
|
formatTimestamp,
|
|
1400
|
-
formatValue,
|
|
2062
|
+
formatValue as formatValue2,
|
|
2063
|
+
levelColor,
|
|
1401
2064
|
optBoolean as optBoolean3,
|
|
1402
|
-
optNumber as
|
|
1403
|
-
optString as
|
|
2065
|
+
optNumber as optNumber4,
|
|
2066
|
+
optString as optString8,
|
|
2067
|
+
optThresholds as optThresholds2,
|
|
1404
2068
|
pickSample as pickSample2,
|
|
1405
2069
|
primaryMeasurementName as primaryMeasurementName2,
|
|
1406
|
-
|
|
2070
|
+
spaceVar as spaceVar3,
|
|
2071
|
+
useSeverityColors as useSeverityColors3,
|
|
2072
|
+
useStickyChars as useStickyChars2,
|
|
1407
2073
|
useFlashOnChange as useFlashOnChange2,
|
|
1408
2074
|
useWidgetTimeZone as useWidgetTimeZone3,
|
|
1409
2075
|
WidgetFrame as WidgetFrame8
|
|
1410
2076
|
} from "@devicechain/widget-sdk";
|
|
1411
|
-
import { jsx as
|
|
1412
|
-
var
|
|
2077
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2078
|
+
var TIMESTAMP_ROOM_PX = 18;
|
|
1413
2079
|
function LatestCard({ widget, data }) {
|
|
1414
|
-
const
|
|
2080
|
+
const severity = useSeverityColors3();
|
|
1415
2081
|
const timeZone = useWidgetTimeZone3();
|
|
1416
2082
|
const name = primaryMeasurementName2(widget);
|
|
1417
2083
|
const sample = pickSample2(data.latest, name);
|
|
1418
|
-
const title =
|
|
1419
|
-
const unit =
|
|
1420
|
-
const precision =
|
|
1421
|
-
const display =
|
|
2084
|
+
const title = optString8(widget.options, "title") ?? name;
|
|
2085
|
+
const unit = optString8(widget.options, "unit");
|
|
2086
|
+
const precision = optNumber4(widget.options, "precision");
|
|
2087
|
+
const display = formatValue2(sample?.value, precision);
|
|
2088
|
+
const thresholds = useMemo3(() => optThresholds2(widget.options, "thresholds"), [widget.options]);
|
|
2089
|
+
const band = bandAt(thresholds, sample?.value);
|
|
2090
|
+
const valueColor = levelColor(severity, band?.level) ?? css8("foreground");
|
|
1422
2091
|
const flashDirection = useFlashOnChange2(
|
|
1423
2092
|
sample?.value,
|
|
1424
2093
|
optBoolean3(widget.options, "flashOnChange"),
|
|
1425
2094
|
sample && `${sample.deviceToken}:${sample.name}`
|
|
1426
2095
|
);
|
|
1427
|
-
const
|
|
1428
|
-
return /* @__PURE__ */
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
"
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
2096
|
+
const chars = useStickyChars2(display.length + (sample?.value != null && unit ? Math.ceil(unit.length / 2) + 1 : 0));
|
|
2097
|
+
return /* @__PURE__ */ jsx9(WidgetFrame8, { title, sampleTime: sample?.occurredTime ?? null, children: ({ size, scale }) => {
|
|
2098
|
+
const floor = Math.round(FIT_MIN_FONT_PX2 * scale);
|
|
2099
|
+
const measured = size.width > 0 && size.height > 0;
|
|
2100
|
+
const valuePx = measured ? fitValueFont2(size, chars, { floor }) : void 0;
|
|
2101
|
+
const valueFont = valuePx ?? fitValueFontCss2(chars, { floor });
|
|
2102
|
+
const showTimestamp = valuePx === void 0 || size.height - Math.ceil(valuePx * 1.1) >= TIMESTAMP_ROOM_PX * scale;
|
|
2103
|
+
return /* @__PURE__ */ jsxs6(
|
|
2104
|
+
"div",
|
|
2105
|
+
{
|
|
2106
|
+
style: {
|
|
2107
|
+
display: "flex",
|
|
2108
|
+
flexDirection: "column",
|
|
2109
|
+
justifyContent: "safe center",
|
|
2110
|
+
height: "100%",
|
|
2111
|
+
boxSizing: "border-box",
|
|
2112
|
+
overflow: "hidden"
|
|
2113
|
+
},
|
|
2114
|
+
children: [
|
|
2115
|
+
/* @__PURE__ */ jsxs6(
|
|
2116
|
+
"div",
|
|
2117
|
+
{
|
|
2118
|
+
"data-threshold-level": band?.level,
|
|
2119
|
+
title: band?.label,
|
|
2120
|
+
style: {
|
|
2121
|
+
fontSize: valueFont,
|
|
2122
|
+
fontWeight: 600,
|
|
2123
|
+
lineHeight: 1.1,
|
|
2124
|
+
// One line, and digits of one width: the fit counts characters, so a live value
|
|
2125
|
+
// must not wrap or shift sideways as its digits change.
|
|
2126
|
+
whiteSpace: "nowrap",
|
|
2127
|
+
fontVariantNumeric: "tabular-nums",
|
|
2128
|
+
color: valueColor,
|
|
2129
|
+
...flashTextStyle(flashDirection)
|
|
2130
|
+
},
|
|
2131
|
+
children: [
|
|
2132
|
+
display,
|
|
2133
|
+
sample?.value != null && unit ? /* @__PURE__ */ jsx9(
|
|
2134
|
+
"span",
|
|
2135
|
+
{
|
|
2136
|
+
style: {
|
|
2137
|
+
fontSize: valuePx !== void 0 ? Math.round(valuePx / 2) : `calc(${valueFont} / 2)`,
|
|
2138
|
+
color: css8("muted-foreground"),
|
|
2139
|
+
marginLeft: spaceVar3(1)
|
|
2140
|
+
},
|
|
2141
|
+
children: unit
|
|
2142
|
+
}
|
|
2143
|
+
) : null
|
|
2144
|
+
]
|
|
2145
|
+
}
|
|
2146
|
+
),
|
|
2147
|
+
sample?.occurredTime && showTimestamp ? /* @__PURE__ */ jsx9("div", { style: { fontSize: fontVar7("xs"), color: css8("muted-foreground"), marginTop: spaceVar3(1) }, children: formatTimestamp(sample.occurredTime, timeZone) }) : null
|
|
2148
|
+
]
|
|
2149
|
+
}
|
|
2150
|
+
);
|
|
2151
|
+
} });
|
|
1460
2152
|
}
|
|
1461
|
-
var latestCardWidget =
|
|
2153
|
+
var latestCardWidget = defineWidget10({
|
|
1462
2154
|
descriptor: builtinWidgetDescriptors["latest-card"],
|
|
1463
2155
|
implementation: { kind: "trusted-react", component: LatestCard }
|
|
1464
2156
|
});
|
|
1465
2157
|
|
|
2158
|
+
// src/widgets/level-lazy.tsx
|
|
2159
|
+
import { defineWidget as defineWidget11 } from "@devicechain/widget-sdk";
|
|
2160
|
+
var chunk3 = lazyChunk(() => import("./chunks/level-widgets-MJB7TOLU.js"));
|
|
2161
|
+
var loadLevelWidgets = chunk3.load;
|
|
2162
|
+
var lazyComponent = chunk3.component;
|
|
2163
|
+
var linearGaugeWidget = defineWidget11({
|
|
2164
|
+
descriptor: builtinWidgetDescriptors["linear-gauge"],
|
|
2165
|
+
implementation: { kind: "trusted-react", component: lazyComponent("LinearGauge") }
|
|
2166
|
+
});
|
|
2167
|
+
var levelGaugeWidget = defineWidget11({
|
|
2168
|
+
descriptor: builtinWidgetDescriptors["level-gauge"],
|
|
2169
|
+
implementation: { kind: "trusted-react", component: lazyComponent("LevelGauge") }
|
|
2170
|
+
});
|
|
2171
|
+
var batteryWidget = defineWidget11({
|
|
2172
|
+
descriptor: builtinWidgetDescriptors["battery"],
|
|
2173
|
+
implementation: { kind: "trusted-react", component: lazyComponent("Battery") }
|
|
2174
|
+
});
|
|
2175
|
+
var statusLightWidget = defineWidget11({
|
|
2176
|
+
descriptor: builtinWidgetDescriptors["status-light"],
|
|
2177
|
+
implementation: { kind: "trusted-react", component: lazyComponent("StatusLight") }
|
|
2178
|
+
});
|
|
2179
|
+
var signalBarsWidget = defineWidget11({
|
|
2180
|
+
descriptor: builtinWidgetDescriptors["signal-bars"],
|
|
2181
|
+
implementation: { kind: "trusted-react", component: lazyComponent("SignalBars") }
|
|
2182
|
+
});
|
|
2183
|
+
var compassWidget = defineWidget11({
|
|
2184
|
+
descriptor: builtinWidgetDescriptors["compass"],
|
|
2185
|
+
implementation: { kind: "trusted-react", component: lazyComponent("Compass") }
|
|
2186
|
+
});
|
|
2187
|
+
var progressRingWidget = defineWidget11({
|
|
2188
|
+
descriptor: builtinWidgetDescriptors["progress-ring"],
|
|
2189
|
+
implementation: { kind: "trusted-react", component: lazyComponent("ProgressRing") }
|
|
2190
|
+
});
|
|
2191
|
+
|
|
1466
2192
|
// src/widgets/map.tsx
|
|
1467
|
-
import { useEffect as
|
|
2193
|
+
import { useEffect as useEffect4, useRef as useRef2, useState as useState6 } from "react";
|
|
1468
2194
|
import { renderableBasemap, resolveBasemap } from "@devicechain/client";
|
|
1469
2195
|
|
|
1470
2196
|
// src/basemap-context.tsx
|
|
1471
2197
|
import { createContext, useContext } from "react";
|
|
1472
|
-
import { jsx as
|
|
2198
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1473
2199
|
var TenantBasemapContext = createContext(null);
|
|
1474
2200
|
function TenantBasemapProvider({
|
|
1475
2201
|
basemap,
|
|
1476
2202
|
children
|
|
1477
2203
|
}) {
|
|
1478
|
-
return /* @__PURE__ */
|
|
2204
|
+
return /* @__PURE__ */ jsx10(TenantBasemapContext.Provider, { value: basemap, children });
|
|
1479
2205
|
}
|
|
1480
2206
|
function useTenantBasemap() {
|
|
1481
2207
|
return useContext(TenantBasemapContext);
|
|
@@ -1483,13 +2209,13 @@ function useTenantBasemap() {
|
|
|
1483
2209
|
|
|
1484
2210
|
// src/map-runtime-context.tsx
|
|
1485
2211
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
1486
|
-
import { jsx as
|
|
2212
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
1487
2213
|
var MapRuntimeContext = createContext2(null);
|
|
1488
2214
|
function MapRuntimeProvider({
|
|
1489
2215
|
runtime,
|
|
1490
2216
|
children
|
|
1491
2217
|
}) {
|
|
1492
|
-
return /* @__PURE__ */
|
|
2218
|
+
return /* @__PURE__ */ jsx11(MapRuntimeContext.Provider, { value: runtime, children });
|
|
1493
2219
|
}
|
|
1494
2220
|
function useMapRuntime() {
|
|
1495
2221
|
return useContext2(MapRuntimeContext);
|
|
@@ -1497,9 +2223,10 @@ function useMapRuntime() {
|
|
|
1497
2223
|
|
|
1498
2224
|
// src/widgets/map.tsx
|
|
1499
2225
|
import {
|
|
1500
|
-
css as
|
|
1501
|
-
|
|
1502
|
-
|
|
2226
|
+
css as css9,
|
|
2227
|
+
fontVar as fontVar8,
|
|
2228
|
+
defineWidget as defineWidget12,
|
|
2229
|
+
optString as optString9,
|
|
1503
2230
|
WidgetFrame as WidgetFrame9
|
|
1504
2231
|
} from "@devicechain/widget-sdk";
|
|
1505
2232
|
|
|
@@ -1596,7 +2323,7 @@ function loadLandStyle() {
|
|
|
1596
2323
|
}
|
|
1597
2324
|
|
|
1598
2325
|
// src/widgets/map.tsx
|
|
1599
|
-
import { jsx as
|
|
2326
|
+
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1600
2327
|
var mapLibrePromise;
|
|
1601
2328
|
function loadMapLibre(runtime) {
|
|
1602
2329
|
if (!mapLibrePromise) {
|
|
@@ -1612,20 +2339,20 @@ function loadMapLibre(runtime) {
|
|
|
1612
2339
|
}
|
|
1613
2340
|
var SINGLE_DEVICE_ZOOM = 14;
|
|
1614
2341
|
function MapWidget({ widget, data }) {
|
|
1615
|
-
const title =
|
|
2342
|
+
const title = optString9(widget.options, "title");
|
|
1616
2343
|
const positions = placeable(data.locations);
|
|
1617
2344
|
const runtime = useMapRuntime();
|
|
1618
2345
|
const basemap = renderableBasemap(
|
|
1619
2346
|
resolveBasemap(
|
|
1620
2347
|
{
|
|
1621
|
-
tileUrl:
|
|
1622
|
-
attribution:
|
|
2348
|
+
tileUrl: optString9(widget.options, "tileUrl"),
|
|
2349
|
+
attribution: optString9(widget.options, "attribution")
|
|
1623
2350
|
},
|
|
1624
2351
|
useTenantBasemap()
|
|
1625
2352
|
)
|
|
1626
2353
|
);
|
|
1627
2354
|
if (data.forbidden) {
|
|
1628
|
-
return /* @__PURE__ */
|
|
2355
|
+
return /* @__PURE__ */ jsx12(WidgetFrame9, { title, children: /* @__PURE__ */ jsx12(
|
|
1629
2356
|
MapNotice,
|
|
1630
2357
|
{
|
|
1631
2358
|
glyph: "\u{1F512}",
|
|
@@ -1635,7 +2362,7 @@ function MapWidget({ widget, data }) {
|
|
|
1635
2362
|
) });
|
|
1636
2363
|
}
|
|
1637
2364
|
if (!runtime) {
|
|
1638
|
-
return /* @__PURE__ */
|
|
2365
|
+
return /* @__PURE__ */ jsx12(WidgetFrame9, { title, children: /* @__PURE__ */ jsx12(
|
|
1639
2366
|
MapNotice,
|
|
1640
2367
|
{
|
|
1641
2368
|
glyph: "\u{1F9E9}",
|
|
@@ -1645,10 +2372,10 @@ function MapWidget({ widget, data }) {
|
|
|
1645
2372
|
) });
|
|
1646
2373
|
}
|
|
1647
2374
|
if (data.loading && positions.length === 0) {
|
|
1648
|
-
return /* @__PURE__ */
|
|
2375
|
+
return /* @__PURE__ */ jsx12(WidgetFrame9, { title, children: /* @__PURE__ */ jsx12(MapNotice, { heading: "Loading\u2026" }) });
|
|
1649
2376
|
}
|
|
1650
2377
|
if (positions.length === 0) {
|
|
1651
|
-
return /* @__PURE__ */
|
|
2378
|
+
return /* @__PURE__ */ jsx12(WidgetFrame9, { title, children: /* @__PURE__ */ jsx12(
|
|
1652
2379
|
MapNotice,
|
|
1653
2380
|
{
|
|
1654
2381
|
heading: data.deviceTokens.length === 0 ? "No devices selected" : "No positions reported",
|
|
@@ -1656,11 +2383,11 @@ function MapWidget({ widget, data }) {
|
|
|
1656
2383
|
}
|
|
1657
2384
|
) });
|
|
1658
2385
|
}
|
|
1659
|
-
return /* @__PURE__ */
|
|
2386
|
+
return /* @__PURE__ */ jsx12(WidgetFrame9, { title, flush: true, children: /* @__PURE__ */ jsx12(LiveMap, { basemap, positions, runtime }) });
|
|
1660
2387
|
}
|
|
1661
2388
|
function PlainPanel({ positions, note }) {
|
|
1662
2389
|
const points = projectToPanel(positions);
|
|
1663
|
-
return /* @__PURE__ */
|
|
2390
|
+
return /* @__PURE__ */ jsxs7(
|
|
1664
2391
|
"div",
|
|
1665
2392
|
{
|
|
1666
2393
|
"data-testid": "map-plain-panel",
|
|
@@ -1669,10 +2396,10 @@ function PlainPanel({ positions, note }) {
|
|
|
1669
2396
|
width: "100%",
|
|
1670
2397
|
height: "100%",
|
|
1671
2398
|
overflow: "hidden",
|
|
1672
|
-
background:
|
|
2399
|
+
background: css9("muted")
|
|
1673
2400
|
},
|
|
1674
2401
|
children: [
|
|
1675
|
-
positions.map((position, i) => /* @__PURE__ */
|
|
2402
|
+
positions.map((position, i) => /* @__PURE__ */ jsx12(
|
|
1676
2403
|
Marker,
|
|
1677
2404
|
{
|
|
1678
2405
|
position,
|
|
@@ -1685,7 +2412,7 @@ function PlainPanel({ positions, note }) {
|
|
|
1685
2412
|
},
|
|
1686
2413
|
position.deviceToken
|
|
1687
2414
|
)),
|
|
1688
|
-
/* @__PURE__ */
|
|
2415
|
+
/* @__PURE__ */ jsx12(
|
|
1689
2416
|
"div",
|
|
1690
2417
|
{
|
|
1691
2418
|
"data-testid": "map-no-tiles-note",
|
|
@@ -1695,11 +2422,11 @@ function PlainPanel({ positions, note }) {
|
|
|
1695
2422
|
right: 0,
|
|
1696
2423
|
bottom: 0,
|
|
1697
2424
|
padding: "4px 8px",
|
|
1698
|
-
fontSize:
|
|
2425
|
+
fontSize: fontVar8("sm"),
|
|
1699
2426
|
lineHeight: 1.3,
|
|
1700
2427
|
textAlign: "center",
|
|
1701
|
-
color:
|
|
1702
|
-
background:
|
|
2428
|
+
color: css9("muted-foreground"),
|
|
2429
|
+
background: css9("card"),
|
|
1703
2430
|
opacity: 0.9
|
|
1704
2431
|
},
|
|
1705
2432
|
children: note
|
|
@@ -1717,10 +2444,10 @@ function LiveMap({
|
|
|
1717
2444
|
const containerRef = useRef2(null);
|
|
1718
2445
|
const mapRef = useRef2(null);
|
|
1719
2446
|
const markersRef = useRef2([]);
|
|
1720
|
-
const [status, setStatus] =
|
|
2447
|
+
const [status, setStatus] = useState6("loading");
|
|
1721
2448
|
const tileUrl = basemap?.tileUrl ?? null;
|
|
1722
2449
|
const attribution = basemap?.attribution ?? null;
|
|
1723
|
-
|
|
2450
|
+
useEffect4(() => {
|
|
1724
2451
|
let cancelled = false;
|
|
1725
2452
|
setStatus("loading");
|
|
1726
2453
|
Promise.all([
|
|
@@ -1748,7 +2475,7 @@ function LiveMap({
|
|
|
1748
2475
|
mapRef.current = null;
|
|
1749
2476
|
};
|
|
1750
2477
|
}, [tileUrl, attribution, runtime.workerUrl]);
|
|
1751
|
-
|
|
2478
|
+
useEffect4(() => {
|
|
1752
2479
|
const map = mapRef.current;
|
|
1753
2480
|
if (status !== "ready" || !map) return;
|
|
1754
2481
|
let live = true;
|
|
@@ -1777,7 +2504,7 @@ function LiveMap({
|
|
|
1777
2504
|
};
|
|
1778
2505
|
}, [status, positionsKey(positions)]);
|
|
1779
2506
|
if (status === "unavailable") {
|
|
1780
|
-
return /* @__PURE__ */
|
|
2507
|
+
return /* @__PURE__ */ jsx12(
|
|
1781
2508
|
PlainPanel,
|
|
1782
2509
|
{
|
|
1783
2510
|
positions,
|
|
@@ -1785,7 +2512,7 @@ function LiveMap({
|
|
|
1785
2512
|
}
|
|
1786
2513
|
);
|
|
1787
2514
|
}
|
|
1788
|
-
return /* @__PURE__ */
|
|
2515
|
+
return /* @__PURE__ */ jsx12("div", { ref: containerRef, "data-testid": "map-canvas", style: { width: "100%", height: "100%" } });
|
|
1789
2516
|
}
|
|
1790
2517
|
function positionsKey(positions) {
|
|
1791
2518
|
return positions.map((p) => `${p.deviceToken}:${p.latitude}:${p.longitude}:${p.heading ?? ""}`).join("|");
|
|
@@ -1794,8 +2521,8 @@ var MARKER_DOT_STYLE = {
|
|
|
1794
2521
|
width: "12px",
|
|
1795
2522
|
height: "12px",
|
|
1796
2523
|
borderRadius: "50%",
|
|
1797
|
-
background:
|
|
1798
|
-
border: `2px solid ${
|
|
2524
|
+
background: css9("primary"),
|
|
2525
|
+
border: `2px solid ${css9("card")}`,
|
|
1799
2526
|
boxShadow: "0 0 0 1px rgba(0,0,0,0.25)",
|
|
1800
2527
|
cursor: "default"
|
|
1801
2528
|
};
|
|
@@ -1804,7 +2531,7 @@ function Marker({
|
|
|
1804
2531
|
style
|
|
1805
2532
|
}) {
|
|
1806
2533
|
const label = describePosition(position);
|
|
1807
|
-
return /* @__PURE__ */
|
|
2534
|
+
return /* @__PURE__ */ jsx12(
|
|
1808
2535
|
"div",
|
|
1809
2536
|
{
|
|
1810
2537
|
"data-testid": "map-marker",
|
|
@@ -1816,72 +2543,74 @@ function Marker({
|
|
|
1816
2543
|
);
|
|
1817
2544
|
}
|
|
1818
2545
|
function MapNotice({ glyph, heading, detail }) {
|
|
1819
|
-
return /* @__PURE__ */
|
|
2546
|
+
return /* @__PURE__ */ jsxs7(
|
|
1820
2547
|
"div",
|
|
1821
2548
|
{
|
|
1822
2549
|
style: {
|
|
1823
2550
|
display: "flex",
|
|
1824
2551
|
flexDirection: "column",
|
|
1825
2552
|
alignItems: "center",
|
|
1826
|
-
justifyContent: "center",
|
|
2553
|
+
justifyContent: "safe center",
|
|
2554
|
+
overflow: "hidden",
|
|
1827
2555
|
gap: 4,
|
|
1828
2556
|
width: "100%",
|
|
1829
2557
|
height: "100%",
|
|
1830
|
-
padding: 12,
|
|
1831
2558
|
boxSizing: "border-box",
|
|
1832
2559
|
textAlign: "center",
|
|
1833
|
-
color:
|
|
2560
|
+
color: css9("muted-foreground")
|
|
1834
2561
|
},
|
|
1835
2562
|
children: [
|
|
1836
|
-
glyph ? /* @__PURE__ */
|
|
1837
|
-
/* @__PURE__ */
|
|
1838
|
-
detail ? /* @__PURE__ */
|
|
2563
|
+
glyph ? /* @__PURE__ */ jsx12("div", { style: { fontSize: fontVar8("xl"), lineHeight: 1 }, "aria-hidden": "true", children: glyph }) : null,
|
|
2564
|
+
/* @__PURE__ */ jsx12("div", { style: { fontSize: fontVar8("md"), fontWeight: 600 }, children: heading }),
|
|
2565
|
+
detail ? /* @__PURE__ */ jsx12("div", { style: { fontSize: fontVar8("sm"), opacity: 0.8 }, children: detail }) : null
|
|
1839
2566
|
]
|
|
1840
2567
|
}
|
|
1841
2568
|
);
|
|
1842
2569
|
}
|
|
1843
|
-
var mapWidget =
|
|
2570
|
+
var mapWidget = defineWidget12({
|
|
1844
2571
|
descriptor: builtinWidgetDescriptors["map"],
|
|
1845
2572
|
implementation: { kind: "trusted-react", component: MapWidget }
|
|
1846
2573
|
});
|
|
1847
2574
|
|
|
1848
2575
|
// src/widgets/table.tsx
|
|
1849
2576
|
import {
|
|
1850
|
-
css as
|
|
1851
|
-
defineWidget as
|
|
2577
|
+
css as css10,
|
|
2578
|
+
defineWidget as defineWidget13,
|
|
2579
|
+
fontVar as fontVar9,
|
|
2580
|
+
spaceVar as spaceVar4,
|
|
1852
2581
|
FlashValue as FlashValue2,
|
|
1853
2582
|
formatTimestamp as formatTimestamp2,
|
|
1854
2583
|
optBoolean as optBoolean4,
|
|
1855
|
-
optNumber as
|
|
1856
|
-
optString as
|
|
2584
|
+
optNumber as optNumber5,
|
|
2585
|
+
optString as optString10,
|
|
1857
2586
|
useWidgetTimeZone as useWidgetTimeZone4,
|
|
1858
2587
|
WidgetFrame as WidgetFrame10
|
|
1859
2588
|
} from "@devicechain/widget-sdk";
|
|
1860
|
-
import { jsx as
|
|
1861
|
-
var cell2 = { padding:
|
|
2589
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2590
|
+
var cell2 = { padding: `${spaceVar4(1)} ${spaceVar4(3)}`, textAlign: "left" };
|
|
1862
2591
|
var headCell2 = {
|
|
1863
2592
|
...cell2,
|
|
1864
|
-
fontSize:
|
|
2593
|
+
fontSize: fontVar9("xs"),
|
|
1865
2594
|
fontWeight: 600,
|
|
1866
2595
|
textTransform: "uppercase",
|
|
1867
2596
|
letterSpacing: "0.04em",
|
|
1868
|
-
color:
|
|
1869
|
-
borderBottom: `1px solid ${
|
|
2597
|
+
color: css10("muted-foreground"),
|
|
2598
|
+
borderBottom: `1px solid ${css10("border")}`
|
|
1870
2599
|
};
|
|
1871
2600
|
function Table({ widget, data }) {
|
|
1872
2601
|
const timeZone = useWidgetTimeZone4();
|
|
1873
2602
|
const rows = Object.values(data.latest).sort((a, b) => a.name.localeCompare(b.name));
|
|
1874
|
-
const precision =
|
|
2603
|
+
const precision = optNumber5(widget.options, "precision");
|
|
1875
2604
|
const flash = optBoolean4(widget.options, "flashOnChange");
|
|
1876
|
-
return /* @__PURE__ */
|
|
1877
|
-
/* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
1879
|
-
/* @__PURE__ */
|
|
1880
|
-
/* @__PURE__ */
|
|
2605
|
+
return /* @__PURE__ */ jsx13(WidgetFrame10, { title: optString10(widget.options, "title"), flush: true, children: /* @__PURE__ */ jsx13("div", { style: { height: "100%", overflow: "auto" }, children: /* @__PURE__ */ jsxs8("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: fontVar9("md"), color: css10("foreground") }, children: [
|
|
2606
|
+
/* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs8("tr", { children: [
|
|
2607
|
+
/* @__PURE__ */ jsx13("th", { style: headCell2, children: "Measurement" }),
|
|
2608
|
+
/* @__PURE__ */ jsx13("th", { style: headCell2, children: "Value" }),
|
|
2609
|
+
/* @__PURE__ */ jsx13("th", { style: headCell2, children: "Time" })
|
|
1881
2610
|
] }) }),
|
|
1882
|
-
/* @__PURE__ */
|
|
1883
|
-
/* @__PURE__ */
|
|
1884
|
-
/* @__PURE__ */
|
|
2611
|
+
/* @__PURE__ */ jsx13("tbody", { children: rows.length === 0 ? /* @__PURE__ */ jsx13("tr", { children: /* @__PURE__ */ jsx13("td", { colSpan: 3, style: { ...cell2, textAlign: "center", color: css10("muted-foreground") }, children: "No data yet" }) }) : rows.map((row) => /* @__PURE__ */ jsxs8("tr", { children: [
|
|
2612
|
+
/* @__PURE__ */ jsx13("td", { style: cell2, children: row.name }),
|
|
2613
|
+
/* @__PURE__ */ jsx13("td", { style: { ...cell2, fontVariantNumeric: "tabular-nums" }, children: /* @__PURE__ */ jsx13(
|
|
1885
2614
|
FlashValue2,
|
|
1886
2615
|
{
|
|
1887
2616
|
value: row.value,
|
|
@@ -1890,36 +2619,105 @@ function Table({ widget, data }) {
|
|
|
1890
2619
|
identity: `${row.deviceToken}:${row.name}`
|
|
1891
2620
|
}
|
|
1892
2621
|
) }),
|
|
1893
|
-
/* @__PURE__ */
|
|
2622
|
+
/* @__PURE__ */ jsx13("td", { style: { ...cell2, color: css10("muted-foreground") }, children: formatTimestamp2(row.occurredTime, timeZone) })
|
|
1894
2623
|
] }, row.name)) })
|
|
1895
2624
|
] }) }) });
|
|
1896
2625
|
}
|
|
1897
|
-
var tableWidget =
|
|
2626
|
+
var tableWidget = defineWidget13({
|
|
1898
2627
|
descriptor: builtinWidgetDescriptors["table"],
|
|
1899
2628
|
implementation: { kind: "trusted-react", component: Table }
|
|
1900
2629
|
});
|
|
1901
2630
|
|
|
1902
2631
|
// src/widgets/time-series-chart.tsx
|
|
1903
|
-
import { useMemo as
|
|
2632
|
+
import { useMemo as useMemo4 } from "react";
|
|
2633
|
+
import { isValidSpanSeconds, spanLabel } from "@devicechain/dashboards";
|
|
1904
2634
|
import {
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
2635
|
+
css as css11,
|
|
2636
|
+
defineWidget as defineWidget14,
|
|
2637
|
+
fontVar as fontVar10,
|
|
2638
|
+
formatDateTime as formatDateTime3,
|
|
2639
|
+
formatTimestamp as formatTimestamp3,
|
|
2640
|
+
optNumber as optNumber6,
|
|
2641
|
+
optString as optString11,
|
|
2642
|
+
optThresholds as optThresholds3,
|
|
2643
|
+
spaceVar as spaceVar5,
|
|
2644
|
+
useWidgetScale as useWidgetScale3,
|
|
2645
|
+
useWidgetTimeRange,
|
|
2646
|
+
useWidgetTimeZone as useWidgetTimeZone5,
|
|
2647
|
+
viewerLocale,
|
|
1908
2648
|
WidgetFrame as WidgetFrame11
|
|
1909
2649
|
} from "@devicechain/widget-sdk";
|
|
1910
2650
|
import { buildLineOption, EChart as EChart2, useChartTheme as useChartTheme2 } from "@devicechain/widget-sdk/echarts";
|
|
1911
|
-
import { jsx as
|
|
2651
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1912
2652
|
function TimeSeriesChart({ widget, data }) {
|
|
2653
|
+
return /* @__PURE__ */ jsx14(WidgetFrame11, { title: optString11(widget.options, "title"), flush: true, children: /* @__PURE__ */ jsx14(TimeSeriesBody, { widget, data }) });
|
|
2654
|
+
}
|
|
2655
|
+
function TimeSeriesBody({ widget, data }) {
|
|
1913
2656
|
const theme = useChartTheme2();
|
|
1914
|
-
const
|
|
2657
|
+
const { size, scale } = useWidgetScale3();
|
|
1915
2658
|
const measurements = widget.datasource?.measurements;
|
|
1916
|
-
const
|
|
1917
|
-
|
|
1918
|
-
|
|
2659
|
+
const unit = optString11(widget.options, "unit");
|
|
2660
|
+
const precision = optNumber6(widget.options, "precision");
|
|
2661
|
+
const thresholds = useMemo4(() => optThresholds3(widget.options, "thresholds"), [widget.options]);
|
|
2662
|
+
const option = useMemo4(
|
|
2663
|
+
() => buildLineOption(data.samples, theme, { measurements, unit, precision, thresholds, scale }, size),
|
|
2664
|
+
[data.samples, theme, measurements, unit, precision, thresholds, scale, size]
|
|
2665
|
+
);
|
|
2666
|
+
return /* @__PURE__ */ jsxs9("div", { style: { position: "relative", width: "100%", height: "100%" }, children: [
|
|
2667
|
+
/* @__PURE__ */ jsx14(EChart2, { option }),
|
|
2668
|
+
/* @__PURE__ */ jsx14(SpanBadge, { widget, data })
|
|
2669
|
+
] });
|
|
2670
|
+
}
|
|
2671
|
+
var SPAN_STRINGS = {
|
|
2672
|
+
en: {
|
|
2673
|
+
from: (time) => `from ${time}`,
|
|
2674
|
+
pinned: (span) => `Pinned to ${span}: the board's time range does not apply to this chart.`,
|
|
2675
|
+
cut: (time, span) => `Showing from ${time}, not the whole ${span}: older samples were dropped to bound memory.`
|
|
2676
|
+
},
|
|
2677
|
+
es: {
|
|
2678
|
+
from: (time) => `desde ${time}`,
|
|
2679
|
+
pinned: (span) => `Fijado a ${span}: el intervalo de tiempo del panel no se aplica a este gr\xE1fico.`,
|
|
2680
|
+
cut: (time, span) => `Se muestra desde ${time}, no todo el intervalo de ${span}: se descartaron las muestras m\xE1s antiguas para acotar la memoria.`
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
var DAY_MS = 864e5;
|
|
2684
|
+
function SpanBadge({ widget, data }) {
|
|
2685
|
+
const range = useWidgetTimeRange();
|
|
2686
|
+
const timeZone = useWidgetTimeZone5();
|
|
2687
|
+
const pinned = optNumber6(widget.options, "windowSeconds");
|
|
2688
|
+
const span = isValidSpanSeconds(pinned) ? pinned : range?.spanSeconds;
|
|
2689
|
+
const showSpan = range !== void 0 && span !== range.spanSeconds;
|
|
2690
|
+
const cutAt = data.truncatedBefore;
|
|
2691
|
+
if (!showSpan && !cutAt) return null;
|
|
2692
|
+
const words = viewerLocale()?.toLowerCase().startsWith("es") ? SPAN_STRINGS.es : SPAN_STRINGS.en;
|
|
2693
|
+
const label = span === void 0 ? "" : spanLabel(span);
|
|
2694
|
+
const end = range?.paused && range.pausedAt !== void 0 ? range.pausedAt : Date.now();
|
|
2695
|
+
const time = cutAt ? end - Date.parse(cutAt) > DAY_MS ? formatDateTime3(cutAt, timeZone) : formatTimestamp3(cutAt, timeZone) : "";
|
|
2696
|
+
const text = [showSpan ? label : "", cutAt ? words.from(time) : ""].filter(Boolean).join(" \xB7 ");
|
|
2697
|
+
const title = [showSpan ? words.pinned(label) : "", cutAt ? words.cut(time, label) : ""].filter(Boolean).join(" ");
|
|
2698
|
+
return /* @__PURE__ */ jsx14(
|
|
2699
|
+
"span",
|
|
2700
|
+
{
|
|
2701
|
+
"data-chart-span": showSpan ? label : "",
|
|
2702
|
+
"data-chart-truncated": cutAt ? "" : void 0,
|
|
2703
|
+
title,
|
|
2704
|
+
style: {
|
|
2705
|
+
position: "absolute",
|
|
2706
|
+
top: spaceVar5(1),
|
|
2707
|
+
right: spaceVar5(2),
|
|
2708
|
+
padding: `0 ${spaceVar5(1)}`,
|
|
2709
|
+
fontSize: fontVar10("xs"),
|
|
2710
|
+
whiteSpace: "nowrap",
|
|
2711
|
+
color: css11("muted-foreground"),
|
|
2712
|
+
background: css11("muted"),
|
|
2713
|
+
borderRadius: 4,
|
|
2714
|
+
pointerEvents: "auto"
|
|
2715
|
+
},
|
|
2716
|
+
children: text
|
|
2717
|
+
}
|
|
1919
2718
|
);
|
|
1920
|
-
return /* @__PURE__ */ jsx13(WidgetFrame11, { title: optString12(widget.options, "title"), children: /* @__PURE__ */ jsx13("div", { ref: sizeRef, style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx13(EChart2, { option }) }) });
|
|
1921
2719
|
}
|
|
1922
|
-
var timeSeriesChartWidget =
|
|
2720
|
+
var timeSeriesChartWidget = defineWidget14({
|
|
1923
2721
|
descriptor: builtinWidgetDescriptors["timeseries-chart"],
|
|
1924
2722
|
implementation: { kind: "trusted-react", component: TimeSeriesChart }
|
|
1925
2723
|
});
|
|
@@ -1936,7 +2734,20 @@ var builtinWidgetDefinitions = [
|
|
|
1936
2734
|
alarmCountWidget,
|
|
1937
2735
|
commandButtonWidget,
|
|
1938
2736
|
entitySelectorWidget,
|
|
1939
|
-
mapWidget
|
|
2737
|
+
mapWidget,
|
|
2738
|
+
linearGaugeWidget,
|
|
2739
|
+
levelGaugeWidget,
|
|
2740
|
+
batteryWidget,
|
|
2741
|
+
statusLightWidget,
|
|
2742
|
+
signalBarsWidget,
|
|
2743
|
+
compassWidget,
|
|
2744
|
+
progressRingWidget,
|
|
2745
|
+
commandSliderWidget,
|
|
2746
|
+
commandToggleWidget,
|
|
2747
|
+
statCardWidget,
|
|
2748
|
+
barChartWidget,
|
|
2749
|
+
pieChartWidget,
|
|
2750
|
+
fleetTableWidget
|
|
1940
2751
|
];
|
|
1941
2752
|
function createBuiltinWidgetRegistry() {
|
|
1942
2753
|
return createWidgetRegistry(builtinWidgetDefinitions);
|
|
@@ -1951,7 +2762,7 @@ function defaultWidgetRegistry() {
|
|
|
1951
2762
|
|
|
1952
2763
|
// src/connected-widget.tsx
|
|
1953
2764
|
import {
|
|
1954
|
-
optString as
|
|
2765
|
+
optString as optString12,
|
|
1955
2766
|
useAlarmStream,
|
|
1956
2767
|
useCommandStream,
|
|
1957
2768
|
useDatasourceAvailability,
|
|
@@ -1963,7 +2774,7 @@ import {
|
|
|
1963
2774
|
} from "@devicechain/widget-sdk";
|
|
1964
2775
|
|
|
1965
2776
|
// src/widget-containment.tsx
|
|
1966
|
-
import { Component, createContext as createContext3, useCallback, useContext as useContext3, useState as
|
|
2777
|
+
import { Component, createContext as createContext3, useCallback, useContext as useContext3, useState as useState7 } from "react";
|
|
1967
2778
|
import { WidgetFrame as WidgetFrame12 } from "@devicechain/widget-sdk";
|
|
1968
2779
|
|
|
1969
2780
|
// src/widget-host.ts
|
|
@@ -2022,7 +2833,7 @@ function narrowCandidates(candidates, descriptor, report) {
|
|
|
2022
2833
|
}
|
|
2023
2834
|
|
|
2024
2835
|
// src/widget-containment.tsx
|
|
2025
|
-
import { jsx as
|
|
2836
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2026
2837
|
var MESSAGE_STYLE = {
|
|
2027
2838
|
display: "flex",
|
|
2028
2839
|
flexDirection: "column",
|
|
@@ -2066,23 +2877,23 @@ var WidgetErrorBoundary = class extends Component {
|
|
|
2066
2877
|
render() {
|
|
2067
2878
|
const { error } = this.state;
|
|
2068
2879
|
if (!error) return this.props.children;
|
|
2069
|
-
return /* @__PURE__ */
|
|
2070
|
-
/* @__PURE__ */
|
|
2071
|
-
/* @__PURE__ */
|
|
2072
|
-
/* @__PURE__ */
|
|
2073
|
-
/* @__PURE__ */
|
|
2880
|
+
return /* @__PURE__ */ jsx15(WidgetFrame12, { title: this.props.title, children: /* @__PURE__ */ jsxs10("div", { style: MESSAGE_STYLE, role: "alert", children: [
|
|
2881
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 20, lineHeight: 1 }, "aria-hidden": "true", children: "\u26A0" }),
|
|
2882
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 13, fontWeight: 600 }, children: "This widget failed to render" }),
|
|
2883
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 11, opacity: 0.8, wordBreak: "break-word" }, children: error.message }),
|
|
2884
|
+
/* @__PURE__ */ jsx15("button", { type: "button", style: BUTTON_STYLE, onClick: this.reset, children: "Reset widget" })
|
|
2074
2885
|
] }) });
|
|
2075
2886
|
}
|
|
2076
2887
|
};
|
|
2077
2888
|
function WidgetContainment({ title, resetKey, children }) {
|
|
2078
|
-
const [messages, setMessages] =
|
|
2889
|
+
const [messages, setMessages] = useState7([]);
|
|
2079
2890
|
const report = useCallback((message) => {
|
|
2080
2891
|
setMessages((prev) => prev.includes(message) ? prev : [...prev, message].slice(-3));
|
|
2081
2892
|
}, []);
|
|
2082
2893
|
const clear = useCallback(() => setMessages([]), []);
|
|
2083
|
-
return /* @__PURE__ */
|
|
2084
|
-
/* @__PURE__ */
|
|
2085
|
-
messages.length > 0 && /* @__PURE__ */
|
|
2894
|
+
return /* @__PURE__ */ jsxs10("div", { style: { position: "relative", width: "100%", height: "100%" }, children: [
|
|
2895
|
+
/* @__PURE__ */ jsx15(ReportContext.Provider, { value: report, children: /* @__PURE__ */ jsx15(WidgetErrorBoundary, { title, resetKey, onReset: clear, children }) }),
|
|
2896
|
+
messages.length > 0 && /* @__PURE__ */ jsxs10(
|
|
2086
2897
|
"div",
|
|
2087
2898
|
{
|
|
2088
2899
|
role: "status",
|
|
@@ -2101,8 +2912,8 @@ function WidgetContainment({ title, resetKey, children }) {
|
|
|
2101
2912
|
alignItems: "center"
|
|
2102
2913
|
},
|
|
2103
2914
|
children: [
|
|
2104
|
-
/* @__PURE__ */
|
|
2105
|
-
/* @__PURE__ */
|
|
2915
|
+
/* @__PURE__ */ jsx15("span", { style: { flex: 1, wordBreak: "break-word" }, children: messages.join(" \xB7 ") }),
|
|
2916
|
+
/* @__PURE__ */ jsx15("button", { type: "button", "aria-label": "Dismiss", onClick: clear, style: { cursor: "pointer" }, children: "\xD7" })
|
|
2106
2917
|
]
|
|
2107
2918
|
}
|
|
2108
2919
|
)
|
|
@@ -2110,12 +2921,12 @@ function WidgetContainment({ title, resetKey, children }) {
|
|
|
2110
2921
|
}
|
|
2111
2922
|
|
|
2112
2923
|
// src/connected-widget.tsx
|
|
2113
|
-
import { jsx as
|
|
2924
|
+
import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2114
2925
|
function ConnectedWidget(props) {
|
|
2115
2926
|
const { widget } = props;
|
|
2116
2927
|
const registry = props.registry ?? defaultWidgetRegistry();
|
|
2117
2928
|
const resetKey = JSON.stringify([widget.type, widget.options ?? null, widget.datasource ?? null]);
|
|
2118
|
-
return /* @__PURE__ */
|
|
2929
|
+
return /* @__PURE__ */ jsx16(WidgetContainment, { title: optString12(widget.options, "title"), resetKey, children: /* @__PURE__ */ jsx16(ConnectedWidgetBody, { ...props, registry }) });
|
|
2119
2930
|
}
|
|
2120
2931
|
function ConnectedWidgetBody({ widget, hub, registry, actions, initialSamples }) {
|
|
2121
2932
|
const unresolved = widget.type === UNKNOWN_WIDGET_TYPE;
|
|
@@ -2124,13 +2935,13 @@ function ConnectedWidgetBody({ widget, hub, registry, actions, initialSamples })
|
|
|
2124
2935
|
const readsDatasource = descriptor !== void 0 && descriptor.datasource.mode !== "none";
|
|
2125
2936
|
const availability = useDatasourceAvailability(hub, readsDatasource ? widget.datasource : void 0);
|
|
2126
2937
|
if (availability === "unavailable") {
|
|
2127
|
-
return /* @__PURE__ */
|
|
2938
|
+
return /* @__PURE__ */ jsx16(WidgetUnavailableFrame, { widget });
|
|
2128
2939
|
}
|
|
2129
2940
|
if (widget.type === UNKNOWN_WIDGET_TYPE) {
|
|
2130
|
-
return /* @__PURE__ */
|
|
2941
|
+
return /* @__PURE__ */ jsx16(UnresolvedWidgetFrame, { storedType: widget.raw.type, reason: widget.reason });
|
|
2131
2942
|
}
|
|
2132
2943
|
if (!descriptor || !implementation) {
|
|
2133
|
-
return /* @__PURE__ */
|
|
2944
|
+
return /* @__PURE__ */ jsx16(
|
|
2134
2945
|
UnresolvedWidgetFrame,
|
|
2135
2946
|
{
|
|
2136
2947
|
storedType: widget.type,
|
|
@@ -2138,7 +2949,7 @@ function ConnectedWidgetBody({ widget, hub, registry, actions, initialSamples })
|
|
|
2138
2949
|
}
|
|
2139
2950
|
);
|
|
2140
2951
|
}
|
|
2141
|
-
return /* @__PURE__ */
|
|
2952
|
+
return /* @__PURE__ */ jsx16(HostScope, { descriptor, actions, children: (hostActions) => /* @__PURE__ */ jsx16(
|
|
2142
2953
|
ChannelBody,
|
|
2143
2954
|
{
|
|
2144
2955
|
widget,
|
|
@@ -2158,57 +2969,91 @@ function HostScope({
|
|
|
2158
2969
|
const report = useReportRejection();
|
|
2159
2970
|
const select = useWidgetSelect3();
|
|
2160
2971
|
const candidates = useWidgetCandidates();
|
|
2161
|
-
const narrowed =
|
|
2162
|
-
const narrowedSelect =
|
|
2163
|
-
const narrowedCandidates =
|
|
2972
|
+
const narrowed = useMemo5(() => narrowActions(actions, descriptor, report), [actions, descriptor, report]);
|
|
2973
|
+
const narrowedSelect = useMemo5(() => narrowSelect(select, descriptor), [select, descriptor]);
|
|
2974
|
+
const narrowedCandidates = useMemo5(
|
|
2164
2975
|
() => narrowCandidates(candidates, descriptor, report),
|
|
2165
2976
|
[candidates, descriptor, report]
|
|
2166
2977
|
);
|
|
2167
|
-
return /* @__PURE__ */
|
|
2978
|
+
return /* @__PURE__ */ jsx16(WidgetSelectProvider, { select: narrowedSelect, children: /* @__PURE__ */ jsx16(WidgetCandidatesProvider, { candidates: narrowedCandidates, children: children(narrowed) }) });
|
|
2168
2979
|
}
|
|
2169
2980
|
function ChannelBody(props) {
|
|
2170
2981
|
switch (props.descriptor.channel) {
|
|
2171
2982
|
case "alarm":
|
|
2172
|
-
return /* @__PURE__ */
|
|
2983
|
+
return /* @__PURE__ */ jsx16(AlarmConnectedWidget, { ...props });
|
|
2173
2984
|
case "control":
|
|
2174
|
-
return /* @__PURE__ */
|
|
2985
|
+
return /* @__PURE__ */ jsx16(ControlConnectedWidget, { ...props });
|
|
2175
2986
|
case "location":
|
|
2176
|
-
return /* @__PURE__ */
|
|
2987
|
+
return /* @__PURE__ */ jsx16(LocationConnectedWidget, { ...props });
|
|
2988
|
+
case "aggregate":
|
|
2989
|
+
return /* @__PURE__ */ jsx16(AggregateConnectedWidget, { ...props });
|
|
2177
2990
|
case "selection":
|
|
2178
2991
|
case "none":
|
|
2179
|
-
return /* @__PURE__ */
|
|
2992
|
+
return /* @__PURE__ */ jsx16(StaticConnectedWidget, { ...props });
|
|
2180
2993
|
case "measurement":
|
|
2181
|
-
return /* @__PURE__ */
|
|
2994
|
+
return /* @__PURE__ */ jsx16(MeasurementConnectedWidget, { ...props });
|
|
2182
2995
|
}
|
|
2183
2996
|
}
|
|
2184
2997
|
function StaticConnectedWidget({ widget, Component: Component2, actions }) {
|
|
2185
|
-
return /* @__PURE__ */
|
|
2998
|
+
return /* @__PURE__ */ jsx16(Component2, { widget, data: null, actions });
|
|
2999
|
+
}
|
|
3000
|
+
var FleetFeed = lazyChunk(() => import("./chunks/fleet-feed-FK4ZMKIK.js")).component("default");
|
|
3001
|
+
function MeasurementConnectedWidget(props) {
|
|
3002
|
+
const { widget, hub, descriptor, Component: Component2, actions } = props;
|
|
3003
|
+
const members = membersMode(descriptor, widget);
|
|
3004
|
+
if (members || widget.datasource?.kind === "anchor") {
|
|
3005
|
+
return /* @__PURE__ */ jsx16(FleetFeed, { hub, widget, descriptor, Component: Component2, actions, ErrorFrame: WidgetErrorFrame, refuse: !members });
|
|
3006
|
+
}
|
|
3007
|
+
return /* @__PURE__ */ jsx16(StreamConnectedWidget, { ...props });
|
|
2186
3008
|
}
|
|
2187
|
-
function
|
|
3009
|
+
function StreamConnectedWidget({ widget, hub, descriptor, Component: Component2, actions, initialSamples }) {
|
|
3010
|
+
const range = useWidgetTimeRange2();
|
|
3011
|
+
const now = useMemo5(() => hub.now ? () => hub.now() : Date.now, [hub]);
|
|
2188
3012
|
const data = useMeasurementStream(hub, widget.datasource, {
|
|
2189
3013
|
window: measurementWindow(descriptor, widget),
|
|
3014
|
+
windowSeconds: measurementSpan(descriptor, widget, range?.spanSeconds),
|
|
3015
|
+
pausedAt: range?.paused ? range.pausedAt : void 0,
|
|
3016
|
+
now,
|
|
2190
3017
|
initialSamples
|
|
2191
3018
|
});
|
|
2192
|
-
|
|
2193
|
-
|
|
3019
|
+
const freshness = useMemo5(() => {
|
|
3020
|
+
if (descriptor.freshness !== true) return void 0;
|
|
3021
|
+
const pinned = optString12(widget.options, "measurement");
|
|
3022
|
+
const names = pinned ? [pinned] : widget.datasource?.measurements;
|
|
3023
|
+
return {
|
|
3024
|
+
at: newestSampleTime(data.latest, names),
|
|
3025
|
+
staleAfterSeconds: resolveStaleAfterSeconds(widget.options)
|
|
3026
|
+
};
|
|
3027
|
+
}, [descriptor, widget.options, widget.datasource, data.latest]);
|
|
3028
|
+
if (data.error) return /* @__PURE__ */ jsx16(WidgetErrorFrame, { widget, error: data.error });
|
|
3029
|
+
return /* @__PURE__ */ jsx16(WidgetFreshnessProvider, { freshness, children: /* @__PURE__ */ jsx16(Component2, { widget, data, actions }) });
|
|
3030
|
+
}
|
|
3031
|
+
var AggregateFeed = lazyChunk(() => import("./chunks/aggregate-feed-6ZYKOQOB.js")).component("default");
|
|
3032
|
+
function AggregateConnectedWidget({ widget, hub, descriptor, Component: Component2, actions }) {
|
|
3033
|
+
const props = { hub, widget, descriptor, Component: Component2, actions, ErrorFrame: WidgetErrorFrame };
|
|
3034
|
+
return widget.datasource?.kind === "anchor" ? /* @__PURE__ */ jsx16(FleetFeed, { ...props, refuse: true }) : /* @__PURE__ */ jsx16(AggregateFeed, { ...props });
|
|
2194
3035
|
}
|
|
2195
3036
|
function AlarmConnectedWidget({ widget, hub, descriptor, Component: Component2, actions }) {
|
|
2196
|
-
const
|
|
3037
|
+
const range = useWidgetTimeRange2();
|
|
3038
|
+
const live = useAlarmStream(hub, alarmSubscription(descriptor, widget));
|
|
3039
|
+
const data = useHeld(live, range?.paused === true, (s) => !s.loading);
|
|
2197
3040
|
if (data.error && data.alarms.length === 0 && data.total === 0) {
|
|
2198
|
-
return /* @__PURE__ */
|
|
3041
|
+
return /* @__PURE__ */ jsx16(WidgetErrorFrame, { widget, error: data.error });
|
|
2199
3042
|
}
|
|
2200
|
-
return /* @__PURE__ */
|
|
3043
|
+
return /* @__PURE__ */ jsx16(Component2, { widget, data, actions });
|
|
2201
3044
|
}
|
|
2202
3045
|
function ControlConnectedWidget({ widget, hub, descriptor, Component: Component2, actions }) {
|
|
2203
3046
|
const data = useCommandStream(hub, commandSubscription(descriptor, widget));
|
|
2204
|
-
return /* @__PURE__ */
|
|
3047
|
+
return /* @__PURE__ */ jsx16(Component2, { widget, data, actions });
|
|
2205
3048
|
}
|
|
2206
3049
|
function LocationConnectedWidget({ widget, hub, Component: Component2, actions }) {
|
|
2207
|
-
const
|
|
3050
|
+
const range = useWidgetTimeRange2();
|
|
3051
|
+
const live = useLocationStream(hub, { datasource: widget.datasource });
|
|
3052
|
+
const data = useHeld(live, range?.paused === true, (s) => !s.loading);
|
|
2208
3053
|
if (data.error && data.locations.length === 0) {
|
|
2209
|
-
return /* @__PURE__ */
|
|
3054
|
+
return /* @__PURE__ */ jsx16(WidgetErrorFrame, { widget, error: data.error });
|
|
2210
3055
|
}
|
|
2211
|
-
return /* @__PURE__ */
|
|
3056
|
+
return /* @__PURE__ */ jsx16(Component2, { widget, data, actions });
|
|
2212
3057
|
}
|
|
2213
3058
|
function UnresolvedWidgetFrame({
|
|
2214
3059
|
storedType: stored,
|
|
@@ -2216,7 +3061,7 @@ function UnresolvedWidgetFrame({
|
|
|
2216
3061
|
}) {
|
|
2217
3062
|
const storedType = typeof stored === "string" ? stored : "";
|
|
2218
3063
|
const heading = (reason?.code ?? "unregistered") === "unregistered" ? "This widget needs a newer viewer" : "This widget cannot be shown here";
|
|
2219
|
-
return /* @__PURE__ */
|
|
3064
|
+
return /* @__PURE__ */ jsx16(WidgetFrame13, { children: /* @__PURE__ */ jsxs11(
|
|
2220
3065
|
"div",
|
|
2221
3066
|
{
|
|
2222
3067
|
style: {
|
|
@@ -2233,18 +3078,18 @@ function UnresolvedWidgetFrame({
|
|
|
2233
3078
|
color: "hsl(var(--muted-foreground))"
|
|
2234
3079
|
},
|
|
2235
3080
|
children: [
|
|
2236
|
-
/* @__PURE__ */
|
|
2237
|
-
storedType && !reason?.message.includes(storedType) && /* @__PURE__ */
|
|
3081
|
+
/* @__PURE__ */ jsx16("div", { style: { fontSize: 13, fontWeight: 600 }, children: heading }),
|
|
3082
|
+
storedType && !reason?.message.includes(storedType) && /* @__PURE__ */ jsxs11("div", { style: { fontSize: 11, opacity: 0.8 }, children: [
|
|
2238
3083
|
"Widget type: ",
|
|
2239
3084
|
storedType
|
|
2240
3085
|
] }),
|
|
2241
|
-
reason && /* @__PURE__ */
|
|
3086
|
+
reason && /* @__PURE__ */ jsx16("div", { style: { fontSize: 11, opacity: 0.8, wordBreak: "break-word" }, children: reason.message })
|
|
2242
3087
|
]
|
|
2243
3088
|
}
|
|
2244
3089
|
) });
|
|
2245
3090
|
}
|
|
2246
3091
|
function WidgetUnavailableFrame({ widget }) {
|
|
2247
|
-
return /* @__PURE__ */
|
|
3092
|
+
return /* @__PURE__ */ jsx16(WidgetFrame13, { title: optString12(widget.options, "title"), children: /* @__PURE__ */ jsxs11(
|
|
2248
3093
|
"div",
|
|
2249
3094
|
{
|
|
2250
3095
|
style: {
|
|
@@ -2261,15 +3106,15 @@ function WidgetUnavailableFrame({ widget }) {
|
|
|
2261
3106
|
color: "hsl(var(--muted-foreground))"
|
|
2262
3107
|
},
|
|
2263
3108
|
children: [
|
|
2264
|
-
/* @__PURE__ */
|
|
2265
|
-
/* @__PURE__ */
|
|
2266
|
-
/* @__PURE__ */
|
|
3109
|
+
/* @__PURE__ */ jsx16("div", { style: { fontSize: 20, lineHeight: 1 }, "aria-hidden": "true", children: "\u2298" }),
|
|
3110
|
+
/* @__PURE__ */ jsx16("div", { style: { fontSize: 13, fontWeight: 600 }, children: "Device unavailable" }),
|
|
3111
|
+
/* @__PURE__ */ jsx16("div", { style: { fontSize: 11, opacity: 0.8 }, children: "The bound device no longer exists." })
|
|
2267
3112
|
]
|
|
2268
3113
|
}
|
|
2269
3114
|
) });
|
|
2270
3115
|
}
|
|
2271
3116
|
function WidgetErrorFrame({ widget, error }) {
|
|
2272
|
-
return /* @__PURE__ */
|
|
3117
|
+
return /* @__PURE__ */ jsx16(WidgetFrame13, { title: optString12(widget.options, "title"), children: /* @__PURE__ */ jsxs11(
|
|
2273
3118
|
"div",
|
|
2274
3119
|
{
|
|
2275
3120
|
style: {
|
|
@@ -2286,9 +3131,9 @@ function WidgetErrorFrame({ widget, error }) {
|
|
|
2286
3131
|
color: "hsl(var(--muted-foreground))"
|
|
2287
3132
|
},
|
|
2288
3133
|
children: [
|
|
2289
|
-
/* @__PURE__ */
|
|
2290
|
-
/* @__PURE__ */
|
|
2291
|
-
/* @__PURE__ */
|
|
3134
|
+
/* @__PURE__ */ jsx16("div", { style: { fontSize: 20, lineHeight: 1 }, children: "\u26A0" }),
|
|
3135
|
+
/* @__PURE__ */ jsx16("div", { style: { fontSize: 13, fontWeight: 600 }, children: "Data unavailable" }),
|
|
3136
|
+
/* @__PURE__ */ jsx16("div", { style: { fontSize: 11, opacity: 0.8, wordBreak: "break-word" }, children: String(error) })
|
|
2292
3137
|
]
|
|
2293
3138
|
}
|
|
2294
3139
|
) });
|
|
@@ -2302,7 +3147,7 @@ import {
|
|
|
2302
3147
|
WidgetThemeRootProvider,
|
|
2303
3148
|
WidgetTimeZoneProvider
|
|
2304
3149
|
} from "@devicechain/widget-sdk";
|
|
2305
|
-
import { jsx as
|
|
3150
|
+
import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2306
3151
|
function DashboardRenderer({
|
|
2307
3152
|
definition,
|
|
2308
3153
|
hub,
|
|
@@ -2314,57 +3159,164 @@ function DashboardRenderer({
|
|
|
2314
3159
|
select,
|
|
2315
3160
|
candidates,
|
|
2316
3161
|
timeZone,
|
|
2317
|
-
themeRoot
|
|
3162
|
+
themeRoot,
|
|
3163
|
+
density,
|
|
3164
|
+
timeRange
|
|
2318
3165
|
}) {
|
|
2319
|
-
const
|
|
2320
|
-
const widgets = useMemo5(() => resolveWidgets(definition.widgets, bindings), [definition.widgets, bindings]);
|
|
3166
|
+
const widgets = useMemo6(() => resolveWidgets(definition.widgets, bindings), [definition.widgets, bindings]);
|
|
2321
3167
|
const registry = registryProp ?? defaultWidgetRegistry();
|
|
2322
|
-
const
|
|
2323
|
-
const
|
|
2324
|
-
|
|
2325
|
-
|
|
3168
|
+
const defaultSeconds = definition.timeRange?.defaultSeconds;
|
|
3169
|
+
const range = useMemo6(
|
|
3170
|
+
() => timeRange ?? initialTimeRange(defaultSeconds),
|
|
3171
|
+
[timeRange, defaultSeconds]
|
|
3172
|
+
);
|
|
3173
|
+
const histories = useWidgetHistories(widgets, seedHistory, registry, range);
|
|
3174
|
+
const { grid, background: bg, frame: canvasFrame } = definition.canvas;
|
|
2326
3175
|
const effectiveSizing = sizing ?? definition.canvas.sizing;
|
|
2327
|
-
|
|
3176
|
+
const effectiveDensity = density ?? definition.canvas.density ?? DEFAULT_CANVAS_DENSITY;
|
|
3177
|
+
const scale = densityScale(effectiveDensity);
|
|
3178
|
+
const { canvas } = definition;
|
|
3179
|
+
const shapeKey = useCallback2((width) => JSON.stringify(layoutShape(canvas, width, scale)), [canvas, scale]);
|
|
3180
|
+
const [boardRef, containerWidth] = useContainerWidth(shapeKey);
|
|
3181
|
+
const layout = useMemo6(
|
|
3182
|
+
() => effectiveLayout(definition, containerWidth, {
|
|
3183
|
+
scale,
|
|
3184
|
+
limitsOf: (w) => w.type === UNKNOWN_WIDGET_TYPE2 ? void 0 : spanLimitsOfLayout(registry.descriptor(w.type)?.layout)
|
|
3185
|
+
}),
|
|
3186
|
+
[definition, containerWidth, scale, registry]
|
|
3187
|
+
);
|
|
3188
|
+
const { breakpoint, columns } = layout;
|
|
3189
|
+
const lengths = gridCss(grid, columns);
|
|
3190
|
+
return /* @__PURE__ */ jsxs12(
|
|
2328
3191
|
"div",
|
|
2329
3192
|
{
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
const box = resolveWidgetBox(widget.layout, breakpoint);
|
|
2340
|
-
return /* @__PURE__ */ jsx16("div", { style: gridItemStyle(box, grid.columns), children: /* @__PURE__ */ jsx16(WidgetSubjectProvider, { label: widgetSubjectLabel(widget), children: /* @__PURE__ */ jsx16(
|
|
2341
|
-
ConnectedWidget,
|
|
3193
|
+
ref: boardRef,
|
|
3194
|
+
"data-density": effectiveDensity,
|
|
3195
|
+
"data-columns": columns,
|
|
3196
|
+
"data-time-paused": range.paused ? "" : void 0,
|
|
3197
|
+
style: { ...sizingStyle(effectiveSizing, bg), ...tokenStyle(scale) },
|
|
3198
|
+
children: [
|
|
3199
|
+
/* @__PURE__ */ jsx17(WidgetTokenStyles, {}),
|
|
3200
|
+
/* @__PURE__ */ jsx17(WidgetDensityProvider, { scale, children: /* @__PURE__ */ jsx17(WidgetThemeRootProvider, { root: themeRoot, children: /* @__PURE__ */ jsx17(WidgetTimeZoneProvider, { timeZone, children: /* @__PURE__ */ jsx17(WidgetTimeRangeProvider, { range, children: /* @__PURE__ */ jsx17(WidgetSelectProvider2, { select, children: /* @__PURE__ */ jsx17(WidgetCandidatesProvider2, { candidates, children: /* @__PURE__ */ jsx17("div", { style: { padding: lengths.padding }, children: /* @__PURE__ */ jsx17("div", { style: { containerType: "inline-size" }, children: /* @__PURE__ */ jsx17(
|
|
3201
|
+
"div",
|
|
2342
3202
|
{
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
3203
|
+
"data-grid": "",
|
|
3204
|
+
style: {
|
|
3205
|
+
display: "grid",
|
|
3206
|
+
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
|
3207
|
+
gridAutoRows: CELL,
|
|
3208
|
+
columnGap: lengths.gutterX,
|
|
3209
|
+
rowGap: lengths.gutterY,
|
|
3210
|
+
width: "100%",
|
|
3211
|
+
...{ "--dc-cell": lengths.cell }
|
|
3212
|
+
},
|
|
3213
|
+
children: /* @__PURE__ */ jsx17(
|
|
3214
|
+
PlacedWidgets,
|
|
3215
|
+
{
|
|
3216
|
+
widgets,
|
|
3217
|
+
breakpoint,
|
|
3218
|
+
columns,
|
|
3219
|
+
grid: layout.grid,
|
|
3220
|
+
lengths,
|
|
3221
|
+
render: (widget) => /* @__PURE__ */ jsx17(WidgetFrameStyleProvider, { frame: resolveFrame(canvasFrame, widgetFrame(widget)), children: /* @__PURE__ */ jsx17(WidgetSubjectProvider, { label: widgetSubjectLabel(widget), children: /* @__PURE__ */ jsx17(
|
|
3222
|
+
ConnectedWidget,
|
|
3223
|
+
{
|
|
3224
|
+
widget,
|
|
3225
|
+
hub,
|
|
3226
|
+
registry,
|
|
3227
|
+
actions,
|
|
3228
|
+
initialSamples: histories[widget.id]
|
|
3229
|
+
}
|
|
3230
|
+
) }) })
|
|
3231
|
+
}
|
|
3232
|
+
)
|
|
2348
3233
|
}
|
|
2349
|
-
) }) }
|
|
2350
|
-
|
|
3234
|
+
) }) }) }) }) }) }) }) })
|
|
3235
|
+
]
|
|
2351
3236
|
}
|
|
2352
|
-
)
|
|
3237
|
+
);
|
|
2353
3238
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
3239
|
+
var CELL = "var(--dc-cell)";
|
|
3240
|
+
function gridLengthCss(len) {
|
|
3241
|
+
if (typeof len === "number") return `calc(${len}px * var(--dc-scale, 1))`;
|
|
3242
|
+
return len === "space-0" ? "0px" : `var(--dc-${len})`;
|
|
3243
|
+
}
|
|
3244
|
+
function gridCss(grid, columns) {
|
|
3245
|
+
const g = grid.gutter;
|
|
3246
|
+
const gutterX = gridLengthCss(typeof g === "object" ? g.x : g);
|
|
3247
|
+
const gutterY = gridLengthCss(typeof g === "object" ? g.y : g);
|
|
3248
|
+
const p = grid.padding;
|
|
3249
|
+
const padding = typeof p === "object" ? [p.top, p.right, p.bottom, p.left].map(gridLengthCss).join(" ") : gridLengthCss(p);
|
|
3250
|
+
return { gutterX, gutterY, padding, cell: `calc((100cqw - ${columns - 1} * ${gutterX}) / ${columns})` };
|
|
3251
|
+
}
|
|
3252
|
+
function widgetFrame(widget) {
|
|
3253
|
+
return widget.type === UNKNOWN_WIDGET_TYPE2 ? void 0 : widget.frame;
|
|
3254
|
+
}
|
|
3255
|
+
function gridItemStyle(p, columns) {
|
|
3256
|
+
const col = Math.min(Math.max(0, p.col), columns - 1);
|
|
3257
|
+
const cols = Math.min(Math.max(1, p.cols), columns - col);
|
|
2357
3258
|
return {
|
|
2358
|
-
gridColumn: `${col + 1} / span ${
|
|
2359
|
-
gridRow: `${
|
|
2360
|
-
zIndex: box.z,
|
|
2361
|
-
transform: box.offset ? `translate(${box.offset.x}px, ${box.offset.y}px)` : void 0,
|
|
3259
|
+
gridColumn: `${col + 1} / span ${cols}`,
|
|
3260
|
+
gridRow: `${p.row + 1} / span ${p.rows}`,
|
|
2362
3261
|
// Let a grid item shrink below its content's intrinsic size so a wide chart/table
|
|
2363
3262
|
// fits its track instead of overflowing it.
|
|
2364
3263
|
minWidth: 0,
|
|
2365
3264
|
minHeight: 0
|
|
2366
3265
|
};
|
|
2367
3266
|
}
|
|
3267
|
+
var RELATIVE_Z = 100;
|
|
3268
|
+
function relativeBoxStyle(p, lengths) {
|
|
3269
|
+
const a = anchorFraction(p.anchor);
|
|
3270
|
+
const s = anchorFraction(p.self ?? p.anchor);
|
|
3271
|
+
const span = (n, gutter) => `calc(${n} * ${CELL} + ${n - 1} * ${gutter})`;
|
|
3272
|
+
const pitches = (n, gutter) => `${n} * (${CELL} + ${gutter})`;
|
|
3273
|
+
const fill = "fill" in p.size;
|
|
3274
|
+
return {
|
|
3275
|
+
position: "absolute",
|
|
3276
|
+
left: `calc(${a.fx * 100}% + ${pitches(p.offset?.x ?? 0, lengths.gutterX)})`,
|
|
3277
|
+
top: `calc(${a.fy * 100}% + ${pitches(p.offset?.y ?? 0, lengths.gutterY)})`,
|
|
3278
|
+
width: fill ? "100%" : span(p.size.cols, lengths.gutterX),
|
|
3279
|
+
height: fill ? "100%" : span(p.size.rows, lengths.gutterY),
|
|
3280
|
+
transform: `translate(${-s.fx * 100}%, ${-s.fy * 100}%)`,
|
|
3281
|
+
zIndex: RELATIVE_Z + (p.layer ?? 0),
|
|
3282
|
+
pointerEvents: "auto"
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
function PlacedWidgets({
|
|
3286
|
+
widgets,
|
|
3287
|
+
breakpoint,
|
|
3288
|
+
columns,
|
|
3289
|
+
grid,
|
|
3290
|
+
lengths,
|
|
3291
|
+
render
|
|
3292
|
+
}) {
|
|
3293
|
+
const placed = widgets.map((w) => ({ widget: w, placement: resolvePlacement(w.layout, breakpoint) }));
|
|
3294
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
3295
|
+
for (const { widget, placement } of placed) {
|
|
3296
|
+
if (placement.mode !== "relative") continue;
|
|
3297
|
+
dependents.set(placement.host, [...dependents.get(placement.host) ?? [], { widget, placement }]);
|
|
3298
|
+
}
|
|
3299
|
+
const relative = (host) => (dependents.get(host) ?? []).map(({ widget, placement }) => (
|
|
3300
|
+
// data-widget names the box a widget occupies, so a check over the rendered board (the
|
|
3301
|
+
// readability gate in apps/board-replay) can find it.
|
|
3302
|
+
/* @__PURE__ */ jsxs12("div", { "data-widget": widget.id, "data-host": host, style: relativeBoxStyle(placement, lengths), children: [
|
|
3303
|
+
render(widget),
|
|
3304
|
+
relative(widget.id)
|
|
3305
|
+
] }, widget.id)
|
|
3306
|
+
));
|
|
3307
|
+
return /* @__PURE__ */ jsx17(Fragment6, { children: placed.map(({ widget, placement }) => {
|
|
3308
|
+
if (placement.mode !== "grid") return null;
|
|
3309
|
+
const style = gridItemStyle(grid.get(widget.id) ?? placement, columns);
|
|
3310
|
+
return /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
3311
|
+
/* @__PURE__ */ jsx17("div", { "data-widget": widget.id, style, children: render(widget) }),
|
|
3312
|
+
dependents.has(widget.id) ? (
|
|
3313
|
+
// The host's area again, as a layer for its relative widgets to be positioned in.
|
|
3314
|
+
// It takes no pointer events itself, so the host underneath stays usable.
|
|
3315
|
+
/* @__PURE__ */ jsx17("div", { style: { ...style, position: "relative", pointerEvents: "none", zIndex: RELATIVE_Z }, children: relative(widget.id) })
|
|
3316
|
+
) : null
|
|
3317
|
+
] }, widget.id);
|
|
3318
|
+
}) });
|
|
3319
|
+
}
|
|
2368
3320
|
function sizingStyle(sizing, bg) {
|
|
2369
3321
|
const base = {
|
|
2370
3322
|
overflow: "auto",
|
|
@@ -2389,16 +3341,28 @@ function widgetSubjectLabel(widget) {
|
|
|
2389
3341
|
if (ds.kind === "anchor") return ds.anchor.targetToken;
|
|
2390
3342
|
return void 0;
|
|
2391
3343
|
}
|
|
2392
|
-
function
|
|
2393
|
-
const
|
|
2394
|
-
|
|
2395
|
-
);
|
|
2396
|
-
|
|
2397
|
-
const
|
|
2398
|
-
|
|
2399
|
-
|
|
3344
|
+
function useContainerWidth(keyOf) {
|
|
3345
|
+
const ref = useRef3(null);
|
|
3346
|
+
const [width, setWidth] = useState8(0);
|
|
3347
|
+
const keyOfRef = useRef3(keyOf);
|
|
3348
|
+
const measure = useCallback2(() => {
|
|
3349
|
+
const el = ref.current;
|
|
3350
|
+
if (!el) return;
|
|
3351
|
+
const next = el.clientWidth;
|
|
3352
|
+
setWidth((prev) => keyOfRef.current(prev) === keyOfRef.current(next) ? prev : next);
|
|
2400
3353
|
}, []);
|
|
2401
|
-
|
|
3354
|
+
useLayoutEffect(() => {
|
|
3355
|
+
keyOfRef.current = keyOf;
|
|
3356
|
+
measure();
|
|
3357
|
+
}, [keyOf, measure]);
|
|
3358
|
+
useLayoutEffect(() => {
|
|
3359
|
+
const el = ref.current;
|
|
3360
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
3361
|
+
const ro = new ResizeObserver(measure);
|
|
3362
|
+
ro.observe(el);
|
|
3363
|
+
return () => ro.disconnect();
|
|
3364
|
+
}, [measure]);
|
|
3365
|
+
return [ref, width];
|
|
2402
3366
|
}
|
|
2403
3367
|
function seedKey(widget, registry) {
|
|
2404
3368
|
if (widget.type === UNKNOWN_WIDGET_TYPE2 || registry.descriptor(widget.type)?.channel !== "measurement") {
|
|
@@ -2406,11 +3370,11 @@ function seedKey(widget, registry) {
|
|
|
2406
3370
|
}
|
|
2407
3371
|
return JSON.stringify(widget.datasource ?? null);
|
|
2408
3372
|
}
|
|
2409
|
-
function useWidgetHistories(widgets, enabled, registry) {
|
|
2410
|
-
const [seeds, setSeeds] =
|
|
3373
|
+
function useWidgetHistories(widgets, enabled, registry, range) {
|
|
3374
|
+
const [seeds, setSeeds] = useState8({});
|
|
2411
3375
|
const requested = useRef3({});
|
|
2412
3376
|
const mounted = useRef3(true);
|
|
2413
|
-
|
|
3377
|
+
useEffect5(() => {
|
|
2414
3378
|
mounted.current = true;
|
|
2415
3379
|
return () => {
|
|
2416
3380
|
mounted.current = false;
|
|
@@ -2418,31 +3382,36 @@ function useWidgetHistories(widgets, enabled, registry) {
|
|
|
2418
3382
|
}, []);
|
|
2419
3383
|
const wanted = widgets.flatMap((w) => {
|
|
2420
3384
|
const key = seedKey(w, registry);
|
|
2421
|
-
|
|
3385
|
+
if (key === void 0) return [];
|
|
3386
|
+
const descriptor = w.type === UNKNOWN_WIDGET_TYPE2 ? void 0 : registry.descriptor(w.type);
|
|
3387
|
+
const span = descriptor ? measurementSpan(descriptor, w, range.spanSeconds) ?? null : null;
|
|
3388
|
+
return [[w.id, key, span]];
|
|
2422
3389
|
});
|
|
2423
3390
|
const wantedKey = JSON.stringify(wanted);
|
|
2424
|
-
|
|
3391
|
+
useEffect5(() => {
|
|
2425
3392
|
if (!enabled) {
|
|
2426
3393
|
requested.current = {};
|
|
2427
3394
|
setSeeds({});
|
|
2428
3395
|
return;
|
|
2429
3396
|
}
|
|
2430
|
-
const
|
|
3397
|
+
const now = Date.now();
|
|
2431
3398
|
const byId = new Map(widgets.map((w) => [w.id, w]));
|
|
2432
3399
|
const next = {};
|
|
2433
|
-
for (const [id, key] of wanted) {
|
|
2434
|
-
|
|
2435
|
-
|
|
3400
|
+
for (const [id, key, span] of wanted) {
|
|
3401
|
+
const asked = `${key}|${span ?? ""}`;
|
|
3402
|
+
next[id] = asked;
|
|
3403
|
+
if (requested.current[id] === asked) continue;
|
|
2436
3404
|
const widget = byId.get(id);
|
|
2437
3405
|
if (!widget) continue;
|
|
3406
|
+
const historyWindow = span === null ? defaultHistoryWindow() : historyWindowFor(span, range.paused && range.pausedAt !== void 0 ? range.pausedAt : now);
|
|
2438
3407
|
void fetchWidgetHistory(widget, historyWindow).then((samples) => {
|
|
2439
|
-
if (!mounted.current || requested.current[id] !==
|
|
3408
|
+
if (!mounted.current || requested.current[id] !== asked) return;
|
|
2440
3409
|
setSeeds((prev) => ({ ...prev, [id]: { key, samples } }));
|
|
2441
3410
|
});
|
|
2442
3411
|
}
|
|
2443
3412
|
requested.current = next;
|
|
2444
3413
|
}, [wantedKey, enabled]);
|
|
2445
|
-
return
|
|
3414
|
+
return useMemo6(() => {
|
|
2446
3415
|
const out = {};
|
|
2447
3416
|
if (!enabled) return out;
|
|
2448
3417
|
for (const w of widgets) {
|
|
@@ -2453,6 +3422,125 @@ function useWidgetHistories(widgets, enabled, registry) {
|
|
|
2453
3422
|
}, [widgets, seeds, enabled, registry]);
|
|
2454
3423
|
}
|
|
2455
3424
|
|
|
3425
|
+
// src/time-range-bar.tsx
|
|
3426
|
+
import {
|
|
3427
|
+
DEFAULT_TIME_RANGE_SECONDS,
|
|
3428
|
+
TIME_RANGE_PRESETS,
|
|
3429
|
+
initialTimeRange as initialTimeRange2,
|
|
3430
|
+
isValidSpanSeconds as isValidSpanSeconds2,
|
|
3431
|
+
pauseRange,
|
|
3432
|
+
presetIdOf,
|
|
3433
|
+
resumeRange,
|
|
3434
|
+
spanLabel as spanLabel2,
|
|
3435
|
+
withSpan
|
|
3436
|
+
} from "@devicechain/dashboards";
|
|
3437
|
+
import { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo7, useRef as useRef4, useState as useState9 } from "react";
|
|
3438
|
+
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3439
|
+
function useBoardTimeRange(defaultSeconds, link = {}) {
|
|
3440
|
+
const [state, setState] = useState9(() => initialTimeRange2(defaultSeconds, link));
|
|
3441
|
+
const lastDefault = useRef4(defaultSeconds);
|
|
3442
|
+
useEffect6(() => {
|
|
3443
|
+
if (lastDefault.current === defaultSeconds) return;
|
|
3444
|
+
lastDefault.current = defaultSeconds;
|
|
3445
|
+
setState((s) => withSpan(s, isValidSpanSeconds2(defaultSeconds) ? defaultSeconds : DEFAULT_TIME_RANGE_SECONDS));
|
|
3446
|
+
}, [defaultSeconds]);
|
|
3447
|
+
const controls = useMemo7(
|
|
3448
|
+
() => ({
|
|
3449
|
+
setSpan: (spanSeconds) => setState((s) => withSpan(s, spanSeconds)),
|
|
3450
|
+
pause: () => setState((s) => pauseRange(s, Date.now())),
|
|
3451
|
+
resume: () => setState((s) => resumeRange(s))
|
|
3452
|
+
}),
|
|
3453
|
+
[]
|
|
3454
|
+
);
|
|
3455
|
+
return [state, controls];
|
|
3456
|
+
}
|
|
3457
|
+
var DEFAULT_TIME_RANGE_LABELS = {
|
|
3458
|
+
range: "Time range",
|
|
3459
|
+
pause: "Pause",
|
|
3460
|
+
resume: "Resume",
|
|
3461
|
+
paused: "Paused",
|
|
3462
|
+
pausedAt: (time) => `Paused at ${time}`,
|
|
3463
|
+
actionsLive: "commands still send"
|
|
3464
|
+
};
|
|
3465
|
+
var button = (active) => ({
|
|
3466
|
+
padding: "2px 8px",
|
|
3467
|
+
fontSize: 12,
|
|
3468
|
+
lineHeight: "18px",
|
|
3469
|
+
cursor: "pointer",
|
|
3470
|
+
color: active ? "hsl(var(--background))" : "hsl(var(--foreground))",
|
|
3471
|
+
background: active ? "hsl(var(--foreground))" : "transparent",
|
|
3472
|
+
border: "1px solid hsl(var(--border))",
|
|
3473
|
+
borderRadius: 4
|
|
3474
|
+
});
|
|
3475
|
+
function PausedIndicator({
|
|
3476
|
+
pausedAt,
|
|
3477
|
+
labels = DEFAULT_TIME_RANGE_LABELS
|
|
3478
|
+
}) {
|
|
3479
|
+
const time = pausedAt === void 0 ? void 0 : new Date(pausedAt).toLocaleTimeString();
|
|
3480
|
+
return /* @__PURE__ */ jsxs13(
|
|
3481
|
+
"span",
|
|
3482
|
+
{
|
|
3483
|
+
role: "status",
|
|
3484
|
+
"data-time-range-paused": "",
|
|
3485
|
+
title: time ? labels.pausedAt(time) : labels.paused,
|
|
3486
|
+
style: {
|
|
3487
|
+
display: "inline-flex",
|
|
3488
|
+
alignItems: "center",
|
|
3489
|
+
gap: 4,
|
|
3490
|
+
padding: "2px 8px",
|
|
3491
|
+
fontSize: 12,
|
|
3492
|
+
fontWeight: 700,
|
|
3493
|
+
letterSpacing: 0.4,
|
|
3494
|
+
textTransform: "uppercase",
|
|
3495
|
+
color: "hsl(var(--background))",
|
|
3496
|
+
background: "hsl(var(--foreground))",
|
|
3497
|
+
border: "2px solid hsl(var(--foreground))",
|
|
3498
|
+
borderRadius: 4
|
|
3499
|
+
},
|
|
3500
|
+
children: [
|
|
3501
|
+
/* @__PURE__ */ jsx18("span", { "aria-hidden": "true", children: "\u23F8" }),
|
|
3502
|
+
labels.paused,
|
|
3503
|
+
time ? /* @__PURE__ */ jsx18("span", { style: { fontWeight: 400, textTransform: "none" }, children: ` ${time}` }) : null,
|
|
3504
|
+
/* @__PURE__ */ jsx18("span", { "data-time-range-actions-live": "", style: { fontWeight: 400, textTransform: "none" }, children: `\xB7 ${labels.actionsLive}` })
|
|
3505
|
+
]
|
|
3506
|
+
}
|
|
3507
|
+
);
|
|
3508
|
+
}
|
|
3509
|
+
function TimeRangeBar({
|
|
3510
|
+
state,
|
|
3511
|
+
controls,
|
|
3512
|
+
labels = DEFAULT_TIME_RANGE_LABELS
|
|
3513
|
+
}) {
|
|
3514
|
+
const preset = presetIdOf(state.spanSeconds);
|
|
3515
|
+
const onPause = useCallback3(() => state.paused ? controls.resume() : controls.pause(), [state.paused, controls]);
|
|
3516
|
+
return /* @__PURE__ */ jsxs13(
|
|
3517
|
+
"div",
|
|
3518
|
+
{
|
|
3519
|
+
"data-time-range-bar": "",
|
|
3520
|
+
role: "group",
|
|
3521
|
+
"aria-label": labels.range,
|
|
3522
|
+
style: { display: "inline-flex", alignItems: "center", gap: 6, flexWrap: "wrap" },
|
|
3523
|
+
children: [
|
|
3524
|
+
preset === void 0 ? /* @__PURE__ */ jsx18("button", { type: "button", "aria-pressed": "true", "data-range": spanLabel2(state.spanSeconds), style: button(true), children: spanLabel2(state.spanSeconds) }) : null,
|
|
3525
|
+
TIME_RANGE_PRESETS.map((p) => /* @__PURE__ */ jsx18(
|
|
3526
|
+
"button",
|
|
3527
|
+
{
|
|
3528
|
+
type: "button",
|
|
3529
|
+
"aria-pressed": p.id === preset,
|
|
3530
|
+
"data-range": p.id,
|
|
3531
|
+
onClick: () => controls.setSpan(p.seconds),
|
|
3532
|
+
style: button(p.id === preset),
|
|
3533
|
+
children: p.id
|
|
3534
|
+
},
|
|
3535
|
+
p.id
|
|
3536
|
+
)),
|
|
3537
|
+
/* @__PURE__ */ jsx18("button", { type: "button", "aria-pressed": state.paused, "data-time-range-pause": "", onClick: onPause, style: button(state.paused), children: state.paused ? labels.resume : labels.pause }),
|
|
3538
|
+
state.paused ? /* @__PURE__ */ jsx18(PausedIndicator, { pausedAt: state.pausedAt, labels }) : null
|
|
3539
|
+
]
|
|
3540
|
+
}
|
|
3541
|
+
);
|
|
3542
|
+
}
|
|
3543
|
+
|
|
2456
3544
|
// src/registry.ts
|
|
2457
3545
|
var entries = Object.entries(builtinWidgetDescriptors);
|
|
2458
3546
|
var WIDGET_CHANNEL = Object.freeze(
|
|
@@ -2542,7 +3630,7 @@ import {
|
|
|
2542
3630
|
WidgetSelectProvider as WidgetSelectProvider3,
|
|
2543
3631
|
useWidgetSelect as useWidgetSelect4,
|
|
2544
3632
|
WidgetTimeZoneProvider as WidgetTimeZoneProvider2,
|
|
2545
|
-
useWidgetTimeZone as
|
|
3633
|
+
useWidgetTimeZone as useWidgetTimeZone6,
|
|
2546
3634
|
WidgetThemeRootProvider as WidgetThemeRootProvider2,
|
|
2547
3635
|
useWidgetThemeRoot,
|
|
2548
3636
|
WidgetCandidatesProvider as WidgetCandidatesProvider3,
|
|
@@ -2551,16 +3639,19 @@ import {
|
|
|
2551
3639
|
flashTextStyle as flashTextStyle2,
|
|
2552
3640
|
flashBackgroundStyle as flashBackgroundStyle2,
|
|
2553
3641
|
FlashValue as FlashValue3,
|
|
3642
|
+
fitValueFont as fitValueFont3,
|
|
2554
3643
|
useMeasurementStream as useMeasurementStream2,
|
|
2555
3644
|
useAlarmStream as useAlarmStream2,
|
|
2556
3645
|
useCommandStream as useCommandStream2,
|
|
2557
3646
|
useLocationStream as useLocationStream2,
|
|
2558
3647
|
useDatasourceAvailability as useDatasourceAvailability2,
|
|
2559
3648
|
useDashboardHub,
|
|
3649
|
+
useHubConnectionState,
|
|
3650
|
+
HubConnectionBadge,
|
|
2560
3651
|
useResolvedBindings,
|
|
2561
3652
|
useSlotCandidates,
|
|
2562
3653
|
useCandidates as useCandidates2,
|
|
2563
|
-
css as
|
|
3654
|
+
css as css12
|
|
2564
3655
|
} from "@devicechain/widget-sdk";
|
|
2565
3656
|
import {
|
|
2566
3657
|
EChart as EChart3,
|
|
@@ -2575,18 +3666,22 @@ export {
|
|
|
2575
3666
|
BUNDLED_BASEMAP_ATTRIBUTION,
|
|
2576
3667
|
CommandButton,
|
|
2577
3668
|
ConnectedWidget,
|
|
3669
|
+
DEFAULT_TIME_RANGE_LABELS,
|
|
2578
3670
|
DashboardRenderer,
|
|
2579
3671
|
EChart3 as EChart,
|
|
2580
3672
|
EntitySelector,
|
|
2581
3673
|
FlashValue3 as FlashValue,
|
|
2582
3674
|
Gauge,
|
|
3675
|
+
HubConnectionBadge,
|
|
2583
3676
|
Image,
|
|
2584
3677
|
Label,
|
|
2585
3678
|
LatestCard,
|
|
2586
3679
|
MapRuntimeProvider,
|
|
2587
3680
|
MapWidget,
|
|
3681
|
+
PausedIndicator,
|
|
2588
3682
|
Table,
|
|
2589
3683
|
TenantBasemapProvider,
|
|
3684
|
+
TimeRangeBar,
|
|
2590
3685
|
TimeSeriesChart,
|
|
2591
3686
|
WIDGET_BINDS_DATASOURCE,
|
|
2592
3687
|
WIDGET_CHANNEL,
|
|
@@ -2607,10 +3702,11 @@ export {
|
|
|
2607
3702
|
commandStatusColor,
|
|
2608
3703
|
commandStatusLabel,
|
|
2609
3704
|
createBuiltinWidgetRegistry,
|
|
2610
|
-
|
|
3705
|
+
css12 as css,
|
|
2611
3706
|
defaultValues,
|
|
2612
3707
|
describePosition,
|
|
2613
3708
|
enumOptionValues,
|
|
3709
|
+
fitValueFont3 as fitValueFont,
|
|
2614
3710
|
flashBackgroundStyle2 as flashBackgroundStyle,
|
|
2615
3711
|
flashTextStyle2 as flashTextStyle,
|
|
2616
3712
|
formatDegrees,
|
|
@@ -2633,12 +3729,14 @@ export {
|
|
|
2633
3729
|
severityRank,
|
|
2634
3730
|
stripUnknownOptions,
|
|
2635
3731
|
useAlarmStream2 as useAlarmStream,
|
|
3732
|
+
useBoardTimeRange,
|
|
2636
3733
|
useCandidates2 as useCandidates,
|
|
2637
3734
|
useChartTheme3 as useChartTheme,
|
|
2638
3735
|
useCommandStream2 as useCommandStream,
|
|
2639
3736
|
useDashboardHub,
|
|
2640
3737
|
useDatasourceAvailability2 as useDatasourceAvailability,
|
|
2641
3738
|
useFlashOnChange3 as useFlashOnChange,
|
|
3739
|
+
useHubConnectionState,
|
|
2642
3740
|
useLocationStream2 as useLocationStream,
|
|
2643
3741
|
useMapRuntime,
|
|
2644
3742
|
useMeasurementStream2 as useMeasurementStream,
|
|
@@ -2648,7 +3746,7 @@ export {
|
|
|
2648
3746
|
useWidgetCandidates2 as useWidgetCandidates,
|
|
2649
3747
|
useWidgetSelect4 as useWidgetSelect,
|
|
2650
3748
|
useWidgetThemeRoot,
|
|
2651
|
-
|
|
3749
|
+
useWidgetTimeZone6 as useWidgetTimeZone,
|
|
2652
3750
|
validateDefinitionOptions,
|
|
2653
3751
|
validateParams,
|
|
2654
3752
|
validateWidgetOptions,
|