@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.88 → 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.
Files changed (31) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +17477 -16780
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +742 -43
  4. package/dist/building-blocks/readout-block/readout-block.css.js +229 -0
  5. package/dist/building-blocks/readout-block/readout-block.css.js.map +1 -0
  6. package/dist/building-blocks/readout-block/readout-block.d.ts +146 -0
  7. package/dist/building-blocks/readout-block/readout-block.d.ts.map +1 -0
  8. package/dist/building-blocks/readout-block/readout-block.js +273 -0
  9. package/dist/building-blocks/readout-block/readout-block.js.map +1 -0
  10. package/dist/navigation-instruments/readout-list/readout-list.css.js +26 -0
  11. package/dist/navigation-instruments/readout-list/readout-list.css.js.map +1 -0
  12. package/dist/navigation-instruments/readout-list/readout-list.d.ts +70 -0
  13. package/dist/navigation-instruments/readout-list/readout-list.d.ts.map +1 -0
  14. package/dist/navigation-instruments/readout-list/readout-list.js +154 -0
  15. package/dist/navigation-instruments/readout-list/readout-list.js.map +1 -0
  16. package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js +139 -124
  17. package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js.map +1 -1
  18. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts +57 -26
  19. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
  20. package/dist/navigation-instruments/readout-list-item/readout-list-item.js +139 -125
  21. package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
  22. package/package.json +1 -1
  23. package/src/building-blocks/readout-block/readout-block.css +192 -0
  24. package/src/building-blocks/readout-block/readout-block.stories.ts +347 -0
  25. package/src/building-blocks/readout-block/readout-block.ts +355 -0
  26. package/src/navigation-instruments/readout-list/readout-list.css +15 -0
  27. package/src/navigation-instruments/readout-list/readout-list.stories.ts +358 -0
  28. package/src/navigation-instruments/readout-list/readout-list.ts +216 -0
  29. package/src/navigation-instruments/readout-list-item/readout-list-item.css +141 -120
  30. package/src/navigation-instruments/readout-list-item/readout-list-item.stories.ts +145 -29
  31. package/src/navigation-instruments/readout-list-item/readout-list-item.ts +169 -146
@@ -7,6 +7,55 @@
7
7
  width: 100%;
8
8
  }
9
9
 
10
+ /* Bridge the list-item's public colour custom properties onto the block's, so
11
+ consumers keep theming the value / setpoint / advice / hinted-zero colours via
12
+ `--obc-readout-list-item-*` even though they now render inside obc-readout-block. */
13
+ obc-readout-block {
14
+ --obc-readout-block-value-color: var(
15
+ --obc-readout-list-item-value-color,
16
+ var(--element-neutral-color)
17
+ );
18
+ --obc-readout-block-value-enhanced-color: var(
19
+ --obc-readout-list-item-value-enhanced-color,
20
+ var(--element-neutral-enhanced-color)
21
+ );
22
+ --obc-readout-block-setpoint-color: var(
23
+ --obc-readout-list-item-setpoint-color,
24
+ var(--element-neutral-color)
25
+ );
26
+ --obc-readout-block-setpoint-enhanced-color: var(
27
+ --obc-readout-list-item-setpoint-enhanced-color,
28
+ var(--instrument-enhanced-secondary-color)
29
+ );
30
+ --obc-readout-block-setpoint-touching-outline-color: var(
31
+ --obc-readout-list-item-setpoint-touching-outline-color,
32
+ var(--element-neutral-enhanced-color)
33
+ );
34
+ --obc-readout-block-setpoint-touching-color: var(
35
+ --obc-readout-list-item-setpoint-touching-color,
36
+ var(--base-blue-100)
37
+ );
38
+ --obc-readout-block-advice-color: var(
39
+ --obc-readout-list-item-advice-color,
40
+ var(--element-neutral-color)
41
+ );
42
+ --obc-readout-block-hinted-color: var(
43
+ --obc-readout-list-item-hinted-color,
44
+ var(--element-inactive-color)
45
+ );
46
+ }
47
+
48
+ /* Size the consumer-provided block icons forwarded into obc-readout-block's
49
+ `icon` slot (the block sizes the forwarded slot element; this sizes the
50
+ consumer's icon to fill it). */
51
+ ::slotted([slot="value-icon"]),
52
+ ::slotted([slot="setpoint-icon"]),
53
+ ::slotted([slot="advice-icon"]) {
54
+ display: block;
55
+ inline-size: 100%;
56
+ block-size: 100%;
57
+ }
58
+
10
59
  /* ---------- Root / surface ----------
11
60
  * `.root` is the touch target (a <button> when clickable, otherwise a <div>).
12
61
  * `.surface` is the visible box that carries padding, corner radius, the
@@ -140,115 +189,84 @@
140
189
  align-items: flex-end;
141
190
  }
142
191
 
143
- /* ---------- Readout building block (advice / setpoint / value) ---------- */
144
- .block {
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;
145
198
  display: inline-flex;
146
199
  align-items: flex-end;
147
200
  }
148
201
 
149
- /* Number + its (setpoint/advice) degree glyph, hugging with no gap — the
150
- `.block` gap only separates the icon from this group. */
151
- .block-content {
152
- display: inline-flex;
153
- align-items: flex-end;
154
- }
155
-
156
- .block-icon {
157
- display: inline-flex;
158
- align-items: center;
159
- justify-content: center;
160
- flex: none;
161
- color: inherit;
162
- /* Centre the icon against the block's text rather than bottom-aligning it (the
163
- block is flex-end). Matters for the value block, whose text is taller than
164
- the icon, so the value icon lines up with the number like the smaller
165
- setpoint/advice icons do. */
166
- align-self: center;
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);
167
214
  }
168
-
169
- .block-icon obi-input-right,
170
- .block-icon obi-notification-advice,
171
- .block-icon ::slotted(*) {
172
- display: block;
173
- inline-size: 100%;
174
- block-size: 100%;
215
+ .value-reading.data-low-integrity {
216
+ background: var(--alert-low-integrity-background-color);
217
+ outline-color: var(--alert-low-integrity-border-color);
175
218
  }
176
-
177
- .hinted-zero {
178
- color: var(
179
- --obc-readout-list-item-hinted-color,
180
- var(--element-inactive-color)
181
- );
219
+ .value-reading.data-invalid {
220
+ background: var(--alert-invalid-background-color);
221
+ outline-color: var(--alert-invalid-border-color);
182
222
  }
183
223
 
184
- /* ---------- Text colours (overridable via custom properties) ----------
185
- * obc-textbox does not set its own colour, so it inherits these. */
186
- .block-value {
187
- color: var(--obc-readout-list-item-value-color, var(--element-neutral-color));
188
- }
189
- .block-value.tone-enhanced {
190
- color: var(
191
- --obc-readout-list-item-value-enhanced-color,
192
- var(--element-neutral-enhanced-color)
193
- );
194
- }
195
- /* The setpoint shares the value's colour state: neutral by default, enhanced
196
- (blue) only when the row is enhanced never a blue setpoint next to a grey
197
- value. `--instrument-enhanced-secondary-color` is the same #2d548b as the
198
- value's `--element-neutral-enhanced-color`. */
199
- .block-setpoint {
200
- color: var(
201
- --obc-readout-list-item-setpoint-color,
202
- var(--element-neutral-color)
203
- );
204
- /* Flip-flop emphasis + pop-up fade animate at 100ms. */
205
- transition:
206
- opacity 100ms ease,
207
- color 100ms ease;
208
- }
209
- .block-setpoint.tone-enhanced {
210
- color: var(
211
- --obc-readout-list-item-setpoint-enhanced-color,
212
- var(--instrument-enhanced-secondary-color)
213
- );
214
- }
215
- /* Pop-up: fade the setpoint out once value === setpoint, keeping its space so
216
- the value stays column-aligned. */
217
- .block-setpoint.is-hiding {
218
- opacity: 0;
219
- }
220
- .block-setpoint.is-hidden {
221
- opacity: 0;
222
- visibility: hidden;
223
- }
224
- /* Touching / focus state: the setpoint triangle takes the marker's focus look — a
225
- pale fill (--base-blue-100) with a dark 1px edge (--element-neutral-enhanced),
226
- mirroring `svghelpers/setpoint.ts` focus (fill base-blue-100 + 2px stroke).
227
- The glyph icon is `fill: currentColor` and can't take a CSS `stroke`, so the
228
- edge is layered drop-shadows. The setpoint number keeps its normal, readable
229
- colour. */
230
- .block-setpoint.touching {
231
- --_touching-outline: var(
232
- --obc-readout-list-item-setpoint-touching-outline-color,
233
- var(--element-neutral-enhanced-color)
234
- );
235
- }
236
- .block-setpoint.touching .block-icon {
237
- color: var(
238
- --obc-readout-list-item-setpoint-touching-color,
239
- var(--base-blue-100)
240
- );
241
- filter: drop-shadow(1px 0 0 var(--_touching-outline))
242
- drop-shadow(-1px 0 0 var(--_touching-outline))
243
- drop-shadow(0 1px 0 var(--_touching-outline))
244
- drop-shadow(0 -1px 0 var(--_touching-outline));
245
- }
246
- .block-advice {
247
- color: var(
248
- --obc-readout-list-item-advice-color,
249
- var(--element-neutral-color)
250
- );
251
- }
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
+
266
+ /* The readout building blocks (advice / setpoint / value) now render inside
267
+ `obc-readout-block`; their layout / colour / hinted-zero / data-quality CSS
268
+ lives there. The list-item only bridges the public colour vars (see top) and
269
+ keeps the surrounding row layout below. */
252
270
 
253
271
  .label {
254
272
  color: var(
@@ -266,23 +284,19 @@
266
284
  );
267
285
  }
268
286
 
269
- /* ---------- Per-block data quality ----------
287
+ /* ---------- Source data quality ----------
270
288
  * Independent of (and nests inside) the row-level data-quality frame. Uses
271
- * `outline` (not `border`) so the chip never shifts the block's width — the
272
- * value stays column-aligned across rows. */
273
- .block.data-low-integrity,
274
- .block.data-invalid,
289
+ * `outline` (not `border`) so the chip never shifts the source's width. (The
290
+ * per-block value/setpoint/advice chips live in `obc-readout-block`.) */
275
291
  .source.data-low-integrity,
276
292
  .source.data-invalid {
277
293
  outline: 1px solid;
278
294
  border-radius: var(--global-border-radius-border-radius-check);
279
295
  }
280
- .block.data-low-integrity,
281
296
  .source.data-low-integrity {
282
297
  background: var(--alert-low-integrity-background-color);
283
298
  outline-color: var(--alert-low-integrity-border-color);
284
299
  }
285
- .block.data-invalid,
286
300
  .source.data-invalid {
287
301
  background: var(--alert-invalid-background-color);
288
302
  outline-color: var(--alert-invalid-border-color);
@@ -407,10 +421,6 @@
407
421
  --instrument-components-readout-new-list-item-regular-value-container-gap
408
422
  );
409
423
  }
410
- .size-small .block {
411
- gap: var(--instrument-components-readout-new-general-regular-icon-gap);
412
- }
413
- .size-small .block-icon,
414
424
  .size-small .leading-icon {
415
425
  inline-size: var(
416
426
  --instrument-components-readout-new-general-regular-icon-size
@@ -443,10 +453,6 @@
443
453
  --instrument-components-readout-new-list-item-medium-value-container-gap
444
454
  );
445
455
  }
446
- .size-medium .block {
447
- gap: var(--instrument-components-readout-new-general-medium-icon-gap);
448
- }
449
- .size-medium .block-icon,
450
456
  .size-medium .leading-icon {
451
457
  inline-size: var(
452
458
  --instrument-components-readout-new-general-medium-icon-size
@@ -474,10 +480,6 @@
474
480
  .size-large .value-cluster {
475
481
  gap: var(--instrument-components-readout-new-list-item-large-value-gap);
476
482
  }
477
- .size-large .block {
478
- gap: var(--instrument-components-readout-new-general-large-icon-gap);
479
- }
480
- .size-large .block-icon,
481
483
  .size-large .leading-icon {
482
484
  inline-size: var(--instrument-components-readout-new-general-large-icon-size);
483
485
  block-size: var(--instrument-components-readout-new-general-large-icon-size);
@@ -517,3 +519,22 @@
517
519
  );
518
520
  --_obc-readout-list-item-data-border: var(--alert-invalid-border-color);
519
521
  }
522
+
523
+ /* ---------- Debug overlay ----------
524
+ * `showDebugOverlay` outlines each readout building block (red), the degree
525
+ * columns (blue) and the degree spacer (green) so reserver widths / alignment
526
+ * are visible. A development aid (mirrors the donut chart's `showDebugOverlay`);
527
+ * off by default. */
528
+ :host([showDebugOverlay]) obc-readout-block::part(block) {
529
+ outline: 1px solid rgba(220, 0, 0, 0.7);
530
+ outline-offset: -1px;
531
+ }
532
+ :host([showDebugOverlay]) obc-readout-block::part(degree),
533
+ :host([showDebugOverlay]) .degree-column {
534
+ outline: 1px solid rgba(0, 0, 220, 0.7);
535
+ outline-offset: -1px;
536
+ }
537
+ :host([showDebugOverlay]) .degree-spacer {
538
+ outline: 1px solid rgba(0, 160, 0, 0.8);
539
+ outline-offset: -1px;
540
+ }
@@ -99,6 +99,7 @@ type ReadoutItemConfig = {
99
99
  options?: StoryOptions;
100
100
  hasLeadingIcon?: boolean;
101
101
  hasValueIcon?: boolean;
102
+ showDebugOverlay?: boolean;
102
103
  };
103
104
 
104
105
  type ShowcaseCase = {label: string; config: ReadoutItemConfig};
@@ -179,6 +180,7 @@ function renderItem(config: ReadoutItemConfig) {
179
180
  .adviceOptions=${o.advice}
180
181
  .unitOptions=${o.unit}
181
182
  .srcOptions=${o.src}
183
+ .showDebugOverlay=${config.showDebugOverlay ?? false}
182
184
  >
183
185
  ${config.hasLeadingIcon
184
186
  ? html`<obi-placeholder slot="leading-icon"></obi-placeholder>`
@@ -1260,7 +1262,7 @@ export const PerBlockState: Story = {
1260
1262
  value: {
1261
1263
  alert: {
1262
1264
  status: AlertType.Warning,
1263
- mode: ObcAlertFrameMode.unackedActive,
1265
+ mode: ObcAlertFrameMode.ackedActive,
1264
1266
  type: ObcAlertFrameType.Regular,
1265
1267
  },
1266
1268
  },
@@ -1294,7 +1296,7 @@ export const PerBlockState: Story = {
1294
1296
  value: {
1295
1297
  alert: {
1296
1298
  status: AlertType.Warning,
1297
- mode: ObcAlertFrameMode.unackedActive,
1299
+ mode: ObcAlertFrameMode.ackedActive,
1298
1300
  type: ObcAlertFrameType.Regular,
1299
1301
  },
1300
1302
  },
@@ -1320,7 +1322,7 @@ export const PerBlockState: Story = {
1320
1322
  dataQuality: ReadoutListItemDataQuality.lowIntegrity,
1321
1323
  alert: {
1322
1324
  status: AlertType.Warning,
1323
- mode: ObcAlertFrameMode.unackedActive,
1325
+ mode: ObcAlertFrameMode.ackedActive,
1324
1326
  type: ObcAlertFrameType.SmallSideFlip,
1325
1327
  showAlertCategoryIcon: true,
1326
1328
  },
@@ -1335,7 +1337,7 @@ export const PerBlockState: Story = {
1335
1337
  dataQuality: ReadoutListItemDataQuality.invalid,
1336
1338
  alert: {
1337
1339
  status: AlertType.Alarm,
1338
- mode: ObcAlertFrameMode.unackedActive,
1340
+ mode: ObcAlertFrameMode.ackedActive,
1339
1341
  type: ObcAlertFrameType.LargeSideFlip,
1340
1342
  showAlertCategoryIcon: true,
1341
1343
  },
@@ -1394,6 +1396,94 @@ export const Alarm: Story = {
1394
1396
  }),
1395
1397
  };
1396
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
+
1397
1487
  // Each row drops a different part; the per-row outline makes it obvious that the
1398
1488
  // row height and the bottom baseline stay constant regardless of what's missing.
1399
1489
  const MISSING_PARTS_ROWS: {title: string; config: ReadoutItemConfig}[] = [
@@ -1498,9 +1588,11 @@ export const MissingParts: Story = {
1498
1588
  * value digit edges, not both. Aligning the value edges would need either a
1499
1589
  * constant degree reserve (which widens the smaller rows' `°` — visually
1500
1590
  * undesirable) or pinning the value edge and letting the unit column stagger;
1501
- * left for the designer. Two rows also carry per-block (nested) state — a value
1502
- * `invalid` chip and a value alert frame to confirm per-block state does not
1503
- * shift the value edge.
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.
1504
1596
  */
1505
1597
  type AlignmentRow = {
1506
1598
  label: string;
@@ -1521,6 +1613,9 @@ type AlignmentRow = {
1521
1613
  hasValueIcon?: boolean;
1522
1614
  valueDataQuality?: ReadoutListItemDataQuality;
1523
1615
  valueAlert?: AlertFrameConfig;
1616
+ setpointDataQuality?: ReadoutListItemDataQuality;
1617
+ adviceDataQuality?: ReadoutListItemDataQuality;
1618
+ alert?: AlertFrameConfig;
1524
1619
  };
1525
1620
 
1526
1621
  const ALIGNMENT_ROWS: AlignmentRow[] = [
@@ -1546,6 +1641,8 @@ const ALIGNMENT_ROWS: AlignmentRow[] = [
1546
1641
  setpoint: 1015,
1547
1642
  hasAdvice: true,
1548
1643
  advice: 1008,
1644
+ // per-block advice low-integrity — the advice chip must not shift the columns
1645
+ adviceDataQuality: ReadoutListItemDataQuality.lowIntegrity,
1549
1646
  },
1550
1647
  // negative value + fraction + low-integrity data quality
1551
1648
  {
@@ -1555,7 +1652,10 @@ const ALIGNMENT_ROWS: AlignmentRow[] = [
1555
1652
  fractionDigits: 1,
1556
1653
  dataQuality: ReadoutListItemDataQuality.lowIntegrity,
1557
1654
  },
1558
- // 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.
1559
1659
  {
1560
1660
  label: 'Distance',
1561
1661
  value: 4.2,
@@ -1565,6 +1665,14 @@ const ALIGNMENT_ROWS: AlignmentRow[] = [
1565
1665
  setpoint: 4.5,
1566
1666
  hasAdvice: true,
1567
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
+ },
1568
1676
  },
1569
1677
  // kitchen sink: value + setpoint + advice + leading icon + value icon
1570
1678
  {
@@ -1610,7 +1718,7 @@ const ALIGNMENT_ROWS: AlignmentRow[] = [
1610
1718
  unit: '%',
1611
1719
  valueAlert: {
1612
1720
  status: AlertType.Warning,
1613
- mode: ObcAlertFrameMode.unackedActive,
1721
+ mode: ObcAlertFrameMode.ackedActive,
1614
1722
  type: ObcAlertFrameType.Regular,
1615
1723
  },
1616
1724
  },
@@ -1649,24 +1757,12 @@ const alignmentStyle = `
1649
1757
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--element-neutral-color, #777);
1650
1758
  }
1651
1759
  .rli-align-col {
1652
- display: flex; flex-direction: column; gap: 2px; width: 480px;
1760
+ display: flex; flex-direction: column; gap: 8px; width: 480px;
1653
1761
  padding: 8px; border: 1px dashed var(--border-divider-color, #ccc); border-radius: 8px;
1654
1762
  }
1655
- /* Debug aid (temporary): outline each readout building block (red) and the
1656
- degree column (blue) so reserver widths / alignment are visible. Scoped to
1657
- this story only via .rli-align-wrap — safe to delete when no longer needed. */
1658
- .rli-align-wrap obc-readout-list-item::part(block) {
1659
- outline: 1px solid rgba(220, 0, 0, 0.7); outline-offset: -1px;
1660
- }
1661
- .rli-align-wrap obc-readout-list-item::part(degree) {
1662
- outline: 1px solid rgba(0, 0, 220, 0.7); outline-offset: -1px;
1663
- }
1664
- .rli-align-wrap obc-readout-list-item::part(degree-spacer) {
1665
- outline: 1px solid rgba(0, 160, 0, 0.8); outline-offset: -1px;
1666
- }
1667
1763
  `;
1668
1764
 
1669
- function renderAlignmentColumn(aligned: boolean) {
1765
+ function renderAlignmentColumn(aligned: boolean, showDebugOverlay: boolean) {
1670
1766
  return html`
1671
1767
  <div class="rli-align-col">
1672
1768
  ${ALIGNMENT_ROWS.map((row) =>
@@ -1681,12 +1777,14 @@ function renderAlignmentColumn(aligned: boolean) {
1681
1777
  advice: row.advice,
1682
1778
  hasLeadingIcon: row.hasLeadingIcon,
1683
1779
  hasValueIcon: row.hasValueIcon,
1780
+ showDebugOverlay,
1684
1781
  options: {
1685
1782
  size: row.size ?? ReadoutListItemSize.small,
1686
1783
  hasDegree: row.hasDegree ?? false,
1687
1784
  fractionDigits: row.fractionDigits ?? 0,
1688
1785
  priority: row.priority,
1689
1786
  dataQuality: row.dataQuality,
1787
+ alert: row.alert,
1690
1788
  hasLeadingIcon: row.hasLeadingIcon,
1691
1789
  value: {
1692
1790
  hasIcon: row.hasValueIcon,
@@ -1698,6 +1796,16 @@ function renderAlignmentColumn(aligned: boolean) {
1698
1796
  // independent of each row's own fractionDigits.
1699
1797
  ...(aligned ? {spaceReserver: VALUE_RESERVER} : {}),
1700
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
+ },
1701
1809
  ...(aligned
1702
1810
  ? {
1703
1811
  // Non-degree rows reserve the degree column so their digits
@@ -1705,8 +1813,6 @@ function renderAlignmentColumn(aligned: boolean) {
1705
1813
  hasDegreeSpacer: !(row.hasDegree ?? false),
1706
1814
  maxDigits: MAX_INTEGER_DIGITS,
1707
1815
  unit: {spaceReserver: LONGEST_UNIT},
1708
- setpoint: {spaceReserver: VALUE_RESERVER},
1709
- advice: {spaceReserver: VALUE_RESERVER},
1710
1816
  }
1711
1817
  : {}),
1712
1818
  },
@@ -1716,8 +1822,18 @@ function renderAlignmentColumn(aligned: boolean) {
1716
1822
  `;
1717
1823
  }
1718
1824
 
1719
- export const ColumnAlignment: Story = {
1720
- render: () => html`
1825
+ export const ColumnAlignment: StoryObj<
1826
+ ReadoutListItemStoryArgs & {showDebugOverlay: boolean}
1827
+ > = {
1828
+ args: {showDebugOverlay: true},
1829
+ argTypes: {
1830
+ showDebugOverlay: {
1831
+ name: 'Show Debug Overlay',
1832
+ control: {type: 'boolean'},
1833
+ table: {category: 'Debug'},
1834
+ },
1835
+ },
1836
+ render: (args) => html`
1721
1837
  <style>
1722
1838
  ${alignmentStyle}
1723
1839
  </style>
@@ -1726,13 +1842,13 @@ export const ColumnAlignment: Story = {
1726
1842
  <h3 class="rli-align-section-title">
1727
1843
  Without reservers — columns drift
1728
1844
  </h3>
1729
- ${renderAlignmentColumn(false)}
1845
+ ${renderAlignmentColumn(false, args.showDebugOverlay)}
1730
1846
  </section>
1731
1847
  <section class="rli-align-section">
1732
1848
  <h3 class="rli-align-section-title">
1733
1849
  With shared reservers — unit column &amp; value right edge aligned
1734
1850
  </h3>
1735
- ${renderAlignmentColumn(true)}
1851
+ ${renderAlignmentColumn(true, args.showDebugOverlay)}
1736
1852
  </section>
1737
1853
  </div>
1738
1854
  `,