@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.151 → 2.0.0-next.152

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 (136) hide show
  1. package/.storybook/vitest.setup.ts +12 -1
  2. package/bundle/openbridge-webcomponents.bundle.js +989 -319
  3. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  4. package/custom-elements.json +1514 -77
  5. package/dist/alert-severity.d.ts +12 -9
  6. package/dist/alert-severity.d.ts.map +1 -1
  7. package/dist/alert-severity.js +30 -38
  8. package/dist/alert-severity.js.map +1 -1
  9. package/dist/building-blocks/bar-horizontal/bar-horizontal.d.ts +6 -0
  10. package/dist/building-blocks/bar-horizontal/bar-horizontal.d.ts.map +1 -1
  11. package/dist/building-blocks/bar-horizontal/bar-horizontal.js +6 -1
  12. package/dist/building-blocks/bar-horizontal/bar-horizontal.js.map +1 -1
  13. package/dist/building-blocks/bar-vertical/bar-vertical.d.ts +6 -0
  14. package/dist/building-blocks/bar-vertical/bar-vertical.d.ts.map +1 -1
  15. package/dist/building-blocks/bar-vertical/bar-vertical.js +6 -1
  16. package/dist/building-blocks/bar-vertical/bar-vertical.js.map +1 -1
  17. package/dist/building-blocks/chart-line/chart-line-base.d.ts +129 -0
  18. package/dist/building-blocks/chart-line/chart-line-base.d.ts.map +1 -1
  19. package/dist/building-blocks/chart-line/chart-line-base.js +464 -79
  20. package/dist/building-blocks/chart-line/chart-line-base.js.map +1 -1
  21. package/dist/building-blocks/external-scale/external-scale.d.ts +11 -0
  22. package/dist/building-blocks/external-scale/external-scale.d.ts.map +1 -1
  23. package/dist/building-blocks/external-scale/external-scale.js +45 -12
  24. package/dist/building-blocks/external-scale/external-scale.js.map +1 -1
  25. package/dist/charthelpers/index.d.ts +1 -0
  26. package/dist/charthelpers/index.d.ts.map +1 -1
  27. package/dist/charthelpers/index.js +7 -1
  28. package/dist/charthelpers/index.js.map +1 -1
  29. package/dist/charthelpers/range-labels.d.ts +55 -0
  30. package/dist/charthelpers/range-labels.d.ts.map +1 -0
  31. package/dist/charthelpers/range-labels.js +36 -0
  32. package/dist/charthelpers/range-labels.js.map +1 -0
  33. package/dist/components/alert-button/alert-button.css.js +13 -21
  34. package/dist/components/alert-button/alert-button.css.js.map +1 -1
  35. package/dist/components/alert-button/alert-button.d.ts +9 -4
  36. package/dist/components/alert-button/alert-button.d.ts.map +1 -1
  37. package/dist/components/alert-button/alert-button.js +24 -15
  38. package/dist/components/alert-button/alert-button.js.map +1 -1
  39. package/dist/components/alert-frame/alert-frame.css.js +109 -33
  40. package/dist/components/alert-frame/alert-frame.css.js.map +1 -1
  41. package/dist/components/alert-frame/alert-frame.d.ts +34 -7
  42. package/dist/components/alert-frame/alert-frame.d.ts.map +1 -1
  43. package/dist/components/alert-frame/alert-frame.js +134 -30
  44. package/dist/components/alert-frame/alert-frame.js.map +1 -1
  45. package/dist/components/alert-icon/alert-icon.d.ts +10 -7
  46. package/dist/components/alert-icon/alert-icon.d.ts.map +1 -1
  47. package/dist/components/alert-icon/alert-icon.js +37 -60
  48. package/dist/components/alert-icon/alert-icon.js.map +1 -1
  49. package/dist/components/alert-menu-item/alert-menu-item.css.js +1 -4
  50. package/dist/components/alert-menu-item/alert-menu-item.css.js.map +1 -1
  51. package/dist/components/date-item/date-item.css.js +3 -3
  52. package/dist/components/keyboard-full/keyboard-full.css.js +4 -4
  53. package/dist/components/message-menu-item/message-menu-item.css.js +2 -6
  54. package/dist/components/message-menu-item/message-menu-item.css.js.map +1 -1
  55. package/dist/components/sequence-toolbar/sequence-toolbar.css.js +1 -2
  56. package/dist/components/sequence-toolbar/sequence-toolbar.css.js.map +1 -1
  57. package/dist/index.js +2 -1
  58. package/dist/navigation-instruments/gauge-trend/gauge-trend.d.ts +6 -0
  59. package/dist/navigation-instruments/gauge-trend/gauge-trend.d.ts.map +1 -1
  60. package/dist/navigation-instruments/gauge-trend/gauge-trend.js +8 -0
  61. package/dist/navigation-instruments/gauge-trend/gauge-trend.js.map +1 -1
  62. package/dist/navigation-instruments/instrument-field/instrument-field.css.js +2 -2
  63. package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
  64. package/dist/navigation-instruments/readout/readout.js +2 -1
  65. package/dist/navigation-instruments/readout/readout.js.map +1 -1
  66. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
  67. package/dist/navigation-instruments/readout-list-item/readout-list-item.js +2 -1
  68. package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
  69. package/dist/openbridge.css +19 -4
  70. package/dist/palettes/blinking.d.ts +14 -4
  71. package/dist/palettes/blinking.d.ts.map +1 -1
  72. package/dist/palettes/blinking.js +41 -35
  73. package/dist/palettes/blinking.js.map +1 -1
  74. package/dist/palettes/flashing-controller.d.ts +21 -0
  75. package/dist/palettes/flashing-controller.d.ts.map +1 -0
  76. package/dist/palettes/flashing-controller.js +45 -0
  77. package/dist/palettes/flashing-controller.js.map +1 -0
  78. package/dist/svghelpers/index.d.ts +1 -0
  79. package/dist/svghelpers/index.d.ts.map +1 -1
  80. package/dist/svghelpers/index.js +2 -0
  81. package/dist/svghelpers/index.js.map +1 -1
  82. package/dist/svghelpers/rounded-rect.d.ts +15 -0
  83. package/dist/svghelpers/rounded-rect.d.ts.map +1 -0
  84. package/dist/svghelpers/rounded-rect.js +29 -0
  85. package/dist/svghelpers/rounded-rect.js.map +1 -0
  86. package/dist/types.d.ts +14 -0
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/types.js +9 -0
  89. package/dist/types.js.map +1 -1
  90. package/package.json +1 -1
  91. package/script/check-css-variables.ts +2 -15
  92. package/src/alert-severity.spec.ts +56 -0
  93. package/src/alert-severity.ts +45 -43
  94. package/src/bars-graphs/area-graph/area-graph.stories.ts +235 -1
  95. package/src/bars-graphs/line-graph/line-graph.stories.ts +24 -0
  96. package/src/building-blocks/bar-horizontal/bar-horizontal.ts +7 -0
  97. package/src/building-blocks/bar-vertical/bar-vertical.ts +7 -0
  98. package/src/building-blocks/chart-line/chart-line-base.spec.ts +767 -0
  99. package/src/building-blocks/chart-line/chart-line-base.stories.ts +124 -0
  100. package/src/building-blocks/chart-line/chart-line-base.ts +671 -97
  101. package/src/building-blocks/chart-line/chart-sizing-battleground.stories.ts +17 -0
  102. package/src/building-blocks/external-scale/external-scale.spec.ts +154 -0
  103. package/src/building-blocks/external-scale/external-scale.ts +78 -20
  104. package/src/charthelpers/index.ts +1 -0
  105. package/src/charthelpers/range-labels.spec.ts +46 -0
  106. package/src/charthelpers/range-labels.ts +93 -0
  107. package/src/components/alert-button/alert-button.css +10 -23
  108. package/src/components/alert-button/alert-button.spec.ts +56 -15
  109. package/src/components/alert-button/alert-button.stories.ts +5 -1
  110. package/src/components/alert-button/alert-button.ts +33 -24
  111. package/src/components/alert-frame/alert-flash-scenario.stories.ts +669 -0
  112. package/src/components/alert-frame/alert-frame.css +106 -33
  113. package/src/components/alert-frame/alert-frame.spec.ts +176 -17
  114. package/src/components/alert-frame/alert-frame.stories.ts +61 -0
  115. package/src/components/alert-frame/alert-frame.ts +179 -41
  116. package/src/components/alert-icon/alert-icon.spec.ts +82 -27
  117. package/src/components/alert-icon/alert-icon.stories.ts +7 -1
  118. package/src/components/alert-icon/alert-icon.ts +48 -74
  119. package/src/components/alert-menu-item/alert-menu-item.css +1 -4
  120. package/src/components/date-item/date-item.css +3 -3
  121. package/src/components/keyboard-full/keyboard-full.css +4 -4
  122. package/src/components/message-menu-item/message-menu-item.css +2 -6
  123. package/src/components/sequence-toolbar/sequence-toolbar.css +1 -2
  124. package/src/navigation-instruments/gauge-trend/gauge-trend.stories.ts +29 -0
  125. package/src/navigation-instruments/gauge-trend/gauge-trend.ts +11 -0
  126. package/src/navigation-instruments/instrument-field/instrument-field.css +2 -2
  127. package/src/navigation-instruments/readout/readout.ts +2 -1
  128. package/src/navigation-instruments/readout-list-item/readout-list-item.ts +2 -1
  129. package/src/palettes/blinking.spec.ts +83 -0
  130. package/src/palettes/blinking.ts +47 -35
  131. package/src/palettes/flashing-controller.ts +62 -0
  132. package/src/palettes/manual.css +18 -4
  133. package/src/svghelpers/index.ts +1 -0
  134. package/src/svghelpers/rounded-rect.spec.ts +47 -0
  135. package/src/svghelpers/rounded-rect.ts +42 -0
  136. package/src/types.ts +20 -0
@@ -4183,7 +4183,7 @@
4183
4183
  {
4184
4184
  "kind": "variable",
4185
4185
  "name": "ObcBarHorizontal",
4186
- "default": "class extends SetpointMixin(LitElement, { defaultDeadband: 1 }) { constructor() { super(...arguments); this.minValue = 0; this.maxValue = 100; this.width = 320; this.paddingLeft = CHART_DIMENSIONS.CANVAS_PADDING; this.paddingRight = CHART_DIMENSIONS.CANVAS_PADDING; this.side = ExternalScaleSide.bottom; this.fixedAspectRatio = false; this.scaleReferenceSize = 384; this._scale = 1; this._resizeController = new ResizeController(this, { callback: (entries) => { if (!this.fixedAspectRatio) return; const entry = entries[0]; if (!entry) return; const containerMainAxisSize = entry.contentRect.width; this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.horizontal, containerMainAxisSize, scaleReferenceSize: this.scaleReferenceSize }); this.reportDimensions(); } }); this.hasScale = true; this.showLabels = true; this.hasBar = false; this.scaleBackground = false; this.barContainerStyle = void 0; this.barThickness = 24; this.tickThickness = 24; this.labelThickness = 60; this.mainTickmarks = []; this.primaryTickmarkInterval = void 0; this.secondaryTickmarkInterval = void 0; this.tertiaryTickmarkInterval = void 0; this.scaleType = ScaleType.regular; this.frameStyle = FrameStyle.regular; this.borderRadiusPosition = void 0; this.instrumentMode = false; this.borderRadius = void 0; this._borderRadiusResizeController = new ResizeController(this, { callback: () => { if (!this.instrumentMode) { this._refreshBorderRadiusFromCssVar(); } } }); this.priority = Priority.regular; this.fillMode = FillMode.fill; this.fillMin = void 0; this.fillMax = void 0; this.value = void 0; this.state = InstrumentState.active; this.advicePosition = AdvicePosition.inner; this.advices = []; this.highlightCurrentValue = false; } render() { const effectiveLength = this.fixedAspectRatio ? this.scaleReferenceSize : this.width; const config = { orientation: ExternalScaleOrientation.horizontal, side: this.side, length: effectiveLength, paddingStart: this.paddingLeft, paddingEnd: this.paddingRight, minValue: this.minValue, maxValue: this.maxValue, hasScale: this.hasScale, labels: this.showLabels, hasBar: this.hasBar, scaleBackground: this.scaleBackground, barContainerStyle: this.barContainerStyle, barThickness: this.barThickness, tickThickness: this.tickThickness, labelThickness: this.labelThickness, borderRadius: this._getEffectiveBorderRadius(), mainTickmarks: this.mainTickmarks, primaryTickmarkInterval: this.primaryTickmarkInterval, secondaryTickmarkInterval: this.secondaryTickmarkInterval, tertiaryTickmarkInterval: this.tertiaryTickmarkInterval, scaleType: this.scaleType, frameStyle: this.frameStyle, borderRadiusPosition: this.borderRadiusPosition, priority: this.priority, setpointOverride: this.setpointOverride, fillMode: this.fillMode, fillMin: this.fillMin, fillMax: this.fillMax, value: this.value, setpoint: this.setpoint, newSetpoint: this.newSetpoint, atSetpoint: this.atSetpoint, autoAtSetpoint: this.autoAtSetpoint, autoAtSetpointDeadband: this.autoAtSetpointDeadband, setpointAtZeroDeadband: this.setpointAtZeroDeadband, animateSetpoint: this.animateSetpoint, departingNewSetpoint: this.departingNewSetpoint, state: this.state, touching: this.touching, advicePosition: this.advicePosition, advices: this.advices, fixedAspectRatio: this.fixedAspectRatio, instrumentMode: this.instrumentMode, highlightCurrentValue: this.highlightCurrentValue }; const layout = computeExternalScaleLayout( toExternalScaleLayoutConfig(config) ); const parts = renderExternalScale(config); const viewBox = computeExternalScaleViewBox( { orientation: config.orientation, length: effectiveLength }, layout ); const preserveAspectRatio = this.fixedAspectRatio ? \"xMidYMid meet\" : \"none\"; return html` <svg width=${this.fixedAspectRatio ? \"100%\" : `${this.width}px`} height=${this.fixedAspectRatio ? \"100%\" : `${viewBox.height}px`} viewBox=\"${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}\" preserveAspectRatio=\"${preserveAspectRatio}\" style=\"--scale: ${this.fixedAspectRatio ? this._scale : 1};\" part=\"svg\" > ${parts.barContainer} ${parts.barFill} ${parts.scaleBackground} ${parts.tickmarks} ${parts.labels} ${parts.adviceOverlays} ${parts.currentValueDot} ${parts.setpoint} </svg> `; } updated(changed) { super.updated(changed); if (changed.has(\"scaleType\")) { this._refreshBorderRadiusFromCssVar(); } if (changed.has(\"fixedAspectRatio\")) { if (this.fixedAspectRatio) { this._applyFixedAspectRatioStyles(); this._updateScaleFromCurrentSize(); } else { this._removeFixedAspectRatioStyles(); this._scale = 1; } } if (changed.has(\"scaleReferenceSize\") && this.fixedAspectRatio) { this._updateScaleFromCurrentSize(); } const layoutChanged = changed.has(\"side\") || changed.has(\"showLabels\") || changed.has(\"hasScale\") || changed.has(\"hasBar\") || changed.has(\"barThickness\") || changed.has(\"tickThickness\") || changed.has(\"labelThickness\") || changed.has(\"scaleType\") || changed.has(\"borderRadiusPosition\") || changed.has(\"borderRadius\") || changed.has(\"advices\") || changed.has(\"advicePosition\") || this.setpointPresenceChanged(changed); if (!this.fixedAspectRatio || layoutChanged) { this.reportDimensions(); } } /** * Report scale dimensions to parent chart component. * When fixedAspectRatio=true, reports the actual visual (scaled) thickness * so the chart can correctly reserve space for the scale. * When fixedAspectRatio=false, reports the base/unscaled thickness. */ reportDimensions() { const effectiveBarThickness = computeExternalScaleEffectiveBarThickness({ hasBar: this.hasBar, barThickness: this.barThickness, borderRadius: this._getEffectiveBorderRadius(), scaleType: this.scaleType }); const effectiveLength = this.fixedAspectRatio ? this.scaleReferenceSize : this.width; const baseDimensions = computeScaleDimensionsForReport({ orientation: ExternalScaleOrientation.horizontal, side: this.side, hasBar: this.hasBar, hasScale: this.hasScale, labels: this.showLabels, barThickness: effectiveBarThickness, tickThickness: this.tickThickness, labelThickness: this.labelThickness, length: effectiveLength, scaleType: this.scaleType, advicePosition: this.advicePosition, hasAdvice: !!this.advices && this.advices.length > 0, hasSetpoint: this.setpoint !== void 0 || this.newSetpoint !== void 0 || this.departingNewSetpoint !== void 0 }); const dimensions = this.fixedAspectRatio ? { ...baseDimensions, thickness: Math.round(baseDimensions.thickness * this._scale) } : baseDimensions; this.dispatchEvent( new CustomEvent(\"scale-dimensions-changed\", { detail: dimensions, bubbles: true, composed: true }) ); } createRenderRoot() { return this; } connectedCallback() { super.connectedCallback(); if (!this.instrumentMode) { this._refreshBorderRadiusFromCssVar(); this._startBorderRadiusObserver(); } if (this.fixedAspectRatio) { this._applyFixedAspectRatioStyles(); } } disconnectedCallback() { this._borderRadiusObserver?.disconnect(); this._borderRadiusObserver = void 0; super.disconnectedCallback(); } _startBorderRadiusObserver() { this._borderRadiusObserver?.disconnect(); this._borderRadiusObserver = startExternalScaleBorderRadiusObserver( this, () => this._refreshBorderRadiusFromCssVar() ); } /** * Apply CSS styles needed for fixedAspectRatio mode. * Custom elements are display:inline by default, which doesn't work with percentage-based sizing. * We need display:block and width:100% to fill the slot container. */ _applyFixedAspectRatioStyles() { this.style.display = \"block\"; this.style.width = \"100%\"; this.style.height = \"auto\"; } /** * Remove fixed aspect ratio styles when switching back to pixel mode. */ _removeFixedAspectRatioStyles() { this.style.display = \"\"; this.style.width = \"\"; this.style.height = \"\"; } /** * Calculate scale based on current element size. * Called when fixedAspectRatio is enabled or scaleReferenceSize changes. */ _updateScaleFromCurrentSize() { requestAnimationFrame(() => { const containerWidth = this.clientWidth; if (containerWidth > 0) { this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.horizontal, containerMainAxisSize: containerWidth, scaleReferenceSize: this.scaleReferenceSize }); this.requestUpdate(); this.reportDimensions(); } }); } _refreshBorderRadiusFromCssVar() { if (this.instrumentMode) return; const next = readExternalScaleBorderRadiusPx(this, this.scaleType); if (this._computedBorderRadius !== next) { this._computedBorderRadius = next; } if (this.fixedAspectRatio) { const rect = this.getBoundingClientRect(); if (rect.width > 0) { this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.horizontal, containerMainAxisSize: rect.width, scaleReferenceSize: this.scaleReferenceSize }); this.reportDimensions(); } } } /** * Get the effective border radius to use. * In instrument mode: use explicit borderRadius prop or default based on scaleType. * In normal mode: use CSS variable computed value. */ _getEffectiveBorderRadius() { if (this.instrumentMode) { if (this.borderRadius !== void 0) { return this.borderRadius; } return this.scaleType === ScaleType.condensed ? 4 : 8; } return this._computedBorderRadius ?? (this.scaleType === ScaleType.condensed ? 4 : 8); } }"
4186
+ "default": "class extends SetpointMixin(LitElement, { defaultDeadband: 1 }) { constructor() { super(...arguments); this.minValue = 0; this.maxValue = 100; this.width = 320; this.paddingLeft = CHART_DIMENSIONS.CANVAS_PADDING; this.paddingRight = CHART_DIMENSIONS.CANVAS_PADDING; this.side = ExternalScaleSide.bottom; this.fixedAspectRatio = false; this.scaleReferenceSize = 384; this._scale = 1; this._resizeController = new ResizeController(this, { callback: (entries) => { if (!this.fixedAspectRatio) return; const entry = entries[0]; if (!entry) return; const containerMainAxisSize = entry.contentRect.width; this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.horizontal, containerMainAxisSize, scaleReferenceSize: this.scaleReferenceSize }); this.reportDimensions(); } }); this.hasScale = true; this.showLabels = true; this.showMainTickmarkLabels = false; this.hasBar = false; this.scaleBackground = false; this.barContainerStyle = void 0; this.barThickness = 24; this.tickThickness = 24; this.labelThickness = 60; this.mainTickmarks = []; this.primaryTickmarkInterval = void 0; this.secondaryTickmarkInterval = void 0; this.tertiaryTickmarkInterval = void 0; this.scaleType = ScaleType.regular; this.frameStyle = FrameStyle.regular; this.borderRadiusPosition = void 0; this.instrumentMode = false; this.borderRadius = void 0; this._borderRadiusResizeController = new ResizeController(this, { callback: () => { if (!this.instrumentMode) { this._refreshBorderRadiusFromCssVar(); } } }); this.priority = Priority.regular; this.fillMode = FillMode.fill; this.fillMin = void 0; this.fillMax = void 0; this.value = void 0; this.state = InstrumentState.active; this.advicePosition = AdvicePosition.inner; this.advices = []; this.highlightCurrentValue = false; } render() { const effectiveLength = this.fixedAspectRatio ? this.scaleReferenceSize : this.width; const config = { orientation: ExternalScaleOrientation.horizontal, side: this.side, length: effectiveLength, paddingStart: this.paddingLeft, paddingEnd: this.paddingRight, minValue: this.minValue, maxValue: this.maxValue, hasScale: this.hasScale, labels: this.showLabels, mainTickmarkLabels: this.showMainTickmarkLabels, hasBar: this.hasBar, scaleBackground: this.scaleBackground, barContainerStyle: this.barContainerStyle, barThickness: this.barThickness, tickThickness: this.tickThickness, labelThickness: this.labelThickness, borderRadius: this._getEffectiveBorderRadius(), mainTickmarks: this.mainTickmarks, primaryTickmarkInterval: this.primaryTickmarkInterval, secondaryTickmarkInterval: this.secondaryTickmarkInterval, tertiaryTickmarkInterval: this.tertiaryTickmarkInterval, scaleType: this.scaleType, frameStyle: this.frameStyle, borderRadiusPosition: this.borderRadiusPosition, priority: this.priority, setpointOverride: this.setpointOverride, fillMode: this.fillMode, fillMin: this.fillMin, fillMax: this.fillMax, value: this.value, setpoint: this.setpoint, newSetpoint: this.newSetpoint, atSetpoint: this.atSetpoint, autoAtSetpoint: this.autoAtSetpoint, autoAtSetpointDeadband: this.autoAtSetpointDeadband, setpointAtZeroDeadband: this.setpointAtZeroDeadband, animateSetpoint: this.animateSetpoint, departingNewSetpoint: this.departingNewSetpoint, state: this.state, touching: this.touching, advicePosition: this.advicePosition, advices: this.advices, fixedAspectRatio: this.fixedAspectRatio, instrumentMode: this.instrumentMode, highlightCurrentValue: this.highlightCurrentValue }; const layout = computeExternalScaleLayout( toExternalScaleLayoutConfig(config) ); const parts = renderExternalScale(config); const viewBox = computeExternalScaleViewBox( { orientation: config.orientation, length: effectiveLength }, layout ); const preserveAspectRatio = this.fixedAspectRatio ? \"xMidYMid meet\" : \"none\"; return html` <svg width=${this.fixedAspectRatio ? \"100%\" : `${this.width}px`} height=${this.fixedAspectRatio ? \"100%\" : `${viewBox.height}px`} viewBox=\"${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}\" preserveAspectRatio=\"${preserveAspectRatio}\" style=\"--scale: ${this.fixedAspectRatio ? this._scale : 1};\" part=\"svg\" > ${parts.barContainer} ${parts.barFill} ${parts.scaleBackground} ${parts.tickmarks} ${parts.labels} ${parts.adviceOverlays} ${parts.currentValueDot} ${parts.setpoint} </svg> `; } updated(changed) { super.updated(changed); if (changed.has(\"scaleType\")) { this._refreshBorderRadiusFromCssVar(); } if (changed.has(\"fixedAspectRatio\")) { if (this.fixedAspectRatio) { this._applyFixedAspectRatioStyles(); this._updateScaleFromCurrentSize(); } else { this._removeFixedAspectRatioStyles(); this._scale = 1; } } if (changed.has(\"scaleReferenceSize\") && this.fixedAspectRatio) { this._updateScaleFromCurrentSize(); } const layoutChanged = changed.has(\"side\") || changed.has(\"showLabels\") || changed.has(\"showMainTickmarkLabels\") || changed.has(\"hasScale\") || changed.has(\"hasBar\") || changed.has(\"barThickness\") || changed.has(\"tickThickness\") || changed.has(\"labelThickness\") || changed.has(\"scaleType\") || changed.has(\"borderRadiusPosition\") || changed.has(\"borderRadius\") || changed.has(\"advices\") || changed.has(\"advicePosition\") || this.setpointPresenceChanged(changed); if (!this.fixedAspectRatio || layoutChanged) { this.reportDimensions(); } } /** * Report scale dimensions to parent chart component. * When fixedAspectRatio=true, reports the actual visual (scaled) thickness * so the chart can correctly reserve space for the scale. * When fixedAspectRatio=false, reports the base/unscaled thickness. */ reportDimensions() { const effectiveBarThickness = computeExternalScaleEffectiveBarThickness({ hasBar: this.hasBar, barThickness: this.barThickness, borderRadius: this._getEffectiveBorderRadius(), scaleType: this.scaleType }); const effectiveLength = this.fixedAspectRatio ? this.scaleReferenceSize : this.width; const baseDimensions = computeScaleDimensionsForReport({ orientation: ExternalScaleOrientation.horizontal, side: this.side, hasBar: this.hasBar, hasScale: this.hasScale, labels: this.showLabels, barThickness: effectiveBarThickness, tickThickness: this.tickThickness, labelThickness: this.labelThickness, length: effectiveLength, scaleType: this.scaleType, advicePosition: this.advicePosition, hasAdvice: !!this.advices && this.advices.length > 0, hasSetpoint: this.setpoint !== void 0 || this.newSetpoint !== void 0 || this.departingNewSetpoint !== void 0 }); const dimensions = this.fixedAspectRatio ? { ...baseDimensions, thickness: Math.round(baseDimensions.thickness * this._scale) } : baseDimensions; this.dispatchEvent( new CustomEvent(\"scale-dimensions-changed\", { detail: dimensions, bubbles: true, composed: true }) ); } createRenderRoot() { return this; } connectedCallback() { super.connectedCallback(); if (!this.instrumentMode) { this._refreshBorderRadiusFromCssVar(); this._startBorderRadiusObserver(); } if (this.fixedAspectRatio) { this._applyFixedAspectRatioStyles(); } } disconnectedCallback() { this._borderRadiusObserver?.disconnect(); this._borderRadiusObserver = void 0; super.disconnectedCallback(); } _startBorderRadiusObserver() { this._borderRadiusObserver?.disconnect(); this._borderRadiusObserver = startExternalScaleBorderRadiusObserver( this, () => this._refreshBorderRadiusFromCssVar() ); } /** * Apply CSS styles needed for fixedAspectRatio mode. * Custom elements are display:inline by default, which doesn't work with percentage-based sizing. * We need display:block and width:100% to fill the slot container. */ _applyFixedAspectRatioStyles() { this.style.display = \"block\"; this.style.width = \"100%\"; this.style.height = \"auto\"; } /** * Remove fixed aspect ratio styles when switching back to pixel mode. */ _removeFixedAspectRatioStyles() { this.style.display = \"\"; this.style.width = \"\"; this.style.height = \"\"; } /** * Calculate scale based on current element size. * Called when fixedAspectRatio is enabled or scaleReferenceSize changes. */ _updateScaleFromCurrentSize() { requestAnimationFrame(() => { const containerWidth = this.clientWidth; if (containerWidth > 0) { this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.horizontal, containerMainAxisSize: containerWidth, scaleReferenceSize: this.scaleReferenceSize }); this.requestUpdate(); this.reportDimensions(); } }); } _refreshBorderRadiusFromCssVar() { if (this.instrumentMode) return; const next = readExternalScaleBorderRadiusPx(this, this.scaleType); if (this._computedBorderRadius !== next) { this._computedBorderRadius = next; } if (this.fixedAspectRatio) { const rect = this.getBoundingClientRect(); if (rect.width > 0) { this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.horizontal, containerMainAxisSize: rect.width, scaleReferenceSize: this.scaleReferenceSize }); this.reportDimensions(); } } } /** * Get the effective border radius to use. * In instrument mode: use explicit borderRadius prop or default based on scaleType. * In normal mode: use CSS variable computed value. */ _getEffectiveBorderRadius() { if (this.instrumentMode) { if (this.borderRadius !== void 0) { return this.borderRadius; } return this.scaleType === ScaleType.condensed ? 4 : 8; } return this._computedBorderRadius ?? (this.scaleType === ScaleType.condensed ? 4 : 8); } }"
4187
4187
  }
4188
4188
  ],
4189
4189
  "exports": [
@@ -4276,7 +4276,7 @@
4276
4276
  {
4277
4277
  "kind": "variable",
4278
4278
  "name": "ObcBarVertical",
4279
- "default": "class extends SetpointMixin(LitElement, { defaultDeadband: 1 }) { constructor() { super(...arguments); this.minValue = 0; this.maxValue = 100; this.height = 320; this.paddingTop = CHART_DIMENSIONS.CANVAS_PADDING; this.paddingBottom = CHART_DIMENSIONS.CANVAS_PADDING; this.side = ExternalScaleSide.right; this.fixedAspectRatio = false; this.scaleReferenceSize = 384; this._scale = 1; this._resizeController = new ResizeController(this, { callback: (entries) => { if (!this.fixedAspectRatio) return; const entry = entries[0]; if (!entry) return; const containerMainAxisSize = entry.contentRect.height; this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.vertical, containerMainAxisSize, scaleReferenceSize: this.scaleReferenceSize }); this.reportDimensions(); } }); this.hasScale = true; this.showLabels = true; this.hasBar = false; this.scaleBackground = false; this.barContainerStyle = void 0; this.barThickness = 24; this.tickThickness = 24; this.labelThickness = 60; this.mainTickmarks = []; this.primaryTickmarkInterval = void 0; this.secondaryTickmarkInterval = void 0; this.tertiaryTickmarkInterval = void 0; this.scaleType = ScaleType.regular; this.frameStyle = FrameStyle.regular; this.borderRadiusPosition = void 0; this.instrumentMode = false; this.borderRadius = void 0; this._borderRadiusResizeController = new ResizeController(this, { callback: () => { if (!this.instrumentMode) { this._refreshBorderRadiusFromCssVar(); } } }); this.priority = Priority.regular; this.fillMode = FillMode.fill; this.fillMin = void 0; this.fillMax = void 0; this.value = void 0; this.state = InstrumentState.active; this.advicePosition = AdvicePosition.inner; this.advices = []; this.highlightCurrentValue = false; } render() { const effectiveLength = this.fixedAspectRatio ? this.scaleReferenceSize : this.height; const config = { orientation: ExternalScaleOrientation.vertical, side: this.side, length: effectiveLength, paddingStart: this.paddingTop, paddingEnd: this.paddingBottom, minValue: this.minValue, maxValue: this.maxValue, hasScale: this.hasScale, labels: this.showLabels, hasBar: this.hasBar, scaleBackground: this.scaleBackground, barContainerStyle: this.barContainerStyle, barThickness: this.barThickness, tickThickness: this.tickThickness, labelThickness: this.labelThickness, borderRadius: this._getEffectiveBorderRadius(), mainTickmarks: this.mainTickmarks, primaryTickmarkInterval: this.primaryTickmarkInterval, secondaryTickmarkInterval: this.secondaryTickmarkInterval, tertiaryTickmarkInterval: this.tertiaryTickmarkInterval, scaleType: this.scaleType, frameStyle: this.frameStyle, borderRadiusPosition: this.borderRadiusPosition, priority: this.priority, setpointOverride: this.setpointOverride, fillMode: this.fillMode, fillMin: this.fillMin, fillMax: this.fillMax, value: this.value, setpoint: this.setpoint, newSetpoint: this.newSetpoint, atSetpoint: this.atSetpoint, autoAtSetpoint: this.autoAtSetpoint, autoAtSetpointDeadband: this.autoAtSetpointDeadband, setpointAtZeroDeadband: this.setpointAtZeroDeadband, animateSetpoint: this.animateSetpoint, departingNewSetpoint: this.departingNewSetpoint, state: this.state, touching: this.touching, advicePosition: this.advicePosition, advices: this.advices, fixedAspectRatio: this.fixedAspectRatio, instrumentMode: this.instrumentMode, highlightCurrentValue: this.highlightCurrentValue }; const layout = computeExternalScaleLayout( toExternalScaleLayoutConfig(config) ); const parts = renderExternalScale(config); const viewBox = computeExternalScaleViewBox( { orientation: config.orientation, length: effectiveLength }, layout ); const preserveAspectRatio = this.fixedAspectRatio ? \"xMidYMid meet\" : \"none\"; return html` <svg width=${this.fixedAspectRatio ? \"100%\" : `${viewBox.width}px`} height=${this.fixedAspectRatio ? \"100%\" : `${this.height}px`} viewBox=\"${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}\" preserveAspectRatio=\"${preserveAspectRatio}\" style=\"--scale: ${this.fixedAspectRatio ? this._scale : 1};\" part=\"svg\" > ${parts.barContainer} ${parts.barFill} ${parts.scaleBackground} ${parts.tickmarks} ${parts.labels} ${parts.adviceOverlays} ${parts.currentValueDot} ${parts.setpoint} </svg> `; } updated(changed) { super.updated(changed); if (changed.has(\"scaleType\")) { this._refreshBorderRadiusFromCssVar(); } if (changed.has(\"fixedAspectRatio\")) { if (this.fixedAspectRatio) { this._applyFixedAspectRatioStyles(); this._updateScaleFromCurrentSize(); } else { this._removeFixedAspectRatioStyles(); this._scale = 1; } } if (changed.has(\"scaleReferenceSize\") && this.fixedAspectRatio) { this._updateScaleFromCurrentSize(); } const layoutChanged = changed.has(\"side\") || changed.has(\"showLabels\") || changed.has(\"hasScale\") || changed.has(\"hasBar\") || changed.has(\"barThickness\") || changed.has(\"tickThickness\") || changed.has(\"labelThickness\") || changed.has(\"scaleType\") || changed.has(\"borderRadiusPosition\") || changed.has(\"borderRadius\") || changed.has(\"advices\") || changed.has(\"advicePosition\") || this.setpointPresenceChanged(changed); if (!this.fixedAspectRatio || layoutChanged) { this.reportDimensions(); } } /** * Report scale dimensions to parent chart component. * When fixedAspectRatio=true, reports the actual visual (scaled) thickness * so the chart can correctly reserve space for the scale. * When fixedAspectRatio=false, reports the base/unscaled thickness. */ reportDimensions() { const effectiveBarThickness = computeExternalScaleEffectiveBarThickness({ hasBar: this.hasBar, barThickness: this.barThickness, borderRadius: this._getEffectiveBorderRadius(), scaleType: this.scaleType }); const effectiveLength = this.fixedAspectRatio ? this.scaleReferenceSize : this.height; const baseDimensions = computeScaleDimensionsForReport({ orientation: ExternalScaleOrientation.vertical, side: this.side, hasBar: this.hasBar, hasScale: this.hasScale, labels: this.showLabels, barThickness: effectiveBarThickness, tickThickness: this.tickThickness, labelThickness: this.labelThickness, length: effectiveLength, scaleType: this.scaleType, advicePosition: this.advicePosition, hasAdvice: !!this.advices && this.advices.length > 0, hasSetpoint: this.setpoint !== void 0 || this.newSetpoint !== void 0 || this.departingNewSetpoint !== void 0 }); const dimensions = this.fixedAspectRatio ? { ...baseDimensions, thickness: Math.round(baseDimensions.thickness * this._scale) } : baseDimensions; this.dispatchEvent( new CustomEvent(\"scale-dimensions-changed\", { detail: dimensions, bubbles: true, composed: true }) ); } createRenderRoot() { return this; } connectedCallback() { super.connectedCallback(); if (!this.instrumentMode) { this._refreshBorderRadiusFromCssVar(); this._startBorderRadiusObserver(); } if (this.fixedAspectRatio) { this._applyFixedAspectRatioStyles(); } } disconnectedCallback() { this._borderRadiusObserver?.disconnect(); this._borderRadiusObserver = void 0; super.disconnectedCallback(); } _startBorderRadiusObserver() { this._borderRadiusObserver?.disconnect(); this._borderRadiusObserver = startExternalScaleBorderRadiusObserver( this, () => this._refreshBorderRadiusFromCssVar() ); } /** * Apply CSS styles needed for fixedAspectRatio mode. * Custom elements are display:inline by default, which doesn't work with percentage-based sizing. * We need display:block and height:100% to fill the slot container. */ _applyFixedAspectRatioStyles() { this.style.display = \"block\"; this.style.height = \"100%\"; this.style.width = \"auto\"; } /** * Remove fixed aspect ratio styles when switching back to pixel mode. */ _removeFixedAspectRatioStyles() { this.style.display = \"\"; this.style.height = \"\"; this.style.width = \"\"; } /** * Calculate scale based on current element size. * Called when fixedAspectRatio is enabled or scaleReferenceSize changes. */ _updateScaleFromCurrentSize() { requestAnimationFrame(() => { const containerHeight = this.clientHeight; if (containerHeight > 0) { this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.vertical, containerMainAxisSize: containerHeight, scaleReferenceSize: this.scaleReferenceSize }); this.requestUpdate(); this.reportDimensions(); } }); } _refreshBorderRadiusFromCssVar() { if (this.instrumentMode) return; const next = readExternalScaleBorderRadiusPx(this, this.scaleType); if (this._computedBorderRadius !== next) { this._computedBorderRadius = next; } if (this.fixedAspectRatio) { const rect = this.getBoundingClientRect(); if (rect.height > 0) { this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.vertical, containerMainAxisSize: rect.height, scaleReferenceSize: this.scaleReferenceSize }); this.reportDimensions(); } } } /** * Get the effective border radius to use. * In instrument mode: use explicit borderRadius prop or default based on scaleType. * In normal mode: use CSS variable computed value. */ _getEffectiveBorderRadius() { if (this.instrumentMode) { if (this.borderRadius !== void 0) { return this.borderRadius; } return this.scaleType === ScaleType.condensed ? 4 : 8; } return this._computedBorderRadius ?? (this.scaleType === ScaleType.condensed ? 4 : 8); } }"
4279
+ "default": "class extends SetpointMixin(LitElement, { defaultDeadband: 1 }) { constructor() { super(...arguments); this.minValue = 0; this.maxValue = 100; this.height = 320; this.paddingTop = CHART_DIMENSIONS.CANVAS_PADDING; this.paddingBottom = CHART_DIMENSIONS.CANVAS_PADDING; this.side = ExternalScaleSide.right; this.fixedAspectRatio = false; this.scaleReferenceSize = 384; this._scale = 1; this._resizeController = new ResizeController(this, { callback: (entries) => { if (!this.fixedAspectRatio) return; const entry = entries[0]; if (!entry) return; const containerMainAxisSize = entry.contentRect.height; this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.vertical, containerMainAxisSize, scaleReferenceSize: this.scaleReferenceSize }); this.reportDimensions(); } }); this.hasScale = true; this.showLabels = true; this.showMainTickmarkLabels = false; this.hasBar = false; this.scaleBackground = false; this.barContainerStyle = void 0; this.barThickness = 24; this.tickThickness = 24; this.labelThickness = 60; this.mainTickmarks = []; this.primaryTickmarkInterval = void 0; this.secondaryTickmarkInterval = void 0; this.tertiaryTickmarkInterval = void 0; this.scaleType = ScaleType.regular; this.frameStyle = FrameStyle.regular; this.borderRadiusPosition = void 0; this.instrumentMode = false; this.borderRadius = void 0; this._borderRadiusResizeController = new ResizeController(this, { callback: () => { if (!this.instrumentMode) { this._refreshBorderRadiusFromCssVar(); } } }); this.priority = Priority.regular; this.fillMode = FillMode.fill; this.fillMin = void 0; this.fillMax = void 0; this.value = void 0; this.state = InstrumentState.active; this.advicePosition = AdvicePosition.inner; this.advices = []; this.highlightCurrentValue = false; } render() { const effectiveLength = this.fixedAspectRatio ? this.scaleReferenceSize : this.height; const config = { orientation: ExternalScaleOrientation.vertical, side: this.side, length: effectiveLength, paddingStart: this.paddingTop, paddingEnd: this.paddingBottom, minValue: this.minValue, maxValue: this.maxValue, hasScale: this.hasScale, labels: this.showLabels, mainTickmarkLabels: this.showMainTickmarkLabels, hasBar: this.hasBar, scaleBackground: this.scaleBackground, barContainerStyle: this.barContainerStyle, barThickness: this.barThickness, tickThickness: this.tickThickness, labelThickness: this.labelThickness, borderRadius: this._getEffectiveBorderRadius(), mainTickmarks: this.mainTickmarks, primaryTickmarkInterval: this.primaryTickmarkInterval, secondaryTickmarkInterval: this.secondaryTickmarkInterval, tertiaryTickmarkInterval: this.tertiaryTickmarkInterval, scaleType: this.scaleType, frameStyle: this.frameStyle, borderRadiusPosition: this.borderRadiusPosition, priority: this.priority, setpointOverride: this.setpointOverride, fillMode: this.fillMode, fillMin: this.fillMin, fillMax: this.fillMax, value: this.value, setpoint: this.setpoint, newSetpoint: this.newSetpoint, atSetpoint: this.atSetpoint, autoAtSetpoint: this.autoAtSetpoint, autoAtSetpointDeadband: this.autoAtSetpointDeadband, setpointAtZeroDeadband: this.setpointAtZeroDeadband, animateSetpoint: this.animateSetpoint, departingNewSetpoint: this.departingNewSetpoint, state: this.state, touching: this.touching, advicePosition: this.advicePosition, advices: this.advices, fixedAspectRatio: this.fixedAspectRatio, instrumentMode: this.instrumentMode, highlightCurrentValue: this.highlightCurrentValue }; const layout = computeExternalScaleLayout( toExternalScaleLayoutConfig(config) ); const parts = renderExternalScale(config); const viewBox = computeExternalScaleViewBox( { orientation: config.orientation, length: effectiveLength }, layout ); const preserveAspectRatio = this.fixedAspectRatio ? \"xMidYMid meet\" : \"none\"; return html` <svg width=${this.fixedAspectRatio ? \"100%\" : `${viewBox.width}px`} height=${this.fixedAspectRatio ? \"100%\" : `${this.height}px`} viewBox=\"${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}\" preserveAspectRatio=\"${preserveAspectRatio}\" style=\"--scale: ${this.fixedAspectRatio ? this._scale : 1};\" part=\"svg\" > ${parts.barContainer} ${parts.barFill} ${parts.scaleBackground} ${parts.tickmarks} ${parts.labels} ${parts.adviceOverlays} ${parts.currentValueDot} ${parts.setpoint} </svg> `; } updated(changed) { super.updated(changed); if (changed.has(\"scaleType\")) { this._refreshBorderRadiusFromCssVar(); } if (changed.has(\"fixedAspectRatio\")) { if (this.fixedAspectRatio) { this._applyFixedAspectRatioStyles(); this._updateScaleFromCurrentSize(); } else { this._removeFixedAspectRatioStyles(); this._scale = 1; } } if (changed.has(\"scaleReferenceSize\") && this.fixedAspectRatio) { this._updateScaleFromCurrentSize(); } const layoutChanged = changed.has(\"side\") || changed.has(\"showLabels\") || changed.has(\"showMainTickmarkLabels\") || changed.has(\"hasScale\") || changed.has(\"hasBar\") || changed.has(\"barThickness\") || changed.has(\"tickThickness\") || changed.has(\"labelThickness\") || changed.has(\"scaleType\") || changed.has(\"borderRadiusPosition\") || changed.has(\"borderRadius\") || changed.has(\"advices\") || changed.has(\"advicePosition\") || this.setpointPresenceChanged(changed); if (!this.fixedAspectRatio || layoutChanged) { this.reportDimensions(); } } /** * Report scale dimensions to parent chart component. * When fixedAspectRatio=true, reports the actual visual (scaled) thickness * so the chart can correctly reserve space for the scale. * When fixedAspectRatio=false, reports the base/unscaled thickness. */ reportDimensions() { const effectiveBarThickness = computeExternalScaleEffectiveBarThickness({ hasBar: this.hasBar, barThickness: this.barThickness, borderRadius: this._getEffectiveBorderRadius(), scaleType: this.scaleType }); const effectiveLength = this.fixedAspectRatio ? this.scaleReferenceSize : this.height; const baseDimensions = computeScaleDimensionsForReport({ orientation: ExternalScaleOrientation.vertical, side: this.side, hasBar: this.hasBar, hasScale: this.hasScale, labels: this.showLabels, barThickness: effectiveBarThickness, tickThickness: this.tickThickness, labelThickness: this.labelThickness, length: effectiveLength, scaleType: this.scaleType, advicePosition: this.advicePosition, hasAdvice: !!this.advices && this.advices.length > 0, hasSetpoint: this.setpoint !== void 0 || this.newSetpoint !== void 0 || this.departingNewSetpoint !== void 0 }); const dimensions = this.fixedAspectRatio ? { ...baseDimensions, thickness: Math.round(baseDimensions.thickness * this._scale) } : baseDimensions; this.dispatchEvent( new CustomEvent(\"scale-dimensions-changed\", { detail: dimensions, bubbles: true, composed: true }) ); } createRenderRoot() { return this; } connectedCallback() { super.connectedCallback(); if (!this.instrumentMode) { this._refreshBorderRadiusFromCssVar(); this._startBorderRadiusObserver(); } if (this.fixedAspectRatio) { this._applyFixedAspectRatioStyles(); } } disconnectedCallback() { this._borderRadiusObserver?.disconnect(); this._borderRadiusObserver = void 0; super.disconnectedCallback(); } _startBorderRadiusObserver() { this._borderRadiusObserver?.disconnect(); this._borderRadiusObserver = startExternalScaleBorderRadiusObserver( this, () => this._refreshBorderRadiusFromCssVar() ); } /** * Apply CSS styles needed for fixedAspectRatio mode. * Custom elements are display:inline by default, which doesn't work with percentage-based sizing. * We need display:block and height:100% to fill the slot container. */ _applyFixedAspectRatioStyles() { this.style.display = \"block\"; this.style.height = \"100%\"; this.style.width = \"auto\"; } /** * Remove fixed aspect ratio styles when switching back to pixel mode. */ _removeFixedAspectRatioStyles() { this.style.display = \"\"; this.style.height = \"\"; this.style.width = \"\"; } /** * Calculate scale based on current element size. * Called when fixedAspectRatio is enabled or scaleReferenceSize changes. */ _updateScaleFromCurrentSize() { requestAnimationFrame(() => { const containerHeight = this.clientHeight; if (containerHeight > 0) { this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.vertical, containerMainAxisSize: containerHeight, scaleReferenceSize: this.scaleReferenceSize }); this.requestUpdate(); this.reportDimensions(); } }); } _refreshBorderRadiusFromCssVar() { if (this.instrumentMode) return; const next = readExternalScaleBorderRadiusPx(this, this.scaleType); if (this._computedBorderRadius !== next) { this._computedBorderRadius = next; } if (this.fixedAspectRatio) { const rect = this.getBoundingClientRect(); if (rect.height > 0) { this._scale = computeFixedAspectRatioScale({ orientation: ExternalScaleOrientation.vertical, containerMainAxisSize: rect.height, scaleReferenceSize: this.scaleReferenceSize }); this.reportDimensions(); } } } /** * Get the effective border radius to use. * In instrument mode: use explicit borderRadius prop or default based on scaleType. * In normal mode: use CSS variable computed value. */ _getEffectiveBorderRadius() { if (this.instrumentMode) { if (this.borderRadius !== void 0) { return this.borderRadius; } return this.scaleType === ScaleType.condensed ? 4 : 8; } return this._computedBorderRadius ?? (this.scaleType === ScaleType.condensed ? 4 : 8); } }"
4280
4280
  }
4281
4281
  ],
4282
4282
  "exports": [
@@ -4389,6 +4389,14 @@
4389
4389
  "module": "dist/building-blocks/chart-line/chart-line-base.js"
4390
4390
  }
4391
4391
  },
4392
+ {
4393
+ "kind": "js",
4394
+ "name": "RangeLabels",
4395
+ "declaration": {
4396
+ "name": "RangeLabels",
4397
+ "module": "dist/building-blocks/chart-line/chart-line-base.js"
4398
+ }
4399
+ },
4392
4400
  {
4393
4401
  "kind": "js",
4394
4402
  "name": "TimeDisplay",
@@ -4755,7 +4763,7 @@
4755
4763
  {
4756
4764
  "kind": "variable",
4757
4765
  "name": "ObcAlertButton",
4758
- "default": "class extends LitElement { constructor() { super(...arguments); this.nAlerts = 0; this.type = \"normal\"; this.large = false; this.showSilenceButton = false; this.silenceButtonDisabled = false; this.counter = false; this.blinking = false; this.flatMaxBreakpointPx = 0; this.silenceButtonMinBreakpointPx = 0; this.width = window.innerWidth; this.resizeListener = () => { this.width = window.innerWidth; }; } connectedCallback() { super.connectedCallback(); window.addEventListener(\"resize\", this.resizeListener); if (this.hasUpdated) { this.installBlinking(); } } disconnectedCallback() { window.removeEventListener(\"resize\", this.resizeListener); this._blinkAnimationCancel?.(); super.disconnectedCallback(); } renderAlertTwotoneIcon() { const twotone = this.alertType ? getAlertTwotoneComponent(this.alertType) : AlertTwotoneComponent.Caution; switch (twotone) { case AlertTwotoneComponent.Critical: return html`<obi-alerts-critical-twotone useCssColor class=\"icon\" ></obi-alerts-critical-twotone>`; case AlertTwotoneComponent.Diagnostic: return html`<obi-alerts-diagnostic-twotone useCssColor class=\"icon\" ></obi-alerts-diagnostic-twotone>`; case AlertTwotoneComponent.Warning: return html`<obi-alerts-warning-twotone useCssColor class=\"icon\" ></obi-alerts-warning-twotone>`; case AlertTwotoneComponent.Caution: return html`<obi-alerts-caution-twotone useCssColor class=\"icon\" ></obi-alerts-caution-twotone>`; default: return html`<obi-alerts-alarm-twotone useCssColor class=\"icon\" ></obi-alerts-alarm-twotone>`; } } alertIcon() { const isIdle = this.nAlerts === 0; if (isIdle) { return html`<obi-alerts class=\"icon\"></obi-alerts>`; } if (this.type === \"enhanced\") { return html`<obi-alerts-active class=\"icon\"></obi-alerts-active>`; } return this.renderAlertTwotoneIcon(); } alertIconNegative() { const useIdle = this.nAlerts === 0 || this.type !== \"enhanced\"; if (useIdle) { return html`<obi-alerts class=\"icon\"></obi-alerts>`; } return this.renderAlertTwotoneIcon(); } get activeType() { if (this.type === \"flat\") { return \"flat\"; } if (this.width < this.flatMaxBreakpointPx) { return \"flat\"; } return this.type; } get showSilenceButtonDynamic() { return this.showSilenceButton && this.width >= this.silenceButtonMinBreakpointPx && this.activeType !== \"flat\"; } installBlinking() { this._blinkAnimationCancel?.(); this._blinkAnimationCancel = blinkingAll(this); } updated() { this.installBlinking(); } render() { const hasAlerts = this.nAlerts > 0; const showCounter = this.counter && hasAlerts && this.activeType !== \"flat\"; const showBlinking = this.blinking && hasAlerts && this.alertType !== void 0 && supportsBlinking(this.alertType, false); return html` <div class=${classMap({ wrapper: true, [`alert-type-${this.alertType ?? \"none\"}`]: true, counter: showCounter, \"has-silence\": this.showSilenceButtonDynamic, [`type-${this.activeType}`]: true, blinking: showBlinking, large: this.large })} > <button class=\"alert-button\" @click=${() => this.dispatchEvent(new CustomEvent(\"click-alert\"))} > ${showBlinking ? html` <div class=\"blink\"> ${this.alertIconNegative()} ${showCounter ? html`<div class=\"badge\">${this.nAlerts}</div>` : null} </div>` : nothing} <div class=\"visible-wrapper\"> ${this.alertIcon()} ${showCounter ? html`<div class=\"badge\">${this.nAlerts}</div>` : nothing} </div> </button> ${this.showSilenceButtonDynamic ? html` <button class=\"silence-button\" @click=${() => this.dispatchEvent(new CustomEvent(\"click-silence\"))} ?disabled=${this.silenceButtonDisabled} > <div class=\"visible-wrapper\"> <obi-silence-iec class=\"icon\"></obi-silence-iec> </div> </button> ` : null} </div> `; } }"
4766
+ "default": "class extends LitElement { constructor() { super(...arguments); this.nAlerts = 0; this.type = \"normal\"; this.large = false; this.showSilenceButton = false; this.silenceButtonDisabled = false; this.counter = false; this.blinking = false; this.flashingSpeed = FlashingSpeed.Default; this.flashing = new FlashingController( this, () => this.resolvedFlashingSpeed ); this.flatMaxBreakpointPx = 0; this.silenceButtonMinBreakpointPx = 0; this.width = window.innerWidth; this.resizeListener = () => { this.width = window.innerWidth; }; } get resolvedFlashingSpeed() { if (!this.blinking || this.nAlerts <= 0 || this.alertType === void 0) { return FlashingSpeed.Fixed; } return resolveFlashingSpeed( this.flashingSpeed, this.alertType, AlertFlashPhase.Active ); } connectedCallback() { super.connectedCallback(); window.addEventListener(\"resize\", this.resizeListener); } disconnectedCallback() { window.removeEventListener(\"resize\", this.resizeListener); super.disconnectedCallback(); } renderAlertTwotoneIcon() { const twotone = this.alertType ? getAlertTwotoneComponent(this.alertType) : AlertTwotoneComponent.Caution; switch (twotone) { case AlertTwotoneComponent.Critical: return html`<obi-alerts-critical-twotone useCssColor class=\"icon\" ></obi-alerts-critical-twotone>`; case AlertTwotoneComponent.Diagnostic: return html`<obi-alerts-diagnostic-twotone useCssColor class=\"icon\" ></obi-alerts-diagnostic-twotone>`; case AlertTwotoneComponent.Warning: return html`<obi-alerts-warning-twotone useCssColor class=\"icon\" ></obi-alerts-warning-twotone>`; case AlertTwotoneComponent.Caution: return html`<obi-alerts-caution-twotone useCssColor class=\"icon\" ></obi-alerts-caution-twotone>`; default: return html`<obi-alerts-alarm-twotone useCssColor class=\"icon\" ></obi-alerts-alarm-twotone>`; } } alertIcon() { const isIdle = this.nAlerts === 0; if (isIdle) { return html`<obi-alerts class=\"icon\"></obi-alerts>`; } if (this.type === \"enhanced\") { return html`<obi-alerts-active class=\"icon\"></obi-alerts-active>`; } return this.renderAlertTwotoneIcon(); } alertIconNegative() { const useIdle = this.nAlerts === 0 || this.type !== \"enhanced\"; if (useIdle) { return html`<obi-alerts class=\"icon\"></obi-alerts>`; } return this.renderAlertTwotoneIcon(); } get activeType() { if (this.type === \"flat\") { return \"flat\"; } if (this.width < this.flatMaxBreakpointPx) { return \"flat\"; } return this.type; } get showSilenceButtonDynamic() { return this.showSilenceButton && this.width >= this.silenceButtonMinBreakpointPx && this.activeType !== \"flat\"; } render() { const hasAlerts = this.nAlerts > 0; const showCounter = this.counter && hasAlerts && this.activeType !== \"flat\"; const tempo = this.resolvedFlashingSpeed; const showBlinking = tempo !== FlashingSpeed.Fixed; return html` <div class=${classMap({ wrapper: true, [`alert-type-${this.alertType ?? \"none\"}`]: true, counter: showCounter, \"has-silence\": this.showSilenceButtonDynamic, [`type-${this.activeType}`]: true, [`flash-${tempo}`]: true, large: this.large })} > <button class=\"alert-button\" @click=${() => this.dispatchEvent(new CustomEvent(\"click-alert\"))} > ${showBlinking ? html` <div class=\"blink\"> ${this.alertIconNegative()} ${showCounter ? html`<div class=\"badge\">${this.nAlerts}</div>` : null} </div>` : nothing} <div class=\"visible-wrapper\"> ${this.alertIcon()} ${showCounter ? html`<div class=\"badge\">${this.nAlerts}</div>` : nothing} </div> </button> ${this.showSilenceButtonDynamic ? html` <button class=\"silence-button\" @click=${() => this.dispatchEvent(new CustomEvent(\"click-silence\"))} ?disabled=${this.silenceButtonDisabled} > <div class=\"visible-wrapper\"> <obi-silence-iec class=\"icon\"></obi-silence-iec> </div> </button> ` : null} </div> `; } }"
4759
4767
  }
4760
4768
  ],
4761
4769
  "exports": [
@@ -4814,10 +4822,14 @@
4814
4822
  "kind": "variable",
4815
4823
  "name": "ObcAlertFrameMode"
4816
4824
  },
4825
+ {
4826
+ "kind": "variable",
4827
+ "name": "ObcAlertFrameFlashEffect"
4828
+ },
4817
4829
  {
4818
4830
  "kind": "variable",
4819
4831
  "name": "ObcAlertFrame",
4820
- "default": "class extends LitElement { constructor() { super(...arguments); this.type = \"small-side-flip\"; this.thickness = \"small\"; this.status = AlertType.Alarm; this.mode = \"acked-active\"; this.wrapContent = false; this.fullWidth = false; this.sharpEdgeTopLeft = false; this.sharpEdgeTopRight = false; this.sharpEdgeBottomLeft = false; this.sharpEdgeBottomRight = false; this.textSize = \"regular\"; this.showIcon = false; this.showAlertCategoryIcon = true; } render() { return html` <div class=${classMap({ wrapper: true, \"wrap-content\": this.wrapContent, \"full-width\": this.fullWidth, [\"thickness-\" + this.thickness]: true, [this.type]: true, [this.status]: true, [\"text-size-\" + this.textSize]: true, \"sharp-edge-top-left\": this.sharpEdgeTopLeft, \"sharp-edge-top-right\": this.sharpEdgeTopRight, \"sharp-edge-bottom-left\": this.sharpEdgeBottomLeft, \"sharp-edge-bottom-right\": this.sharpEdgeBottomRight, [this.mode]: true })} > <slot></slot> ${this.flap()} </div> `; } syncBlinking() { if (this.mode !== \"unacked-active\" && this._blinkAnimationCancel) { this._blinkAnimationCancel(); this._blinkAnimationCancel = void 0; } if (this.mode === \"unacked-active\" && !this._blinkAnimationCancel) { this._blinkAnimationCancel = blinkingAll(this); } } connectedCallback() { super.connectedCallback(); if (this.hasUpdated) { this.syncBlinking(); } } updated() { this.syncBlinking(); } disconnectedCallback() { super.disconnectedCallback(); this._blinkAnimationCancel?.(); this._blinkAnimationCancel = void 0; } flap() { if (this.type === \"regular\") { return nothing; } if (this.type === \"small-side-flip\" && !this.showAlertCategoryIcon) { return nothing; } if (this.type === \"large-side-flip\" && !this.showIcon && !this.showAlertCategoryIcon) { return nothing; } let icon; if (!this.showAlertCategoryIcon) { icon = nothing; } else { icon = this.renderBadgeIcon(); } if (this.type === \"small-side-flip\") { return html`<div class=\"flap small\"> ${icon} <div class=\"mask up\"></div> </div>`; } if (this.type === \"large-side-flip\") { return html`<div class=\"flap large\"> ${icon} ${this.showIcon ? html`<div class=\"icon\"><slot name=\"icon\"></slot></div>` : nothing} <div class=\"mask up\"></div> <div class=\"mask down\"></div> </div>`; } if (this.type === \"bottom-flip\" || this.type === \"top-flip\") { return html`<div class=\"flap ${this.type === \"bottom-flip\" ? \"bottom\" : \"top\"}\" > ${icon} ${this.showIcon ? html`<div class=\"icon\"><slot name=\"icon\"></slot></div>` : nothing} <div class=\"label\"><slot name=\"label\"></slot></div> <div class=\"spacer\"></div> <div class=\"timer\"><slot name=\"timer\"></slot></div> <div class=\"mask right\"></div> <div class=\"mask left\"></div> </div>`; } console.error(\"Unknown type of alert frame:\", this.type); return nothing; } renderBadgeIcon() { switch (getAlertBadgeComponent(this.status)) { case AlertBadgeComponent.Critical: return html`<obi-critical-badge class=\"icon badge\" ></obi-critical-badge>`; case AlertBadgeComponent.Warning: return html`<obi-warning-badge class=\"icon badge\"></obi-warning-badge>`; case AlertBadgeComponent.Caution: return html`<obi-caution-badge class=\"icon badge\"></obi-caution-badge>`; case AlertBadgeComponent.Diagnostic: return html`<obi-diagnostic-badge class=\"icon badge\" ></obi-diagnostic-badge>`; default: return html`<obi-alarm-badge class=\"icon badge\"></obi-alarm-badge>`; } } }"
4832
+ "default": "class extends LitElement { constructor() { super(...arguments); this.type = \"small-side-flip\"; this.thickness = \"small\"; this.status = AlertType.Alarm; this.mode = \"acked-active\"; this.flashingSpeed = FlashingSpeed.Default; this.flashEffect = \"outline\"; this.flashing = new FlashingController( this, () => this.resolvedFlashingSpeed ); this.measureDash = () => { const wrapper = this.wrapper; if (!wrapper) { return; } const style = getComputedStyle(wrapper); const px = (value) => parseFloat(value) || 0; const box = { width: wrapper.offsetWidth, height: wrapper.offsetHeight, thickness: px(style.getPropertyValue(\"--thickness\")), radii: [ px(style.borderTopLeftRadius), px(style.borderTopRightRadius), px(style.borderBottomRightRadius), px(style.borderBottomLeftRadius) ] }; if (!sameDashBox(box, this.dashBox)) { this.dashBox = box; } }; this.wrapContent = false; this.fullWidth = false; this.sharpEdgeTopLeft = false; this.sharpEdgeTopRight = false; this.sharpEdgeBottomLeft = false; this.sharpEdgeBottomRight = false; this.textSize = \"regular\"; this.showIcon = false; this.showAlertCategoryIcon = true; } connectedCallback() { super.connectedCallback(); if (this.hasUpdated) { this.requestUpdate(); } } updated() { const wantsDash = this.mode === \"unacked-rectified\" && this.isConnected; if (wantsDash && !this.dashObserver && this.wrapper) { this.dashObserver = new ResizeObserver(this.measureDash); this.dashObserver.observe(this.wrapper); } if (wantsDash) { this.measureDash(); } if (!wantsDash && this.dashObserver) { this.dashObserver.disconnect(); this.dashObserver = void 0; this.dashBox = void 0; } } disconnectedCallback() { super.disconnectedCallback(); this.dashObserver?.disconnect(); this.dashObserver = void 0; } renderDash() { const box = this.dashBox; if (this.mode !== \"unacked-rectified\" || !box) { return nothing; } const pad = box.thickness + DASH_FLASH_GROWTH_PX; const centreline = { x: pad - box.thickness / 2, y: pad - box.thickness / 2, width: box.width + box.thickness, height: box.height + box.thickness, // A radius of 0 is a sharp edge and must stay square. radii: box.radii.map( (r) => r > 0 ? r + box.thickness / 2 : 0 ) }; const width = box.width + 2 * pad; const height = box.height + 2 * pad; return html`<svg class=\"dash\" aria-hidden=\"true\" width=${width} height=${height} viewBox=\"0 0 ${width} ${height}\" style=\"--dash-pad: ${pad}px\" > <path d=${roundedRectPath(centreline)}></path> </svg>`; } get resolvedFlashingSpeed() { switch (this.mode) { case \"unacked-active\": return resolveFlashingSpeed( this.flashingSpeed, this.status, AlertFlashPhase.Active ); case \"unacked-rectified\": return resolveFlashingSpeed( this.flashingSpeed, this.status, AlertFlashPhase.Rectified ); default: return FlashingSpeed.Fixed; } } render() { return html` <div class=${classMap({ wrapper: true, \"wrap-content\": this.wrapContent, \"full-width\": this.fullWidth, [\"thickness-\" + this.thickness]: true, [this.type]: true, [this.status]: true, [\"text-size-\" + this.textSize]: true, \"sharp-edge-top-left\": this.sharpEdgeTopLeft, \"sharp-edge-top-right\": this.sharpEdgeTopRight, \"sharp-edge-bottom-left\": this.sharpEdgeBottomLeft, \"sharp-edge-bottom-right\": this.sharpEdgeBottomRight, [this.mode]: true, [\"flash-\" + this.resolvedFlashingSpeed]: true, [\"flash-effect-\" + this.flashEffect]: true })} > <slot></slot> ${this.renderDash()} ${this.flap()} </div> `; } flap() { if (this.type === \"regular\") { return nothing; } if (this.type === \"small-side-flip\" && !this.showAlertCategoryIcon) { return nothing; } if (this.type === \"large-side-flip\" && !this.showIcon && !this.showAlertCategoryIcon) { return nothing; } let icon; if (!this.showAlertCategoryIcon) { icon = nothing; } else { icon = this.renderBadgeIcon(); } if (this.type === \"small-side-flip\") { return html`<div class=\"flap small\"> ${icon} <div class=\"mask up\"></div> </div>`; } if (this.type === \"large-side-flip\") { return html`<div class=\"flap large\"> ${icon} ${this.showIcon ? html`<div class=\"icon\"><slot name=\"icon\"></slot></div>` : nothing} <div class=\"mask up\"></div> <div class=\"mask down\"></div> </div>`; } if (this.type === \"bottom-flip\" || this.type === \"top-flip\") { return html`<div class=\"flap ${this.type === \"bottom-flip\" ? \"bottom\" : \"top\"}\" > ${icon} ${this.showIcon ? html`<div class=\"icon\"><slot name=\"icon\"></slot></div>` : nothing} <div class=\"label\"><slot name=\"label\"></slot></div> <div class=\"spacer\"></div> <div class=\"timer\"><slot name=\"timer\"></slot></div> <div class=\"mask right\"></div> <div class=\"mask left\"></div> </div>`; } console.error(\"Unknown type of alert frame:\", this.type); return nothing; } renderBadgeIcon() { switch (getAlertBadgeComponent(this.status)) { case AlertBadgeComponent.Critical: return html`<obi-critical-badge class=\"icon badge\" ></obi-critical-badge>`; case AlertBadgeComponent.Warning: return html`<obi-warning-badge class=\"icon badge\"></obi-warning-badge>`; case AlertBadgeComponent.Caution: return html`<obi-caution-badge class=\"icon badge\"></obi-caution-badge>`; case AlertBadgeComponent.Diagnostic: return html`<obi-diagnostic-badge class=\"icon badge\" ></obi-diagnostic-badge>`; default: return html`<obi-alarm-badge class=\"icon badge\"></obi-alarm-badge>`; } } }"
4821
4833
  }
4822
4834
  ],
4823
4835
  "exports": [
@@ -4837,6 +4849,14 @@
4837
4849
  "module": "dist/components/alert-frame/alert-frame.js"
4838
4850
  }
4839
4851
  },
4852
+ {
4853
+ "kind": "js",
4854
+ "name": "ObcAlertFrameFlashEffect",
4855
+ "declaration": {
4856
+ "name": "ObcAlertFrameFlashEffect",
4857
+ "module": "dist/components/alert-frame/alert-frame.js"
4858
+ }
4859
+ },
4840
4860
  {
4841
4861
  "kind": "js",
4842
4862
  "name": "ObcAlertFrameMode",
@@ -4886,7 +4906,7 @@
4886
4906
  {
4887
4907
  "kind": "variable",
4888
4908
  "name": "ObcAlertIcon",
4889
- "default": "class extends LitElement { constructor() { super(...arguments); this.alertType = AlertType.Alarm; } get _effectiveType() { if (this.type) { return this.type; } return this.alertType; } get bamType() { return getBamAlertTypeForBlinking(this._effectiveType); } get _effectiveState() { if (this.active === false) { return \"rectified\"; } else if (this.acknowledged) { return \"acknowledged\"; } else if (this.silenced) { return \"silenced\"; } else if (this.active === true) { return \"unacknowledged\"; } return \"unacknowledged\"; } get icon() { if (this._effectiveType === AlertType.LevelCritical) { switch (this._effectiveState) { case \"rectified\": return mapping[ \"critical-rectified\" /* CriticalRectified */ ]; case \"silenced\": return mapping[ \"critical-silenced\" /* CriticalSilenced */ ]; case \"unacknowledged\": return mapping[ \"critical-unack\" /* CriticalUnack */ ]; default: return mapping[ \"critical\" /* Critical */ ]; } } else if (this._effectiveType === AlertType.LevelLow) { switch (this._effectiveState) { case \"rectified\": return mapping[ \"low-rectified\" /* LowRectified */ ]; case \"silenced\": return mapping[ \"low-silenced\" /* LowSilenced */ ]; case \"unacknowledged\": return mapping[ \"low-unack\" /* LowUnack */ ]; default: return mapping[ \"low-unack\" /* LowUnack */ ]; } } else if (this.bamType === AlertType.Alarm) { switch (this._effectiveState) { case \"rectified\": return mapping[ \"alarm-rectified\" /* AlarmRectified */ ]; case \"silenced\": return mapping[ \"alarm-silenced\" /* AlarmSilenced */ ]; case \"unacknowledged\": return mapping[ \"alarm-unack\" /* AlarmUnack */ ]; default: return null; } } else if (this.bamType === AlertType.Warning) { if (this.active === false) { return mapping[ \"warning-rectified\" /* WarningRectified */ ]; } else if (this.acknowledged) { return mapping[ \"warning-silenced\" /* WarningSilenced */ ]; } else { return mapping[ \"warning-unack\" /* WarningUnack */ ]; } } return null; } renderStaticIcon() { if (this._effectiveType === AlertType.Caution) { return html`<obi-caution-color-iec usecsscolor></obi-caution-color-iec>`; } if (this._effectiveType === AlertType.LevelDiagnostic) { return html`<obi-diagnostic-badge style=\"color: var(--alert-diagnostic-color);\" ></obi-diagnostic-badge>`; } if (this._effectiveState === \"acknowledged\") { if (this._effectiveType === AlertType.LevelCritical) { return html`${criticalAcknowledged}`; } if (this._effectiveType === AlertType.LevelLow) { return html`${lowAcknowledged}`; } switch (this.bamType) { case AlertType.Alarm: return html`<obi-alarm-acknowledged-iec usecsscolor ></obi-alarm-acknowledged-iec>`; case AlertType.Warning: return html`<obi-warning-acknowledged-iec usecsscolor ></obi-warning-acknowledged-iec>`; default: return nothing; } } else if (this._effectiveState === \"rectified\") { if (this._effectiveType === AlertType.LevelCritical) { return html`${criticalRectifiedB}`; } if (this._effectiveType === AlertType.LevelLow) { return html`${lowRectifiedB}`; } switch (this.bamType) { case AlertType.Alarm: return html`<obi-alarm-rectified-outlined usecsscolor ></obi-alarm-rectified-outlined>`; case AlertType.Warning: return html`<obi-warning-rectified-outlined usecsscolor ></obi-warning-rectified-outlined>`; default: return nothing; } } return nothing; } render() { if (!this._effectiveType) { return html`<div>No alarm</div>`; } if (supportsBlinking(this._effectiveType, this.acknowledged ?? false)) { const icons = this.icon; if (!icons) { throw new Error(\"No icon found\"); } const blinkMode = getAlertBlinkMode(this._effectiveType); return html` <div class=${classMap({ wrapper: true, [`blink-${blinkMode}`]: true })} > <span class=\"a\">${icons.a}</span> <span class=\"b\">${icons.b}</span> </div> `; } return html`<div class=\"wrapper\">${this.renderStaticIcon()}</div>`; } syncBlinking() { if (this._blinkAnimationCancel && !supportsBlinking(this._effectiveType, this.acknowledged ?? false)) { this._blinkAnimationCancel(); this._blinkAnimationCancel = void 0; } if (!this._blinkAnimationCancel && supportsBlinking(this._effectiveType, this.acknowledged ?? false)) { this._blinkAnimationCancel = blinkingAll(this); } } connectedCallback() { super.connectedCallback(); if (this.hasUpdated) { this.syncBlinking(); } } updated() { this.syncBlinking(); } disconnectedCallback() { super.disconnectedCallback(); if (this._blinkAnimationCancel) { this._blinkAnimationCancel(); this._blinkAnimationCancel = void 0; } } }"
4909
+ "default": "class extends LitElement { constructor() { super(...arguments); this.alertType = AlertType.Alarm; this.flashingSpeed = FlashingSpeed.Default; this.flashing = new FlashingController( this, () => this.resolvedFlashingSpeed ); } get resolvedFlashingSpeed() { if (this.acknowledged || !this.icon) { return FlashingSpeed.Fixed; } return resolveFlashingSpeed( this.flashingSpeed, this._effectiveType, this.active === false ? AlertFlashPhase.Rectified : AlertFlashPhase.Active ); } get _effectiveType() { if (this.type) { return this.type; } return this.alertType; } get bamType() { return getBamAlertTypeForBlinking(this._effectiveType); } get _effectiveState() { if (this.active === false) { return \"rectified\"; } else if (this.acknowledged) { return \"acknowledged\"; } else if (this.silenced) { return \"silenced\"; } else if (this.active === true) { return \"unacknowledged\"; } return \"unacknowledged\"; } get icon() { if (this._effectiveType === AlertType.LevelCritical) { switch (this._effectiveState) { case \"rectified\": return mapping[ \"critical-rectified\" /* CriticalRectified */ ]; case \"silenced\": return mapping[ \"critical-silenced\" /* CriticalSilenced */ ]; case \"unacknowledged\": return mapping[ \"critical-unack\" /* CriticalUnack */ ]; default: return mapping[ \"critical\" /* Critical */ ]; } } else if (this._effectiveType === AlertType.LevelLow) { switch (this._effectiveState) { case \"rectified\": return mapping[ \"low-rectified\" /* LowRectified */ ]; case \"silenced\": return mapping[ \"low-silenced\" /* LowSilenced */ ]; case \"unacknowledged\": return mapping[ \"low-unack\" /* LowUnack */ ]; default: return mapping[ \"low-unack\" /* LowUnack */ ]; } } else if (this.bamType === AlertType.Alarm) { switch (this._effectiveState) { case \"rectified\": return mapping[ \"alarm-rectified\" /* AlarmRectified */ ]; case \"silenced\": return mapping[ \"alarm-silenced\" /* AlarmSilenced */ ]; case \"unacknowledged\": return mapping[ \"alarm-unack\" /* AlarmUnack */ ]; default: return null; } } else if (this.bamType === AlertType.Warning) { if (this.active === false) { return mapping[ \"warning-rectified\" /* WarningRectified */ ]; } else if (this.acknowledged) { return mapping[ \"warning-silenced\" /* WarningSilenced */ ]; } else { return mapping[ \"warning-unack\" /* WarningUnack */ ]; } } return null; } renderStaticIcon() { if (this._effectiveType === AlertType.Caution) { return html`<obi-caution-color-iec usecsscolor></obi-caution-color-iec>`; } if (this._effectiveType === AlertType.LevelDiagnostic) { return html`<obi-diagnostic-badge style=\"color: var(--alert-diagnostic-color);\" ></obi-diagnostic-badge>`; } if (this._effectiveState === \"acknowledged\") { if (this._effectiveType === AlertType.LevelCritical) { return html`${criticalAcknowledged}`; } if (this._effectiveType === AlertType.LevelLow) { return html`${lowAcknowledged}`; } switch (this.bamType) { case AlertType.Alarm: return html`<obi-alarm-acknowledged-iec usecsscolor ></obi-alarm-acknowledged-iec>`; case AlertType.Warning: return html`<obi-warning-acknowledged-iec usecsscolor ></obi-warning-acknowledged-iec>`; default: return nothing; } } else if (this._effectiveState === \"rectified\") { if (this._effectiveType === AlertType.LevelCritical) { return html`${criticalRectifiedB}`; } if (this._effectiveType === AlertType.LevelLow) { return html`${lowRectifiedB}`; } switch (this.bamType) { case AlertType.Alarm: return html`<obi-alarm-rectified-outlined usecsscolor ></obi-alarm-rectified-outlined>`; case AlertType.Warning: return html`<obi-warning-rectified-outlined usecsscolor ></obi-warning-rectified-outlined>`; default: return nothing; } } return nothing; } render() { if (!this._effectiveType) { return html`<div>No alarm</div>`; } const tempo = this.resolvedFlashingSpeed; const icons = this.icon; if (tempo !== FlashingSpeed.Fixed && icons) { return html` <div class=${classMap({ wrapper: true, [`flash-${tempo}`]: true })}> <span class=\"a\">${icons.a}</span> <span class=\"b\">${icons.b}</span> </div> `; } return html`<div class=\"wrapper\">${this.renderStaticIcon()}</div>`; } }"
4890
4910
  }
4891
4911
  ],
4892
4912
  "exports": [
@@ -9436,7 +9456,7 @@
9436
9456
  {
9437
9457
  "kind": "variable",
9438
9458
  "name": "ObcGaugeTrend",
9439
- "default": "class extends SetpointMixin(ObcChartLineBase) { constructor() { super(); this._isFirstUpdate = false; this._explicitXAxisType = false; this.scaleType = ScaleType.regular; this.minValue = 0; this.maxValue = 100; this.chartMinValue = void 0; this.chartMaxValue = void 0; this.value = void 0; this.hasBar = false; this.hasScale = false; this.hasAdvice = false; this.fillMode = FillMode.fill; this.fillMin = 0; this.fillMax = void 0; this.advice = []; this.primaryTickmarkInterval = void 0; this.secondaryTickmarkInterval = 0.5; this.tertiaryTickmarkInterval = void 0; this.chartFill = false; this.legend = false; this.showDebugOverlay = false; this.showGrid = false; this.showTickMarks = false; this.showPoints = false; this.xAxisType = XAxisType.category; this.yAxisPosition = YAxisPosition.left; this.lineMode = LineMode.smooth; this.unit = \"\"; this.timeDisplay = TimeDisplay.date; this.fixedAspectRatioScaling = true; this.instrumentMode = true; this.borderRadius = 8; this.borderRadiusPosition = BorderRadiusPosition.innerFirstChild; this.borderRadiusPositionExternalScales = BorderRadiusPosition.middleChild; this.yAxes = [ { id: \"y\", position: \"left\", min: this.chartMinValue ?? this.minValue, max: this.chartMaxValue ?? this.maxValue } ]; } async firstUpdated() { this._createBarVerticalElement(); this._updateBorderRadiusPosition(); await super.firstUpdated(); this._isFirstUpdate = false; } disconnectedCallback() { super.disconnectedCallback(); } _createBarVerticalElement() { if (!this._barVerticalElement) { const barVertical = document.createElement(\"obc-bar-vertical\"); barVertical.setAttribute(\"slot\", \"right-scale\"); this._barVerticalElement = barVertical; this.appendChild(barVertical); this._updateBarVerticalProperties(); } } _updateBorderRadiusPosition() { if (!this.hasBar && !this.hasScale) { this.borderRadiusPosition = BorderRadiusPosition.middleRoundedChild; } else { this.borderRadiusPosition = BorderRadiusPosition.innerFirstChild; } } _updateBarVerticalProperties() { if (!this._barVerticalElement) { return; } const barVertical = this._barVerticalElement; const effectiveHeight = this.getEffectiveHeight(); barVertical.minValue = this.minValue; barVertical.maxValue = this.maxValue; barVertical.height = effectiveHeight; barVertical.side = \"right\"; barVertical.hasScale = this.hasScale; barVertical.hasBar = this.hasBar; barVertical.barThickness = this.hasScale ? 48 : 24; barVertical.scaleType = this.scaleType; barVertical.fillMode = this.fillMode; barVertical.fillMin = this.fillMin; barVertical.fillMax = this.fillMode === FillMode.fill ? this.value : this.fillMax ?? this.value; barVertical.value = this.value; barVertical.setpoint = this.setpoint; barVertical.newSetpoint = this.newSetpoint; barVertical.touching = this.touching; barVertical.atSetpoint = this.atSetpoint; barVertical.autoAtSetpoint = this.autoAtSetpoint; barVertical.autoAtSetpointDeadband = this.autoAtSetpointDeadband; barVertical.setpointAtZeroDeadband = this.setpointAtZeroDeadband; barVertical.animateSetpoint = this.animateSetpoint; barVertical.setpointOverride = this.setpointOverride; barVertical.advicePosition = AdvicePosition.inner; barVertical.advices = this.hasAdvice ? this.advice : []; barVertical.primaryTickmarkInterval = this.primaryTickmarkInterval; barVertical.secondaryTickmarkInterval = this.secondaryTickmarkInterval; barVertical.tertiaryTickmarkInterval = this.tertiaryTickmarkInterval; barVertical.scaleBackground = this.hasScale; barVertical.barContainerStyle = this.hasScale ? void 0 : BarContainerStyle.secondary; barVertical.fixedAspectRatio = this.fixedAspectRatioScaling; barVertical.scaleReferenceSize = this.scaleReferenceSize; barVertical.hasPrimaryTickmarks = this.primaryTickmarkInterval !== void 0; barVertical.hasTertiaryTickmarks = this.tertiaryTickmarkInterval !== void 0; barVertical.priority = this.priority; barVertical.state = this.state; barVertical.instrumentMode = true; barVertical.borderRadius = 8; barVertical.borderRadiusPosition = this.hasScale ? BorderRadiusPosition.middleChild : BorderRadiusPosition.outerLastChild; barVertical.highlightCurrentValue = !this.hasBar; } /** * Apply fill when chartFill is true. */ shouldApplyFill() { return this.chartFill; } /** * Return the fill mode for area rendering. * Maps chartFill boolean to base class fillMode. */ getFillMode() { return this.chartFill ? \"semitransparent\" : void 0; } /** * Stacking is not used in gauge-trend (always false). */ shouldStack() { return false; } willUpdate(changed) { if (changed.has(\"xAxisType\") && this.xAxisType !== this._autoAppliedXAxisType && (this.hasUpdated || this.xAxisType !== XAxisType.category)) { this._explicitXAxisType = true; } if (!this._explicitXAxisType && changed.has(\"data\")) { const allHaveX = (this.data?.length ?? 0) > 0 && this.data.every((d) => d.x != null); const target = allHaveX ? XAxisType.time : XAxisType.category; if (this.xAxisType !== target) { this._autoAppliedXAxisType = target; this.xAxisType = target; } } super.willUpdate(changed); if (changed.has(\"chartMinValue\") || changed.has(\"chartMaxValue\") || changed.has(\"minValue\") || changed.has(\"maxValue\")) { const chartMin = this.chartMinValue ?? this.minValue; const chartMax = this.chartMaxValue ?? this.maxValue; this.yAxes = [ { id: \"y\", position: \"left\", min: chartMin, max: chartMax } ]; } if (changed.has(\"hasScale\")) { this.borderRadiusPositionExternalScales = this.hasScale ? BorderRadiusPosition.middleChild : BorderRadiusPosition.outerLastChild; } } updated(changed) { if (changed.has(\"chartFill\") && this.data) { this.data = [...this.data]; } super.updated(changed); if (changed.has(\"hasBar\") || changed.has(\"hasScale\")) { this._updateBorderRadiusPosition(); } if (!this._barVerticalElement || this._isFirstUpdate) return; const shouldUpdateScale = changed.has(\"minValue\") || changed.has(\"maxValue\") || changed.has(\"value\") || changed.has(\"setpoint\") || changed.has(\"newSetpoint\") || changed.has(\"touching\") || changed.has(\"atSetpoint\") || changed.has(\"autoAtSetpoint\") || changed.has(\"autoAtSetpointDeadband\") || changed.has(\"setpointAtZeroDeadband\") || changed.has(\"setpointOverride\") || changed.has(\"hasBar\") || changed.has(\"hasScale\") || changed.has(\"hasAdvice\") || changed.has(\"fillMode\") || changed.has(\"fillMin\") || changed.has(\"fillMax\") || changed.has(\"advice\") || changed.has(\"primaryTickmarkInterval\") || changed.has(\"secondaryTickmarkInterval\") || changed.has(\"tertiaryTickmarkInterval\") || changed.has(\"scaleType\") || changed.has(\"state\") || // Scale state inherits from parent 'state' changed.has(\"priority\") || changed.has(\"height\") || changed.has(\"width\") || changed.has(\"scaleReferenceSize\") || changed.has(\"chartMinValue\") || changed.has(\"chartMaxValue\"); if (shouldUpdateScale) this._updateBarVerticalProperties(); } }"
9459
+ "default": "class extends SetpointMixin(ObcChartLineBase) { constructor() { super(); this._isFirstUpdate = false; this._explicitXAxisType = false; this.scaleType = ScaleType.regular; this.minValue = 0; this.maxValue = 100; this.chartMinValue = void 0; this.chartMaxValue = void 0; this.value = void 0; this.hasBar = false; this.hasScale = false; this.hasAdvice = false; this.fillMode = FillMode.fill; this.fillMin = 0; this.fillMax = void 0; this.advice = []; this.primaryTickmarkInterval = void 0; this.secondaryTickmarkInterval = 0.5; this.tertiaryTickmarkInterval = void 0; this.chartFill = false; this.legend = false; this.showDebugOverlay = false; this.showGrid = false; this.showTickMarks = false; this.showPoints = false; this.xAxisType = XAxisType.category; this.yAxisPosition = YAxisPosition.left; this.lineMode = LineMode.smooth; this.unit = \"\"; this.timeDisplay = TimeDisplay.date; this.fixedAspectRatioScaling = true; this.instrumentMode = true; this.borderRadius = 8; this.borderRadiusPosition = BorderRadiusPosition.innerFirstChild; this.borderRadiusPositionExternalScales = BorderRadiusPosition.middleChild; this.yAxes = [ { id: \"y\", position: \"left\", min: this.chartMinValue ?? this.minValue, max: this.chartMaxValue ?? this.maxValue } ]; } async firstUpdated() { this._createBarVerticalElement(); this._updateBorderRadiusPosition(); await super.firstUpdated(); this._isFirstUpdate = false; } disconnectedCallback() { super.disconnectedCallback(); } _createBarVerticalElement() { if (!this._barVerticalElement) { const barVertical = document.createElement(\"obc-bar-vertical\"); barVertical.setAttribute(\"slot\", \"right-scale\"); this._barVerticalElement = barVertical; this.appendChild(barVertical); this._updateBarVerticalProperties(); } } _updateBorderRadiusPosition() { if (!this.hasBar && !this.hasScale) { this.borderRadiusPosition = BorderRadiusPosition.middleRoundedChild; } else { this.borderRadiusPosition = BorderRadiusPosition.innerFirstChild; } } _updateBarVerticalProperties() { if (!this._barVerticalElement) { return; } const barVertical = this._barVerticalElement; const effectiveHeight = this.getEffectiveHeight(); barVertical.minValue = this.minValue; barVertical.maxValue = this.maxValue; barVertical.height = effectiveHeight; barVertical.side = \"right\"; barVertical.hasScale = this.hasScale; barVertical.hasBar = this.hasBar; barVertical.barThickness = this.hasScale ? 48 : 24; barVertical.scaleType = this.scaleType; barVertical.fillMode = this.fillMode; barVertical.fillMin = this.fillMin; barVertical.fillMax = this.fillMode === FillMode.fill ? this.value : this.fillMax ?? this.value; barVertical.value = this.value; barVertical.setpoint = this.setpoint; barVertical.newSetpoint = this.newSetpoint; barVertical.touching = this.touching; barVertical.atSetpoint = this.atSetpoint; barVertical.autoAtSetpoint = this.autoAtSetpoint; barVertical.autoAtSetpointDeadband = this.autoAtSetpointDeadband; barVertical.setpointAtZeroDeadband = this.setpointAtZeroDeadband; barVertical.animateSetpoint = this.animateSetpoint; barVertical.setpointOverride = this.setpointOverride; barVertical.advicePosition = AdvicePosition.inner; barVertical.advices = this.hasAdvice ? this.advice : []; barVertical.primaryTickmarkInterval = this.primaryTickmarkInterval; barVertical.secondaryTickmarkInterval = this.secondaryTickmarkInterval; barVertical.tertiaryTickmarkInterval = this.tertiaryTickmarkInterval; barVertical.scaleBackground = this.hasScale; barVertical.barContainerStyle = this.hasScale ? void 0 : BarContainerStyle.secondary; barVertical.fixedAspectRatio = this.fixedAspectRatioScaling; barVertical.scaleReferenceSize = this.scaleReferenceSize; barVertical.hasPrimaryTickmarks = this.primaryTickmarkInterval !== void 0; barVertical.hasTertiaryTickmarks = this.tertiaryTickmarkInterval !== void 0; barVertical.priority = this.priority; barVertical.state = this.state; barVertical.instrumentMode = true; barVertical.borderRadius = 8; barVertical.borderRadiusPosition = this.hasScale ? BorderRadiusPosition.middleChild : BorderRadiusPosition.outerLastChild; barVertical.highlightCurrentValue = !this.hasBar; } /** * Apply fill when chartFill is true. */ shouldApplyFill() { return this.chartFill; } /** * Return the fill mode for area rendering. * Maps chartFill boolean to base class fillMode. */ getFillMode() { return this.chartFill ? \"semitransparent\" : void 0; } /** * Stacking is not used in gauge-trend (always false). */ shouldStack() { return false; } /** * The right scale is this component's own bar, ranged by `minValue` / * `maxValue`. `chartMinValue` / `chartMaxValue` exist so the plotted range * can differ from it, so the chart's axis range must not overwrite it. */ ownsSlottedScaleRange(side) { return side !== \"right\"; } willUpdate(changed) { if (changed.has(\"xAxisType\") && this.xAxisType !== this._autoAppliedXAxisType && (this.hasUpdated || this.xAxisType !== XAxisType.category)) { this._explicitXAxisType = true; } if (!this._explicitXAxisType && changed.has(\"data\")) { const allHaveX = (this.data?.length ?? 0) > 0 && this.data.every((d) => d.x != null); const target = allHaveX ? XAxisType.time : XAxisType.category; if (this.xAxisType !== target) { this._autoAppliedXAxisType = target; this.xAxisType = target; } } super.willUpdate(changed); if (changed.has(\"chartMinValue\") || changed.has(\"chartMaxValue\") || changed.has(\"minValue\") || changed.has(\"maxValue\")) { const chartMin = this.chartMinValue ?? this.minValue; const chartMax = this.chartMaxValue ?? this.maxValue; this.yAxes = [ { id: \"y\", position: \"left\", min: chartMin, max: chartMax } ]; } if (changed.has(\"hasScale\")) { this.borderRadiusPositionExternalScales = this.hasScale ? BorderRadiusPosition.middleChild : BorderRadiusPosition.outerLastChild; } } updated(changed) { if (changed.has(\"chartFill\") && this.data) { this.data = [...this.data]; } super.updated(changed); if (changed.has(\"hasBar\") || changed.has(\"hasScale\")) { this._updateBorderRadiusPosition(); } if (!this._barVerticalElement || this._isFirstUpdate) return; const shouldUpdateScale = changed.has(\"minValue\") || changed.has(\"maxValue\") || changed.has(\"value\") || changed.has(\"setpoint\") || changed.has(\"newSetpoint\") || changed.has(\"touching\") || changed.has(\"atSetpoint\") || changed.has(\"autoAtSetpoint\") || changed.has(\"autoAtSetpointDeadband\") || changed.has(\"setpointAtZeroDeadband\") || changed.has(\"setpointOverride\") || changed.has(\"hasBar\") || changed.has(\"hasScale\") || changed.has(\"hasAdvice\") || changed.has(\"fillMode\") || changed.has(\"fillMin\") || changed.has(\"fillMax\") || changed.has(\"advice\") || changed.has(\"primaryTickmarkInterval\") || changed.has(\"secondaryTickmarkInterval\") || changed.has(\"tertiaryTickmarkInterval\") || changed.has(\"scaleType\") || changed.has(\"state\") || // Scale state inherits from parent 'state' changed.has(\"priority\") || changed.has(\"height\") || changed.has(\"width\") || changed.has(\"scaleReferenceSize\") || changed.has(\"chartMinValue\") || changed.has(\"chartMaxValue\"); if (shouldUpdateScale) this._updateBarVerticalProperties(); } }"
9440
9460
  }
9441
9461
  ],
9442
9462
  "exports": [
@@ -10140,7 +10160,7 @@
10140
10160
  {
10141
10161
  "kind": "variable",
10142
10162
  "name": "ObcReadoutListItem",
10143
- "default": "class extends LitElement { constructor() { super(...arguments); this.hasValue = true; this.value = null; this.valueType = ReadoutValueType.number; this.off = false; this.offText = \"OFF\"; this.hasSetpoint = false; this.hasAdvice = false; this.clickable = false; this.hasLeadingIcon = false; this.hasDegree = false; this.hasDegreeSpacer = false; this.fractionDigits = 0; this.maxDigits = 0; this.alert = false; this.showDebugOverlay = false; this.deferredSetpointHidePhase = ReadoutBlockHidePhase.none; this.hasCompletedFirstUpdate = false; } get resolvedSize() { return this.size ?? ReadoutListItemSize.small; } get resolvedStacking() { return this.stacking ?? \"trailing-unit\"; } /** Both leading-src stackings move the source into the label stack. */ get hasLeadingSrc() { const stacking = this.resolvedStacking; return stacking === \"leading-src\" || stacking === \"leading-src-inline\"; } get resolvedPriority() { return this.priority ?? \"regular\"; } /** * Whether a unit box follows the value: a unit, or a reserver alone — the * blank reserved column keeps a unit-less row aligned with its neighbours. */ get hasTrailingUnitBox() { return this.resolvedStacking !== \"leading-unit\" && (Boolean(this.unit) || Boolean(this.unitOptions?.spaceReserver)); } get resolvedFractionDigits() { return this.fractionDigits ?? 0; } get resolvedMaxDigits() { return resolveReadoutDigitCount(this.maxDigits); } /** * Whether a digit knob failed to arrive (`NaN` / `null` / `undefined`). * The raw knobs are forwarded to the blocks, which render the reading as * the unavailable dash (see `obc-readout-block.digitCountsMissing`); this * getter only keeps the setpoint comparison in agreement with what is * displayed. */ get digitCountsMissing() { return isReadoutDigitCountMissing(this.fractionDigits) || isReadoutDigitCountMissing(this.maxDigits); } get resolvedClickable() { const clickable = this.clickable; if (!clickable) { return false; } if (clickable === true) { return { border: \"squared\" /* squared */ }; } return { border: clickable.border ?? \"squared\" /* squared */ }; } get isAtSetpoint() { if (!this.hasSetpoint) { return false; } if (this.digitCountsMissing) { return false; } return isDisplayedAtSetpoint( resolveReadoutNumericValue(this.value, this.valueType) ?? null, this.setpoint, this.numericFormatOptions(this.resolvedMaxDigits) ); } get resolvedSetpointInteraction() { return this.setpointOptions?.interaction ?? \"always-visible\"; } get isFlipFlop() { return this.resolvedSetpointInteraction === \"flip-flop\"; } get isEqualSize() { return this.resolvedSetpointInteraction === \"equal-size\"; } get isPopUp() { return this.resolvedSetpointInteraction === \"pop-up\"; } get setpointTouching() { return this.setpointOptions?.touching ?? false; } /** * The setpoint is rendered \"emphasised\" (primary size + SemiBold weight) when * it is the focus of attention: while actively adjusting (`touching`), or while * a flip-flop has the value away from the setpoint. Otherwise it is a secondary * (smaller, regular-weight) reference next to the value. */ get isSetpointEmphasized() { if (!this.hasSetpoint) { return false; } if (this.setpointTouching) { return true; } return this.isFlipFlop && !this.isAtSetpoint; } /** * The row's enhanced (in-command) colour state, applied uniformly to BOTH the * value and the setpoint — they are always either both neutral or both enhanced * (never a blue setpoint next to a grey value). Driven by `priority` only; * `valueOptions.weight` changes weight, not colour. */ get rowEnhanced() { return this.resolvedPriority === \"enhanced\"; } /** Primary value-typography size for the current density tier. */ get primarySize() { return readoutPrimarySize(this.resolvedSize); } /** Secondary (de-emphasised) value-typography size for the current density tier. */ get secondarySize() { return readoutSecondarySize(this.resolvedSize); } get valueSize() { return readoutValueSize({ primary: this.primarySize, secondary: this.secondarySize, setpointEmphasized: this.isSetpointEmphasized, equalSize: this.isEqualSize }); } get setpointSize() { return readoutSetpointSize({ primary: this.primarySize, secondary: this.secondarySize, emphasized: this.isSetpointEmphasized, equalSize: this.isEqualSize }); } /** Value font weight passes straight to obc-textbox; regular when unset. Colour is separate. */ get valueWeight() { return this.valueOptions?.weight ?? ObcTextboxFontWeight.regular; } /** Label size: `xs` for dense list rows unless overridden (see {@link ReadoutLabelOptions}). */ get labelSize() { return this.labelOptions?.size ?? ObcTextboxSize.xs; } /** * Label weight: SemiBold only for enhanced (in-command) rows, regular * otherwise — the designer reduced the amount of semibold in the interface * (Figma 6.1 review). */ get labelWeight() { return this.rowEnhanced ? ObcTextboxFontWeight.semibold : ObcTextboxFontWeight.regular; } /** Setpoint is SemiBold only while emphasised, otherwise regular weight. */ get setpointWeight() { return readoutSetpointWeight(this.isSetpointEmphasized); } numericFormatOptions(maxDigits) { return readoutNumericFormatOptions(maxDigits, this.resolvedFractionDigits); } /** classMap fragment for a block / source carrying per-block data quality. */ dataQualityClasses(dataQuality) { return readoutDataQualityClasses(dataQuality); } /** * Forward the matching list-item icon slot into the block's single `icon` * slot. The variant's default marker lives in `obc-readout-block` and shows * when nothing is assigned here (an empty forwarded slot flattens to nothing). */ renderForwardedIcon(variant) { if (variant === ReadoutBlockVariant.setpoint) { return html`<slot name=\"setpoint-icon\" slot=\"icon\"></slot>`; } if (variant === ReadoutBlockVariant.advice) { return html`<slot name=\"advice-icon\" slot=\"icon\"></slot>`; } return html`<slot name=\"value-icon\" slot=\"icon\"></slot>`; } renderBlock(config) { return html` <obc-readout-block exportparts=\"block, block-content, block-text, block-icon, degree\" .variant=${config.variant} .value=${config.value ?? null} .valueType=${config.valueType ?? ReadoutValueType.number} .size=${this.resolvedSize} .valueSize=${config.valueSize} .enhanced=${config.enhanced} .weight=${config.weight} .hasDegree=${config.hasDegree ?? false} .hasIcon=${config.hasIcon ?? false} .fractionDigits=${this.fractionDigits} .maxDigits=${this.maxDigits} .hintedZeros=${config.hintedZeros} .hasSignSpacer=${config.hasSignSpacer ?? false} .spaceReserver=${config.spaceReserver} .off=${config.off ?? false} .offText=${this.offText} .touching=${config.touching ?? false} .hidePhase=${config.hidePhase ?? ReadoutBlockHidePhase.none} .dataQuality=${config.dataQuality} .alert=${config.alert ?? false} .category=${config.category ?? ReadoutAdviceCategory.regular} .active=${config.active ?? false} > ${this.renderForwardedIcon(config.variant)} </obc-readout-block> `; } /** * A cap-height `°` column whose width scales with the value size. Used after * the value (as the value↔unit boundary, via {@link renderValueUnitGap}) and * inside the setpoint / advice blocks. `inherit` makes the glyph take the * surrounding block's colour (setpoint/advice); otherwise it uses the value * colour, optionally `enhanced`. */ renderDegreeGlyph(size, opts = {}) { return html` <span class=${classMap({ \"degree-column\": true, [`degree-${size}`]: true, \"tone-enhanced\": !opts.inherit && Boolean(opts.enhanced), \"degree-inherit\": Boolean(opts.inherit) })} part=\"degree\" > <obc-textbox class=\"degree-glyph\" .size=${size} alignment=\"center\" >°</obc-textbox > </span> `; } /** * The gap rendered between the value digits and the unit. * * - `hasDegree`: a cap-height `°` column whose width scales with the value * size (the `°` replaces the default gap). * - otherwise: the default 2px gap (only when a trailing unit follows). * * `hasDegreeSpacer` deliberately does NOT add anything here — it keeps the 2px * gap and instead widens the unit column via {@link renderDegreeSpacer} (a * spacer AFTER the unit). That way a non-degree row's value digits stay * aligned with degree rows (degree column width = spacer width + 2px gap) * while its unit shifts left. Mirrors Figma `1:2920` (spacer) / `1:2970` * (degree). * * TODO(designer): cross-size alignment is deferred. Degree rows of different * value sizes have different `°` column widths (6/8/12px), so their value digit * edges stagger by `degree-width`. For degree rows of mixed sizes you cannot * align the value digit edges AND keep the unit column aligned — resolving it * needs a design decision (a constant degree reserve, which widens the smaller * rows' `°`, OR pinning the value edge and letting the unit column stagger). */ renderValueUnitGap() { if (!this.hasValue) { return nothing; } if ((this.hasDegree ?? false) && !this.off) { return this.renderDegreeGlyph(this.valueSize, { enhanced: this.rowEnhanced }); } if (this.hasTrailingUnitBox) { return html`<span class=\"value-unit-gap\" aria-hidden=\"true\"></span>`; } return nothing; } /** * A spacer rendered AFTER the unit when `hasDegreeSpacer` is set on a * non-degree row. Its width (`degree-compensation-padding`) = the degree * column width minus the 2px gap, so the row's value digits align with degree * rows in the same column while its unit shifts left. See * {@link renderValueUnitGap}. */ renderDegreeSpacer() { const hasDegree = this.hasDegree ?? false; const hasDegreeSpacer = this.hasDegreeSpacer ?? false; if (hasDegree || !hasDegreeSpacer) { return nothing; } return html`<span class=\"degree-spacer\" part=\"degree-spacer\" aria-hidden=\"true\" ></span>`; } renderTextbox(role, text, reserver, state2) { const weight = role === \"label\" ? this.labelWeight : ObcTextboxFontWeight.regular; const size = role === \"label\" ? this.labelSize : ObcTextboxSize.xs; const box = html` <obc-textbox class=${classMap({ [role]: true, ...this.dataQualityClasses(state2?.dataQuality) })} part=${role} .size=${size} .fontWeight=${weight} alignment=\"left\" > ${text} ${reserver ? html`<span slot=\"length\">${reserver}</span>` : nothing} </obc-textbox> `; return wrapWithAlertFrame(state2?.alert ?? false, box); } renderValueCluster() { const popUpAtSetpoint = this.isPopUp && this.isAtSetpoint && !this.setpointTouching; const setpointHidePhase = resolveSetpointHidePhase( popUpAtSetpoint, this.deferredSetpointHidePhase ); return html` <div class=\"value-cluster\" part=\"value-cluster\"> ${this.hasAdvice ? this.renderBlock({ variant: ReadoutBlockVariant.advice, value: this.advice, valueSize: this.secondarySize, enhanced: false, weight: ObcTextboxFontWeight.regular, hintedZeros: this.adviceOptions?.hintedZeros ?? false, hasSignSpacer: this.adviceOptions?.hasSignSpacer ?? false, spaceReserver: this.adviceOptions?.spaceReserver, hasDegree: this.hasDegree ?? false, dataQuality: this.adviceOptions?.dataQuality, alert: this.adviceOptions?.alert, category: this.adviceOptions?.category, active: this.adviceOptions?.active }) : nothing} ${this.hasSetpoint ? this.renderBlock({ variant: ReadoutBlockVariant.setpoint, value: this.setpoint, valueSize: this.setpointSize, // Value and setpoint share the enhanced colour state (both neutral // or both enhanced); the setpoint is bold only while emphasised. enhanced: this.rowEnhanced, weight: this.setpointWeight, hintedZeros: this.setpointOptions?.hintedZeros ?? false, hasSignSpacer: this.setpointOptions?.hasSignSpacer ?? false, spaceReserver: this.setpointOptions?.spaceReserver, hasDegree: this.hasDegree ?? false, touching: this.setpointTouching, hidePhase: setpointHidePhase, dataQuality: this.setpointOptions?.dataQuality, alert: this.setpointOptions?.alert }) : nothing} ${this.renderValueReading()} </div> `; } /** * The value reading: the value block, its degree column and the trailing unit * grouped in one relatively-positioned wrapper so the value alert frame AND the * value data-quality chip can wrap value + degree + unit together (Figma * 58:10120). * * Moving the degree / unit into this wrapper (the new last child of * `.value-cluster`) preserves every existing gap, so the underlying content * stays column-aligned with or without the frame / chip. Both the value alert * (overlay) and the value data-quality chip are applied here — NOT inside * `obc-readout-block` — so they extend over the unit; setpoint/advice keep their * own block-level frame and chip. The chip uses `outline` (not `border`), so it * never shifts the value's width / column alignment. */ renderValueReading() { return html` <div class=${classMap({ \"value-reading\": true, ...this.dataQualityClasses(this.valueOptions?.dataQuality) })} part=\"value-reading\" > ${this.hasValue ? this.renderBlock({ variant: ReadoutBlockVariant.value, value: this.value, valueType: this.valueType, valueSize: this.valueSize, enhanced: this.rowEnhanced, weight: this.valueWeight, hintedZeros: this.valueOptions?.hintedZeros ?? false, hasSignSpacer: this.valueOptions?.hasSignSpacer ?? false, spaceReserver: this.valueOptions?.spaceReserver, off: this.off, hasIcon: this.valueOptions?.hasIcon ?? false }) : nothing} ${this.renderValueUnitGap()} <div class=\"unit-area\" part=\"unit-area\"> ${this.renderTrailingUnit()} ${this.renderDegreeSpacer()} </div> ${this.renderValueAlertOverlay()} </div> `; } /** * The value alert frame, drawn as a pure overlay around the value reading * (value + degree + unit). It reserves no space — the `obc-alert-frame` sits in * an absolutely-positioned box offset outward (see `.value-alert-overlay` in * the CSS) so its stroke is centred on the 4px/2px padding line and toggling it * never shifts content or row height. Renders only when `valueOptions.alert` is * a config object. */ renderValueAlertOverlay() { const alert = this.valueOptions?.alert; if (typeof alert !== \"object\" || alert === null) { return nothing; } const thickness = alert.thickness ?? ObcAlertFrameThickness.Small; return html` <div class=${classMap({ \"value-alert-overlay\": true, // The outward offset is thickness-dependent (see the CSS): large frames // draw a wider outline, so the box must sit further out to stay centred. \"thickness-large\": thickness === ObcAlertFrameThickness.Large })} aria-hidden=\"true\" > <obc-alert-frame part=\"value-alert-frame\" .type=${alert.type ?? ObcAlertFrameType.Regular} .thickness=${thickness} .status=${alert.status ?? AlertType.Alarm} .mode=${alert.mode ?? ObcAlertFrameMode.ackedActive} .showIcon=${alert.showIcon ?? false} .showAlertCategoryIcon=${alert.showAlertCategoryIcon ?? true} .wrapContent=${false} .fullWidth=${false} ></obc-alert-frame> </div> `; } renderLabelContainer() { const stacking = this.resolvedStacking; const showLeadingUnit = stacking === \"leading-unit\" && Boolean(this.unit); const showLeadingSrc = this.hasLeadingSrc && Boolean(this.src); return html` <div class=\"label-container\" part=\"label-container\"> ${this.hasLeadingIcon ? html`<span class=\"leading-icon\" aria-hidden=\"true\" ><slot name=\"leading-icon\"></slot ></span>` : nothing} <div class=\"label-stack\" part=\"label-stack\"> ${this.label ? this.renderTextbox( \"label\", this.label, this.labelOptions?.spaceReserver ) : nothing} ${showLeadingUnit ? this.renderTextbox( \"unit\", this.unit ?? \"\", this.unitOptions?.spaceReserver ) : nothing} ${showLeadingSrc ? this.renderSourceBlock() : nothing} </div> </div> `; } renderTrailingUnit() { if (!this.hasTrailingUnitBox) { return nothing; } return this.renderTextbox( \"unit\", this.unit ?? \"\", this.unitOptions?.spaceReserver ); } /** * The source text plus its optional state chip and deviation line. The * plain (regular, no-deviation) case renders the bare textbox; a state or a * deviation wraps it in the `.source-block` stack. */ renderSourceBlock() { const state2 = this.srcOptions?.state ?? \"regular\"; const deviation = this.srcOptions?.deviation; const hasDeviation = deviation !== void 0 && Number.isFinite(deviation); const box = this.renderTextbox( \"source\", this.src ?? \"\", this.srcOptions?.spaceReserver, this.srcOptions ); if (state2 === \"regular\" && !hasDeviation) { return box; } return html` <div class=${classMap({ \"source-block\": true, [`source-state-${state2}`]: state2 !== \"regular\" /* regular */ })} part=\"source-block\" > ${box} ${hasDeviation ? html`<span class=\"source-deviation\"> <obi-delta aria-hidden=\"true\"></obi-delta> <obc-textbox class=\"source-deviation-value\" .size=${ObcTextboxSize.xs} .tabularNums=${true} alignment=\"left\" >${deviation}</obc-textbox > </span>` : nothing} </div> `; } renderTrailingSource() { if (this.hasLeadingSrc || !this.src) { return nothing; } return html` <div class=\"divider\" part=\"divider\" aria-hidden=\"true\"></div> ${this.renderSourceBlock()} `; } renderContent() { return html` <div class=\"content\" part=\"content\"> ${this.renderLabelContainer()} <div class=\"value-area\" part=\"value-area\"> ${this.renderValueCluster()} </div> ${this.renderTrailingSource()} </div> `; } willUpdate(changed) { super.willUpdate(changed); assertReadoutValueType(\"obc-readout-list-item\", this.value, this.valueType); assertReadoutFractionDigits(\"obc-readout-list-item\", this.fractionDigits); } updated(changed) { super.updated(changed); const firstUpdate = !this.hasCompletedFirstUpdate; this.hasCompletedFirstUpdate = true; if (!this.isPopUp || this.setpointTouching) { this.clearDeferredSetpointHide(); return; } const shouldHide = this.hasSetpoint && this.isAtSetpoint; if (firstUpdate) { this.deferredSetpointHidePhase = shouldHide ? ReadoutBlockHidePhase.hidden : ReadoutBlockHidePhase.none; return; } if (!shouldHide) { this.clearDeferredSetpointHide(); return; } if (this.deferredSetpointHidePhase !== ReadoutBlockHidePhase.none) { return; } this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hiding; window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = window.setTimeout(() => { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hidden; this.deferredSetpointHideTimer = void 0; }, 100); } clearDeferredSetpointHide() { if (this.deferredSetpointHidePhase !== ReadoutBlockHidePhase.none) { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.none; } window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = void 0; } disconnectedCallback() { window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = void 0; if (this.deferredSetpointHidePhase === ReadoutBlockHidePhase.hiding) { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hidden; } super.disconnectedCallback(); } render() { const clickable = this.resolvedClickable; const dataQuality = this.dataQuality; const classes = classMap({ root: true, [`size-${this.resolvedSize}`]: true, [`stacking-${this.resolvedStacking}`]: true, [`priority-${this.resolvedPriority}`]: true, \"data-low-integrity\": dataQuality === ReadoutListItemDataQuality.lowIntegrity, \"data-invalid\": dataQuality === ReadoutListItemDataQuality.invalid, \"flip-flop\": this.isFlipFlop, clickable: Boolean(clickable), [`border-${clickable ? clickable.border : \"squared\"}`]: Boolean(clickable) }); const surface = html`<div class=\"surface\" part=\"surface\"> ${this.renderContent()} </div>`; const root = clickable ? html`<button class=${classes} part=\"root\" type=\"button\"> ${surface} </button>` : html`<div class=${classes} part=\"root\">${surface}</div>`; const alert = this.alert === true ? {} : this.alert; return wrapWithAlertFrame(alert, root, true); } }"
10163
+ "default": "class extends LitElement { constructor() { super(...arguments); this.hasValue = true; this.value = null; this.valueType = ReadoutValueType.number; this.off = false; this.offText = \"OFF\"; this.hasSetpoint = false; this.hasAdvice = false; this.clickable = false; this.hasLeadingIcon = false; this.hasDegree = false; this.hasDegreeSpacer = false; this.fractionDigits = 0; this.maxDigits = 0; this.alert = false; this.showDebugOverlay = false; this.deferredSetpointHidePhase = ReadoutBlockHidePhase.none; this.hasCompletedFirstUpdate = false; } get resolvedSize() { return this.size ?? ReadoutListItemSize.small; } get resolvedStacking() { return this.stacking ?? \"trailing-unit\"; } /** Both leading-src stackings move the source into the label stack. */ get hasLeadingSrc() { const stacking = this.resolvedStacking; return stacking === \"leading-src\" || stacking === \"leading-src-inline\"; } get resolvedPriority() { return this.priority ?? \"regular\"; } /** * Whether a unit box follows the value: a unit, or a reserver alone — the * blank reserved column keeps a unit-less row aligned with its neighbours. */ get hasTrailingUnitBox() { return this.resolvedStacking !== \"leading-unit\" && (Boolean(this.unit) || Boolean(this.unitOptions?.spaceReserver)); } get resolvedFractionDigits() { return this.fractionDigits ?? 0; } get resolvedMaxDigits() { return resolveReadoutDigitCount(this.maxDigits); } /** * Whether a digit knob failed to arrive (`NaN` / `null` / `undefined`). * The raw knobs are forwarded to the blocks, which render the reading as * the unavailable dash (see `obc-readout-block.digitCountsMissing`); this * getter only keeps the setpoint comparison in agreement with what is * displayed. */ get digitCountsMissing() { return isReadoutDigitCountMissing(this.fractionDigits) || isReadoutDigitCountMissing(this.maxDigits); } get resolvedClickable() { const clickable = this.clickable; if (!clickable) { return false; } if (clickable === true) { return { border: \"squared\" /* squared */ }; } return { border: clickable.border ?? \"squared\" /* squared */ }; } get isAtSetpoint() { if (!this.hasSetpoint) { return false; } if (this.digitCountsMissing) { return false; } return isDisplayedAtSetpoint( resolveReadoutNumericValue(this.value, this.valueType) ?? null, this.setpoint, this.numericFormatOptions(this.resolvedMaxDigits) ); } get resolvedSetpointInteraction() { return this.setpointOptions?.interaction ?? \"always-visible\"; } get isFlipFlop() { return this.resolvedSetpointInteraction === \"flip-flop\"; } get isEqualSize() { return this.resolvedSetpointInteraction === \"equal-size\"; } get isPopUp() { return this.resolvedSetpointInteraction === \"pop-up\"; } get setpointTouching() { return this.setpointOptions?.touching ?? false; } /** * The setpoint is rendered \"emphasised\" (primary size + SemiBold weight) when * it is the focus of attention: while actively adjusting (`touching`), or while * a flip-flop has the value away from the setpoint. Otherwise it is a secondary * (smaller, regular-weight) reference next to the value. */ get isSetpointEmphasized() { if (!this.hasSetpoint) { return false; } if (this.setpointTouching) { return true; } return this.isFlipFlop && !this.isAtSetpoint; } /** * The row's enhanced (in-command) colour state, applied uniformly to BOTH the * value and the setpoint — they are always either both neutral or both enhanced * (never a blue setpoint next to a grey value). Driven by `priority` only; * `valueOptions.weight` changes weight, not colour. */ get rowEnhanced() { return this.resolvedPriority === \"enhanced\"; } /** Primary value-typography size for the current density tier. */ get primarySize() { return readoutPrimarySize(this.resolvedSize); } /** Secondary (de-emphasised) value-typography size for the current density tier. */ get secondarySize() { return readoutSecondarySize(this.resolvedSize); } get valueSize() { return readoutValueSize({ primary: this.primarySize, secondary: this.secondarySize, setpointEmphasized: this.isSetpointEmphasized, equalSize: this.isEqualSize }); } get setpointSize() { return readoutSetpointSize({ primary: this.primarySize, secondary: this.secondarySize, emphasized: this.isSetpointEmphasized, equalSize: this.isEqualSize }); } /** Value font weight passes straight to obc-textbox; regular when unset. Colour is separate. */ get valueWeight() { return this.valueOptions?.weight ?? ObcTextboxFontWeight.regular; } /** Label size: `xs` for dense list rows unless overridden (see {@link ReadoutLabelOptions}). */ get labelSize() { return this.labelOptions?.size ?? ObcTextboxSize.xs; } /** * Label weight: SemiBold only for enhanced (in-command) rows, regular * otherwise — the designer reduced the amount of semibold in the interface * (Figma 6.1 review). */ get labelWeight() { return this.rowEnhanced ? ObcTextboxFontWeight.semibold : ObcTextboxFontWeight.regular; } /** Setpoint is SemiBold only while emphasised, otherwise regular weight. */ get setpointWeight() { return readoutSetpointWeight(this.isSetpointEmphasized); } numericFormatOptions(maxDigits) { return readoutNumericFormatOptions(maxDigits, this.resolvedFractionDigits); } /** classMap fragment for a block / source carrying per-block data quality. */ dataQualityClasses(dataQuality) { return readoutDataQualityClasses(dataQuality); } /** * Forward the matching list-item icon slot into the block's single `icon` * slot. The variant's default marker lives in `obc-readout-block` and shows * when nothing is assigned here (an empty forwarded slot flattens to nothing). */ renderForwardedIcon(variant) { if (variant === ReadoutBlockVariant.setpoint) { return html`<slot name=\"setpoint-icon\" slot=\"icon\"></slot>`; } if (variant === ReadoutBlockVariant.advice) { return html`<slot name=\"advice-icon\" slot=\"icon\"></slot>`; } return html`<slot name=\"value-icon\" slot=\"icon\"></slot>`; } renderBlock(config) { return html` <obc-readout-block exportparts=\"block, block-content, block-text, block-icon, degree\" .variant=${config.variant} .value=${config.value ?? null} .valueType=${config.valueType ?? ReadoutValueType.number} .size=${this.resolvedSize} .valueSize=${config.valueSize} .enhanced=${config.enhanced} .weight=${config.weight} .hasDegree=${config.hasDegree ?? false} .hasIcon=${config.hasIcon ?? false} .fractionDigits=${this.fractionDigits} .maxDigits=${this.maxDigits} .hintedZeros=${config.hintedZeros} .hasSignSpacer=${config.hasSignSpacer ?? false} .spaceReserver=${config.spaceReserver} .off=${config.off ?? false} .offText=${this.offText} .touching=${config.touching ?? false} .hidePhase=${config.hidePhase ?? ReadoutBlockHidePhase.none} .dataQuality=${config.dataQuality} .alert=${config.alert ?? false} .category=${config.category ?? ReadoutAdviceCategory.regular} .active=${config.active ?? false} > ${this.renderForwardedIcon(config.variant)} </obc-readout-block> `; } /** * A cap-height `°` column whose width scales with the value size. Used after * the value (as the value↔unit boundary, via {@link renderValueUnitGap}) and * inside the setpoint / advice blocks. `inherit` makes the glyph take the * surrounding block's colour (setpoint/advice); otherwise it uses the value * colour, optionally `enhanced`. */ renderDegreeGlyph(size, opts = {}) { return html` <span class=${classMap({ \"degree-column\": true, [`degree-${size}`]: true, \"tone-enhanced\": !opts.inherit && Boolean(opts.enhanced), \"degree-inherit\": Boolean(opts.inherit) })} part=\"degree\" > <obc-textbox class=\"degree-glyph\" .size=${size} alignment=\"center\" >°</obc-textbox > </span> `; } /** * The gap rendered between the value digits and the unit. * * - `hasDegree`: a cap-height `°` column whose width scales with the value * size (the `°` replaces the default gap). * - otherwise: the default 2px gap (only when a trailing unit follows). * * `hasDegreeSpacer` deliberately does NOT add anything here — it keeps the 2px * gap and instead widens the unit column via {@link renderDegreeSpacer} (a * spacer AFTER the unit). That way a non-degree row's value digits stay * aligned with degree rows (degree column width = spacer width + 2px gap) * while its unit shifts left. Mirrors Figma `1:2920` (spacer) / `1:2970` * (degree). * * TODO(designer): cross-size alignment is deferred. Degree rows of different * value sizes have different `°` column widths (6/8/12px), so their value digit * edges stagger by `degree-width`. For degree rows of mixed sizes you cannot * align the value digit edges AND keep the unit column aligned — resolving it * needs a design decision (a constant degree reserve, which widens the smaller * rows' `°`, OR pinning the value edge and letting the unit column stagger). */ renderValueUnitGap() { if (!this.hasValue) { return nothing; } if ((this.hasDegree ?? false) && !this.off) { return this.renderDegreeGlyph(this.valueSize, { enhanced: this.rowEnhanced }); } if (this.hasTrailingUnitBox) { return html`<span class=\"value-unit-gap\" aria-hidden=\"true\"></span>`; } return nothing; } /** * A spacer rendered AFTER the unit when `hasDegreeSpacer` is set on a * non-degree row. Its width (`degree-compensation-padding`) = the degree * column width minus the 2px gap, so the row's value digits align with degree * rows in the same column while its unit shifts left. See * {@link renderValueUnitGap}. */ renderDegreeSpacer() { const hasDegree = this.hasDegree ?? false; const hasDegreeSpacer = this.hasDegreeSpacer ?? false; if (hasDegree || !hasDegreeSpacer) { return nothing; } return html`<span class=\"degree-spacer\" part=\"degree-spacer\" aria-hidden=\"true\" ></span>`; } renderTextbox(role, text, reserver, state2) { const weight = role === \"label\" ? this.labelWeight : ObcTextboxFontWeight.regular; const size = role === \"label\" ? this.labelSize : ObcTextboxSize.xs; const box = html` <obc-textbox class=${classMap({ [role]: true, ...this.dataQualityClasses(state2?.dataQuality) })} part=${role} .size=${size} .fontWeight=${weight} alignment=\"left\" > ${text} ${reserver ? html`<span slot=\"length\">${reserver}</span>` : nothing} </obc-textbox> `; return wrapWithAlertFrame(state2?.alert ?? false, box); } renderValueCluster() { const popUpAtSetpoint = this.isPopUp && this.isAtSetpoint && !this.setpointTouching; const setpointHidePhase = resolveSetpointHidePhase( popUpAtSetpoint, this.deferredSetpointHidePhase ); return html` <div class=\"value-cluster\" part=\"value-cluster\"> ${this.hasAdvice ? this.renderBlock({ variant: ReadoutBlockVariant.advice, value: this.advice, valueSize: this.secondarySize, enhanced: false, weight: ObcTextboxFontWeight.regular, hintedZeros: this.adviceOptions?.hintedZeros ?? false, hasSignSpacer: this.adviceOptions?.hasSignSpacer ?? false, spaceReserver: this.adviceOptions?.spaceReserver, hasDegree: this.hasDegree ?? false, dataQuality: this.adviceOptions?.dataQuality, alert: this.adviceOptions?.alert, category: this.adviceOptions?.category, active: this.adviceOptions?.active }) : nothing} ${this.hasSetpoint ? this.renderBlock({ variant: ReadoutBlockVariant.setpoint, value: this.setpoint, valueSize: this.setpointSize, // Value and setpoint share the enhanced colour state (both neutral // or both enhanced); the setpoint is bold only while emphasised. enhanced: this.rowEnhanced, weight: this.setpointWeight, hintedZeros: this.setpointOptions?.hintedZeros ?? false, hasSignSpacer: this.setpointOptions?.hasSignSpacer ?? false, spaceReserver: this.setpointOptions?.spaceReserver, hasDegree: this.hasDegree ?? false, touching: this.setpointTouching, hidePhase: setpointHidePhase, dataQuality: this.setpointOptions?.dataQuality, alert: this.setpointOptions?.alert }) : nothing} ${this.renderValueReading()} </div> `; } /** * The value reading: the value block, its degree column and the trailing unit * grouped in one relatively-positioned wrapper so the value alert frame AND the * value data-quality chip can wrap value + degree + unit together (Figma * 58:10120). * * Moving the degree / unit into this wrapper (the new last child of * `.value-cluster`) preserves every existing gap, so the underlying content * stays column-aligned with or without the frame / chip. Both the value alert * (overlay) and the value data-quality chip are applied here — NOT inside * `obc-readout-block` — so they extend over the unit; setpoint/advice keep their * own block-level frame and chip. The chip uses `outline` (not `border`), so it * never shifts the value's width / column alignment. */ renderValueReading() { return html` <div class=${classMap({ \"value-reading\": true, ...this.dataQualityClasses(this.valueOptions?.dataQuality) })} part=\"value-reading\" > ${this.hasValue ? this.renderBlock({ variant: ReadoutBlockVariant.value, value: this.value, valueType: this.valueType, valueSize: this.valueSize, enhanced: this.rowEnhanced, weight: this.valueWeight, hintedZeros: this.valueOptions?.hintedZeros ?? false, hasSignSpacer: this.valueOptions?.hasSignSpacer ?? false, spaceReserver: this.valueOptions?.spaceReserver, off: this.off, hasIcon: this.valueOptions?.hasIcon ?? false }) : nothing} ${this.renderValueUnitGap()} <div class=\"unit-area\" part=\"unit-area\"> ${this.renderTrailingUnit()} ${this.renderDegreeSpacer()} </div> ${this.renderValueAlertOverlay()} </div> `; } /** * The value alert frame, drawn as a pure overlay around the value reading * (value + degree + unit). It reserves no space — the `obc-alert-frame` sits in * an absolutely-positioned box offset outward (see `.value-alert-overlay` in * the CSS) so its stroke is centred on the 4px/2px padding line and toggling it * never shifts content or row height. Renders only when `valueOptions.alert` is * a config object. */ renderValueAlertOverlay() { const alert = this.valueOptions?.alert; if (typeof alert !== \"object\" || alert === null) { return nothing; } const thickness = alert.thickness ?? ObcAlertFrameThickness.Small; return html` <div class=${classMap({ \"value-alert-overlay\": true, // The outward offset is thickness-dependent (see the CSS): large frames // draw a wider outline, so the box must sit further out to stay centred. \"thickness-large\": thickness === ObcAlertFrameThickness.Large })} aria-hidden=\"true\" > <obc-alert-frame part=\"value-alert-frame\" .type=${alert.type ?? ObcAlertFrameType.Regular} .thickness=${thickness} .status=${alert.status ?? AlertType.Alarm} .mode=${alert.mode ?? ObcAlertFrameMode.ackedActive} .flashingSpeed=${alert.flashingSpeed ?? FlashingSpeed.Default} .showIcon=${alert.showIcon ?? false} .showAlertCategoryIcon=${alert.showAlertCategoryIcon ?? true} .wrapContent=${false} .fullWidth=${false} ></obc-alert-frame> </div> `; } renderLabelContainer() { const stacking = this.resolvedStacking; const showLeadingUnit = stacking === \"leading-unit\" && Boolean(this.unit); const showLeadingSrc = this.hasLeadingSrc && Boolean(this.src); return html` <div class=\"label-container\" part=\"label-container\"> ${this.hasLeadingIcon ? html`<span class=\"leading-icon\" aria-hidden=\"true\" ><slot name=\"leading-icon\"></slot ></span>` : nothing} <div class=\"label-stack\" part=\"label-stack\"> ${this.label ? this.renderTextbox( \"label\", this.label, this.labelOptions?.spaceReserver ) : nothing} ${showLeadingUnit ? this.renderTextbox( \"unit\", this.unit ?? \"\", this.unitOptions?.spaceReserver ) : nothing} ${showLeadingSrc ? this.renderSourceBlock() : nothing} </div> </div> `; } renderTrailingUnit() { if (!this.hasTrailingUnitBox) { return nothing; } return this.renderTextbox( \"unit\", this.unit ?? \"\", this.unitOptions?.spaceReserver ); } /** * The source text plus its optional state chip and deviation line. The * plain (regular, no-deviation) case renders the bare textbox; a state or a * deviation wraps it in the `.source-block` stack. */ renderSourceBlock() { const state2 = this.srcOptions?.state ?? \"regular\"; const deviation = this.srcOptions?.deviation; const hasDeviation = deviation !== void 0 && Number.isFinite(deviation); const box = this.renderTextbox( \"source\", this.src ?? \"\", this.srcOptions?.spaceReserver, this.srcOptions ); if (state2 === \"regular\" && !hasDeviation) { return box; } return html` <div class=${classMap({ \"source-block\": true, [`source-state-${state2}`]: state2 !== \"regular\" /* regular */ })} part=\"source-block\" > ${box} ${hasDeviation ? html`<span class=\"source-deviation\"> <obi-delta aria-hidden=\"true\"></obi-delta> <obc-textbox class=\"source-deviation-value\" .size=${ObcTextboxSize.xs} .tabularNums=${true} alignment=\"left\" >${deviation}</obc-textbox > </span>` : nothing} </div> `; } renderTrailingSource() { if (this.hasLeadingSrc || !this.src) { return nothing; } return html` <div class=\"divider\" part=\"divider\" aria-hidden=\"true\"></div> ${this.renderSourceBlock()} `; } renderContent() { return html` <div class=\"content\" part=\"content\"> ${this.renderLabelContainer()} <div class=\"value-area\" part=\"value-area\"> ${this.renderValueCluster()} </div> ${this.renderTrailingSource()} </div> `; } willUpdate(changed) { super.willUpdate(changed); assertReadoutValueType(\"obc-readout-list-item\", this.value, this.valueType); assertReadoutFractionDigits(\"obc-readout-list-item\", this.fractionDigits); } updated(changed) { super.updated(changed); const firstUpdate = !this.hasCompletedFirstUpdate; this.hasCompletedFirstUpdate = true; if (!this.isPopUp || this.setpointTouching) { this.clearDeferredSetpointHide(); return; } const shouldHide = this.hasSetpoint && this.isAtSetpoint; if (firstUpdate) { this.deferredSetpointHidePhase = shouldHide ? ReadoutBlockHidePhase.hidden : ReadoutBlockHidePhase.none; return; } if (!shouldHide) { this.clearDeferredSetpointHide(); return; } if (this.deferredSetpointHidePhase !== ReadoutBlockHidePhase.none) { return; } this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hiding; window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = window.setTimeout(() => { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hidden; this.deferredSetpointHideTimer = void 0; }, 100); } clearDeferredSetpointHide() { if (this.deferredSetpointHidePhase !== ReadoutBlockHidePhase.none) { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.none; } window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = void 0; } disconnectedCallback() { window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = void 0; if (this.deferredSetpointHidePhase === ReadoutBlockHidePhase.hiding) { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hidden; } super.disconnectedCallback(); } render() { const clickable = this.resolvedClickable; const dataQuality = this.dataQuality; const classes = classMap({ root: true, [`size-${this.resolvedSize}`]: true, [`stacking-${this.resolvedStacking}`]: true, [`priority-${this.resolvedPriority}`]: true, \"data-low-integrity\": dataQuality === ReadoutListItemDataQuality.lowIntegrity, \"data-invalid\": dataQuality === ReadoutListItemDataQuality.invalid, \"flip-flop\": this.isFlipFlop, clickable: Boolean(clickable), [`border-${clickable ? clickable.border : \"squared\"}`]: Boolean(clickable) }); const surface = html`<div class=\"surface\" part=\"surface\"> ${this.renderContent()} </div>`; const root = clickable ? html`<button class=${classes} part=\"root\" type=\"button\"> ${surface} </button>` : html`<div class=${classes} part=\"root\">${surface}</div>`; const alert = this.alert === true ? {} : this.alert; return wrapWithAlertFrame(alert, root, true); } }"
10144
10164
  }
10145
10165
  ],
10146
10166
  "exports": [
@@ -10262,7 +10282,7 @@
10262
10282
  {
10263
10283
  "kind": "variable",
10264
10284
  "name": "ObcReadout",
10265
- "default": "class extends LitElement { constructor() { super(...arguments); this.hasValue = true; this.value = null; this.valueType = ReadoutValueType.number; this.off = false; this.offText = \"OFF\"; this.hasSetpoint = false; this.hasAdvice = false; this.hasDegree = false; this.hasDegreeSpacer = false; this.hasLeadingIcon = false; this.fractionDigits = 0; this.maxDigits = 0; this.alert = false; this.showDebugOverlay = false; this.deferredSetpointHidePhase = ReadoutBlockHidePhase.none; this.hasCompletedFirstUpdate = false; this.hasWarnedHorizontalSize = false; this.sourcePickerContentVisible = false; this.sourcePickerOptions = []; this.onWindowPointerDown = (event) => { if (!this.sourcePickerContentVisible) { return; } if (event.composedPath().includes(this)) { return; } this.sourcePickerContentVisible = false; }; this.syncSourcePickerOptions = () => { this.sourcePickerOptions = this.getSourcePickerNavigationItems().map( (item, index) => { const { itemLabel, itemValue } = this.getSourcePickerItemInfo( item, index ); return { value: itemValue, label: itemLabel, icon: this.createSourcePickerOptionIcon(item) }; } ); }; } get resolvedSize() { if (this.isHorizontal) { return ReadoutSize.large; } return this.size ?? ReadoutSize.small; } get resolvedPriority() { return this.priority ?? ReadoutPriority.regular; } get resolvedDirection() { return this.direction ?? \"vertical\"; } get resolvedStacking() { return this.stacking ?? \"inline\"; } get resolvedAlignment() { return this.alignment ?? \"vertical\"; } get isHorizontal() { return this.resolvedDirection === \"horizontal\"; } get resolvedFractionDigits() { return this.fractionDigits ?? 0; } get resolvedMaxDigits() { return resolveReadoutDigitCount(this.maxDigits); } /** * Whether a digit knob failed to arrive (`NaN` / `null` / `undefined`). * The raw knobs are forwarded to the blocks, which render the reading as * the unavailable dash (see `obc-readout-block.digitCountsMissing`); this * getter only keeps the setpoint comparison in agreement with what is * displayed. */ get digitCountsMissing() { return isReadoutDigitCountMissing(this.fractionDigits) || isReadoutDigitCountMissing(this.maxDigits); } get hasSrc() { return this.src !== void 0 && this.src.trim() !== \"\"; } get resolvedSourceInteraction() { return this.srcOptions?.interaction ?? \"none\"; } get isAtSetpoint() { if (!this.hasSetpoint) { return false; } if (this.digitCountsMissing) { return false; } return isDisplayedAtSetpoint( resolveReadoutNumericValue(this.value, this.valueType) ?? null, this.setpoint, this.numericFormatOptions(this.resolvedMaxDigits) ); } get resolvedSetpointInteraction() { return this.setpointOptions?.interaction ?? ReadoutSetpointInteraction.alwaysVisible; } get isFlipFlop() { return this.resolvedSetpointInteraction === ReadoutSetpointInteraction.flipFlop; } get isEqualSize() { return this.resolvedSetpointInteraction === ReadoutSetpointInteraction.equalSize; } get isPopUp() { return this.resolvedSetpointInteraction === ReadoutSetpointInteraction.popUp; } get setpointTouching() { return this.setpointOptions?.touching ?? false; } /** * Whether the setpoint interaction re-sizes or hides blocks at runtime — * flip-flop (emphasis swap) and pop-up (touch swap + fade-out) — the modes * that need the width/height reservation so the readout never shifts (see * {@link renderSetpointWidthReserve}). */ get hasDynamicSetpoint() { return this.hasSetpoint && (this.isFlipFlop || this.isPopUp); } /** * The setpoint is rendered \"emphasised\" (primary size + SemiBold weight) when * it is the focus of attention: while actively adjusting (`touching`), or while * a flip-flop has the value away from the setpoint. Otherwise it is a secondary * (smaller, regular-weight) reference next to the value. */ get isSetpointEmphasized() { if (!this.hasSetpoint) { return false; } if (this.setpointTouching) { return true; } return this.isFlipFlop && !this.isAtSetpoint; } /** * The readout's enhanced (in-command) colour state, applied uniformly to BOTH * the value and the setpoint — they are always either both neutral or both * enhanced (never a blue setpoint next to a grey value). Driven by `priority` * only; `valueOptions.weight` changes weight, not colour. */ get rowEnhanced() { return this.resolvedPriority === ReadoutPriority.enhanced; } /** Primary value-typography size for the current density tier. */ get primarySize() { return readoutPrimarySize(this.resolvedSize); } /** Secondary (de-emphasised) value-typography size for the current density tier. */ get secondarySize() { return readoutSecondarySize(this.resolvedSize); } get valueSize() { return readoutValueSize({ primary: this.primarySize, secondary: this.secondarySize, setpointEmphasized: this.isSetpointEmphasized, equalSize: this.isEqualSize }); } get setpointSize() { return readoutSetpointSize({ primary: this.primarySize, secondary: this.secondarySize, emphasized: this.isSetpointEmphasized, equalSize: this.isEqualSize }); } /** Value font weight passes straight to obc-textbox; regular when unset. Colour is separate. */ get valueWeight() { return this.valueOptions?.weight ?? ObcTextboxFontWeight.regular; } /** * Label size. The Figma 6.1 title block defaults large readouts to `s` — a * scannable label should not sit at the smallest permitted size — while * small/medium tiers stay `xs`; `labelOptions.size` overrides either way * (per the design team, for context / density / secondary instruments). * TODO(designer): the medium-tier default is unverified in the new sheets * (only XS and S exist on the title's size axis). */ get labelSize() { return this.labelOptions?.size ?? (this.resolvedSize === ReadoutSize.large ? ObcTextboxSize.s : ObcTextboxSize.xs); } /** * Label weight: SemiBold only for enhanced (in-command) readouts, regular * otherwise — the designer reduced the amount of semibold in the interface * (Figma 6.1 review). */ get labelWeight() { return this.rowEnhanced ? ObcTextboxFontWeight.semibold : ObcTextboxFontWeight.regular; } /** Setpoint is SemiBold only while emphasised, otherwise regular weight. */ get setpointWeight() { return readoutSetpointWeight(this.isSetpointEmphasized); } numericFormatOptions(maxDigits) { return readoutNumericFormatOptions(maxDigits, this.resolvedFractionDigits); } /** classMap fragment for a block / source carrying per-block data quality. */ dataQualityClasses(dataQuality) { return readoutDataQualityClasses(dataQuality); } /** * Forward the matching readout icon slot into the block's single `icon` * slot. The variant's default marker lives in `obc-readout-block` and shows * when nothing is assigned here (an empty forwarded slot flattens to nothing). */ renderForwardedIcon(variant) { if (variant === ReadoutBlockVariant.setpoint) { return html`<slot name=\"setpoint-icon\" slot=\"icon\"></slot>`; } if (variant === ReadoutBlockVariant.advice) { return html`<slot name=\"advice-icon\" slot=\"icon\"></slot>`; } return html`<slot name=\"value-icon\" slot=\"icon\"></slot>`; } renderBlock(config) { return html` <obc-readout-block exportparts=\"block, block-content, block-text, block-icon, degree\" .variant=${config.variant} .value=${config.value ?? null} .valueType=${config.valueType ?? ReadoutValueType.number} .size=${this.resolvedSize} .valueSize=${config.valueSize} .enhanced=${config.enhanced} .weight=${config.weight} .hasDegree=${config.hasDegree ?? false} .hasIcon=${config.hasIcon ?? false} .fractionDigits=${this.fractionDigits} .maxDigits=${this.maxDigits} .hintedZeros=${config.hintedZeros} .hasSignSpacer=${config.hasSignSpacer ?? false} .spaceReserver=${config.spaceReserver} .off=${config.off ?? false} .offText=${this.offText} .touching=${config.touching ?? false} .hidePhase=${config.hidePhase ?? ReadoutBlockHidePhase.none} .dataQuality=${config.dataQuality} .alert=${config.alert ?? false} .category=${config.category ?? ReadoutAdviceCategory.regular} .active=${config.active ?? false} > ${config.ghost ? nothing : this.renderForwardedIcon(config.variant)} </obc-readout-block> `; } /** * A cap-height `°` column whose width scales with the value size, rendered * after the value block. Setpoint / advice blocks render their own degree via * `obc-readout-block`. */ renderDegreeGlyph(size) { return html` <span class=${classMap({ \"degree-column\": true, [`degree-${size}`]: true, \"tone-enhanced\": this.rowEnhanced })} part=\"degree\" > <obc-textbox class=\"degree-glyph\" .size=${size} alignment=\"center\" >°</obc-textbox > </span> `; } /** * The column rendered after the value digits: * * - `hasDegree`: a cap-height `°` column whose width scales with the value size. * - `hasDegreeSpacer` (non-degree readouts): an invisible reserve of the same * width, so this readout's value digits stay aligned with degree readouts * in the same column. */ renderDegreeColumn(size = this.valueSize) { if ((this.hasDegree ?? false) && !this.off) { return this.renderDegreeGlyph(size); } if (this.hasDegreeSpacer ?? false) { return html`<span class=\"degree-spacer\" part=\"degree-spacer\" aria-hidden=\"true\" ></span>`; } return nothing; } renderTextbox(role, text, reserver, blockState) { const weight = role === \"label\" ? this.labelWeight : ObcTextboxFontWeight.regular; const size = role === \"label\" ? this.labelSize : ObcTextboxSize.xs; const box = html` <obc-textbox class=${classMap({ [role]: true, ...this.dataQualityClasses(blockState?.dataQuality) })} part=${role} .size=${size} .fontWeight=${weight} alignment=\"left\" > ${text} ${reserver ? html`<span slot=\"length\">${reserver}</span>` : nothing} </obc-textbox> `; return wrapWithAlertFrame(blockState?.alert ?? false, box); } get setpointHidePhase() { const popUpAtSetpoint = this.isPopUp && this.isAtSetpoint && !this.setpointTouching; return resolveSetpointHidePhase( popUpAtSetpoint, this.deferredSetpointHidePhase ); } // `hasDegree` is deliberately NOT forwarded to the advice / setpoint blocks: // the unit is written once per readout and the degree follows the same rule // (Figma 6.1). `obc-readout-list-item` keeps its own per-row convention. renderAdviceBlock() { return this.renderBlock({ variant: ReadoutBlockVariant.advice, value: this.advice, valueSize: this.secondarySize, enhanced: false, weight: ObcTextboxFontWeight.regular, hintedZeros: this.adviceOptions?.hintedZeros ?? false, hasSignSpacer: this.adviceOptions?.hasSignSpacer ?? false, spaceReserver: this.adviceOptions?.spaceReserver, dataQuality: this.adviceOptions?.dataQuality, alert: this.adviceOptions?.alert, category: this.adviceOptions?.category, active: this.adviceOptions?.active }); } renderSetpointBlock() { return this.renderBlock({ variant: ReadoutBlockVariant.setpoint, value: this.setpoint, valueSize: this.setpointSize, // Value and setpoint share the enhanced colour state (both neutral // or both enhanced); the setpoint is bold only while emphasised. enhanced: this.rowEnhanced, weight: this.setpointWeight, hintedZeros: this.setpointOptions?.hintedZeros ?? false, hasSignSpacer: this.setpointOptions?.hasSignSpacer ?? false, spaceReserver: this.setpointOptions?.spaceReserver, touching: this.setpointTouching, hidePhase: this.setpointHidePhase, dataQuality: this.setpointOptions?.dataQuality, alert: this.setpointOptions?.alert }); } /** * The value reading: the value block and its degree column (or spacer) * grouped in one relatively-positioned wrapper so the value alert frame AND * the value data-quality chip can wrap both together. Unlike * `obc-readout-list-item` the unit is not part of the reading — it lives in * the label/unit meta zone. */ renderValueReading() { return html` <div class=${classMap({ \"value-reading\": true, ...this.dataQualityClasses(this.valueOptions?.dataQuality) })} part=\"value-reading\" > ${this.renderBlock({ variant: ReadoutBlockVariant.value, value: this.value, valueType: this.valueType, valueSize: this.valueSize, enhanced: this.rowEnhanced, weight: this.valueWeight, hintedZeros: this.valueOptions?.hintedZeros ?? false, hasSignSpacer: this.valueOptions?.hasSignSpacer ?? false, spaceReserver: this.valueOptions?.spaceReserver, off: this.off, hasIcon: this.valueOptions?.hasIcon ?? false })} ${this.renderDegreeColumn()} ${this.renderValueAlertOverlay()} </div> `; } /** * The value alert frame, drawn as a pure overlay around the value reading * (value + degree). It reserves no space — the `obc-alert-frame` sits in an * absolutely-positioned box offset outward (see `.value-alert-overlay` in the * CSS) so its stroke is centred on the 4px/2px padding line and toggling it * never shifts content. Renders only when `valueOptions.alert` is a config * object. */ renderValueAlertOverlay() { const alert = this.valueOptions?.alert; if (typeof alert !== \"object\" || alert === null) { return nothing; } const thickness = alert.thickness ?? ObcAlertFrameThickness.Small; return html` <div class=${classMap({ \"value-alert-overlay\": true, // The outward offset is thickness-dependent (see the CSS): large frames // draw a wider outline, so the box must sit further out to stay centred. \"thickness-large\": thickness === ObcAlertFrameThickness.Large })} aria-hidden=\"true\" > <obc-alert-frame part=\"value-alert-frame\" .type=${alert.type ?? ObcAlertFrameType.Regular} .thickness=${thickness} .status=${alert.status ?? AlertType.Alarm} .mode=${alert.mode ?? ObcAlertFrameMode.ackedActive} .showIcon=${alert.showIcon ?? false} .showAlertCategoryIcon=${alert.showAlertCategoryIcon ?? true} .wrapContent=${false} .fullWidth=${false} ></obc-alert-frame> </div> `; } /** The label/unit meta zone; `stacking` picks the inline or stacked arrangement. */ renderMetaZone() { if (!this.label && !this.unit) { return nothing; } const stacked = this.isHorizontal || this.resolvedStacking === \"stacked\"; return html` <div class=${classMap({ meta: true, \"meta-inline\": !stacked, \"meta-stacked\": stacked, \"label-only\": !!this.label && !this.unit, \"unit-only\": !!this.unit && !this.label })} part=\"meta-wrapper\" > ${this.hasLeadingIcon ? html`<span class=\"leading-icon\" aria-hidden=\"true\" ><slot name=\"leading-icon\"></slot ></span>` : nothing} <div class=\"meta-labels\" part=\"meta-labels\"> ${this.label ? this.renderTextbox( \"label\", this.label, this.labelOptions?.spaceReserver ) : nothing} ${this.unit ? this.renderTextbox( \"unit\", this.unit, this.unitOptions?.spaceReserver ) : nothing} </div> </div> `; } renderSourceContent() { const interaction = this.resolvedSourceInteraction; const src = this.src ?? \"\"; const buttonClasses = classMap({ \"source-button\": true, ...this.dataQualityClasses(this.srcOptions?.dataQuality) }); if (interaction === \"picker\") { const button = html` <obc-button class=${buttonClasses} variant=\"flat\" .fullWidth=${false} showTrailingIcon @click=${() => { this.sourcePickerContentVisible = !this.sourcePickerContentVisible; }} > <obi-drop-down-google slot=\"trailing-icon\"></obi-drop-down-google> ${src} </obc-button> `; return wrapWithAlertFrame(this.srcOptions?.alert ?? false, button); } if (interaction === \"flyout\") { const button = html` <obc-button class=${buttonClasses} variant=\"flat\" .fullWidth=${false} showTrailingIcon @click=${() => { this.dispatchEvent( new CustomEvent(\"source-flyout-click\", { bubbles: true, composed: true, detail: { src } }) ); }} > <obi-chevron-right-google slot=\"trailing-icon\" ></obi-chevron-right-google> ${src} </obc-button> `; return wrapWithAlertFrame(this.srcOptions?.alert ?? false, button); } return this.renderSourceBlock(src); } /** * The plain (non-interactive) source: the textbox plus its optional state * chip and deviation line. The regular no-deviation case renders the bare * textbox; a state or deviation wraps it in the `.source-block` stack. The * picker / flyout button variants do not carry state / deviation yet — * TODO(designer): undefined in the 6.1 sheets. */ renderSourceBlock(src) { const state2 = this.srcOptions?.state ?? ReadoutSourceState.regular; const deviation = this.srcOptions?.deviation; const hasDeviation = deviation !== void 0 && Number.isFinite(deviation); const box = this.renderTextbox( \"source\", src, this.srcOptions?.spaceReserver, this.srcOptions ); if (state2 === ReadoutSourceState.regular && !hasDeviation) { return box; } return html` <div class=${classMap({ \"source-block\": true, [`source-state-${state2}`]: state2 !== ReadoutSourceState.regular })} part=\"source-block\" > ${box} ${hasDeviation ? html`<span class=\"source-deviation\"> <obi-delta aria-hidden=\"true\"></obi-delta> <obc-textbox class=\"source-deviation-value\" .size=${ObcTextboxSize.xs} .tabularNums=${true} alignment=\"left\" >${deviation}</obc-textbox > </span>` : nothing} </div> `; } /** * The source row (vertical) / segment (horizontal). Only the horizontal * direction draws an auto divider before the source — the Figma 6.1 review * (node 95:24490 \"remove dividers\", 95:26891 \"Source-divider are there\") * keeps the vertical source row divider-free while the horizontal flow * separates the source with a vertical rule. */ renderSource() { if (!this.hasSrc) { return nothing; } return html` ${this.isHorizontal ? html`<div class=\"divider divider-vertical\" part=\"divider\" aria-hidden=\"true\" ></div>` : nothing} <div class=\"source-row\" part=\"source-wrapper\"> ${this.renderSourceContent()} </div> `; } // ---------- Source picker (context menu built from src-picker-content) ---------- getSourcePickerNavigationItems() { const assignedElements = this.sourcePickerSlot?.assignedElements({ flatten: true }) ?? []; return assignedElements.flatMap((element) => { if (!(element instanceof HTMLElement)) { return []; } if (element.localName === \"obc-navigation-item\") { return [element]; } return Array.from( element.querySelectorAll(\"obc-navigation-item\") ); }); } createSourcePickerOptionIcon(element) { const iconElement = element.querySelector('[slot=\"icon\"]'); if (!(iconElement instanceof HTMLElement)) { return void 0; } return html`${iconElement.cloneNode(true)}`; } getSourcePickerItemInfo(item, index) { const itemWithValues = item; const itemLabel = itemWithValues.label || item.getAttribute(\"label\") || \"\"; const itemValue = item.getAttribute(\"data-value\") || itemWithValues.value || item.getAttribute(\"value\") || itemLabel || `source-option-${index}`; return { itemLabel, itemValue }; } findSourcePickerOptionElement(value) { const navigationItems = this.getSourcePickerNavigationItems(); for (const [index, item] of navigationItems.entries()) { const { itemValue } = this.getSourcePickerItemInfo(item, index); if (itemValue === value) { return item; } } return void 0; } handleSourcePickerItemClick(event) { this.dispatchEvent( new CustomEvent(\"source-change\", { bubbles: true, composed: true, detail: { value: event.detail.value, label: event.detail.option?.label } }) ); this.findSourcePickerOptionElement(event.detail.value)?.click(); this.sourcePickerContentVisible = false; } /** * The context menu highlights options by their VALUE, while `src` holds the * displayed source text — which may be an option's label when the two * differ (e.g. `data-value=\"gps-1\"` with label \"GPS 1\"). Resolve the current * source to the matching option's value (by value or label) so the open * picker always highlights the active source. */ get selectedSourceValues() { if (!this.src) { return []; } const match = this.sourcePickerOptions.find( (option) => option.value === this.src || option.label === this.src ); return [match?.value ?? this.src]; } renderSourcePickerContent() { if (this.resolvedSourceInteraction !== \"picker\" || !this.sourcePickerContentVisible) { return nothing; } return html` <obc-context-menu-input .type=${ContextMenuType.Regular} .options=${this.sourcePickerOptions} .selectedValues=${this.selectedSourceValues} class=\"source-picker-content\" @item-click=${this.handleSourcePickerItemClick} @close=${() => { this.sourcePickerContentVisible = false; }} ></obc-context-menu-input> `; } renderSourcePickerSlot() { return html` <slot name=\"src-picker-content\" hidden @slotchange=${this.syncSourcePickerOptions} ></slot> `; } // ---------- Layouts ---------- /** * An invisible primary-size duplicate of a dynamic setpoint row's content, * stacked under the visible block in the same grid cell purely to reserve * width. With both rows permanently holding their emphasised width, the * widest row — and therefore the readout's total width — stays constant * while a flip-flop swaps the value/setpoint sizes, while a touch swap * runs, and while a pop-up setpoint fades out, so the meta zone and source * never shift. This is the vertical analogue of the horizontal arrangement, * where the swapped pair sits on one row and its summed width is constant * by construction (the designer's flip-flop invariant). Only the vertical * direction needs it; `always-visible` layouts are untouched. */ renderSetpointWidthReserve(kind) { if (!this.hasDynamicSetpoint || this.isHorizontal) { return nothing; } const content = kind === \"setpoint\" ? this.renderBlock({ variant: ReadoutBlockVariant.setpoint, value: this.setpoint, valueSize: this.primarySize, enhanced: false, // The emphasised setpoint is SemiBold — reserve its widest form. // No degree: the visible setpoint block carries none (the degree // renders on the actual value only), so the ghost mirrors it. weight: ObcTextboxFontWeight.semibold, hintedZeros: this.setpointOptions?.hintedZeros ?? false, hasSignSpacer: this.setpointOptions?.hasSignSpacer ?? false, spaceReserver: this.setpointOptions?.spaceReserver, alert: this.setpointOptions?.alert, ghost: true }) : html`${this.renderBlock({ variant: ReadoutBlockVariant.value, value: this.value, valueType: this.valueType, valueSize: this.primarySize, enhanced: false, weight: this.valueWeight, hintedZeros: this.valueOptions?.hintedZeros ?? false, hasSignSpacer: this.valueOptions?.hasSignSpacer ?? false, spaceReserver: this.valueOptions?.spaceReserver, off: this.off, hasIcon: this.valueOptions?.hasIcon ?? false, ghost: true })}${this.renderDegreeColumn(this.primarySize)}`; return html`<div class=\"setpoint-width-reserve\" aria-hidden=\"true\"> ${content} </div>`; } /** * Vertical: advice / setpoint / value rows right-aligned in a cluster, the * label+unit meta zone below, then the source row under an auto divider. * `alignment` re-aligns the whole column for stacked readouts. */ renderVerticalLayout() { return html` <div class=\"value-cluster\" part=\"value-cluster\"> ${this.hasAdvice ? html`<div class=\"advice-row\" part=\"advice-wrapper\"> ${this.renderAdviceBlock()} </div>` : nothing} ${this.hasSetpoint ? html`<div class=\"setpoint-row\" part=\"setpoint-wrapper\"> ${this.renderSetpointBlock()} ${this.renderSetpointWidthReserve(\"setpoint\")} </div>` : nothing} ${this.hasValue ? html`<div class=\"value-row\" part=\"value-wrapper\"> ${this.renderValueReading()} ${this.renderSetpointWidthReserve(\"value\")} </div>` : nothing} </div> ${this.renderMetaZone()} ${this.renderSource()} `; } /** * Horizontal: advice, setpoint, value and the stacked label/unit meta flow on * one cap-height-aligned row, with the source at the end after an auto * divider. */ renderHorizontalLayout() { return html` <div class=\"inline-row\" part=\"value-cluster\"> ${this.hasAdvice ? html`<div class=\"advice-row\" part=\"advice-wrapper\"> ${this.renderAdviceBlock()} </div>` : nothing} ${this.hasSetpoint ? html`<div class=\"setpoint-row\" part=\"setpoint-wrapper\"> ${this.renderSetpointBlock()} </div>` : nothing} ${this.hasValue ? html`<div class=\"value-row\" part=\"value-wrapper\"> ${this.renderValueReading()} </div>` : nothing} ${this.renderMetaZone()} ${this.renderSource()} </div> `; } willUpdate(changed) { super.willUpdate(changed); assertReadoutValueType(\"obc-readout\", this.value, this.valueType); assertReadoutFractionDigits(\"obc-readout\", this.fractionDigits); this.warnHorizontalSizeIgnored(); } /** * A horizontal readout discards any `size` but `large` (see `resolvedSize`). * A silent discard is indistinguishable from a bug to a consumer, so each * offending element says so once and logs itself for locating (#1182). * A removed `size` attribute arrives as `null` and counts as unset. */ warnHorizontalSizeIgnored() { if (this.hasWarnedHorizontalSize || !this.isHorizontal || !this.size || this.size === ReadoutSize.large) { return; } this.hasWarnedHorizontalSize = true; console.warn( `[obc-readout] size=\"${this.size}\" is ignored when direction=\"horizontal\": the horizontal arrangement exists in the large tier only. Remove the size, or use direction=\"vertical\" for the small / medium tiers.`, this ); } updated(changed) { super.updated(changed); if (changed.has(\"sourcePickerContentVisible\")) { if (this.sourcePickerContentVisible) { window.addEventListener(\"pointerdown\", this.onWindowPointerDown, true); } else { window.removeEventListener( \"pointerdown\", this.onWindowPointerDown, true ); } } const firstUpdate = !this.hasCompletedFirstUpdate; this.hasCompletedFirstUpdate = true; if (!this.isPopUp || this.setpointTouching) { this.clearDeferredSetpointHide(); return; } const shouldHide = this.hasSetpoint && this.isAtSetpoint; if (firstUpdate) { this.deferredSetpointHidePhase = shouldHide ? ReadoutBlockHidePhase.hidden : ReadoutBlockHidePhase.none; return; } if (!shouldHide) { this.clearDeferredSetpointHide(); return; } if (this.deferredSetpointHidePhase !== ReadoutBlockHidePhase.none) { return; } this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hiding; window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = window.setTimeout(() => { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hidden; this.deferredSetpointHideTimer = void 0; }, 100); } clearDeferredSetpointHide() { if (this.deferredSetpointHidePhase !== ReadoutBlockHidePhase.none) { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.none; } window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = void 0; } disconnectedCallback() { window.removeEventListener(\"pointerdown\", this.onWindowPointerDown, true); window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = void 0; if (this.deferredSetpointHidePhase === ReadoutBlockHidePhase.hiding) { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hidden; } super.disconnectedCallback(); } render() { const dataQuality = this.dataQuality; const classes = classMap({ readout: true, [`size-${this.resolvedSize}`]: true, [`direction-${this.resolvedDirection}`]: true, [`stacking-${this.resolvedStacking}`]: true, [`alignment-${this.resolvedAlignment}`]: true, [`priority-${this.resolvedPriority}`]: true, \"data-low-integrity\": dataQuality === ReadoutDataQuality.lowIntegrity, \"data-invalid\": dataQuality === ReadoutDataQuality.invalid, \"flip-flop\": this.isFlipFlop, \"setpoint-dynamic\": this.hasDynamicSetpoint }); const root = html` <div class=${classes} part=\"root\"> ${this.isHorizontal ? this.renderHorizontalLayout() : this.renderVerticalLayout()} ${this.renderSourcePickerSlot()} </div> ${this.renderSourcePickerContent()} `; const alert = this.alert === true ? {} : this.alert; return wrapWithAlertFrame(alert, root); } }"
10285
+ "default": "class extends LitElement { constructor() { super(...arguments); this.hasValue = true; this.value = null; this.valueType = ReadoutValueType.number; this.off = false; this.offText = \"OFF\"; this.hasSetpoint = false; this.hasAdvice = false; this.hasDegree = false; this.hasDegreeSpacer = false; this.hasLeadingIcon = false; this.fractionDigits = 0; this.maxDigits = 0; this.alert = false; this.showDebugOverlay = false; this.deferredSetpointHidePhase = ReadoutBlockHidePhase.none; this.hasCompletedFirstUpdate = false; this.hasWarnedHorizontalSize = false; this.sourcePickerContentVisible = false; this.sourcePickerOptions = []; this.onWindowPointerDown = (event) => { if (!this.sourcePickerContentVisible) { return; } if (event.composedPath().includes(this)) { return; } this.sourcePickerContentVisible = false; }; this.syncSourcePickerOptions = () => { this.sourcePickerOptions = this.getSourcePickerNavigationItems().map( (item, index) => { const { itemLabel, itemValue } = this.getSourcePickerItemInfo( item, index ); return { value: itemValue, label: itemLabel, icon: this.createSourcePickerOptionIcon(item) }; } ); }; } get resolvedSize() { if (this.isHorizontal) { return ReadoutSize.large; } return this.size ?? ReadoutSize.small; } get resolvedPriority() { return this.priority ?? ReadoutPriority.regular; } get resolvedDirection() { return this.direction ?? \"vertical\"; } get resolvedStacking() { return this.stacking ?? \"inline\"; } get resolvedAlignment() { return this.alignment ?? \"vertical\"; } get isHorizontal() { return this.resolvedDirection === \"horizontal\"; } get resolvedFractionDigits() { return this.fractionDigits ?? 0; } get resolvedMaxDigits() { return resolveReadoutDigitCount(this.maxDigits); } /** * Whether a digit knob failed to arrive (`NaN` / `null` / `undefined`). * The raw knobs are forwarded to the blocks, which render the reading as * the unavailable dash (see `obc-readout-block.digitCountsMissing`); this * getter only keeps the setpoint comparison in agreement with what is * displayed. */ get digitCountsMissing() { return isReadoutDigitCountMissing(this.fractionDigits) || isReadoutDigitCountMissing(this.maxDigits); } get hasSrc() { return this.src !== void 0 && this.src.trim() !== \"\"; } get resolvedSourceInteraction() { return this.srcOptions?.interaction ?? \"none\"; } get isAtSetpoint() { if (!this.hasSetpoint) { return false; } if (this.digitCountsMissing) { return false; } return isDisplayedAtSetpoint( resolveReadoutNumericValue(this.value, this.valueType) ?? null, this.setpoint, this.numericFormatOptions(this.resolvedMaxDigits) ); } get resolvedSetpointInteraction() { return this.setpointOptions?.interaction ?? ReadoutSetpointInteraction.alwaysVisible; } get isFlipFlop() { return this.resolvedSetpointInteraction === ReadoutSetpointInteraction.flipFlop; } get isEqualSize() { return this.resolvedSetpointInteraction === ReadoutSetpointInteraction.equalSize; } get isPopUp() { return this.resolvedSetpointInteraction === ReadoutSetpointInteraction.popUp; } get setpointTouching() { return this.setpointOptions?.touching ?? false; } /** * Whether the setpoint interaction re-sizes or hides blocks at runtime — * flip-flop (emphasis swap) and pop-up (touch swap + fade-out) — the modes * that need the width/height reservation so the readout never shifts (see * {@link renderSetpointWidthReserve}). */ get hasDynamicSetpoint() { return this.hasSetpoint && (this.isFlipFlop || this.isPopUp); } /** * The setpoint is rendered \"emphasised\" (primary size + SemiBold weight) when * it is the focus of attention: while actively adjusting (`touching`), or while * a flip-flop has the value away from the setpoint. Otherwise it is a secondary * (smaller, regular-weight) reference next to the value. */ get isSetpointEmphasized() { if (!this.hasSetpoint) { return false; } if (this.setpointTouching) { return true; } return this.isFlipFlop && !this.isAtSetpoint; } /** * The readout's enhanced (in-command) colour state, applied uniformly to BOTH * the value and the setpoint — they are always either both neutral or both * enhanced (never a blue setpoint next to a grey value). Driven by `priority` * only; `valueOptions.weight` changes weight, not colour. */ get rowEnhanced() { return this.resolvedPriority === ReadoutPriority.enhanced; } /** Primary value-typography size for the current density tier. */ get primarySize() { return readoutPrimarySize(this.resolvedSize); } /** Secondary (de-emphasised) value-typography size for the current density tier. */ get secondarySize() { return readoutSecondarySize(this.resolvedSize); } get valueSize() { return readoutValueSize({ primary: this.primarySize, secondary: this.secondarySize, setpointEmphasized: this.isSetpointEmphasized, equalSize: this.isEqualSize }); } get setpointSize() { return readoutSetpointSize({ primary: this.primarySize, secondary: this.secondarySize, emphasized: this.isSetpointEmphasized, equalSize: this.isEqualSize }); } /** Value font weight passes straight to obc-textbox; regular when unset. Colour is separate. */ get valueWeight() { return this.valueOptions?.weight ?? ObcTextboxFontWeight.regular; } /** * Label size. The Figma 6.1 title block defaults large readouts to `s` — a * scannable label should not sit at the smallest permitted size — while * small/medium tiers stay `xs`; `labelOptions.size` overrides either way * (per the design team, for context / density / secondary instruments). * TODO(designer): the medium-tier default is unverified in the new sheets * (only XS and S exist on the title's size axis). */ get labelSize() { return this.labelOptions?.size ?? (this.resolvedSize === ReadoutSize.large ? ObcTextboxSize.s : ObcTextboxSize.xs); } /** * Label weight: SemiBold only for enhanced (in-command) readouts, regular * otherwise — the designer reduced the amount of semibold in the interface * (Figma 6.1 review). */ get labelWeight() { return this.rowEnhanced ? ObcTextboxFontWeight.semibold : ObcTextboxFontWeight.regular; } /** Setpoint is SemiBold only while emphasised, otherwise regular weight. */ get setpointWeight() { return readoutSetpointWeight(this.isSetpointEmphasized); } numericFormatOptions(maxDigits) { return readoutNumericFormatOptions(maxDigits, this.resolvedFractionDigits); } /** classMap fragment for a block / source carrying per-block data quality. */ dataQualityClasses(dataQuality) { return readoutDataQualityClasses(dataQuality); } /** * Forward the matching readout icon slot into the block's single `icon` * slot. The variant's default marker lives in `obc-readout-block` and shows * when nothing is assigned here (an empty forwarded slot flattens to nothing). */ renderForwardedIcon(variant) { if (variant === ReadoutBlockVariant.setpoint) { return html`<slot name=\"setpoint-icon\" slot=\"icon\"></slot>`; } if (variant === ReadoutBlockVariant.advice) { return html`<slot name=\"advice-icon\" slot=\"icon\"></slot>`; } return html`<slot name=\"value-icon\" slot=\"icon\"></slot>`; } renderBlock(config) { return html` <obc-readout-block exportparts=\"block, block-content, block-text, block-icon, degree\" .variant=${config.variant} .value=${config.value ?? null} .valueType=${config.valueType ?? ReadoutValueType.number} .size=${this.resolvedSize} .valueSize=${config.valueSize} .enhanced=${config.enhanced} .weight=${config.weight} .hasDegree=${config.hasDegree ?? false} .hasIcon=${config.hasIcon ?? false} .fractionDigits=${this.fractionDigits} .maxDigits=${this.maxDigits} .hintedZeros=${config.hintedZeros} .hasSignSpacer=${config.hasSignSpacer ?? false} .spaceReserver=${config.spaceReserver} .off=${config.off ?? false} .offText=${this.offText} .touching=${config.touching ?? false} .hidePhase=${config.hidePhase ?? ReadoutBlockHidePhase.none} .dataQuality=${config.dataQuality} .alert=${config.alert ?? false} .category=${config.category ?? ReadoutAdviceCategory.regular} .active=${config.active ?? false} > ${config.ghost ? nothing : this.renderForwardedIcon(config.variant)} </obc-readout-block> `; } /** * A cap-height `°` column whose width scales with the value size, rendered * after the value block. Setpoint / advice blocks render their own degree via * `obc-readout-block`. */ renderDegreeGlyph(size) { return html` <span class=${classMap({ \"degree-column\": true, [`degree-${size}`]: true, \"tone-enhanced\": this.rowEnhanced })} part=\"degree\" > <obc-textbox class=\"degree-glyph\" .size=${size} alignment=\"center\" >°</obc-textbox > </span> `; } /** * The column rendered after the value digits: * * - `hasDegree`: a cap-height `°` column whose width scales with the value size. * - `hasDegreeSpacer` (non-degree readouts): an invisible reserve of the same * width, so this readout's value digits stay aligned with degree readouts * in the same column. */ renderDegreeColumn(size = this.valueSize) { if ((this.hasDegree ?? false) && !this.off) { return this.renderDegreeGlyph(size); } if (this.hasDegreeSpacer ?? false) { return html`<span class=\"degree-spacer\" part=\"degree-spacer\" aria-hidden=\"true\" ></span>`; } return nothing; } renderTextbox(role, text, reserver, blockState) { const weight = role === \"label\" ? this.labelWeight : ObcTextboxFontWeight.regular; const size = role === \"label\" ? this.labelSize : ObcTextboxSize.xs; const box = html` <obc-textbox class=${classMap({ [role]: true, ...this.dataQualityClasses(blockState?.dataQuality) })} part=${role} .size=${size} .fontWeight=${weight} alignment=\"left\" > ${text} ${reserver ? html`<span slot=\"length\">${reserver}</span>` : nothing} </obc-textbox> `; return wrapWithAlertFrame(blockState?.alert ?? false, box); } get setpointHidePhase() { const popUpAtSetpoint = this.isPopUp && this.isAtSetpoint && !this.setpointTouching; return resolveSetpointHidePhase( popUpAtSetpoint, this.deferredSetpointHidePhase ); } // `hasDegree` is deliberately NOT forwarded to the advice / setpoint blocks: // the unit is written once per readout and the degree follows the same rule // (Figma 6.1). `obc-readout-list-item` keeps its own per-row convention. renderAdviceBlock() { return this.renderBlock({ variant: ReadoutBlockVariant.advice, value: this.advice, valueSize: this.secondarySize, enhanced: false, weight: ObcTextboxFontWeight.regular, hintedZeros: this.adviceOptions?.hintedZeros ?? false, hasSignSpacer: this.adviceOptions?.hasSignSpacer ?? false, spaceReserver: this.adviceOptions?.spaceReserver, dataQuality: this.adviceOptions?.dataQuality, alert: this.adviceOptions?.alert, category: this.adviceOptions?.category, active: this.adviceOptions?.active }); } renderSetpointBlock() { return this.renderBlock({ variant: ReadoutBlockVariant.setpoint, value: this.setpoint, valueSize: this.setpointSize, // Value and setpoint share the enhanced colour state (both neutral // or both enhanced); the setpoint is bold only while emphasised. enhanced: this.rowEnhanced, weight: this.setpointWeight, hintedZeros: this.setpointOptions?.hintedZeros ?? false, hasSignSpacer: this.setpointOptions?.hasSignSpacer ?? false, spaceReserver: this.setpointOptions?.spaceReserver, touching: this.setpointTouching, hidePhase: this.setpointHidePhase, dataQuality: this.setpointOptions?.dataQuality, alert: this.setpointOptions?.alert }); } /** * The value reading: the value block and its degree column (or spacer) * grouped in one relatively-positioned wrapper so the value alert frame AND * the value data-quality chip can wrap both together. Unlike * `obc-readout-list-item` the unit is not part of the reading — it lives in * the label/unit meta zone. */ renderValueReading() { return html` <div class=${classMap({ \"value-reading\": true, ...this.dataQualityClasses(this.valueOptions?.dataQuality) })} part=\"value-reading\" > ${this.renderBlock({ variant: ReadoutBlockVariant.value, value: this.value, valueType: this.valueType, valueSize: this.valueSize, enhanced: this.rowEnhanced, weight: this.valueWeight, hintedZeros: this.valueOptions?.hintedZeros ?? false, hasSignSpacer: this.valueOptions?.hasSignSpacer ?? false, spaceReserver: this.valueOptions?.spaceReserver, off: this.off, hasIcon: this.valueOptions?.hasIcon ?? false })} ${this.renderDegreeColumn()} ${this.renderValueAlertOverlay()} </div> `; } /** * The value alert frame, drawn as a pure overlay around the value reading * (value + degree). It reserves no space — the `obc-alert-frame` sits in an * absolutely-positioned box offset outward (see `.value-alert-overlay` in the * CSS) so its stroke is centred on the 4px/2px padding line and toggling it * never shifts content. Renders only when `valueOptions.alert` is a config * object. */ renderValueAlertOverlay() { const alert = this.valueOptions?.alert; if (typeof alert !== \"object\" || alert === null) { return nothing; } const thickness = alert.thickness ?? ObcAlertFrameThickness.Small; return html` <div class=${classMap({ \"value-alert-overlay\": true, // The outward offset is thickness-dependent (see the CSS): large frames // draw a wider outline, so the box must sit further out to stay centred. \"thickness-large\": thickness === ObcAlertFrameThickness.Large })} aria-hidden=\"true\" > <obc-alert-frame part=\"value-alert-frame\" .type=${alert.type ?? ObcAlertFrameType.Regular} .thickness=${thickness} .status=${alert.status ?? AlertType.Alarm} .mode=${alert.mode ?? ObcAlertFrameMode.ackedActive} .flashingSpeed=${alert.flashingSpeed ?? FlashingSpeed.Default} .showIcon=${alert.showIcon ?? false} .showAlertCategoryIcon=${alert.showAlertCategoryIcon ?? true} .wrapContent=${false} .fullWidth=${false} ></obc-alert-frame> </div> `; } /** The label/unit meta zone; `stacking` picks the inline or stacked arrangement. */ renderMetaZone() { if (!this.label && !this.unit) { return nothing; } const stacked = this.isHorizontal || this.resolvedStacking === \"stacked\"; return html` <div class=${classMap({ meta: true, \"meta-inline\": !stacked, \"meta-stacked\": stacked, \"label-only\": !!this.label && !this.unit, \"unit-only\": !!this.unit && !this.label })} part=\"meta-wrapper\" > ${this.hasLeadingIcon ? html`<span class=\"leading-icon\" aria-hidden=\"true\" ><slot name=\"leading-icon\"></slot ></span>` : nothing} <div class=\"meta-labels\" part=\"meta-labels\"> ${this.label ? this.renderTextbox( \"label\", this.label, this.labelOptions?.spaceReserver ) : nothing} ${this.unit ? this.renderTextbox( \"unit\", this.unit, this.unitOptions?.spaceReserver ) : nothing} </div> </div> `; } renderSourceContent() { const interaction = this.resolvedSourceInteraction; const src = this.src ?? \"\"; const buttonClasses = classMap({ \"source-button\": true, ...this.dataQualityClasses(this.srcOptions?.dataQuality) }); if (interaction === \"picker\") { const button = html` <obc-button class=${buttonClasses} variant=\"flat\" .fullWidth=${false} showTrailingIcon @click=${() => { this.sourcePickerContentVisible = !this.sourcePickerContentVisible; }} > <obi-drop-down-google slot=\"trailing-icon\"></obi-drop-down-google> ${src} </obc-button> `; return wrapWithAlertFrame(this.srcOptions?.alert ?? false, button); } if (interaction === \"flyout\") { const button = html` <obc-button class=${buttonClasses} variant=\"flat\" .fullWidth=${false} showTrailingIcon @click=${() => { this.dispatchEvent( new CustomEvent(\"source-flyout-click\", { bubbles: true, composed: true, detail: { src } }) ); }} > <obi-chevron-right-google slot=\"trailing-icon\" ></obi-chevron-right-google> ${src} </obc-button> `; return wrapWithAlertFrame(this.srcOptions?.alert ?? false, button); } return this.renderSourceBlock(src); } /** * The plain (non-interactive) source: the textbox plus its optional state * chip and deviation line. The regular no-deviation case renders the bare * textbox; a state or deviation wraps it in the `.source-block` stack. The * picker / flyout button variants do not carry state / deviation yet — * TODO(designer): undefined in the 6.1 sheets. */ renderSourceBlock(src) { const state2 = this.srcOptions?.state ?? ReadoutSourceState.regular; const deviation = this.srcOptions?.deviation; const hasDeviation = deviation !== void 0 && Number.isFinite(deviation); const box = this.renderTextbox( \"source\", src, this.srcOptions?.spaceReserver, this.srcOptions ); if (state2 === ReadoutSourceState.regular && !hasDeviation) { return box; } return html` <div class=${classMap({ \"source-block\": true, [`source-state-${state2}`]: state2 !== ReadoutSourceState.regular })} part=\"source-block\" > ${box} ${hasDeviation ? html`<span class=\"source-deviation\"> <obi-delta aria-hidden=\"true\"></obi-delta> <obc-textbox class=\"source-deviation-value\" .size=${ObcTextboxSize.xs} .tabularNums=${true} alignment=\"left\" >${deviation}</obc-textbox > </span>` : nothing} </div> `; } /** * The source row (vertical) / segment (horizontal). Only the horizontal * direction draws an auto divider before the source — the Figma 6.1 review * (node 95:24490 \"remove dividers\", 95:26891 \"Source-divider are there\") * keeps the vertical source row divider-free while the horizontal flow * separates the source with a vertical rule. */ renderSource() { if (!this.hasSrc) { return nothing; } return html` ${this.isHorizontal ? html`<div class=\"divider divider-vertical\" part=\"divider\" aria-hidden=\"true\" ></div>` : nothing} <div class=\"source-row\" part=\"source-wrapper\"> ${this.renderSourceContent()} </div> `; } // ---------- Source picker (context menu built from src-picker-content) ---------- getSourcePickerNavigationItems() { const assignedElements = this.sourcePickerSlot?.assignedElements({ flatten: true }) ?? []; return assignedElements.flatMap((element) => { if (!(element instanceof HTMLElement)) { return []; } if (element.localName === \"obc-navigation-item\") { return [element]; } return Array.from( element.querySelectorAll(\"obc-navigation-item\") ); }); } createSourcePickerOptionIcon(element) { const iconElement = element.querySelector('[slot=\"icon\"]'); if (!(iconElement instanceof HTMLElement)) { return void 0; } return html`${iconElement.cloneNode(true)}`; } getSourcePickerItemInfo(item, index) { const itemWithValues = item; const itemLabel = itemWithValues.label || item.getAttribute(\"label\") || \"\"; const itemValue = item.getAttribute(\"data-value\") || itemWithValues.value || item.getAttribute(\"value\") || itemLabel || `source-option-${index}`; return { itemLabel, itemValue }; } findSourcePickerOptionElement(value) { const navigationItems = this.getSourcePickerNavigationItems(); for (const [index, item] of navigationItems.entries()) { const { itemValue } = this.getSourcePickerItemInfo(item, index); if (itemValue === value) { return item; } } return void 0; } handleSourcePickerItemClick(event) { this.dispatchEvent( new CustomEvent(\"source-change\", { bubbles: true, composed: true, detail: { value: event.detail.value, label: event.detail.option?.label } }) ); this.findSourcePickerOptionElement(event.detail.value)?.click(); this.sourcePickerContentVisible = false; } /** * The context menu highlights options by their VALUE, while `src` holds the * displayed source text — which may be an option's label when the two * differ (e.g. `data-value=\"gps-1\"` with label \"GPS 1\"). Resolve the current * source to the matching option's value (by value or label) so the open * picker always highlights the active source. */ get selectedSourceValues() { if (!this.src) { return []; } const match = this.sourcePickerOptions.find( (option) => option.value === this.src || option.label === this.src ); return [match?.value ?? this.src]; } renderSourcePickerContent() { if (this.resolvedSourceInteraction !== \"picker\" || !this.sourcePickerContentVisible) { return nothing; } return html` <obc-context-menu-input .type=${ContextMenuType.Regular} .options=${this.sourcePickerOptions} .selectedValues=${this.selectedSourceValues} class=\"source-picker-content\" @item-click=${this.handleSourcePickerItemClick} @close=${() => { this.sourcePickerContentVisible = false; }} ></obc-context-menu-input> `; } renderSourcePickerSlot() { return html` <slot name=\"src-picker-content\" hidden @slotchange=${this.syncSourcePickerOptions} ></slot> `; } // ---------- Layouts ---------- /** * An invisible primary-size duplicate of a dynamic setpoint row's content, * stacked under the visible block in the same grid cell purely to reserve * width. With both rows permanently holding their emphasised width, the * widest row — and therefore the readout's total width — stays constant * while a flip-flop swaps the value/setpoint sizes, while a touch swap * runs, and while a pop-up setpoint fades out, so the meta zone and source * never shift. This is the vertical analogue of the horizontal arrangement, * where the swapped pair sits on one row and its summed width is constant * by construction (the designer's flip-flop invariant). Only the vertical * direction needs it; `always-visible` layouts are untouched. */ renderSetpointWidthReserve(kind) { if (!this.hasDynamicSetpoint || this.isHorizontal) { return nothing; } const content = kind === \"setpoint\" ? this.renderBlock({ variant: ReadoutBlockVariant.setpoint, value: this.setpoint, valueSize: this.primarySize, enhanced: false, // The emphasised setpoint is SemiBold — reserve its widest form. // No degree: the visible setpoint block carries none (the degree // renders on the actual value only), so the ghost mirrors it. weight: ObcTextboxFontWeight.semibold, hintedZeros: this.setpointOptions?.hintedZeros ?? false, hasSignSpacer: this.setpointOptions?.hasSignSpacer ?? false, spaceReserver: this.setpointOptions?.spaceReserver, alert: this.setpointOptions?.alert, ghost: true }) : html`${this.renderBlock({ variant: ReadoutBlockVariant.value, value: this.value, valueType: this.valueType, valueSize: this.primarySize, enhanced: false, weight: this.valueWeight, hintedZeros: this.valueOptions?.hintedZeros ?? false, hasSignSpacer: this.valueOptions?.hasSignSpacer ?? false, spaceReserver: this.valueOptions?.spaceReserver, off: this.off, hasIcon: this.valueOptions?.hasIcon ?? false, ghost: true })}${this.renderDegreeColumn(this.primarySize)}`; return html`<div class=\"setpoint-width-reserve\" aria-hidden=\"true\"> ${content} </div>`; } /** * Vertical: advice / setpoint / value rows right-aligned in a cluster, the * label+unit meta zone below, then the source row under an auto divider. * `alignment` re-aligns the whole column for stacked readouts. */ renderVerticalLayout() { return html` <div class=\"value-cluster\" part=\"value-cluster\"> ${this.hasAdvice ? html`<div class=\"advice-row\" part=\"advice-wrapper\"> ${this.renderAdviceBlock()} </div>` : nothing} ${this.hasSetpoint ? html`<div class=\"setpoint-row\" part=\"setpoint-wrapper\"> ${this.renderSetpointBlock()} ${this.renderSetpointWidthReserve(\"setpoint\")} </div>` : nothing} ${this.hasValue ? html`<div class=\"value-row\" part=\"value-wrapper\"> ${this.renderValueReading()} ${this.renderSetpointWidthReserve(\"value\")} </div>` : nothing} </div> ${this.renderMetaZone()} ${this.renderSource()} `; } /** * Horizontal: advice, setpoint, value and the stacked label/unit meta flow on * one cap-height-aligned row, with the source at the end after an auto * divider. */ renderHorizontalLayout() { return html` <div class=\"inline-row\" part=\"value-cluster\"> ${this.hasAdvice ? html`<div class=\"advice-row\" part=\"advice-wrapper\"> ${this.renderAdviceBlock()} </div>` : nothing} ${this.hasSetpoint ? html`<div class=\"setpoint-row\" part=\"setpoint-wrapper\"> ${this.renderSetpointBlock()} </div>` : nothing} ${this.hasValue ? html`<div class=\"value-row\" part=\"value-wrapper\"> ${this.renderValueReading()} </div>` : nothing} ${this.renderMetaZone()} ${this.renderSource()} </div> `; } willUpdate(changed) { super.willUpdate(changed); assertReadoutValueType(\"obc-readout\", this.value, this.valueType); assertReadoutFractionDigits(\"obc-readout\", this.fractionDigits); this.warnHorizontalSizeIgnored(); } /** * A horizontal readout discards any `size` but `large` (see `resolvedSize`). * A silent discard is indistinguishable from a bug to a consumer, so each * offending element says so once and logs itself for locating (#1182). * A removed `size` attribute arrives as `null` and counts as unset. */ warnHorizontalSizeIgnored() { if (this.hasWarnedHorizontalSize || !this.isHorizontal || !this.size || this.size === ReadoutSize.large) { return; } this.hasWarnedHorizontalSize = true; console.warn( `[obc-readout] size=\"${this.size}\" is ignored when direction=\"horizontal\": the horizontal arrangement exists in the large tier only. Remove the size, or use direction=\"vertical\" for the small / medium tiers.`, this ); } updated(changed) { super.updated(changed); if (changed.has(\"sourcePickerContentVisible\")) { if (this.sourcePickerContentVisible) { window.addEventListener(\"pointerdown\", this.onWindowPointerDown, true); } else { window.removeEventListener( \"pointerdown\", this.onWindowPointerDown, true ); } } const firstUpdate = !this.hasCompletedFirstUpdate; this.hasCompletedFirstUpdate = true; if (!this.isPopUp || this.setpointTouching) { this.clearDeferredSetpointHide(); return; } const shouldHide = this.hasSetpoint && this.isAtSetpoint; if (firstUpdate) { this.deferredSetpointHidePhase = shouldHide ? ReadoutBlockHidePhase.hidden : ReadoutBlockHidePhase.none; return; } if (!shouldHide) { this.clearDeferredSetpointHide(); return; } if (this.deferredSetpointHidePhase !== ReadoutBlockHidePhase.none) { return; } this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hiding; window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = window.setTimeout(() => { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hidden; this.deferredSetpointHideTimer = void 0; }, 100); } clearDeferredSetpointHide() { if (this.deferredSetpointHidePhase !== ReadoutBlockHidePhase.none) { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.none; } window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = void 0; } disconnectedCallback() { window.removeEventListener(\"pointerdown\", this.onWindowPointerDown, true); window.clearTimeout(this.deferredSetpointHideTimer); this.deferredSetpointHideTimer = void 0; if (this.deferredSetpointHidePhase === ReadoutBlockHidePhase.hiding) { this.deferredSetpointHidePhase = ReadoutBlockHidePhase.hidden; } super.disconnectedCallback(); } render() { const dataQuality = this.dataQuality; const classes = classMap({ readout: true, [`size-${this.resolvedSize}`]: true, [`direction-${this.resolvedDirection}`]: true, [`stacking-${this.resolvedStacking}`]: true, [`alignment-${this.resolvedAlignment}`]: true, [`priority-${this.resolvedPriority}`]: true, \"data-low-integrity\": dataQuality === ReadoutDataQuality.lowIntegrity, \"data-invalid\": dataQuality === ReadoutDataQuality.invalid, \"flip-flop\": this.isFlipFlop, \"setpoint-dynamic\": this.hasDynamicSetpoint }); const root = html` <div class=${classes} part=\"root\"> ${this.isHorizontal ? this.renderHorizontalLayout() : this.renderVerticalLayout()} ${this.renderSourcePickerSlot()} </div> ${this.renderSourcePickerContent()} `; const alert = this.alert === true ? {} : this.alert; return wrapWithAlertFrame(alert, root); } }"
10266
10286
  }
10267
10287
  ],
10268
10288
  "exports": [
@@ -40375,6 +40395,19 @@
40375
40395
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
40376
40396
  }
40377
40397
  },
40398
+ {
40399
+ "kind": "field",
40400
+ "name": "xAxis",
40401
+ "type": {
40402
+ "text": "ChartLineXAxisConfig | undefined"
40403
+ },
40404
+ "default": "undefined",
40405
+ "description": "Pinned x range (`min`/`max`) for time and number axes. Without it the axis spans exactly the data, so a window that is still filling stretches across the full width. In `minutes` display `max` is the `0min` reference.",
40406
+ "inheritedFrom": {
40407
+ "name": "ObcChartLineBase",
40408
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40409
+ }
40410
+ },
40378
40411
  {
40379
40412
  "kind": "field",
40380
40413
  "name": "showGrid",
@@ -40440,6 +40473,32 @@
40440
40473
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
40441
40474
  }
40442
40475
  },
40476
+ {
40477
+ "kind": "field",
40478
+ "name": "rangeLabels",
40479
+ "type": {
40480
+ "text": "RangeLabels"
40481
+ },
40482
+ "description": "Labels kept below the 192px threshold, where the axis labels are otherwise hidden: 'y' draws min, max and (inside the range) 0 in a gutter on the y-axis side; 'x' draws the first and last x value in a bottom gutter; 'xy' both. Ignored when `hasLabelPadding` is false.",
40483
+ "inheritedFrom": {
40484
+ "name": "ObcChartLineBase",
40485
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40486
+ }
40487
+ },
40488
+ {
40489
+ "kind": "field",
40490
+ "name": "primaryYAxisId",
40491
+ "type": {
40492
+ "text": "string"
40493
+ },
40494
+ "privacy": "private",
40495
+ "description": "Id of the y scale a dataset lands on when it names none. Chart.js would\notherwise add a default `y` scale next to the configured ones and draw it.",
40496
+ "readonly": true,
40497
+ "inheritedFrom": {
40498
+ "name": "ObcChartLineBase",
40499
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40500
+ }
40501
+ },
40443
40502
  {
40444
40503
  "kind": "field",
40445
40504
  "name": "DEFAULT_TENSION",
@@ -40694,6 +40753,237 @@
40694
40753
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
40695
40754
  }
40696
40755
  },
40756
+ {
40757
+ "kind": "field",
40758
+ "name": "savedMainTickmarkLabels",
40759
+ "privacy": "private",
40760
+ "default": "new WeakMap< ExternalScaleElement, boolean >()",
40761
+ "inheritedFrom": {
40762
+ "name": "ObcChartLineBase",
40763
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40764
+ }
40765
+ },
40766
+ {
40767
+ "kind": "field",
40768
+ "name": "wasBelowThreshold",
40769
+ "type": {
40770
+ "text": "boolean | undefined"
40771
+ },
40772
+ "privacy": "private",
40773
+ "description": "Last known threshold side, to notice a crossing in `updated()`.",
40774
+ "inheritedFrom": {
40775
+ "name": "ObcChartLineBase",
40776
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40777
+ }
40778
+ },
40779
+ {
40780
+ "kind": "method",
40781
+ "name": "compactLabelThickness",
40782
+ "privacy": "private",
40783
+ "return": {
40784
+ "type": {
40785
+ "text": "number | undefined"
40786
+ }
40787
+ },
40788
+ "parameters": [
40789
+ {
40790
+ "name": "axis",
40791
+ "type": {
40792
+ "text": "'x' | 'y'"
40793
+ }
40794
+ }
40795
+ ],
40796
+ "description": "Label band a slotted scale needs for its compact labels, in the scale's\nown viewBox units. The scale prints the raw values, so those are what is\nmeasured; the x band is one text line.",
40797
+ "inheritedFrom": {
40798
+ "name": "ObcChartLineBase",
40799
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40800
+ }
40801
+ },
40802
+ {
40803
+ "kind": "field",
40804
+ "name": "rangeLabelCache",
40805
+ "type": {
40806
+ "text": "{\n y: string[];\n x: string[];\n bounds?: {min: number; max: number};\n }"
40807
+ },
40808
+ "privacy": "private",
40809
+ "default": "{y: [], x: []}",
40810
+ "description": "Range labels for the current data and size, refreshed with every\n`getChartOptions()` so the plugin never rebuilds the datasets on a draw.",
40811
+ "inheritedFrom": {
40812
+ "name": "ObcChartLineBase",
40813
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40814
+ }
40815
+ },
40816
+ {
40817
+ "kind": "method",
40818
+ "name": "slotFor",
40819
+ "privacy": "private",
40820
+ "return": {
40821
+ "type": {
40822
+ "text": "HTMLSlotElement | undefined"
40823
+ }
40824
+ },
40825
+ "parameters": [
40826
+ {
40827
+ "name": "side",
40828
+ "type": {
40829
+ "text": "'left' | 'right' | 'top' | 'bottom'"
40830
+ }
40831
+ }
40832
+ ],
40833
+ "inheritedFrom": {
40834
+ "name": "ObcChartLineBase",
40835
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40836
+ }
40837
+ },
40838
+ {
40839
+ "kind": "method",
40840
+ "name": "hasSlottedScale",
40841
+ "privacy": "private",
40842
+ "return": {
40843
+ "type": {
40844
+ "text": "boolean"
40845
+ }
40846
+ },
40847
+ "parameters": [
40848
+ {
40849
+ "name": "side",
40850
+ "type": {
40851
+ "text": "'left' | 'right' | 'top' | 'bottom'"
40852
+ }
40853
+ }
40854
+ ],
40855
+ "inheritedFrom": {
40856
+ "name": "ObcChartLineBase",
40857
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40858
+ }
40859
+ },
40860
+ {
40861
+ "kind": "field",
40862
+ "name": "ySide",
40863
+ "type": {
40864
+ "text": "'left' | 'right'"
40865
+ },
40866
+ "privacy": "private",
40867
+ "readonly": true,
40868
+ "inheritedFrom": {
40869
+ "name": "ObcChartLineBase",
40870
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40871
+ }
40872
+ },
40873
+ {
40874
+ "kind": "method",
40875
+ "name": "rangeLabelFont",
40876
+ "privacy": "private",
40877
+ "return": {
40878
+ "type": {
40879
+ "text": "RangeLabelFont"
40880
+ }
40881
+ },
40882
+ "inheritedFrom": {
40883
+ "name": "ObcChartLineBase",
40884
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40885
+ }
40886
+ },
40887
+ {
40888
+ "kind": "method",
40889
+ "name": "dataExtents",
40890
+ "privacy": "private",
40891
+ "return": {
40892
+ "type": {
40893
+ "text": "{\n x?: {min: number; max: number};\n y: Map<string, {min: number; max: number}>;\n }"
40894
+ }
40895
+ },
40896
+ "parameters": [
40897
+ {
40898
+ "name": "prepared",
40899
+ "type": {
40900
+ "text": "ReturnType<ObcChartLineBase['prepareChartDataAndLabels']>"
40901
+ }
40902
+ }
40903
+ ],
40904
+ "description": "Extent of the prepared data: one for x, one per y scale id. Each y axis\nranges from its own datasets, so a merged extent would label one axis\nwith another's values.",
40905
+ "inheritedFrom": {
40906
+ "name": "ObcChartLineBase",
40907
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40908
+ }
40909
+ },
40910
+ {
40911
+ "kind": "method",
40912
+ "name": "yRangeBounds",
40913
+ "privacy": "private",
40914
+ "return": {
40915
+ "type": {
40916
+ "text": "{min: number; max: number} | undefined"
40917
+ }
40918
+ },
40919
+ "parameters": [
40920
+ {
40921
+ "name": "extents",
40922
+ "type": {
40923
+ "text": "Map<string, {min: number; max: number}>"
40924
+ }
40925
+ }
40926
+ ],
40927
+ "description": "Range the y labels describe. Below the threshold the axis runs on data\nbounds, so its datasets' extent is exact and needs no chart to read from.",
40928
+ "inheritedFrom": {
40929
+ "name": "ObcChartLineBase",
40930
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40931
+ }
40932
+ },
40933
+ {
40934
+ "kind": "method",
40935
+ "name": "xEdgeLabelTexts",
40936
+ "privacy": "private",
40937
+ "return": {
40938
+ "type": {
40939
+ "text": "string[]"
40940
+ }
40941
+ },
40942
+ "parameters": [
40943
+ {
40944
+ "name": "prepared",
40945
+ "type": {
40946
+ "text": "ReturnType<ObcChartLineBase['prepareChartDataAndLabels']>"
40947
+ }
40948
+ },
40949
+ {
40950
+ "name": "extent",
40951
+ "type": {
40952
+ "text": "{min: number; max: number} | undefined"
40953
+ }
40954
+ }
40955
+ ],
40956
+ "description": "First and last x label, or nothing when there is no span to label.",
40957
+ "inheritedFrom": {
40958
+ "name": "ObcChartLineBase",
40959
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40960
+ }
40961
+ },
40962
+ {
40963
+ "kind": "method",
40964
+ "name": "refreshRangeLabels",
40965
+ "privacy": "private",
40966
+ "return": {
40967
+ "type": {
40968
+ "text": "typeof this.rangeLabelCache"
40969
+ }
40970
+ },
40971
+ "description": "Recompute the range labels. A side with a slotted scale is labelled by\nthe scale, so it gets no text here; the y bounds are kept regardless,\nsince the scale's compact band is measured from them.",
40972
+ "inheritedFrom": {
40973
+ "name": "ObcChartLineBase",
40974
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40975
+ }
40976
+ },
40977
+ {
40978
+ "kind": "method",
40979
+ "name": "createRangeLabelsPlugin",
40980
+ "privacy": "private",
40981
+ "description": "Paints min / 0 / max and first / last once Chart.js has drawn, inside the\ngutters `getChartOptions()` reserved. The end labels sit flush with the\nplot edges so no vertical padding is needed for them.",
40982
+ "inheritedFrom": {
40983
+ "name": "ObcChartLineBase",
40984
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
40985
+ }
40986
+ },
40697
40987
  {
40698
40988
  "kind": "method",
40699
40989
  "name": "hasVisibleScale",
@@ -40821,6 +41111,44 @@
40821
41111
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
40822
41112
  }
40823
41113
  },
41114
+ {
41115
+ "kind": "method",
41116
+ "name": "resolveAxisRange",
41117
+ "privacy": "private",
41118
+ "return": {
41119
+ "type": {
41120
+ "text": "{\n min: number;\n max: number;\n }"
41121
+ }
41122
+ },
41123
+ "parameters": [
41124
+ {
41125
+ "name": "side",
41126
+ "type": {
41127
+ "text": "'left' | 'right' | 'x'"
41128
+ }
41129
+ }
41130
+ ],
41131
+ "description": "Range a slotted scale on the given side has to cover.\n\nA range pinned in the axis configuration is used as is: it is the source\nof truth, and on the first sync there is no chart to read yet. Otherwise\nthe live Chart.js scale is read under the id `buildScalesConfig()` gives\nit. A side with no axis of its own follows the first configured one.",
41132
+ "inheritedFrom": {
41133
+ "name": "ObcChartLineBase",
41134
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
41135
+ }
41136
+ },
41137
+ {
41138
+ "kind": "method",
41139
+ "name": "resolveSlottedXRange",
41140
+ "privacy": "private",
41141
+ "return": {
41142
+ "type": {
41143
+ "text": "{min: number; max: number} | undefined"
41144
+ }
41145
+ },
41146
+ "description": "Range pushed to the top and bottom scales, or `undefined` to leave them\nalone. A scale prints its values verbatim, and on a `time` axis the raw\nrange is epoch milliseconds. In `minutes` display the range is converted\nto the minutes the chart's own labels show; `date` display has no numeric\nequivalent, so the scale keeps its own range.",
41147
+ "inheritedFrom": {
41148
+ "name": "ObcChartLineBase",
41149
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
41150
+ }
41151
+ },
40824
41152
  {
40825
41153
  "kind": "method",
40826
41154
  "name": "updateScaleProperties",
@@ -40839,6 +41167,39 @@
40839
41167
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
40840
41168
  }
40841
41169
  },
41170
+ {
41171
+ "kind": "method",
41172
+ "name": "ownsSlottedScaleRange",
41173
+ "privacy": "protected",
41174
+ "return": {
41175
+ "type": {
41176
+ "text": "boolean"
41177
+ }
41178
+ },
41179
+ "parameters": [
41180
+ {
41181
+ "name": "_side",
41182
+ "type": {
41183
+ "text": "'left' | 'right' | 'top' | 'bottom'"
41184
+ }
41185
+ }
41186
+ ],
41187
+ "description": "Whether the chart's axis range is what a slotted scale on this side should\ndescribe. A subclass that slots a scale of its own and gives it a separate\nrange returns false for that side, otherwise the sync below overwrites it.",
41188
+ "inheritedFrom": {
41189
+ "name": "ObcChartLineBase",
41190
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
41191
+ }
41192
+ },
41193
+ {
41194
+ "kind": "method",
41195
+ "name": "syncSlottedScaleRanges",
41196
+ "privacy": "private",
41197
+ "description": "Push the current axis ranges to the slotted scales. The full\n`updateScaleProperties()` cascade runs before the chart is (re)built and\nso reads the previous chart's ranges; this closes that gap once the new\nchart exists, and keeps auto-ranged scales in step with the data.",
41198
+ "inheritedFrom": {
41199
+ "name": "ObcChartLineBase",
41200
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
41201
+ }
41202
+ },
40842
41203
  {
40843
41204
  "kind": "method",
40844
41205
  "name": "hasAnyChanged",
@@ -41073,6 +41434,21 @@
41073
41434
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
41074
41435
  }
41075
41436
  },
41437
+ {
41438
+ "kind": "method",
41439
+ "name": "computeChartPadding",
41440
+ "privacy": "private",
41441
+ "return": {
41442
+ "type": {
41443
+ "text": "{\n top: number;\n right: number;\n bottom: number;\n left: number;\n }"
41444
+ }
41445
+ },
41446
+ "description": "One padding source for the Chart.js layout and the slotted scales: two\nsources painted the canvas over a scale and inset a scale inside a chart.",
41447
+ "inheritedFrom": {
41448
+ "name": "ObcChartLineBase",
41449
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
41450
+ }
41451
+ },
41076
41452
  {
41077
41453
  "kind": "method",
41078
41454
  "name": "getChartOptions",
@@ -41097,7 +41473,7 @@
41097
41473
  "text": "number | undefined"
41098
41474
  }
41099
41475
  },
41100
- "description": "Compute reference timestamp for time axis formatting.\nReturns earliest timestamp for 'date' mode, latest for 'minutes' mode.",
41476
+ "description": "Compute reference timestamp for time axis formatting.\nReturns earliest timestamp for 'date' mode, latest for 'minutes' mode.\nA pinned x range supplies the reference instead, so the axis edge stays\nthe reference while the data has not reached it yet.",
41101
41477
  "inheritedFrom": {
41102
41478
  "name": "ObcChartLineBase",
41103
41479
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
@@ -41789,6 +42165,19 @@
41789
42165
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
41790
42166
  }
41791
42167
  },
42168
+ {
42169
+ "kind": "field",
42170
+ "name": "xAxis",
42171
+ "type": {
42172
+ "text": "ChartLineXAxisConfig | undefined"
42173
+ },
42174
+ "default": "undefined",
42175
+ "description": "Pinned x range (`min`/`max`) for time and number axes. Without it the axis spans exactly the data, so a window that is still filling stretches across the full width. In `minutes` display `max` is the `0min` reference.",
42176
+ "inheritedFrom": {
42177
+ "name": "ObcChartLineBase",
42178
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42179
+ }
42180
+ },
41792
42181
  {
41793
42182
  "kind": "field",
41794
42183
  "name": "showGrid",
@@ -41854,6 +42243,32 @@
41854
42243
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
41855
42244
  }
41856
42245
  },
42246
+ {
42247
+ "kind": "field",
42248
+ "name": "rangeLabels",
42249
+ "type": {
42250
+ "text": "RangeLabels"
42251
+ },
42252
+ "description": "Labels kept below the 192px threshold, where the axis labels are otherwise hidden: 'y' draws min, max and (inside the range) 0 in a gutter on the y-axis side; 'x' draws the first and last x value in a bottom gutter; 'xy' both. Ignored when `hasLabelPadding` is false.",
42253
+ "inheritedFrom": {
42254
+ "name": "ObcChartLineBase",
42255
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42256
+ }
42257
+ },
42258
+ {
42259
+ "kind": "field",
42260
+ "name": "primaryYAxisId",
42261
+ "type": {
42262
+ "text": "string"
42263
+ },
42264
+ "privacy": "private",
42265
+ "description": "Id of the y scale a dataset lands on when it names none. Chart.js would\notherwise add a default `y` scale next to the configured ones and draw it.",
42266
+ "readonly": true,
42267
+ "inheritedFrom": {
42268
+ "name": "ObcChartLineBase",
42269
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42270
+ }
42271
+ },
41857
42272
  {
41858
42273
  "kind": "field",
41859
42274
  "name": "DEFAULT_TENSION",
@@ -42108,6 +42523,237 @@
42108
42523
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
42109
42524
  }
42110
42525
  },
42526
+ {
42527
+ "kind": "field",
42528
+ "name": "savedMainTickmarkLabels",
42529
+ "privacy": "private",
42530
+ "default": "new WeakMap< ExternalScaleElement, boolean >()",
42531
+ "inheritedFrom": {
42532
+ "name": "ObcChartLineBase",
42533
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42534
+ }
42535
+ },
42536
+ {
42537
+ "kind": "field",
42538
+ "name": "wasBelowThreshold",
42539
+ "type": {
42540
+ "text": "boolean | undefined"
42541
+ },
42542
+ "privacy": "private",
42543
+ "description": "Last known threshold side, to notice a crossing in `updated()`.",
42544
+ "inheritedFrom": {
42545
+ "name": "ObcChartLineBase",
42546
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42547
+ }
42548
+ },
42549
+ {
42550
+ "kind": "method",
42551
+ "name": "compactLabelThickness",
42552
+ "privacy": "private",
42553
+ "return": {
42554
+ "type": {
42555
+ "text": "number | undefined"
42556
+ }
42557
+ },
42558
+ "parameters": [
42559
+ {
42560
+ "name": "axis",
42561
+ "type": {
42562
+ "text": "'x' | 'y'"
42563
+ }
42564
+ }
42565
+ ],
42566
+ "description": "Label band a slotted scale needs for its compact labels, in the scale's\nown viewBox units. The scale prints the raw values, so those are what is\nmeasured; the x band is one text line.",
42567
+ "inheritedFrom": {
42568
+ "name": "ObcChartLineBase",
42569
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42570
+ }
42571
+ },
42572
+ {
42573
+ "kind": "field",
42574
+ "name": "rangeLabelCache",
42575
+ "type": {
42576
+ "text": "{\n y: string[];\n x: string[];\n bounds?: {min: number; max: number};\n }"
42577
+ },
42578
+ "privacy": "private",
42579
+ "default": "{y: [], x: []}",
42580
+ "description": "Range labels for the current data and size, refreshed with every\n`getChartOptions()` so the plugin never rebuilds the datasets on a draw.",
42581
+ "inheritedFrom": {
42582
+ "name": "ObcChartLineBase",
42583
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42584
+ }
42585
+ },
42586
+ {
42587
+ "kind": "method",
42588
+ "name": "slotFor",
42589
+ "privacy": "private",
42590
+ "return": {
42591
+ "type": {
42592
+ "text": "HTMLSlotElement | undefined"
42593
+ }
42594
+ },
42595
+ "parameters": [
42596
+ {
42597
+ "name": "side",
42598
+ "type": {
42599
+ "text": "'left' | 'right' | 'top' | 'bottom'"
42600
+ }
42601
+ }
42602
+ ],
42603
+ "inheritedFrom": {
42604
+ "name": "ObcChartLineBase",
42605
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42606
+ }
42607
+ },
42608
+ {
42609
+ "kind": "method",
42610
+ "name": "hasSlottedScale",
42611
+ "privacy": "private",
42612
+ "return": {
42613
+ "type": {
42614
+ "text": "boolean"
42615
+ }
42616
+ },
42617
+ "parameters": [
42618
+ {
42619
+ "name": "side",
42620
+ "type": {
42621
+ "text": "'left' | 'right' | 'top' | 'bottom'"
42622
+ }
42623
+ }
42624
+ ],
42625
+ "inheritedFrom": {
42626
+ "name": "ObcChartLineBase",
42627
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42628
+ }
42629
+ },
42630
+ {
42631
+ "kind": "field",
42632
+ "name": "ySide",
42633
+ "type": {
42634
+ "text": "'left' | 'right'"
42635
+ },
42636
+ "privacy": "private",
42637
+ "readonly": true,
42638
+ "inheritedFrom": {
42639
+ "name": "ObcChartLineBase",
42640
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42641
+ }
42642
+ },
42643
+ {
42644
+ "kind": "method",
42645
+ "name": "rangeLabelFont",
42646
+ "privacy": "private",
42647
+ "return": {
42648
+ "type": {
42649
+ "text": "RangeLabelFont"
42650
+ }
42651
+ },
42652
+ "inheritedFrom": {
42653
+ "name": "ObcChartLineBase",
42654
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42655
+ }
42656
+ },
42657
+ {
42658
+ "kind": "method",
42659
+ "name": "dataExtents",
42660
+ "privacy": "private",
42661
+ "return": {
42662
+ "type": {
42663
+ "text": "{\n x?: {min: number; max: number};\n y: Map<string, {min: number; max: number}>;\n }"
42664
+ }
42665
+ },
42666
+ "parameters": [
42667
+ {
42668
+ "name": "prepared",
42669
+ "type": {
42670
+ "text": "ReturnType<ObcChartLineBase['prepareChartDataAndLabels']>"
42671
+ }
42672
+ }
42673
+ ],
42674
+ "description": "Extent of the prepared data: one for x, one per y scale id. Each y axis\nranges from its own datasets, so a merged extent would label one axis\nwith another's values.",
42675
+ "inheritedFrom": {
42676
+ "name": "ObcChartLineBase",
42677
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42678
+ }
42679
+ },
42680
+ {
42681
+ "kind": "method",
42682
+ "name": "yRangeBounds",
42683
+ "privacy": "private",
42684
+ "return": {
42685
+ "type": {
42686
+ "text": "{min: number; max: number} | undefined"
42687
+ }
42688
+ },
42689
+ "parameters": [
42690
+ {
42691
+ "name": "extents",
42692
+ "type": {
42693
+ "text": "Map<string, {min: number; max: number}>"
42694
+ }
42695
+ }
42696
+ ],
42697
+ "description": "Range the y labels describe. Below the threshold the axis runs on data\nbounds, so its datasets' extent is exact and needs no chart to read from.",
42698
+ "inheritedFrom": {
42699
+ "name": "ObcChartLineBase",
42700
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42701
+ }
42702
+ },
42703
+ {
42704
+ "kind": "method",
42705
+ "name": "xEdgeLabelTexts",
42706
+ "privacy": "private",
42707
+ "return": {
42708
+ "type": {
42709
+ "text": "string[]"
42710
+ }
42711
+ },
42712
+ "parameters": [
42713
+ {
42714
+ "name": "prepared",
42715
+ "type": {
42716
+ "text": "ReturnType<ObcChartLineBase['prepareChartDataAndLabels']>"
42717
+ }
42718
+ },
42719
+ {
42720
+ "name": "extent",
42721
+ "type": {
42722
+ "text": "{min: number; max: number} | undefined"
42723
+ }
42724
+ }
42725
+ ],
42726
+ "description": "First and last x label, or nothing when there is no span to label.",
42727
+ "inheritedFrom": {
42728
+ "name": "ObcChartLineBase",
42729
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42730
+ }
42731
+ },
42732
+ {
42733
+ "kind": "method",
42734
+ "name": "refreshRangeLabels",
42735
+ "privacy": "private",
42736
+ "return": {
42737
+ "type": {
42738
+ "text": "typeof this.rangeLabelCache"
42739
+ }
42740
+ },
42741
+ "description": "Recompute the range labels. A side with a slotted scale is labelled by\nthe scale, so it gets no text here; the y bounds are kept regardless,\nsince the scale's compact band is measured from them.",
42742
+ "inheritedFrom": {
42743
+ "name": "ObcChartLineBase",
42744
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42745
+ }
42746
+ },
42747
+ {
42748
+ "kind": "method",
42749
+ "name": "createRangeLabelsPlugin",
42750
+ "privacy": "private",
42751
+ "description": "Paints min / 0 / max and first / last once Chart.js has drawn, inside the\ngutters `getChartOptions()` reserved. The end labels sit flush with the\nplot edges so no vertical padding is needed for them.",
42752
+ "inheritedFrom": {
42753
+ "name": "ObcChartLineBase",
42754
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42755
+ }
42756
+ },
42111
42757
  {
42112
42758
  "kind": "method",
42113
42759
  "name": "hasVisibleScale",
@@ -42235,6 +42881,44 @@
42235
42881
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
42236
42882
  }
42237
42883
  },
42884
+ {
42885
+ "kind": "method",
42886
+ "name": "resolveAxisRange",
42887
+ "privacy": "private",
42888
+ "return": {
42889
+ "type": {
42890
+ "text": "{\n min: number;\n max: number;\n }"
42891
+ }
42892
+ },
42893
+ "parameters": [
42894
+ {
42895
+ "name": "side",
42896
+ "type": {
42897
+ "text": "'left' | 'right' | 'x'"
42898
+ }
42899
+ }
42900
+ ],
42901
+ "description": "Range a slotted scale on the given side has to cover.\n\nA range pinned in the axis configuration is used as is: it is the source\nof truth, and on the first sync there is no chart to read yet. Otherwise\nthe live Chart.js scale is read under the id `buildScalesConfig()` gives\nit. A side with no axis of its own follows the first configured one.",
42902
+ "inheritedFrom": {
42903
+ "name": "ObcChartLineBase",
42904
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42905
+ }
42906
+ },
42907
+ {
42908
+ "kind": "method",
42909
+ "name": "resolveSlottedXRange",
42910
+ "privacy": "private",
42911
+ "return": {
42912
+ "type": {
42913
+ "text": "{min: number; max: number} | undefined"
42914
+ }
42915
+ },
42916
+ "description": "Range pushed to the top and bottom scales, or `undefined` to leave them\nalone. A scale prints its values verbatim, and on a `time` axis the raw\nrange is epoch milliseconds. In `minutes` display the range is converted\nto the minutes the chart's own labels show; `date` display has no numeric\nequivalent, so the scale keeps its own range.",
42917
+ "inheritedFrom": {
42918
+ "name": "ObcChartLineBase",
42919
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42920
+ }
42921
+ },
42238
42922
  {
42239
42923
  "kind": "method",
42240
42924
  "name": "updateScaleProperties",
@@ -42253,6 +42937,39 @@
42253
42937
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
42254
42938
  }
42255
42939
  },
42940
+ {
42941
+ "kind": "method",
42942
+ "name": "ownsSlottedScaleRange",
42943
+ "privacy": "protected",
42944
+ "return": {
42945
+ "type": {
42946
+ "text": "boolean"
42947
+ }
42948
+ },
42949
+ "parameters": [
42950
+ {
42951
+ "name": "_side",
42952
+ "type": {
42953
+ "text": "'left' | 'right' | 'top' | 'bottom'"
42954
+ }
42955
+ }
42956
+ ],
42957
+ "description": "Whether the chart's axis range is what a slotted scale on this side should\ndescribe. A subclass that slots a scale of its own and gives it a separate\nrange returns false for that side, otherwise the sync below overwrites it.",
42958
+ "inheritedFrom": {
42959
+ "name": "ObcChartLineBase",
42960
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42961
+ }
42962
+ },
42963
+ {
42964
+ "kind": "method",
42965
+ "name": "syncSlottedScaleRanges",
42966
+ "privacy": "private",
42967
+ "description": "Push the current axis ranges to the slotted scales. The full\n`updateScaleProperties()` cascade runs before the chart is (re)built and\nso reads the previous chart's ranges; this closes that gap once the new\nchart exists, and keeps auto-ranged scales in step with the data.",
42968
+ "inheritedFrom": {
42969
+ "name": "ObcChartLineBase",
42970
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
42971
+ }
42972
+ },
42256
42973
  {
42257
42974
  "kind": "method",
42258
42975
  "name": "hasAnyChanged",
@@ -42487,6 +43204,21 @@
42487
43204
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
42488
43205
  }
42489
43206
  },
43207
+ {
43208
+ "kind": "method",
43209
+ "name": "computeChartPadding",
43210
+ "privacy": "private",
43211
+ "return": {
43212
+ "type": {
43213
+ "text": "{\n top: number;\n right: number;\n bottom: number;\n left: number;\n }"
43214
+ }
43215
+ },
43216
+ "description": "One padding source for the Chart.js layout and the slotted scales: two\nsources painted the canvas over a scale and inset a scale inside a chart.",
43217
+ "inheritedFrom": {
43218
+ "name": "ObcChartLineBase",
43219
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
43220
+ }
43221
+ },
42490
43222
  {
42491
43223
  "kind": "method",
42492
43224
  "name": "getChartOptions",
@@ -42511,7 +43243,7 @@
42511
43243
  "text": "number | undefined"
42512
43244
  }
42513
43245
  },
42514
- "description": "Compute reference timestamp for time axis formatting.\nReturns earliest timestamp for 'date' mode, latest for 'minutes' mode.",
43246
+ "description": "Compute reference timestamp for time axis formatting.\nReturns earliest timestamp for 'date' mode, latest for 'minutes' mode.\nA pinned x range supplies the reference instead, so the axis edge stays\nthe reference while the data has not reached it yet.",
42515
43247
  "inheritedFrom": {
42516
43248
  "name": "ObcChartLineBase",
42517
43249
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
@@ -43713,6 +44445,15 @@
43713
44445
  },
43714
44446
  "default": "true"
43715
44447
  },
44448
+ {
44449
+ "kind": "field",
44450
+ "name": "showMainTickmarkLabels",
44451
+ "type": {
44452
+ "text": "boolean"
44453
+ },
44454
+ "default": "false",
44455
+ "description": "Label the main tickmarks (min / 0 / max) instead of the primary interval ladder, for a scale too short for a ladder. A chart sets it on its slotted scales below its label threshold."
44456
+ },
43716
44457
  {
43717
44458
  "kind": "field",
43718
44459
  "name": "hasBar",
@@ -44387,6 +45128,15 @@
44387
45128
  },
44388
45129
  "default": "true"
44389
45130
  },
45131
+ {
45132
+ "kind": "field",
45133
+ "name": "showMainTickmarkLabels",
45134
+ "type": {
45135
+ "text": "boolean"
45136
+ },
45137
+ "default": "false",
45138
+ "description": "Label the main tickmarks (min / 0 / max) instead of the primary interval ladder, for a scale too short for a ladder. A chart sets it on its slotted scales below its label threshold."
45139
+ },
44390
45140
  {
44391
45141
  "kind": "field",
44392
45142
  "name": "hasBar",
@@ -45075,6 +45825,15 @@
45075
45825
  "default": "undefined",
45076
45826
  "description": "Multiple y-axis definitions for complex multi-axis charts."
45077
45827
  },
45828
+ {
45829
+ "kind": "field",
45830
+ "name": "xAxis",
45831
+ "type": {
45832
+ "text": "ChartLineXAxisConfig | undefined"
45833
+ },
45834
+ "default": "undefined",
45835
+ "description": "Pinned x range (`min`/`max`) for time and number axes. Without it the axis spans exactly the data, so a window that is still filling stretches across the full width. In `minutes` display `max` is the `0min` reference."
45836
+ },
45078
45837
  {
45079
45838
  "kind": "field",
45080
45839
  "name": "showGrid",
@@ -45120,6 +45879,24 @@
45120
45879
  "default": "true",
45121
45880
  "description": "Reserve canvas padding for axis tick labels on sides without an external scale.\n\nWhen `true` (default), the chart leaves room for tick labels and renders them\n(subject to `showTickMarks` and the 192px auto-compact threshold).\n\nWhen `false`, the chart renders edge-to-edge on sides without a slotted external\nscale AND axis tick labels are force-hidden so they cannot be clipped. This also\nsuppresses the automatic edge-to-edge switch that normally happens below the\n192px threshold, eliminating the visible padding \"jump\" when crossing it.\n\nUseful when embedding the chart inside another component that owns framing\nand never wants to show axis labels (e.g. `obc-automation-tank`).\n\nDefaults to `true` to preserve existing behavior. Declared with `attribute: false`\nbecause a `true`-default boolean cannot work as an HTML boolean attribute."
45122
45881
  },
45882
+ {
45883
+ "kind": "field",
45884
+ "name": "rangeLabels",
45885
+ "type": {
45886
+ "text": "RangeLabels"
45887
+ },
45888
+ "description": "Labels kept below the 192px threshold, where the axis labels are otherwise hidden: 'y' draws min, max and (inside the range) 0 in a gutter on the y-axis side; 'x' draws the first and last x value in a bottom gutter; 'xy' both. Ignored when `hasLabelPadding` is false."
45889
+ },
45890
+ {
45891
+ "kind": "field",
45892
+ "name": "primaryYAxisId",
45893
+ "type": {
45894
+ "text": "string"
45895
+ },
45896
+ "privacy": "private",
45897
+ "description": "Id of the y scale a dataset lands on when it names none. Chart.js would\notherwise add a default `y` scale next to the configured ones and draw it.",
45898
+ "readonly": true
45899
+ },
45123
45900
  {
45124
45901
  "kind": "field",
45125
45902
  "name": "DEFAULT_TENSION",
@@ -45327,6 +46104,185 @@
45327
46104
  },
45328
46105
  "description": "Should multi-series datasets be stacked?\nLine graph returns false, area graph returns stacked property value.\nMust be implemented in subclass."
45329
46106
  },
46107
+ {
46108
+ "kind": "field",
46109
+ "name": "savedMainTickmarkLabels",
46110
+ "privacy": "private",
46111
+ "default": "new WeakMap< ExternalScaleElement, boolean >()"
46112
+ },
46113
+ {
46114
+ "kind": "field",
46115
+ "name": "wasBelowThreshold",
46116
+ "type": {
46117
+ "text": "boolean | undefined"
46118
+ },
46119
+ "privacy": "private",
46120
+ "description": "Last known threshold side, to notice a crossing in `updated()`."
46121
+ },
46122
+ {
46123
+ "kind": "method",
46124
+ "name": "compactLabelThickness",
46125
+ "privacy": "private",
46126
+ "return": {
46127
+ "type": {
46128
+ "text": "number | undefined"
46129
+ }
46130
+ },
46131
+ "parameters": [
46132
+ {
46133
+ "name": "axis",
46134
+ "type": {
46135
+ "text": "'x' | 'y'"
46136
+ }
46137
+ }
46138
+ ],
46139
+ "description": "Label band a slotted scale needs for its compact labels, in the scale's\nown viewBox units. The scale prints the raw values, so those are what is\nmeasured; the x band is one text line."
46140
+ },
46141
+ {
46142
+ "kind": "field",
46143
+ "name": "rangeLabelCache",
46144
+ "type": {
46145
+ "text": "{\n y: string[];\n x: string[];\n bounds?: {min: number; max: number};\n }"
46146
+ },
46147
+ "privacy": "private",
46148
+ "default": "{y: [], x: []}",
46149
+ "description": "Range labels for the current data and size, refreshed with every\n`getChartOptions()` so the plugin never rebuilds the datasets on a draw."
46150
+ },
46151
+ {
46152
+ "kind": "method",
46153
+ "name": "slotFor",
46154
+ "privacy": "private",
46155
+ "return": {
46156
+ "type": {
46157
+ "text": "HTMLSlotElement | undefined"
46158
+ }
46159
+ },
46160
+ "parameters": [
46161
+ {
46162
+ "name": "side",
46163
+ "type": {
46164
+ "text": "'left' | 'right' | 'top' | 'bottom'"
46165
+ }
46166
+ }
46167
+ ]
46168
+ },
46169
+ {
46170
+ "kind": "method",
46171
+ "name": "hasSlottedScale",
46172
+ "privacy": "private",
46173
+ "return": {
46174
+ "type": {
46175
+ "text": "boolean"
46176
+ }
46177
+ },
46178
+ "parameters": [
46179
+ {
46180
+ "name": "side",
46181
+ "type": {
46182
+ "text": "'left' | 'right' | 'top' | 'bottom'"
46183
+ }
46184
+ }
46185
+ ]
46186
+ },
46187
+ {
46188
+ "kind": "field",
46189
+ "name": "ySide",
46190
+ "type": {
46191
+ "text": "'left' | 'right'"
46192
+ },
46193
+ "privacy": "private",
46194
+ "readonly": true
46195
+ },
46196
+ {
46197
+ "kind": "method",
46198
+ "name": "rangeLabelFont",
46199
+ "privacy": "private",
46200
+ "return": {
46201
+ "type": {
46202
+ "text": "RangeLabelFont"
46203
+ }
46204
+ }
46205
+ },
46206
+ {
46207
+ "kind": "method",
46208
+ "name": "dataExtents",
46209
+ "privacy": "private",
46210
+ "return": {
46211
+ "type": {
46212
+ "text": "{\n x?: {min: number; max: number};\n y: Map<string, {min: number; max: number}>;\n }"
46213
+ }
46214
+ },
46215
+ "parameters": [
46216
+ {
46217
+ "name": "prepared",
46218
+ "type": {
46219
+ "text": "ReturnType<ObcChartLineBase['prepareChartDataAndLabels']>"
46220
+ }
46221
+ }
46222
+ ],
46223
+ "description": "Extent of the prepared data: one for x, one per y scale id. Each y axis\nranges from its own datasets, so a merged extent would label one axis\nwith another's values."
46224
+ },
46225
+ {
46226
+ "kind": "method",
46227
+ "name": "yRangeBounds",
46228
+ "privacy": "private",
46229
+ "return": {
46230
+ "type": {
46231
+ "text": "{min: number; max: number} | undefined"
46232
+ }
46233
+ },
46234
+ "parameters": [
46235
+ {
46236
+ "name": "extents",
46237
+ "type": {
46238
+ "text": "Map<string, {min: number; max: number}>"
46239
+ }
46240
+ }
46241
+ ],
46242
+ "description": "Range the y labels describe. Below the threshold the axis runs on data\nbounds, so its datasets' extent is exact and needs no chart to read from."
46243
+ },
46244
+ {
46245
+ "kind": "method",
46246
+ "name": "xEdgeLabelTexts",
46247
+ "privacy": "private",
46248
+ "return": {
46249
+ "type": {
46250
+ "text": "string[]"
46251
+ }
46252
+ },
46253
+ "parameters": [
46254
+ {
46255
+ "name": "prepared",
46256
+ "type": {
46257
+ "text": "ReturnType<ObcChartLineBase['prepareChartDataAndLabels']>"
46258
+ }
46259
+ },
46260
+ {
46261
+ "name": "extent",
46262
+ "type": {
46263
+ "text": "{min: number; max: number} | undefined"
46264
+ }
46265
+ }
46266
+ ],
46267
+ "description": "First and last x label, or nothing when there is no span to label."
46268
+ },
46269
+ {
46270
+ "kind": "method",
46271
+ "name": "refreshRangeLabels",
46272
+ "privacy": "private",
46273
+ "return": {
46274
+ "type": {
46275
+ "text": "typeof this.rangeLabelCache"
46276
+ }
46277
+ },
46278
+ "description": "Recompute the range labels. A side with a slotted scale is labelled by\nthe scale, so it gets no text here; the y bounds are kept regardless,\nsince the scale's compact band is measured from them."
46279
+ },
46280
+ {
46281
+ "kind": "method",
46282
+ "name": "createRangeLabelsPlugin",
46283
+ "privacy": "private",
46284
+ "description": "Paints min / 0 / max and first / last once Chart.js has drawn, inside the\ngutters `getChartOptions()` reserved. The end labels sit flush with the\nplot edges so no vertical padding is needed for them."
46285
+ },
45330
46286
  {
45331
46287
  "kind": "method",
45332
46288
  "name": "hasVisibleScale",
@@ -45414,6 +46370,36 @@
45414
46370
  "privacy": "private",
45415
46371
  "description": "Calculate chart padding from external scale dimensions.\n\nFor sides without an external scale, falls back to `CHART_DIMENSIONS.CANVAS_PADDING`\n(the chart's own padding to reserve room for axis tick labels). When\n`hasLabelPadding=false` that fallback is 0 so the chart renders edge-to-edge,\nand this same value is cascaded to slotted scales via `updateScaleProperties()`\n— so the bar's main-axis padding (`paddingTop`/`paddingBottom` for vertical bars)\ncollapses with the chart's perpendicular padding and they stay visually aligned."
45416
46372
  },
46373
+ {
46374
+ "kind": "method",
46375
+ "name": "resolveAxisRange",
46376
+ "privacy": "private",
46377
+ "return": {
46378
+ "type": {
46379
+ "text": "{\n min: number;\n max: number;\n }"
46380
+ }
46381
+ },
46382
+ "parameters": [
46383
+ {
46384
+ "name": "side",
46385
+ "type": {
46386
+ "text": "'left' | 'right' | 'x'"
46387
+ }
46388
+ }
46389
+ ],
46390
+ "description": "Range a slotted scale on the given side has to cover.\n\nA range pinned in the axis configuration is used as is: it is the source\nof truth, and on the first sync there is no chart to read yet. Otherwise\nthe live Chart.js scale is read under the id `buildScalesConfig()` gives\nit. A side with no axis of its own follows the first configured one."
46391
+ },
46392
+ {
46393
+ "kind": "method",
46394
+ "name": "resolveSlottedXRange",
46395
+ "privacy": "private",
46396
+ "return": {
46397
+ "type": {
46398
+ "text": "{min: number; max: number} | undefined"
46399
+ }
46400
+ },
46401
+ "description": "Range pushed to the top and bottom scales, or `undefined` to leave them\nalone. A scale prints its values verbatim, and on a `time` axis the raw\nrange is epoch milliseconds. In `minutes` display the range is converted\nto the minutes the chart's own labels show; `date` display has no numeric\nequivalent, so the scale keeps its own range."
46402
+ },
45417
46403
  {
45418
46404
  "kind": "method",
45419
46405
  "name": "updateScaleProperties",
@@ -45428,6 +46414,31 @@
45428
46414
  ],
45429
46415
  "description": "Update properties on slotted scale elements"
45430
46416
  },
46417
+ {
46418
+ "kind": "method",
46419
+ "name": "ownsSlottedScaleRange",
46420
+ "privacy": "protected",
46421
+ "return": {
46422
+ "type": {
46423
+ "text": "boolean"
46424
+ }
46425
+ },
46426
+ "parameters": [
46427
+ {
46428
+ "name": "_side",
46429
+ "type": {
46430
+ "text": "'left' | 'right' | 'top' | 'bottom'"
46431
+ }
46432
+ }
46433
+ ],
46434
+ "description": "Whether the chart's axis range is what a slotted scale on this side should\ndescribe. A subclass that slots a scale of its own and gives it a separate\nrange returns false for that side, otherwise the sync below overwrites it."
46435
+ },
46436
+ {
46437
+ "kind": "method",
46438
+ "name": "syncSlottedScaleRanges",
46439
+ "privacy": "private",
46440
+ "description": "Push the current axis ranges to the slotted scales. The full\n`updateScaleProperties()` cascade runs before the chart is (re)built and\nso reads the previous chart's ranges; this closes that gap once the new\nchart exists, and keeps auto-ranged scales in step with the data."
46441
+ },
45431
46442
  {
45432
46443
  "kind": "method",
45433
46444
  "name": "hasAnyChanged",
@@ -45606,6 +46617,17 @@
45606
46617
  "privacy": "protected",
45607
46618
  "description": "Prepare datasets for single-series charts.\nCategory mode: labels + numeric values (unchanged legacy path).\nTime/number mode: normalized {x, y} points on a linear scale.\nHandles both regular and threshold fill modes."
45608
46619
  },
46620
+ {
46621
+ "kind": "method",
46622
+ "name": "computeChartPadding",
46623
+ "privacy": "private",
46624
+ "return": {
46625
+ "type": {
46626
+ "text": "{\n top: number;\n right: number;\n bottom: number;\n left: number;\n }"
46627
+ }
46628
+ },
46629
+ "description": "One padding source for the Chart.js layout and the slotted scales: two\nsources painted the canvas over a scale and inset a scale inside a chart."
46630
+ },
45609
46631
  {
45610
46632
  "kind": "method",
45611
46633
  "name": "getChartOptions",
@@ -45626,7 +46648,7 @@
45626
46648
  "text": "number | undefined"
45627
46649
  }
45628
46650
  },
45629
- "description": "Compute reference timestamp for time axis formatting.\nReturns earliest timestamp for 'date' mode, latest for 'minutes' mode."
46651
+ "description": "Compute reference timestamp for time axis formatting.\nReturns earliest timestamp for 'date' mode, latest for 'minutes' mode.\nA pinned x range supplies the reference instead, so the axis edge stays\nthe reference while the data has not reached it yet."
45630
46652
  },
45631
46653
  {
45632
46654
  "kind": "method",
@@ -47738,7 +48760,7 @@
47738
48760
  "declarations": [
47739
48761
  {
47740
48762
  "kind": "class",
47741
- "description": "`<obc-alert-button>` – A compact, icon-based button for displaying alert or notification status and count.\n\nThis component provides a visual indicator for active alerts or notifications, supporting different alert types (such as alarm, warning, or caution) and an optional counter badge. It can also include a secondary \"silence\" action button for muting alerts, adapting its layout responsively for different screen widths.\n\nAppears in toolbars or notification areas to give users a quick overview of alert status and provide direct access to alert-related actions.\n\n## Features\n\n- **Variants:**\n - **Flat:** Minimal, icon-only button for space-constrained layouts.\n - **Normal:** Standard button with icon and optional alert counter.\n - **Enhanced:** Visually prominent button for high-priority alerts, with accent styling.\n- **Alert Types:** Supports `alarm`, `warning`, `caution`, or no alert (idle). Icon and color adapt to the alert type.\n- **Alert Counter:** Optional badge displays the number of active alerts (except in flat mode).\n- **Silence Button:** Optional secondary button to mute or silence alerts, shown when enabled and at sufficient width.\n- **Blinking Animation:** Can animate (blink) to draw attention to active alerts (except for caution type).\n- **Responsive Layout:** Automatically switches to flat mode below a configurable width, and hides the silence button below another configurable width.\n- **Large Size Option:** Increases button height and padding for touch-friendly or prominent use.\n\n## Usage Guidelines\n\nUse `<obc-alert-button>` to provide a persistent, easily accessible indicator of alert or notification status. Ideal for toolbars, headers, or notification panels where users need to be aware of active alerts and may need to silence them quickly.\n\n- Use the counter to show the number of active alerts when relevant.\n- Use the silence button to provide a quick mute action, but only when space allows.\n- Choose the variant (`flat`, `normal`, `enhanced`) based on available space and the importance of the alert.\n- The blinking feature should be reserved for urgent or high-priority alerts to avoid unnecessary distraction.\n\n**TODO(designer):** Confirm if there are recommended default behaviors for auto-blinking, and if there are any design constraints for when to use each variant.\n\n## Properties\n\n- `nAlerts` (number): Number of active alerts to display in the counter badge.\n- `alertType` (`AlertType`): Type of alert (`alarm`, `warning`, `caution`, or undefined for idle).\n- `type` (`ObcAlertButtonType`): Visual variant of the button (`flat`, `normal`, `enhanced`). Default is `normal`.\n- `counter` (boolean): Whether to show the alert counter badge (not available in flat mode).\n- `showSilenceButton` (boolean): Whether to display the silence button (hidden in flat mode or below min breakpoint).\n- `silenceButtonDisabled` (boolean): Disables the silence button when true.\n- `flatMaxBreakpointPx` (number): Maximum width (in px) for normal/enhanced mode; below this, switches to flat mode.\n- `silenceButtonMinBreakpointPx` (number): Minimum width (in px) to show the silence button; below this, it is hidden.\n- `blinking` (boolean): Enables blinking animation for active alerts (not for caution type).\n- `large` (boolean): Increases button size for prominent or touch-friendly use.\n\n## Events\n\n- `click-alert` – Fired when the main alert button is clicked.\n- `click-silence` – Fired when the silence button is clicked.\n\n## Best Practices & Constraints\n\n- Only show the counter when there are active alerts and the button is not in flat mode.\n- The silence button is only visible if enabled, the width is above the minimum breakpoint, and the button is not flat.\n- Blinking should be used sparingly to avoid overwhelming the user.\n- Use the large variant for touch interfaces or when the button needs to stand out.\n\n## Example\n\n```html\n<obc-alert-button\n nAlerts=\"3\"\n alertType=\"alarm\"\n type=\"enhanced\"\n counter\n showSilenceButton\n blinking\n large\n flatMaxBreakpointPx=\"600\"\n silenceButtonMinBreakpointPx=\"600\"\n></obc-alert-button>\n```\n\nIn this example, the button shows an alarm icon, a counter badge with \"3\", is styled as enhanced, blinks to indicate urgency, and includes a silence button if the width allows.",
48763
+ "description": "`<obc-alert-button>` – A compact, icon-based button for displaying alert or notification status and count.\n\nThis component provides a visual indicator for active alerts or notifications, supporting different alert types (such as alarm, warning, or caution) and an optional counter badge. It can also include a secondary \"silence\" action button for muting alerts, adapting its layout responsively for different screen widths.\n\nAppears in toolbars or notification areas to give users a quick overview of alert status and provide direct access to alert-related actions.\n\n## Features\n\n- **Variants:**\n - **Flat:** Minimal, icon-only button for space-constrained layouts.\n - **Normal:** Standard button with icon and optional alert counter.\n - **Enhanced:** Visually prominent button for high-priority alerts, with accent styling.\n- **Alert Types:** Supports `alarm`, `warning`, `caution`, or no alert (idle). Icon and color adapt to the alert type.\n- **Alert Counter:** Optional badge displays the number of active alerts (except in flat mode).\n- **Silence Button:** Optional secondary button to mute or silence alerts, shown when enabled and at sufficient width.\n- **Blinking Animation:** Can animate (blink) to draw attention to active alerts (except for caution type).\n- **Responsive Layout:** Automatically switches to flat mode below a configurable width, and hides the silence button below another configurable width.\n- **Large Size Option:** Increases button height and padding for touch-friendly or prominent use.\n\n## Usage Guidelines\n\nUse `<obc-alert-button>` to provide a persistent, easily accessible indicator of alert or notification status. Ideal for toolbars, headers, or notification panels where users need to be aware of active alerts and may need to silence them quickly.\n\n- Use the counter to show the number of active alerts when relevant.\n- Use the silence button to provide a quick mute action, but only when space allows.\n- Choose the variant (`flat`, `normal`, `enhanced`) based on available space and the importance of the alert.\n- The blinking feature should be reserved for urgent or high-priority alerts to avoid unnecessary distraction.\n\n**TODO(designer):** Confirm if there are recommended default behaviors for auto-blinking, and if there are any design constraints for when to use each variant.\n\n## Properties\n\n- `nAlerts` (number): Number of active alerts to display in the counter badge.\n- `alertType` (`AlertType`): Type of alert (`alarm`, `warning`, `caution`, or undefined for idle).\n- `type` (`ObcAlertButtonType`): Visual variant of the button (`flat`, `normal`, `enhanced`). Default is `normal`.\n- `counter` (boolean): Whether to show the alert counter badge (not available in flat mode).\n- `showSilenceButton` (boolean): Whether to display the silence button (hidden in flat mode or below min breakpoint).\n- `silenceButtonDisabled` (boolean): Disables the silence button when true.\n- `flatMaxBreakpointPx` (number): Maximum width (in px) for normal/enhanced mode; below this, switches to flat mode.\n- `silenceButtonMinBreakpointPx` (number): Minimum width (in px) to show the silence button; below this, it is hidden.\n- `blinking` (boolean): Enables blinking animation for active alerts (not for caution type).\n- `flashingSpeed`: tempo of the blink (`default` follows the alert type: critical/alarm/high fast, warning/medium slow, low very slow; `fixed` never flashes).\n- `large` (boolean): Increases button size for prominent or touch-friendly use.\n\n## Events\n\n- `click-alert` – Fired when the main alert button is clicked.\n- `click-silence` – Fired when the silence button is clicked.\n\n## Best Practices & Constraints\n\n- Only show the counter when there are active alerts and the button is not in flat mode.\n- The silence button is only visible if enabled, the width is above the minimum breakpoint, and the button is not flat.\n- Blinking should be used sparingly to avoid overwhelming the user.\n- Use the large variant for touch interfaces or when the button needs to stand out.\n\n## Example\n\n```html\n<obc-alert-button\n nAlerts=\"3\"\n alertType=\"alarm\"\n type=\"enhanced\"\n counter\n showSilenceButton\n blinking\n large\n flatMaxBreakpointPx=\"600\"\n silenceButtonMinBreakpointPx=\"600\"\n></obc-alert-button>\n```\n\nIn this example, the button shows an alarm icon, a counter badge with \"3\", is styled as enhanced, blinks to indicate urgency, and includes a silence button if the width allows.",
47742
48764
  "name": "ObcAlertButton",
47743
48765
  "slots": [
47744
48766
  {
@@ -47814,6 +48836,29 @@
47814
48836
  "default": "false",
47815
48837
  "description": "Enables blinking animation for active alerts.\n\nBlinking is only shown for alarm and warning types, not for caution."
47816
48838
  },
48839
+ {
48840
+ "kind": "field",
48841
+ "name": "flashingSpeed",
48842
+ "type": {
48843
+ "text": "FlashingSpeed"
48844
+ },
48845
+ "description": "Flash tempo while `blinking` is on: `default` resolves from `alertType`, `fast`, `slow`, `very-slow` force a tempo, `fixed` never flashes."
48846
+ },
48847
+ {
48848
+ "kind": "field",
48849
+ "name": "flashing",
48850
+ "privacy": "protected",
48851
+ "readonly": true,
48852
+ "default": "new FlashingController( this, () => this.resolvedFlashingSpeed )"
48853
+ },
48854
+ {
48855
+ "kind": "field",
48856
+ "name": "resolvedFlashingSpeed",
48857
+ "type": {
48858
+ "text": "ResolvedFlashingSpeed"
48859
+ },
48860
+ "readonly": true
48861
+ },
47817
48862
  {
47818
48863
  "kind": "field",
47819
48864
  "name": "flatMaxBreakpointPx",
@@ -47875,23 +48920,6 @@
47875
48920
  "privacy": "private",
47876
48921
  "readonly": true
47877
48922
  },
47878
- {
47879
- "kind": "field",
47880
- "name": "_blinkAnimationCancel",
47881
- "type": {
47882
- "text": "() => void | undefined"
47883
- },
47884
- "privacy": "private"
47885
- },
47886
- {
47887
- "kind": "method",
47888
- "name": "installBlinking",
47889
- "privacy": "private"
47890
- },
47891
- {
47892
- "kind": "method",
47893
- "name": "updated"
47894
- },
47895
48923
  {
47896
48924
  "kind": "method",
47897
48925
  "name": "render"
@@ -48077,7 +49105,7 @@
48077
49105
  "declarations": [
48078
49106
  {
48079
49107
  "kind": "class",
48080
- "description": "`<obc-alert-frame>` – A bordered frame component for visually emphasizing alert or status messages.\n\nThe alert frame provides a prominent outline and optional \"flap\" with status icon to draw attention to critical information or UI regions. It supports multiple visual variants and thicknesses, and can be configured to indicate different alert statuses (alarm, warning, caution). The component is designed to wrap content and visually distinguish it from the surrounding UI.\n\n### Features\n- **Variants (type):**\n - `regular`: Standard outlined frame with no flap or icon.\n - `small-side-flip`: Adds a small side flap with a status icon at the top right.\n - `large-side-flip`: Adds a larger, vertical side flap with a status icon and optional custom icon.\n - `bottom-flip`: Adds a bottom flap with a status icon, label, and timer slots.\n- **Thickness options:** Choose between `small` (thin border) and `large` (thick border) for visual emphasis.\n- **Status indication:** Displays different color schemes and icons for the legacy statuses (`alarm`, `warning`, `caution`) and the level statuses (`level-critical`, `level-high`, `level-medium`, `level-low`, `level-diagnostic`).\n- **Acknowledgement mode:** The `mode` property reflects the alert lifecycle state — `acked-active` (default), `unacked-active`, and `unacked-rectified` driving the blinking/animation treatment of the frame.\n- **Content wrapping:** When `wrapContent` is true, the frame wraps and sizes itself to its slotted content rather than overlaying a fixed region.\n- **Customizable corners:** Each corner can be set to a sharp (non-rounded) edge for integration with other UI elements.\n- **Slot-based content:** Supports custom icons, labels, and timers in flap variants via named slots.\n\n### Usage Guidelines\nUse `obc-alert-frame` to highlight important content, such as alerts, warnings, or status panels. The flap variants are ideal for drawing extra attention to urgent or time-sensitive information, while the regular type provides a subtle but clear border. Choose the status to match the severity of the message (alarm, warning, caution). Adjust thickness for visual hierarchy—use large for high-priority alerts and small for less critical notices.\n\n**TODO(designer):** Provide guidance on when to use each flap type (small-side-flip, large-side-flip, bottom-flip) and recommended scenarios for sharp edge usage.\n\n### Features/Variants\n- **Type (Visual Variant):**\n - `regular`: Simple outlined frame, no flap or icon.\n - `small-side-flip`: Small right-side flap with status icon.\n - `large-side-flip`: Large vertical right-side flap with status icon and optional custom icon.\n - `bottom-flip`: Bottom flap with status icon, label, and timer.\n- **Thickness:** `small` (default) or `large` for border width.\n- **Status:** `alarm`, `warning`, `caution`, or the level severities (`level-critical`, `level-high`, `level-medium`, `level-low`, `level-diagnostic`)—affects color and icon.\n- **Mode:** `acked-active` (default), `unacked-active`, or `unacked-rectified`—affects blinking/animation.\n- **Corner Customization:** Each corner can be made sharp (not rounded) via boolean properties.\n\n### Slots and Content Structure\n\n| Slot Name | Renders When... | Purpose |\n|-----------|-----------------|---------|\n| (default) | Always | Main content inside the alert frame. |\n| icon | `large-side-flip` or `bottom-flip` | Custom icon displayed in the flap (in addition to status icon). |\n| label | `bottom-flip` only | Label text shown in the bottom flap. |\n| timer | `bottom-flip` only | Timer or time label in the bottom flap. |\n\n### Properties and Attributes\n- `type`: Selects the visual variant/flap style. Default is `small-side-flip`.\n- `thickness`: Controls border thickness (`small` or `large`). Default is `small`.\n- `status`: Sets the alert status and color/icon (`alarm`, `warning`, `caution`, or the `level-*` severities). Default is `alarm`.\n- `mode`: Acknowledgement lifecycle state (`acked-active`, `unacked-active`, `unacked-rectified`) controlling blinking/animation. Default is `acked-active`.\n- `wrapContent`: When true, the frame wraps and sizes to its slotted content instead of overlaying a fixed region. Default is `false`.\n- `sharpEdgeTopLeft`, `sharpEdgeTopRight`, `sharpEdgeBottomLeft`, `sharpEdgeBottomRight`: Boolean flags to make each corner sharp instead of rounded.\n\n### Best Practices and Constraints\n- Use the status property to match the severity of the alert.\n- Only use the bottom-flip variant when both label and timer are relevant.\n- For visual consistency, align sharp edge settings with adjacent UI elements.\n- The default slot is for the main content; use named slots for icons, labels, and timers as needed.\n\n### Example:\n```\n<obc-alert-frame\n type=\"bottom-flip\"\n thickness=\"large\"\n status=\"warning\"\n sharpEdgeTopLeft\n>\n <obi-placeholder slot=\"icon\"></obi-placeholder>\n <div slot=\"label\">Low Battery</div>\n <div slot=\"timer\">00:15</div>\n <div>Critical system message goes here.</div>\n</obc-alert-frame>\n```",
49108
+ "description": "`<obc-alert-frame>` – A bordered frame component for visually emphasizing alert or status messages.\n\nThe alert frame provides a prominent outline and optional \"flap\" with status icon to draw attention to critical information or UI regions. It supports multiple visual variants and thicknesses, and can be configured to indicate different alert statuses (alarm, warning, caution). The component is designed to wrap content and visually distinguish it from the surrounding UI.\n\n### Features\n- **Variants (type):**\n - `regular`: Standard outlined frame with no flap or icon.\n - `small-side-flip`: Adds a small side flap with a status icon at the top right.\n - `large-side-flip`: Adds a larger, vertical side flap with a status icon and optional custom icon.\n - `bottom-flip`: Adds a bottom flap with a status icon, label, and timer slots.\n- **Thickness options:** Choose between `small` (thin border) and `large` (thick border) for visual emphasis.\n- **Status indication:** Displays different color schemes and icons for the legacy statuses (`alarm`, `warning`, `caution`) and the level statuses (`level-critical`, `level-high`, `level-medium`, `level-low`, `level-diagnostic`).\n- **Acknowledgement mode:** The `mode` property reflects the alert lifecycle state — `acked-active` (default, steady), `unacked-active` (flashes) and `unacked-rectified` (dashed, flashes) and `flashingSpeed` picks the tempo.\n- **Content wrapping:** When `wrapContent` is true, the frame wraps and sizes itself to its slotted content rather than overlaying a fixed region.\n- **Customizable corners:** Each corner can be set to a sharp (non-rounded) edge for integration with other UI elements.\n- **Slot-based content:** Supports custom icons, labels, and timers in flap variants via named slots.\n\n### Usage Guidelines\nUse `obc-alert-frame` to highlight important content, such as alerts, warnings, or status panels. The flap variants are ideal for drawing extra attention to urgent or time-sensitive information, while the regular type provides a subtle but clear border. Choose the status to match the severity of the message (alarm, warning, caution). Adjust thickness for visual hierarchy—use large for high-priority alerts and small for less critical notices.\n\n**TODO(designer):** Provide guidance on when to use each flap type (small-side-flip, large-side-flip, bottom-flip) and recommended scenarios for sharp edge usage.\n\n### Features/Variants\n- **Type (Visual Variant):**\n - `regular`: Simple outlined frame, no flap or icon.\n - `small-side-flip`: Small right-side flap with status icon.\n - `large-side-flip`: Large vertical right-side flap with status icon and optional custom icon.\n - `bottom-flip`: Bottom flap with status icon, label, and timer.\n- **Thickness:** `small` (default) or `large` for border width.\n- **Status:** `alarm`, `warning`, `caution`, or the level severities (`level-critical`, `level-high`, `level-medium`, `level-low`, `level-diagnostic`)—affects color and icon.\n- **Mode:** `acked-active` (default), `unacked-active`, or `unacked-rectified`—affects blinking/animation.\n- **Corner Customization:** Each corner can be made sharp (not rounded) via boolean properties.\n\n### Slots and Content Structure\n\n| Slot Name | Renders When... | Purpose |\n|-----------|-----------------|---------|\n| (default) | Always | Main content inside the alert frame. |\n| icon | `large-side-flip` or `bottom-flip` | Custom icon displayed in the flap (in addition to status icon). |\n| label | `bottom-flip` only | Label text shown in the bottom flap. |\n| timer | `bottom-flip` only | Timer or time label in the bottom flap. |\n\n### Properties and Attributes\n- `type`: Selects the visual variant/flap style. Default is `small-side-flip`.\n- `thickness`: Controls border thickness (`small` or `large`). Default is `small`.\n- `status`: Sets the alert status and color/icon (`alarm`, `warning`, `caution`, or the `level-*` severities). Default is `alarm`.\n- `mode`: Acknowledgement lifecycle state (`acked-active`, `unacked-active`, `unacked-rectified`) controlling blinking/animation. Default is `acked-active`.\n- `wrapContent`: When true, the frame wraps and sizes to its slotted content instead of overlaying a fixed region. Default is `false`.\n- `sharpEdgeTopLeft`, `sharpEdgeTopRight`, `sharpEdgeBottomLeft`, `sharpEdgeBottomRight`: Boolean flags to make each corner sharp instead of rounded.\n\n### Best Practices and Constraints\n- Use the status property to match the severity of the alert.\n- Only use the bottom-flip variant when both label and timer are relevant.\n- For visual consistency, align sharp edge settings with adjacent UI elements.\n- The default slot is for the main content; use named slots for icons, labels, and timers as needed.\n\n### Example:\n```\n<obc-alert-frame\n type=\"bottom-flip\"\n thickness=\"large\"\n status=\"warning\"\n sharpEdgeTopLeft\n>\n <obi-placeholder slot=\"icon\"></obi-placeholder>\n <div slot=\"label\">Low Battery</div>\n <div slot=\"timer\">00:15</div>\n <div>Critical system message goes here.</div>\n</obc-alert-frame>\n```",
48081
49109
  "name": "ObcAlertFrame",
48082
49110
  "slots": [
48083
49111
  {
@@ -48130,6 +49158,80 @@
48130
49158
  },
48131
49159
  "description": "Acknowledgement lifecycle state, controlling the frame's blinking/animation.\n- `acked-active`: Active and acknowledged (default); no blinking.\n- `unacked-active`: Active and not yet acknowledged; blinks.\n- `unacked-rectified`: Condition cleared but not yet acknowledged."
48132
49160
  },
49161
+ {
49162
+ "kind": "field",
49163
+ "name": "flashingSpeed",
49164
+ "type": {
49165
+ "text": "FlashingSpeed"
49166
+ },
49167
+ "description": "Flash tempo: `default` resolves from the alert type and mode (critical/alarm/high fast, warning/medium slow, low very slow, every rectified alert very slow, caution and diagnostic fixed), `fast`, `slow`, `very-slow` force a tempo, `fixed` never flashes. Acknowledged frames are always steady."
49168
+ },
49169
+ {
49170
+ "kind": "field",
49171
+ "name": "flashEffect",
49172
+ "type": {
49173
+ "text": "ObcAlertFrameFlashEffect"
49174
+ },
49175
+ "description": "`outline` (default) grows the outline by 2 px while on in one step; `outline-eased` grows it over a 50 ms transition, a design-evaluation option."
49176
+ },
49177
+ {
49178
+ "kind": "field",
49179
+ "name": "flashing",
49180
+ "privacy": "protected",
49181
+ "readonly": true,
49182
+ "default": "new FlashingController( this, () => this.resolvedFlashingSpeed )"
49183
+ },
49184
+ {
49185
+ "kind": "field",
49186
+ "name": "dashBox",
49187
+ "type": {
49188
+ "text": "DashBox | undefined"
49189
+ },
49190
+ "privacy": "private"
49191
+ },
49192
+ {
49193
+ "kind": "field",
49194
+ "name": "wrapper",
49195
+ "type": {
49196
+ "text": "HTMLElement | undefined"
49197
+ },
49198
+ "privacy": "private"
49199
+ },
49200
+ {
49201
+ "kind": "field",
49202
+ "name": "dashObserver",
49203
+ "type": {
49204
+ "text": "ResizeObserver | undefined"
49205
+ },
49206
+ "privacy": "private"
49207
+ },
49208
+ {
49209
+ "kind": "field",
49210
+ "name": "measureDash",
49211
+ "privacy": "private"
49212
+ },
49213
+ {
49214
+ "kind": "method",
49215
+ "name": "updated"
49216
+ },
49217
+ {
49218
+ "kind": "method",
49219
+ "name": "renderDash",
49220
+ "privacy": "private",
49221
+ "return": {
49222
+ "type": {
49223
+ "text": "TemplateResult | typeof nothing"
49224
+ }
49225
+ }
49226
+ },
49227
+ {
49228
+ "kind": "field",
49229
+ "name": "resolvedFlashingSpeed",
49230
+ "type": {
49231
+ "text": "ResolvedFlashingSpeed"
49232
+ },
49233
+ "readonly": true
49234
+ },
48133
49235
  {
48134
49236
  "kind": "field",
48135
49237
  "name": "wrapContent",
@@ -48211,23 +49313,6 @@
48211
49313
  "kind": "method",
48212
49314
  "name": "render"
48213
49315
  },
48214
- {
48215
- "kind": "field",
48216
- "name": "_blinkAnimationCancel",
48217
- "type": {
48218
- "text": "() => void | undefined"
48219
- },
48220
- "privacy": "private"
48221
- },
48222
- {
48223
- "kind": "method",
48224
- "name": "syncBlinking",
48225
- "privacy": "private"
48226
- },
48227
- {
48228
- "kind": "method",
48229
- "name": "updated"
48230
- },
48231
49316
  {
48232
49317
  "kind": "method",
48233
49318
  "name": "flap",
@@ -48289,7 +49374,7 @@
48289
49374
  "declarations": [
48290
49375
  {
48291
49376
  "kind": "class",
48292
- "description": "`obc-alert-icon` – Animated alert icon for signaling alarm and warning states.\n\nDisplays a specialized icon with a blinking effect to visually indicate alert or warning conditions. The icon type is determined by the `name` property, supporting both alarm and warning variants in different states (such as silenced, unacknowledged, or rectified).\n\nAppears in notification areas, alert banners, or status panels to draw attention to active or historical alert conditions. The blinking animation helps highlight urgency or status changes without requiring user interaction.\n\n## Features\n- **Multiple Icon Types:** Supports all alarm and warning types.\n- **Blinking Animation:** Uses two SVG layers with alternating opacity to create a blinking effect, visually emphasizing the alert or warning state.\n- **Adaptive Styling:** Applies different CSS variables for alarm and warning types to allow for distinct visual cues (e.g., color, blink timing).\n- **Scalable:** Designed to fit any container size; scales with its parent element.\n\n## Usage Guidelines\nUse `obc-alert-icon` to visually represent the status of alarms or warnings in notification panels, alert lists, or system status areas. The blinking effect is intended to draw user attention to active or recent alert conditions. Select the appropriate `name` value to match the alert state you wish to indicate.\n\n**TODO(designer):** Provide guidance on when to use each icon variant (e.g., when to use \"rectified\" vs. \"silenced\"), and any best practices for icon placement or blink timing.\n\n## Properties\n- `type` (AlertType): The type of alarm to display.\n- `status` (AlarmStatus): The status of the alarm to display.\n\n## Best Practices\n- Only use the blinking alert icon for states that require immediate or prominent user attention.\n- Ensure the icon is sized appropriately for its context (e.g., 32x32px or 64x64px for status panels).\n- Avoid using multiple blinking icons in close proximity to prevent visual overload.\n\n## Example\n```html\n<obc-alert-icon .alarmType=${alarm.type} .alarmStatus=${alarm.status}></obc-alert-icon>\n```",
49377
+ "description": "`obc-alert-icon` – Animated alert icon for signaling alarm and warning states.\n\nDisplays a specialized icon with a blinking effect to visually indicate alert or warning conditions. The icon type is determined by the `name` property, supporting both alarm and warning variants in different states (such as silenced, unacknowledged, or rectified).\n\nAppears in notification areas, alert banners, or status panels to draw attention to active or historical alert conditions. The blinking animation helps highlight urgency or status changes without requiring user interaction.\n\n## Features\n- **Multiple Icon Types:** Supports all alarm and warning types.\n- **Flashing:** Two SVG layers alternate at the design tempo (fast for critical/alarm/high, slow for warning/medium, very slow for low and for every rectified alert); acknowledged icons are steady.\n- **Adaptive Styling:** Applies different CSS variables for alarm and warning types to allow for distinct visual cues (e.g., color, blink timing).\n- **Scalable:** Designed to fit any container size; scales with its parent element.\n\n## Usage Guidelines\nUse `obc-alert-icon` to visually represent the status of alarms or warnings in notification panels, alert lists, or system status areas. The blinking effect is intended to draw user attention to active or recent alert conditions. Select the appropriate `name` value to match the alert state you wish to indicate.\n\n**TODO(designer):** Provide guidance on when to use each icon variant (e.g., when to use \"rectified\" vs. \"silenced\"), and any best practices for icon placement or blink timing.\n\n## Properties\n- `type` (AlertType): The type of alarm to display.\n- `status` (AlarmStatus): The status of the alarm to display.\n\n## Best Practices\n- Only use the blinking alert icon for states that require immediate or prominent user attention.\n- Ensure the icon is sized appropriately for its context (e.g., 32x32px or 64x64px for status panels).\n- Avoid using multiple blinking icons in close proximity to prevent visual overload.\n\n## Example\n```html\n<obc-alert-icon .alarmType=${alarm.type} .alarmStatus=${alarm.status}></obc-alert-icon>\n```",
48293
49378
  "name": "ObcAlertIcon",
48294
49379
  "members": [
48295
49380
  {
@@ -48327,6 +49412,29 @@
48327
49412
  "text": "boolean | undefined"
48328
49413
  }
48329
49414
  },
49415
+ {
49416
+ "kind": "field",
49417
+ "name": "flashingSpeed",
49418
+ "type": {
49419
+ "text": "FlashingSpeed"
49420
+ },
49421
+ "description": "Flash tempo: `default` resolves from the alert type and state, `fast`, `slow`, `very-slow` force a tempo, `fixed` never flashes. Acknowledged icons are always steady."
49422
+ },
49423
+ {
49424
+ "kind": "field",
49425
+ "name": "flashing",
49426
+ "privacy": "protected",
49427
+ "readonly": true,
49428
+ "default": "new FlashingController( this, () => this.resolvedFlashingSpeed )"
49429
+ },
49430
+ {
49431
+ "kind": "field",
49432
+ "name": "resolvedFlashingSpeed",
49433
+ "type": {
49434
+ "text": "ResolvedFlashingSpeed"
49435
+ },
49436
+ "readonly": true
49437
+ },
48330
49438
  {
48331
49439
  "kind": "field",
48332
49440
  "name": "_effectiveType",
@@ -48373,38 +49481,11 @@
48373
49481
  "kind": "method",
48374
49482
  "name": "render"
48375
49483
  },
48376
- {
48377
- "kind": "field",
48378
- "name": "_blinkAnimationCancel",
48379
- "type": {
48380
- "text": "() => void | undefined"
48381
- },
48382
- "privacy": "private"
48383
- },
48384
- {
48385
- "kind": "method",
48386
- "name": "syncBlinking",
48387
- "privacy": "private",
48388
- "return": {
48389
- "type": {
48390
- "text": "void"
48391
- }
48392
- }
48393
- },
48394
- {
48395
- "kind": "method",
48396
- "name": "updated",
48397
- "return": {
48398
- "type": {
48399
- "text": "void"
48400
- }
48401
- }
48402
- },
48403
49484
  {
48404
49485
  "kind": "field",
48405
49486
  "name": "styles",
48406
49487
  "static": true,
48407
- "default": "css` .wrapper { height: 100%; width: 100%; position: relative; obi-diagnostic-badge, obi-critical-badge { display: block; height: 100%; width: 100%; position: absolute; top: 0; left: 0; } svg, .a, .b { height: 100%; width: 100%; position: absolute; top: 0; left: 0; } &.blink-alarm .a { opacity: var(--alarm-blink-on); } &.blink-alarm .b { opacity: var(--alarm-blink-off); } &.blink-critical .a { opacity: var(--critical-blink-on); } &.blink-critical .b { opacity: var(--critical-blink-off); } &.blink-warning .a { opacity: var(--warning-blink-on); } &.blink-warning .b { opacity: var(--warning-blink-off); } &.blink-low .a { opacity: var(--low-blink-on); } &.blink-low .b { opacity: var(--low-blink-off); } } `"
49488
+ "default": "css` .wrapper { height: 100%; width: 100%; position: relative; obi-diagnostic-badge, obi-critical-badge { display: block; height: 100%; width: 100%; position: absolute; top: 0; left: 0; } svg, .a, .b { height: 100%; width: 100%; position: absolute; top: 0; left: 0; } &.flash-fast .a { opacity: var(--flash-fast-on); } &.flash-fast .b { opacity: var(--flash-fast-off); } &.flash-slow .a { opacity: var(--flash-slow-on); } &.flash-slow .b { opacity: var(--flash-slow-off); } &.flash-very-slow .a { opacity: var(--flash-very-slow-on); } &.flash-very-slow .b { opacity: var(--flash-very-slow-off); } } `"
48408
49489
  }
48409
49490
  ],
48410
49491
  "superclass": {
@@ -78497,6 +79578,29 @@
78497
79578
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
78498
79579
  }
78499
79580
  },
79581
+ {
79582
+ "kind": "method",
79583
+ "name": "ownsSlottedScaleRange",
79584
+ "privacy": "protected",
79585
+ "return": {
79586
+ "type": {
79587
+ "text": "boolean"
79588
+ }
79589
+ },
79590
+ "parameters": [
79591
+ {
79592
+ "name": "side",
79593
+ "type": {
79594
+ "text": "'left' | 'right' | 'top' | 'bottom'"
79595
+ }
79596
+ }
79597
+ ],
79598
+ "description": "The right scale is this component's own bar, ranged by `minValue` /\n`maxValue`. `chartMinValue` / `chartMaxValue` exist so the plotted range\ncan differ from it, so the chart's axis range must not overwrite it.",
79599
+ "inheritedFrom": {
79600
+ "name": "ObcChartLineBase",
79601
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
79602
+ }
79603
+ },
78500
79604
  {
78501
79605
  "kind": "method",
78502
79606
  "name": "willUpdate",
@@ -78946,6 +80050,19 @@
78946
80050
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
78947
80051
  }
78948
80052
  },
80053
+ {
80054
+ "kind": "field",
80055
+ "name": "xAxis",
80056
+ "type": {
80057
+ "text": "ChartLineXAxisConfig | undefined"
80058
+ },
80059
+ "default": "undefined",
80060
+ "description": "Pinned x range (`min`/`max`) for time and number axes. Without it the axis spans exactly the data, so a window that is still filling stretches across the full width. In `minutes` display `max` is the `0min` reference.",
80061
+ "inheritedFrom": {
80062
+ "name": "ObcChartLineBase",
80063
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80064
+ }
80065
+ },
78949
80066
  {
78950
80067
  "kind": "field",
78951
80068
  "name": "showGridX",
@@ -78985,6 +80102,32 @@
78985
80102
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
78986
80103
  }
78987
80104
  },
80105
+ {
80106
+ "kind": "field",
80107
+ "name": "rangeLabels",
80108
+ "type": {
80109
+ "text": "RangeLabels"
80110
+ },
80111
+ "description": "Labels kept below the 192px threshold, where the axis labels are otherwise hidden: 'y' draws min, max and (inside the range) 0 in a gutter on the y-axis side; 'x' draws the first and last x value in a bottom gutter; 'xy' both. Ignored when `hasLabelPadding` is false.",
80112
+ "inheritedFrom": {
80113
+ "name": "ObcChartLineBase",
80114
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80115
+ }
80116
+ },
80117
+ {
80118
+ "kind": "field",
80119
+ "name": "primaryYAxisId",
80120
+ "type": {
80121
+ "text": "string"
80122
+ },
80123
+ "privacy": "private",
80124
+ "description": "Id of the y scale a dataset lands on when it names none. Chart.js would\notherwise add a default `y` scale next to the configured ones and draw it.",
80125
+ "readonly": true,
80126
+ "inheritedFrom": {
80127
+ "name": "ObcChartLineBase",
80128
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80129
+ }
80130
+ },
78988
80131
  {
78989
80132
  "kind": "field",
78990
80133
  "name": "DEFAULT_TENSION",
@@ -79137,6 +80280,237 @@
79137
80280
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
79138
80281
  }
79139
80282
  },
80283
+ {
80284
+ "kind": "field",
80285
+ "name": "savedMainTickmarkLabels",
80286
+ "privacy": "private",
80287
+ "default": "new WeakMap< ExternalScaleElement, boolean >()",
80288
+ "inheritedFrom": {
80289
+ "name": "ObcChartLineBase",
80290
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80291
+ }
80292
+ },
80293
+ {
80294
+ "kind": "field",
80295
+ "name": "wasBelowThreshold",
80296
+ "type": {
80297
+ "text": "boolean | undefined"
80298
+ },
80299
+ "privacy": "private",
80300
+ "description": "Last known threshold side, to notice a crossing in `updated()`.",
80301
+ "inheritedFrom": {
80302
+ "name": "ObcChartLineBase",
80303
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80304
+ }
80305
+ },
80306
+ {
80307
+ "kind": "method",
80308
+ "name": "compactLabelThickness",
80309
+ "privacy": "private",
80310
+ "return": {
80311
+ "type": {
80312
+ "text": "number | undefined"
80313
+ }
80314
+ },
80315
+ "parameters": [
80316
+ {
80317
+ "name": "axis",
80318
+ "type": {
80319
+ "text": "'x' | 'y'"
80320
+ }
80321
+ }
80322
+ ],
80323
+ "description": "Label band a slotted scale needs for its compact labels, in the scale's\nown viewBox units. The scale prints the raw values, so those are what is\nmeasured; the x band is one text line.",
80324
+ "inheritedFrom": {
80325
+ "name": "ObcChartLineBase",
80326
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80327
+ }
80328
+ },
80329
+ {
80330
+ "kind": "field",
80331
+ "name": "rangeLabelCache",
80332
+ "type": {
80333
+ "text": "{\n y: string[];\n x: string[];\n bounds?: {min: number; max: number};\n }"
80334
+ },
80335
+ "privacy": "private",
80336
+ "default": "{y: [], x: []}",
80337
+ "description": "Range labels for the current data and size, refreshed with every\n`getChartOptions()` so the plugin never rebuilds the datasets on a draw.",
80338
+ "inheritedFrom": {
80339
+ "name": "ObcChartLineBase",
80340
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80341
+ }
80342
+ },
80343
+ {
80344
+ "kind": "method",
80345
+ "name": "slotFor",
80346
+ "privacy": "private",
80347
+ "return": {
80348
+ "type": {
80349
+ "text": "HTMLSlotElement | undefined"
80350
+ }
80351
+ },
80352
+ "parameters": [
80353
+ {
80354
+ "name": "side",
80355
+ "type": {
80356
+ "text": "'left' | 'right' | 'top' | 'bottom'"
80357
+ }
80358
+ }
80359
+ ],
80360
+ "inheritedFrom": {
80361
+ "name": "ObcChartLineBase",
80362
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80363
+ }
80364
+ },
80365
+ {
80366
+ "kind": "method",
80367
+ "name": "hasSlottedScale",
80368
+ "privacy": "private",
80369
+ "return": {
80370
+ "type": {
80371
+ "text": "boolean"
80372
+ }
80373
+ },
80374
+ "parameters": [
80375
+ {
80376
+ "name": "side",
80377
+ "type": {
80378
+ "text": "'left' | 'right' | 'top' | 'bottom'"
80379
+ }
80380
+ }
80381
+ ],
80382
+ "inheritedFrom": {
80383
+ "name": "ObcChartLineBase",
80384
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80385
+ }
80386
+ },
80387
+ {
80388
+ "kind": "field",
80389
+ "name": "ySide",
80390
+ "type": {
80391
+ "text": "'left' | 'right'"
80392
+ },
80393
+ "privacy": "private",
80394
+ "readonly": true,
80395
+ "inheritedFrom": {
80396
+ "name": "ObcChartLineBase",
80397
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80398
+ }
80399
+ },
80400
+ {
80401
+ "kind": "method",
80402
+ "name": "rangeLabelFont",
80403
+ "privacy": "private",
80404
+ "return": {
80405
+ "type": {
80406
+ "text": "RangeLabelFont"
80407
+ }
80408
+ },
80409
+ "inheritedFrom": {
80410
+ "name": "ObcChartLineBase",
80411
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80412
+ }
80413
+ },
80414
+ {
80415
+ "kind": "method",
80416
+ "name": "dataExtents",
80417
+ "privacy": "private",
80418
+ "return": {
80419
+ "type": {
80420
+ "text": "{\n x?: {min: number; max: number};\n y: Map<string, {min: number; max: number}>;\n }"
80421
+ }
80422
+ },
80423
+ "parameters": [
80424
+ {
80425
+ "name": "prepared",
80426
+ "type": {
80427
+ "text": "ReturnType<ObcChartLineBase['prepareChartDataAndLabels']>"
80428
+ }
80429
+ }
80430
+ ],
80431
+ "description": "Extent of the prepared data: one for x, one per y scale id. Each y axis\nranges from its own datasets, so a merged extent would label one axis\nwith another's values.",
80432
+ "inheritedFrom": {
80433
+ "name": "ObcChartLineBase",
80434
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80435
+ }
80436
+ },
80437
+ {
80438
+ "kind": "method",
80439
+ "name": "yRangeBounds",
80440
+ "privacy": "private",
80441
+ "return": {
80442
+ "type": {
80443
+ "text": "{min: number; max: number} | undefined"
80444
+ }
80445
+ },
80446
+ "parameters": [
80447
+ {
80448
+ "name": "extents",
80449
+ "type": {
80450
+ "text": "Map<string, {min: number; max: number}>"
80451
+ }
80452
+ }
80453
+ ],
80454
+ "description": "Range the y labels describe. Below the threshold the axis runs on data\nbounds, so its datasets' extent is exact and needs no chart to read from.",
80455
+ "inheritedFrom": {
80456
+ "name": "ObcChartLineBase",
80457
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80458
+ }
80459
+ },
80460
+ {
80461
+ "kind": "method",
80462
+ "name": "xEdgeLabelTexts",
80463
+ "privacy": "private",
80464
+ "return": {
80465
+ "type": {
80466
+ "text": "string[]"
80467
+ }
80468
+ },
80469
+ "parameters": [
80470
+ {
80471
+ "name": "prepared",
80472
+ "type": {
80473
+ "text": "ReturnType<ObcChartLineBase['prepareChartDataAndLabels']>"
80474
+ }
80475
+ },
80476
+ {
80477
+ "name": "extent",
80478
+ "type": {
80479
+ "text": "{min: number; max: number} | undefined"
80480
+ }
80481
+ }
80482
+ ],
80483
+ "description": "First and last x label, or nothing when there is no span to label.",
80484
+ "inheritedFrom": {
80485
+ "name": "ObcChartLineBase",
80486
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80487
+ }
80488
+ },
80489
+ {
80490
+ "kind": "method",
80491
+ "name": "refreshRangeLabels",
80492
+ "privacy": "private",
80493
+ "return": {
80494
+ "type": {
80495
+ "text": "typeof this.rangeLabelCache"
80496
+ }
80497
+ },
80498
+ "description": "Recompute the range labels. A side with a slotted scale is labelled by\nthe scale, so it gets no text here; the y bounds are kept regardless,\nsince the scale's compact band is measured from them.",
80499
+ "inheritedFrom": {
80500
+ "name": "ObcChartLineBase",
80501
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80502
+ }
80503
+ },
80504
+ {
80505
+ "kind": "method",
80506
+ "name": "createRangeLabelsPlugin",
80507
+ "privacy": "private",
80508
+ "description": "Paints min / 0 / max and first / last once Chart.js has drawn, inside the\ngutters `getChartOptions()` reserved. The end labels sit flush with the\nplot edges so no vertical padding is needed for them.",
80509
+ "inheritedFrom": {
80510
+ "name": "ObcChartLineBase",
80511
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80512
+ }
80513
+ },
79140
80514
  {
79141
80515
  "kind": "method",
79142
80516
  "name": "hasVisibleScale",
@@ -79264,6 +80638,44 @@
79264
80638
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
79265
80639
  }
79266
80640
  },
80641
+ {
80642
+ "kind": "method",
80643
+ "name": "resolveAxisRange",
80644
+ "privacy": "private",
80645
+ "return": {
80646
+ "type": {
80647
+ "text": "{\n min: number;\n max: number;\n }"
80648
+ }
80649
+ },
80650
+ "parameters": [
80651
+ {
80652
+ "name": "side",
80653
+ "type": {
80654
+ "text": "'left' | 'right' | 'x'"
80655
+ }
80656
+ }
80657
+ ],
80658
+ "description": "Range a slotted scale on the given side has to cover.\n\nA range pinned in the axis configuration is used as is: it is the source\nof truth, and on the first sync there is no chart to read yet. Otherwise\nthe live Chart.js scale is read under the id `buildScalesConfig()` gives\nit. A side with no axis of its own follows the first configured one.",
80659
+ "inheritedFrom": {
80660
+ "name": "ObcChartLineBase",
80661
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80662
+ }
80663
+ },
80664
+ {
80665
+ "kind": "method",
80666
+ "name": "resolveSlottedXRange",
80667
+ "privacy": "private",
80668
+ "return": {
80669
+ "type": {
80670
+ "text": "{min: number; max: number} | undefined"
80671
+ }
80672
+ },
80673
+ "description": "Range pushed to the top and bottom scales, or `undefined` to leave them\nalone. A scale prints its values verbatim, and on a `time` axis the raw\nrange is epoch milliseconds. In `minutes` display the range is converted\nto the minutes the chart's own labels show; `date` display has no numeric\nequivalent, so the scale keeps its own range.",
80674
+ "inheritedFrom": {
80675
+ "name": "ObcChartLineBase",
80676
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80677
+ }
80678
+ },
79267
80679
  {
79268
80680
  "kind": "method",
79269
80681
  "name": "updateScaleProperties",
@@ -79282,6 +80694,16 @@
79282
80694
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
79283
80695
  }
79284
80696
  },
80697
+ {
80698
+ "kind": "method",
80699
+ "name": "syncSlottedScaleRanges",
80700
+ "privacy": "private",
80701
+ "description": "Push the current axis ranges to the slotted scales. The full\n`updateScaleProperties()` cascade runs before the chart is (re)built and\nso reads the previous chart's ranges; this closes that gap once the new\nchart exists, and keeps auto-ranged scales in step with the data.",
80702
+ "inheritedFrom": {
80703
+ "name": "ObcChartLineBase",
80704
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80705
+ }
80706
+ },
79285
80707
  {
79286
80708
  "kind": "method",
79287
80709
  "name": "hasAnyChanged",
@@ -79492,6 +80914,21 @@
79492
80914
  "module": "src/building-blocks/chart-line/chart-line-base.ts"
79493
80915
  }
79494
80916
  },
80917
+ {
80918
+ "kind": "method",
80919
+ "name": "computeChartPadding",
80920
+ "privacy": "private",
80921
+ "return": {
80922
+ "type": {
80923
+ "text": "{\n top: number;\n right: number;\n bottom: number;\n left: number;\n }"
80924
+ }
80925
+ },
80926
+ "description": "One padding source for the Chart.js layout and the slotted scales: two\nsources painted the canvas over a scale and inset a scale inside a chart.",
80927
+ "inheritedFrom": {
80928
+ "name": "ObcChartLineBase",
80929
+ "module": "src/building-blocks/chart-line/chart-line-base.ts"
80930
+ }
80931
+ },
79495
80932
  {
79496
80933
  "kind": "method",
79497
80934
  "name": "getChartOptions",
@@ -79516,7 +80953,7 @@
79516
80953
  "text": "number | undefined"
79517
80954
  }
79518
80955
  },
79519
- "description": "Compute reference timestamp for time axis formatting.\nReturns earliest timestamp for 'date' mode, latest for 'minutes' mode.",
80956
+ "description": "Compute reference timestamp for time axis formatting.\nReturns earliest timestamp for 'date' mode, latest for 'minutes' mode.\nA pinned x range supplies the reference instead, so the axis edge stays\nthe reference while the data has not reached it yet.",
79520
80957
  "inheritedFrom": {
79521
80958
  "name": "ObcChartLineBase",
79522
80959
  "module": "src/building-blocks/chart-line/chart-line-base.ts"