@devicechain/widgets 0.19.0 → 0.20.0-rc.4
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 +13 -3
- package/dist/binding-policy.d.ts +4 -0
- package/dist/builtin-descriptors.d.ts +390 -0
- package/dist/builtin-registry.d.ts +378 -0
- package/dist/connected-widget.d.ts +4 -2
- package/dist/dashboard-renderer.d.ts +9 -4
- package/dist/default-registry.d.ts +2 -0
- package/dist/definition-options.d.ts +9 -5
- package/dist/index.d.ts +8 -10
- package/dist/index.js +1329 -1343
- package/dist/index.js.map +4 -4
- package/dist/options.d.ts +8 -302
- package/dist/registry.d.ts +6 -40
- package/dist/widget-containment.d.ts +9 -0
- package/dist/widget-host.d.ts +7 -0
- package/dist/widgets/alarm-count.d.ts +47 -2
- package/dist/widgets/alarm-table.d.ts +77 -2
- package/dist/widgets/command-button.d.ts +39 -2
- package/dist/widgets/entity-selector.d.ts +16 -1
- package/dist/widgets/gauge.d.ts +43 -1
- package/dist/widgets/image.d.ts +32 -2
- package/dist/widgets/label.d.ts +34 -2
- package/dist/widgets/latest-card.d.ts +42 -1
- package/dist/widgets/map.d.ts +22 -2
- package/dist/widgets/table.d.ts +31 -1
- package/dist/widgets/time-series-chart.d.ts +16 -1
- package/package.json +21 -20
- package/dist/chart-options.d.ts +0 -14
- package/dist/echart.d.ts +0 -8
- package/dist/flash.d.ts +0 -13
- package/dist/format.d.ts +0 -3
- package/dist/frame.d.ts +0 -25
- package/dist/hooks.d.ts +0 -54
- package/dist/theme.d.ts +0 -9
- package/dist/widget.d.ts +0 -16
package/dist/index.js
CHANGED
|
@@ -3,420 +3,477 @@ import {
|
|
|
3
3
|
activeBreakpoint,
|
|
4
4
|
defaultHistoryWindow,
|
|
5
5
|
fetchWidgetHistory,
|
|
6
|
-
resolveWidgetBox
|
|
6
|
+
resolveWidgetBox,
|
|
7
|
+
resolveWidgetDatasource,
|
|
8
|
+
UNKNOWN_WIDGET_TYPE as UNKNOWN_WIDGET_TYPE2
|
|
7
9
|
} from "@devicechain/dashboards";
|
|
8
|
-
import { useEffect as
|
|
10
|
+
import { useEffect as useEffect4, useMemo as useMemo5, useRef as useRef3, useState as useState6 } from "react";
|
|
9
11
|
|
|
10
|
-
// src/
|
|
11
|
-
import {
|
|
12
|
+
// src/connected-widget.tsx
|
|
13
|
+
import {
|
|
14
|
+
UNKNOWN_WIDGET_TYPE
|
|
15
|
+
} from "@devicechain/dashboards";
|
|
16
|
+
import { WidgetCandidatesProvider, WidgetSelectProvider } from "@devicechain/widget-sdk";
|
|
17
|
+
import { useMemo as useMemo4 } from "react";
|
|
12
18
|
|
|
13
|
-
// src/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
19
|
+
// src/binding-policy.ts
|
|
20
|
+
import { optNumber, optString } from "@devicechain/widget-sdk";
|
|
21
|
+
function numberValue(binding, options) {
|
|
22
|
+
if (!binding) return void 0;
|
|
23
|
+
let value;
|
|
24
|
+
if (typeof binding.const === "number") value = binding.const;
|
|
25
|
+
else if (binding.option !== void 0) value = optNumber(options, binding.option) ?? binding.default;
|
|
26
|
+
if (value === void 0) return void 0;
|
|
27
|
+
return binding.min !== void 0 ? Math.max(binding.min, value) : value;
|
|
28
|
+
}
|
|
29
|
+
function stringValue(binding, options) {
|
|
30
|
+
if (!binding || binding.option === void 0) return void 0;
|
|
31
|
+
return optString(options, binding.option) ?? binding.default;
|
|
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;
|
|
32
43
|
}
|
|
33
|
-
function
|
|
34
|
-
const
|
|
44
|
+
function alarmSubscription(descriptor, widget) {
|
|
45
|
+
const p = policy(descriptor);
|
|
35
46
|
return {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
40
52
|
};
|
|
41
53
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
label,
|
|
48
|
-
children
|
|
49
|
-
}) {
|
|
50
|
-
return /* @__PURE__ */ jsx(WidgetSubjectContext.Provider, { value: label, children });
|
|
51
|
-
}
|
|
52
|
-
var WidgetSelectContext = createContext(void 0);
|
|
53
|
-
function WidgetSelectProvider({
|
|
54
|
-
select,
|
|
55
|
-
children
|
|
56
|
-
}) {
|
|
57
|
-
return /* @__PURE__ */ jsx(WidgetSelectContext.Provider, { value: select, children });
|
|
58
|
-
}
|
|
59
|
-
function useWidgetSelect() {
|
|
60
|
-
return useContext(WidgetSelectContext);
|
|
61
|
-
}
|
|
62
|
-
var WidgetCandidatesContext = createContext(void 0);
|
|
63
|
-
function WidgetCandidatesProvider({
|
|
64
|
-
candidates,
|
|
65
|
-
children
|
|
66
|
-
}) {
|
|
67
|
-
return /* @__PURE__ */ jsx(WidgetCandidatesContext.Provider, { value: candidates, children });
|
|
68
|
-
}
|
|
69
|
-
function useWidgetCandidates() {
|
|
70
|
-
return useContext(WidgetCandidatesContext);
|
|
71
|
-
}
|
|
72
|
-
function WidgetFrame({ title, subtitle, children, bodyStyle }) {
|
|
73
|
-
const subject = useContext(WidgetSubjectContext);
|
|
74
|
-
const sub = subtitle ?? subject;
|
|
75
|
-
return /* @__PURE__ */ jsxs(
|
|
76
|
-
"div",
|
|
77
|
-
{
|
|
78
|
-
style: {
|
|
79
|
-
display: "flex",
|
|
80
|
-
flexDirection: "column",
|
|
81
|
-
width: "100%",
|
|
82
|
-
height: "100%",
|
|
83
|
-
boxSizing: "border-box",
|
|
84
|
-
background: css("card"),
|
|
85
|
-
color: css("card-foreground"),
|
|
86
|
-
border: `1px solid ${css("border")}`,
|
|
87
|
-
borderRadius: "var(--radius, 8px)",
|
|
88
|
-
overflow: "hidden"
|
|
89
|
-
},
|
|
90
|
-
children: [
|
|
91
|
-
title || sub ? /* @__PURE__ */ jsxs(
|
|
92
|
-
"div",
|
|
93
|
-
{
|
|
94
|
-
style: {
|
|
95
|
-
padding: "8px 12px",
|
|
96
|
-
borderBottom: `1px solid ${css("border")}`
|
|
97
|
-
},
|
|
98
|
-
children: [
|
|
99
|
-
title ? /* @__PURE__ */ jsx("div", { style: { fontSize: 12, fontWeight: 600, color: css("muted-foreground") }, children: title }) : null,
|
|
100
|
-
sub ? /* @__PURE__ */ jsx(
|
|
101
|
-
"div",
|
|
102
|
-
{
|
|
103
|
-
title: sub,
|
|
104
|
-
style: {
|
|
105
|
-
fontSize: 11,
|
|
106
|
-
fontWeight: 400,
|
|
107
|
-
color: css("muted-foreground"),
|
|
108
|
-
opacity: 0.75,
|
|
109
|
-
marginTop: title ? 2 : 0,
|
|
110
|
-
whiteSpace: "nowrap",
|
|
111
|
-
overflow: "hidden",
|
|
112
|
-
textOverflow: "ellipsis"
|
|
113
|
-
},
|
|
114
|
-
children: sub
|
|
115
|
-
}
|
|
116
|
-
) : null
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
) : null,
|
|
120
|
-
/* @__PURE__ */ jsx("div", { style: { flex: 1, minHeight: 0, ...bodyStyle }, children })
|
|
121
|
-
]
|
|
122
|
-
}
|
|
123
|
-
);
|
|
54
|
+
function commandSubscription(descriptor, widget) {
|
|
55
|
+
return {
|
|
56
|
+
datasource: widget.datasource,
|
|
57
|
+
pageSize: numberValue(policy(descriptor).pageSize, widget.options) ?? 1
|
|
58
|
+
};
|
|
124
59
|
}
|
|
125
60
|
|
|
126
|
-
// src/
|
|
61
|
+
// src/builtin-registry.ts
|
|
62
|
+
import { createWidgetRegistry } from "@devicechain/widget-sdk";
|
|
63
|
+
|
|
64
|
+
// src/widgets/alarm-count.tsx
|
|
65
|
+
import {
|
|
66
|
+
css,
|
|
67
|
+
defineWidget,
|
|
68
|
+
optString as optString2,
|
|
69
|
+
useElementSize,
|
|
70
|
+
WidgetFrame
|
|
71
|
+
} from "@devicechain/widget-sdk";
|
|
72
|
+
|
|
73
|
+
// src/builtin-descriptors.ts
|
|
127
74
|
import {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
resolveContextBindings,
|
|
131
|
-
resolveSlotCandidates
|
|
75
|
+
createWidgetCatalog,
|
|
76
|
+
defineWidgetDescriptor
|
|
132
77
|
} from "@devicechain/dashboards";
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const [resolved, setResolved] = useState(() => bindingsWithoutScopedSlots(definition, overlaid));
|
|
138
|
-
const genRef = useRef(0);
|
|
139
|
-
useEffect(() => {
|
|
140
|
-
if (!scoped) {
|
|
141
|
-
setResolved((prev) => sameBindings(prev, overlaid) ? prev : overlaid);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const gen = ++genRef.current;
|
|
145
|
-
let live = true;
|
|
146
|
-
resolveContextBindings(definition, base, selection, resolver).then((next) => {
|
|
147
|
-
if (!live || gen !== genRef.current) return;
|
|
148
|
-
setResolved((prev) => sameBindings(prev, next) ? prev : next);
|
|
149
|
-
}).catch(() => {
|
|
150
|
-
});
|
|
151
|
-
return () => {
|
|
152
|
-
live = false;
|
|
153
|
-
};
|
|
154
|
-
}, [scoped, definition, base, selection, resolver, overlaid]);
|
|
155
|
-
return scoped ? resolved : overlaid;
|
|
156
|
-
}
|
|
157
|
-
function sameBindings(a, b) {
|
|
158
|
-
const ak = Object.keys(a).sort();
|
|
159
|
-
const bk = Object.keys(b).sort();
|
|
160
|
-
if (ak.length !== bk.length) return false;
|
|
161
|
-
for (let i = 0; i < ak.length; i += 1) {
|
|
162
|
-
if (ak[i] !== bk[i]) return false;
|
|
163
|
-
if (JSON.stringify(a[ak[i]]) !== JSON.stringify(b[bk[i]])) return false;
|
|
164
|
-
}
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
function useSlotCandidates(definition, bindings, resolver, lister) {
|
|
168
|
-
const bindingsKey = JSON.stringify(bindings);
|
|
169
|
-
return useMemo(
|
|
170
|
-
() => {
|
|
171
|
-
if (!lister) return void 0;
|
|
172
|
-
return (slot) => resolveSlotCandidates(definition, slot, bindings, resolver, lister);
|
|
173
|
-
},
|
|
174
|
-
// bindings read via bindingsKey (value identity), not reference.
|
|
175
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
176
|
-
[definition, bindingsKey, resolver, lister]
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
function useCandidates(slot) {
|
|
180
|
-
const provider = useWidgetCandidates();
|
|
181
|
-
const wired = !!provider;
|
|
182
|
-
const [state, setState] = useState(() => ({
|
|
183
|
-
candidates: [],
|
|
184
|
-
loading: Boolean(provider && slot),
|
|
185
|
-
error: null
|
|
186
|
-
}));
|
|
187
|
-
const genRef = useRef(0);
|
|
188
|
-
useEffect(() => {
|
|
189
|
-
if (!provider || !slot) {
|
|
190
|
-
setState({ candidates: [], loading: false, error: null });
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const gen = ++genRef.current;
|
|
194
|
-
let live = true;
|
|
195
|
-
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
196
|
-
provider(slot).then((candidates) => {
|
|
197
|
-
if (live && gen === genRef.current) setState({ candidates, loading: false, error: null });
|
|
198
|
-
}).catch((error) => {
|
|
199
|
-
if (live && gen === genRef.current) setState({ candidates: [], loading: false, error });
|
|
200
|
-
});
|
|
201
|
-
return () => {
|
|
202
|
-
live = false;
|
|
203
|
-
};
|
|
204
|
-
}, [provider, slot]);
|
|
205
|
-
return { ...state, wired };
|
|
206
|
-
}
|
|
207
|
-
var EMPTY = { latest: {}, samples: [], error: null };
|
|
208
|
-
function latestOf(samples) {
|
|
209
|
-
const latest = {};
|
|
210
|
-
for (const s of samples) latest[s.name] = s;
|
|
211
|
-
return latest;
|
|
212
|
-
}
|
|
213
|
-
function useMeasurementStream(hub, datasource, options = {}) {
|
|
214
|
-
const windowSize = options.window ?? 300;
|
|
215
|
-
const initialSamples = options.initialSamples;
|
|
216
|
-
const [live, setLive] = useState(EMPTY);
|
|
217
|
-
const key = datasource ? JSON.stringify(datasource) : null;
|
|
218
|
-
useEffect(() => {
|
|
219
|
-
setLive(EMPTY);
|
|
220
|
-
if (!datasource) return;
|
|
221
|
-
return hub.subscribeWidget(datasource, {
|
|
222
|
-
next: (sample) => setLive((prev) => {
|
|
223
|
-
const samples = prev.samples.concat(sample);
|
|
224
|
-
if (samples.length > windowSize) samples.splice(0, samples.length - windowSize);
|
|
225
|
-
return { latest: { ...prev.latest, [sample.name]: sample }, samples, error: null };
|
|
226
|
-
}),
|
|
227
|
-
error: (err) => setLive((prev) => ({ ...prev, error: err }))
|
|
228
|
-
});
|
|
229
|
-
}, [hub, key, windowSize]);
|
|
230
|
-
return useMemo(() => {
|
|
231
|
-
if (!initialSamples || initialSamples.length === 0) return live;
|
|
232
|
-
const merged = initialSamples.concat(live.samples);
|
|
233
|
-
const samples = merged.length > windowSize ? merged.slice(merged.length - windowSize) : merged;
|
|
234
|
-
return {
|
|
235
|
-
latest: { ...latestOf(initialSamples), ...live.latest },
|
|
236
|
-
samples,
|
|
237
|
-
error: live.error
|
|
238
|
-
};
|
|
239
|
-
}, [initialSamples, live, windowSize]);
|
|
240
|
-
}
|
|
241
|
-
var EMPTY_ALARMS = { alarms: [], total: 0, loading: true, error: null };
|
|
242
|
-
function useAlarmStream(hub, subscription) {
|
|
243
|
-
const [state, setState] = useState(EMPTY_ALARMS);
|
|
244
|
-
const key = JSON.stringify(subscription);
|
|
245
|
-
useEffect(() => {
|
|
246
|
-
setState(EMPTY_ALARMS);
|
|
247
|
-
return hub.subscribeAlarms(subscription, {
|
|
248
|
-
next: (snapshot) => setState({ alarms: snapshot.alarms, total: snapshot.total, loading: false, error: null }),
|
|
249
|
-
error: (err) => setState((prev) => ({ ...prev, loading: false, error: err }))
|
|
250
|
-
});
|
|
251
|
-
}, [hub, key]);
|
|
252
|
-
return state;
|
|
253
|
-
}
|
|
254
|
-
var EMPTY_COMMANDS = {
|
|
255
|
-
deviceToken: null,
|
|
256
|
-
commands: [],
|
|
257
|
-
total: 0,
|
|
258
|
-
loading: true,
|
|
259
|
-
error: null
|
|
78
|
+
var TITLE = {
|
|
79
|
+
kind: "string",
|
|
80
|
+
labelId: "widgetLabelTitle",
|
|
81
|
+
doc: "Heading shown in the widget frame. Blank hides the frame heading."
|
|
260
82
|
};
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
setState(EMPTY_COMMANDS);
|
|
266
|
-
return hub.subscribeCommands(subscription, {
|
|
267
|
-
next: (snapshot) => setState({
|
|
268
|
-
deviceToken: snapshot.deviceToken,
|
|
269
|
-
commands: snapshot.commands,
|
|
270
|
-
total: snapshot.total,
|
|
271
|
-
loading: false,
|
|
272
|
-
error: null
|
|
273
|
-
}),
|
|
274
|
-
error: (err) => setState((prev) => ({ ...prev, loading: false, error: err }))
|
|
275
|
-
});
|
|
276
|
-
}, [hub, key]);
|
|
277
|
-
return state;
|
|
278
|
-
}
|
|
279
|
-
var EMPTY_LOCATIONS = {
|
|
280
|
-
locations: [],
|
|
281
|
-
deviceTokens: [],
|
|
282
|
-
forbidden: false,
|
|
283
|
-
loading: true,
|
|
284
|
-
error: null
|
|
83
|
+
var TITLE_OR_MEASUREMENT = {
|
|
84
|
+
kind: "string",
|
|
85
|
+
labelId: "widgetLabelTitle",
|
|
86
|
+
doc: "Heading shown in the widget frame. Defaults to the displayed measurement name."
|
|
285
87
|
};
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
88
|
+
var PRECISION = {
|
|
89
|
+
kind: "number",
|
|
90
|
+
integer: true,
|
|
91
|
+
min: 0,
|
|
92
|
+
max: 100,
|
|
93
|
+
labelId: "widgetLabelPrecision",
|
|
94
|
+
helpId: "widgetHintPrecision",
|
|
95
|
+
doc: "Decimal places to round displayed values to. Unset shows the raw value."
|
|
96
|
+
};
|
|
97
|
+
var FLASH_ON_CHANGE = {
|
|
98
|
+
kind: "boolean",
|
|
99
|
+
labelId: "widgetFlashOnChange",
|
|
100
|
+
helpId: "widgetFlashOnChangeHint",
|
|
101
|
+
doc: "Briefly tint a value green on a rise and red on a fall when it changes."
|
|
102
|
+
};
|
|
103
|
+
var WINDOW = {
|
|
104
|
+
kind: "number",
|
|
105
|
+
integer: true,
|
|
106
|
+
min: 1,
|
|
107
|
+
labelId: "widgetLabelWindow",
|
|
108
|
+
helpId: "widgetHintWindow",
|
|
109
|
+
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
|
+
};
|
|
111
|
+
var UNIT = {
|
|
112
|
+
kind: "string",
|
|
113
|
+
labelId: "widgetLabelUnit",
|
|
114
|
+
doc: "Unit suffix shown beside the value."
|
|
115
|
+
};
|
|
116
|
+
var ALARM_STATE = {
|
|
117
|
+
kind: "enum",
|
|
118
|
+
values: ["ACTIVE", "CLEARED"],
|
|
119
|
+
labelId: "widgetLabelState",
|
|
120
|
+
helpId: "widgetStateHint",
|
|
121
|
+
valueLabelIds: { ACTIVE: "widgetAlarmStateActive", CLEARED: "widgetAlarmStateCleared" },
|
|
122
|
+
placeholderId: "widgetPlaceholderAny",
|
|
123
|
+
doc: "Show only alarms in this state. Unset shows every state."
|
|
124
|
+
};
|
|
125
|
+
var ALARM_SEVERITY = {
|
|
126
|
+
kind: "enum",
|
|
127
|
+
values: ["CRITICAL", "MAJOR", "MINOR", "WARNING", "INDETERMINATE"],
|
|
128
|
+
labelId: "widgetLabelSeverity",
|
|
129
|
+
helpId: "widgetSeverityHint",
|
|
130
|
+
valueLabelIds: { CRITICAL: "widgetAlarmSeverityCritical", MAJOR: "widgetAlarmSeverityMajor", MINOR: "widgetAlarmSeverityMinor", WARNING: "widgetAlarmSeverityWarning", INDETERMINATE: "widgetAlarmSeverityIndeterminate" },
|
|
131
|
+
placeholderId: "widgetPlaceholderAny",
|
|
132
|
+
doc: "Show only alarms at this severity. Unset shows every severity."
|
|
133
|
+
};
|
|
134
|
+
var ALARM_ACKNOWLEDGED = {
|
|
135
|
+
kind: "enum",
|
|
136
|
+
values: ["true", "false"],
|
|
137
|
+
labelId: "widgetLabelAcknowledged",
|
|
138
|
+
helpId: "widgetAcknowledgedHint",
|
|
139
|
+
valueLabelIds: { true: "widgetAlarmAckAcknowledged", false: "widgetAlarmAckUnacknowledged" },
|
|
140
|
+
placeholderId: "widgetPlaceholderAny",
|
|
141
|
+
doc: "Show only acknowledged ('true') or unacknowledged ('false') alarms. Unset shows both."
|
|
142
|
+
};
|
|
143
|
+
var SELECTION_TARGET = {
|
|
144
|
+
kind: "string",
|
|
145
|
+
labelId: "widgetLabelTargetSlot",
|
|
146
|
+
helpId: "widgetTargetSlotHint",
|
|
147
|
+
control: "slot-target",
|
|
148
|
+
doc: "Name of the slot this widget re-points when the viewer picks an entity."
|
|
149
|
+
};
|
|
150
|
+
var VERSION = "1.0.0";
|
|
151
|
+
var ALL_SELECTORS = ["device", "anchor", "slot"];
|
|
152
|
+
var NO_DATASOURCE = { mode: "none" };
|
|
153
|
+
var ALARM_FILTERS = {
|
|
154
|
+
state: { option: "state" },
|
|
155
|
+
severity: { option: "severity" },
|
|
156
|
+
acknowledged: { option: "acknowledged" }
|
|
157
|
+
};
|
|
158
|
+
var builtinWidgetDescriptors = {
|
|
159
|
+
"timeseries-chart": defineWidgetDescriptor({
|
|
160
|
+
id: "timeseries-chart",
|
|
161
|
+
version: VERSION,
|
|
162
|
+
sdkApiVersion: 1,
|
|
163
|
+
optionsVersion: 1,
|
|
164
|
+
display: { title: "Timeseries chart", category: "Telemetry", titleId: "widgetTypeTimeseriesChart", categoryId: "widgetCategoryTelemetry" },
|
|
165
|
+
channel: "measurement",
|
|
166
|
+
datasource: { mode: "required", selectors: ALL_SELECTORS },
|
|
167
|
+
binding: { window: { option: "window", min: 1 } },
|
|
168
|
+
options: {
|
|
169
|
+
title: TITLE,
|
|
170
|
+
window: WINDOW
|
|
171
|
+
},
|
|
172
|
+
defaults: { title: "Timeseries chart" },
|
|
173
|
+
interactions: {}
|
|
174
|
+
}),
|
|
175
|
+
"latest-card": defineWidgetDescriptor({
|
|
176
|
+
id: "latest-card",
|
|
177
|
+
version: VERSION,
|
|
178
|
+
sdkApiVersion: 1,
|
|
179
|
+
optionsVersion: 1,
|
|
180
|
+
display: { title: "Latest card", category: "Telemetry", titleId: "widgetTypeLatestCard", categoryId: "widgetCategoryTelemetry" },
|
|
181
|
+
channel: "measurement",
|
|
182
|
+
datasource: { mode: "required", selectors: ALL_SELECTORS },
|
|
183
|
+
binding: { window: { option: "window", min: 1 } },
|
|
184
|
+
options: {
|
|
185
|
+
title: TITLE_OR_MEASUREMENT,
|
|
186
|
+
measurement: {
|
|
187
|
+
kind: "string",
|
|
188
|
+
labelId: "widgetLabelMeasurement",
|
|
189
|
+
helpId: "widgetHintMeasurement",
|
|
190
|
+
doc: "Which measurement to display. Defaults to the first the datasource selects."
|
|
191
|
+
},
|
|
192
|
+
unit: UNIT,
|
|
193
|
+
precision: PRECISION,
|
|
194
|
+
flashOnChange: FLASH_ON_CHANGE,
|
|
195
|
+
window: WINDOW
|
|
196
|
+
},
|
|
197
|
+
defaults: { title: "Latest card" },
|
|
198
|
+
interactions: {}
|
|
199
|
+
}),
|
|
200
|
+
gauge: defineWidgetDescriptor({
|
|
201
|
+
id: "gauge",
|
|
202
|
+
version: VERSION,
|
|
203
|
+
sdkApiVersion: 1,
|
|
204
|
+
optionsVersion: 1,
|
|
205
|
+
display: { title: "Gauge", category: "Telemetry", titleId: "widgetTypeGauge", categoryId: "widgetCategoryTelemetry" },
|
|
206
|
+
channel: "measurement",
|
|
207
|
+
datasource: { mode: "required", selectors: ALL_SELECTORS },
|
|
208
|
+
binding: { window: { option: "window", min: 1 } },
|
|
209
|
+
options: {
|
|
210
|
+
title: TITLE_OR_MEASUREMENT,
|
|
211
|
+
measurement: {
|
|
212
|
+
kind: "string",
|
|
213
|
+
labelId: "widgetLabelMeasurement",
|
|
214
|
+
helpId: "widgetHintMeasurement",
|
|
215
|
+
doc: "Which measurement to display. Defaults to the first the datasource selects."
|
|
216
|
+
},
|
|
217
|
+
min: {
|
|
218
|
+
kind: "number",
|
|
219
|
+
labelId: "widgetLabelMin",
|
|
220
|
+
doc: "Low end of the gauge scale. Default 0."
|
|
221
|
+
},
|
|
222
|
+
max: {
|
|
223
|
+
kind: "number",
|
|
224
|
+
labelId: "widgetLabelMax",
|
|
225
|
+
doc: "High end of the gauge scale. Default 100."
|
|
226
|
+
},
|
|
227
|
+
unit: UNIT,
|
|
228
|
+
flashOnChange: FLASH_ON_CHANGE,
|
|
229
|
+
window: WINDOW
|
|
230
|
+
},
|
|
231
|
+
constraints: [{ kind: "greater-than", key: "max", than: "min" }],
|
|
232
|
+
defaults: { title: "Gauge" },
|
|
233
|
+
interactions: {}
|
|
234
|
+
}),
|
|
235
|
+
table: defineWidgetDescriptor({
|
|
236
|
+
id: "table",
|
|
237
|
+
version: VERSION,
|
|
238
|
+
sdkApiVersion: 1,
|
|
239
|
+
optionsVersion: 1,
|
|
240
|
+
display: { title: "Table", category: "Telemetry", titleId: "widgetTypeTable", categoryId: "widgetCategoryTelemetry" },
|
|
241
|
+
channel: "measurement",
|
|
242
|
+
datasource: { mode: "required", selectors: ALL_SELECTORS },
|
|
243
|
+
binding: { window: { option: "window", min: 1 } },
|
|
244
|
+
options: {
|
|
245
|
+
title: TITLE,
|
|
246
|
+
precision: PRECISION,
|
|
247
|
+
flashOnChange: FLASH_ON_CHANGE,
|
|
248
|
+
window: WINDOW
|
|
249
|
+
},
|
|
250
|
+
defaults: { title: "Table" },
|
|
251
|
+
interactions: {}
|
|
252
|
+
}),
|
|
253
|
+
// label and image are static: the `none` channel, no datasource, no subscription and no
|
|
254
|
+
// history seed.
|
|
255
|
+
label: defineWidgetDescriptor({
|
|
256
|
+
id: "label",
|
|
257
|
+
version: VERSION,
|
|
258
|
+
sdkApiVersion: 1,
|
|
259
|
+
optionsVersion: 1,
|
|
260
|
+
display: { title: "Label", category: "Static", titleId: "widgetTypeLabel", categoryId: "widgetCategoryStatic" },
|
|
261
|
+
channel: "none",
|
|
262
|
+
datasource: NO_DATASOURCE,
|
|
263
|
+
binding: {},
|
|
264
|
+
options: {
|
|
265
|
+
title: TITLE,
|
|
266
|
+
text: {
|
|
267
|
+
kind: "string",
|
|
268
|
+
required: true,
|
|
269
|
+
labelId: "widgetLabelText",
|
|
270
|
+
doc: "The text to show. Rendered as plain text \u2014 markup is never interpreted."
|
|
271
|
+
},
|
|
272
|
+
align: {
|
|
273
|
+
kind: "enum",
|
|
274
|
+
values: ["left", "center", "right"],
|
|
275
|
+
labelId: "widgetLabelAlign",
|
|
276
|
+
valueLabelIds: { left: "widgetAlignLeft", center: "widgetAlignCenter", right: "widgetAlignRight" },
|
|
277
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
278
|
+
doc: "Horizontal alignment of the text. Default center."
|
|
279
|
+
},
|
|
280
|
+
fontSize: {
|
|
281
|
+
kind: "number",
|
|
282
|
+
min: 1,
|
|
283
|
+
labelId: "widgetLabelFontSize",
|
|
284
|
+
helpId: "widgetHintFontSize",
|
|
285
|
+
doc: "Text size in pixels. Default 16."
|
|
322
286
|
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
287
|
+
},
|
|
288
|
+
defaults: { text: "New label" },
|
|
289
|
+
interactions: {}
|
|
290
|
+
}),
|
|
291
|
+
image: defineWidgetDescriptor({
|
|
292
|
+
id: "image",
|
|
293
|
+
version: VERSION,
|
|
294
|
+
sdkApiVersion: 1,
|
|
295
|
+
optionsVersion: 1,
|
|
296
|
+
display: { title: "Image", category: "Static", titleId: "widgetTypeImage", categoryId: "widgetCategoryStatic" },
|
|
297
|
+
channel: "none",
|
|
298
|
+
datasource: NO_DATASOURCE,
|
|
299
|
+
binding: {},
|
|
300
|
+
options: {
|
|
301
|
+
title: TITLE,
|
|
302
|
+
url: {
|
|
303
|
+
kind: "string",
|
|
304
|
+
required: true,
|
|
305
|
+
labelId: "widgetLabelImageUrl",
|
|
306
|
+
doc: "Source URL of the image."
|
|
307
|
+
},
|
|
308
|
+
alt: {
|
|
309
|
+
kind: "string",
|
|
310
|
+
labelId: "widgetLabelAltText",
|
|
311
|
+
doc: "Alternative text describing the image for assistive technology."
|
|
312
|
+
},
|
|
313
|
+
fit: {
|
|
314
|
+
kind: "enum",
|
|
315
|
+
values: ["contain", "cover", "fill"],
|
|
316
|
+
labelId: "widgetLabelFit",
|
|
317
|
+
valueLabelIds: { contain: "widgetFitContain", cover: "widgetFitCover", fill: "widgetFitFill" },
|
|
318
|
+
placeholderId: "widgetPlaceholderDefault",
|
|
319
|
+
doc: "How the image fills its box. Default contain."
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
defaults: { title: "Image" },
|
|
323
|
+
interactions: {}
|
|
324
|
+
}),
|
|
325
|
+
"alarm-table": defineWidgetDescriptor({
|
|
326
|
+
id: "alarm-table",
|
|
327
|
+
version: VERSION,
|
|
328
|
+
sdkApiVersion: 1,
|
|
329
|
+
optionsVersion: 1,
|
|
330
|
+
display: { title: "Alarm table", category: "Alarms", titleId: "widgetTypeAlarmTable", categoryId: "widgetCategoryAlarms" },
|
|
331
|
+
channel: "alarm",
|
|
332
|
+
datasource: { mode: "optional", selectors: ALL_SELECTORS },
|
|
333
|
+
binding: { pageSize: { option: "maxRows", default: 50, min: 1 }, ...ALARM_FILTERS },
|
|
334
|
+
options: {
|
|
335
|
+
title: TITLE,
|
|
336
|
+
state: ALARM_STATE,
|
|
337
|
+
severity: ALARM_SEVERITY,
|
|
338
|
+
acknowledged: ALARM_ACKNOWLEDGED,
|
|
339
|
+
maxRows: {
|
|
340
|
+
kind: "number",
|
|
341
|
+
integer: true,
|
|
342
|
+
min: 1,
|
|
343
|
+
labelId: "widgetLabelMaxRows",
|
|
344
|
+
helpId: "widgetMaxRowsAlarmHint",
|
|
345
|
+
doc: "Alarms loaded into the table. Default 50."
|
|
346
|
+
},
|
|
347
|
+
precision: PRECISION,
|
|
348
|
+
flashOnChange: FLASH_ON_CHANGE,
|
|
349
|
+
selectionTarget: SELECTION_TARGET
|
|
350
|
+
},
|
|
351
|
+
defaults: { title: "Alarm table", state: "ACTIVE" },
|
|
352
|
+
interactions: { alarmActions: true, selection: true }
|
|
353
|
+
}),
|
|
354
|
+
// The count shows the SERVER total and reads a page only to sample the worst loaded
|
|
355
|
+
// severity, so its page size is a constant rather than an authorable option.
|
|
356
|
+
"alarm-count": defineWidgetDescriptor({
|
|
357
|
+
id: "alarm-count",
|
|
358
|
+
version: VERSION,
|
|
359
|
+
sdkApiVersion: 1,
|
|
360
|
+
optionsVersion: 1,
|
|
361
|
+
display: { title: "Alarm count", category: "Alarms", titleId: "widgetTypeAlarmCount", categoryId: "widgetCategoryAlarms" },
|
|
362
|
+
channel: "alarm",
|
|
363
|
+
datasource: { mode: "optional", selectors: ALL_SELECTORS },
|
|
364
|
+
binding: { pageSize: { const: 50 }, ...ALARM_FILTERS },
|
|
365
|
+
// No maxRows: alarmSubscription() forces the default page size for a count, because the
|
|
366
|
+
// number it shows is the SERVER total and the page is read only to sample the worst
|
|
367
|
+
// loaded severity. An authored maxRows here would be read by nothing.
|
|
368
|
+
options: {
|
|
369
|
+
title: TITLE,
|
|
370
|
+
state: ALARM_STATE,
|
|
371
|
+
severity: ALARM_SEVERITY,
|
|
372
|
+
acknowledged: ALARM_ACKNOWLEDGED
|
|
373
|
+
},
|
|
374
|
+
defaults: { title: "Alarm count", state: "ACTIVE" },
|
|
375
|
+
interactions: {}
|
|
376
|
+
}),
|
|
377
|
+
"command-button": defineWidgetDescriptor({
|
|
378
|
+
id: "command-button",
|
|
379
|
+
version: VERSION,
|
|
380
|
+
sdkApiVersion: 1,
|
|
381
|
+
optionsVersion: 1,
|
|
382
|
+
display: { title: "Command button", category: "Control", titleId: "widgetTypeCommandButton", categoryId: "widgetCategoryControl" },
|
|
383
|
+
channel: "control",
|
|
384
|
+
datasource: { mode: "required", selectors: ["device", "slot"] },
|
|
385
|
+
binding: { pageSize: { option: "maxRows", default: 20, min: 1 } },
|
|
386
|
+
options: {
|
|
387
|
+
title: TITLE,
|
|
388
|
+
commandName: {
|
|
389
|
+
kind: "string",
|
|
390
|
+
required: true,
|
|
391
|
+
labelId: "widgetCommandHeading",
|
|
392
|
+
helpId: "widgetCommandFieldHint",
|
|
393
|
+
control: "command-vocabulary",
|
|
394
|
+
doc: "Command key issued to the target device. Without it the widget cannot send."
|
|
395
|
+
},
|
|
396
|
+
commandLabel: {
|
|
397
|
+
kind: "string",
|
|
398
|
+
labelId: "widgetCommandHeading",
|
|
399
|
+
helpId: "widgetCommandFieldHint",
|
|
400
|
+
control: "command-vocabulary",
|
|
401
|
+
doc: "Text on the send button. Defaults to the command key."
|
|
402
|
+
},
|
|
403
|
+
parameterSchema: {
|
|
404
|
+
kind: "string",
|
|
405
|
+
format: "json",
|
|
406
|
+
labelId: "widgetCommandHeading",
|
|
407
|
+
helpId: "widgetCommandFieldHint",
|
|
408
|
+
control: "command-vocabulary",
|
|
409
|
+
doc: "The command's parameter descriptors, baked in as JSON at author time. Drives the typed form."
|
|
410
|
+
},
|
|
411
|
+
maxRows: {
|
|
412
|
+
kind: "number",
|
|
413
|
+
integer: true,
|
|
414
|
+
min: 1,
|
|
415
|
+
labelId: "widgetLabelMaxRows",
|
|
416
|
+
helpId: "widgetMaxRowsCommandHint",
|
|
417
|
+
doc: "Recent commands shown in the history. Default 20."
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
defaults: { title: "Command button" },
|
|
421
|
+
interactions: { commandSend: true }
|
|
422
|
+
}),
|
|
423
|
+
"entity-selector": defineWidgetDescriptor({
|
|
424
|
+
id: "entity-selector",
|
|
425
|
+
version: VERSION,
|
|
426
|
+
sdkApiVersion: 1,
|
|
427
|
+
optionsVersion: 1,
|
|
428
|
+
display: { title: "Entity selector", category: "Navigation", titleId: "widgetTypeEntitySelector", categoryId: "widgetCategoryNavigation" },
|
|
429
|
+
channel: "selection",
|
|
430
|
+
datasource: NO_DATASOURCE,
|
|
431
|
+
binding: {},
|
|
432
|
+
options: {
|
|
433
|
+
title: TITLE,
|
|
434
|
+
selectionTarget: { ...SELECTION_TARGET, required: true }
|
|
435
|
+
},
|
|
436
|
+
defaults: { title: "Entity selector" },
|
|
437
|
+
interactions: { selection: true, candidates: true }
|
|
438
|
+
}),
|
|
439
|
+
map: defineWidgetDescriptor({
|
|
440
|
+
id: "map",
|
|
441
|
+
version: VERSION,
|
|
442
|
+
sdkApiVersion: 1,
|
|
443
|
+
optionsVersion: 1,
|
|
444
|
+
display: { title: "Map", category: "Location", titleId: "widgetTypeMap", categoryId: "widgetCategoryLocation" },
|
|
445
|
+
channel: "location",
|
|
446
|
+
datasource: { mode: "required", selectors: ALL_SELECTORS },
|
|
447
|
+
binding: {},
|
|
448
|
+
// 🔴 tileUrl is NOT `required`, and that is the decision rather than an oversight.
|
|
449
|
+
// `required` means "absent leaves the widget with nothing to show", and a map without
|
|
450
|
+
// a basemap still shows every position it was asked to — on a plain panel, saying why
|
|
451
|
+
// there is no map behind them. Marking it required would report a correctly-rendering
|
|
452
|
+
// widget as broken on every board that names no provider of its own — which is most
|
|
453
|
+
// of them, because the usual and recommended answer is to inherit the tenant's
|
|
454
|
+
// basemap rather than to configure tiles per board.
|
|
455
|
+
options: {
|
|
456
|
+
title: TITLE,
|
|
457
|
+
tileUrl: {
|
|
458
|
+
kind: "string",
|
|
459
|
+
labelId: "widgetLabelTileUrl",
|
|
460
|
+
helpId: "widgetTileUrlHint",
|
|
461
|
+
control: "basemap",
|
|
462
|
+
doc: "Raster tile URL template, e.g. https://tiles.example.com/{z}/{x}/{y}.png. Leave empty to inherit the tenant's basemap; setting one overrides it for this board only."
|
|
463
|
+
},
|
|
464
|
+
attribution: {
|
|
465
|
+
kind: "string",
|
|
466
|
+
labelId: "widgetLabelAttribution",
|
|
467
|
+
helpId: "widgetAttributionHint",
|
|
468
|
+
control: "basemap",
|
|
469
|
+
doc: "Credit line the tile source requires, shown on the map. Set whatever the provider you chose asks for."
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
defaults: { title: "Map" },
|
|
473
|
+
interactions: {}
|
|
474
|
+
})
|
|
475
|
+
};
|
|
476
|
+
var builtinWidgetCatalog = createWidgetCatalog(Object.values(builtinWidgetDescriptors));
|
|
420
477
|
|
|
421
478
|
// src/widgets/severity.ts
|
|
422
479
|
var SEVERITY_COLORS = {
|
|
@@ -458,7 +515,7 @@ function highestSeverity(severities) {
|
|
|
458
515
|
}
|
|
459
516
|
|
|
460
517
|
// src/widgets/alarm-count.tsx
|
|
461
|
-
import { jsx
|
|
518
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
462
519
|
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
463
520
|
function AlarmCount({ widget, data }) {
|
|
464
521
|
const [sizeRef, size] = useElementSize();
|
|
@@ -470,7 +527,7 @@ function AlarmCount({ widget, data }) {
|
|
|
470
527
|
const display = loading && total === 0 ? "\u2014" : String(total);
|
|
471
528
|
const unit = total === 1 ? "alarm" : "alarms";
|
|
472
529
|
const ariaLabel = alerting ? `${total} ${unit}, worst severity ${severityLabel(worst)}` : `${total} ${unit}`;
|
|
473
|
-
return /* @__PURE__ */
|
|
530
|
+
return /* @__PURE__ */ jsx(WidgetFrame, { title: optString2(widget.options, "title"), children: /* @__PURE__ */ jsxs(
|
|
474
531
|
"div",
|
|
475
532
|
{
|
|
476
533
|
ref: sizeRef,
|
|
@@ -484,8 +541,8 @@ function AlarmCount({ widget, data }) {
|
|
|
484
541
|
textAlign: "center"
|
|
485
542
|
},
|
|
486
543
|
children: [
|
|
487
|
-
/* @__PURE__ */
|
|
488
|
-
/* @__PURE__ */
|
|
544
|
+
/* @__PURE__ */ jsx("div", { "aria-label": ariaLabel, style: { fontSize: valueFont, fontWeight: 700, lineHeight: 1, color }, children: display }),
|
|
545
|
+
/* @__PURE__ */ jsxs(
|
|
489
546
|
"div",
|
|
490
547
|
{
|
|
491
548
|
style: {
|
|
@@ -496,8 +553,8 @@ function AlarmCount({ widget, data }) {
|
|
|
496
553
|
letterSpacing: "0.05em"
|
|
497
554
|
},
|
|
498
555
|
children: [
|
|
499
|
-
/* @__PURE__ */
|
|
500
|
-
alerting ? /* @__PURE__ */
|
|
556
|
+
/* @__PURE__ */ jsx("span", { children: unit }),
|
|
557
|
+
alerting ? /* @__PURE__ */ jsx("span", { children: ` \xB7 worst ${severityLabel(worst)}` }) : null
|
|
501
558
|
]
|
|
502
559
|
}
|
|
503
560
|
)
|
|
@@ -505,98 +562,38 @@ function AlarmCount({ widget, data }) {
|
|
|
505
562
|
}
|
|
506
563
|
) });
|
|
507
564
|
}
|
|
565
|
+
var alarmCountWidget = defineWidget({
|
|
566
|
+
descriptor: builtinWidgetDescriptors["alarm-count"],
|
|
567
|
+
implementation: { kind: "trusted-react", component: AlarmCount }
|
|
568
|
+
});
|
|
508
569
|
|
|
509
570
|
// src/widgets/alarm-table.tsx
|
|
510
|
-
import { useState
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
return Number.isNaN(date.getTime()) ? iso : date.toLocaleString();
|
|
525
|
-
}
|
|
526
|
-
function formatValue(value, precision) {
|
|
527
|
-
if (value == null) return "\u2014";
|
|
528
|
-
if (precision == null) return String(value);
|
|
529
|
-
const places = Math.min(100, Math.max(0, Math.trunc(precision)));
|
|
530
|
-
return value.toFixed(places);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// src/flash.tsx
|
|
534
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
535
|
-
var UP = {
|
|
536
|
-
fg: "var(--flash-up, hsl(142 72% 40%))",
|
|
537
|
-
bg: "var(--flash-up-bg, hsl(142 72% 45% / 0.16))"
|
|
538
|
-
};
|
|
539
|
-
var DOWN = {
|
|
540
|
-
fg: "var(--flash-down, hsl(0 72% 51%))",
|
|
541
|
-
bg: "var(--flash-down-bg, hsl(0 72% 55% / 0.16))"
|
|
542
|
-
};
|
|
543
|
-
var HOLD_MS = 400;
|
|
544
|
-
var FADE_MS = 600;
|
|
545
|
-
function useFlashOnChange(value, enabled, identity) {
|
|
546
|
-
const prev = useRef2(value);
|
|
547
|
-
const prevIdentity = useRef2(identity);
|
|
548
|
-
const [direction, setDirection] = useState2(null);
|
|
549
|
-
useEffect2(() => {
|
|
550
|
-
const before = prev.current;
|
|
551
|
-
const sameEntity = prevIdentity.current === identity;
|
|
552
|
-
prev.current = value;
|
|
553
|
-
prevIdentity.current = identity;
|
|
554
|
-
if (!enabled || !sameEntity || typeof value !== "number" || typeof before !== "number" || !Number.isFinite(value) || !Number.isFinite(before) || value === before) {
|
|
555
|
-
setDirection(null);
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
setDirection(value > before ? "up" : "down");
|
|
559
|
-
const timer = setTimeout(() => setDirection(null), HOLD_MS);
|
|
560
|
-
return () => clearTimeout(timer);
|
|
561
|
-
}, [value, enabled, identity]);
|
|
562
|
-
return enabled ? direction : null;
|
|
563
|
-
}
|
|
564
|
-
function flashTextStyle(direction, fadeMs = FADE_MS) {
|
|
565
|
-
if (direction) return { color: direction === "up" ? UP.fg : DOWN.fg, transition: "none" };
|
|
566
|
-
return { transition: `color ${fadeMs}ms ease-out` };
|
|
567
|
-
}
|
|
568
|
-
function flashBackgroundStyle(direction, fadeMs = FADE_MS) {
|
|
569
|
-
if (direction) {
|
|
570
|
-
return { backgroundColor: direction === "up" ? UP.bg : DOWN.bg, transition: "none" };
|
|
571
|
-
}
|
|
572
|
-
return { transition: `background-color ${fadeMs}ms ease-out` };
|
|
573
|
-
}
|
|
574
|
-
function FlashValue({
|
|
575
|
-
value,
|
|
576
|
-
precision,
|
|
577
|
-
enabled,
|
|
578
|
-
identity,
|
|
579
|
-
baseColor = "inherit",
|
|
580
|
-
style
|
|
581
|
-
}) {
|
|
582
|
-
const direction = useFlashOnChange(value, enabled, identity);
|
|
583
|
-
return /* @__PURE__ */ jsx3("span", { style: { color: baseColor, ...style, ...flashTextStyle(direction) }, children: formatValue(value, precision) });
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// src/widgets/alarm-table.tsx
|
|
587
|
-
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
571
|
+
import { useState } from "react";
|
|
572
|
+
import {
|
|
573
|
+
css as css2,
|
|
574
|
+
defineWidget as defineWidget2,
|
|
575
|
+
FlashValue,
|
|
576
|
+
formatDateTime,
|
|
577
|
+
optBoolean,
|
|
578
|
+
optNumber as optNumber2,
|
|
579
|
+
optString as optString3,
|
|
580
|
+
useWidgetSelect,
|
|
581
|
+
useWidgetTimeZone,
|
|
582
|
+
WidgetFrame as WidgetFrame2
|
|
583
|
+
} from "@devicechain/widget-sdk";
|
|
584
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
588
585
|
var cell = { padding: "6px 10px", textAlign: "left", whiteSpace: "nowrap" };
|
|
589
586
|
var headCell = {
|
|
590
587
|
...cell,
|
|
591
588
|
position: "sticky",
|
|
592
589
|
top: 0,
|
|
593
|
-
background:
|
|
590
|
+
background: css2("card"),
|
|
594
591
|
fontSize: 11,
|
|
595
592
|
fontWeight: 600,
|
|
596
593
|
textTransform: "uppercase",
|
|
597
594
|
letterSpacing: "0.04em",
|
|
598
|
-
color:
|
|
599
|
-
borderBottom: `1px solid ${
|
|
595
|
+
color: css2("muted-foreground"),
|
|
596
|
+
borderBottom: `1px solid ${css2("border")}`
|
|
600
597
|
};
|
|
601
598
|
function statusLabel(alarm) {
|
|
602
599
|
if (alarm.state === "CLEARED") return "Cleared";
|
|
@@ -606,39 +603,40 @@ function AlarmTable({ widget, data, actions }) {
|
|
|
606
603
|
const { alarms, total, loading } = data;
|
|
607
604
|
const canAct = actions?.can("alarm:write") ?? false;
|
|
608
605
|
const columns = canAct ? 8 : 7;
|
|
609
|
-
const precision =
|
|
606
|
+
const precision = optNumber2(widget.options, "precision");
|
|
610
607
|
const flash = optBoolean(widget.options, "flashOnChange");
|
|
611
608
|
const select = useWidgetSelect();
|
|
612
|
-
const
|
|
609
|
+
const timeZone = useWidgetTimeZone();
|
|
610
|
+
const selectionTarget = optString3(widget.options, "selectionTarget");
|
|
613
611
|
const drillTo = select && selectionTarget ? { select, slot: selectionTarget } : void 0;
|
|
614
|
-
return /* @__PURE__ */
|
|
615
|
-
/* @__PURE__ */
|
|
612
|
+
return /* @__PURE__ */ jsx2(WidgetFrame2, { title: optString3(widget.options, "title"), children: /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
|
|
613
|
+
/* @__PURE__ */ jsx2("div", { style: { flex: 1, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ jsxs2(
|
|
616
614
|
"table",
|
|
617
615
|
{
|
|
618
616
|
style: {
|
|
619
617
|
width: "100%",
|
|
620
618
|
borderCollapse: "collapse",
|
|
621
619
|
fontSize: 13,
|
|
622
|
-
color:
|
|
620
|
+
color: css2("foreground"),
|
|
623
621
|
fontVariantNumeric: "tabular-nums"
|
|
624
622
|
},
|
|
625
623
|
children: [
|
|
626
|
-
/* @__PURE__ */
|
|
627
|
-
/* @__PURE__ */
|
|
628
|
-
/* @__PURE__ */
|
|
629
|
-
/* @__PURE__ */
|
|
630
|
-
/* @__PURE__ */
|
|
631
|
-
/* @__PURE__ */
|
|
632
|
-
/* @__PURE__ */
|
|
633
|
-
/* @__PURE__ */
|
|
634
|
-
canAct ? /* @__PURE__ */
|
|
624
|
+
/* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
|
|
625
|
+
/* @__PURE__ */ jsx2("th", { style: { ...headCell, width: 4, padding: 0 }, "aria-hidden": "true" }),
|
|
626
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", style: headCell, children: "Severity" }),
|
|
627
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", style: headCell, children: "Status" }),
|
|
628
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", style: headCell, children: "Originator" }),
|
|
629
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", style: headCell, children: "Alarm" }),
|
|
630
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", style: { ...headCell, textAlign: "right" }, children: "Value" }),
|
|
631
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", style: headCell, children: "Raised" }),
|
|
632
|
+
canAct ? /* @__PURE__ */ jsx2("th", { scope: "col", style: { ...headCell, textAlign: "right" }, children: "Actions" }) : null
|
|
635
633
|
] }) }),
|
|
636
|
-
/* @__PURE__ */
|
|
634
|
+
/* @__PURE__ */ jsx2("tbody", { children: alarms.length === 0 ? /* @__PURE__ */ jsx2("tr", { children: /* @__PURE__ */ jsx2("td", { colSpan: columns, style: { ...cell, textAlign: "center", color: css2("muted-foreground") }, children: loading ? "Loading\u2026" : "No alarms" }) }) : alarms.map((alarm) => {
|
|
637
635
|
const color = severityColor(alarm.severity);
|
|
638
|
-
return /* @__PURE__ */
|
|
639
|
-
/* @__PURE__ */
|
|
640
|
-
/* @__PURE__ */
|
|
641
|
-
/* @__PURE__ */
|
|
636
|
+
return /* @__PURE__ */ jsxs2("tr", { style: { borderBottom: `1px solid ${css2("border")}` }, children: [
|
|
637
|
+
/* @__PURE__ */ jsx2("td", { "aria-hidden": "true", style: { padding: 0, width: 4, background: color } }),
|
|
638
|
+
/* @__PURE__ */ jsx2("td", { style: cell, children: /* @__PURE__ */ jsxs2("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
|
|
639
|
+
/* @__PURE__ */ jsx2(
|
|
642
640
|
"span",
|
|
643
641
|
{
|
|
644
642
|
"aria-hidden": "true",
|
|
@@ -647,10 +645,10 @@ function AlarmTable({ widget, data, actions }) {
|
|
|
647
645
|
),
|
|
648
646
|
severityLabel(alarm.severity)
|
|
649
647
|
] }) }),
|
|
650
|
-
/* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
/* @__PURE__ */
|
|
653
|
-
/* @__PURE__ */
|
|
648
|
+
/* @__PURE__ */ jsx2("td", { style: { ...cell, color: css2("muted-foreground") }, children: statusLabel(alarm) }),
|
|
649
|
+
/* @__PURE__ */ jsx2("td", { style: cell, children: /* @__PURE__ */ jsx2(OriginatorCell, { alarm, drillTo }) }),
|
|
650
|
+
/* @__PURE__ */ jsx2("td", { style: cell, children: alarm.alarmKey }),
|
|
651
|
+
/* @__PURE__ */ jsx2("td", { style: { ...cell, textAlign: "right" }, children: /* @__PURE__ */ jsx2(
|
|
654
652
|
FlashValue,
|
|
655
653
|
{
|
|
656
654
|
value: alarm.lastValue,
|
|
@@ -659,21 +657,21 @@ function AlarmTable({ widget, data, actions }) {
|
|
|
659
657
|
identity: alarm.token
|
|
660
658
|
}
|
|
661
659
|
) }),
|
|
662
|
-
/* @__PURE__ */
|
|
663
|
-
canAct ? /* @__PURE__ */
|
|
660
|
+
/* @__PURE__ */ jsx2("td", { style: { ...cell, color: css2("muted-foreground") }, children: formatDateTime(alarm.raisedTime, timeZone) }),
|
|
661
|
+
canAct ? /* @__PURE__ */ jsx2("td", { style: { ...cell, textAlign: "right" }, children: /* @__PURE__ */ jsx2(AlarmRowActions, { alarm, actions }) }) : null
|
|
664
662
|
] }, alarm.token);
|
|
665
663
|
}) })
|
|
666
664
|
]
|
|
667
665
|
}
|
|
668
666
|
) }),
|
|
669
|
-
total > alarms.length ? /* @__PURE__ */
|
|
667
|
+
total > alarms.length ? /* @__PURE__ */ jsxs2(
|
|
670
668
|
"div",
|
|
671
669
|
{
|
|
672
670
|
style: {
|
|
673
671
|
padding: "4px 10px",
|
|
674
672
|
fontSize: 11,
|
|
675
|
-
color:
|
|
676
|
-
borderTop: `1px solid ${
|
|
673
|
+
color: css2("muted-foreground"),
|
|
674
|
+
borderTop: `1px solid ${css2("border")}`
|
|
677
675
|
},
|
|
678
676
|
children: [
|
|
679
677
|
"Showing ",
|
|
@@ -691,7 +689,7 @@ function OriginatorCell({
|
|
|
691
689
|
}) {
|
|
692
690
|
if (drillTo && alarm.originatorType === "device" && alarm.originatorToken) {
|
|
693
691
|
const token = alarm.originatorToken;
|
|
694
|
-
return /* @__PURE__ */
|
|
692
|
+
return /* @__PURE__ */ jsx2(
|
|
695
693
|
"button",
|
|
696
694
|
{
|
|
697
695
|
type: "button",
|
|
@@ -702,7 +700,7 @@ function OriginatorCell({
|
|
|
702
700
|
border: "none",
|
|
703
701
|
background: "none",
|
|
704
702
|
font: "inherit",
|
|
705
|
-
color:
|
|
703
|
+
color: css2("primary"),
|
|
706
704
|
cursor: "pointer",
|
|
707
705
|
textDecoration: "underline",
|
|
708
706
|
textUnderlineOffset: 2
|
|
@@ -711,23 +709,23 @@ function OriginatorCell({
|
|
|
711
709
|
}
|
|
712
710
|
);
|
|
713
711
|
}
|
|
714
|
-
return /* @__PURE__ */
|
|
712
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: alarm.originatorToken ?? alarm.originatorType });
|
|
715
713
|
}
|
|
716
714
|
var actionButton = {
|
|
717
715
|
padding: "2px 8px",
|
|
718
716
|
fontSize: 12,
|
|
719
717
|
lineHeight: 1.4,
|
|
720
718
|
borderRadius: 4,
|
|
721
|
-
border: `1px solid ${
|
|
722
|
-
background:
|
|
723
|
-
color:
|
|
719
|
+
border: `1px solid ${css2("border")}`,
|
|
720
|
+
background: css2("card"),
|
|
721
|
+
color: css2("foreground"),
|
|
724
722
|
cursor: "pointer"
|
|
725
723
|
};
|
|
726
724
|
function AlarmRowActions({ alarm, actions }) {
|
|
727
|
-
const [pending, setPending] =
|
|
728
|
-
const [failed, setFailed] =
|
|
725
|
+
const [pending, setPending] = useState(null);
|
|
726
|
+
const [failed, setFailed] = useState(false);
|
|
729
727
|
if (alarm.state !== "ACTIVE") {
|
|
730
|
-
return /* @__PURE__ */
|
|
728
|
+
return /* @__PURE__ */ jsx2("span", { style: { color: css2("muted-foreground") }, children: "\u2014" });
|
|
731
729
|
}
|
|
732
730
|
const run = (kind, op) => {
|
|
733
731
|
setPending(kind);
|
|
@@ -742,8 +740,8 @@ function AlarmRowActions({ alarm, actions }) {
|
|
|
742
740
|
}
|
|
743
741
|
promise.catch(() => setFailed(true)).finally(() => setPending(null));
|
|
744
742
|
};
|
|
745
|
-
return /* @__PURE__ */
|
|
746
|
-
failed ? /* @__PURE__ */
|
|
743
|
+
return /* @__PURE__ */ jsxs2("span", { style: { display: "inline-flex", gap: 6, justifyContent: "flex-end", alignItems: "center" }, children: [
|
|
744
|
+
failed ? /* @__PURE__ */ jsx2(
|
|
747
745
|
"span",
|
|
748
746
|
{
|
|
749
747
|
role: "img",
|
|
@@ -753,7 +751,7 @@ function AlarmRowActions({ alarm, actions }) {
|
|
|
753
751
|
children: "\u26A0"
|
|
754
752
|
}
|
|
755
753
|
) : null,
|
|
756
|
-
!alarm.acknowledged ? /* @__PURE__ */
|
|
754
|
+
!alarm.acknowledged ? /* @__PURE__ */ jsx2(
|
|
757
755
|
"button",
|
|
758
756
|
{
|
|
759
757
|
type: "button",
|
|
@@ -765,7 +763,7 @@ function AlarmRowActions({ alarm, actions }) {
|
|
|
765
763
|
children: pending === "ack" ? "\u2026" : "Ack"
|
|
766
764
|
}
|
|
767
765
|
) : null,
|
|
768
|
-
/* @__PURE__ */
|
|
766
|
+
/* @__PURE__ */ jsx2(
|
|
769
767
|
"button",
|
|
770
768
|
{
|
|
771
769
|
type: "button",
|
|
@@ -779,9 +777,21 @@ function AlarmRowActions({ alarm, actions }) {
|
|
|
779
777
|
)
|
|
780
778
|
] });
|
|
781
779
|
}
|
|
780
|
+
var alarmTableWidget = defineWidget2({
|
|
781
|
+
descriptor: builtinWidgetDescriptors["alarm-table"],
|
|
782
|
+
implementation: { kind: "trusted-react", component: AlarmTable }
|
|
783
|
+
});
|
|
782
784
|
|
|
783
785
|
// src/widgets/command-button.tsx
|
|
784
|
-
import { useEffect
|
|
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";
|
|
785
795
|
|
|
786
796
|
// src/widgets/command-status.ts
|
|
787
797
|
import { isTerminalCommandStatus } from "@devicechain/dashboards";
|
|
@@ -929,20 +939,20 @@ function coerce(param, raw) {
|
|
|
929
939
|
}
|
|
930
940
|
|
|
931
941
|
// src/widgets/command-button.tsx
|
|
932
|
-
import { Fragment as Fragment2, jsx as
|
|
942
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
933
943
|
function CommandButton({ widget, data, actions }) {
|
|
934
|
-
const commandName =
|
|
935
|
-
const commandLabel =
|
|
936
|
-
const parameterSchema =
|
|
937
|
-
const params =
|
|
944
|
+
const commandName = optString4(widget.options, "commandName");
|
|
945
|
+
const commandLabel = optString4(widget.options, "commandLabel") ?? commandName;
|
|
946
|
+
const parameterSchema = optString4(widget.options, "parameterSchema") ?? null;
|
|
947
|
+
const params = useMemo(() => parseParameterSchema(parameterSchema), [parameterSchema]);
|
|
938
948
|
const schemaKey = `${commandName ?? ""}::${parameterSchema ?? ""}`;
|
|
939
|
-
const [values, setValues] =
|
|
940
|
-
const [errors, setErrors] =
|
|
941
|
-
const [pending, setPending] =
|
|
942
|
-
const [sendError, setSendError] =
|
|
943
|
-
const [sentToken, setSentToken] =
|
|
944
|
-
const sendGen =
|
|
945
|
-
|
|
949
|
+
const [values, setValues] = useState2(() => defaultValues(params));
|
|
950
|
+
const [errors, setErrors] = useState2({});
|
|
951
|
+
const [pending, setPending] = useState2(false);
|
|
952
|
+
const [sendError, setSendError] = useState2(null);
|
|
953
|
+
const [sentToken, setSentToken] = useState2(null);
|
|
954
|
+
const sendGen = useRef(0);
|
|
955
|
+
useEffect(() => {
|
|
946
956
|
sendGen.current += 1;
|
|
947
957
|
setValues(defaultValues(params));
|
|
948
958
|
setErrors({});
|
|
@@ -992,10 +1002,10 @@ function CommandButton({ widget, data, actions }) {
|
|
|
992
1002
|
if (gen === sendGen.current) setPending(false);
|
|
993
1003
|
});
|
|
994
1004
|
};
|
|
995
|
-
return /* @__PURE__ */
|
|
996
|
-
/* @__PURE__ */
|
|
1005
|
+
return /* @__PURE__ */ jsx3(WidgetFrame3, { title: optString4(widget.options, "title"), children: /* @__PURE__ */ jsx3("div", { style: { display: "flex", flexDirection: "column", height: "100%", gap: 10 }, children: !commandName ? /* @__PURE__ */ jsx3("div", { style: { ...hint, margin: "auto" }, children: "No command configured." }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1006
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
997
1007
|
params.map(
|
|
998
|
-
(param) => isScalar(param) ? /* @__PURE__ */
|
|
1008
|
+
(param) => isScalar(param) ? /* @__PURE__ */ jsx3(
|
|
999
1009
|
ParamField,
|
|
1000
1010
|
{
|
|
1001
1011
|
widgetId: widget.id,
|
|
@@ -1009,7 +1019,7 @@ function CommandButton({ widget, data, actions }) {
|
|
|
1009
1019
|
) : (
|
|
1010
1020
|
// A structured (OBJECT) parameter isn't form-editable. A REQUIRED one
|
|
1011
1021
|
// blocks sending (validateParams flags it); flag that in the note.
|
|
1012
|
-
/* @__PURE__ */
|
|
1022
|
+
/* @__PURE__ */ jsxs3("div", { style: { ...hint, color: param.required ? commandStatusColor("FAILED") : css3("muted-foreground") }, children: [
|
|
1013
1023
|
param.name,
|
|
1014
1024
|
": ",
|
|
1015
1025
|
param.required ? "required " : "",
|
|
@@ -1017,7 +1027,7 @@ function CommandButton({ widget, data, actions }) {
|
|
|
1017
1027
|
] }, param.name)
|
|
1018
1028
|
)
|
|
1019
1029
|
),
|
|
1020
|
-
/* @__PURE__ */
|
|
1030
|
+
/* @__PURE__ */ jsx3(
|
|
1021
1031
|
"button",
|
|
1022
1032
|
{
|
|
1023
1033
|
type: "button",
|
|
@@ -1028,10 +1038,10 @@ function CommandButton({ widget, data, actions }) {
|
|
|
1028
1038
|
children: pending ? "Sending\u2026" : `Send ${commandLabel}`
|
|
1029
1039
|
}
|
|
1030
1040
|
),
|
|
1031
|
-
!canWrite ? /* @__PURE__ */
|
|
1032
|
-
sendError ? /* @__PURE__ */
|
|
1041
|
+
!canWrite ? /* @__PURE__ */ jsx3("div", { style: hint, children: "You don\u2019t have permission to issue commands." }) : !hasSeam ? /* @__PURE__ */ jsx3("div", { style: hint, children: "Commands aren\u2019t available in this view." }) : !deviceToken ? /* @__PURE__ */ jsx3("div", { style: hint, children: "No device is selected for this command." }) : null,
|
|
1042
|
+
sendError ? /* @__PURE__ */ jsx3("div", { role: "alert", style: { ...hint, color: commandStatusColor("FAILED") }, children: sendError }) : null
|
|
1033
1043
|
] }),
|
|
1034
|
-
/* @__PURE__ */
|
|
1044
|
+
/* @__PURE__ */ jsx3(CommandHistory, { commands: data.commands, sentToken, loading: data.loading, error: data.error })
|
|
1035
1045
|
] }) }) });
|
|
1036
1046
|
}
|
|
1037
1047
|
function ParamField({
|
|
@@ -1044,14 +1054,14 @@ function ParamField({
|
|
|
1044
1054
|
}) {
|
|
1045
1055
|
const labelId = `cmd-${widgetId}-${param.name}`;
|
|
1046
1056
|
const unit = param.unit ? ` (${param.unit})` : "";
|
|
1047
|
-
return /* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1057
|
+
return /* @__PURE__ */ jsxs3("label", { htmlFor: labelId, style: { display: "flex", flexDirection: "column", gap: 3 }, children: [
|
|
1058
|
+
/* @__PURE__ */ jsxs3("span", { style: { fontSize: 12, color: css3("muted-foreground") }, children: [
|
|
1049
1059
|
param.name,
|
|
1050
1060
|
unit,
|
|
1051
|
-
param.required ? /* @__PURE__ */
|
|
1061
|
+
param.required ? /* @__PURE__ */ jsx3("span", { style: { color: commandStatusColor("FAILED") }, children: " *" }) : null
|
|
1052
1062
|
] }),
|
|
1053
|
-
/* @__PURE__ */
|
|
1054
|
-
error ? /* @__PURE__ */
|
|
1063
|
+
/* @__PURE__ */ jsx3(ParamInput, { id: labelId, param, value, disabled, onChange }),
|
|
1064
|
+
error ? /* @__PURE__ */ jsx3("span", { style: { fontSize: 11, color: commandStatusColor("FAILED") }, children: error }) : null
|
|
1055
1065
|
] });
|
|
1056
1066
|
}
|
|
1057
1067
|
function ParamInput({
|
|
@@ -1062,13 +1072,13 @@ function ParamInput({
|
|
|
1062
1072
|
onChange
|
|
1063
1073
|
}) {
|
|
1064
1074
|
if (param.enum && param.enum.length > 0) {
|
|
1065
|
-
return /* @__PURE__ */
|
|
1066
|
-
!param.required ? /* @__PURE__ */
|
|
1067
|
-
param.enum.map((opt) => /* @__PURE__ */
|
|
1075
|
+
return /* @__PURE__ */ jsxs3("select", { id, style: inputStyle, value, disabled, onChange: (e) => onChange(e.target.value), children: [
|
|
1076
|
+
!param.required ? /* @__PURE__ */ jsx3("option", { value: "", children: "\u2014" }) : null,
|
|
1077
|
+
param.enum.map((opt) => /* @__PURE__ */ jsx3("option", { value: opt, children: opt }, opt))
|
|
1068
1078
|
] });
|
|
1069
1079
|
}
|
|
1070
1080
|
if (param.dataType === "BOOLEAN") {
|
|
1071
|
-
return /* @__PURE__ */
|
|
1081
|
+
return /* @__PURE__ */ jsx3(
|
|
1072
1082
|
"input",
|
|
1073
1083
|
{
|
|
1074
1084
|
id,
|
|
@@ -1081,7 +1091,7 @@ function ParamInput({
|
|
|
1081
1091
|
);
|
|
1082
1092
|
}
|
|
1083
1093
|
const numeric = param.dataType === "INT" || param.dataType === "DOUBLE";
|
|
1084
|
-
return /* @__PURE__ */
|
|
1094
|
+
return /* @__PURE__ */ jsx3(
|
|
1085
1095
|
"input",
|
|
1086
1096
|
{
|
|
1087
1097
|
id,
|
|
@@ -1104,17 +1114,18 @@ function CommandHistory({
|
|
|
1104
1114
|
error
|
|
1105
1115
|
}) {
|
|
1106
1116
|
const empty = commands.length === 0;
|
|
1107
|
-
|
|
1117
|
+
const timeZone = useWidgetTimeZone2();
|
|
1118
|
+
return /* @__PURE__ */ jsx3(
|
|
1108
1119
|
"div",
|
|
1109
1120
|
{
|
|
1110
1121
|
style: {
|
|
1111
1122
|
flex: 1,
|
|
1112
1123
|
minHeight: 0,
|
|
1113
1124
|
overflow: "auto",
|
|
1114
|
-
borderTop: `1px solid ${
|
|
1125
|
+
borderTop: `1px solid ${css3("border")}`,
|
|
1115
1126
|
paddingTop: 6
|
|
1116
1127
|
},
|
|
1117
|
-
children: empty ? /* @__PURE__ */
|
|
1128
|
+
children: empty ? /* @__PURE__ */ jsx3("div", { style: { ...hint, textAlign: "center", color: error ? commandStatusColor("FAILED") : css3("muted-foreground") }, children: error ? "Couldn\u2019t load command history." : loading ? "Loading\u2026" : "No commands issued yet." }) : /* @__PURE__ */ jsx3("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 4 }, children: commands.map((command) => /* @__PURE__ */ jsxs3(
|
|
1118
1129
|
"li",
|
|
1119
1130
|
{
|
|
1120
1131
|
style: {
|
|
@@ -1126,9 +1137,9 @@ function CommandHistory({
|
|
|
1126
1137
|
fontWeight: command.token === sentToken ? 600 : 400
|
|
1127
1138
|
},
|
|
1128
1139
|
children: [
|
|
1129
|
-
/* @__PURE__ */
|
|
1130
|
-
/* @__PURE__ */
|
|
1131
|
-
/* @__PURE__ */
|
|
1140
|
+
/* @__PURE__ */ jsx3(StatusBadge, { status: command.status }),
|
|
1141
|
+
/* @__PURE__ */ jsx3("span", { style: { color: css3("foreground"), whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: command.name }),
|
|
1142
|
+
/* @__PURE__ */ jsx3("span", { style: { marginLeft: "auto", color: css3("muted-foreground"), whiteSpace: "nowrap" }, children: formatDateTime2(command.queuedTime, timeZone) })
|
|
1132
1143
|
]
|
|
1133
1144
|
},
|
|
1134
1145
|
command.token
|
|
@@ -1138,7 +1149,7 @@ function CommandHistory({
|
|
|
1138
1149
|
}
|
|
1139
1150
|
function StatusBadge({ status }) {
|
|
1140
1151
|
const color = commandStatusColor(status);
|
|
1141
|
-
return /* @__PURE__ */
|
|
1152
|
+
return /* @__PURE__ */ jsxs3(
|
|
1142
1153
|
"span",
|
|
1143
1154
|
{
|
|
1144
1155
|
style: {
|
|
@@ -1151,13 +1162,13 @@ function StatusBadge({ status }) {
|
|
|
1151
1162
|
color
|
|
1152
1163
|
},
|
|
1153
1164
|
children: [
|
|
1154
|
-
/* @__PURE__ */
|
|
1165
|
+
/* @__PURE__ */ jsx3("span", { "aria-hidden": "true", style: { width: 7, height: 7, borderRadius: "50%", background: color } }),
|
|
1155
1166
|
commandStatusLabel(status)
|
|
1156
1167
|
]
|
|
1157
1168
|
}
|
|
1158
1169
|
);
|
|
1159
1170
|
}
|
|
1160
|
-
var hint = { fontSize: 11, color:
|
|
1171
|
+
var hint = { fontSize: 11, color: css3("muted-foreground") };
|
|
1161
1172
|
var SEND_FAILED = "Couldn\u2019t issue the command. Try again.";
|
|
1162
1173
|
var inputStyle = {
|
|
1163
1174
|
width: "100%",
|
|
@@ -1165,9 +1176,9 @@ var inputStyle = {
|
|
|
1165
1176
|
padding: "4px 8px",
|
|
1166
1177
|
fontSize: 13,
|
|
1167
1178
|
borderRadius: 4,
|
|
1168
|
-
border: `1px solid ${
|
|
1169
|
-
background:
|
|
1170
|
-
color:
|
|
1179
|
+
border: `1px solid ${css3("border")}`,
|
|
1180
|
+
background: css3("background"),
|
|
1181
|
+
color: css3("foreground")
|
|
1171
1182
|
};
|
|
1172
1183
|
var sendButton = {
|
|
1173
1184
|
padding: "6px 12px",
|
|
@@ -1175,14 +1186,26 @@ var sendButton = {
|
|
|
1175
1186
|
fontWeight: 600,
|
|
1176
1187
|
borderRadius: 4,
|
|
1177
1188
|
border: "none",
|
|
1178
|
-
background:
|
|
1179
|
-
color:
|
|
1189
|
+
background: css3("primary"),
|
|
1190
|
+
color: css3("background"),
|
|
1180
1191
|
marginTop: 2
|
|
1181
1192
|
};
|
|
1193
|
+
var commandButtonWidget = defineWidget3({
|
|
1194
|
+
descriptor: builtinWidgetDescriptors["command-button"],
|
|
1195
|
+
implementation: { kind: "trusted-react", component: CommandButton }
|
|
1196
|
+
});
|
|
1182
1197
|
|
|
1183
1198
|
// src/widgets/entity-selector.tsx
|
|
1184
|
-
import { useEffect as
|
|
1185
|
-
import {
|
|
1199
|
+
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
1200
|
+
import {
|
|
1201
|
+
css as css4,
|
|
1202
|
+
defineWidget as defineWidget4,
|
|
1203
|
+
optString as optString5,
|
|
1204
|
+
useCandidates,
|
|
1205
|
+
useWidgetSelect as useWidgetSelect2,
|
|
1206
|
+
WidgetFrame as WidgetFrame4
|
|
1207
|
+
} from "@devicechain/widget-sdk";
|
|
1208
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1186
1209
|
function tokenOf(b) {
|
|
1187
1210
|
return b.kind === "device" ? b.deviceToken : b.anchor.targetToken;
|
|
1188
1211
|
}
|
|
@@ -1191,23 +1214,23 @@ var control = {
|
|
|
1191
1214
|
fontSize: 14,
|
|
1192
1215
|
padding: "8px 10px",
|
|
1193
1216
|
borderRadius: 6,
|
|
1194
|
-
border: `1px solid ${
|
|
1195
|
-
background:
|
|
1196
|
-
color:
|
|
1217
|
+
border: `1px solid ${css4("border")}`,
|
|
1218
|
+
background: css4("background"),
|
|
1219
|
+
color: css4("foreground")
|
|
1197
1220
|
};
|
|
1198
1221
|
var hint2 = {
|
|
1199
1222
|
fontSize: 12,
|
|
1200
|
-
color:
|
|
1223
|
+
color: css4("muted-foreground")
|
|
1201
1224
|
};
|
|
1202
1225
|
function EntitySelector({ widget }) {
|
|
1203
|
-
const select =
|
|
1204
|
-
const target =
|
|
1226
|
+
const select = useWidgetSelect2();
|
|
1227
|
+
const target = optString5(widget.options, "selectionTarget");
|
|
1205
1228
|
const { candidates, loading, error, wired } = useCandidates(target);
|
|
1206
|
-
const title =
|
|
1207
|
-
const [pending, setPending] =
|
|
1229
|
+
const title = optString5(widget.options, "title");
|
|
1230
|
+
const [pending, setPending] = useState3(null);
|
|
1208
1231
|
const selected = candidates.find((c) => c.selected);
|
|
1209
1232
|
const selectedToken = selected ? tokenOf(selected.binding) : "";
|
|
1210
|
-
|
|
1233
|
+
useEffect2(() => {
|
|
1211
1234
|
if (pending !== null && (selectedToken === pending || !candidates.some((c) => tokenOf(c.binding) === pending))) {
|
|
1212
1235
|
setPending(null);
|
|
1213
1236
|
}
|
|
@@ -1223,8 +1246,8 @@ function EntitySelector({ widget }) {
|
|
|
1223
1246
|
select({ slot: target, binding: candidate.binding });
|
|
1224
1247
|
}
|
|
1225
1248
|
};
|
|
1226
|
-
return /* @__PURE__ */
|
|
1227
|
-
/* @__PURE__ */
|
|
1249
|
+
return /* @__PURE__ */ jsx4(WidgetFrame4, { title, children: /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "column", gap: 8, padding: "12px 16px", height: "100%" }, children: inert ? /* @__PURE__ */ jsx4("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(Fragment3, { children: [
|
|
1250
|
+
/* @__PURE__ */ jsxs4(
|
|
1228
1251
|
"select",
|
|
1229
1252
|
{
|
|
1230
1253
|
style: { ...control, opacity: loading ? 0.6 : 1 },
|
|
@@ -1233,165 +1256,74 @@ function EntitySelector({ widget }) {
|
|
|
1233
1256
|
onChange,
|
|
1234
1257
|
"aria-label": title || `Select ${target}`,
|
|
1235
1258
|
children: [
|
|
1236
|
-
/* @__PURE__ */
|
|
1237
|
-
candidates.map((c) => /* @__PURE__ */
|
|
1259
|
+
/* @__PURE__ */ jsx4("option", { value: "", disabled: true, children: loading ? "Loading\u2026" : candidates.length === 0 ? "No options" : "Select\u2026" }),
|
|
1260
|
+
candidates.map((c) => /* @__PURE__ */ jsx4("option", { value: tokenOf(c.binding), children: c.label }, tokenOf(c.binding)))
|
|
1238
1261
|
]
|
|
1239
1262
|
}
|
|
1240
1263
|
),
|
|
1241
|
-
error ? /* @__PURE__ */
|
|
1264
|
+
error ? /* @__PURE__ */ jsx4("span", { style: hint2, children: "Couldn\u2019t load options." }) : null
|
|
1242
1265
|
] }) }) });
|
|
1243
1266
|
}
|
|
1267
|
+
var entitySelectorWidget = defineWidget4({
|
|
1268
|
+
descriptor: builtinWidgetDescriptors["entity-selector"],
|
|
1269
|
+
implementation: { kind: "trusted-react", component: EntitySelector }
|
|
1270
|
+
});
|
|
1244
1271
|
|
|
1245
1272
|
// src/widgets/gauge.tsx
|
|
1246
|
-
import { useMemo as
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
}
|
|
1261
|
-
function
|
|
1262
|
-
const
|
|
1263
|
-
const
|
|
1264
|
-
const
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
tooltip: { trigger: "axis" },
|
|
1280
|
-
legend: names.length > 1 ? { textStyle: { color: theme.mutedForeground, fontSize: axisFont }, top: 0 } : void 0,
|
|
1281
|
-
xAxis: {
|
|
1282
|
-
type: "time",
|
|
1283
|
-
axisLine: { lineStyle: { color: theme.border } },
|
|
1284
|
-
axisLabel: { color: theme.mutedForeground, fontSize: axisFont, hideOverlap: true }
|
|
1285
|
-
},
|
|
1286
|
-
yAxis: {
|
|
1287
|
-
type: "value",
|
|
1288
|
-
splitLine: { lineStyle: { color: theme.border } },
|
|
1289
|
-
axisLabel: { color: theme.mutedForeground, fontSize: axisFont }
|
|
1290
|
-
},
|
|
1291
|
-
series
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
function buildGaugeOption(value, theme, options = {}, size) {
|
|
1295
|
-
const min = options.min ?? 0;
|
|
1296
|
-
const max = options.max ?? 100;
|
|
1297
|
-
const accent = theme.series[0];
|
|
1298
|
-
const unitSuffix = options.unit ? ` ${options.unit}` : "";
|
|
1299
|
-
const s = shortSide(size);
|
|
1300
|
-
const arcWidth = clamp2(Math.round(s / 26), 4, 12);
|
|
1301
|
-
const labelFont = clamp2(Math.round(s / 20), 8, 13);
|
|
1302
|
-
const valueFont = clamp2(Math.round(s / 7), 14, 40);
|
|
1303
|
-
return {
|
|
1304
|
-
animation: false,
|
|
1305
|
-
series: [
|
|
1306
|
-
{
|
|
1307
|
-
type: "gauge",
|
|
1308
|
-
min,
|
|
1309
|
-
max,
|
|
1310
|
-
splitNumber: s < 160 ? 4 : 5,
|
|
1311
|
-
// fewer ticks than the default 10 → no cram
|
|
1312
|
-
radius: "95%",
|
|
1313
|
-
center: ["50%", "58%"],
|
|
1314
|
-
progress: { show: true, width: arcWidth, itemStyle: { color: accent } },
|
|
1315
|
-
pointer: { itemStyle: { color: accent }, length: "55%", width: clamp2(Math.round(s / 50), 2, 6) },
|
|
1316
|
-
anchor: { show: false },
|
|
1317
|
-
axisLine: { lineStyle: { width: arcWidth, color: [[1, theme.border]] } },
|
|
1318
|
-
axisTick: { show: false },
|
|
1319
|
-
splitLine: { length: arcWidth, lineStyle: { color: theme.border, width: 1 } },
|
|
1320
|
-
axisLabel: { color: theme.mutedForeground, fontSize: labelFont, distance: arcWidth + 2 },
|
|
1321
|
-
detail: {
|
|
1322
|
-
valueAnimation: false,
|
|
1323
|
-
color: theme.foreground,
|
|
1324
|
-
fontSize: valueFont,
|
|
1325
|
-
offsetCenter: [0, "30%"],
|
|
1326
|
-
formatter: (v) => value == null ? "\u2014" : `${v}${unitSuffix}`
|
|
1327
|
-
},
|
|
1328
|
-
data: [{ value: value ?? min }]
|
|
1329
|
-
}
|
|
1330
|
-
]
|
|
1331
|
-
};
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
// src/echart.tsx
|
|
1335
|
-
import { GaugeChart, LineChart } from "echarts/charts";
|
|
1336
|
-
import { GridComponent, TooltipComponent } from "echarts/components";
|
|
1337
|
-
import * as echarts from "echarts/core";
|
|
1338
|
-
import { CanvasRenderer } from "echarts/renderers";
|
|
1339
|
-
import { useEffect as useEffect5, useRef as useRef4 } from "react";
|
|
1340
|
-
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1341
|
-
echarts.use([LineChart, GaugeChart, GridComponent, TooltipComponent, CanvasRenderer]);
|
|
1342
|
-
function EChart({ option, className, style }) {
|
|
1343
|
-
const containerRef = useRef4(null);
|
|
1344
|
-
const chartRef = useRef4(null);
|
|
1345
|
-
useEffect5(() => {
|
|
1346
|
-
const el = containerRef.current;
|
|
1347
|
-
if (!el) return;
|
|
1348
|
-
const chart = echarts.init(el);
|
|
1349
|
-
chartRef.current = chart;
|
|
1350
|
-
const observer = new ResizeObserver(() => chart.resize());
|
|
1351
|
-
observer.observe(el);
|
|
1352
|
-
return () => {
|
|
1353
|
-
observer.disconnect();
|
|
1354
|
-
chart.dispose();
|
|
1355
|
-
chartRef.current = null;
|
|
1356
|
-
};
|
|
1357
|
-
}, []);
|
|
1358
|
-
useEffect5(() => {
|
|
1359
|
-
chartRef.current?.setOption(option, { notMerge: true });
|
|
1360
|
-
}, [option]);
|
|
1361
|
-
return /* @__PURE__ */ jsx7("div", { ref: containerRef, className, style: { width: "100%", height: "100%", ...style } });
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
// src/widgets/gauge.tsx
|
|
1365
|
-
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1366
|
-
function Gauge({ widget, data }) {
|
|
1367
|
-
const theme = useChartTheme();
|
|
1368
|
-
const [sizeRef, size] = useElementSize();
|
|
1369
|
-
const name = primaryMeasurementName(widget);
|
|
1370
|
-
const sample = pickSample(data.latest, name);
|
|
1371
|
-
const min = optNumber(widget.options, "min");
|
|
1372
|
-
const max = optNumber(widget.options, "max");
|
|
1373
|
-
const unit = optString(widget.options, "unit");
|
|
1374
|
-
const option = useMemo3(
|
|
1375
|
-
() => buildGaugeOption(sample?.value ?? null, theme, { min, max, unit }, size),
|
|
1376
|
-
[sample?.value, theme, min, max, unit, size.width, size.height]
|
|
1377
|
-
);
|
|
1378
|
-
const flashDirection = useFlashOnChange(
|
|
1379
|
-
sample?.value,
|
|
1380
|
-
optBoolean(widget.options, "flashOnChange"),
|
|
1381
|
-
sample && `${sample.deviceToken}:${sample.name}`
|
|
1382
|
-
);
|
|
1383
|
-
return /* @__PURE__ */ jsx8(WidgetFrame, { title: optString(widget.options, "title") ?? name, children: /* @__PURE__ */ jsx8("div", { ref: sizeRef, style: { width: "100%", height: "100%", ...flashBackgroundStyle(flashDirection) }, children: /* @__PURE__ */ jsx8(EChart, { option }) }) });
|
|
1273
|
+
import { useMemo as useMemo2 } from "react";
|
|
1274
|
+
import {
|
|
1275
|
+
defineWidget as defineWidget5,
|
|
1276
|
+
flashBackgroundStyle,
|
|
1277
|
+
optBoolean as optBoolean2,
|
|
1278
|
+
optNumber as optNumber3,
|
|
1279
|
+
optString as optString6,
|
|
1280
|
+
pickSample,
|
|
1281
|
+
primaryMeasurementName,
|
|
1282
|
+
useElementSize as useElementSize2,
|
|
1283
|
+
useFlashOnChange,
|
|
1284
|
+
WidgetFrame as WidgetFrame5
|
|
1285
|
+
} from "@devicechain/widget-sdk";
|
|
1286
|
+
import { buildGaugeOption, EChart, useChartTheme } from "@devicechain/widget-sdk/echarts";
|
|
1287
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1288
|
+
function Gauge({ widget, data }) {
|
|
1289
|
+
const theme = useChartTheme();
|
|
1290
|
+
const [sizeRef, size] = useElementSize2();
|
|
1291
|
+
const name = primaryMeasurementName(widget);
|
|
1292
|
+
const sample = pickSample(data.latest, name);
|
|
1293
|
+
const min = optNumber3(widget.options, "min");
|
|
1294
|
+
const max = optNumber3(widget.options, "max");
|
|
1295
|
+
const unit = optString6(widget.options, "unit");
|
|
1296
|
+
const option = useMemo2(
|
|
1297
|
+
() => buildGaugeOption(sample?.value ?? null, theme, { min, max, unit }, size),
|
|
1298
|
+
[sample?.value, theme, min, max, unit, size.width, size.height]
|
|
1299
|
+
);
|
|
1300
|
+
const flashDirection = useFlashOnChange(
|
|
1301
|
+
sample?.value,
|
|
1302
|
+
optBoolean2(widget.options, "flashOnChange"),
|
|
1303
|
+
sample && `${sample.deviceToken}:${sample.name}`
|
|
1304
|
+
);
|
|
1305
|
+
return /* @__PURE__ */ jsx5(WidgetFrame5, { title: optString6(widget.options, "title") ?? name, children: /* @__PURE__ */ jsx5("div", { ref: sizeRef, style: { width: "100%", height: "100%", ...flashBackgroundStyle(flashDirection) }, children: /* @__PURE__ */ jsx5(EChart, { option }) }) });
|
|
1384
1306
|
}
|
|
1307
|
+
var gaugeWidget = defineWidget5({
|
|
1308
|
+
descriptor: builtinWidgetDescriptors["gauge"],
|
|
1309
|
+
implementation: { kind: "trusted-react", component: Gauge }
|
|
1310
|
+
});
|
|
1385
1311
|
|
|
1386
1312
|
// src/widgets/image.tsx
|
|
1387
|
-
import {
|
|
1313
|
+
import {
|
|
1314
|
+
css as css5,
|
|
1315
|
+
defineWidget as defineWidget6,
|
|
1316
|
+
optString as optString7,
|
|
1317
|
+
WidgetFrame as WidgetFrame6
|
|
1318
|
+
} from "@devicechain/widget-sdk";
|
|
1319
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1388
1320
|
var FITS = /* @__PURE__ */ new Set(["contain", "cover", "fill"]);
|
|
1389
1321
|
function Image({ widget }) {
|
|
1390
|
-
const url =
|
|
1391
|
-
const alt =
|
|
1392
|
-
const fitOption =
|
|
1322
|
+
const url = optString7(widget.options, "url");
|
|
1323
|
+
const alt = optString7(widget.options, "alt") ?? "";
|
|
1324
|
+
const fitOption = optString7(widget.options, "fit");
|
|
1393
1325
|
const objectFit = fitOption && FITS.has(fitOption) ? fitOption : "contain";
|
|
1394
|
-
return /* @__PURE__ */
|
|
1326
|
+
return /* @__PURE__ */ jsx6(WidgetFrame6, { title: optString7(widget.options, "title"), children: url ? /* @__PURE__ */ jsx6("img", { src: url, alt, style: { width: "100%", height: "100%", objectFit, display: "block" } }) : /* @__PURE__ */ jsx6(
|
|
1395
1327
|
"div",
|
|
1396
1328
|
{
|
|
1397
1329
|
style: {
|
|
@@ -1399,26 +1331,37 @@ function Image({ widget }) {
|
|
|
1399
1331
|
alignItems: "center",
|
|
1400
1332
|
justifyContent: "center",
|
|
1401
1333
|
height: "100%",
|
|
1402
|
-
color:
|
|
1334
|
+
color: css5("muted-foreground"),
|
|
1403
1335
|
fontSize: 13
|
|
1404
1336
|
},
|
|
1405
1337
|
children: "No image URL"
|
|
1406
1338
|
}
|
|
1407
1339
|
) });
|
|
1408
1340
|
}
|
|
1341
|
+
var imageWidget = defineWidget6({
|
|
1342
|
+
descriptor: builtinWidgetDescriptors["image"],
|
|
1343
|
+
implementation: { kind: "trusted-react", component: Image }
|
|
1344
|
+
});
|
|
1409
1345
|
|
|
1410
1346
|
// src/widgets/label.tsx
|
|
1411
|
-
import {
|
|
1347
|
+
import {
|
|
1348
|
+
css as css6,
|
|
1349
|
+
defineWidget as defineWidget7,
|
|
1350
|
+
optNumber as optNumber4,
|
|
1351
|
+
optString as optString8,
|
|
1352
|
+
WidgetFrame as WidgetFrame7
|
|
1353
|
+
} from "@devicechain/widget-sdk";
|
|
1354
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1412
1355
|
var justify = {
|
|
1413
1356
|
left: "flex-start",
|
|
1414
1357
|
center: "center",
|
|
1415
1358
|
right: "flex-end"
|
|
1416
1359
|
};
|
|
1417
1360
|
function Label({ widget }) {
|
|
1418
|
-
const text =
|
|
1419
|
-
const align =
|
|
1420
|
-
const fontSize =
|
|
1421
|
-
return /* @__PURE__ */
|
|
1361
|
+
const text = optString8(widget.options, "text") ?? "";
|
|
1362
|
+
const align = optString8(widget.options, "align") ?? "center";
|
|
1363
|
+
const fontSize = optNumber4(widget.options, "fontSize") ?? 16;
|
|
1364
|
+
return /* @__PURE__ */ jsx7(WidgetFrame7, { title: optString8(widget.options, "title"), children: /* @__PURE__ */ jsx7(
|
|
1422
1365
|
"div",
|
|
1423
1366
|
{
|
|
1424
1367
|
style: {
|
|
@@ -1428,12 +1371,12 @@ function Label({ widget }) {
|
|
|
1428
1371
|
height: "100%",
|
|
1429
1372
|
padding: 12
|
|
1430
1373
|
},
|
|
1431
|
-
children: /* @__PURE__ */
|
|
1374
|
+
children: /* @__PURE__ */ jsx7(
|
|
1432
1375
|
"span",
|
|
1433
1376
|
{
|
|
1434
1377
|
style: {
|
|
1435
1378
|
fontSize,
|
|
1436
|
-
color:
|
|
1379
|
+
color: css6("foreground"),
|
|
1437
1380
|
textAlign: align,
|
|
1438
1381
|
whiteSpace: "pre-wrap"
|
|
1439
1382
|
},
|
|
@@ -1443,25 +1386,46 @@ function Label({ widget }) {
|
|
|
1443
1386
|
}
|
|
1444
1387
|
) });
|
|
1445
1388
|
}
|
|
1389
|
+
var labelWidget = defineWidget7({
|
|
1390
|
+
descriptor: builtinWidgetDescriptors["label"],
|
|
1391
|
+
implementation: { kind: "trusted-react", component: Label }
|
|
1392
|
+
});
|
|
1446
1393
|
|
|
1447
1394
|
// src/widgets/latest-card.tsx
|
|
1448
|
-
import {
|
|
1449
|
-
|
|
1395
|
+
import {
|
|
1396
|
+
css as css7,
|
|
1397
|
+
defineWidget as defineWidget8,
|
|
1398
|
+
flashTextStyle,
|
|
1399
|
+
formatTimestamp,
|
|
1400
|
+
formatValue,
|
|
1401
|
+
optBoolean as optBoolean3,
|
|
1402
|
+
optNumber as optNumber5,
|
|
1403
|
+
optString as optString9,
|
|
1404
|
+
pickSample as pickSample2,
|
|
1405
|
+
primaryMeasurementName as primaryMeasurementName2,
|
|
1406
|
+
useElementSize as useElementSize3,
|
|
1407
|
+
useFlashOnChange as useFlashOnChange2,
|
|
1408
|
+
useWidgetTimeZone as useWidgetTimeZone3,
|
|
1409
|
+
WidgetFrame as WidgetFrame8
|
|
1410
|
+
} from "@devicechain/widget-sdk";
|
|
1411
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1412
|
+
var clamp2 = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
1450
1413
|
function LatestCard({ widget, data }) {
|
|
1451
|
-
const [sizeRef, size] =
|
|
1452
|
-
const
|
|
1453
|
-
const
|
|
1454
|
-
const
|
|
1455
|
-
const
|
|
1456
|
-
const
|
|
1414
|
+
const [sizeRef, size] = useElementSize3();
|
|
1415
|
+
const timeZone = useWidgetTimeZone3();
|
|
1416
|
+
const name = primaryMeasurementName2(widget);
|
|
1417
|
+
const sample = pickSample2(data.latest, name);
|
|
1418
|
+
const title = optString9(widget.options, "title") ?? name;
|
|
1419
|
+
const unit = optString9(widget.options, "unit");
|
|
1420
|
+
const precision = optNumber5(widget.options, "precision");
|
|
1457
1421
|
const display = formatValue(sample?.value, precision);
|
|
1458
|
-
const flashDirection =
|
|
1422
|
+
const flashDirection = useFlashOnChange2(
|
|
1459
1423
|
sample?.value,
|
|
1460
|
-
|
|
1424
|
+
optBoolean3(widget.options, "flashOnChange"),
|
|
1461
1425
|
sample && `${sample.deviceToken}:${sample.name}`
|
|
1462
1426
|
);
|
|
1463
|
-
const valueFont = size.height > 0 ?
|
|
1464
|
-
return /* @__PURE__ */
|
|
1427
|
+
const valueFont = size.height > 0 ? clamp2(Math.round(size.height / 3.5), 16, 44) : 32;
|
|
1428
|
+
return /* @__PURE__ */ jsx8(WidgetFrame8, { title, children: /* @__PURE__ */ jsxs5(
|
|
1465
1429
|
"div",
|
|
1466
1430
|
{
|
|
1467
1431
|
ref: sizeRef,
|
|
@@ -1473,60 +1437,72 @@ function LatestCard({ widget, data }) {
|
|
|
1473
1437
|
padding: "12px 16px"
|
|
1474
1438
|
},
|
|
1475
1439
|
children: [
|
|
1476
|
-
/* @__PURE__ */
|
|
1440
|
+
/* @__PURE__ */ jsxs5(
|
|
1477
1441
|
"div",
|
|
1478
1442
|
{
|
|
1479
1443
|
style: {
|
|
1480
1444
|
fontSize: valueFont,
|
|
1481
1445
|
fontWeight: 600,
|
|
1482
1446
|
lineHeight: 1.1,
|
|
1483
|
-
color:
|
|
1447
|
+
color: css7("foreground"),
|
|
1484
1448
|
...flashTextStyle(flashDirection)
|
|
1485
1449
|
},
|
|
1486
1450
|
children: [
|
|
1487
1451
|
display,
|
|
1488
|
-
sample?.value != null && unit ? /* @__PURE__ */
|
|
1452
|
+
sample?.value != null && unit ? /* @__PURE__ */ jsx8("span", { style: { fontSize: Math.round(valueFont / 2), color: css7("muted-foreground"), marginLeft: 6 }, children: unit }) : null
|
|
1489
1453
|
]
|
|
1490
1454
|
}
|
|
1491
1455
|
),
|
|
1492
|
-
sample?.occurredTime ? /* @__PURE__ */
|
|
1456
|
+
sample?.occurredTime ? /* @__PURE__ */ jsx8("div", { style: { fontSize: 11, color: css7("muted-foreground"), marginTop: 4 }, children: formatTimestamp(sample.occurredTime, timeZone) }) : null
|
|
1493
1457
|
]
|
|
1494
1458
|
}
|
|
1495
1459
|
) });
|
|
1496
1460
|
}
|
|
1461
|
+
var latestCardWidget = defineWidget8({
|
|
1462
|
+
descriptor: builtinWidgetDescriptors["latest-card"],
|
|
1463
|
+
implementation: { kind: "trusted-react", component: LatestCard }
|
|
1464
|
+
});
|
|
1497
1465
|
|
|
1498
1466
|
// src/widgets/map.tsx
|
|
1499
|
-
import { useEffect as
|
|
1467
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
|
|
1500
1468
|
import { renderableBasemap, resolveBasemap } from "@devicechain/client";
|
|
1501
1469
|
|
|
1502
1470
|
// src/basemap-context.tsx
|
|
1503
|
-
import { createContext
|
|
1504
|
-
import { jsx as
|
|
1505
|
-
var TenantBasemapContext =
|
|
1471
|
+
import { createContext, useContext } from "react";
|
|
1472
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1473
|
+
var TenantBasemapContext = createContext(null);
|
|
1506
1474
|
function TenantBasemapProvider({
|
|
1507
1475
|
basemap,
|
|
1508
1476
|
children
|
|
1509
1477
|
}) {
|
|
1510
|
-
return /* @__PURE__ */
|
|
1478
|
+
return /* @__PURE__ */ jsx9(TenantBasemapContext.Provider, { value: basemap, children });
|
|
1511
1479
|
}
|
|
1512
1480
|
function useTenantBasemap() {
|
|
1513
|
-
return
|
|
1481
|
+
return useContext(TenantBasemapContext);
|
|
1514
1482
|
}
|
|
1515
1483
|
|
|
1516
1484
|
// src/map-runtime-context.tsx
|
|
1517
|
-
import { createContext as
|
|
1518
|
-
import { jsx as
|
|
1519
|
-
var MapRuntimeContext =
|
|
1485
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
1486
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1487
|
+
var MapRuntimeContext = createContext2(null);
|
|
1520
1488
|
function MapRuntimeProvider({
|
|
1521
1489
|
runtime,
|
|
1522
1490
|
children
|
|
1523
1491
|
}) {
|
|
1524
|
-
return /* @__PURE__ */
|
|
1492
|
+
return /* @__PURE__ */ jsx10(MapRuntimeContext.Provider, { value: runtime, children });
|
|
1525
1493
|
}
|
|
1526
1494
|
function useMapRuntime() {
|
|
1527
|
-
return
|
|
1495
|
+
return useContext2(MapRuntimeContext);
|
|
1528
1496
|
}
|
|
1529
1497
|
|
|
1498
|
+
// src/widgets/map.tsx
|
|
1499
|
+
import {
|
|
1500
|
+
css as css8,
|
|
1501
|
+
defineWidget as defineWidget9,
|
|
1502
|
+
optString as optString10,
|
|
1503
|
+
WidgetFrame as WidgetFrame9
|
|
1504
|
+
} from "@devicechain/widget-sdk";
|
|
1505
|
+
|
|
1530
1506
|
// src/widgets/map-geometry.ts
|
|
1531
1507
|
function placeable(samples) {
|
|
1532
1508
|
return samples.filter(
|
|
@@ -1620,7 +1596,7 @@ function loadLandStyle() {
|
|
|
1620
1596
|
}
|
|
1621
1597
|
|
|
1622
1598
|
// src/widgets/map.tsx
|
|
1623
|
-
import { jsx as
|
|
1599
|
+
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1624
1600
|
var mapLibrePromise;
|
|
1625
1601
|
function loadMapLibre(runtime) {
|
|
1626
1602
|
if (!mapLibrePromise) {
|
|
@@ -1636,20 +1612,20 @@ function loadMapLibre(runtime) {
|
|
|
1636
1612
|
}
|
|
1637
1613
|
var SINGLE_DEVICE_ZOOM = 14;
|
|
1638
1614
|
function MapWidget({ widget, data }) {
|
|
1639
|
-
const title =
|
|
1615
|
+
const title = optString10(widget.options, "title");
|
|
1640
1616
|
const positions = placeable(data.locations);
|
|
1641
1617
|
const runtime = useMapRuntime();
|
|
1642
1618
|
const basemap = renderableBasemap(
|
|
1643
1619
|
resolveBasemap(
|
|
1644
1620
|
{
|
|
1645
|
-
tileUrl:
|
|
1646
|
-
attribution:
|
|
1621
|
+
tileUrl: optString10(widget.options, "tileUrl"),
|
|
1622
|
+
attribution: optString10(widget.options, "attribution")
|
|
1647
1623
|
},
|
|
1648
1624
|
useTenantBasemap()
|
|
1649
1625
|
)
|
|
1650
1626
|
);
|
|
1651
1627
|
if (data.forbidden) {
|
|
1652
|
-
return /* @__PURE__ */
|
|
1628
|
+
return /* @__PURE__ */ jsx11(WidgetFrame9, { title, children: /* @__PURE__ */ jsx11(
|
|
1653
1629
|
MapNotice,
|
|
1654
1630
|
{
|
|
1655
1631
|
glyph: "\u{1F512}",
|
|
@@ -1659,7 +1635,7 @@ function MapWidget({ widget, data }) {
|
|
|
1659
1635
|
) });
|
|
1660
1636
|
}
|
|
1661
1637
|
if (!runtime) {
|
|
1662
|
-
return /* @__PURE__ */
|
|
1638
|
+
return /* @__PURE__ */ jsx11(WidgetFrame9, { title, children: /* @__PURE__ */ jsx11(
|
|
1663
1639
|
MapNotice,
|
|
1664
1640
|
{
|
|
1665
1641
|
glyph: "\u{1F9E9}",
|
|
@@ -1669,10 +1645,10 @@ function MapWidget({ widget, data }) {
|
|
|
1669
1645
|
) });
|
|
1670
1646
|
}
|
|
1671
1647
|
if (data.loading && positions.length === 0) {
|
|
1672
|
-
return /* @__PURE__ */
|
|
1648
|
+
return /* @__PURE__ */ jsx11(WidgetFrame9, { title, children: /* @__PURE__ */ jsx11(MapNotice, { heading: "Loading\u2026" }) });
|
|
1673
1649
|
}
|
|
1674
1650
|
if (positions.length === 0) {
|
|
1675
|
-
return /* @__PURE__ */
|
|
1651
|
+
return /* @__PURE__ */ jsx11(WidgetFrame9, { title, children: /* @__PURE__ */ jsx11(
|
|
1676
1652
|
MapNotice,
|
|
1677
1653
|
{
|
|
1678
1654
|
heading: data.deviceTokens.length === 0 ? "No devices selected" : "No positions reported",
|
|
@@ -1680,11 +1656,11 @@ function MapWidget({ widget, data }) {
|
|
|
1680
1656
|
}
|
|
1681
1657
|
) });
|
|
1682
1658
|
}
|
|
1683
|
-
return /* @__PURE__ */
|
|
1659
|
+
return /* @__PURE__ */ jsx11(WidgetFrame9, { title, bodyStyle: { padding: 0 }, children: /* @__PURE__ */ jsx11(LiveMap, { basemap, positions, runtime }) });
|
|
1684
1660
|
}
|
|
1685
1661
|
function PlainPanel({ positions, note }) {
|
|
1686
1662
|
const points = projectToPanel(positions);
|
|
1687
|
-
return /* @__PURE__ */
|
|
1663
|
+
return /* @__PURE__ */ jsxs6(
|
|
1688
1664
|
"div",
|
|
1689
1665
|
{
|
|
1690
1666
|
"data-testid": "map-plain-panel",
|
|
@@ -1693,10 +1669,10 @@ function PlainPanel({ positions, note }) {
|
|
|
1693
1669
|
width: "100%",
|
|
1694
1670
|
height: "100%",
|
|
1695
1671
|
overflow: "hidden",
|
|
1696
|
-
background:
|
|
1672
|
+
background: css8("muted")
|
|
1697
1673
|
},
|
|
1698
1674
|
children: [
|
|
1699
|
-
positions.map((position, i) => /* @__PURE__ */
|
|
1675
|
+
positions.map((position, i) => /* @__PURE__ */ jsx11(
|
|
1700
1676
|
Marker,
|
|
1701
1677
|
{
|
|
1702
1678
|
position,
|
|
@@ -1709,7 +1685,7 @@ function PlainPanel({ positions, note }) {
|
|
|
1709
1685
|
},
|
|
1710
1686
|
position.deviceToken
|
|
1711
1687
|
)),
|
|
1712
|
-
/* @__PURE__ */
|
|
1688
|
+
/* @__PURE__ */ jsx11(
|
|
1713
1689
|
"div",
|
|
1714
1690
|
{
|
|
1715
1691
|
"data-testid": "map-no-tiles-note",
|
|
@@ -1722,8 +1698,8 @@ function PlainPanel({ positions, note }) {
|
|
|
1722
1698
|
fontSize: 10,
|
|
1723
1699
|
lineHeight: 1.3,
|
|
1724
1700
|
textAlign: "center",
|
|
1725
|
-
color:
|
|
1726
|
-
background:
|
|
1701
|
+
color: css8("muted-foreground"),
|
|
1702
|
+
background: css8("card"),
|
|
1727
1703
|
opacity: 0.9
|
|
1728
1704
|
},
|
|
1729
1705
|
children: note
|
|
@@ -1738,13 +1714,13 @@ function LiveMap({
|
|
|
1738
1714
|
positions,
|
|
1739
1715
|
runtime
|
|
1740
1716
|
}) {
|
|
1741
|
-
const containerRef =
|
|
1742
|
-
const mapRef =
|
|
1743
|
-
const markersRef =
|
|
1744
|
-
const [status, setStatus] =
|
|
1717
|
+
const containerRef = useRef2(null);
|
|
1718
|
+
const mapRef = useRef2(null);
|
|
1719
|
+
const markersRef = useRef2([]);
|
|
1720
|
+
const [status, setStatus] = useState4("loading");
|
|
1745
1721
|
const tileUrl = basemap?.tileUrl ?? null;
|
|
1746
1722
|
const attribution = basemap?.attribution ?? null;
|
|
1747
|
-
|
|
1723
|
+
useEffect3(() => {
|
|
1748
1724
|
let cancelled = false;
|
|
1749
1725
|
setStatus("loading");
|
|
1750
1726
|
Promise.all([
|
|
@@ -1772,7 +1748,7 @@ function LiveMap({
|
|
|
1772
1748
|
mapRef.current = null;
|
|
1773
1749
|
};
|
|
1774
1750
|
}, [tileUrl, attribution, runtime.workerUrl]);
|
|
1775
|
-
|
|
1751
|
+
useEffect3(() => {
|
|
1776
1752
|
const map = mapRef.current;
|
|
1777
1753
|
if (status !== "ready" || !map) return;
|
|
1778
1754
|
let live = true;
|
|
@@ -1801,7 +1777,7 @@ function LiveMap({
|
|
|
1801
1777
|
};
|
|
1802
1778
|
}, [status, positionsKey(positions)]);
|
|
1803
1779
|
if (status === "unavailable") {
|
|
1804
|
-
return /* @__PURE__ */
|
|
1780
|
+
return /* @__PURE__ */ jsx11(
|
|
1805
1781
|
PlainPanel,
|
|
1806
1782
|
{
|
|
1807
1783
|
positions,
|
|
@@ -1809,7 +1785,7 @@ function LiveMap({
|
|
|
1809
1785
|
}
|
|
1810
1786
|
);
|
|
1811
1787
|
}
|
|
1812
|
-
return /* @__PURE__ */
|
|
1788
|
+
return /* @__PURE__ */ jsx11("div", { ref: containerRef, "data-testid": "map-canvas", style: { width: "100%", height: "100%" } });
|
|
1813
1789
|
}
|
|
1814
1790
|
function positionsKey(positions) {
|
|
1815
1791
|
return positions.map((p) => `${p.deviceToken}:${p.latitude}:${p.longitude}:${p.heading ?? ""}`).join("|");
|
|
@@ -1818,8 +1794,8 @@ var MARKER_DOT_STYLE = {
|
|
|
1818
1794
|
width: "12px",
|
|
1819
1795
|
height: "12px",
|
|
1820
1796
|
borderRadius: "50%",
|
|
1821
|
-
background:
|
|
1822
|
-
border: `2px solid ${
|
|
1797
|
+
background: css8("primary"),
|
|
1798
|
+
border: `2px solid ${css8("card")}`,
|
|
1823
1799
|
boxShadow: "0 0 0 1px rgba(0,0,0,0.25)",
|
|
1824
1800
|
cursor: "default"
|
|
1825
1801
|
};
|
|
@@ -1828,7 +1804,7 @@ function Marker({
|
|
|
1828
1804
|
style
|
|
1829
1805
|
}) {
|
|
1830
1806
|
const label = describePosition(position);
|
|
1831
|
-
return /* @__PURE__ */
|
|
1807
|
+
return /* @__PURE__ */ jsx11(
|
|
1832
1808
|
"div",
|
|
1833
1809
|
{
|
|
1834
1810
|
"data-testid": "map-marker",
|
|
@@ -1840,7 +1816,7 @@ function Marker({
|
|
|
1840
1816
|
);
|
|
1841
1817
|
}
|
|
1842
1818
|
function MapNotice({ glyph, heading, detail }) {
|
|
1843
|
-
return /* @__PURE__ */
|
|
1819
|
+
return /* @__PURE__ */ jsxs6(
|
|
1844
1820
|
"div",
|
|
1845
1821
|
{
|
|
1846
1822
|
style: {
|
|
@@ -1854,19 +1830,34 @@ function MapNotice({ glyph, heading, detail }) {
|
|
|
1854
1830
|
padding: 12,
|
|
1855
1831
|
boxSizing: "border-box",
|
|
1856
1832
|
textAlign: "center",
|
|
1857
|
-
color:
|
|
1833
|
+
color: css8("muted-foreground")
|
|
1858
1834
|
},
|
|
1859
1835
|
children: [
|
|
1860
|
-
glyph ? /* @__PURE__ */
|
|
1861
|
-
/* @__PURE__ */
|
|
1862
|
-
detail ? /* @__PURE__ */
|
|
1836
|
+
glyph ? /* @__PURE__ */ jsx11("div", { style: { fontSize: 20, lineHeight: 1 }, "aria-hidden": "true", children: glyph }) : null,
|
|
1837
|
+
/* @__PURE__ */ jsx11("div", { style: { fontSize: 13, fontWeight: 600 }, children: heading }),
|
|
1838
|
+
detail ? /* @__PURE__ */ jsx11("div", { style: { fontSize: 11, opacity: 0.8 }, children: detail }) : null
|
|
1863
1839
|
]
|
|
1864
1840
|
}
|
|
1865
1841
|
);
|
|
1866
1842
|
}
|
|
1843
|
+
var mapWidget = defineWidget9({
|
|
1844
|
+
descriptor: builtinWidgetDescriptors["map"],
|
|
1845
|
+
implementation: { kind: "trusted-react", component: MapWidget }
|
|
1846
|
+
});
|
|
1867
1847
|
|
|
1868
1848
|
// src/widgets/table.tsx
|
|
1869
|
-
import {
|
|
1849
|
+
import {
|
|
1850
|
+
css as css9,
|
|
1851
|
+
defineWidget as defineWidget10,
|
|
1852
|
+
FlashValue as FlashValue2,
|
|
1853
|
+
formatTimestamp as formatTimestamp2,
|
|
1854
|
+
optBoolean as optBoolean4,
|
|
1855
|
+
optNumber as optNumber6,
|
|
1856
|
+
optString as optString11,
|
|
1857
|
+
useWidgetTimeZone as useWidgetTimeZone4,
|
|
1858
|
+
WidgetFrame as WidgetFrame10
|
|
1859
|
+
} from "@devicechain/widget-sdk";
|
|
1860
|
+
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1870
1861
|
var cell2 = { padding: "6px 12px", textAlign: "left" };
|
|
1871
1862
|
var headCell2 = {
|
|
1872
1863
|
...cell2,
|
|
@@ -1874,23 +1865,24 @@ var headCell2 = {
|
|
|
1874
1865
|
fontWeight: 600,
|
|
1875
1866
|
textTransform: "uppercase",
|
|
1876
1867
|
letterSpacing: "0.04em",
|
|
1877
|
-
color:
|
|
1878
|
-
borderBottom: `1px solid ${
|
|
1868
|
+
color: css9("muted-foreground"),
|
|
1869
|
+
borderBottom: `1px solid ${css9("border")}`
|
|
1879
1870
|
};
|
|
1880
1871
|
function Table({ widget, data }) {
|
|
1872
|
+
const timeZone = useWidgetTimeZone4();
|
|
1881
1873
|
const rows = Object.values(data.latest).sort((a, b) => a.name.localeCompare(b.name));
|
|
1882
|
-
const precision =
|
|
1883
|
-
const flash =
|
|
1884
|
-
return /* @__PURE__ */
|
|
1885
|
-
/* @__PURE__ */
|
|
1886
|
-
/* @__PURE__ */
|
|
1887
|
-
/* @__PURE__ */
|
|
1888
|
-
/* @__PURE__ */
|
|
1874
|
+
const precision = optNumber6(widget.options, "precision");
|
|
1875
|
+
const flash = optBoolean4(widget.options, "flashOnChange");
|
|
1876
|
+
return /* @__PURE__ */ jsx12(WidgetFrame10, { title: optString11(widget.options, "title"), children: /* @__PURE__ */ jsx12("div", { style: { height: "100%", overflow: "auto" }, children: /* @__PURE__ */ jsxs7("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: 13, color: css9("foreground") }, children: [
|
|
1877
|
+
/* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsxs7("tr", { children: [
|
|
1878
|
+
/* @__PURE__ */ jsx12("th", { style: headCell2, children: "Measurement" }),
|
|
1879
|
+
/* @__PURE__ */ jsx12("th", { style: headCell2, children: "Value" }),
|
|
1880
|
+
/* @__PURE__ */ jsx12("th", { style: headCell2, children: "Time" })
|
|
1889
1881
|
] }) }),
|
|
1890
|
-
/* @__PURE__ */
|
|
1891
|
-
/* @__PURE__ */
|
|
1892
|
-
/* @__PURE__ */
|
|
1893
|
-
|
|
1882
|
+
/* @__PURE__ */ jsx12("tbody", { children: rows.length === 0 ? /* @__PURE__ */ jsx12("tr", { children: /* @__PURE__ */ jsx12("td", { colSpan: 3, style: { ...cell2, textAlign: "center", color: css9("muted-foreground") }, children: "No data yet" }) }) : rows.map((row) => /* @__PURE__ */ jsxs7("tr", { children: [
|
|
1883
|
+
/* @__PURE__ */ jsx12("td", { style: cell2, children: row.name }),
|
|
1884
|
+
/* @__PURE__ */ jsx12("td", { style: { ...cell2, fontVariantNumeric: "tabular-nums" }, children: /* @__PURE__ */ jsx12(
|
|
1885
|
+
FlashValue2,
|
|
1894
1886
|
{
|
|
1895
1887
|
value: row.value,
|
|
1896
1888
|
precision,
|
|
@@ -1898,176 +1890,361 @@ function Table({ widget, data }) {
|
|
|
1898
1890
|
identity: `${row.deviceToken}:${row.name}`
|
|
1899
1891
|
}
|
|
1900
1892
|
) }),
|
|
1901
|
-
/* @__PURE__ */
|
|
1893
|
+
/* @__PURE__ */ jsx12("td", { style: { ...cell2, color: css9("muted-foreground") }, children: formatTimestamp2(row.occurredTime, timeZone) })
|
|
1902
1894
|
] }, row.name)) })
|
|
1903
1895
|
] }) }) });
|
|
1904
1896
|
}
|
|
1897
|
+
var tableWidget = defineWidget10({
|
|
1898
|
+
descriptor: builtinWidgetDescriptors["table"],
|
|
1899
|
+
implementation: { kind: "trusted-react", component: Table }
|
|
1900
|
+
});
|
|
1905
1901
|
|
|
1906
1902
|
// src/widgets/time-series-chart.tsx
|
|
1907
|
-
import { useMemo as
|
|
1908
|
-
import {
|
|
1903
|
+
import { useMemo as useMemo3 } from "react";
|
|
1904
|
+
import {
|
|
1905
|
+
defineWidget as defineWidget11,
|
|
1906
|
+
optString as optString12,
|
|
1907
|
+
useElementSize as useElementSize4,
|
|
1908
|
+
WidgetFrame as WidgetFrame11
|
|
1909
|
+
} from "@devicechain/widget-sdk";
|
|
1910
|
+
import { buildLineOption, EChart as EChart2, useChartTheme as useChartTheme2 } from "@devicechain/widget-sdk/echarts";
|
|
1911
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1909
1912
|
function TimeSeriesChart({ widget, data }) {
|
|
1910
|
-
const theme =
|
|
1911
|
-
const [sizeRef, size] =
|
|
1913
|
+
const theme = useChartTheme2();
|
|
1914
|
+
const [sizeRef, size] = useElementSize4();
|
|
1912
1915
|
const measurements = widget.datasource?.measurements;
|
|
1913
|
-
const option =
|
|
1916
|
+
const option = useMemo3(
|
|
1914
1917
|
() => buildLineOption(data.samples, theme, { measurements }, size),
|
|
1915
1918
|
[data.samples, theme, measurements, size.width, size.height]
|
|
1916
1919
|
);
|
|
1917
|
-
return /* @__PURE__ */
|
|
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 }) }) });
|
|
1918
1921
|
}
|
|
1922
|
+
var timeSeriesChartWidget = defineWidget11({
|
|
1923
|
+
descriptor: builtinWidgetDescriptors["timeseries-chart"],
|
|
1924
|
+
implementation: { kind: "trusted-react", component: TimeSeriesChart }
|
|
1925
|
+
});
|
|
1919
1926
|
|
|
1920
|
-
// src/registry.ts
|
|
1921
|
-
var
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1927
|
+
// src/builtin-registry.ts
|
|
1928
|
+
var builtinWidgetDefinitions = [
|
|
1929
|
+
timeSeriesChartWidget,
|
|
1930
|
+
latestCardWidget,
|
|
1931
|
+
gaugeWidget,
|
|
1932
|
+
tableWidget,
|
|
1933
|
+
labelWidget,
|
|
1934
|
+
imageWidget,
|
|
1935
|
+
alarmTableWidget,
|
|
1936
|
+
alarmCountWidget,
|
|
1937
|
+
commandButtonWidget,
|
|
1938
|
+
entitySelectorWidget,
|
|
1939
|
+
mapWidget
|
|
1940
|
+
];
|
|
1941
|
+
function createBuiltinWidgetRegistry() {
|
|
1942
|
+
return createWidgetRegistry(builtinWidgetDefinitions);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
// src/default-registry.ts
|
|
1946
|
+
var builtin;
|
|
1947
|
+
function defaultWidgetRegistry() {
|
|
1948
|
+
builtin ?? (builtin = createBuiltinWidgetRegistry());
|
|
1949
|
+
return builtin;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// src/connected-widget.tsx
|
|
1953
|
+
import {
|
|
1954
|
+
optString as optString13,
|
|
1955
|
+
useAlarmStream,
|
|
1956
|
+
useCommandStream,
|
|
1957
|
+
useDatasourceAvailability,
|
|
1958
|
+
useLocationStream,
|
|
1959
|
+
useMeasurementStream,
|
|
1960
|
+
useWidgetCandidates,
|
|
1961
|
+
useWidgetSelect as useWidgetSelect3,
|
|
1962
|
+
WidgetFrame as WidgetFrame13
|
|
1963
|
+
} from "@devicechain/widget-sdk";
|
|
1964
|
+
|
|
1965
|
+
// src/widget-containment.tsx
|
|
1966
|
+
import { Component, createContext as createContext3, useCallback, useContext as useContext3, useState as useState5 } from "react";
|
|
1967
|
+
import { WidgetFrame as WidgetFrame12 } from "@devicechain/widget-sdk";
|
|
1968
|
+
|
|
1969
|
+
// src/widget-host.ts
|
|
1970
|
+
function describeError(error) {
|
|
1971
|
+
if (error instanceof Error) return error.message || error.name;
|
|
1972
|
+
return String(error);
|
|
1973
|
+
}
|
|
1974
|
+
function guardAsync(fn, report) {
|
|
1975
|
+
return (...args) => {
|
|
1976
|
+
let pending;
|
|
1977
|
+
try {
|
|
1978
|
+
pending = Promise.resolve(fn(...args));
|
|
1979
|
+
} catch (e) {
|
|
1980
|
+
report(describeError(e));
|
|
1981
|
+
return Promise.reject(e);
|
|
1982
|
+
}
|
|
1983
|
+
return pending.catch((e) => {
|
|
1984
|
+
report(describeError(e));
|
|
1985
|
+
throw e;
|
|
1986
|
+
});
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
var ALARM_AUTHORITY = "alarm:write";
|
|
1990
|
+
var COMMAND_AUTHORITY = "command:write";
|
|
1991
|
+
function undeclared(capability, report) {
|
|
1992
|
+
return guardAsync(
|
|
1993
|
+
() => Promise.reject(new Error(`this widget did not declare the ${capability} interaction`)),
|
|
1994
|
+
report
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
function narrowActions(actions, descriptor, report) {
|
|
1998
|
+
if (!actions) return void 0;
|
|
1999
|
+
const alarm = descriptor.interactions.alarmActions === true;
|
|
2000
|
+
const command = descriptor.interactions.commandSend === true;
|
|
2001
|
+
if (!alarm && !command) return void 0;
|
|
2002
|
+
const narrowed = {
|
|
2003
|
+
acknowledgeAlarm: alarm ? guardAsync((t) => actions.acknowledgeAlarm(t), report) : undeclared("alarmActions", report),
|
|
2004
|
+
clearAlarm: alarm ? guardAsync((t) => actions.clearAlarm(t), report) : undeclared("alarmActions", report),
|
|
2005
|
+
can: (authority) => {
|
|
2006
|
+
if (authority === ALARM_AUTHORITY && !alarm) return false;
|
|
2007
|
+
if (authority === COMMAND_AUTHORITY && !command) return false;
|
|
2008
|
+
return actions.can(authority);
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
if (command && typeof actions.sendCommand === "function") {
|
|
2012
|
+
narrowed.sendCommand = guardAsync((d, n, p) => actions.sendCommand(d, n, p), report);
|
|
2013
|
+
}
|
|
2014
|
+
return narrowed;
|
|
2015
|
+
}
|
|
2016
|
+
function narrowSelect(select, descriptor) {
|
|
2017
|
+
return descriptor.interactions.selection === true ? select : void 0;
|
|
2018
|
+
}
|
|
2019
|
+
function narrowCandidates(candidates, descriptor, report) {
|
|
2020
|
+
if (!candidates || descriptor.interactions.candidates !== true) return void 0;
|
|
2021
|
+
return guardAsync((slot) => candidates(slot), report);
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// src/widget-containment.tsx
|
|
2025
|
+
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2026
|
+
var MESSAGE_STYLE = {
|
|
2027
|
+
display: "flex",
|
|
2028
|
+
flexDirection: "column",
|
|
2029
|
+
alignItems: "center",
|
|
2030
|
+
justifyContent: "center",
|
|
2031
|
+
gap: 4,
|
|
2032
|
+
width: "100%",
|
|
2033
|
+
height: "100%",
|
|
2034
|
+
padding: 12,
|
|
2035
|
+
boxSizing: "border-box",
|
|
2036
|
+
textAlign: "center",
|
|
2037
|
+
color: "hsl(var(--muted-foreground))"
|
|
1951
2038
|
};
|
|
1952
|
-
var
|
|
1953
|
-
|
|
2039
|
+
var BUTTON_STYLE = {
|
|
2040
|
+
marginTop: 4,
|
|
2041
|
+
fontSize: 11,
|
|
2042
|
+
padding: "2px 8px",
|
|
2043
|
+
cursor: "pointer"
|
|
1954
2044
|
};
|
|
1955
|
-
var
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
2045
|
+
var ReportContext = createContext3(() => {
|
|
2046
|
+
});
|
|
2047
|
+
function useReportRejection() {
|
|
2048
|
+
return useContext3(ReportContext);
|
|
2049
|
+
}
|
|
2050
|
+
var WidgetErrorBoundary = class extends Component {
|
|
2051
|
+
constructor() {
|
|
2052
|
+
super(...arguments);
|
|
2053
|
+
this.state = { error: null, resetKey: this.props.resetKey };
|
|
2054
|
+
this.reset = () => {
|
|
2055
|
+
this.props.onReset();
|
|
2056
|
+
this.setState({ error: null });
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
static getDerivedStateFromError(error) {
|
|
2060
|
+
return { error: { message: describeError(error) } };
|
|
2061
|
+
}
|
|
2062
|
+
static getDerivedStateFromProps(props, state) {
|
|
2063
|
+
if (props.resetKey === state.resetKey) return null;
|
|
2064
|
+
return { error: null, resetKey: props.resetKey };
|
|
2065
|
+
}
|
|
2066
|
+
render() {
|
|
2067
|
+
const { error } = this.state;
|
|
2068
|
+
if (!error) return this.props.children;
|
|
2069
|
+
return /* @__PURE__ */ jsx14(WidgetFrame12, { title: this.props.title, children: /* @__PURE__ */ jsxs8("div", { style: MESSAGE_STYLE, role: "alert", children: [
|
|
2070
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 20, lineHeight: 1 }, "aria-hidden": "true", children: "\u26A0" }),
|
|
2071
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 13, fontWeight: 600 }, children: "This widget failed to render" }),
|
|
2072
|
+
/* @__PURE__ */ jsx14("div", { style: { fontSize: 11, opacity: 0.8, wordBreak: "break-word" }, children: error.message }),
|
|
2073
|
+
/* @__PURE__ */ jsx14("button", { type: "button", style: BUTTON_STYLE, onClick: this.reset, children: "Reset widget" })
|
|
2074
|
+
] }) });
|
|
2075
|
+
}
|
|
1969
2076
|
};
|
|
2077
|
+
function WidgetContainment({ title, resetKey, children }) {
|
|
2078
|
+
const [messages, setMessages] = useState5([]);
|
|
2079
|
+
const report = useCallback((message) => {
|
|
2080
|
+
setMessages((prev) => prev.includes(message) ? prev : [...prev, message].slice(-3));
|
|
2081
|
+
}, []);
|
|
2082
|
+
const clear = useCallback(() => setMessages([]), []);
|
|
2083
|
+
return /* @__PURE__ */ jsxs8("div", { style: { position: "relative", width: "100%", height: "100%" }, children: [
|
|
2084
|
+
/* @__PURE__ */ jsx14(ReportContext.Provider, { value: report, children: /* @__PURE__ */ jsx14(WidgetErrorBoundary, { title, resetKey, onReset: clear, children }) }),
|
|
2085
|
+
messages.length > 0 && /* @__PURE__ */ jsxs8(
|
|
2086
|
+
"div",
|
|
2087
|
+
{
|
|
2088
|
+
role: "status",
|
|
2089
|
+
style: {
|
|
2090
|
+
position: "absolute",
|
|
2091
|
+
left: 4,
|
|
2092
|
+
right: 4,
|
|
2093
|
+
bottom: 4,
|
|
2094
|
+
fontSize: 11,
|
|
2095
|
+
padding: "2px 6px",
|
|
2096
|
+
borderRadius: 4,
|
|
2097
|
+
background: "hsl(var(--destructive, 0 70% 45%))",
|
|
2098
|
+
color: "hsl(var(--destructive-foreground, 0 0% 100%))",
|
|
2099
|
+
display: "flex",
|
|
2100
|
+
gap: 6,
|
|
2101
|
+
alignItems: "center"
|
|
2102
|
+
},
|
|
2103
|
+
children: [
|
|
2104
|
+
/* @__PURE__ */ jsx14("span", { style: { flex: 1, wordBreak: "break-word" }, children: messages.join(" \xB7 ") }),
|
|
2105
|
+
/* @__PURE__ */ jsx14("button", { type: "button", "aria-label": "Dismiss", onClick: clear, style: { cursor: "pointer" }, children: "\xD7" })
|
|
2106
|
+
]
|
|
2107
|
+
}
|
|
2108
|
+
)
|
|
2109
|
+
] });
|
|
2110
|
+
}
|
|
1970
2111
|
|
|
1971
2112
|
// src/connected-widget.tsx
|
|
1972
|
-
import { jsx as
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
const
|
|
2113
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2114
|
+
function ConnectedWidget(props) {
|
|
2115
|
+
const { widget } = props;
|
|
2116
|
+
const registry = props.registry ?? defaultWidgetRegistry();
|
|
2117
|
+
const resetKey = JSON.stringify([widget.type, widget.options ?? null, widget.datasource ?? null]);
|
|
2118
|
+
return /* @__PURE__ */ jsx15(WidgetContainment, { title: optString13(widget.options, "title"), resetKey, children: /* @__PURE__ */ jsx15(ConnectedWidgetBody, { ...props, registry }) });
|
|
2119
|
+
}
|
|
2120
|
+
function ConnectedWidgetBody({ widget, hub, registry, actions, initialSamples }) {
|
|
2121
|
+
const unresolved = widget.type === UNKNOWN_WIDGET_TYPE;
|
|
2122
|
+
const descriptor = unresolved ? void 0 : registry.descriptor(widget.type);
|
|
2123
|
+
const implementation = unresolved ? void 0 : registry.implementation(widget.type);
|
|
2124
|
+
const readsDatasource = descriptor !== void 0 && descriptor.datasource.mode !== "none";
|
|
2125
|
+
const availability = useDatasourceAvailability(hub, readsDatasource ? widget.datasource : void 0);
|
|
1977
2126
|
if (availability === "unavailable") {
|
|
1978
|
-
return /* @__PURE__ */
|
|
2127
|
+
return /* @__PURE__ */ jsx15(WidgetUnavailableFrame, { widget });
|
|
1979
2128
|
}
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
return /* @__PURE__ */ jsx17(AlarmConnectedWidget, { widget, hub, actions });
|
|
2129
|
+
if (widget.type === UNKNOWN_WIDGET_TYPE) {
|
|
2130
|
+
return /* @__PURE__ */ jsx15(UnresolvedWidgetFrame, { storedType: widget.raw.type, reason: widget.reason });
|
|
1983
2131
|
}
|
|
1984
|
-
if (
|
|
1985
|
-
return /* @__PURE__ */
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2132
|
+
if (!descriptor || !implementation) {
|
|
2133
|
+
return /* @__PURE__ */ jsx15(
|
|
2134
|
+
UnresolvedWidgetFrame,
|
|
2135
|
+
{
|
|
2136
|
+
storedType: widget.type,
|
|
2137
|
+
reason: { code: "unregistered", message: `"${widget.type}" is not registered in this host's widget registry` }
|
|
2138
|
+
}
|
|
2139
|
+
);
|
|
1989
2140
|
}
|
|
1990
|
-
|
|
1991
|
-
|
|
2141
|
+
return /* @__PURE__ */ jsx15(HostScope, { descriptor, actions, children: (hostActions) => /* @__PURE__ */ jsx15(
|
|
2142
|
+
ChannelBody,
|
|
2143
|
+
{
|
|
2144
|
+
widget,
|
|
2145
|
+
hub,
|
|
2146
|
+
descriptor,
|
|
2147
|
+
Component: implementation.component,
|
|
2148
|
+
actions: hostActions,
|
|
2149
|
+
initialSamples
|
|
2150
|
+
}
|
|
2151
|
+
) });
|
|
2152
|
+
}
|
|
2153
|
+
function HostScope({
|
|
2154
|
+
descriptor,
|
|
2155
|
+
actions,
|
|
2156
|
+
children
|
|
2157
|
+
}) {
|
|
2158
|
+
const report = useReportRejection();
|
|
2159
|
+
const select = useWidgetSelect3();
|
|
2160
|
+
const candidates = useWidgetCandidates();
|
|
2161
|
+
const narrowed = useMemo4(() => narrowActions(actions, descriptor, report), [actions, descriptor, report]);
|
|
2162
|
+
const narrowedSelect = useMemo4(() => narrowSelect(select, descriptor), [select, descriptor]);
|
|
2163
|
+
const narrowedCandidates = useMemo4(
|
|
2164
|
+
() => narrowCandidates(candidates, descriptor, report),
|
|
2165
|
+
[candidates, descriptor, report]
|
|
2166
|
+
);
|
|
2167
|
+
return /* @__PURE__ */ jsx15(WidgetSelectProvider, { select: narrowedSelect, children: /* @__PURE__ */ jsx15(WidgetCandidatesProvider, { candidates: narrowedCandidates, children: children(narrowed) }) });
|
|
2168
|
+
}
|
|
2169
|
+
function ChannelBody(props) {
|
|
2170
|
+
switch (props.descriptor.channel) {
|
|
2171
|
+
case "alarm":
|
|
2172
|
+
return /* @__PURE__ */ jsx15(AlarmConnectedWidget, { ...props });
|
|
2173
|
+
case "control":
|
|
2174
|
+
return /* @__PURE__ */ jsx15(ControlConnectedWidget, { ...props });
|
|
2175
|
+
case "location":
|
|
2176
|
+
return /* @__PURE__ */ jsx15(LocationConnectedWidget, { ...props });
|
|
2177
|
+
case "selection":
|
|
2178
|
+
case "none":
|
|
2179
|
+
return /* @__PURE__ */ jsx15(StaticConnectedWidget, { ...props });
|
|
2180
|
+
case "measurement":
|
|
2181
|
+
return /* @__PURE__ */ jsx15(MeasurementConnectedWidget, { ...props });
|
|
1992
2182
|
}
|
|
1993
|
-
return /* @__PURE__ */ jsx17(MeasurementConnectedWidget, { widget, hub, initialSamples });
|
|
1994
2183
|
}
|
|
1995
|
-
function
|
|
1996
|
-
|
|
1997
|
-
if (!Component) return null;
|
|
1998
|
-
return /* @__PURE__ */ jsx17(Component, { widget, data: null });
|
|
2184
|
+
function StaticConnectedWidget({ widget, Component: Component2, actions }) {
|
|
2185
|
+
return /* @__PURE__ */ jsx15(Component2, { widget, data: null, actions });
|
|
1999
2186
|
}
|
|
2000
|
-
function MeasurementConnectedWidget({ widget, hub, initialSamples }) {
|
|
2187
|
+
function MeasurementConnectedWidget({ widget, hub, descriptor, Component: Component2, actions, initialSamples }) {
|
|
2001
2188
|
const data = useMeasurementStream(hub, widget.datasource, {
|
|
2002
|
-
window:
|
|
2189
|
+
window: measurementWindow(descriptor, widget),
|
|
2003
2190
|
initialSamples
|
|
2004
2191
|
});
|
|
2005
|
-
if (data.error) return /* @__PURE__ */
|
|
2006
|
-
|
|
2007
|
-
if (!Component) return null;
|
|
2008
|
-
return /* @__PURE__ */ jsx17(Component, { widget, data });
|
|
2192
|
+
if (data.error) return /* @__PURE__ */ jsx15(WidgetErrorFrame, { widget, error: data.error });
|
|
2193
|
+
return /* @__PURE__ */ jsx15(Component2, { widget, data, actions });
|
|
2009
2194
|
}
|
|
2010
|
-
function AlarmConnectedWidget({
|
|
2011
|
-
widget
|
|
2012
|
-
hub,
|
|
2013
|
-
actions
|
|
2014
|
-
}) {
|
|
2015
|
-
const data = useAlarmStream(hub, alarmSubscription(widget));
|
|
2195
|
+
function AlarmConnectedWidget({ widget, hub, descriptor, Component: Component2, actions }) {
|
|
2196
|
+
const data = useAlarmStream(hub, alarmSubscription(descriptor, widget));
|
|
2016
2197
|
if (data.error && data.alarms.length === 0 && data.total === 0) {
|
|
2017
|
-
return /* @__PURE__ */
|
|
2198
|
+
return /* @__PURE__ */ jsx15(WidgetErrorFrame, { widget, error: data.error });
|
|
2018
2199
|
}
|
|
2019
|
-
|
|
2020
|
-
if (!Component) return null;
|
|
2021
|
-
return /* @__PURE__ */ jsx17(Component, { widget, data, actions });
|
|
2200
|
+
return /* @__PURE__ */ jsx15(Component2, { widget, data, actions });
|
|
2022
2201
|
}
|
|
2023
|
-
function ControlConnectedWidget({
|
|
2024
|
-
widget
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
}) {
|
|
2028
|
-
const data =
|
|
2029
|
-
const Component = CONTROL_WIDGET_REGISTRY[widget.type];
|
|
2030
|
-
if (!Component) return null;
|
|
2031
|
-
return /* @__PURE__ */ jsx17(Component, { widget, data, actions });
|
|
2032
|
-
}
|
|
2033
|
-
function LocationConnectedWidget({
|
|
2034
|
-
widget,
|
|
2035
|
-
hub
|
|
2036
|
-
}) {
|
|
2037
|
-
const data = useLocationStream(hub, locationSubscription(widget));
|
|
2202
|
+
function ControlConnectedWidget({ widget, hub, descriptor, Component: Component2, actions }) {
|
|
2203
|
+
const data = useCommandStream(hub, commandSubscription(descriptor, widget));
|
|
2204
|
+
return /* @__PURE__ */ jsx15(Component2, { widget, data, actions });
|
|
2205
|
+
}
|
|
2206
|
+
function LocationConnectedWidget({ widget, hub, Component: Component2, actions }) {
|
|
2207
|
+
const data = useLocationStream(hub, { datasource: widget.datasource });
|
|
2038
2208
|
if (data.error && data.locations.length === 0) {
|
|
2039
|
-
return /* @__PURE__ */
|
|
2209
|
+
return /* @__PURE__ */ jsx15(WidgetErrorFrame, { widget, error: data.error });
|
|
2040
2210
|
}
|
|
2041
|
-
|
|
2042
|
-
if (!Component) return null;
|
|
2043
|
-
return /* @__PURE__ */ jsx17(Component, { widget, data });
|
|
2044
|
-
}
|
|
2045
|
-
function locationSubscription(widget) {
|
|
2046
|
-
return { datasource: widget.datasource };
|
|
2047
|
-
}
|
|
2048
|
-
function commandSubscription(widget) {
|
|
2049
|
-
return {
|
|
2050
|
-
datasource: widget.datasource,
|
|
2051
|
-
pageSize: Math.max(1, optNumber(widget.options, "maxRows") ?? DEFAULT_COMMAND_ROWS)
|
|
2052
|
-
};
|
|
2053
|
-
}
|
|
2054
|
-
function alarmSubscription(widget) {
|
|
2055
|
-
const pageSize = widget.type === "alarm-count" ? DEFAULT_ALARM_ROWS : Math.max(1, optNumber(widget.options, "maxRows") ?? DEFAULT_ALARM_ROWS);
|
|
2056
|
-
return {
|
|
2057
|
-
datasource: widget.datasource,
|
|
2058
|
-
state: optString(widget.options, "state"),
|
|
2059
|
-
severity: optString(widget.options, "severity"),
|
|
2060
|
-
acknowledged: parseAcknowledged(optString(widget.options, "acknowledged")),
|
|
2061
|
-
pageSize
|
|
2062
|
-
};
|
|
2211
|
+
return /* @__PURE__ */ jsx15(Component2, { widget, data, actions });
|
|
2063
2212
|
}
|
|
2064
|
-
function
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2213
|
+
function UnresolvedWidgetFrame({
|
|
2214
|
+
storedType: stored,
|
|
2215
|
+
reason
|
|
2216
|
+
}) {
|
|
2217
|
+
const storedType = typeof stored === "string" ? stored : "";
|
|
2218
|
+
const heading = (reason?.code ?? "unregistered") === "unregistered" ? "This widget needs a newer viewer" : "This widget cannot be shown here";
|
|
2219
|
+
return /* @__PURE__ */ jsx15(WidgetFrame13, { children: /* @__PURE__ */ jsxs9(
|
|
2220
|
+
"div",
|
|
2221
|
+
{
|
|
2222
|
+
style: {
|
|
2223
|
+
display: "flex",
|
|
2224
|
+
flexDirection: "column",
|
|
2225
|
+
alignItems: "center",
|
|
2226
|
+
justifyContent: "center",
|
|
2227
|
+
gap: 4,
|
|
2228
|
+
width: "100%",
|
|
2229
|
+
height: "100%",
|
|
2230
|
+
padding: 12,
|
|
2231
|
+
boxSizing: "border-box",
|
|
2232
|
+
textAlign: "center",
|
|
2233
|
+
color: "hsl(var(--muted-foreground))"
|
|
2234
|
+
},
|
|
2235
|
+
children: [
|
|
2236
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 13, fontWeight: 600 }, children: heading }),
|
|
2237
|
+
storedType && !reason?.message.includes(storedType) && /* @__PURE__ */ jsxs9("div", { style: { fontSize: 11, opacity: 0.8 }, children: [
|
|
2238
|
+
"Widget type: ",
|
|
2239
|
+
storedType
|
|
2240
|
+
] }),
|
|
2241
|
+
reason && /* @__PURE__ */ jsx15("div", { style: { fontSize: 11, opacity: 0.8, wordBreak: "break-word" }, children: reason.message })
|
|
2242
|
+
]
|
|
2243
|
+
}
|
|
2244
|
+
) });
|
|
2068
2245
|
}
|
|
2069
2246
|
function WidgetUnavailableFrame({ widget }) {
|
|
2070
|
-
return /* @__PURE__ */
|
|
2247
|
+
return /* @__PURE__ */ jsx15(WidgetFrame13, { title: optString13(widget.options, "title"), children: /* @__PURE__ */ jsxs9(
|
|
2071
2248
|
"div",
|
|
2072
2249
|
{
|
|
2073
2250
|
style: {
|
|
@@ -2084,15 +2261,15 @@ function WidgetUnavailableFrame({ widget }) {
|
|
|
2084
2261
|
color: "hsl(var(--muted-foreground))"
|
|
2085
2262
|
},
|
|
2086
2263
|
children: [
|
|
2087
|
-
/* @__PURE__ */
|
|
2088
|
-
/* @__PURE__ */
|
|
2089
|
-
/* @__PURE__ */
|
|
2264
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 20, lineHeight: 1 }, "aria-hidden": "true", children: "\u2298" }),
|
|
2265
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 13, fontWeight: 600 }, children: "Device unavailable" }),
|
|
2266
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 11, opacity: 0.8 }, children: "The bound device no longer exists." })
|
|
2090
2267
|
]
|
|
2091
2268
|
}
|
|
2092
2269
|
) });
|
|
2093
2270
|
}
|
|
2094
2271
|
function WidgetErrorFrame({ widget, error }) {
|
|
2095
|
-
return /* @__PURE__ */
|
|
2272
|
+
return /* @__PURE__ */ jsx15(WidgetFrame13, { title: optString13(widget.options, "title"), children: /* @__PURE__ */ jsxs9(
|
|
2096
2273
|
"div",
|
|
2097
2274
|
{
|
|
2098
2275
|
style: {
|
|
@@ -2109,33 +2286,45 @@ function WidgetErrorFrame({ widget, error }) {
|
|
|
2109
2286
|
color: "hsl(var(--muted-foreground))"
|
|
2110
2287
|
},
|
|
2111
2288
|
children: [
|
|
2112
|
-
/* @__PURE__ */
|
|
2113
|
-
/* @__PURE__ */
|
|
2114
|
-
/* @__PURE__ */
|
|
2289
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 20, lineHeight: 1 }, children: "\u26A0" }),
|
|
2290
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 13, fontWeight: 600 }, children: "Data unavailable" }),
|
|
2291
|
+
/* @__PURE__ */ jsx15("div", { style: { fontSize: 11, opacity: 0.8, wordBreak: "break-word" }, children: String(error) })
|
|
2115
2292
|
]
|
|
2116
2293
|
}
|
|
2117
2294
|
) });
|
|
2118
2295
|
}
|
|
2119
2296
|
|
|
2120
2297
|
// src/dashboard-renderer.tsx
|
|
2121
|
-
import {
|
|
2298
|
+
import {
|
|
2299
|
+
WidgetCandidatesProvider as WidgetCandidatesProvider2,
|
|
2300
|
+
WidgetSelectProvider as WidgetSelectProvider2,
|
|
2301
|
+
WidgetSubjectProvider,
|
|
2302
|
+
WidgetThemeRootProvider,
|
|
2303
|
+
WidgetTimeZoneProvider
|
|
2304
|
+
} from "@devicechain/widget-sdk";
|
|
2305
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2122
2306
|
function DashboardRenderer({
|
|
2123
2307
|
definition,
|
|
2124
2308
|
hub,
|
|
2309
|
+
registry: registryProp,
|
|
2125
2310
|
actions,
|
|
2126
2311
|
seedHistory = true,
|
|
2127
2312
|
bindings,
|
|
2128
2313
|
sizing,
|
|
2129
2314
|
select,
|
|
2130
|
-
candidates
|
|
2315
|
+
candidates,
|
|
2316
|
+
timeZone,
|
|
2317
|
+
themeRoot
|
|
2131
2318
|
}) {
|
|
2132
2319
|
const breakpoint = useActiveBreakpoint(definition.canvas.breakpoints);
|
|
2133
|
-
const
|
|
2320
|
+
const widgets = useMemo5(() => resolveWidgets(definition.widgets, bindings), [definition.widgets, bindings]);
|
|
2321
|
+
const registry = registryProp ?? defaultWidgetRegistry();
|
|
2322
|
+
const histories = useWidgetHistories(widgets, seedHistory, registry);
|
|
2134
2323
|
const { grid, background: bg } = definition.canvas;
|
|
2135
2324
|
const rowGap = typeof grid.gap === "number" ? grid.gap : grid.gap.row;
|
|
2136
2325
|
const colGap = typeof grid.gap === "number" ? grid.gap : grid.gap.col;
|
|
2137
2326
|
const effectiveSizing = sizing ?? definition.canvas.sizing;
|
|
2138
|
-
return /* @__PURE__ */
|
|
2327
|
+
return /* @__PURE__ */ jsx16("div", { style: sizingStyle(effectiveSizing, bg), children: /* @__PURE__ */ jsx16(WidgetThemeRootProvider, { root: themeRoot, children: /* @__PURE__ */ jsx16(WidgetTimeZoneProvider, { timeZone, children: /* @__PURE__ */ jsx16(WidgetSelectProvider2, { select, children: /* @__PURE__ */ jsx16(WidgetCandidatesProvider2, { candidates, children: /* @__PURE__ */ jsx16(
|
|
2139
2328
|
"div",
|
|
2140
2329
|
{
|
|
2141
2330
|
style: {
|
|
@@ -2146,20 +2335,21 @@ function DashboardRenderer({
|
|
|
2146
2335
|
rowGap,
|
|
2147
2336
|
width: "100%"
|
|
2148
2337
|
},
|
|
2149
|
-
children:
|
|
2338
|
+
children: widgets.map((widget) => {
|
|
2150
2339
|
const box = resolveWidgetBox(widget.layout, breakpoint);
|
|
2151
|
-
return /* @__PURE__ */
|
|
2340
|
+
return /* @__PURE__ */ jsx16("div", { style: gridItemStyle(box, grid.columns), children: /* @__PURE__ */ jsx16(WidgetSubjectProvider, { label: widgetSubjectLabel(widget), children: /* @__PURE__ */ jsx16(
|
|
2152
2341
|
ConnectedWidget,
|
|
2153
2342
|
{
|
|
2154
2343
|
widget,
|
|
2155
2344
|
hub,
|
|
2345
|
+
registry,
|
|
2156
2346
|
actions,
|
|
2157
2347
|
initialSamples: histories[widget.id]
|
|
2158
2348
|
}
|
|
2159
2349
|
) }) }, widget.id);
|
|
2160
2350
|
})
|
|
2161
2351
|
}
|
|
2162
|
-
) }) }) });
|
|
2352
|
+
) }) }) }) }) });
|
|
2163
2353
|
}
|
|
2164
2354
|
function gridItemStyle(box, columns) {
|
|
2165
2355
|
const col = Math.min(Math.max(0, box.col), columns - 1);
|
|
@@ -2186,401 +2376,157 @@ function sizingStyle(sizing, bg) {
|
|
|
2186
2376
|
if ("width" in sizing) return { ...base, width: sizing.width, maxWidth: "100%", height: "100%" };
|
|
2187
2377
|
return { ...base, width: "100%", height: sizing.height };
|
|
2188
2378
|
}
|
|
2189
|
-
function
|
|
2190
|
-
return map
|
|
2379
|
+
function resolveWidgets(widgets, bindings) {
|
|
2380
|
+
return widgets.map((widget) => {
|
|
2381
|
+
if (widget.type === UNKNOWN_WIDGET_TYPE2 || widget.datasource?.kind !== "slot") return widget;
|
|
2382
|
+
return { ...widget, datasource: resolveWidgetDatasource(widget.datasource, bindings) };
|
|
2383
|
+
});
|
|
2191
2384
|
}
|
|
2192
|
-
function widgetSubjectLabel(widget
|
|
2385
|
+
function widgetSubjectLabel(widget) {
|
|
2193
2386
|
const ds = widget.datasource;
|
|
2194
2387
|
if (!ds) return void 0;
|
|
2195
|
-
|
|
2196
|
-
if (ds.kind === "
|
|
2197
|
-
binding = ds.deviceToken ? { kind: "device", deviceToken: ds.deviceToken } : void 0;
|
|
2198
|
-
} else if (ds.kind === "anchor") {
|
|
2199
|
-
binding = { kind: "anchor", anchor: ds.anchor };
|
|
2200
|
-
} else if (ds.kind === "slot") {
|
|
2201
|
-
const slot = ownGet(slots, ds.slot);
|
|
2202
|
-
binding = slot?.scope ? ownGet(bindings, ds.slot) : ownGet(bindings, ds.slot) ?? slot?.defaultBinding;
|
|
2203
|
-
}
|
|
2204
|
-
if (binding?.kind === "device") return binding.deviceToken;
|
|
2205
|
-
if (binding?.kind === "anchor") return binding.anchor.targetToken;
|
|
2388
|
+
if (ds.kind === "device") return ds.deviceToken || void 0;
|
|
2389
|
+
if (ds.kind === "anchor") return ds.anchor.targetToken;
|
|
2206
2390
|
return void 0;
|
|
2207
2391
|
}
|
|
2208
2392
|
function useActiveBreakpoint(breakpoints) {
|
|
2209
|
-
const [width, setWidth] =
|
|
2393
|
+
const [width, setWidth] = useState6(
|
|
2210
2394
|
() => typeof window === "undefined" ? 0 : window.innerWidth
|
|
2211
2395
|
);
|
|
2212
|
-
|
|
2396
|
+
useEffect4(() => {
|
|
2213
2397
|
const onResize = () => setWidth(window.innerWidth);
|
|
2214
2398
|
window.addEventListener("resize", onResize);
|
|
2215
2399
|
return () => window.removeEventListener("resize", onResize);
|
|
2216
2400
|
}, []);
|
|
2217
2401
|
return activeBreakpoint(breakpoints, width);
|
|
2218
2402
|
}
|
|
2219
|
-
function
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2403
|
+
function seedKey(widget, registry) {
|
|
2404
|
+
if (widget.type === UNKNOWN_WIDGET_TYPE2 || registry.descriptor(widget.type)?.channel !== "measurement") {
|
|
2405
|
+
return void 0;
|
|
2406
|
+
}
|
|
2407
|
+
return JSON.stringify(widget.datasource ?? null);
|
|
2408
|
+
}
|
|
2409
|
+
function useWidgetHistories(widgets, enabled, registry) {
|
|
2410
|
+
const [seeds, setSeeds] = useState6({});
|
|
2411
|
+
const requested = useRef3({});
|
|
2412
|
+
const mounted = useRef3(true);
|
|
2413
|
+
useEffect4(() => {
|
|
2414
|
+
mounted.current = true;
|
|
2415
|
+
return () => {
|
|
2416
|
+
mounted.current = false;
|
|
2417
|
+
};
|
|
2418
|
+
}, []);
|
|
2419
|
+
const wanted = widgets.flatMap((w) => {
|
|
2420
|
+
const key = seedKey(w, registry);
|
|
2421
|
+
return key === void 0 ? [] : [[w.id, key]];
|
|
2422
|
+
});
|
|
2423
|
+
const wantedKey = JSON.stringify(wanted);
|
|
2424
|
+
useEffect4(() => {
|
|
2223
2425
|
if (!enabled) {
|
|
2224
|
-
|
|
2426
|
+
requested.current = {};
|
|
2427
|
+
setSeeds({});
|
|
2225
2428
|
return;
|
|
2226
2429
|
}
|
|
2227
|
-
let cancelled = false;
|
|
2228
2430
|
const historyWindow = defaultHistoryWindow();
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
]
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
};
|
|
2245
|
-
|
|
2246
|
-
|
|
2431
|
+
const byId = new Map(widgets.map((w) => [w.id, w]));
|
|
2432
|
+
const next = {};
|
|
2433
|
+
for (const [id, key] of wanted) {
|
|
2434
|
+
next[id] = key;
|
|
2435
|
+
if (requested.current[id] === key) continue;
|
|
2436
|
+
const widget = byId.get(id);
|
|
2437
|
+
if (!widget) continue;
|
|
2438
|
+
void fetchWidgetHistory(widget, historyWindow).then((samples) => {
|
|
2439
|
+
if (!mounted.current || requested.current[id] !== key) return;
|
|
2440
|
+
setSeeds((prev) => ({ ...prev, [id]: { key, samples } }));
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
requested.current = next;
|
|
2444
|
+
}, [wantedKey, enabled]);
|
|
2445
|
+
return useMemo5(() => {
|
|
2446
|
+
const out = {};
|
|
2447
|
+
if (!enabled) return out;
|
|
2448
|
+
for (const w of widgets) {
|
|
2449
|
+
const seed = seeds[w.id];
|
|
2450
|
+
out[w.id] = seed && seed.key === seedKey(w, registry) ? seed.samples : void 0;
|
|
2451
|
+
}
|
|
2452
|
+
return out;
|
|
2453
|
+
}, [widgets, seeds, enabled, registry]);
|
|
2247
2454
|
}
|
|
2248
2455
|
|
|
2456
|
+
// src/registry.ts
|
|
2457
|
+
var entries = Object.entries(builtinWidgetDescriptors);
|
|
2458
|
+
var WIDGET_CHANNEL = Object.freeze(
|
|
2459
|
+
Object.fromEntries(entries.map(([id, d]) => [id, d.channel]))
|
|
2460
|
+
);
|
|
2461
|
+
var WIDGET_BINDS_DATASOURCE = Object.freeze(
|
|
2462
|
+
Object.fromEntries(entries.map(([id, d]) => [id, d.datasource.mode !== "none"]))
|
|
2463
|
+
);
|
|
2464
|
+
|
|
2465
|
+
// src/index.ts
|
|
2466
|
+
import { pickSample as pickSample3, optBoolean as optBoolean5 } from "@devicechain/widget-sdk";
|
|
2467
|
+
|
|
2249
2468
|
// src/options.ts
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
max: 100,
|
|
2263
|
-
doc: "Decimal places to round displayed values to. Unset shows the raw value."
|
|
2264
|
-
};
|
|
2265
|
-
var FLASH_ON_CHANGE = {
|
|
2266
|
-
kind: "boolean",
|
|
2267
|
-
doc: "Briefly tint a value green on a rise and red on a fall when it changes."
|
|
2268
|
-
};
|
|
2269
|
-
var WINDOW = {
|
|
2270
|
-
kind: "number",
|
|
2271
|
-
integer: true,
|
|
2272
|
-
min: 1,
|
|
2273
|
-
doc: "Samples retained across ALL of the widget's measurements before the oldest are dropped. Bounds memory; only the chart draws them. Default 300."
|
|
2274
|
-
};
|
|
2275
|
-
var UNIT = {
|
|
2276
|
-
kind: "string",
|
|
2277
|
-
doc: "Unit suffix shown beside the value."
|
|
2278
|
-
};
|
|
2279
|
-
var ALARM_STATE = {
|
|
2280
|
-
kind: "enum",
|
|
2281
|
-
values: ["ACTIVE", "CLEARED"],
|
|
2282
|
-
doc: "Show only alarms in this state. Unset shows every state."
|
|
2283
|
-
};
|
|
2284
|
-
var ALARM_SEVERITY = {
|
|
2285
|
-
kind: "enum",
|
|
2286
|
-
values: ["CRITICAL", "MAJOR", "MINOR", "WARNING", "INDETERMINATE"],
|
|
2287
|
-
doc: "Show only alarms at this severity. Unset shows every severity."
|
|
2288
|
-
};
|
|
2289
|
-
var ALARM_ACKNOWLEDGED = {
|
|
2290
|
-
kind: "enum",
|
|
2291
|
-
values: ["true", "false"],
|
|
2292
|
-
doc: "Show only acknowledged ('true') or unacknowledged ('false') alarms. Unset shows both."
|
|
2293
|
-
};
|
|
2294
|
-
var SELECTION_TARGET = {
|
|
2295
|
-
kind: "string",
|
|
2296
|
-
doc: "Name of the slot this widget re-points when the viewer picks an entity."
|
|
2297
|
-
};
|
|
2298
|
-
var WIDGET_OPTIONS = {
|
|
2299
|
-
"timeseries-chart": {
|
|
2300
|
-
title: TITLE,
|
|
2301
|
-
window: WINDOW
|
|
2302
|
-
},
|
|
2303
|
-
"latest-card": {
|
|
2304
|
-
title: TITLE_OR_MEASUREMENT,
|
|
2305
|
-
measurement: {
|
|
2306
|
-
kind: "string",
|
|
2307
|
-
doc: "Which measurement to display. Defaults to the first the datasource selects."
|
|
2308
|
-
},
|
|
2309
|
-
unit: UNIT,
|
|
2310
|
-
precision: PRECISION,
|
|
2311
|
-
flashOnChange: FLASH_ON_CHANGE,
|
|
2312
|
-
window: WINDOW
|
|
2313
|
-
},
|
|
2314
|
-
gauge: {
|
|
2315
|
-
title: TITLE_OR_MEASUREMENT,
|
|
2316
|
-
measurement: {
|
|
2317
|
-
kind: "string",
|
|
2318
|
-
doc: "Which measurement to display. Defaults to the first the datasource selects."
|
|
2319
|
-
},
|
|
2320
|
-
min: {
|
|
2321
|
-
kind: "number",
|
|
2322
|
-
doc: "Low end of the gauge scale. Default 0."
|
|
2323
|
-
},
|
|
2324
|
-
max: {
|
|
2325
|
-
kind: "number",
|
|
2326
|
-
doc: "High end of the gauge scale. Default 100."
|
|
2327
|
-
},
|
|
2328
|
-
unit: UNIT,
|
|
2329
|
-
flashOnChange: FLASH_ON_CHANGE,
|
|
2330
|
-
window: WINDOW
|
|
2331
|
-
},
|
|
2332
|
-
table: {
|
|
2333
|
-
title: TITLE,
|
|
2334
|
-
precision: PRECISION,
|
|
2335
|
-
flashOnChange: FLASH_ON_CHANGE,
|
|
2336
|
-
window: WINDOW
|
|
2337
|
-
},
|
|
2338
|
-
// label and image carry no datasource: they sit on the measurement channel only
|
|
2339
|
-
// because that is ConnectedWidget's default branch, and read no stream data at all.
|
|
2340
|
-
label: {
|
|
2341
|
-
title: TITLE,
|
|
2342
|
-
text: {
|
|
2343
|
-
kind: "string",
|
|
2344
|
-
required: true,
|
|
2345
|
-
doc: "The text to show. Rendered as plain text \u2014 markup is never interpreted."
|
|
2346
|
-
},
|
|
2347
|
-
align: {
|
|
2348
|
-
kind: "enum",
|
|
2349
|
-
values: ["left", "center", "right"],
|
|
2350
|
-
doc: "Horizontal alignment of the text. Default center."
|
|
2351
|
-
},
|
|
2352
|
-
fontSize: {
|
|
2353
|
-
kind: "number",
|
|
2354
|
-
min: 1,
|
|
2355
|
-
doc: "Text size in pixels. Default 16."
|
|
2356
|
-
}
|
|
2357
|
-
},
|
|
2358
|
-
image: {
|
|
2359
|
-
title: TITLE,
|
|
2360
|
-
url: {
|
|
2361
|
-
kind: "string",
|
|
2362
|
-
required: true,
|
|
2363
|
-
doc: "Source URL of the image."
|
|
2364
|
-
},
|
|
2365
|
-
alt: {
|
|
2366
|
-
kind: "string",
|
|
2367
|
-
doc: "Alternative text describing the image for assistive technology."
|
|
2368
|
-
},
|
|
2369
|
-
fit: {
|
|
2370
|
-
kind: "enum",
|
|
2371
|
-
values: ["contain", "cover", "fill"],
|
|
2372
|
-
doc: "How the image fills its box. Default contain."
|
|
2373
|
-
}
|
|
2374
|
-
},
|
|
2375
|
-
"alarm-table": {
|
|
2376
|
-
title: TITLE,
|
|
2377
|
-
state: ALARM_STATE,
|
|
2378
|
-
severity: ALARM_SEVERITY,
|
|
2379
|
-
acknowledged: ALARM_ACKNOWLEDGED,
|
|
2380
|
-
maxRows: {
|
|
2381
|
-
kind: "number",
|
|
2382
|
-
integer: true,
|
|
2383
|
-
min: 1,
|
|
2384
|
-
doc: "Alarms loaded into the table. Default 50."
|
|
2385
|
-
},
|
|
2386
|
-
precision: PRECISION,
|
|
2387
|
-
flashOnChange: FLASH_ON_CHANGE,
|
|
2388
|
-
selectionTarget: SELECTION_TARGET
|
|
2389
|
-
},
|
|
2390
|
-
// No maxRows: alarmSubscription() forces the default page size for a count, because the
|
|
2391
|
-
// number it shows is the SERVER total and the page is read only to sample the worst
|
|
2392
|
-
// loaded severity. An authored maxRows here would be read by nothing.
|
|
2393
|
-
"alarm-count": {
|
|
2394
|
-
title: TITLE,
|
|
2395
|
-
state: ALARM_STATE,
|
|
2396
|
-
severity: ALARM_SEVERITY,
|
|
2397
|
-
acknowledged: ALARM_ACKNOWLEDGED
|
|
2398
|
-
},
|
|
2399
|
-
"command-button": {
|
|
2400
|
-
title: TITLE,
|
|
2401
|
-
commandName: {
|
|
2402
|
-
kind: "string",
|
|
2403
|
-
required: true,
|
|
2404
|
-
doc: "Command key issued to the target device. Without it the widget cannot send."
|
|
2405
|
-
},
|
|
2406
|
-
commandLabel: {
|
|
2407
|
-
kind: "string",
|
|
2408
|
-
doc: "Text on the send button. Defaults to the command key."
|
|
2409
|
-
},
|
|
2410
|
-
parameterSchema: {
|
|
2411
|
-
kind: "string",
|
|
2412
|
-
format: "json",
|
|
2413
|
-
doc: "The command's parameter descriptors, baked in as JSON at author time. Drives the typed form."
|
|
2414
|
-
},
|
|
2415
|
-
maxRows: {
|
|
2416
|
-
kind: "number",
|
|
2417
|
-
integer: true,
|
|
2418
|
-
min: 1,
|
|
2419
|
-
doc: "Recent commands shown in the history. Default 20."
|
|
2420
|
-
}
|
|
2421
|
-
},
|
|
2422
|
-
"entity-selector": {
|
|
2423
|
-
title: TITLE,
|
|
2424
|
-
selectionTarget: { ...SELECTION_TARGET, required: true }
|
|
2425
|
-
},
|
|
2426
|
-
// 🔴 tileUrl is NOT `required`, and that is the decision rather than an oversight.
|
|
2427
|
-
// `required` means "absent leaves the widget with nothing to show", and a map without
|
|
2428
|
-
// a basemap still shows every position it was asked to — on a plain panel, saying why
|
|
2429
|
-
// there is no map behind them. Marking it required would report a correctly-rendering
|
|
2430
|
-
// widget as broken on every board that names no provider of its own — which is most
|
|
2431
|
-
// of them, because the usual and recommended answer is to inherit the tenant's
|
|
2432
|
-
// basemap rather than to configure tiles per board.
|
|
2433
|
-
map: {
|
|
2434
|
-
title: TITLE,
|
|
2435
|
-
tileUrl: {
|
|
2436
|
-
kind: "string",
|
|
2437
|
-
doc: "Raster tile URL template, e.g. https://tiles.example.com/{z}/{x}/{y}.png. Leave empty to inherit the tenant's basemap; setting one overrides it for this board only."
|
|
2438
|
-
},
|
|
2439
|
-
attribution: {
|
|
2440
|
-
kind: "string",
|
|
2441
|
-
doc: "Credit line the tile source requires, shown on the map. Set whatever the provider you chose asks for."
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
};
|
|
2445
|
-
var CROSS_FIELD_RULES = {
|
|
2446
|
-
gauge: [
|
|
2447
|
-
(options) => {
|
|
2448
|
-
const min = options.min;
|
|
2449
|
-
const max = options.max;
|
|
2450
|
-
if (typeof min !== "number" || typeof max !== "number") return void 0;
|
|
2451
|
-
if (!Number.isFinite(min) || !Number.isFinite(max)) return void 0;
|
|
2452
|
-
if (max > min) return void 0;
|
|
2453
|
-
return {
|
|
2454
|
-
key: "max",
|
|
2455
|
-
code: "invariant",
|
|
2456
|
-
message: `max (${max}) must be greater than min (${min})`
|
|
2457
|
-
};
|
|
2458
|
-
}
|
|
2459
|
-
]
|
|
2460
|
-
};
|
|
2469
|
+
import {
|
|
2470
|
+
clampNumberOption,
|
|
2471
|
+
enumValues,
|
|
2472
|
+
numberSpec,
|
|
2473
|
+
validateOptions
|
|
2474
|
+
} from "@devicechain/dashboards";
|
|
2475
|
+
var WIDGET_OPTIONS = Object.freeze(
|
|
2476
|
+
Object.fromEntries(Object.entries(builtinWidgetDescriptors).map(([id, d]) => [id, d.options]))
|
|
2477
|
+
);
|
|
2478
|
+
function constraintsFor(widgetType) {
|
|
2479
|
+
return builtinWidgetCatalog.get(widgetType)?.constraints ?? [];
|
|
2480
|
+
}
|
|
2461
2481
|
function validateWidgetOptions(widgetType, options) {
|
|
2462
2482
|
const specs = WIDGET_OPTIONS[widgetType];
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
const value = bag[key];
|
|
2467
|
-
if (value === void 0 || spec.required && value === "") {
|
|
2468
|
-
if (spec.required) {
|
|
2469
|
-
issues.push({ widgetType, key, code: "missing", message: `${key} is required but not set` });
|
|
2470
|
-
}
|
|
2471
|
-
continue;
|
|
2472
|
-
}
|
|
2473
|
-
issues.push(...checkValue(widgetType, key, spec, value));
|
|
2474
|
-
}
|
|
2475
|
-
for (const key of Object.keys(bag)) {
|
|
2476
|
-
if (Object.prototype.hasOwnProperty.call(specs, key)) continue;
|
|
2477
|
-
issues.push({
|
|
2478
|
-
widgetType,
|
|
2479
|
-
key,
|
|
2480
|
-
code: "unknown",
|
|
2481
|
-
message: `${key} is not read by a ${widgetType} widget`
|
|
2482
|
-
});
|
|
2483
|
-
}
|
|
2484
|
-
for (const rule of CROSS_FIELD_RULES[widgetType] ?? []) {
|
|
2485
|
-
const issue = rule(bag);
|
|
2486
|
-
if (issue) issues.push({ widgetType, ...issue });
|
|
2487
|
-
}
|
|
2488
|
-
return issues;
|
|
2489
|
-
}
|
|
2490
|
-
function checkValue(widgetType, key, spec, value) {
|
|
2491
|
-
const issue = (code, message) => [{ widgetType, key, code, message }];
|
|
2492
|
-
switch (spec.kind) {
|
|
2493
|
-
case "string":
|
|
2494
|
-
if (typeof value !== "string") {
|
|
2495
|
-
return issue("type", `${key} must be a string, got ${describe(value)}`);
|
|
2496
|
-
}
|
|
2497
|
-
if (spec.format === "json" && !parsesAsJson(value)) {
|
|
2498
|
-
return issue("json", `${key} must be valid JSON`);
|
|
2499
|
-
}
|
|
2500
|
-
return [];
|
|
2501
|
-
case "number":
|
|
2502
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2503
|
-
return issue("type", `${key} must be a finite number, got ${describe(value)}`);
|
|
2504
|
-
}
|
|
2505
|
-
if (spec.integer && !Number.isInteger(value)) {
|
|
2506
|
-
return issue("range", `${key} must be a whole number, got ${value}`);
|
|
2507
|
-
}
|
|
2508
|
-
if (spec.min !== void 0 && value < spec.min) {
|
|
2509
|
-
return issue("range", `${key} must be at least ${spec.min}, got ${value}`);
|
|
2510
|
-
}
|
|
2511
|
-
if (spec.max !== void 0 && value > spec.max) {
|
|
2512
|
-
return issue("range", `${key} must be at most ${spec.max}, got ${value}`);
|
|
2513
|
-
}
|
|
2514
|
-
return [];
|
|
2515
|
-
case "boolean":
|
|
2516
|
-
if (typeof value !== "boolean") {
|
|
2517
|
-
return issue("type", `${key} must be a boolean, got ${describe(value)}`);
|
|
2518
|
-
}
|
|
2519
|
-
return [];
|
|
2520
|
-
case "enum":
|
|
2521
|
-
if (typeof value !== "string") {
|
|
2522
|
-
return issue("type", `${key} must be a string, got ${describe(value)}`);
|
|
2523
|
-
}
|
|
2524
|
-
if (!spec.values.includes(value)) {
|
|
2525
|
-
return issue("enum", `${key} must be one of ${spec.values.join(", ")}, got ${JSON.stringify(value)}`);
|
|
2526
|
-
}
|
|
2527
|
-
return [];
|
|
2528
|
-
}
|
|
2529
|
-
}
|
|
2530
|
-
function parsesAsJson(value) {
|
|
2531
|
-
try {
|
|
2532
|
-
JSON.parse(value);
|
|
2533
|
-
return true;
|
|
2534
|
-
} catch {
|
|
2535
|
-
return false;
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
function describe(value) {
|
|
2539
|
-
if (value === null) return "null";
|
|
2540
|
-
if (typeof value === "string") return `string ${JSON.stringify(value)}`;
|
|
2541
|
-
if (Array.isArray(value)) return "an array";
|
|
2542
|
-
return `${typeof value} ${JSON.stringify(value)}`;
|
|
2483
|
+
return validateOptions(specs, options, { constraints: constraintsFor(widgetType), label: widgetType }).map(
|
|
2484
|
+
({ key, code, message }) => ({ widgetType, key, code, message })
|
|
2485
|
+
);
|
|
2543
2486
|
}
|
|
2544
2487
|
function numberOptionSpec(type, key) {
|
|
2545
|
-
|
|
2546
|
-
if (!Object.prototype.hasOwnProperty.call(specs, key)) return void 0;
|
|
2547
|
-
const spec = specs[key];
|
|
2548
|
-
return spec.kind === "number" ? spec : void 0;
|
|
2549
|
-
}
|
|
2550
|
-
function clampNumberOption(spec, value) {
|
|
2551
|
-
if (!Number.isFinite(value)) return void 0;
|
|
2552
|
-
if (!spec) return value;
|
|
2553
|
-
let next = spec.integer ? Math.trunc(value) : value;
|
|
2554
|
-
if (spec.min !== void 0) next = Math.max(spec.min, next);
|
|
2555
|
-
if (spec.max !== void 0) next = Math.min(spec.max, next);
|
|
2556
|
-
return next;
|
|
2488
|
+
return numberSpec(WIDGET_OPTIONS[type], key);
|
|
2557
2489
|
}
|
|
2558
2490
|
function enumOptionValues(type, key) {
|
|
2559
|
-
|
|
2560
|
-
if (!Object.prototype.hasOwnProperty.call(specs, key)) return void 0;
|
|
2561
|
-
const spec = specs[key];
|
|
2562
|
-
return spec.kind === "enum" ? spec.values : void 0;
|
|
2491
|
+
return enumValues(WIDGET_OPTIONS[type], key);
|
|
2563
2492
|
}
|
|
2564
2493
|
|
|
2565
2494
|
// src/definition-options.ts
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2495
|
+
import {
|
|
2496
|
+
UNKNOWN_WIDGET_TYPE as UNKNOWN_WIDGET_TYPE3,
|
|
2497
|
+
validateOptions as validateOptions2
|
|
2498
|
+
} from "@devicechain/dashboards";
|
|
2499
|
+
function validateDefinitionOptions(definition, options = {}) {
|
|
2500
|
+
return definition.widgets.flatMap((widget) => {
|
|
2501
|
+
if (widget.type === UNKNOWN_WIDGET_TYPE3) return [];
|
|
2502
|
+
const type = widget.type;
|
|
2503
|
+
const descriptor = descriptorFor(type, options.catalog);
|
|
2504
|
+
return validateOptions2(descriptor.options, widget.options, {
|
|
2505
|
+
constraints: descriptor.constraints,
|
|
2506
|
+
label: type
|
|
2507
|
+
}).map(({ key, code, message }) => ({ widgetType: type, key, code, message, widgetId: widget.id }));
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
function descriptorFor(type, catalog) {
|
|
2511
|
+
const descriptor = catalog?.get(type) ?? builtinWidgetCatalog.get(type);
|
|
2512
|
+
if (!descriptor) {
|
|
2513
|
+
throw new Error(`no widget descriptor for "${type}"; validate with the catalog the definition was parsed with`);
|
|
2514
|
+
}
|
|
2515
|
+
return descriptor;
|
|
2570
2516
|
}
|
|
2571
|
-
function stripUnknownOptions(definition) {
|
|
2517
|
+
function stripUnknownOptions(definition, options = {}) {
|
|
2572
2518
|
let changed = false;
|
|
2573
2519
|
const widgets = definition.widgets.map((widget) => {
|
|
2574
|
-
const stripped = stripUnknownWidgetOptions(widget);
|
|
2520
|
+
const stripped = stripUnknownWidgetOptions(widget, options.catalog);
|
|
2575
2521
|
if (stripped !== widget) changed = true;
|
|
2576
2522
|
return stripped;
|
|
2577
2523
|
});
|
|
2578
2524
|
return changed ? { ...definition, widgets } : definition;
|
|
2579
2525
|
}
|
|
2580
|
-
function stripUnknownWidgetOptions(widget) {
|
|
2526
|
+
function stripUnknownWidgetOptions(widget, catalog) {
|
|
2581
2527
|
const bag = widget.options;
|
|
2582
2528
|
if (!bag) return widget;
|
|
2583
|
-
const specs =
|
|
2529
|
+
const specs = descriptorFor(widget.type, catalog).options;
|
|
2584
2530
|
const keys = Object.keys(bag);
|
|
2585
2531
|
const kept = keys.filter((key) => Object.prototype.hasOwnProperty.call(specs, key));
|
|
2586
2532
|
if (kept.length === keys.length) return widget;
|
|
@@ -2588,49 +2534,85 @@ function stripUnknownWidgetOptions(widget) {
|
|
|
2588
2534
|
for (const key of kept) options[key] = bag[key];
|
|
2589
2535
|
return { ...widget, options };
|
|
2590
2536
|
}
|
|
2537
|
+
|
|
2538
|
+
// src/index.ts
|
|
2539
|
+
import {
|
|
2540
|
+
WidgetFrame as WidgetFrame14,
|
|
2541
|
+
WidgetSubjectProvider as WidgetSubjectProvider2,
|
|
2542
|
+
WidgetSelectProvider as WidgetSelectProvider3,
|
|
2543
|
+
useWidgetSelect as useWidgetSelect4,
|
|
2544
|
+
WidgetTimeZoneProvider as WidgetTimeZoneProvider2,
|
|
2545
|
+
useWidgetTimeZone as useWidgetTimeZone5,
|
|
2546
|
+
WidgetThemeRootProvider as WidgetThemeRootProvider2,
|
|
2547
|
+
useWidgetThemeRoot,
|
|
2548
|
+
WidgetCandidatesProvider as WidgetCandidatesProvider3,
|
|
2549
|
+
useWidgetCandidates as useWidgetCandidates2,
|
|
2550
|
+
useFlashOnChange as useFlashOnChange3,
|
|
2551
|
+
flashTextStyle as flashTextStyle2,
|
|
2552
|
+
flashBackgroundStyle as flashBackgroundStyle2,
|
|
2553
|
+
FlashValue as FlashValue3,
|
|
2554
|
+
useMeasurementStream as useMeasurementStream2,
|
|
2555
|
+
useAlarmStream as useAlarmStream2,
|
|
2556
|
+
useCommandStream as useCommandStream2,
|
|
2557
|
+
useLocationStream as useLocationStream2,
|
|
2558
|
+
useDatasourceAvailability as useDatasourceAvailability2,
|
|
2559
|
+
useDashboardHub,
|
|
2560
|
+
useResolvedBindings,
|
|
2561
|
+
useSlotCandidates,
|
|
2562
|
+
useCandidates as useCandidates2,
|
|
2563
|
+
css as css10
|
|
2564
|
+
} from "@devicechain/widget-sdk";
|
|
2565
|
+
import {
|
|
2566
|
+
EChart as EChart3,
|
|
2567
|
+
buildLineOption as buildLineOption2,
|
|
2568
|
+
buildGaugeOption as buildGaugeOption2,
|
|
2569
|
+
resolveChartTheme,
|
|
2570
|
+
useChartTheme as useChartTheme3
|
|
2571
|
+
} from "@devicechain/widget-sdk/echarts";
|
|
2591
2572
|
export {
|
|
2592
|
-
ALARM_WIDGET_REGISTRY,
|
|
2593
2573
|
AlarmCount,
|
|
2594
2574
|
AlarmTable,
|
|
2595
2575
|
BUNDLED_BASEMAP_ATTRIBUTION,
|
|
2596
|
-
CONTROL_WIDGET_REGISTRY,
|
|
2597
2576
|
CommandButton,
|
|
2598
2577
|
ConnectedWidget,
|
|
2599
2578
|
DashboardRenderer,
|
|
2600
|
-
EChart,
|
|
2579
|
+
EChart3 as EChart,
|
|
2601
2580
|
EntitySelector,
|
|
2602
|
-
FlashValue,
|
|
2581
|
+
FlashValue3 as FlashValue,
|
|
2603
2582
|
Gauge,
|
|
2604
2583
|
Image,
|
|
2605
|
-
LOCATION_WIDGET_REGISTRY,
|
|
2606
2584
|
Label,
|
|
2607
2585
|
LatestCard,
|
|
2608
2586
|
MapRuntimeProvider,
|
|
2609
2587
|
MapWidget,
|
|
2610
|
-
SELECTION_WIDGET_REGISTRY,
|
|
2611
2588
|
Table,
|
|
2612
2589
|
TenantBasemapProvider,
|
|
2613
2590
|
TimeSeriesChart,
|
|
2614
2591
|
WIDGET_BINDS_DATASOURCE,
|
|
2615
2592
|
WIDGET_CHANNEL,
|
|
2616
2593
|
WIDGET_OPTIONS,
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2594
|
+
WidgetCandidatesProvider3 as WidgetCandidatesProvider,
|
|
2595
|
+
WidgetFrame14 as WidgetFrame,
|
|
2596
|
+
WidgetSelectProvider3 as WidgetSelectProvider,
|
|
2597
|
+
WidgetSubjectProvider2 as WidgetSubjectProvider,
|
|
2598
|
+
WidgetThemeRootProvider2 as WidgetThemeRootProvider,
|
|
2599
|
+
WidgetTimeZoneProvider2 as WidgetTimeZoneProvider,
|
|
2600
|
+
buildGaugeOption2 as buildGaugeOption,
|
|
2601
|
+
buildLineOption2 as buildLineOption,
|
|
2624
2602
|
buildPayload,
|
|
2603
|
+
builtinWidgetCatalog,
|
|
2604
|
+
builtinWidgetDefinitions,
|
|
2605
|
+
builtinWidgetDescriptors,
|
|
2625
2606
|
clampNumberOption,
|
|
2626
2607
|
commandStatusColor,
|
|
2627
2608
|
commandStatusLabel,
|
|
2628
|
-
|
|
2609
|
+
createBuiltinWidgetRegistry,
|
|
2610
|
+
css10 as css,
|
|
2629
2611
|
defaultValues,
|
|
2630
2612
|
describePosition,
|
|
2631
2613
|
enumOptionValues,
|
|
2632
|
-
flashBackgroundStyle,
|
|
2633
|
-
flashTextStyle,
|
|
2614
|
+
flashBackgroundStyle2 as flashBackgroundStyle,
|
|
2615
|
+
flashTextStyle2 as flashTextStyle,
|
|
2634
2616
|
formatDegrees,
|
|
2635
2617
|
highestSeverity,
|
|
2636
2618
|
isScalar,
|
|
@@ -2638,31 +2620,35 @@ export {
|
|
|
2638
2620
|
landStyleFrom,
|
|
2639
2621
|
loadLandStyle,
|
|
2640
2622
|
numberOptionSpec,
|
|
2641
|
-
optBoolean,
|
|
2623
|
+
optBoolean5 as optBoolean,
|
|
2642
2624
|
parseParameterSchema,
|
|
2643
|
-
pickSample,
|
|
2625
|
+
pickSample3 as pickSample,
|
|
2644
2626
|
placeable,
|
|
2645
2627
|
projectToPanel,
|
|
2646
2628
|
rasterStyleFor,
|
|
2647
2629
|
resolveChartTheme,
|
|
2630
|
+
resolveWidgets,
|
|
2648
2631
|
severityColor,
|
|
2649
2632
|
severityLabel,
|
|
2650
2633
|
severityRank,
|
|
2651
2634
|
stripUnknownOptions,
|
|
2652
|
-
useAlarmStream,
|
|
2653
|
-
useCandidates,
|
|
2654
|
-
useChartTheme,
|
|
2655
|
-
useCommandStream,
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2635
|
+
useAlarmStream2 as useAlarmStream,
|
|
2636
|
+
useCandidates2 as useCandidates,
|
|
2637
|
+
useChartTheme3 as useChartTheme,
|
|
2638
|
+
useCommandStream2 as useCommandStream,
|
|
2639
|
+
useDashboardHub,
|
|
2640
|
+
useDatasourceAvailability2 as useDatasourceAvailability,
|
|
2641
|
+
useFlashOnChange3 as useFlashOnChange,
|
|
2642
|
+
useLocationStream2 as useLocationStream,
|
|
2659
2643
|
useMapRuntime,
|
|
2660
|
-
useMeasurementStream,
|
|
2644
|
+
useMeasurementStream2 as useMeasurementStream,
|
|
2661
2645
|
useResolvedBindings,
|
|
2662
2646
|
useSlotCandidates,
|
|
2663
2647
|
useTenantBasemap,
|
|
2664
|
-
useWidgetCandidates,
|
|
2665
|
-
useWidgetSelect,
|
|
2648
|
+
useWidgetCandidates2 as useWidgetCandidates,
|
|
2649
|
+
useWidgetSelect4 as useWidgetSelect,
|
|
2650
|
+
useWidgetThemeRoot,
|
|
2651
|
+
useWidgetTimeZone5 as useWidgetTimeZone,
|
|
2666
2652
|
validateDefinitionOptions,
|
|
2667
2653
|
validateParams,
|
|
2668
2654
|
validateWidgetOptions,
|