@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.87 → 2.0.0-next.89

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 (40) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +17409 -16824
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +755 -44
  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/gauge-radial/gauge-radial.css.js +58 -32
  11. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js.map +1 -1
  12. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts +28 -0
  13. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts.map +1 -1
  14. package/dist/navigation-instruments/gauge-radial/gauge-radial.js +25 -2
  15. package/dist/navigation-instruments/gauge-radial/gauge-radial.js.map +1 -1
  16. package/dist/navigation-instruments/readout-list/readout-list.css.js +24 -0
  17. package/dist/navigation-instruments/readout-list/readout-list.css.js.map +1 -0
  18. package/dist/navigation-instruments/readout-list/readout-list.d.ts +70 -0
  19. package/dist/navigation-instruments/readout-list/readout-list.d.ts.map +1 -0
  20. package/dist/navigation-instruments/readout-list/readout-list.js +154 -0
  21. package/dist/navigation-instruments/readout-list/readout-list.js.map +1 -0
  22. package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js +80 -145
  23. package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js.map +1 -1
  24. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts +25 -26
  25. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
  26. package/dist/navigation-instruments/readout-list-item/readout-list-item.js +66 -118
  27. package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/building-blocks/readout-block/readout-block.css +192 -0
  30. package/src/building-blocks/readout-block/readout-block.stories.ts +346 -0
  31. package/src/building-blocks/readout-block/readout-block.ts +355 -0
  32. package/src/navigation-instruments/gauge-radial/gauge-radial.css +53 -30
  33. package/src/navigation-instruments/gauge-radial/gauge-radial.stories.ts +50 -0
  34. package/src/navigation-instruments/gauge-radial/gauge-radial.ts +35 -0
  35. package/src/navigation-instruments/readout-list/readout-list.css +13 -0
  36. package/src/navigation-instruments/readout-list/readout-list.stories.ts +275 -0
  37. package/src/navigation-instruments/readout-list/readout-list.ts +216 -0
  38. package/src/navigation-instruments/readout-list-item/readout-list-item.css +75 -128
  39. package/src/navigation-instruments/readout-list-item/readout-list-item.stories.ts +18 -17
  40. package/src/navigation-instruments/readout-list-item/readout-list-item.ts +84 -140
@@ -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,10 @@
140
189
  align-items: flex-end;
141
190
  }
142
191
 
143
- /* ---------- Readout building block (advice / setpoint / value) ---------- */
144
- .block {
145
- display: inline-flex;
146
- align-items: flex-end;
147
- }
148
-
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;
167
- }
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%;
175
- }
176
-
177
- .hinted-zero {
178
- color: var(
179
- --obc-readout-list-item-hinted-color,
180
- var(--element-inactive-color)
181
- );
182
- }
183
-
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
- }
192
+ /* The readout building blocks (advice / setpoint / value) now render inside
193
+ `obc-readout-block`; their layout / colour / hinted-zero / data-quality CSS
194
+ lives there. The list-item only bridges the public colour vars (see top) and
195
+ keeps the surrounding row layout below. */
252
196
 
253
197
  .label {
254
198
  color: var(
@@ -266,23 +210,19 @@
266
210
  );
267
211
  }
268
212
 
269
- /* ---------- Per-block data quality ----------
213
+ /* ---------- Source data quality ----------
270
214
  * 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,
215
+ * `outline` (not `border`) so the chip never shifts the source's width. (The
216
+ * per-block value/setpoint/advice chips live in `obc-readout-block`.) */
275
217
  .source.data-low-integrity,
276
218
  .source.data-invalid {
277
219
  outline: 1px solid;
278
220
  border-radius: var(--global-border-radius-border-radius-check);
279
221
  }
280
- .block.data-low-integrity,
281
222
  .source.data-low-integrity {
282
223
  background: var(--alert-low-integrity-background-color);
283
224
  outline-color: var(--alert-low-integrity-border-color);
284
225
  }
285
- .block.data-invalid,
286
226
  .source.data-invalid {
287
227
  background: var(--alert-invalid-background-color);
288
228
  outline-color: var(--alert-invalid-border-color);
@@ -407,10 +347,6 @@
407
347
  --instrument-components-readout-new-list-item-regular-value-container-gap
408
348
  );
409
349
  }
410
- .size-small .block {
411
- gap: var(--instrument-components-readout-new-general-regular-icon-gap);
412
- }
413
- .size-small .block-icon,
414
350
  .size-small .leading-icon {
415
351
  inline-size: var(
416
352
  --instrument-components-readout-new-general-regular-icon-size
@@ -443,10 +379,6 @@
443
379
  --instrument-components-readout-new-list-item-medium-value-container-gap
444
380
  );
445
381
  }
446
- .size-medium .block {
447
- gap: var(--instrument-components-readout-new-general-medium-icon-gap);
448
- }
449
- .size-medium .block-icon,
450
382
  .size-medium .leading-icon {
451
383
  inline-size: var(
452
384
  --instrument-components-readout-new-general-medium-icon-size
@@ -474,10 +406,6 @@
474
406
  .size-large .value-cluster {
475
407
  gap: var(--instrument-components-readout-new-list-item-large-value-gap);
476
408
  }
477
- .size-large .block {
478
- gap: var(--instrument-components-readout-new-general-large-icon-gap);
479
- }
480
- .size-large .block-icon,
481
409
  .size-large .leading-icon {
482
410
  inline-size: var(--instrument-components-readout-new-general-large-icon-size);
483
411
  block-size: var(--instrument-components-readout-new-general-large-icon-size);
@@ -517,3 +445,22 @@
517
445
  );
518
446
  --_obc-readout-list-item-data-border: var(--alert-invalid-border-color);
519
447
  }
448
+
449
+ /* ---------- Debug overlay ----------
450
+ * `showDebugOverlay` outlines each readout building block (red), the degree
451
+ * columns (blue) and the degree spacer (green) so reserver widths / alignment
452
+ * are visible. A development aid (mirrors the donut chart's `showDebugOverlay`);
453
+ * off by default. */
454
+ :host([showDebugOverlay]) obc-readout-block::part(block) {
455
+ outline: 1px solid rgba(220, 0, 0, 0.7);
456
+ outline-offset: -1px;
457
+ }
458
+ :host([showDebugOverlay]) obc-readout-block::part(degree),
459
+ :host([showDebugOverlay]) .degree-column {
460
+ outline: 1px solid rgba(0, 0, 220, 0.7);
461
+ outline-offset: -1px;
462
+ }
463
+ :host([showDebugOverlay]) .degree-spacer {
464
+ outline: 1px solid rgba(0, 160, 0, 0.8);
465
+ outline-offset: -1px;
466
+ }
@@ -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>`
@@ -1652,21 +1654,9 @@ const alignmentStyle = `
1652
1654
  display: flex; flex-direction: column; gap: 2px; width: 480px;
1653
1655
  padding: 8px; border: 1px dashed var(--border-divider-color, #ccc); border-radius: 8px;
1654
1656
  }
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
1657
  `;
1668
1658
 
1669
- function renderAlignmentColumn(aligned: boolean) {
1659
+ function renderAlignmentColumn(aligned: boolean, showDebugOverlay: boolean) {
1670
1660
  return html`
1671
1661
  <div class="rli-align-col">
1672
1662
  ${ALIGNMENT_ROWS.map((row) =>
@@ -1681,6 +1671,7 @@ function renderAlignmentColumn(aligned: boolean) {
1681
1671
  advice: row.advice,
1682
1672
  hasLeadingIcon: row.hasLeadingIcon,
1683
1673
  hasValueIcon: row.hasValueIcon,
1674
+ showDebugOverlay,
1684
1675
  options: {
1685
1676
  size: row.size ?? ReadoutListItemSize.small,
1686
1677
  hasDegree: row.hasDegree ?? false,
@@ -1716,8 +1707,18 @@ function renderAlignmentColumn(aligned: boolean) {
1716
1707
  `;
1717
1708
  }
1718
1709
 
1719
- export const ColumnAlignment: Story = {
1720
- render: () => html`
1710
+ export const ColumnAlignment: StoryObj<
1711
+ ReadoutListItemStoryArgs & {showDebugOverlay: boolean}
1712
+ > = {
1713
+ args: {showDebugOverlay: true},
1714
+ argTypes: {
1715
+ showDebugOverlay: {
1716
+ name: 'Show Debug Overlay',
1717
+ control: {type: 'boolean'},
1718
+ table: {category: 'Debug'},
1719
+ },
1720
+ },
1721
+ render: (args) => html`
1721
1722
  <style>
1722
1723
  ${alignmentStyle}
1723
1724
  </style>
@@ -1726,13 +1727,13 @@ export const ColumnAlignment: Story = {
1726
1727
  <h3 class="rli-align-section-title">
1727
1728
  Without reservers — columns drift
1728
1729
  </h3>
1729
- ${renderAlignmentColumn(false)}
1730
+ ${renderAlignmentColumn(false, args.showDebugOverlay)}
1730
1731
  </section>
1731
1732
  <section class="rli-align-section">
1732
1733
  <h3 class="rli-align-section-title">
1733
1734
  With shared reservers — unit column &amp; value right edge aligned
1734
1735
  </h3>
1735
- ${renderAlignmentColumn(true)}
1736
+ ${renderAlignmentColumn(true, args.showDebugOverlay)}
1736
1737
  </section>
1737
1738
  </div>
1738
1739
  `,
@@ -8,11 +8,15 @@ import {
8
8
  ObcTextboxSize,
9
9
  ObcTextboxFontWeight,
10
10
  } from '../../components/textbox/textbox.js';
11
- import '../../icons/icon-input-right.js';
12
- import '../../icons/icon-notification-advice.js';
11
+ import '../../building-blocks/readout-block/readout-block.js';
12
+ import {
13
+ ReadoutBlockVariant,
14
+ ReadoutBlockSize,
15
+ ReadoutBlockDataQuality,
16
+ ReadoutBlockHidePhase,
17
+ } from '../../building-blocks/readout-block/readout-block.js';
13
18
  import {
14
19
  formatNumericValue,
15
- getHintZeros,
16
20
  type ReadoutNumericFormatOptions,
17
21
  } from '../readout/readout-formatters.js';
18
22
  import {
@@ -26,16 +30,13 @@ import {
26
30
  export {ObcTextboxFontWeight} from '../../components/textbox/textbox.js';
27
31
 
28
32
  /**
29
- * Density/size scale of the readout row.
33
+ * Density/size scale of the readout row (an alias of `ReadoutBlockSize`).
30
34
  * - `small`: regular value typography (smallest, densest).
31
35
  * - `medium`: medium value typography.
32
36
  * - `large`: large value typography.
33
37
  */
34
- export enum ReadoutListItemSize {
35
- small = 'small',
36
- medium = 'medium',
37
- large = 'large',
38
- }
38
+ export const ReadoutListItemSize = ReadoutBlockSize;
39
+ export type ReadoutListItemSize = ReadoutBlockSize;
39
40
 
40
41
  /**
41
42
  * Placement of the unit/source relative to the label and value.
@@ -60,13 +61,12 @@ export enum ReadoutListItemPriority {
60
61
  }
61
62
 
62
63
  /**
63
- * Measurement quality of the value. Orthogonal to the row-level `alert`
64
- * – a low-integrity or invalid value can also sit inside an alert frame.
64
+ * Measurement quality of the value (an alias of `ReadoutBlockDataQuality`).
65
+ * Orthogonal to the row-level `alert` – a low-integrity or invalid value can
66
+ * also sit inside an alert frame.
65
67
  */
66
- export enum ReadoutListItemDataQuality {
67
- lowIntegrity = 'low-integrity',
68
- invalid = 'invalid',
69
- }
68
+ export const ReadoutListItemDataQuality = ReadoutBlockDataQuality;
69
+ export type ReadoutListItemDataQuality = ReadoutBlockDataQuality;
70
70
 
71
71
  /**
72
72
  * Corner style of the interactive (clickable) surface.
@@ -162,12 +162,6 @@ export interface ReadoutSrcOptions extends ReadoutBlockState {
162
162
  spaceReserver?: string;
163
163
  }
164
164
 
165
- enum BlockRole {
166
- value = 'value',
167
- setpoint = 'setpoint',
168
- advice = 'advice',
169
- }
170
-
171
165
  /**
172
166
  * `<obc-readout-list-item>` – A compact, dense readout row for lists and tables.
173
167
  *
@@ -225,8 +219,10 @@ export class ObcReadoutListItem extends LitElement {
225
219
 
226
220
  @property({type: Boolean, attribute: false}) hasValue = true;
227
221
  @property({type: Number}) value: number | null = null;
228
- /** Render the value as the literal "OFF" (e.g. equipment powered down). Affects the value only. */
222
+ /** Render the value as `offText` (e.g. equipment powered down). Affects the value only. */
229
223
  @property({type: Boolean}) off = false;
224
+ /** Text shown in place of the value when `off` is true. @availableWhen off==true */
225
+ @property({type: String}) offText = 'OFF';
230
226
 
231
227
  @property({type: Boolean}) hasSetpoint = false;
232
228
  /** @availableWhen hasSetpoint==true */
@@ -261,6 +257,13 @@ export class ObcReadoutListItem extends LitElement {
261
257
  @property({type: Object}) unitOptions?: ReadoutReserverOptions;
262
258
  @property({type: Object}) srcOptions?: ReadoutSrcOptions;
263
259
 
260
+ /**
261
+ * Development aid: outline the readout building blocks (red), the degree
262
+ * columns (blue) and the degree spacer (green) so reserver widths / alignment
263
+ * are visible. Off by default.
264
+ */
265
+ @property({type: Boolean, reflect: true}) showDebugOverlay = false;
266
+
264
267
  /** Pop-up deferred-hide phase for the setpoint (see {@link updated}). */
265
268
  @state() private deferredSetpointHidePhase: 'none' | 'hiding' | 'hidden' =
266
269
  'none';
@@ -426,36 +429,6 @@ export class ObcReadoutListItem extends LitElement {
426
429
  };
427
430
  }
428
431
 
429
- /** Widest possible value string for width reservation (e.g. `"000.0"`). */
430
- private get reserverText(): string {
431
- const maxDigits = this.resolvedMaxDigits;
432
- if (maxDigits <= 0) {
433
- return '';
434
- }
435
- const fractionDigits = this.resolvedFractionDigits;
436
- const integer = '0'.repeat(Math.max(maxDigits, 1));
437
- return fractionDigits > 0
438
- ? `${integer}.${'0'.repeat(fractionDigits)}`
439
- : integer;
440
- }
441
-
442
- /**
443
- * Effective width reserver for a numeric block: the wider of the explicit
444
- * `spaceReserver` and the `maxDigits`/`fractionDigits`-derived reserve, so an
445
- * explicit reserver can never reserve *less* than the formatted value needs.
446
- * Under tabular-nums the rendered width is proportional to character count, so
447
- * "wider" compares string length.
448
- */
449
- private widerReserver(explicit: string | undefined, derived: string): string {
450
- if (!explicit) {
451
- return derived;
452
- }
453
- if (!derived) {
454
- return explicit;
455
- }
456
- return explicit.length >= derived.length ? explicit : derived;
457
- }
458
-
459
432
  /** classMap fragment for a block / source carrying per-block data quality. */
460
433
  private dataQualityClasses(
461
434
  dataQuality: ReadoutListItemDataQuality | undefined
@@ -467,96 +440,65 @@ export class ObcReadoutListItem extends LitElement {
467
440
  };
468
441
  }
469
442
 
470
- private renderIcon(role: BlockRole): TemplateResult | typeof nothing {
471
- if (role === BlockRole.value) {
472
- if (!this.valueOptions?.hasIcon) {
473
- return nothing;
474
- }
475
- return html`<span class="block-icon" aria-hidden="true"
476
- ><slot name="value-icon"></slot
477
- ></span>`;
443
+ /**
444
+ * Forward the matching list-item icon slot into the block's single `icon`
445
+ * slot. The variant's default marker lives in `obc-readout-block` and shows
446
+ * when nothing is assigned here (an empty forwarded slot flattens to nothing).
447
+ */
448
+ private renderForwardedIcon(variant: ReadoutBlockVariant): TemplateResult {
449
+ if (variant === ReadoutBlockVariant.setpoint) {
450
+ return html`<slot name="setpoint-icon" slot="icon"></slot>`;
478
451
  }
479
- if (role === BlockRole.setpoint) {
480
- return html`<span class="block-icon" aria-hidden="true">
481
- <slot name="setpoint-icon"><obi-input-right></obi-input-right></slot>
482
- </span>`;
452
+ if (variant === ReadoutBlockVariant.advice) {
453
+ return html`<slot name="advice-icon" slot="icon"></slot>`;
483
454
  }
484
- return html`<span class="block-icon" aria-hidden="true">
485
- <slot name="advice-icon"
486
- ><obi-notification-advice></obi-notification-advice
487
- ></slot>
488
- </span>`;
455
+ return html`<slot name="value-icon" slot="icon"></slot>`;
489
456
  }
490
457
 
491
458
  private renderBlock(config: {
492
- role: BlockRole;
459
+ variant: ReadoutBlockVariant;
493
460
  value: number | null | undefined;
494
- size: ObcTextboxSize;
461
+ valueSize: ObcTextboxSize;
495
462
  enhanced: boolean;
496
463
  weight: ObcTextboxFontWeight;
497
464
  hintedZeros: boolean;
498
465
  spaceReserver?: string;
499
466
  off?: boolean;
500
467
  hasDegree?: boolean;
501
- extraClasses?: Record<string, boolean>;
502
- dataQuality?: ReadoutListItemDataQuality;
468
+ hasIcon?: boolean;
469
+ touching?: boolean;
470
+ hidePhase?: ReadoutBlockHidePhase;
471
+ dataQuality?: ReadoutBlockDataQuality;
503
472
  alert?: false | AlertFrameConfig;
504
473
  }): TemplateResult {
505
- const formatOptions = this.numericFormatOptions(this.resolvedMaxDigits);
506
- const valueForFormat = config.value ?? undefined;
507
- const text = config.off
508
- ? 'OFF'
509
- : formatNumericValue(valueForFormat, formatOptions);
510
- // `maxDigits` reserves INTEGER digits only (see `reserverText`), but
511
- // getHintZeros measures total digits (it subtracts just the decimal point),
512
- // so pad its target by `fractionDigits` to hint the right number of integer
513
- // zeros (e.g. value 1.2, maxDigits 3, fractionDigits 1 → "001.2", not "01.2").
514
- const hinted =
515
- config.off || !config.hintedZeros
516
- ? ''
517
- : getHintZeros(valueForFormat, {
518
- ...formatOptions,
519
- minValueLength:
520
- formatOptions.minValueLength + formatOptions.fractionDigits,
521
- });
522
- const reserver = this.widerReserver(
523
- config.spaceReserver,
524
- this.reserverText
525
- );
526
-
527
- const block = html`
528
- <div
529
- class=${classMap({
530
- block: true,
531
- [`block-${config.role}`]: true,
532
- 'tone-enhanced': config.enhanced,
533
- ...this.dataQualityClasses(config.dataQuality),
534
- ...(config.extraClasses ?? {}),
535
- })}
536
- part="block block-${config.role}"
474
+ // The block owns the formatting, hinted zeros, reserver, degree and icon; the
475
+ // row keeps the density tier (`size`) and the resolved per-block number size
476
+ // (`valueSize`) so flip-flop/pop-up emphasis stays a row decision.
477
+ return html`
478
+ <obc-readout-block
479
+ exportparts="block, block-content, block-text, block-icon, degree"
480
+ .variant=${config.variant}
481
+ .value=${config.value ?? null}
482
+ .size=${this.resolvedSize}
483
+ .valueSize=${config.valueSize}
484
+ .enhanced=${config.enhanced}
485
+ .weight=${config.weight}
486
+ .hasDegree=${config.hasDegree ?? false}
487
+ .hasIcon=${config.hasIcon ?? false}
488
+ .fractionDigits=${this.resolvedFractionDigits}
489
+ .maxDigits=${this.resolvedMaxDigits}
490
+ .hintedZeros=${config.hintedZeros}
491
+ .spaceReserver=${config.spaceReserver}
492
+ .off=${config.off ?? false}
493
+ .offText=${this.offText}
494
+ .touching=${config.touching ?? false}
495
+ .hidePhase=${config.hidePhase ?? ReadoutBlockHidePhase.none}
496
+ .dataQuality=${config.dataQuality}
497
+ .alert=${config.alert ?? false}
537
498
  >
538
- ${this.renderIcon(config.role)}
539
- <span class="block-content">
540
- <obc-textbox
541
- class="block-text"
542
- .size=${config.size}
543
- .fontWeight=${config.weight}
544
- .tabularNums=${true}
545
- >
546
- ${hinted
547
- ? html`<span class="hinted-zero" aria-hidden="true"
548
- >${hinted}</span
549
- >`
550
- : nothing}${text}
551
- ${reserver ? html`<span slot="length">${reserver}</span>` : nothing}
552
- </obc-textbox>
553
- ${config.hasDegree
554
- ? this.renderDegreeGlyph(config.size, {inherit: true})
555
- : nothing}
556
- </span>
557
- </div>
499
+ ${this.renderForwardedIcon(config.variant)}
500
+ </obc-readout-block>
558
501
  `;
559
- return wrapWithAlertFrame(config.alert ?? false, block);
560
502
  }
561
503
 
562
504
  /**
@@ -678,20 +620,20 @@ export class ObcReadoutListItem extends LitElement {
678
620
  private renderValueCluster(): TemplateResult {
679
621
  const popUpAtSetpoint =
680
622
  this.isPopUp && this.isAtSetpoint && !this.setpointTouching;
681
- const setpointExtraClasses = {
682
- 'is-hiding':
683
- popUpAtSetpoint && this.deferredSetpointHidePhase === 'hiding',
684
- 'is-hidden':
685
- popUpAtSetpoint && this.deferredSetpointHidePhase === 'hidden',
686
- touching: this.setpointTouching,
687
- };
623
+ const setpointHidePhase = !popUpAtSetpoint
624
+ ? ReadoutBlockHidePhase.none
625
+ : this.deferredSetpointHidePhase === 'hiding'
626
+ ? ReadoutBlockHidePhase.hiding
627
+ : this.deferredSetpointHidePhase === 'hidden'
628
+ ? ReadoutBlockHidePhase.hidden
629
+ : ReadoutBlockHidePhase.none;
688
630
  return html`
689
631
  <div class="value-cluster" part="value-cluster">
690
632
  ${this.hasAdvice
691
633
  ? this.renderBlock({
692
- role: BlockRole.advice,
634
+ variant: ReadoutBlockVariant.advice,
693
635
  value: this.advice,
694
- size: this.secondarySize,
636
+ valueSize: this.secondarySize,
695
637
  enhanced: false,
696
638
  weight: ObcTextboxFontWeight.regular,
697
639
  hintedZeros: this.adviceOptions?.hintedZeros ?? false,
@@ -703,9 +645,9 @@ export class ObcReadoutListItem extends LitElement {
703
645
  : nothing}
704
646
  ${this.hasSetpoint
705
647
  ? this.renderBlock({
706
- role: BlockRole.setpoint,
648
+ variant: ReadoutBlockVariant.setpoint,
707
649
  value: this.setpoint,
708
- size: this.setpointSize,
650
+ valueSize: this.setpointSize,
709
651
  // Value and setpoint share the enhanced colour state (both neutral
710
652
  // or both enhanced); the setpoint is bold only while emphasised.
711
653
  enhanced: this.rowEnhanced,
@@ -713,21 +655,23 @@ export class ObcReadoutListItem extends LitElement {
713
655
  hintedZeros: this.setpointOptions?.hintedZeros ?? false,
714
656
  spaceReserver: this.setpointOptions?.spaceReserver,
715
657
  hasDegree: this.hasDegree ?? false,
716
- extraClasses: setpointExtraClasses,
658
+ touching: this.setpointTouching,
659
+ hidePhase: setpointHidePhase,
717
660
  dataQuality: this.setpointOptions?.dataQuality,
718
661
  alert: this.setpointOptions?.alert,
719
662
  })
720
663
  : nothing}
721
664
  ${this.hasValue
722
665
  ? this.renderBlock({
723
- role: BlockRole.value,
666
+ variant: ReadoutBlockVariant.value,
724
667
  value: this.value,
725
- size: this.valueSize,
668
+ valueSize: this.valueSize,
726
669
  enhanced: this.rowEnhanced,
727
670
  weight: this.valueWeight,
728
671
  hintedZeros: this.valueOptions?.hintedZeros ?? false,
729
672
  spaceReserver: this.valueOptions?.spaceReserver,
730
673
  off: this.off,
674
+ hasIcon: this.valueOptions?.hasIcon ?? false,
731
675
  dataQuality: this.valueOptions?.dataQuality,
732
676
  alert: this.valueOptions?.alert,
733
677
  })