@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
@@ -8,17 +8,25 @@ 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 {
19
23
  type AlertFrameConfig,
20
24
  wrapWithAlertFrame,
25
+ ObcAlertFrameType,
26
+ ObcAlertFrameThickness,
27
+ ObcAlertFrameMode,
21
28
  } from '../../components/alert-frame/alert-frame.js';
29
+ import {AlertType} from '../../types.js';
22
30
 
23
31
  // The value weight maps straight to obc-textbox's font weights (regular /
24
32
  // semibold / bold). Re-exported so consumers can set `valueOptions.weight`
@@ -26,16 +34,13 @@ import {
26
34
  export {ObcTextboxFontWeight} from '../../components/textbox/textbox.js';
27
35
 
28
36
  /**
29
- * Density/size scale of the readout row.
37
+ * Density/size scale of the readout row (an alias of `ReadoutBlockSize`).
30
38
  * - `small`: regular value typography (smallest, densest).
31
39
  * - `medium`: medium value typography.
32
40
  * - `large`: large value typography.
33
41
  */
34
- export enum ReadoutListItemSize {
35
- small = 'small',
36
- medium = 'medium',
37
- large = 'large',
38
- }
42
+ export const ReadoutListItemSize = ReadoutBlockSize;
43
+ export type ReadoutListItemSize = ReadoutBlockSize;
39
44
 
40
45
  /**
41
46
  * Placement of the unit/source relative to the label and value.
@@ -60,13 +65,12 @@ export enum ReadoutListItemPriority {
60
65
  }
61
66
 
62
67
  /**
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.
68
+ * Measurement quality of the value (an alias of `ReadoutBlockDataQuality`).
69
+ * Orthogonal to the row-level `alert` – a low-integrity or invalid value can
70
+ * also sit inside an alert frame.
65
71
  */
66
- export enum ReadoutListItemDataQuality {
67
- lowIntegrity = 'low-integrity',
68
- invalid = 'invalid',
69
- }
72
+ export const ReadoutListItemDataQuality = ReadoutBlockDataQuality;
73
+ export type ReadoutListItemDataQuality = ReadoutBlockDataQuality;
70
74
 
71
75
  /**
72
76
  * Corner style of the interactive (clickable) surface.
@@ -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;
@@ -162,12 +174,6 @@ export interface ReadoutSrcOptions extends ReadoutBlockState {
162
174
  spaceReserver?: string;
163
175
  }
164
176
 
165
- enum BlockRole {
166
- value = 'value',
167
- setpoint = 'setpoint',
168
- advice = 'advice',
169
- }
170
-
171
177
  /**
172
178
  * `<obc-readout-list-item>` – A compact, dense readout row for lists and tables.
173
179
  *
@@ -225,8 +231,10 @@ export class ObcReadoutListItem extends LitElement {
225
231
 
226
232
  @property({type: Boolean, attribute: false}) hasValue = true;
227
233
  @property({type: Number}) value: number | null = null;
228
- /** Render the value as the literal "OFF" (e.g. equipment powered down). Affects the value only. */
234
+ /** Render the value as `offText` (e.g. equipment powered down). Affects the value only. */
229
235
  @property({type: Boolean}) off = false;
236
+ /** Text shown in place of the value when `off` is true. @availableWhen off==true */
237
+ @property({type: String}) offText = 'OFF';
230
238
 
231
239
  @property({type: Boolean}) hasSetpoint = false;
232
240
  /** @availableWhen hasSetpoint==true */
@@ -261,6 +269,13 @@ export class ObcReadoutListItem extends LitElement {
261
269
  @property({type: Object}) unitOptions?: ReadoutReserverOptions;
262
270
  @property({type: Object}) srcOptions?: ReadoutSrcOptions;
263
271
 
272
+ /**
273
+ * Development aid: outline the readout building blocks (red), the degree
274
+ * columns (blue) and the degree spacer (green) so reserver widths / alignment
275
+ * are visible. Off by default.
276
+ */
277
+ @property({type: Boolean, reflect: true}) showDebugOverlay = false;
278
+
264
279
  /** Pop-up deferred-hide phase for the setpoint (see {@link updated}). */
265
280
  @state() private deferredSetpointHidePhase: 'none' | 'hiding' | 'hidden' =
266
281
  'none';
@@ -426,36 +441,6 @@ export class ObcReadoutListItem extends LitElement {
426
441
  };
427
442
  }
428
443
 
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
444
  /** classMap fragment for a block / source carrying per-block data quality. */
460
445
  private dataQualityClasses(
461
446
  dataQuality: ReadoutListItemDataQuality | undefined
@@ -467,96 +452,65 @@ export class ObcReadoutListItem extends LitElement {
467
452
  };
468
453
  }
469
454
 
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>`;
455
+ /**
456
+ * Forward the matching list-item icon slot into the block's single `icon`
457
+ * slot. The variant's default marker lives in `obc-readout-block` and shows
458
+ * when nothing is assigned here (an empty forwarded slot flattens to nothing).
459
+ */
460
+ private renderForwardedIcon(variant: ReadoutBlockVariant): TemplateResult {
461
+ if (variant === ReadoutBlockVariant.setpoint) {
462
+ return html`<slot name="setpoint-icon" slot="icon"></slot>`;
478
463
  }
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>`;
464
+ if (variant === ReadoutBlockVariant.advice) {
465
+ return html`<slot name="advice-icon" slot="icon"></slot>`;
483
466
  }
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>`;
467
+ return html`<slot name="value-icon" slot="icon"></slot>`;
489
468
  }
490
469
 
491
470
  private renderBlock(config: {
492
- role: BlockRole;
471
+ variant: ReadoutBlockVariant;
493
472
  value: number | null | undefined;
494
- size: ObcTextboxSize;
473
+ valueSize: ObcTextboxSize;
495
474
  enhanced: boolean;
496
475
  weight: ObcTextboxFontWeight;
497
476
  hintedZeros: boolean;
498
477
  spaceReserver?: string;
499
478
  off?: boolean;
500
479
  hasDegree?: boolean;
501
- extraClasses?: Record<string, boolean>;
502
- dataQuality?: ReadoutListItemDataQuality;
480
+ hasIcon?: boolean;
481
+ touching?: boolean;
482
+ hidePhase?: ReadoutBlockHidePhase;
483
+ dataQuality?: ReadoutBlockDataQuality;
503
484
  alert?: false | AlertFrameConfig;
504
485
  }): 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}"
486
+ // The block owns the formatting, hinted zeros, reserver, degree and icon; the
487
+ // row keeps the density tier (`size`) and the resolved per-block number size
488
+ // (`valueSize`) so flip-flop/pop-up emphasis stays a row decision.
489
+ return html`
490
+ <obc-readout-block
491
+ exportparts="block, block-content, block-text, block-icon, degree"
492
+ .variant=${config.variant}
493
+ .value=${config.value ?? null}
494
+ .size=${this.resolvedSize}
495
+ .valueSize=${config.valueSize}
496
+ .enhanced=${config.enhanced}
497
+ .weight=${config.weight}
498
+ .hasDegree=${config.hasDegree ?? false}
499
+ .hasIcon=${config.hasIcon ?? false}
500
+ .fractionDigits=${this.resolvedFractionDigits}
501
+ .maxDigits=${this.resolvedMaxDigits}
502
+ .hintedZeros=${config.hintedZeros}
503
+ .spaceReserver=${config.spaceReserver}
504
+ .off=${config.off ?? false}
505
+ .offText=${this.offText}
506
+ .touching=${config.touching ?? false}
507
+ .hidePhase=${config.hidePhase ?? ReadoutBlockHidePhase.none}
508
+ .dataQuality=${config.dataQuality}
509
+ .alert=${config.alert ?? false}
537
510
  >
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>
511
+ ${this.renderForwardedIcon(config.variant)}
512
+ </obc-readout-block>
558
513
  `;
559
- return wrapWithAlertFrame(config.alert ?? false, block);
560
514
  }
561
515
 
562
516
  /**
@@ -678,20 +632,20 @@ export class ObcReadoutListItem extends LitElement {
678
632
  private renderValueCluster(): TemplateResult {
679
633
  const popUpAtSetpoint =
680
634
  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
- };
635
+ const setpointHidePhase = !popUpAtSetpoint
636
+ ? ReadoutBlockHidePhase.none
637
+ : this.deferredSetpointHidePhase === 'hiding'
638
+ ? ReadoutBlockHidePhase.hiding
639
+ : this.deferredSetpointHidePhase === 'hidden'
640
+ ? ReadoutBlockHidePhase.hidden
641
+ : ReadoutBlockHidePhase.none;
688
642
  return html`
689
643
  <div class="value-cluster" part="value-cluster">
690
644
  ${this.hasAdvice
691
645
  ? this.renderBlock({
692
- role: BlockRole.advice,
646
+ variant: ReadoutBlockVariant.advice,
693
647
  value: this.advice,
694
- size: this.secondarySize,
648
+ valueSize: this.secondarySize,
695
649
  enhanced: false,
696
650
  weight: ObcTextboxFontWeight.regular,
697
651
  hintedZeros: this.adviceOptions?.hintedZeros ?? false,
@@ -703,9 +657,9 @@ export class ObcReadoutListItem extends LitElement {
703
657
  : nothing}
704
658
  ${this.hasSetpoint
705
659
  ? this.renderBlock({
706
- role: BlockRole.setpoint,
660
+ variant: ReadoutBlockVariant.setpoint,
707
661
  value: this.setpoint,
708
- size: this.setpointSize,
662
+ valueSize: this.setpointSize,
709
663
  // Value and setpoint share the enhanced colour state (both neutral
710
664
  // or both enhanced); the setpoint is bold only while emphasised.
711
665
  enhanced: this.rowEnhanced,
@@ -713,25 +667,97 @@ export class ObcReadoutListItem extends LitElement {
713
667
  hintedZeros: this.setpointOptions?.hintedZeros ?? false,
714
668
  spaceReserver: this.setpointOptions?.spaceReserver,
715
669
  hasDegree: this.hasDegree ?? false,
716
- extraClasses: setpointExtraClasses,
670
+ touching: this.setpointTouching,
671
+ hidePhase: setpointHidePhase,
717
672
  dataQuality: this.setpointOptions?.dataQuality,
718
673
  alert: this.setpointOptions?.alert,
719
674
  })
720
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
+ >
721
704
  ${this.hasValue
722
705
  ? this.renderBlock({
723
- role: BlockRole.value,
706
+ variant: ReadoutBlockVariant.value,
724
707
  value: this.value,
725
- size: this.valueSize,
708
+ valueSize: this.valueSize,
726
709
  enhanced: this.rowEnhanced,
727
710
  weight: this.valueWeight,
728
711
  hintedZeros: this.valueOptions?.hintedZeros ?? false,
729
712
  spaceReserver: this.valueOptions?.spaceReserver,
730
713
  off: this.off,
731
- dataQuality: this.valueOptions?.dataQuality,
732
- alert: this.valueOptions?.alert,
714
+ hasIcon: this.valueOptions?.hasIcon ?? false,
733
715
  })
734
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>
735
761
  </div>
736
762
  `;
737
763
  }
@@ -809,10 +835,7 @@ export class ObcReadoutListItem extends LitElement {
809
835
  <div class="content" part="content">
810
836
  ${this.renderLabelContainer()}
811
837
  <div class="value-area" part="value-area">
812
- ${this.renderValueCluster()} ${this.renderValueUnitGap()}
813
- <div class="unit-area" part="unit-area">
814
- ${this.renderTrailingUnit()} ${this.renderDegreeSpacer()}
815
- </div>
838
+ ${this.renderValueCluster()}
816
839
  </div>
817
840
  ${this.renderTrailingSource()}
818
841
  </div>