@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.89 → 2.0.0-next.90
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/bundle/openbridge-webcomponents.bundle.js +157 -10
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +22 -0
- package/dist/navigation-instruments/readout-list/readout-list.css.js +5 -3
- package/dist/navigation-instruments/readout-list/readout-list.css.js.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js +80 -0
- package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts +32 -0
- package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js +74 -8
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
- package/package.json +1 -1
- package/src/building-blocks/readout-block/readout-block.stories.ts +2 -1
- package/src/navigation-instruments/readout-list/readout-list.css +5 -3
- package/src/navigation-instruments/readout-list/readout-list.stories.ts +102 -19
- package/src/navigation-instruments/readout-list-item/readout-list-item.css +74 -0
- package/src/navigation-instruments/readout-list-item/readout-list-item.stories.ts +127 -12
- package/src/navigation-instruments/readout-list-item/readout-list-item.ts +85 -6
|
@@ -5,9 +5,16 @@ import './readout-list.js';
|
|
|
5
5
|
import {
|
|
6
6
|
ReadoutListItemSize,
|
|
7
7
|
ReadoutListItemPriority,
|
|
8
|
+
ReadoutListItemDataQuality,
|
|
8
9
|
type ReadoutValueOptions,
|
|
9
10
|
} from '../readout-list-item/readout-list-item.js';
|
|
10
11
|
import '../readout-list-item/readout-list-item.js';
|
|
12
|
+
import type {AlertFrameConfig} from '../../components/alert-frame/alert-frame.js';
|
|
13
|
+
import {
|
|
14
|
+
ObcAlertFrameMode,
|
|
15
|
+
ObcAlertFrameType,
|
|
16
|
+
} from '../../components/alert-frame/alert-frame.js';
|
|
17
|
+
import {AlertType} from '../../types.js';
|
|
11
18
|
|
|
12
19
|
type ListArgs = {
|
|
13
20
|
showDebugOverlay: boolean;
|
|
@@ -23,9 +30,26 @@ type Row = {
|
|
|
23
30
|
priority?: ReadoutListItemPriority;
|
|
24
31
|
hasSetpoint?: boolean;
|
|
25
32
|
setpoint?: number;
|
|
33
|
+
hasAdvice?: boolean;
|
|
34
|
+
advice?: number;
|
|
35
|
+
off?: boolean;
|
|
36
|
+
// Row-level and per-block state, so the list stories can smoke-test data
|
|
37
|
+
// quality + alert frames alongside the auto-alignment.
|
|
38
|
+
dataQuality?: ReadoutListItemDataQuality;
|
|
39
|
+
alert?: AlertFrameConfig;
|
|
40
|
+
valueDataQuality?: ReadoutListItemDataQuality;
|
|
41
|
+
valueAlert?: AlertFrameConfig;
|
|
42
|
+
setpointDataQuality?: ReadoutListItemDataQuality;
|
|
26
43
|
};
|
|
27
44
|
|
|
28
45
|
function renderRow(row: Row) {
|
|
46
|
+
const valueOptions =
|
|
47
|
+
row.valueDataQuality || row.valueAlert
|
|
48
|
+
? {dataQuality: row.valueDataQuality, alert: row.valueAlert}
|
|
49
|
+
: undefined;
|
|
50
|
+
const setpointOptions = row.setpointDataQuality
|
|
51
|
+
? {dataQuality: row.setpointDataQuality}
|
|
52
|
+
: undefined;
|
|
29
53
|
return html`
|
|
30
54
|
<obc-readout-list-item
|
|
31
55
|
.label=${row.label}
|
|
@@ -35,8 +59,15 @@ function renderRow(row: Row) {
|
|
|
35
59
|
.hasDegree=${row.hasDegree ?? false}
|
|
36
60
|
.fractionDigits=${row.fractionDigits ?? 0}
|
|
37
61
|
.priority=${row.priority}
|
|
62
|
+
.off=${row.off ?? false}
|
|
38
63
|
.hasSetpoint=${row.hasSetpoint ?? false}
|
|
39
64
|
.setpoint=${row.setpoint}
|
|
65
|
+
.hasAdvice=${row.hasAdvice ?? false}
|
|
66
|
+
.advice=${row.advice}
|
|
67
|
+
.dataQuality=${row.dataQuality}
|
|
68
|
+
.alert=${row.alert ?? false}
|
|
69
|
+
.valueOptions=${valueOptions}
|
|
70
|
+
.setpointOptions=${setpointOptions}
|
|
40
71
|
></obc-readout-list-item>
|
|
41
72
|
`;
|
|
42
73
|
}
|
|
@@ -92,6 +123,18 @@ export const Degrees: Story = {
|
|
|
92
123
|
render: (args) => renderList(DEGREE_ROWS, args.showDebugOverlay),
|
|
93
124
|
};
|
|
94
125
|
|
|
126
|
+
const WARNING_FRAME: AlertFrameConfig = {
|
|
127
|
+
status: AlertType.Warning,
|
|
128
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
129
|
+
type: ObcAlertFrameType.Regular,
|
|
130
|
+
};
|
|
131
|
+
const ALARM_FRAME_ICON: AlertFrameConfig = {
|
|
132
|
+
status: AlertType.Alarm,
|
|
133
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
134
|
+
type: ObcAlertFrameType.SmallSideFlip,
|
|
135
|
+
showAlertCategoryIcon: true,
|
|
136
|
+
};
|
|
137
|
+
|
|
95
138
|
export const WithSetpoints: Story = {
|
|
96
139
|
render: (args) =>
|
|
97
140
|
renderList(
|
|
@@ -104,6 +147,7 @@ export const WithSetpoints: Story = {
|
|
|
104
147
|
hasSetpoint: true,
|
|
105
148
|
setpoint: 290,
|
|
106
149
|
},
|
|
150
|
+
// invalid setpoint chip — auto-aligned alongside the others
|
|
107
151
|
{
|
|
108
152
|
label: 'Speed',
|
|
109
153
|
value: 8,
|
|
@@ -111,8 +155,28 @@ export const WithSetpoints: Story = {
|
|
|
111
155
|
fractionDigits: 1,
|
|
112
156
|
hasSetpoint: true,
|
|
113
157
|
setpoint: 12,
|
|
158
|
+
setpointDataQuality: ReadoutListItemDataQuality.invalid,
|
|
159
|
+
},
|
|
160
|
+
// advice reference
|
|
161
|
+
{label: 'Depth', value: 1013, unit: 'm', hasAdvice: true, advice: 1010},
|
|
162
|
+
// per-value alert frame — wraps value + unit
|
|
163
|
+
{label: 'Fuel', value: 42, unit: '%', valueAlert: WARNING_FRAME},
|
|
164
|
+
// whole row invalid + null value (dash)
|
|
165
|
+
{
|
|
166
|
+
label: 'Wind',
|
|
167
|
+
value: null,
|
|
168
|
+
unit: 'kn',
|
|
169
|
+
dataQuality: ReadoutListItemDataQuality.invalid,
|
|
170
|
+
},
|
|
171
|
+
// whole row low-integrity + row-level alert frame with a badge-icon flap
|
|
172
|
+
{
|
|
173
|
+
label: 'Course',
|
|
174
|
+
value: 120,
|
|
175
|
+
unit: 'T',
|
|
176
|
+
hasDegree: true,
|
|
177
|
+
dataQuality: ReadoutListItemDataQuality.lowIntegrity,
|
|
178
|
+
alert: ALARM_FRAME_ICON,
|
|
114
179
|
},
|
|
115
|
-
{label: 'Depth', value: 1013, unit: 'm'},
|
|
116
180
|
],
|
|
117
181
|
args.showDebugOverlay
|
|
118
182
|
),
|
|
@@ -140,13 +204,47 @@ export const TestDynamicRow: Story = {
|
|
|
140
204
|
},
|
|
141
205
|
};
|
|
142
206
|
|
|
207
|
+
// Richer starting set for the interactive demo: a plain degree row, an invalid
|
|
208
|
+
// setpoint, an advice reference, a per-value alert frame, a null/invalid row and
|
|
209
|
+
// a low-integrity row with a row-level alert frame — so the manual add/remove /
|
|
210
|
+
// re-align flow is exercised against the full range of state.
|
|
211
|
+
const MANUAL_ROWS: Row[] = [
|
|
212
|
+
{label: 'Heading', value: 287, unit: 'T', hasDegree: true},
|
|
213
|
+
{
|
|
214
|
+
label: 'Speed',
|
|
215
|
+
value: 8,
|
|
216
|
+
unit: 'kn',
|
|
217
|
+
fractionDigits: 1,
|
|
218
|
+
hasSetpoint: true,
|
|
219
|
+
setpoint: 12,
|
|
220
|
+
setpointDataQuality: ReadoutListItemDataQuality.invalid,
|
|
221
|
+
},
|
|
222
|
+
{label: 'Depth', value: 1013, unit: 'm', hasAdvice: true, advice: 1010},
|
|
223
|
+
{label: 'Fuel', value: 42, unit: '%', valueAlert: WARNING_FRAME},
|
|
224
|
+
{
|
|
225
|
+
label: 'Wind',
|
|
226
|
+
value: null,
|
|
227
|
+
unit: 'kn',
|
|
228
|
+
dataQuality: ReadoutListItemDataQuality.invalid,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
label: 'Course',
|
|
232
|
+
value: 120,
|
|
233
|
+
unit: 'T',
|
|
234
|
+
hasDegree: true,
|
|
235
|
+
dataQuality: ReadoutListItemDataQuality.lowIntegrity,
|
|
236
|
+
alert: ALARM_FRAME_ICON,
|
|
237
|
+
},
|
|
238
|
+
];
|
|
239
|
+
|
|
143
240
|
/**
|
|
144
241
|
* **Manual (Interactive)** — the rows are slotted children, so they cannot be
|
|
145
242
|
* driven by Storybook controls. Use the buttons to add / remove rows (a
|
|
146
243
|
* structural change the list re-aligns automatically) and to change the first
|
|
147
244
|
* row's value / unit (a property-only change, so it calls the public
|
|
148
245
|
* {@link ObcReadoutList.align} method). The debug overlay shows the reserved
|
|
149
|
-
* column widths reacting to each change.
|
|
246
|
+
* column widths reacting to each change. The starting rows include data-quality
|
|
247
|
+
* and alert-frame variations for a quick visual smoke test.
|
|
150
248
|
*/
|
|
151
249
|
export const Manual: Story = {
|
|
152
250
|
name: 'Manual (Interactive)',
|
|
@@ -168,22 +266,7 @@ export const Manual: Story = {
|
|
|
168
266
|
id="manual-list"
|
|
169
267
|
.showDebugOverlay=${args.showDebugOverlay}
|
|
170
268
|
>
|
|
171
|
-
|
|
172
|
-
.label=${'Heading'}
|
|
173
|
-
.unit=${'T'}
|
|
174
|
-
.value=${287}
|
|
175
|
-
.hasDegree=${true}
|
|
176
|
-
></obc-readout-list-item>
|
|
177
|
-
<obc-readout-list-item
|
|
178
|
-
.label=${'Speed'}
|
|
179
|
-
.unit=${'kn'}
|
|
180
|
-
.value=${18}
|
|
181
|
-
></obc-readout-list-item>
|
|
182
|
-
<obc-readout-list-item
|
|
183
|
-
.label=${'Depth'}
|
|
184
|
-
.unit=${'m'}
|
|
185
|
-
.value=${124}
|
|
186
|
-
></obc-readout-list-item>
|
|
269
|
+
${MANUAL_ROWS.map(renderRow)}
|
|
187
270
|
</obc-readout-list>
|
|
188
271
|
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
|
|
189
272
|
<button id="m-add" style="padding: 6px 12px; cursor: pointer;">
|
|
@@ -203,7 +286,7 @@ export const Manual: Story = {
|
|
|
203
286
|
id="m-status"
|
|
204
287
|
style="font: 12px/1.4 monospace; color: var(--element-neutral-color, #666);"
|
|
205
288
|
>
|
|
206
|
-
|
|
289
|
+
${MANUAL_ROWS.length} rows
|
|
207
290
|
</div>
|
|
208
291
|
</div>
|
|
209
292
|
`,
|
|
@@ -189,6 +189,80 @@ obc-readout-block {
|
|
|
189
189
|
align-items: flex-end;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
/* ---------- Value reading (value + degree + unit) ----------
|
|
193
|
+
* Groups the value block, its degree column and the trailing unit so the value
|
|
194
|
+
* alert frame can wrap all three. Layout-neutral: it adds no gap, so the content
|
|
195
|
+
* sits exactly where it did when these were siblings of `.value-area`. */
|
|
196
|
+
.value-reading {
|
|
197
|
+
position: relative;
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
align-items: flex-end;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* ---------- Value data quality (low-integrity / invalid) ----------
|
|
203
|
+
* The value's per-block data-quality chip, applied to the whole reading (value +
|
|
204
|
+
* degree + unit) rather than hugging the value alone — the same logic as the
|
|
205
|
+
* value alert frame, minus its custom padding. Same look as the block/source
|
|
206
|
+
* chip (1px outline, check radius, alert-quality colours). Uses `outline` (not
|
|
207
|
+
* `border`) so it never shifts the value's width / cross-row column alignment.
|
|
208
|
+
* Setpoint/advice keep their own in-block chip; the row-level data quality on
|
|
209
|
+
* `.surface` is unchanged. */
|
|
210
|
+
.value-reading.data-low-integrity,
|
|
211
|
+
.value-reading.data-invalid {
|
|
212
|
+
outline: 1px solid;
|
|
213
|
+
border-radius: var(--global-border-radius-border-radius-check);
|
|
214
|
+
}
|
|
215
|
+
.value-reading.data-low-integrity {
|
|
216
|
+
background: var(--alert-low-integrity-background-color);
|
|
217
|
+
outline-color: var(--alert-low-integrity-border-color);
|
|
218
|
+
}
|
|
219
|
+
.value-reading.data-invalid {
|
|
220
|
+
background: var(--alert-invalid-background-color);
|
|
221
|
+
outline-color: var(--alert-invalid-border-color);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* ---------- Value alert overlay ----------
|
|
225
|
+
* A pure overlay around the value reading: it reserves no space, so toggling the
|
|
226
|
+
* frame never changes the row height or breaks cross-row column alignment. The
|
|
227
|
+
* box is offset outward by (padding − stroke/2) on each axis so the alert-frame's
|
|
228
|
+
* outline (drawn outside its box) lands CENTRED on the 4px/2px padding line
|
|
229
|
+
* (half inside / half outside), matching Figma 58:10120. */
|
|
230
|
+
.value-alert-overlay {
|
|
231
|
+
--_obc-readout-value-frame-padding-horizontal: 4px;
|
|
232
|
+
--_obc-readout-value-frame-padding-vertical: 2px;
|
|
233
|
+
/* Must match obc-alert-frame's ACTUAL outline width so the stroke stays centred
|
|
234
|
+
on the 4px/2px line. `thickness-small` is hard-coded to 2px in
|
|
235
|
+
alert-frame.css (it does NOT use the themed stroke-width token), so use a
|
|
236
|
+
literal 2px here; `thickness-large` is handled below. */
|
|
237
|
+
--_obc-readout-value-frame-stroke: 2px;
|
|
238
|
+
position: absolute;
|
|
239
|
+
inset-inline: calc(
|
|
240
|
+
-1 *
|
|
241
|
+
(
|
|
242
|
+
var(--_obc-readout-value-frame-padding-horizontal) -
|
|
243
|
+
var(--_obc-readout-value-frame-stroke) / 2
|
|
244
|
+
)
|
|
245
|
+
);
|
|
246
|
+
inset-block: calc(
|
|
247
|
+
-1 *
|
|
248
|
+
(
|
|
249
|
+
var(--_obc-readout-value-frame-padding-vertical) -
|
|
250
|
+
var(--_obc-readout-value-frame-stroke) / 2
|
|
251
|
+
)
|
|
252
|
+
);
|
|
253
|
+
/* Display-only: never intercept clicks on a clickable row. */
|
|
254
|
+
pointer-events: none;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* thickness=large: obc-alert-frame's large outline uses the alert-frame border
|
|
258
|
+
weight token, so the overlay's outward offset must use the same width to keep
|
|
259
|
+
the stroke centred on the padding line. */
|
|
260
|
+
.value-alert-overlay.thickness-large {
|
|
261
|
+
--_obc-readout-value-frame-stroke: var(
|
|
262
|
+
--global-size-spacing-border-weight-alertframe
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
192
266
|
/* The readout building blocks (advice / setpoint / value) now render inside
|
|
193
267
|
`obc-readout-block`; their layout / colour / hinted-zero / data-quality CSS
|
|
194
268
|
lives there. The list-item only bridges the public colour vars (see top) and
|
|
@@ -1262,7 +1262,7 @@ export const PerBlockState: Story = {
|
|
|
1262
1262
|
value: {
|
|
1263
1263
|
alert: {
|
|
1264
1264
|
status: AlertType.Warning,
|
|
1265
|
-
mode: ObcAlertFrameMode.
|
|
1265
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
1266
1266
|
type: ObcAlertFrameType.Regular,
|
|
1267
1267
|
},
|
|
1268
1268
|
},
|
|
@@ -1296,7 +1296,7 @@ export const PerBlockState: Story = {
|
|
|
1296
1296
|
value: {
|
|
1297
1297
|
alert: {
|
|
1298
1298
|
status: AlertType.Warning,
|
|
1299
|
-
mode: ObcAlertFrameMode.
|
|
1299
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
1300
1300
|
type: ObcAlertFrameType.Regular,
|
|
1301
1301
|
},
|
|
1302
1302
|
},
|
|
@@ -1322,7 +1322,7 @@ export const PerBlockState: Story = {
|
|
|
1322
1322
|
dataQuality: ReadoutListItemDataQuality.lowIntegrity,
|
|
1323
1323
|
alert: {
|
|
1324
1324
|
status: AlertType.Warning,
|
|
1325
|
-
mode: ObcAlertFrameMode.
|
|
1325
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
1326
1326
|
type: ObcAlertFrameType.SmallSideFlip,
|
|
1327
1327
|
showAlertCategoryIcon: true,
|
|
1328
1328
|
},
|
|
@@ -1337,7 +1337,7 @@ export const PerBlockState: Story = {
|
|
|
1337
1337
|
dataQuality: ReadoutListItemDataQuality.invalid,
|
|
1338
1338
|
alert: {
|
|
1339
1339
|
status: AlertType.Alarm,
|
|
1340
|
-
mode: ObcAlertFrameMode.
|
|
1340
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
1341
1341
|
type: ObcAlertFrameType.LargeSideFlip,
|
|
1342
1342
|
showAlertCategoryIcon: true,
|
|
1343
1343
|
},
|
|
@@ -1396,6 +1396,94 @@ export const Alarm: Story = {
|
|
|
1396
1396
|
}),
|
|
1397
1397
|
};
|
|
1398
1398
|
|
|
1399
|
+
// The value alert frame now wraps the whole reading — value + degree + unit — and
|
|
1400
|
+
// is drawn as a pure overlay (4px/2px padding, stroke centred on that line), so
|
|
1401
|
+
// toggling it changes neither the row height nor the value/unit column positions.
|
|
1402
|
+
// Each combo is shown unframed then framed so the constant geometry is obvious;
|
|
1403
|
+
// the per-item outline marks the layout box (which the frame never grows).
|
|
1404
|
+
const VALUE_FRAME: AlertFrameConfig = {
|
|
1405
|
+
status: AlertType.Warning,
|
|
1406
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
1407
|
+
type: ObcAlertFrameType.Regular,
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
const VALUE_FRAME_COMBOS: {label: string; config: ReadoutItemConfig}[] = [
|
|
1411
|
+
{label: 'value + unit', config: {value: 123, unit: 'kn'}},
|
|
1412
|
+
{
|
|
1413
|
+
label: 'value + degree + unit',
|
|
1414
|
+
config: {value: 287, unit: 'T', options: {hasDegree: true}},
|
|
1415
|
+
},
|
|
1416
|
+
{
|
|
1417
|
+
label: 'value + unit + setpoint',
|
|
1418
|
+
config: {value: 123, unit: 'kn', hasSetpoint: true, setpoint: 120},
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
label: 'value + unit + advice + setpoint',
|
|
1422
|
+
config: {
|
|
1423
|
+
value: 123,
|
|
1424
|
+
unit: 'kn',
|
|
1425
|
+
hasSetpoint: true,
|
|
1426
|
+
setpoint: 120,
|
|
1427
|
+
hasAdvice: true,
|
|
1428
|
+
advice: 118,
|
|
1429
|
+
},
|
|
1430
|
+
},
|
|
1431
|
+
];
|
|
1432
|
+
|
|
1433
|
+
export const ValueAlertFrame: Story = {
|
|
1434
|
+
render: () => html`
|
|
1435
|
+
<style>
|
|
1436
|
+
.rli-vf {
|
|
1437
|
+
display: flex;
|
|
1438
|
+
flex-direction: column;
|
|
1439
|
+
gap: 12px;
|
|
1440
|
+
width: 380px;
|
|
1441
|
+
}
|
|
1442
|
+
.rli-vf-row {
|
|
1443
|
+
display: flex;
|
|
1444
|
+
align-items: center;
|
|
1445
|
+
gap: 12px;
|
|
1446
|
+
}
|
|
1447
|
+
.rli-vf-tag {
|
|
1448
|
+
width: 180px;
|
|
1449
|
+
flex: none;
|
|
1450
|
+
font: 10px/1.2 var(--global-typography-ui-label-font-family, sans-serif);
|
|
1451
|
+
text-transform: uppercase;
|
|
1452
|
+
letter-spacing: 0.06em;
|
|
1453
|
+
color: var(--element-neutral-color, #777);
|
|
1454
|
+
}
|
|
1455
|
+
.rli-vf-item {
|
|
1456
|
+
flex: 1;
|
|
1457
|
+
outline: 1px solid rgba(0, 0, 0, 0.12);
|
|
1458
|
+
}
|
|
1459
|
+
</style>
|
|
1460
|
+
<div class="rli-vf">
|
|
1461
|
+
${VALUE_FRAME_COMBOS.flatMap((combo) =>
|
|
1462
|
+
[false, true].map(
|
|
1463
|
+
(framed) => html`
|
|
1464
|
+
<div class="rli-vf-row">
|
|
1465
|
+
<div class="rli-vf-tag">
|
|
1466
|
+
${combo.label} ${framed ? '· framed' : '· plain'}
|
|
1467
|
+
</div>
|
|
1468
|
+
<div class="rli-vf-item">
|
|
1469
|
+
${renderItem({
|
|
1470
|
+
...combo.config,
|
|
1471
|
+
label: 'Heading',
|
|
1472
|
+
options: {
|
|
1473
|
+
...combo.config.options,
|
|
1474
|
+
size: ReadoutListItemSize.medium,
|
|
1475
|
+
value: framed ? {alert: VALUE_FRAME} : undefined,
|
|
1476
|
+
},
|
|
1477
|
+
})}
|
|
1478
|
+
</div>
|
|
1479
|
+
</div>
|
|
1480
|
+
`
|
|
1481
|
+
)
|
|
1482
|
+
)}
|
|
1483
|
+
</div>
|
|
1484
|
+
`,
|
|
1485
|
+
};
|
|
1486
|
+
|
|
1399
1487
|
// Each row drops a different part; the per-row outline makes it obvious that the
|
|
1400
1488
|
// row height and the bottom baseline stay constant regardless of what's missing.
|
|
1401
1489
|
const MISSING_PARTS_ROWS: {title: string; config: ReadoutItemConfig}[] = [
|
|
@@ -1500,9 +1588,11 @@ export const MissingParts: Story = {
|
|
|
1500
1588
|
* value digit edges, not both. Aligning the value edges would need either a
|
|
1501
1589
|
* constant degree reserve (which widens the smaller rows' `°` — visually
|
|
1502
1590
|
* undesirable) or pinning the value edge and letting the unit column stagger;
|
|
1503
|
-
* left for the designer.
|
|
1504
|
-
* `invalid` chip
|
|
1505
|
-
*
|
|
1591
|
+
* left for the designer. Several rows also carry per-block / row state — a value
|
|
1592
|
+
* `invalid` chip (Battery), a value alert frame (Fuel), a `low-integrity` advice
|
|
1593
|
+
* chip (Pressure), and a whole-row `low-integrity` + `invalid` setpoint + a
|
|
1594
|
+
* row-level alert frame with a badge-icon flap (Distance) — to confirm none of
|
|
1595
|
+
* them shift the aligned columns.
|
|
1506
1596
|
*/
|
|
1507
1597
|
type AlignmentRow = {
|
|
1508
1598
|
label: string;
|
|
@@ -1523,6 +1613,9 @@ type AlignmentRow = {
|
|
|
1523
1613
|
hasValueIcon?: boolean;
|
|
1524
1614
|
valueDataQuality?: ReadoutListItemDataQuality;
|
|
1525
1615
|
valueAlert?: AlertFrameConfig;
|
|
1616
|
+
setpointDataQuality?: ReadoutListItemDataQuality;
|
|
1617
|
+
adviceDataQuality?: ReadoutListItemDataQuality;
|
|
1618
|
+
alert?: AlertFrameConfig;
|
|
1526
1619
|
};
|
|
1527
1620
|
|
|
1528
1621
|
const ALIGNMENT_ROWS: AlignmentRow[] = [
|
|
@@ -1548,6 +1641,8 @@ const ALIGNMENT_ROWS: AlignmentRow[] = [
|
|
|
1548
1641
|
setpoint: 1015,
|
|
1549
1642
|
hasAdvice: true,
|
|
1550
1643
|
advice: 1008,
|
|
1644
|
+
// per-block advice low-integrity — the advice chip must not shift the columns
|
|
1645
|
+
adviceDataQuality: ReadoutListItemDataQuality.lowIntegrity,
|
|
1551
1646
|
},
|
|
1552
1647
|
// negative value + fraction + low-integrity data quality
|
|
1553
1648
|
{
|
|
@@ -1557,7 +1652,10 @@ const ALIGNMENT_ROWS: AlignmentRow[] = [
|
|
|
1557
1652
|
fractionDigits: 1,
|
|
1558
1653
|
dataQuality: ReadoutListItemDataQuality.lowIntegrity,
|
|
1559
1654
|
},
|
|
1560
|
-
// long unit + advice + setpoint (the "miles" row)
|
|
1655
|
+
// long unit + advice + setpoint (the "miles" row) — also a "kitchen sink of
|
|
1656
|
+
// state": whole row low-integrity, an invalid setpoint chip, and a row-level
|
|
1657
|
+
// alert frame with a badge-icon flap, all at once, to prove none of them shift
|
|
1658
|
+
// the aligned columns.
|
|
1561
1659
|
{
|
|
1562
1660
|
label: 'Distance',
|
|
1563
1661
|
value: 4.2,
|
|
@@ -1567,6 +1665,14 @@ const ALIGNMENT_ROWS: AlignmentRow[] = [
|
|
|
1567
1665
|
setpoint: 4.5,
|
|
1568
1666
|
hasAdvice: true,
|
|
1569
1667
|
advice: 4,
|
|
1668
|
+
dataQuality: ReadoutListItemDataQuality.lowIntegrity,
|
|
1669
|
+
setpointDataQuality: ReadoutListItemDataQuality.invalid,
|
|
1670
|
+
alert: {
|
|
1671
|
+
status: AlertType.Warning,
|
|
1672
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
1673
|
+
type: ObcAlertFrameType.SmallSideFlip,
|
|
1674
|
+
showAlertCategoryIcon: true,
|
|
1675
|
+
},
|
|
1570
1676
|
},
|
|
1571
1677
|
// kitchen sink: value + setpoint + advice + leading icon + value icon
|
|
1572
1678
|
{
|
|
@@ -1612,7 +1718,7 @@ const ALIGNMENT_ROWS: AlignmentRow[] = [
|
|
|
1612
1718
|
unit: '%',
|
|
1613
1719
|
valueAlert: {
|
|
1614
1720
|
status: AlertType.Warning,
|
|
1615
|
-
mode: ObcAlertFrameMode.
|
|
1721
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
1616
1722
|
type: ObcAlertFrameType.Regular,
|
|
1617
1723
|
},
|
|
1618
1724
|
},
|
|
@@ -1651,7 +1757,7 @@ const alignmentStyle = `
|
|
|
1651
1757
|
text-transform: uppercase; letter-spacing: 0.06em; color: var(--element-neutral-color, #777);
|
|
1652
1758
|
}
|
|
1653
1759
|
.rli-align-col {
|
|
1654
|
-
display: flex; flex-direction: column; gap:
|
|
1760
|
+
display: flex; flex-direction: column; gap: 8px; width: 480px;
|
|
1655
1761
|
padding: 8px; border: 1px dashed var(--border-divider-color, #ccc); border-radius: 8px;
|
|
1656
1762
|
}
|
|
1657
1763
|
`;
|
|
@@ -1678,6 +1784,7 @@ function renderAlignmentColumn(aligned: boolean, showDebugOverlay: boolean) {
|
|
|
1678
1784
|
fractionDigits: row.fractionDigits ?? 0,
|
|
1679
1785
|
priority: row.priority,
|
|
1680
1786
|
dataQuality: row.dataQuality,
|
|
1787
|
+
alert: row.alert,
|
|
1681
1788
|
hasLeadingIcon: row.hasLeadingIcon,
|
|
1682
1789
|
value: {
|
|
1683
1790
|
hasIcon: row.hasValueIcon,
|
|
@@ -1689,6 +1796,16 @@ function renderAlignmentColumn(aligned: boolean, showDebugOverlay: boolean) {
|
|
|
1689
1796
|
// independent of each row's own fractionDigits.
|
|
1690
1797
|
...(aligned ? {spaceReserver: VALUE_RESERVER} : {}),
|
|
1691
1798
|
},
|
|
1799
|
+
// Setpoint / advice carry their per-block data quality in both
|
|
1800
|
+
// columns; the shared reserver is added only in the aligned column.
|
|
1801
|
+
setpoint: {
|
|
1802
|
+
dataQuality: row.setpointDataQuality,
|
|
1803
|
+
...(aligned ? {spaceReserver: VALUE_RESERVER} : {}),
|
|
1804
|
+
},
|
|
1805
|
+
advice: {
|
|
1806
|
+
dataQuality: row.adviceDataQuality,
|
|
1807
|
+
...(aligned ? {spaceReserver: VALUE_RESERVER} : {}),
|
|
1808
|
+
},
|
|
1692
1809
|
...(aligned
|
|
1693
1810
|
? {
|
|
1694
1811
|
// Non-degree rows reserve the degree column so their digits
|
|
@@ -1696,8 +1813,6 @@ function renderAlignmentColumn(aligned: boolean, showDebugOverlay: boolean) {
|
|
|
1696
1813
|
hasDegreeSpacer: !(row.hasDegree ?? false),
|
|
1697
1814
|
maxDigits: MAX_INTEGER_DIGITS,
|
|
1698
1815
|
unit: {spaceReserver: LONGEST_UNIT},
|
|
1699
|
-
setpoint: {spaceReserver: VALUE_RESERVER},
|
|
1700
|
-
advice: {spaceReserver: VALUE_RESERVER},
|
|
1701
1816
|
}
|
|
1702
1817
|
: {}),
|
|
1703
1818
|
},
|
|
@@ -22,7 +22,11 @@ import {
|
|
|
22
22
|
import {
|
|
23
23
|
type AlertFrameConfig,
|
|
24
24
|
wrapWithAlertFrame,
|
|
25
|
+
ObcAlertFrameType,
|
|
26
|
+
ObcAlertFrameThickness,
|
|
27
|
+
ObcAlertFrameMode,
|
|
25
28
|
} from '../../components/alert-frame/alert-frame.js';
|
|
29
|
+
import {AlertType} from '../../types.js';
|
|
26
30
|
|
|
27
31
|
// The value weight maps straight to obc-textbox's font weights (regular /
|
|
28
32
|
// semibold / bold). Re-exported so consumers can set `valueOptions.weight`
|
|
@@ -96,6 +100,14 @@ export interface ReadoutBlockState {
|
|
|
96
100
|
alert?: false | AlertFrameConfig;
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Per-value options. Unlike setpoint/advice, the value's `alert` frame AND
|
|
105
|
+
* `dataQuality` chip wrap the whole reading — value (+ value-icon) + degree +
|
|
106
|
+
* trailing unit — rather than the value alone. The alert frame is a pure overlay
|
|
107
|
+
* (4px/2px padding, stroke centred on that line); the data-quality chip reuses
|
|
108
|
+
* the block chip's `outline` styling with no extra padding. Neither shifts
|
|
109
|
+
* content or changes the row height / column alignment (Figma 58:10120).
|
|
110
|
+
*/
|
|
99
111
|
export interface ReadoutValueOptions extends ReadoutBlockState {
|
|
100
112
|
/** Render the unfilled leading positions as muted zeroes (requires `maxDigits`). */
|
|
101
113
|
hintedZeros?: boolean;
|
|
@@ -661,6 +673,34 @@ export class ObcReadoutListItem extends LitElement {
|
|
|
661
673
|
alert: this.setpointOptions?.alert,
|
|
662
674
|
})
|
|
663
675
|
: nothing}
|
|
676
|
+
${this.renderValueReading()}
|
|
677
|
+
</div>
|
|
678
|
+
`;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* The value reading: the value block, its degree column and the trailing unit
|
|
683
|
+
* grouped in one relatively-positioned wrapper so the value alert frame AND the
|
|
684
|
+
* value data-quality chip can wrap value + degree + unit together (Figma
|
|
685
|
+
* 58:10120).
|
|
686
|
+
*
|
|
687
|
+
* Moving the degree / unit into this wrapper (the new last child of
|
|
688
|
+
* `.value-cluster`) preserves every existing gap, so the underlying content
|
|
689
|
+
* stays column-aligned with or without the frame / chip. Both the value alert
|
|
690
|
+
* (overlay) and the value data-quality chip are applied here — NOT inside
|
|
691
|
+
* `obc-readout-block` — so they extend over the unit; setpoint/advice keep their
|
|
692
|
+
* own block-level frame and chip. The chip uses `outline` (not `border`), so it
|
|
693
|
+
* never shifts the value's width / column alignment.
|
|
694
|
+
*/
|
|
695
|
+
private renderValueReading(): TemplateResult {
|
|
696
|
+
return html`
|
|
697
|
+
<div
|
|
698
|
+
class=${classMap({
|
|
699
|
+
'value-reading': true,
|
|
700
|
+
...this.dataQualityClasses(this.valueOptions?.dataQuality),
|
|
701
|
+
})}
|
|
702
|
+
part="value-reading"
|
|
703
|
+
>
|
|
664
704
|
${this.hasValue
|
|
665
705
|
? this.renderBlock({
|
|
666
706
|
variant: ReadoutBlockVariant.value,
|
|
@@ -672,10 +712,52 @@ export class ObcReadoutListItem extends LitElement {
|
|
|
672
712
|
spaceReserver: this.valueOptions?.spaceReserver,
|
|
673
713
|
off: this.off,
|
|
674
714
|
hasIcon: this.valueOptions?.hasIcon ?? false,
|
|
675
|
-
dataQuality: this.valueOptions?.dataQuality,
|
|
676
|
-
alert: this.valueOptions?.alert,
|
|
677
715
|
})
|
|
678
716
|
: nothing}
|
|
717
|
+
${this.renderValueUnitGap()}
|
|
718
|
+
<div class="unit-area" part="unit-area">
|
|
719
|
+
${this.renderTrailingUnit()} ${this.renderDegreeSpacer()}
|
|
720
|
+
</div>
|
|
721
|
+
${this.renderValueAlertOverlay()}
|
|
722
|
+
</div>
|
|
723
|
+
`;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* The value alert frame, drawn as a pure overlay around the value reading
|
|
728
|
+
* (value + degree + unit). It reserves no space — the `obc-alert-frame` sits in
|
|
729
|
+
* an absolutely-positioned box offset outward (see `.value-alert-overlay` in
|
|
730
|
+
* the CSS) so its stroke is centred on the 4px/2px padding line and toggling it
|
|
731
|
+
* never shifts content or row height. Renders only when `valueOptions.alert` is
|
|
732
|
+
* a config object.
|
|
733
|
+
*/
|
|
734
|
+
private renderValueAlertOverlay(): TemplateResult | typeof nothing {
|
|
735
|
+
const alert = this.valueOptions?.alert;
|
|
736
|
+
if (typeof alert !== 'object' || alert === null) {
|
|
737
|
+
return nothing;
|
|
738
|
+
}
|
|
739
|
+
const thickness = alert.thickness ?? ObcAlertFrameThickness.Small;
|
|
740
|
+
return html`
|
|
741
|
+
<div
|
|
742
|
+
class=${classMap({
|
|
743
|
+
'value-alert-overlay': true,
|
|
744
|
+
// The outward offset is thickness-dependent (see the CSS): large frames
|
|
745
|
+
// draw a wider outline, so the box must sit further out to stay centred.
|
|
746
|
+
'thickness-large': thickness === ObcAlertFrameThickness.Large,
|
|
747
|
+
})}
|
|
748
|
+
aria-hidden="true"
|
|
749
|
+
>
|
|
750
|
+
<obc-alert-frame
|
|
751
|
+
part="value-alert-frame"
|
|
752
|
+
.type=${alert.type ?? ObcAlertFrameType.Regular}
|
|
753
|
+
.thickness=${thickness}
|
|
754
|
+
.status=${alert.status ?? AlertType.Alarm}
|
|
755
|
+
.mode=${alert.mode ?? ObcAlertFrameMode.ackedActive}
|
|
756
|
+
.showIcon=${alert.showIcon ?? false}
|
|
757
|
+
.showAlertCategoryIcon=${alert.showAlertCategoryIcon ?? true}
|
|
758
|
+
.wrapContent=${false}
|
|
759
|
+
.fullWidth=${false}
|
|
760
|
+
></obc-alert-frame>
|
|
679
761
|
</div>
|
|
680
762
|
`;
|
|
681
763
|
}
|
|
@@ -753,10 +835,7 @@ export class ObcReadoutListItem extends LitElement {
|
|
|
753
835
|
<div class="content" part="content">
|
|
754
836
|
${this.renderLabelContainer()}
|
|
755
837
|
<div class="value-area" part="value-area">
|
|
756
|
-
${this.renderValueCluster()}
|
|
757
|
-
<div class="unit-area" part="unit-area">
|
|
758
|
-
${this.renderTrailingUnit()} ${this.renderDegreeSpacer()}
|
|
759
|
-
</div>
|
|
838
|
+
${this.renderValueCluster()}
|
|
760
839
|
</div>
|
|
761
840
|
${this.renderTrailingSource()}
|
|
762
841
|
</div>
|