@oicl/openbridge-webcomponents 2.0.0-next.121 → 2.0.0-next.122

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 (29) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +233 -53
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +66 -17
  4. package/dist/automation/automation-tank/automation-tank.css.js +94 -14
  5. package/dist/automation/automation-tank/automation-tank.css.js.map +1 -1
  6. package/dist/automation/automation-tank/automation-tank.d.ts +53 -5
  7. package/dist/automation/automation-tank/automation-tank.d.ts.map +1 -1
  8. package/dist/automation/automation-tank/automation-tank.js +39 -20
  9. package/dist/automation/automation-tank/automation-tank.js.map +1 -1
  10. package/dist/building-blocks/readout-block/readout-block.d.ts +27 -1
  11. package/dist/building-blocks/readout-block/readout-block.d.ts.map +1 -1
  12. package/dist/building-blocks/readout-block/readout-block.js +31 -10
  13. package/dist/building-blocks/readout-block/readout-block.js.map +1 -1
  14. package/dist/navigation-instruments/readout/readout-formatters.d.ts +61 -0
  15. package/dist/navigation-instruments/readout/readout-formatters.d.ts.map +1 -1
  16. package/dist/navigation-instruments/readout/readout-formatters.js +35 -2
  17. package/dist/navigation-instruments/readout/readout-formatters.js.map +1 -1
  18. package/dist/navigation-instruments/readout/readout.d.ts +19 -2
  19. package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
  20. package/dist/navigation-instruments/readout/readout.js +18 -4
  21. package/dist/navigation-instruments/readout/readout.js.map +1 -1
  22. package/dist/navigation-instruments/readout-list/readout-list.d.ts.map +1 -1
  23. package/dist/navigation-instruments/readout-list/readout-list.js +6 -3
  24. package/dist/navigation-instruments/readout-list/readout-list.js.map +1 -1
  25. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts +19 -2
  26. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
  27. package/dist/navigation-instruments/readout-list-item/readout-list-item.js +18 -4
  28. package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
  29. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"automation-tank.js","sources":["../../../src/automation/automation-tank/automation-tank.ts"],"sourcesContent":["import {\n HTMLTemplateResult,\n LitElement,\n html,\n nothing,\n svg,\n unsafeCSS,\n} from 'lit';\nimport {property, state} from 'lit/decorators.js';\nimport compentStyle from './automation-tank.css?inline';\nimport {LineMedium} from '../index.js';\nimport '../automation-badge/automation-badge.js';\nimport {ObcAutomationBadgeType} from '../automation-badge/automation-badge.js';\nimport {\n AutomationButtonBadgeAlert,\n AutomationButtonBadgeCommandLocked,\n AutomationButtonBadgeControl,\n AutomationButtonBadgeInterlock,\n} from '../automation-button/abstract-automation-button.js';\n\nimport '../../icons/icon-chevron-double-up-google.js';\nimport '../../icons/icon-chevron-up-google.js';\nimport '../../icons/icon-chevron-double-down-google.js';\nimport '../../icons/icon-chevron-down-google.js';\nimport '../../icons/icon-arrow-right-google.js';\nimport '../../icons/icon-off.js';\nimport '../../icons/icon-tank.js';\nimport '../../icons/icon-energy-battery.js';\nimport '../../navigation-instruments/gauge-trend/gauge-trend.js';\nimport '../../building-blocks/bar-vertical/bar-vertical.js';\nimport '../../components/alert-frame/alert-frame.js';\nimport {Priority} from '../../navigation-instruments/types.js';\nimport {\n ObcAlertFrameThickness,\n ObcAlertFrameType,\n} from '../../components/alert-frame/alert-frame.js';\nimport {AlertType} from '../../types.js';\nimport type {ChartLineDataItem} from '../../building-blocks/chart-line/chart-line-base.js';\nimport type {LinearAdvice} from '../../building-blocks/instrument-linear/advice.js';\nimport {\n AdvicePosition,\n ExternalScaleSide,\n FillMode,\n computeSetpointBandThickness,\n} from '../../building-blocks/external-scale/external-scale.js';\nimport {classMap} from 'lit/directives/class-map.js';\nimport {customElement} from '../../decorator.js';\nimport {SetpointMixin} from '../../svghelpers/setpoint-mixin.js';\n\nexport enum TankTrend {\n fastRising = 'fast-rising',\n rising = 'rising',\n stable = 'stable',\n falling = 'falling',\n fastFalling = 'fast-falling',\n closed = 'closed',\n}\n\nexport enum TankType {\n generic = 'generic',\n atmospheric = 'atmospheric',\n pressurized = 'pressurized',\n battery = 'battery',\n}\n\nexport enum TankOrientation {\n vertical = 'vertical',\n horizontal = 'horizontal',\n}\n\n/**\n * Host positioning model.\n *\n * - `point`: the host has fixed default dimensions (per\n * orientation and compact / static variant) and a P&ID anchor — the\n * visual content is shifted with `translateX(-50%)` and (in non-compact)\n * `top: -20px` so the tank's top-center aligns with the host's top-left\n * placement coordinate. Use this when dropping the tank onto a P&ID\n * canvas at a pipe-grid coordinate.\n * - `button` (default): the host fills its parent container (100% × 100%) with no\n * anchor offset. Use this when embedding the tank inside a sized layout\n * slot — the parent controls the footprint and the tank renders\n * responsively inside it, just like a regular button. Compact / static\n * inner layout still applies; only the host box is changed.\n */\nexport enum TankPositioning {\n point = 'point',\n button = 'button',\n}\n\nexport enum TankChartMode {\n /** Static fill bar driven by `value` / `max` (default, backward compatible). */\n bar = 'bar',\n /** Embedded `obc-gauge-trend` line/area chart, no side bar. */\n graph = 'graph',\n /** Embedded `obc-gauge-trend` line/area chart with a side bar. */\n graphAndBar = 'graph-and-bar',\n}\n\n/**\n *\n *\n * Setpoint properties are inherited from {@link SetpointMixin}\n * (`setpoint`, `newSetpoint`, `touching`, `atSetpoint`, `autoAtSetpoint`,\n * `autoAtSetpointDeadband`, `setpointAtZeroDeadband`, `setpointOverride`,\n * `animateSetpoint`) and are forwarded to the embedded chart: the SVG bar in\n * `bar` mode, or `obc-gauge-trend` in the graph modes (where the marker\n * renders on the side bar, i.e. `graph-and-bar`). Values share the tank's\n * `value` / `max` scale.\n *\n * @slot badges - Custom badges to be displayed in the badge area.\n * @slot tag - Text or element for the tank's tag/label.\n * @slot readout - Replaces the entire readout content block.\n * @slot max-value - Content for the capacity value.\n * @slot unit - Content for the unit of measurement.\n * @slot current-value - Content for the current level value.\n * @slot rich - Detail rows below the main readout, shown in the regular\n * (non-compact, non-static) layout. Slot an `<obc-readout-list>` of\n * `<obc-readout-list-item>` rows — it owns the row typography and cross-row\n * column alignment; the tank renders a divider above it automatically when\n * the slot is filled.\n * @slot alert-icon - Custom icon for the alert frame.\n * @slot alert-label - Label for the alert frame.\n * @slot alert-timer - Timer for the alert frame.\n * @fires click - Fired when the tank is clicked. In `static` mode the tank renders a `<div role=\"img\">` instead of a `<button>`, so it is not focusable or keyboard-activatable; pointer clicks still reach the host.\n * @beta\n */\n@customElement('obc-automation-tank')\nexport class ObcAutomationTank extends SetpointMixin(LitElement) {\n @property({type: String}) medium: LineMedium = LineMedium.water;\n @property({type: Number}) value: number = 0;\n @property({type: Number}) max: number = 100;\n @property({type: String}) trend: TankTrend = TankTrend.stable;\n @property({type: String, reflect: true}) type: TankType = TankType.generic;\n @property({type: String, reflect: true}) orientation: TankOrientation =\n TankOrientation.vertical;\n @property({type: Boolean, reflect: true}) compact: boolean = false;\n /**\n * Host positioning model — see `TankPositioning` for details. Defaults to\n * `button` (host fills parent container, 100% × 100%, no anchor offset).\n * Set to `point` for the legacy P&ID canvas mode (fixed default dimensions\n * + top-center anchor offset).\n */\n @property({type: String, reflect: true}) positioning: TankPositioning =\n TankPositioning.button;\n /**\n * Static (display-only) variant. Always rendered at the compact size; the\n * inner chart/bar is hidden, the bordered area is filled with\n * `--container-section-color`, and the readout is centered inside the frame.\n * Tag is rendered below the bordered area; badges (when slotted) sit above\n * the frame inside the halo and shrink the frame just like in compact mode.\n * Overrides `compact` (a static tank is always compact-sized).\n *\n * Static tanks render as a non-interactive `<div role=\"img\">` (not a\n * `<button>`), so they are not in the tab order and do not announce as\n * activatable controls.\n */\n @property({type: Boolean, reflect: true}) static: boolean = false;\n /** Enables the activated background color, used to indicate that the tank is activated/selected. */\n @property({type: Boolean}) activated: boolean = false;\n @property({type: String}) tag: string = '';\n\n /**\n * Chart cell rendering mode.\n * - `bar`: static fill bar driven by `value`/`max` (default).\n * - `graph`: embedded `obc-gauge-trend` line/area chart.\n * - `graphAndBar`: embedded `obc-gauge-trend` with an integrated side bar.\n */\n @property({type: String, reflect: true, attribute: 'chart-mode'})\n chartMode: TankChartMode = TankChartMode.bar;\n\n /** Time-series data points for the embedded gauge-trend (graph modes only). */\n @property({type: Array, attribute: false})\n chartData: ChartLineDataItem[] = [];\n\n /** Priority hint forwarded to child charts (regular | enhanced). */\n @property({type: String}) priority: Priority = Priority.regular;\n\n /**\n * Advice overlays. Forwarded to the embedded `obc-gauge-trend` in\n * `graph` / `graph-and-bar` modes, or rendered as pills over the static\n * bar in `bar` mode.\n */\n @property({type: Array, attribute: false})\n advice: LinearAdvice[] = [];\n\n /** Show advice overlays (works in all `chartMode` variants). */\n @property({type: Boolean}) hasAdvice = false;\n\n /**\n * Overlay a 32×32 decorative icon (currently `<obi-tank>`) centered on the\n * chart cell. Works in all three `chartMode` variants (bar, graph,\n * graph-and-bar) and in both orientations. The icon is rendered above the\n * bar/graph in a fixed CSS layer (does not scale with the SVG meet\n * transform) and is silhouetted with a `--border-silhouette-color` halo so\n * it stays legible on any underlying fill. The icon size scales with the\n * ambient `obc-component-size-*` class on an ancestor via the design token\n * `--automation-components-tanks-graphs-graph-icon-size` (32 → 48 → 64 → 64\n * for regular → medium → large → xl).\n *\n * TODO(future): replace the hard-coded `<obi-tank>` with a `slot=\"graph-icon\"`\n * so consumers can pass any `<obi-*>` icon. The current API ships the\n * smallest viable surface; the slot can be added without breaking the\n * boolean property.\n */\n @property({type: Boolean, attribute: false}) hasGraphIcon = false;\n\n /**\n * Show an `<obc-alert-frame>` overlay around the bordered tank area (the\n * `.halo` wrapper). Mirrors the API of `obc-automation-button`: same six\n * properties, same three slots (`alert-icon`, `alert-label`, `alert-timer`).\n * The ring overlays `.halo` only, so the tag and readout that sit outside\n * the halo in compact / static layouts remain unaffected.\n */\n @property({type: Boolean}) alert: boolean = false;\n @property({type: String}) alertFrameType: ObcAlertFrameType =\n ObcAlertFrameType.SmallSideFlip;\n @property({type: String}) alertFrameThickness: ObcAlertFrameThickness =\n ObcAlertFrameThickness.Small;\n @property({type: String}) alertFrameStatus: AlertType = AlertType.Alarm;\n @property({type: Boolean, attribute: false}) showAlertCategoryIcon: boolean =\n true;\n @property({type: Boolean}) showAlertIcon: boolean = false;\n\n /**\n * Show the trend chevron / off icon next to the percent readout. Default\n * `true` preserves existing behavior. Set to `false` to hide the trend\n * indicator in both compact and non-compact readouts — useful when the\n * trend is not meaningful for a given tank, or when the consumer wants\n * to keep the readout compact. Has no effect in `static` mode, which\n * renders the tank's capacity (`max` + `unit`) instead of a percent and\n * intentionally omits the trend indicator (a static tank represents\n * \"device present, current state unknown\"). `attribute: false` per the\n * repo's positive-default-true boolean convention.\n */\n @property({type: Boolean, attribute: false}) showTrendSymbol: boolean = true;\n\n /**\n * Number of fraction digits used to format the percent readout in the\n * non-compact (regular) layout. Defaults to `0` (integer percent). The\n * compact layout always renders integer percent to keep its fixed-width\n * footprint stable. The static layout renders capacity (`max` + `unit`)\n * rather than percent, so this property has no effect there — pass a\n * pre-formatted value through the `max-value` slot if fractional\n * precision is needed (see the `WithFractionDigits` story).\n */\n @property({type: Number}) percentFractionDigits: number = 0;\n\n /**\n * Enum-driven badges rendered inside the `badges` cell. Mirrors the API\n * introduced for `ObcAbstractAutomationButton` in PR #839 (#829). When set\n * to a non-`None` value, an `<obc-automation-badge>` of the corresponding\n * type is rendered as fallback content for the `badges` slot. Any content\n * the consumer slots into `badges` overrides these defaults, preserving\n * backward compatibility with the existing slot-based API.\n *\n * Render order (left → right): control, alert, interlock, commandLocked —\n * matching the positional convention used by `ObcAbstractAutomationButton`\n * (top-left, top-right, bottom-left, bottom-right) read row-by-row.\n */\n @property({type: String}) badgeControl: AutomationButtonBadgeControl =\n AutomationButtonBadgeControl.None;\n @property({type: String}) badgeAlert: AutomationButtonBadgeAlert =\n AutomationButtonBadgeAlert.None;\n @property({type: String}) badgeInterlock: AutomationButtonBadgeInterlock =\n AutomationButtonBadgeInterlock.None;\n @property({type: String})\n badgeCommandLocked: AutomationButtonBadgeCommandLocked =\n AutomationButtonBadgeCommandLocked.None;\n\n @state() private _cellWidth = 0;\n @state() private _cellHeight = 0;\n /**\n * Tracks whether the `badges` and `tag` slots have assigned content. Used\n * in compact/static mode to collapse empty cells to 0px so the tank-frame\n * can absorb the freed space (per spec: \"if there are no badges it would\n * grow upward; if there are no readout or tags it would grow downward\").\n * Non-compact (regular) layout keeps its grid-based reserved rows.\n */\n @state() private _hasBadges = false;\n @state() private _hasTagSlot = false;\n /**\n * Tracks whether the `rich` slot has assigned content, so the regular\n * (non-compact) layout only draws the rich divider when a consumer has\n * slotted detail rows (canonically an `<obc-readout-list>`). Mirrors\n * `_hasBadges` / `_hasTagSlot`.\n */\n @state() private _hasRichSlot = false;\n private _chartResizeObserver?: ResizeObserver;\n private _observedCell?: Element;\n\n private get isCompact(): boolean {\n return this.compact || this.static;\n }\n\n private _badgeAlertType(): ObcAutomationBadgeType | null {\n switch (this.badgeAlert) {\n case AutomationButtonBadgeAlert.Silence:\n return ObcAutomationBadgeType.AlertSilenced;\n case AutomationButtonBadgeAlert.Caution:\n return ObcAutomationBadgeType.Caution;\n case AutomationButtonBadgeAlert.Warning:\n return ObcAutomationBadgeType.Warning;\n case AutomationButtonBadgeAlert.Alarm:\n return ObcAutomationBadgeType.Alarm;\n case AutomationButtonBadgeAlert.LevelCritical:\n return ObcAutomationBadgeType.LevelCritical;\n case AutomationButtonBadgeAlert.LevelHigh:\n return ObcAutomationBadgeType.LevelHigh;\n case AutomationButtonBadgeAlert.LevelMedium:\n return ObcAutomationBadgeType.LevelMedium;\n case AutomationButtonBadgeAlert.LevelLow:\n return ObcAutomationBadgeType.LevelLow;\n case AutomationButtonBadgeAlert.LevelDiagnostic:\n return ObcAutomationBadgeType.LevelDiagnostic;\n default:\n return null;\n }\n }\n\n private _badgeControlType(): ObcAutomationBadgeType | null {\n switch (this.badgeControl) {\n case AutomationButtonBadgeControl.Local:\n return ObcAutomationBadgeType.Local;\n case AutomationButtonBadgeControl.LocalOnly:\n return ObcAutomationBadgeType.LocalOnly;\n case AutomationButtonBadgeControl.Manual:\n return ObcAutomationBadgeType.Manual;\n case AutomationButtonBadgeControl.ManualOnly:\n return ObcAutomationBadgeType.ManualOnly;\n case AutomationButtonBadgeControl.Auto:\n return ObcAutomationBadgeType.Auto;\n default:\n return null;\n }\n }\n\n private _badgeInterlockType(): ObcAutomationBadgeType | null {\n switch (this.badgeInterlock) {\n case AutomationButtonBadgeInterlock.Interlock:\n return ObcAutomationBadgeType.Interlock;\n case AutomationButtonBadgeInterlock.InterlockInhibit:\n return ObcAutomationBadgeType.InterlockInhibit;\n default:\n return null;\n }\n }\n\n private _badgeCommandLockedType(): ObcAutomationBadgeType | null {\n if (\n this.badgeCommandLocked ===\n AutomationButtonBadgeCommandLocked.CommandLocked\n ) {\n return ObcAutomationBadgeType.CommandLocked;\n }\n return null;\n }\n\n private get _usesGaugeTrend(): boolean {\n return (\n this.chartMode === TankChartMode.graph ||\n this.chartMode === TankChartMode.graphAndBar\n );\n }\n\n private _onBadgesSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasBadges = slot\n .assignedNodes({flatten: true})\n .some(\n (n) =>\n n.nodeType === Node.ELEMENT_NODE ||\n (n.nodeType === Node.TEXT_NODE && !!n.textContent?.trim())\n );\n }\n\n private _onTagSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasTagSlot = slot\n .assignedNodes({flatten: true})\n .some(\n (n) =>\n n.nodeType === Node.ELEMENT_NODE ||\n (n.nodeType === Node.TEXT_NODE && !!n.textContent?.trim())\n );\n }\n\n private _onRichSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasRichSlot = slot\n .assignedNodes({flatten: true})\n .some(\n (n) =>\n n.nodeType === Node.ELEMENT_NODE ||\n (n.nodeType === Node.TEXT_NODE && !!n.textContent?.trim())\n );\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._chartResizeObserver?.disconnect();\n this._chartResizeObserver = undefined;\n this._observedCell = undefined;\n }\n\n protected override updated(\n changed: Map<string | number | symbol, unknown>\n ): void {\n super.updated(changed);\n // No imperative forwarding here — child components receive `priority`\n // via template property bindings (`.priority=${this.priority}`). Rely on\n // their reactive `updated()` logic (e.g. `ObcGaugeTrend._updateBarVerticalProperties()`)\n // to propagate palette changes internally.\n // The chart cell DOM element is recreated when chartMode toggles between\n // bar and graph modes (different class/contents), and may also appear/\n // disappear with static/compact. Re-attach the observer to the current\n // .bar-container instance.\n this._syncChartResizeObserver();\n }\n\n private _syncChartResizeObserver(): void {\n // Observe the bar-container in BOTH bar mode and graph modes — the bar\n // mode now renders an SVG `obc-bar-vertical` / `-horizontal` that needs\n // measured pixel dimensions for its viewBox and barThickness.\n const cell = this.renderRoot.querySelector('.bar-container');\n if (!cell) {\n if (this._chartResizeObserver) {\n this._chartResizeObserver.disconnect();\n this._observedCell = undefined;\n }\n return;\n }\n if (cell === this._observedCell) return;\n\n if (!this._chartResizeObserver) {\n this._chartResizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n const w = Math.round(entry.contentRect.width);\n const h = Math.round(entry.contentRect.height);\n if (w !== this._cellWidth) this._cellWidth = w;\n if (h !== this._cellHeight) this._cellHeight = h;\n });\n } else {\n this._chartResizeObserver.disconnect();\n }\n this._chartResizeObserver.observe(cell);\n this._observedCell = cell;\n }\n\n trendIcon(): HTMLTemplateResult | typeof nothing {\n if (this.trend === TankTrend.fastRising) {\n return html`<obi-chevron-double-up-google\n class=\"trend-icon\"\n ></obi-chevron-double-up-google>`;\n } else if (this.trend === TankTrend.rising) {\n return html`<obi-chevron-up-google\n class=\"trend-icon\"\n ></obi-chevron-up-google>`;\n } else if (this.trend === TankTrend.fastFalling) {\n return html`<obi-chevron-double-down-google\n class=\"trend-icon\"\n ></obi-chevron-double-down-google>`;\n } else if (this.trend === TankTrend.falling) {\n return html`<obi-chevron-down-google\n class=\"trend-icon\"\n ></obi-chevron-down-google>`;\n } else if (this.trend === TankTrend.closed) {\n return html`<obi-off class=\"trend-icon\"></obi-off>`;\n } else {\n return html`<obi-arrow-right-google\n class=\"trend-icon\"\n ></obi-arrow-right-google>`;\n }\n }\n\n /**\n * Atmospheric cap, 3-piece (corner-start / stretchable middle arc / corner-end).\n * Each piece draws its own fill (closed) and stroke (open) so adjacent pieces\n * abut without a visible vertical seam line. Coordinates are derived from the\n * Figma 248x14 path; corners stay 12px wide while the middle arc stretches.\n * Position (start vs end) and orientation flipping are handled in CSS.\n *\n * Horizontal orientation reuses the same path data but rotates each piece\n * via an inner `<g transform=\"translate(0 H) rotate(-90)\">`, mapping the\n * source coord (x, y) to (y, H - x). The viewBox is swapped accordingly:\n * corner: 12x14 -> 14x12 (H=12)\n * middle: 224x14 -> 14x224 (H=224)\n * Cap-end (right side in horizontal) is mirrored via CSS scaleX(-1).\n */\n private renderAtmosphericCap(side: 'start' | 'end'): HTMLTemplateResult {\n const isHorizontal = this.orientation === TankOrientation.horizontal;\n\n // Path data. Two variants — regular (14px tall, 12px corners) and compact\n // (10px tall, 7px corners). Curve Y-values are proportionally scaled from\n // the original 18/12-tall Figma reference so the corner curves stay\n // continuous at the new thickness.\n // Horizontal orientation reuses the same vertical path data inside an\n // inner `<g transform=\"translate(0 H) rotate(-90)\">`; the SVG viewBox is\n // swapped accordingly. Cap-end is mirrored via CSS scaleY/scaleX(-1).\n let cornerStartFill: string;\n let cornerStartStroke: string;\n let midFill: string;\n let midStroke: string;\n let cornerEndFill: string;\n let cornerEndStroke: string;\n let cornerW: number;\n let capH: number;\n let midW: number;\n\n if (this.isCompact) {\n cornerW = 7;\n capH = 10;\n midW = 149;\n cornerStartFill =\n 'M 6.374 4.8214 C 6.138 4.8429, 6.005 4.8543, 5.83 4.8771 C 2.937 5.2557, 0.669 7.0957, 0.51 9.1936 C 0.5 9.3214, 0.5 9.4357, 0.5 9.6429 L 0.5 10 L 6.374 10 Z';\n cornerStartStroke =\n 'M 6.374 4.8214 C 6.138 4.8429, 6.005 4.8543, 5.83 4.8771 C 2.937 5.2557, 0.669 7.0957, 0.51 9.1936 C 0.5 9.3214, 0.5 9.4357, 0.5 9.6429 L 0.5 10';\n midFill =\n 'M 0 4.8214 C 10.97 3.8214, 37.83 1.7857, 74.626 1.7857 C 111.42 1.7857, 138.28 3.8214, 149.252 4.8214 L 149.252 10 L 0 10 Z';\n midStroke =\n 'M 0 4.8214 C 10.97 3.8214, 37.83 1.7857, 74.626 1.7857 C 111.42 1.7857, 138.28 3.8214, 149.252 4.8214';\n cornerEndFill =\n 'M 0 4.8214 C 0.236 4.8429, 0.369 4.8543, 0.544 4.8771 C 3.437 5.2557, 5.705 7.0957, 5.864 9.1936 C 5.874 9.3214, 5.874 9.4357, 5.874 9.6429 L 5.874 10 L 0 10 Z';\n cornerEndStroke =\n 'M 0 4.8214 C 0.236 4.8429, 0.369 4.8543, 0.544 4.8771 C 3.437 5.2557, 5.705 7.0957, 5.864 9.1936 C 5.874 9.3214, 5.874 9.4357, 5.874 9.6429 L 5.874 10';\n } else {\n cornerW = 12;\n capH = 14;\n midW = 224;\n cornerStartFill =\n 'M 12 6.5837 C 6.346 6.7737, 5.176 6.8744, 4.168 7.2537 C 2.6445 7.8272, 1.418 8.8983, 0.8613 10.1415 C 0.493 10.9641, 0.5 11.9099, 0.5 13.6111 L 0.5 14 L 12 14 Z';\n // Stroke draws the curve + outer vertical edge only. The seam with\n // `.middle` (y = capH) is painted by a 1px CSS `border-top` on\n // `.tank-frame.type-atmospheric:not(.compact) .middle` so it pixel-\n // snaps identically to the pressurized cap's CSS border. Drawing it\n // as an SVG stroke here would render thinner / blurrier due to\n // sub-pixel anti-aliasing.\n cornerStartStroke =\n 'M 12 6.5837 C 6.346 6.7737, 5.176 6.8744, 4.168 7.2537 C 2.6445 7.8272, 1.418 8.8983, 0.8613 10.1415 C 0.493 10.9641, 0.5 11.9099, 0.5 13.6111 L 0.5 14';\n midFill =\n 'M 0 6.5837 C 16 5.1124, 56 1.9444, 112 1.9444 C 168 1.9444, 208 5.1124, 224 6.5837 L 224 14 L 0 14 Z';\n midStroke =\n 'M 0 6.5837 C 16 5.1124, 56 1.9444, 112 1.9444 C 168 1.9444, 208 5.1124, 224 6.5837';\n cornerEndFill =\n 'M 0 6.5837 C 5.654 6.7737, 6.824 6.8744, 7.832 7.2537 C 9.3555 7.8272, 10.582 8.8983, 11.1387 10.1415 C 11.507 10.9641, 11.5 11.9099, 11.5 13.6111 L 11.5 14 L 0 14 Z';\n cornerEndStroke =\n 'M 0 6.5837 C 5.654 6.7737, 6.824 6.8744, 7.832 7.2537 C 9.3555 7.8272, 10.582 8.8983, 11.1387 10.1415 C 11.507 10.9641, 11.5 11.9099, 11.5 13.6111 L 11.5 14';\n }\n\n if (isHorizontal) {\n // Rotate vertical paths -90deg into a `capH`-wide column. Source coord\n // (x, y) maps to (y, cornerW - x) for corners and (y, midW - x) for mid.\n return html`\n <div class=\"cap cap-atmospheric cap-${side}\">\n <svg\n class=\"cap-corner cap-corner-start\"\n viewBox=\"0 0 ${capH} ${cornerW}\"\n preserveAspectRatio=\"xMidYMax meet\"\n aria-hidden=\"true\"\n >\n ${svg`\n <g transform=\"translate(0 ${cornerW}) rotate(-90)\">\n <path class=\"cap-fill\" d=\"${cornerStartFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" d=\"${cornerStartStroke}\" />\n </g>\n `}\n </svg>\n <svg\n class=\"cap-mid\"\n viewBox=\"0 0 ${capH} ${midW}\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n ${svg`\n <g transform=\"translate(0 ${midW}) rotate(-90)\">\n <path class=\"cap-fill\" d=\"${midFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" vector-effect=\"non-scaling-stroke\" d=\"${midStroke}\" />\n </g>\n `}\n </svg>\n <svg\n class=\"cap-corner cap-corner-end\"\n viewBox=\"0 0 ${capH} ${cornerW}\"\n preserveAspectRatio=\"xMidYMin meet\"\n aria-hidden=\"true\"\n >\n ${svg`\n <g transform=\"translate(0 ${cornerW}) rotate(-90)\">\n <path class=\"cap-fill\" d=\"${cornerEndFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" d=\"${cornerEndStroke}\" />\n </g>\n `}\n </svg>\n </div>\n `;\n }\n\n return html`\n <div class=\"cap cap-atmospheric cap-${side}\">\n <svg\n class=\"cap-corner cap-corner-start\"\n viewBox=\"0 0 ${cornerW} ${capH}\"\n preserveAspectRatio=\"xMaxYMid meet\"\n aria-hidden=\"true\"\n >\n ${svg`\n <path class=\"cap-fill\" d=\"${cornerStartFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" d=\"${cornerStartStroke}\" />\n `}\n </svg>\n <svg\n class=\"cap-mid\"\n viewBox=\"0 0 ${midW} ${capH}\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n ${svg`\n <path class=\"cap-fill\" d=\"${midFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" vector-effect=\"non-scaling-stroke\" d=\"${midStroke}\" />\n `}\n </svg>\n <svg\n class=\"cap-corner cap-corner-end\"\n viewBox=\"0 0 ${cornerW} ${capH}\"\n preserveAspectRatio=\"xMinYMid meet\"\n aria-hidden=\"true\"\n >\n ${svg`\n <path class=\"cap-fill\" d=\"${cornerEndFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" d=\"${cornerEndStroke}\" />\n `}\n </svg>\n </div>\n `;\n }\n\n private renderCap(side: 'start' | 'end'): HTMLTemplateResult | null {\n if (this.type === TankType.generic) return null;\n if (this.type === TankType.battery && side === 'end') return null;\n if (this.type === TankType.atmospheric) {\n return this.renderAtmosphericCap(side);\n }\n if (this.type === TankType.pressurized) {\n return html`<div class=\"cap cap-pressurized cap-${side}\"></div>`;\n }\n // battery, start only\n return html`<div class=\"cap cap-battery cap-${side}\"></div>`;\n }\n\n override render() {\n const safeMax = this.max > 0 ? this.max : 1;\n const percent = Math.max(0, Math.min(100, (this.value / safeMax) * 100));\n const isCompact = this.isCompact;\n\n // In compact/static the `.halo` is a fixed-size column flex container\n // that holds badges + tank-frame + (compact only) readout + tag. To let\n // the tank-frame absorb the freed space when adjacent cells are empty,\n // we collapse the empty cells with the `hidden` attribute (see CSS).\n // In non-compact mode cells live inside the inner `.grid` and always\n // reserve their min-content rows — unchanged from previous behavior.\n const controlBadge = this._badgeControlType();\n const alertBadge = this._badgeAlertType();\n const interlockBadge = this._badgeInterlockType();\n const commandLockedBadge = this._badgeCommandLockedType();\n const hasEnumBadges =\n controlBadge !== null ||\n alertBadge !== null ||\n interlockBadge !== null ||\n commandLockedBadge !== null;\n\n const badgesHidden = isCompact && !this._hasBadges && !hasEnumBadges;\n const tagHidden = isCompact && !this._hasTagSlot && !this.tag;\n\n const badgesCell = html`\n <div class=\"badges\" ?hidden=${badgesHidden}>\n <slot name=\"badges\" @slotchange=${this._onBadgesSlotChange}>\n ${controlBadge\n ? html`<obc-automation-badge\n .type=${controlBadge}\n ></obc-automation-badge>`\n : nothing}\n ${alertBadge\n ? html`<obc-automation-badge\n .type=${alertBadge}\n ></obc-automation-badge>`\n : nothing}\n ${interlockBadge\n ? html`<obc-automation-badge\n .type=${interlockBadge}\n ></obc-automation-badge>`\n : nothing}\n ${commandLockedBadge\n ? html`<obc-automation-badge\n .type=${commandLockedBadge}\n ></obc-automation-badge>`\n : nothing}\n </slot>\n </div>\n `;\n const tagCell = html`\n <div class=\"tag\" ?hidden=${tagHidden}>\n <slot name=\"tag\" @slotchange=${this._onTagSlotChange}>${this.tag}</slot>\n </div>\n `;\n // Static mode intentionally renders the tank's capacity (max + unit)\n // instead of a percent reading: a static tank represents \"device is\n // present, current state unknown\", so a percent value would be\n // misleading. The trend icon is omitted for the same reason. Consumers\n // who need a custom label can still override the whole cell via the\n // `readout` slot, or supply formatted text through the existing\n // `max-value` / `unit` slots (no new API).\n const readoutCell = this.static\n ? html`\n <div class=\"readout readout-compact readout-static\">\n <slot name=\"readout\">\n <slot name=\"max-value\">${this.max.toFixed(0)}</slot>\n <slot name=\"unit\">m<sup>3</sup></slot>\n </slot>\n </div>\n `\n : isCompact\n ? html`\n <div class=\"readout readout-compact\">\n <slot name=\"readout\">\n ${this.showTrendSymbol ? this.trendIcon() : null}\n <span class=\"percent\"\n >${percent.toFixed(0)}<span class=\"percent-symbol\"\n >%</span\n ></span\n >\n </slot>\n </div>\n `\n : html`\n <div class=\"readout\">\n <slot name=\"readout\">\n <div class=\"header\">\n ${this.showTrendSymbol ? this.trendIcon() : null}\n <div class=\"percent\">\n ${percent.toFixed(this.percentFractionDigits)}<span\n class=\"percent-symbol\"\n >%</span\n >\n </div>\n </div>\n <div class=\"value\">\n <div class=\"current\">\n <slot name=\"current-value\" class=\"current-value\"\n >${this.value.toFixed(0)}</slot\n ><span class=\"divider\">/</span>\n </div>\n <div class=\"max\">\n <slot class=\"max-value\" name=\"max-value\"\n >${this.max.toFixed(0)}</slot\n >\n <slot class=\"unit\" name=\"unit\">m<sup>3</sup></slot>\n </div>\n </div>\n <div class=\"rich-divider\" ?hidden=${!this._hasRichSlot}></div>\n <slot name=\"rich\" @slotchange=${this._onRichSlotChange}></slot>\n </slot>\n </div>\n `;\n // Decorative icon overlay (centered on the chart cell). Two stacked\n // icon elements: the back layer paints an inherited SVG `stroke` halo\n // (the icon's paths use `fill=\"currentColor\"`, and SVG `stroke` is\n // inherited across the icon's shadow DOM); the front layer paints the\n // colored fill via the `color` property. This mirrors the established\n // silhouette pattern used by `obc-automation-button` (back-layer slot\n // with stroke, front-layer slot with fill).\n //\n // The icon variant follows `type`: battery tanks show an energy-battery\n // icon, all other types show the generic tank icon.\n const graphIconClasses = classMap({\n 'graph-icon': true,\n 'priority-enhanced': this.priority === Priority.enhanced,\n });\n const graphIconOverlay =\n this.type === TankType.battery\n ? html`\n <div class=${graphIconClasses} aria-hidden=\"true\">\n <obi-energy-battery\n class=\"graph-icon-stroke\"\n ></obi-energy-battery>\n <obi-energy-battery class=\"graph-icon-fill\"></obi-energy-battery>\n </div>\n `\n : html`\n <div class=${graphIconClasses} aria-hidden=\"true\">\n <obi-tank class=\"graph-icon-stroke\"></obi-tank>\n <obi-tank class=\"graph-icon-fill\"></obi-tank>\n </div>\n `;\n // Alert-frame overlay. Mirrors the `obc-automation-button` pattern\n // exactly so consumer API is identical. Placed inside `.halo` so the\n // ring hugs only the bordered tank area; `belowHalo` (tag / readout) is\n // unaffected. `.halo` is `position: relative` (see CSS) to anchor the\n // absolutely-positioned alert-frame.\n const alertFrameOverlay = this.alert\n ? html`<obc-alert-frame\n class=\"alert-frame\"\n .type=${this.alertFrameType}\n .thickness=${this.alertFrameThickness}\n .status=${this.alertFrameStatus}\n .showAlertCategoryIcon=${this.showAlertCategoryIcon}\n .showIcon=${this.showAlertIcon}\n >\n <span slot=\"icon\"><slot name=\"alert-icon\"></slot></span>\n <span slot=\"label\"><slot name=\"alert-label\"></slot></span>\n <span slot=\"timer\"><slot name=\"alert-timer\"></slot></span>\n </obc-alert-frame>`\n : null;\n let chartCell: HTMLTemplateResult;\n if (this._usesGaugeTrend) {\n // Forward the measured cell size as the gauge-trend's width/height,\n // which it interprets as an aspect-ratio reference. Its internal\n // ResizeObserver then picks up its own wrapper's clientWidth (= cell\n // width, since we set style width:100%) and computes height from the\n // aspect ratio — so the chart matches the cell exactly. We delay\n // rendering until both dimensions are measured to avoid a divide-by-\n // zero in the aspect-ratio math on first paint.\n const hasSize = this._cellWidth > 0 && this._cellHeight > 0;\n chartCell = html`\n <div class=\"bar-container chart-cell\">\n ${hasSize\n ? html`<obc-gauge-trend\n .data=${this.chartData}\n .minValue=${0}\n .maxValue=${safeMax}\n .value=${this.value}\n .hasBar=${this.chartMode === TankChartMode.graphAndBar}\n .hasScale=${false}\n .hasLabelPadding=${false}\n .chartFill=${true}\n .hasAdvice=${this.hasAdvice}\n .advice=${this.advice}\n .width=${this._cellWidth}\n .height=${this._cellHeight}\n .setpoint=${this.setpoint}\n .newSetpoint=${this.newSetpoint}\n .touching=${this.touching}\n .atSetpoint=${this.atSetpoint}\n .autoAtSetpoint=${this.autoAtSetpoint}\n .autoAtSetpointDeadband=${this.autoAtSetpointDeadband}\n .setpointAtZeroDeadband=${this.setpointAtZeroDeadband}\n .setpointOverride=${this.setpointOverride}\n .animateSetpoint=${this.animateSetpoint}\n style=\"width: 100%; height: 100%;\"\n .priority=${this.priority}\n ></obc-gauge-trend>`\n : null}\n ${this.hasGraphIcon ? graphIconOverlay : null}\n </div>\n `;\n } else {\n // Bar mode: render the shared SVG bar (`obc-bar-vertical` /\n // `-horizontal`) — the same renderer used by gauge-trend's side bar —\n // so advice overlays work the same way across all three chart modes.\n //\n // TODO(refactor): once `obc-gauge-trend` supports a bar-only layout at\n // this size (no chart-line area), migrate this branch to gauge-trend\n // too so all three chart modes share a single implementation.\n //\n // TODO(theming): the bar fill currently uses the standard instrument\n // palette (`--instrument-regular-tertiary-color` for tint mode). The\n // legacy CSS bar used per-medium colors (e.g. `--automation-medium-fuel`).\n // Extend `external-scale.ts` (and the bar-vertical/-horizontal wrappers)\n // to accept a medium / fuel color so the tank can restore its\n // per-`medium` coloring through the shared renderer.\n const hasSize = this._cellWidth > 0 && this._cellHeight > 0;\n // The inner bar always renders vertically, regardless of the tank's\n // outer `orientation`. Only the tank's outer wrapper flips; its inner\n // chart cell stays portrait. This matches gauge-trend behavior.\n //\n // Match gauge-trend's proportional sizing so advice pills (and other\n // fixed-pixel SVG primitives) render at the same on-screen size in\n // every chart mode. We mirror gauge-trend's defaults exactly:\n // `fixedAspectRatio=true` + `scaleReferenceSize=384`. The bar SVG is\n // then built in a 384-unit-tall viewBox and shrunk into the cell via\n // `xMidYMid meet`, giving on-screen scale = cellHeight / 384.\n //\n // Because the meet transform scales the viewBox uniformly, we must\n // also size `barThickness` in viewBox units (not cell pixels) so the\n // bar still fills the cell width after scaling. We pick the viewBox\n // cross-axis size so width-fit and height-fit ratios match exactly\n // (no horizontal gutters): viewBoxCross = cellWidth * 384 / cellHeight.\n const SCALE_REFERENCE_SIZE = 384;\n // The bar's viewBox cross-axis includes an outside-bar band for advice\n // pills and/or the setpoint marker (see computeExternalScaleLayout).\n // Subtract the same band the bar will reserve so total viewBox width\n // still equals viewBoxCross and the meet-scale fills the cell exactly.\n const hasSetpointMarker =\n this.setpoint !== undefined ||\n this.newSetpoint !== undefined ||\n this.departingNewSetpoint !== undefined;\n const outsideBarReserveVb = Math.max(\n this.hasAdvice ? 16 : 0,\n computeSetpointBandThickness({hasSetpoint: hasSetpointMarker})\n );\n const safeCellHeight = Math.max(1, this._cellHeight);\n const viewBoxCross =\n (this._cellWidth * SCALE_REFERENCE_SIZE) / safeCellHeight;\n const barThickness = Math.max(0, viewBoxCross - outsideBarReserveVb);\n // `tint` is locked in for tank bar mode: it draws a fill from 0 to\n // value plus a small marker at the value position, which mirrors the\n // legacy CSS bar's \"fill + top border at value\" visual idiom.\n const barWrapper = html`<obc-bar-vertical\n .minValue=${0}\n .maxValue=${safeMax}\n .value=${this.value}\n .height=${this._cellHeight}\n .fixedAspectRatio=${true}\n .scaleReferenceSize=${SCALE_REFERENCE_SIZE}\n .paddingTop=${0}\n .paddingBottom=${0}\n .side=${ExternalScaleSide.right}\n .hasBar=${true}\n .hasScale=${false}\n .showLabels=${false}\n .barThickness=${barThickness}\n .fillMode=${FillMode.tint}\n .instrumentMode=${true}\n .borderRadius=${2}\n .advices=${this.hasAdvice ? this.advice : []}\n .advicePosition=${AdvicePosition.inner}\n .setpoint=${this.setpoint}\n .newSetpoint=${this.newSetpoint}\n .touching=${this.touching}\n .atSetpoint=${this.atSetpoint}\n .autoAtSetpoint=${this.autoAtSetpoint}\n .autoAtSetpointDeadband=${this.autoAtSetpointDeadband}\n .setpointAtZeroDeadband=${this.setpointAtZeroDeadband}\n .setpointOverride=${this.setpointOverride}\n .animateSetpoint=${this.animateSetpoint}\n style=\"width: 100%; height: 100%;\"\n .priority=${this.priority}\n ></obc-bar-vertical>`;\n chartCell = html`\n <div class=\"bar-container bar-cell\">\n ${hasSize ? barWrapper : null}\n ${this.hasGraphIcon ? graphIconOverlay : null}\n </div>\n `;\n }\n\n const frameClasses = classMap({\n 'tank-frame': true,\n [`type-${this.type}`]: true,\n [`orientation-${this.orientation}`]: true,\n [this.medium]: true,\n compact: isCompact,\n static: this.static,\n });\n\n let middleContents: HTMLTemplateResult;\n if (this.static) {\n middleContents = html`<div class=\"static-readout\">${readoutCell}</div>`;\n } else if (this.compact) {\n middleContents = chartCell;\n } else {\n middleContents = html`<div class=\"grid\">\n ${tagCell}${badgesCell}${readoutCell}${chartCell}\n </div>`;\n }\n\n const tankFrame = html`\n <div class=${frameClasses}>\n ${this.renderCap('start')}\n <div class=\"middle\">${middleContents}</div>\n ${this.renderCap('end')}\n </div>\n `;\n\n // Whole component is one interactive element. The halo (flat-mixin\n // border/background that appears on hover/pressed/focus) is painted on\n // the inner `.halo` wrapper via `visibleWrapperClass`, so the surround\n // hugs only the bordered area:\n // - non-compact: just the tank-frame (grid inside it carries badges,\n // readout, tag).\n // - compact: fixed-size column flex — badges, tank-frame (flex-grow),\n // readout, tag. Empty cells (badges/tag) collapse via `?hidden`\n // above so the tank-frame absorbs the freed space.\n // - static: same as compact but no separate readout cell (the readout\n // is centered INSIDE the tank-frame via `static-readout`).\n //\n // The alert-frame overlay is the last child of `.halo` so the ring\n // overlays every cell (and any cell-collapse mechanics work uniformly).\n let haloContents: HTMLTemplateResult;\n if (this.static) {\n haloContents = html`${badgesCell}${tankFrame}${tagCell}`;\n } else if (this.compact) {\n haloContents = html`${badgesCell}${tankFrame}${readoutCell}${tagCell}`;\n } else {\n haloContents = tankFrame;\n }\n const halo = html`\n <div class=\"halo\">${haloContents}${alertFrameOverlay}</div>\n `;\n\n // The `activated` class goes on the interactive `.root` so the shared\n // `flat` style mixin paints the activated background/border on `.halo`\n // (its `visibleWrapperClass`), same as the mixin's hover/pressed states.\n const rootClasses = classMap({root: true, activated: this.activated});\n\n // `aria-live=\"polite\"` + `aria-atomic=\"true\"` on the root so the\n // slotted alert label (and any state change of the alert frame) is\n // announced once when `alert` flips on. Always present — an empty live\n // region is harmless and avoids screen-reader re-registration races.\n // TODO(a11y): the rest of the automation component family still lacks\n // this live-region announcement; consolidate when alert support is\n // factored into a shared mixin.\n return html`\n ${this.static\n ? html`<div\n class=${rootClasses}\n role=\"img\"\n aria-label=${this.tag || 'Tank'}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n ${halo}\n </div>`\n : html`<button\n class=${rootClasses}\n type=\"button\"\n aria-label=${this.tag || 'Tank'}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n ${halo}\n </button>`}\n `;\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-automation-tank': ObcAutomationTank;\n }\n}\n"],"names":["TankTrend","TankType","TankOrientation","TankPositioning","TankChartMode"],"mappings":"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"}
1
+ {"version":3,"file":"automation-tank.js","sources":["../../../src/automation/automation-tank/automation-tank.ts"],"sourcesContent":["import {\n HTMLTemplateResult,\n LitElement,\n html,\n nothing,\n svg,\n unsafeCSS,\n} from 'lit';\nimport {property, state} from 'lit/decorators.js';\nimport compentStyle from './automation-tank.css?inline';\nimport {LineMedium} from '../index.js';\nimport '../automation-badge/automation-badge.js';\nimport {ObcAutomationBadgeType} from '../automation-badge/automation-badge.js';\nimport {\n AutomationButtonBadgeAlert,\n AutomationButtonBadgeCommandLocked,\n AutomationButtonBadgeControl,\n AutomationButtonBadgeInterlock,\n} from '../automation-button/abstract-automation-button.js';\n\nimport '../../icons/icon-chevron-double-up-google.js';\nimport '../../icons/icon-chevron-up-google.js';\nimport '../../icons/icon-chevron-double-down-google.js';\nimport '../../icons/icon-chevron-down-google.js';\nimport '../../icons/icon-arrow-right-google.js';\nimport '../../icons/icon-off.js';\nimport '../../icons/icon-tank.js';\nimport '../../icons/icon-energy-battery.js';\nimport '../../navigation-instruments/gauge-trend/gauge-trend.js';\nimport '../../building-blocks/bar-vertical/bar-vertical.js';\nimport '../../components/alert-frame/alert-frame.js';\nimport {Priority} from '../../navigation-instruments/types.js';\nimport {\n ObcAlertFrameThickness,\n ObcAlertFrameType,\n} from '../../components/alert-frame/alert-frame.js';\nimport {AlertType} from '../../types.js';\nimport type {ChartLineDataItem} from '../../building-blocks/chart-line/chart-line-base.js';\nimport type {LinearAdvice} from '../../building-blocks/instrument-linear/advice.js';\nimport {\n AdvicePosition,\n ExternalScaleSide,\n FillMode,\n computeSetpointBandThickness,\n} from '../../building-blocks/external-scale/external-scale.js';\nimport {classMap} from 'lit/directives/class-map.js';\nimport {customElement} from '../../decorator.js';\nimport {SetpointMixin} from '../../svghelpers/setpoint-mixin.js';\n\nexport enum TankTrend {\n fastRising = 'fast-rising',\n rising = 'rising',\n stable = 'stable',\n falling = 'falling',\n fastFalling = 'fast-falling',\n closed = 'closed',\n}\n\nexport enum TankType {\n generic = 'generic',\n atmospheric = 'atmospheric',\n pressurized = 'pressurized',\n battery = 'battery',\n}\n\nexport enum TankOrientation {\n vertical = 'vertical',\n horizontal = 'horizontal',\n}\n\n/**\n * Host positioning model.\n *\n * - `point`: the host has fixed default dimensions (per\n * orientation and compact / static variant) and a P&ID anchor — the\n * visual content is shifted with `translateX(-50%)` and (in non-compact)\n * `top: -20px` so the tank's top-center aligns with the host's top-left\n * placement coordinate. Use this when dropping the tank onto a P&ID\n * canvas at a pipe-grid coordinate.\n * - `button` (default): the host fills its parent container (100% × 100%) with no\n * anchor offset. Use this when embedding the tank inside a sized layout\n * slot — the parent controls the footprint and the tank renders\n * responsively inside it, just like a regular button. Compact / static\n * inner layout still applies; only the host box is changed.\n *\n * If the parent leaves *one* axis indefinite — a flex/grid slot sized with\n * `min-height`/`max-height` rather than `height`, or a cross axis freed by\n * `align-self: center` — the corresponding `100%` computes to `auto` and the\n * tank derives that axis from the other one through the design aspect ratio\n * of the matching `point` footprint (256×376 vertical, 420×256 horizontal,\n * 170×282 compact/static, 244×208 horizontal compact/static). The size then\n * does not depend on the chart cell, which takes its own size from the cell\n * it was measured in and would otherwise make the constraint circular\n * (issue #1121).\n *\n * If *both* axes are indefinite (a shrink-to-fit parent, e.g. an unsized\n * `inline-block`) there is no axis left to derive from, so the host falls\n * back to its text content. That is stable and non-circular, but much\n * smaller than the design footprint — give the parent a definite size on at\n * least one axis, and on both whenever the exact footprint matters.\n */\nexport enum TankPositioning {\n point = 'point',\n button = 'button',\n}\n\nexport enum TankChartMode {\n /** Static fill bar driven by `value` / `max` (default, backward compatible). */\n bar = 'bar',\n /** Embedded `obc-gauge-trend` line/area chart, no side bar. */\n graph = 'graph',\n /** Embedded `obc-gauge-trend` line/area chart with a side bar. */\n graphAndBar = 'graph-and-bar',\n}\n\n/**\n *\n *\n * Setpoint properties are inherited from {@link SetpointMixin}\n * (`setpoint`, `newSetpoint`, `touching`, `atSetpoint`, `autoAtSetpoint`,\n * `autoAtSetpointDeadband`, `setpointAtZeroDeadband`, `setpointOverride`,\n * `animateSetpoint`) and are forwarded to the embedded chart: the SVG bar in\n * `bar` mode, or `obc-gauge-trend` in the graph modes (where the marker\n * renders on the side bar, i.e. `graph-and-bar`). Values share the tank's\n * `value` / `max` scale.\n *\n * @slot badges - Custom badges to be displayed in the badge area.\n * @slot tag - Text or element for the tank's tag/label.\n * @slot readout - Replaces the entire readout content block.\n * @slot max-value - Content for the capacity value.\n * @slot unit - Content for the unit of measurement.\n * @slot current-value - Content for the current level value.\n * @slot rich - Detail rows below the main readout, shown in the regular\n * (non-compact, non-static) layout. Slot an `<obc-readout-list>` of\n * `<obc-readout-list-item>` rows — it owns the row typography and cross-row\n * column alignment; the tank renders a divider above it automatically when\n * the slot is filled.\n * @slot alert-icon - Custom icon for the alert frame.\n * @slot alert-label - Label for the alert frame.\n * @slot alert-timer - Timer for the alert frame.\n * @fires click - Fired when the tank is clicked. When `clickable` is `false` the tank renders a plain `<div>`, and in `static` mode a `<div role=\"img\">`, instead of a `<button>` — in both cases it is not focusable or keyboard-activatable; pointer clicks still reach the host.\n * @beta\n */\n@customElement('obc-automation-tank')\nexport class ObcAutomationTank extends SetpointMixin(LitElement) {\n @property({type: String}) medium: LineMedium = LineMedium.water;\n @property({type: Number}) value: number = 0;\n @property({type: Number}) max: number = 100;\n @property({type: String}) trend: TankTrend = TankTrend.stable;\n @property({type: String, reflect: true}) type: TankType = TankType.generic;\n @property({type: String, reflect: true}) orientation: TankOrientation =\n TankOrientation.vertical;\n @property({type: Boolean, reflect: true}) compact: boolean = false;\n /**\n * Host positioning model — see `TankPositioning` for details. Defaults to\n * `button` (host fills parent container, 100% × 100%, falling back to the\n * design aspect ratio on any axis the parent leaves indefinite, no anchor\n * offset). Set to `point` for the legacy P&ID canvas mode (fixed default\n * dimensions + top-center anchor offset).\n */\n @property({type: String, reflect: true}) positioning: TankPositioning =\n TankPositioning.button;\n /**\n * Static (display-only) variant. Always rendered at the compact size; the\n * inner chart/bar is hidden, the bordered area is filled with\n * `--container-section-color`, and the readout is centered inside the frame.\n * Tag is rendered below the bordered area; badges (when slotted) sit above\n * the frame inside the halo and shrink the frame just like in compact mode.\n * Overrides `compact` (a static tank is always compact-sized).\n *\n * Static tanks render as a non-interactive `<div role=\"img\">` (not a\n * `<button>`), so they are not in the tab order and do not announce as\n * activatable controls.\n */\n @property({type: Boolean, reflect: true}) static: boolean = false;\n /**\n * Whether the tank is interactive. `true` (default) renders the root as a\n * `<button>` with the full flat-mixin interaction surface. `false` renders a\n * non-interactive `<div>` — the resting appearance is unchanged (same\n * enabled-state colors and the same 1px border box, via the mixin's\n * `noClick` variant), but the hover / pressed / focus-visible states are\n * gone and the tank leaves the tab order.\n *\n * Everything else keeps rendering: the chart / bar, badges, readout, tag and\n * the `alert` frame all behave exactly as they do on a clickable tank. Use\n * this for a display-only tank that still shows live data — e.g. a row total\n * aggregating the tanks beside it. For \"device present, current state\n * unknown\" use `static` instead, which also hides the chart and shrinks to\n * the compact footprint.\n *\n * `static` is already non-interactive, so this has no effect there.\n *\n * Property-only (`attribute: false`, per the repo's positive-default-true\n * boolean convention — a `true` default cannot round-trip through an HTML\n * boolean attribute). Set it as a property: `el.clickable = false`,\n * `.clickable=${false}` in a Lit template, or the equivalent binding in the\n * React / Vue / Angular / Svelte wrappers. A `clickable=\"false\"` attribute in\n * plain HTML is **not** observed and leaves the tank interactive.\n */\n @property({type: Boolean, attribute: false}) clickable: boolean = true;\n /**\n * Enables the activated background color, used to indicate that the tank is\n * activated/selected. Requires an interactive tank — the `noClick` mixin\n * variant used when `clickable` is `false` only paints the enabled state, so\n * a non-clickable tank ignores this (matching `obc-elevated-card`).\n * @availableWhen clickable==true\n */\n @property({type: Boolean}) activated: boolean = false;\n @property({type: String}) tag: string = '';\n\n /**\n * Chart cell rendering mode.\n * - `bar`: static fill bar driven by `value`/`max` (default).\n * - `graph`: embedded `obc-gauge-trend` line/area chart.\n * - `graphAndBar`: embedded `obc-gauge-trend` with an integrated side bar.\n */\n @property({type: String, reflect: true, attribute: 'chart-mode'})\n chartMode: TankChartMode = TankChartMode.bar;\n\n /** Time-series data points for the embedded gauge-trend (graph modes only). */\n @property({type: Array, attribute: false})\n chartData: ChartLineDataItem[] = [];\n\n /** Priority hint forwarded to child charts (regular | enhanced). */\n @property({type: String}) priority: Priority = Priority.regular;\n\n /**\n * Advice overlays. Forwarded to the embedded `obc-gauge-trend` in\n * `graph` / `graph-and-bar` modes, or rendered as pills over the static\n * bar in `bar` mode.\n */\n @property({type: Array, attribute: false})\n advice: LinearAdvice[] = [];\n\n /** Show advice overlays (works in all `chartMode` variants). */\n @property({type: Boolean}) hasAdvice = false;\n\n /**\n * Overlay a 32×32 decorative icon (currently `<obi-tank>`) centered on the\n * chart cell. Works in all three `chartMode` variants (bar, graph,\n * graph-and-bar) and in both orientations. The icon is rendered above the\n * bar/graph in a fixed CSS layer (does not scale with the SVG meet\n * transform) and is silhouetted with a `--border-silhouette-color` halo so\n * it stays legible on any underlying fill. The icon size scales with the\n * ambient `obc-component-size-*` class on an ancestor via the design token\n * `--automation-components-tanks-graphs-graph-icon-size` (32 → 48 → 64 → 64\n * for regular → medium → large → xl).\n *\n * TODO(future): replace the hard-coded `<obi-tank>` with a `slot=\"graph-icon\"`\n * so consumers can pass any `<obi-*>` icon. The current API ships the\n * smallest viable surface; the slot can be added without breaking the\n * boolean property.\n */\n @property({type: Boolean, attribute: false}) hasGraphIcon = false;\n\n /**\n * Show an `<obc-alert-frame>` overlay around the bordered tank area (the\n * `.halo` wrapper). Mirrors the API of `obc-automation-button`: same six\n * properties, same three slots (`alert-icon`, `alert-label`, `alert-timer`).\n * The ring overlays `.halo` only, so the tag and readout that sit outside\n * the halo in compact / static layouts remain unaffected.\n */\n @property({type: Boolean}) alert: boolean = false;\n @property({type: String}) alertFrameType: ObcAlertFrameType =\n ObcAlertFrameType.SmallSideFlip;\n @property({type: String}) alertFrameThickness: ObcAlertFrameThickness =\n ObcAlertFrameThickness.Small;\n @property({type: String}) alertFrameStatus: AlertType = AlertType.Alarm;\n @property({type: Boolean, attribute: false}) showAlertCategoryIcon: boolean =\n true;\n @property({type: Boolean}) showAlertIcon: boolean = false;\n\n /**\n * Show the trend chevron / off icon next to the percent readout. Default\n * `true` preserves existing behavior. Set to `false` to hide the trend\n * indicator in both compact and non-compact readouts — useful when the\n * trend is not meaningful for a given tank, or when the consumer wants\n * to keep the readout compact. Has no effect in `static` mode, which\n * renders the tank's capacity (`max` + `unit`) instead of a percent and\n * intentionally omits the trend indicator (a static tank represents\n * \"device present, current state unknown\"). `attribute: false` per the\n * repo's positive-default-true boolean convention.\n */\n @property({type: Boolean, attribute: false}) showTrendSymbol: boolean = true;\n\n /**\n * Number of fraction digits used to format the percent readout in the\n * non-compact (regular) layout. Defaults to `0` (integer percent). The\n * compact layout always renders integer percent to keep its fixed-width\n * footprint stable. The static layout renders capacity (`max` + `unit`)\n * rather than percent, so this property has no effect there — pass a\n * pre-formatted value through the `max-value` slot if fractional\n * precision is needed (see the `WithFractionDigits` story).\n */\n @property({type: Number}) percentFractionDigits: number = 0;\n\n /**\n * Enum-driven badges rendered inside the `badges` cell. Mirrors the API\n * introduced for `ObcAbstractAutomationButton` in PR #839 (#829). When set\n * to a non-`None` value, an `<obc-automation-badge>` of the corresponding\n * type is rendered as fallback content for the `badges` slot. Any content\n * the consumer slots into `badges` overrides these defaults, preserving\n * backward compatibility with the existing slot-based API.\n *\n * Render order (left → right): control, alert, interlock, commandLocked —\n * matching the positional convention used by `ObcAbstractAutomationButton`\n * (top-left, top-right, bottom-left, bottom-right) read row-by-row.\n */\n @property({type: String}) badgeControl: AutomationButtonBadgeControl =\n AutomationButtonBadgeControl.None;\n @property({type: String}) badgeAlert: AutomationButtonBadgeAlert =\n AutomationButtonBadgeAlert.None;\n @property({type: String}) badgeInterlock: AutomationButtonBadgeInterlock =\n AutomationButtonBadgeInterlock.None;\n @property({type: String})\n badgeCommandLocked: AutomationButtonBadgeCommandLocked =\n AutomationButtonBadgeCommandLocked.None;\n\n @state() private _cellWidth = 0;\n @state() private _cellHeight = 0;\n /**\n * Tracks whether the `badges` and `tag` slots have assigned content. Used\n * in compact/static mode to collapse empty cells to 0px so the tank-frame\n * can absorb the freed space (per spec: \"if there are no badges it would\n * grow upward; if there are no readout or tags it would grow downward\").\n * Non-compact (regular) layout keeps its grid-based reserved rows.\n */\n @state() private _hasBadges = false;\n @state() private _hasTagSlot = false;\n /**\n * Tracks whether the `rich` slot has assigned content, so the regular\n * (non-compact) layout only draws the rich divider when a consumer has\n * slotted detail rows (canonically an `<obc-readout-list>`). Mirrors\n * `_hasBadges` / `_hasTagSlot`.\n */\n @state() private _hasRichSlot = false;\n private _chartResizeObserver?: ResizeObserver;\n private _observedCell?: Element;\n\n private get isCompact(): boolean {\n return this.compact || this.static;\n }\n\n private _badgeAlertType(): ObcAutomationBadgeType | null {\n switch (this.badgeAlert) {\n case AutomationButtonBadgeAlert.Silence:\n return ObcAutomationBadgeType.AlertSilenced;\n case AutomationButtonBadgeAlert.Caution:\n return ObcAutomationBadgeType.Caution;\n case AutomationButtonBadgeAlert.Warning:\n return ObcAutomationBadgeType.Warning;\n case AutomationButtonBadgeAlert.Alarm:\n return ObcAutomationBadgeType.Alarm;\n case AutomationButtonBadgeAlert.LevelCritical:\n return ObcAutomationBadgeType.LevelCritical;\n case AutomationButtonBadgeAlert.LevelHigh:\n return ObcAutomationBadgeType.LevelHigh;\n case AutomationButtonBadgeAlert.LevelMedium:\n return ObcAutomationBadgeType.LevelMedium;\n case AutomationButtonBadgeAlert.LevelLow:\n return ObcAutomationBadgeType.LevelLow;\n case AutomationButtonBadgeAlert.LevelDiagnostic:\n return ObcAutomationBadgeType.LevelDiagnostic;\n default:\n return null;\n }\n }\n\n private _badgeControlType(): ObcAutomationBadgeType | null {\n switch (this.badgeControl) {\n case AutomationButtonBadgeControl.Local:\n return ObcAutomationBadgeType.Local;\n case AutomationButtonBadgeControl.LocalOnly:\n return ObcAutomationBadgeType.LocalOnly;\n case AutomationButtonBadgeControl.Manual:\n return ObcAutomationBadgeType.Manual;\n case AutomationButtonBadgeControl.ManualOnly:\n return ObcAutomationBadgeType.ManualOnly;\n case AutomationButtonBadgeControl.Auto:\n return ObcAutomationBadgeType.Auto;\n default:\n return null;\n }\n }\n\n private _badgeInterlockType(): ObcAutomationBadgeType | null {\n switch (this.badgeInterlock) {\n case AutomationButtonBadgeInterlock.Interlock:\n return ObcAutomationBadgeType.Interlock;\n case AutomationButtonBadgeInterlock.InterlockInhibit:\n return ObcAutomationBadgeType.InterlockInhibit;\n default:\n return null;\n }\n }\n\n private _badgeCommandLockedType(): ObcAutomationBadgeType | null {\n if (\n this.badgeCommandLocked ===\n AutomationButtonBadgeCommandLocked.CommandLocked\n ) {\n return ObcAutomationBadgeType.CommandLocked;\n }\n return null;\n }\n\n private get _usesGaugeTrend(): boolean {\n return (\n this.chartMode === TankChartMode.graph ||\n this.chartMode === TankChartMode.graphAndBar\n );\n }\n\n private _onBadgesSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasBadges = slot\n .assignedNodes({flatten: true})\n .some(\n (n) =>\n n.nodeType === Node.ELEMENT_NODE ||\n (n.nodeType === Node.TEXT_NODE && !!n.textContent?.trim())\n );\n }\n\n private _onTagSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasTagSlot = slot\n .assignedNodes({flatten: true})\n .some(\n (n) =>\n n.nodeType === Node.ELEMENT_NODE ||\n (n.nodeType === Node.TEXT_NODE && !!n.textContent?.trim())\n );\n }\n\n private _onRichSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasRichSlot = slot\n .assignedNodes({flatten: true})\n .some(\n (n) =>\n n.nodeType === Node.ELEMENT_NODE ||\n (n.nodeType === Node.TEXT_NODE && !!n.textContent?.trim())\n );\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._chartResizeObserver?.disconnect();\n this._chartResizeObserver = undefined;\n this._observedCell = undefined;\n }\n\n protected override updated(\n changed: Map<string | number | symbol, unknown>\n ): void {\n super.updated(changed);\n // No imperative forwarding here — child components receive `priority`\n // via template property bindings (`.priority=${this.priority}`). Rely on\n // their reactive `updated()` logic (e.g. `ObcGaugeTrend._updateBarVerticalProperties()`)\n // to propagate palette changes internally.\n // The chart cell DOM element is recreated when chartMode toggles between\n // bar and graph modes (different class/contents), and may also appear/\n // disappear with static/compact. Re-attach the observer to the current\n // .bar-container instance.\n this._syncChartResizeObserver();\n }\n\n private _syncChartResizeObserver(): void {\n // Observe the bar-container in BOTH bar mode and graph modes — the bar\n // mode now renders an SVG `obc-bar-vertical` / `-horizontal` that needs\n // measured pixel dimensions for its viewBox and barThickness.\n const cell = this.renderRoot.querySelector('.bar-container');\n if (!cell) {\n if (this._chartResizeObserver) {\n this._chartResizeObserver.disconnect();\n this._observedCell = undefined;\n }\n return;\n }\n if (cell === this._observedCell) return;\n\n if (!this._chartResizeObserver) {\n this._chartResizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0];\n if (!entry) return;\n const w = Math.round(entry.contentRect.width);\n const h = Math.round(entry.contentRect.height);\n if (w !== this._cellWidth) this._cellWidth = w;\n if (h !== this._cellHeight) this._cellHeight = h;\n });\n } else {\n this._chartResizeObserver.disconnect();\n }\n this._chartResizeObserver.observe(cell);\n this._observedCell = cell;\n }\n\n trendIcon(): HTMLTemplateResult | typeof nothing {\n if (this.trend === TankTrend.fastRising) {\n return html`<obi-chevron-double-up-google\n class=\"trend-icon\"\n ></obi-chevron-double-up-google>`;\n } else if (this.trend === TankTrend.rising) {\n return html`<obi-chevron-up-google\n class=\"trend-icon\"\n ></obi-chevron-up-google>`;\n } else if (this.trend === TankTrend.fastFalling) {\n return html`<obi-chevron-double-down-google\n class=\"trend-icon\"\n ></obi-chevron-double-down-google>`;\n } else if (this.trend === TankTrend.falling) {\n return html`<obi-chevron-down-google\n class=\"trend-icon\"\n ></obi-chevron-down-google>`;\n } else if (this.trend === TankTrend.closed) {\n return html`<obi-off class=\"trend-icon\"></obi-off>`;\n } else {\n return html`<obi-arrow-right-google\n class=\"trend-icon\"\n ></obi-arrow-right-google>`;\n }\n }\n\n /**\n * Atmospheric cap, 3-piece (corner-start / stretchable middle arc / corner-end).\n * Each piece draws its own fill (closed) and stroke (open) so adjacent pieces\n * abut without a visible vertical seam line. Coordinates are derived from the\n * Figma 248x14 path; corners stay 12px wide while the middle arc stretches.\n * Position (start vs end) and orientation flipping are handled in CSS.\n *\n * Horizontal orientation reuses the same path data but rotates each piece\n * via an inner `<g transform=\"translate(0 H) rotate(-90)\">`, mapping the\n * source coord (x, y) to (y, H - x). The viewBox is swapped accordingly:\n * corner: 12x14 -> 14x12 (H=12)\n * middle: 224x14 -> 14x224 (H=224)\n * Cap-end (right side in horizontal) is mirrored via CSS scaleX(-1).\n */\n private renderAtmosphericCap(side: 'start' | 'end'): HTMLTemplateResult {\n const isHorizontal = this.orientation === TankOrientation.horizontal;\n\n // Path data. Two variants — regular (14px tall, 12px corners) and compact\n // (10px tall, 7px corners). Curve Y-values are proportionally scaled from\n // the original 18/12-tall Figma reference so the corner curves stay\n // continuous at the new thickness.\n // Horizontal orientation reuses the same vertical path data inside an\n // inner `<g transform=\"translate(0 H) rotate(-90)\">`; the SVG viewBox is\n // swapped accordingly. Cap-end is mirrored via CSS scaleY/scaleX(-1).\n let cornerStartFill: string;\n let cornerStartStroke: string;\n let midFill: string;\n let midStroke: string;\n let cornerEndFill: string;\n let cornerEndStroke: string;\n let cornerW: number;\n let capH: number;\n let midW: number;\n\n if (this.isCompact) {\n cornerW = 7;\n capH = 10;\n midW = 149;\n cornerStartFill =\n 'M 6.374 4.8214 C 6.138 4.8429, 6.005 4.8543, 5.83 4.8771 C 2.937 5.2557, 0.669 7.0957, 0.51 9.1936 C 0.5 9.3214, 0.5 9.4357, 0.5 9.6429 L 0.5 10 L 6.374 10 Z';\n cornerStartStroke =\n 'M 6.374 4.8214 C 6.138 4.8429, 6.005 4.8543, 5.83 4.8771 C 2.937 5.2557, 0.669 7.0957, 0.51 9.1936 C 0.5 9.3214, 0.5 9.4357, 0.5 9.6429 L 0.5 10';\n midFill =\n 'M 0 4.8214 C 10.97 3.8214, 37.83 1.7857, 74.626 1.7857 C 111.42 1.7857, 138.28 3.8214, 149.252 4.8214 L 149.252 10 L 0 10 Z';\n midStroke =\n 'M 0 4.8214 C 10.97 3.8214, 37.83 1.7857, 74.626 1.7857 C 111.42 1.7857, 138.28 3.8214, 149.252 4.8214';\n cornerEndFill =\n 'M 0 4.8214 C 0.236 4.8429, 0.369 4.8543, 0.544 4.8771 C 3.437 5.2557, 5.705 7.0957, 5.864 9.1936 C 5.874 9.3214, 5.874 9.4357, 5.874 9.6429 L 5.874 10 L 0 10 Z';\n cornerEndStroke =\n 'M 0 4.8214 C 0.236 4.8429, 0.369 4.8543, 0.544 4.8771 C 3.437 5.2557, 5.705 7.0957, 5.864 9.1936 C 5.874 9.3214, 5.874 9.4357, 5.874 9.6429 L 5.874 10';\n } else {\n cornerW = 12;\n capH = 14;\n midW = 224;\n cornerStartFill =\n 'M 12 6.5837 C 6.346 6.7737, 5.176 6.8744, 4.168 7.2537 C 2.6445 7.8272, 1.418 8.8983, 0.8613 10.1415 C 0.493 10.9641, 0.5 11.9099, 0.5 13.6111 L 0.5 14 L 12 14 Z';\n // Stroke draws the curve + outer vertical edge only. The seam with\n // `.middle` (y = capH) is painted by a 1px CSS `border-top` on\n // `.tank-frame.type-atmospheric:not(.compact) .middle` so it pixel-\n // snaps identically to the pressurized cap's CSS border. Drawing it\n // as an SVG stroke here would render thinner / blurrier due to\n // sub-pixel anti-aliasing.\n cornerStartStroke =\n 'M 12 6.5837 C 6.346 6.7737, 5.176 6.8744, 4.168 7.2537 C 2.6445 7.8272, 1.418 8.8983, 0.8613 10.1415 C 0.493 10.9641, 0.5 11.9099, 0.5 13.6111 L 0.5 14';\n midFill =\n 'M 0 6.5837 C 16 5.1124, 56 1.9444, 112 1.9444 C 168 1.9444, 208 5.1124, 224 6.5837 L 224 14 L 0 14 Z';\n midStroke =\n 'M 0 6.5837 C 16 5.1124, 56 1.9444, 112 1.9444 C 168 1.9444, 208 5.1124, 224 6.5837';\n cornerEndFill =\n 'M 0 6.5837 C 5.654 6.7737, 6.824 6.8744, 7.832 7.2537 C 9.3555 7.8272, 10.582 8.8983, 11.1387 10.1415 C 11.507 10.9641, 11.5 11.9099, 11.5 13.6111 L 11.5 14 L 0 14 Z';\n cornerEndStroke =\n 'M 0 6.5837 C 5.654 6.7737, 6.824 6.8744, 7.832 7.2537 C 9.3555 7.8272, 10.582 8.8983, 11.1387 10.1415 C 11.507 10.9641, 11.5 11.9099, 11.5 13.6111 L 11.5 14';\n }\n\n if (isHorizontal) {\n // Rotate vertical paths -90deg into a `capH`-wide column. Source coord\n // (x, y) maps to (y, cornerW - x) for corners and (y, midW - x) for mid.\n return html`\n <div class=\"cap cap-atmospheric cap-${side}\">\n <svg\n class=\"cap-corner cap-corner-start\"\n viewBox=\"0 0 ${capH} ${cornerW}\"\n preserveAspectRatio=\"xMidYMax meet\"\n aria-hidden=\"true\"\n >\n ${svg`\n <g transform=\"translate(0 ${cornerW}) rotate(-90)\">\n <path class=\"cap-fill\" d=\"${cornerStartFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" d=\"${cornerStartStroke}\" />\n </g>\n `}\n </svg>\n <svg\n class=\"cap-mid\"\n viewBox=\"0 0 ${capH} ${midW}\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n ${svg`\n <g transform=\"translate(0 ${midW}) rotate(-90)\">\n <path class=\"cap-fill\" d=\"${midFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" vector-effect=\"non-scaling-stroke\" d=\"${midStroke}\" />\n </g>\n `}\n </svg>\n <svg\n class=\"cap-corner cap-corner-end\"\n viewBox=\"0 0 ${capH} ${cornerW}\"\n preserveAspectRatio=\"xMidYMin meet\"\n aria-hidden=\"true\"\n >\n ${svg`\n <g transform=\"translate(0 ${cornerW}) rotate(-90)\">\n <path class=\"cap-fill\" d=\"${cornerEndFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" d=\"${cornerEndStroke}\" />\n </g>\n `}\n </svg>\n </div>\n `;\n }\n\n return html`\n <div class=\"cap cap-atmospheric cap-${side}\">\n <svg\n class=\"cap-corner cap-corner-start\"\n viewBox=\"0 0 ${cornerW} ${capH}\"\n preserveAspectRatio=\"xMaxYMid meet\"\n aria-hidden=\"true\"\n >\n ${svg`\n <path class=\"cap-fill\" d=\"${cornerStartFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" d=\"${cornerStartStroke}\" />\n `}\n </svg>\n <svg\n class=\"cap-mid\"\n viewBox=\"0 0 ${midW} ${capH}\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n ${svg`\n <path class=\"cap-fill\" d=\"${midFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" vector-effect=\"non-scaling-stroke\" d=\"${midStroke}\" />\n `}\n </svg>\n <svg\n class=\"cap-corner cap-corner-end\"\n viewBox=\"0 0 ${cornerW} ${capH}\"\n preserveAspectRatio=\"xMinYMid meet\"\n aria-hidden=\"true\"\n >\n ${svg`\n <path class=\"cap-fill\" d=\"${cornerEndFill}\" />\n <path class=\"cap-stroke\" fill=\"none\" d=\"${cornerEndStroke}\" />\n `}\n </svg>\n </div>\n `;\n }\n\n private renderCap(side: 'start' | 'end'): HTMLTemplateResult | null {\n if (this.type === TankType.generic) return null;\n if (this.type === TankType.battery && side === 'end') return null;\n if (this.type === TankType.atmospheric) {\n return this.renderAtmosphericCap(side);\n }\n if (this.type === TankType.pressurized) {\n return html`<div class=\"cap cap-pressurized cap-${side}\"></div>`;\n }\n // battery, start only\n return html`<div class=\"cap cap-battery cap-${side}\"></div>`;\n }\n\n override render() {\n const safeMax = this.max > 0 ? this.max : 1;\n const percent = Math.max(0, Math.min(100, (this.value / safeMax) * 100));\n const isCompact = this.isCompact;\n\n // In compact/static the `.halo` is a fixed-size column flex container\n // that holds badges + tank-frame + (compact only) readout + tag. To let\n // the tank-frame absorb the freed space when adjacent cells are empty,\n // we collapse the empty cells with the `hidden` attribute (see CSS).\n // In non-compact mode cells live inside the inner `.grid` and always\n // reserve their min-content rows — unchanged from previous behavior.\n const controlBadge = this._badgeControlType();\n const alertBadge = this._badgeAlertType();\n const interlockBadge = this._badgeInterlockType();\n const commandLockedBadge = this._badgeCommandLockedType();\n const hasEnumBadges =\n controlBadge !== null ||\n alertBadge !== null ||\n interlockBadge !== null ||\n commandLockedBadge !== null;\n\n const badgesHidden = isCompact && !this._hasBadges && !hasEnumBadges;\n const tagHidden = isCompact && !this._hasTagSlot && !this.tag;\n\n const badgesCell = html`\n <div class=\"badges\" ?hidden=${badgesHidden}>\n <slot name=\"badges\" @slotchange=${this._onBadgesSlotChange}>\n ${controlBadge\n ? html`<obc-automation-badge\n .type=${controlBadge}\n ></obc-automation-badge>`\n : nothing}\n ${alertBadge\n ? html`<obc-automation-badge\n .type=${alertBadge}\n ></obc-automation-badge>`\n : nothing}\n ${interlockBadge\n ? html`<obc-automation-badge\n .type=${interlockBadge}\n ></obc-automation-badge>`\n : nothing}\n ${commandLockedBadge\n ? html`<obc-automation-badge\n .type=${commandLockedBadge}\n ></obc-automation-badge>`\n : nothing}\n </slot>\n </div>\n `;\n const tagCell = html`\n <div class=\"tag\" ?hidden=${tagHidden}>\n <slot name=\"tag\" @slotchange=${this._onTagSlotChange}>${this.tag}</slot>\n </div>\n `;\n // Static mode intentionally renders the tank's capacity (max + unit)\n // instead of a percent reading: a static tank represents \"device is\n // present, current state unknown\", so a percent value would be\n // misleading. The trend icon is omitted for the same reason. Consumers\n // who need a custom label can still override the whole cell via the\n // `readout` slot, or supply formatted text through the existing\n // `max-value` / `unit` slots (no new API).\n const readoutCell = this.static\n ? html`\n <div class=\"readout readout-compact readout-static\">\n <slot name=\"readout\">\n <slot name=\"max-value\">${this.max.toFixed(0)}</slot>\n <slot name=\"unit\">m<sup>3</sup></slot>\n </slot>\n </div>\n `\n : isCompact\n ? html`\n <div class=\"readout readout-compact\">\n <slot name=\"readout\">\n ${this.showTrendSymbol ? this.trendIcon() : null}\n <span class=\"percent\"\n >${percent.toFixed(0)}<span class=\"percent-symbol\"\n >%</span\n ></span\n >\n </slot>\n </div>\n `\n : html`\n <div class=\"readout\">\n <slot name=\"readout\">\n <div class=\"header\">\n ${this.showTrendSymbol ? this.trendIcon() : null}\n <div class=\"percent\">\n ${percent.toFixed(this.percentFractionDigits)}<span\n class=\"percent-symbol\"\n >%</span\n >\n </div>\n </div>\n <div class=\"value\">\n <div class=\"current\">\n <slot name=\"current-value\" class=\"current-value\"\n >${this.value.toFixed(0)}</slot\n ><span class=\"divider\">/</span>\n </div>\n <div class=\"max\">\n <slot class=\"max-value\" name=\"max-value\"\n >${this.max.toFixed(0)}</slot\n >\n <slot class=\"unit\" name=\"unit\">m<sup>3</sup></slot>\n </div>\n </div>\n <div class=\"rich-divider\" ?hidden=${!this._hasRichSlot}></div>\n <slot name=\"rich\" @slotchange=${this._onRichSlotChange}></slot>\n </slot>\n </div>\n `;\n // Decorative icon overlay (centered on the chart cell). Two stacked\n // icon elements: the back layer paints an inherited SVG `stroke` halo\n // (the icon's paths use `fill=\"currentColor\"`, and SVG `stroke` is\n // inherited across the icon's shadow DOM); the front layer paints the\n // colored fill via the `color` property. This mirrors the established\n // silhouette pattern used by `obc-automation-button` (back-layer slot\n // with stroke, front-layer slot with fill).\n //\n // The icon variant follows `type`: battery tanks show an energy-battery\n // icon, all other types show the generic tank icon.\n const graphIconClasses = classMap({\n 'graph-icon': true,\n 'priority-enhanced': this.priority === Priority.enhanced,\n });\n const graphIconOverlay =\n this.type === TankType.battery\n ? html`\n <div class=${graphIconClasses} aria-hidden=\"true\">\n <obi-energy-battery\n class=\"graph-icon-stroke\"\n ></obi-energy-battery>\n <obi-energy-battery class=\"graph-icon-fill\"></obi-energy-battery>\n </div>\n `\n : html`\n <div class=${graphIconClasses} aria-hidden=\"true\">\n <obi-tank class=\"graph-icon-stroke\"></obi-tank>\n <obi-tank class=\"graph-icon-fill\"></obi-tank>\n </div>\n `;\n // Alert-frame overlay. Mirrors the `obc-automation-button` pattern\n // exactly so consumer API is identical. Placed inside `.halo` so the\n // ring hugs only the bordered tank area; `belowHalo` (tag / readout) is\n // unaffected. `.halo` is `position: relative` (see CSS) to anchor the\n // absolutely-positioned alert-frame.\n const alertFrameOverlay = this.alert\n ? html`<obc-alert-frame\n class=\"alert-frame\"\n .type=${this.alertFrameType}\n .thickness=${this.alertFrameThickness}\n .status=${this.alertFrameStatus}\n .showAlertCategoryIcon=${this.showAlertCategoryIcon}\n .showIcon=${this.showAlertIcon}\n >\n <span slot=\"icon\"><slot name=\"alert-icon\"></slot></span>\n <span slot=\"label\"><slot name=\"alert-label\"></slot></span>\n <span slot=\"timer\"><slot name=\"alert-timer\"></slot></span>\n </obc-alert-frame>`\n : null;\n let chartCell: HTMLTemplateResult;\n if (this._usesGaugeTrend) {\n // Forward the measured cell size as the gauge-trend's width/height,\n // which it interprets as an aspect-ratio reference. Its internal\n // ResizeObserver then picks up its own wrapper's clientWidth (= cell\n // width, since we set style width:100%) and computes height from the\n // aspect ratio — so the chart matches the cell exactly. We delay\n // rendering until both dimensions are measured to avoid a divide-by-\n // zero in the aspect-ratio math on first paint.\n const hasSize = this._cellWidth > 0 && this._cellHeight > 0;\n chartCell = html`\n <div class=\"bar-container chart-cell\">\n ${hasSize\n ? html`<obc-gauge-trend\n .data=${this.chartData}\n .minValue=${0}\n .maxValue=${safeMax}\n .value=${this.value}\n .hasBar=${this.chartMode === TankChartMode.graphAndBar}\n .hasScale=${false}\n .hasLabelPadding=${false}\n .chartFill=${true}\n .hasAdvice=${this.hasAdvice}\n .advice=${this.advice}\n .width=${this._cellWidth}\n .height=${this._cellHeight}\n .setpoint=${this.setpoint}\n .newSetpoint=${this.newSetpoint}\n .touching=${this.touching}\n .atSetpoint=${this.atSetpoint}\n .autoAtSetpoint=${this.autoAtSetpoint}\n .autoAtSetpointDeadband=${this.autoAtSetpointDeadband}\n .setpointAtZeroDeadband=${this.setpointAtZeroDeadband}\n .setpointOverride=${this.setpointOverride}\n .animateSetpoint=${this.animateSetpoint}\n style=\"width: 100%; height: 100%;\"\n .priority=${this.priority}\n ></obc-gauge-trend>`\n : null}\n ${this.hasGraphIcon ? graphIconOverlay : null}\n </div>\n `;\n } else {\n // Bar mode: render the shared SVG bar (`obc-bar-vertical` /\n // `-horizontal`) — the same renderer used by gauge-trend's side bar —\n // so advice overlays work the same way across all three chart modes.\n //\n // TODO(refactor): once `obc-gauge-trend` supports a bar-only layout at\n // this size (no chart-line area), migrate this branch to gauge-trend\n // too so all three chart modes share a single implementation.\n //\n // TODO(theming): the bar fill currently uses the standard instrument\n // palette (`--instrument-regular-tertiary-color` for tint mode). The\n // legacy CSS bar used per-medium colors (e.g. `--automation-medium-fuel`).\n // Extend `external-scale.ts` (and the bar-vertical/-horizontal wrappers)\n // to accept a medium / fuel color so the tank can restore its\n // per-`medium` coloring through the shared renderer.\n const hasSize = this._cellWidth > 0 && this._cellHeight > 0;\n // The inner bar always renders vertically, regardless of the tank's\n // outer `orientation`. Only the tank's outer wrapper flips; its inner\n // chart cell stays portrait. This matches gauge-trend behavior.\n //\n // Match gauge-trend's proportional sizing so advice pills (and other\n // fixed-pixel SVG primitives) render at the same on-screen size in\n // every chart mode. We mirror gauge-trend's defaults exactly:\n // `fixedAspectRatio=true` + `scaleReferenceSize=384`. The bar SVG is\n // then built in a 384-unit-tall viewBox and shrunk into the cell via\n // `xMidYMid meet`, giving on-screen scale = cellHeight / 384.\n //\n // Because the meet transform scales the viewBox uniformly, we must\n // also size `barThickness` in viewBox units (not cell pixels) so the\n // bar still fills the cell width after scaling. We pick the viewBox\n // cross-axis size so width-fit and height-fit ratios match exactly\n // (no horizontal gutters): viewBoxCross = cellWidth * 384 / cellHeight.\n const SCALE_REFERENCE_SIZE = 384;\n // The bar's viewBox cross-axis includes an outside-bar band for advice\n // pills and/or the setpoint marker (see computeExternalScaleLayout).\n // Subtract the same band the bar will reserve so total viewBox width\n // still equals viewBoxCross and the meet-scale fills the cell exactly.\n const hasSetpointMarker =\n this.setpoint !== undefined ||\n this.newSetpoint !== undefined ||\n this.departingNewSetpoint !== undefined;\n const outsideBarReserveVb = Math.max(\n this.hasAdvice ? 16 : 0,\n computeSetpointBandThickness({hasSetpoint: hasSetpointMarker})\n );\n const safeCellHeight = Math.max(1, this._cellHeight);\n const viewBoxCross =\n (this._cellWidth * SCALE_REFERENCE_SIZE) / safeCellHeight;\n const barThickness = Math.max(0, viewBoxCross - outsideBarReserveVb);\n // `tint` is locked in for tank bar mode: it draws a fill from 0 to\n // value plus a small marker at the value position, which mirrors the\n // legacy CSS bar's \"fill + top border at value\" visual idiom.\n const barWrapper = html`<obc-bar-vertical\n .minValue=${0}\n .maxValue=${safeMax}\n .value=${this.value}\n .height=${this._cellHeight}\n .fixedAspectRatio=${true}\n .scaleReferenceSize=${SCALE_REFERENCE_SIZE}\n .paddingTop=${0}\n .paddingBottom=${0}\n .side=${ExternalScaleSide.right}\n .hasBar=${true}\n .hasScale=${false}\n .showLabels=${false}\n .barThickness=${barThickness}\n .fillMode=${FillMode.tint}\n .instrumentMode=${true}\n .borderRadius=${2}\n .advices=${this.hasAdvice ? this.advice : []}\n .advicePosition=${AdvicePosition.inner}\n .setpoint=${this.setpoint}\n .newSetpoint=${this.newSetpoint}\n .touching=${this.touching}\n .atSetpoint=${this.atSetpoint}\n .autoAtSetpoint=${this.autoAtSetpoint}\n .autoAtSetpointDeadband=${this.autoAtSetpointDeadband}\n .setpointAtZeroDeadband=${this.setpointAtZeroDeadband}\n .setpointOverride=${this.setpointOverride}\n .animateSetpoint=${this.animateSetpoint}\n style=\"width: 100%; height: 100%;\"\n .priority=${this.priority}\n ></obc-bar-vertical>`;\n chartCell = html`\n <div class=\"bar-container bar-cell\">\n ${hasSize ? barWrapper : null}\n ${this.hasGraphIcon ? graphIconOverlay : null}\n </div>\n `;\n }\n\n const frameClasses = classMap({\n 'tank-frame': true,\n [`type-${this.type}`]: true,\n [`orientation-${this.orientation}`]: true,\n [this.medium]: true,\n compact: isCompact,\n static: this.static,\n });\n\n let middleContents: HTMLTemplateResult;\n if (this.static) {\n middleContents = html`<div class=\"static-readout\">${readoutCell}</div>`;\n } else if (this.compact) {\n middleContents = chartCell;\n } else {\n middleContents = html`<div class=\"grid\">\n ${tagCell}${badgesCell}${readoutCell}${chartCell}\n </div>`;\n }\n\n const tankFrame = html`\n <div class=${frameClasses}>\n ${this.renderCap('start')}\n <div class=\"middle\">${middleContents}</div>\n ${this.renderCap('end')}\n </div>\n `;\n\n // Whole component is one interactive element. The halo (flat-mixin\n // border/background that appears on hover/pressed/focus) is painted on\n // the inner `.halo` wrapper via `visibleWrapperClass`, so the surround\n // hugs only the bordered area:\n // - non-compact: just the tank-frame (grid inside it carries badges,\n // readout, tag).\n // - compact: fixed-size column flex — badges, tank-frame (flex-grow),\n // readout, tag. Empty cells (badges/tag) collapse via `?hidden`\n // above so the tank-frame absorbs the freed space.\n // - static: same as compact but no separate readout cell (the readout\n // is centered INSIDE the tank-frame via `static-readout`).\n //\n // The alert-frame overlay is the last child of `.halo` so the ring\n // overlays every cell (and any cell-collapse mechanics work uniformly).\n let haloContents: HTMLTemplateResult;\n if (this.static) {\n haloContents = html`${badgesCell}${tankFrame}${tagCell}`;\n } else if (this.compact) {\n haloContents = html`${badgesCell}${tankFrame}${readoutCell}${tagCell}`;\n } else {\n haloContents = tankFrame;\n }\n const halo = html`\n <div class=\"halo\">${haloContents}${alertFrameOverlay}</div>\n `;\n\n // The `activated` class goes on the interactive `.root` so the shared\n // `flat` style mixin paints the activated background/border on `.halo`\n // (its `visibleWrapperClass`), same as the mixin's hover/pressed states.\n //\n // `.clickable` selects between the two flat-mixin variants in CSS: the\n // full six-state one, or the `noClick` one that paints only the resting\n // enabled state. `static` is already display-only, so it never counts as\n // clickable. Same shape as `obc-elevated-card`'s `.not-clickable` split\n // and `obc-readout-list-item`'s `.root.clickable`.\n const isClickable = this.clickable && !this.static;\n const rootClasses = classMap({\n root: true,\n activated: this.activated,\n clickable: isClickable,\n });\n\n // `aria-live=\"polite\"` + `aria-atomic=\"true\"` on the root so the\n // slotted alert label (and any state change of the alert frame) is\n // announced once when `alert` flips on. Always present — an empty live\n // region is harmless and avoids screen-reader re-registration races.\n // TODO(a11y): the rest of the automation component family still lacks\n // this live-region announcement; consolidate when alert support is\n // factored into a shared mixin.\n // Three root shapes:\n // - static: <div role=\"img\"> — an opaque graphic standing in\n // for a device whose state is unknown, named by its tag.\n // - clickable false: a plain <div>. Deliberately no `role=\"img\"` and no\n // `aria-label` here: unlike a static tank this one still shows live\n // data, and both would collapse the readout into a single opaque name\n // and hide the percent / value / tag from screen readers. The visible\n // content is the accessible content. (Same reasoning as the non-\n // clickable branch of `obc-readout-list-item`.)\n // - default: <button>.\n // The live region stays on all three so an `alert` label is announced\n // regardless of interactivity.\n if (this.static) {\n return html`<div\n class=${rootClasses}\n role=\"img\"\n aria-label=${this.tag || 'Tank'}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n ${halo}\n </div>`;\n }\n if (!isClickable) {\n return html`<div\n class=${rootClasses}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n ${halo}\n </div>`;\n }\n return html`<button\n class=${rootClasses}\n type=\"button\"\n aria-label=${this.tag || 'Tank'}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n ${halo}\n </button>`;\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-automation-tank': ObcAutomationTank;\n }\n}\n"],"names":["TankTrend","TankType","TankOrientation","TankPositioning","TankChartMode"],"mappings":"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"}
@@ -108,12 +108,22 @@ export declare class ObcReadoutBlock extends LitElement {
108
108
  /** Show the leading marker-icon container (always on for setpoint/advice). */
109
109
  hasIcon: boolean;
110
110
  /**
111
- * Number of fraction digits.
111
+ * Number of fraction digits. Must be between 0 and 100 — the range
112
+ * `Number.prototype.toFixed` accepts; outside it throws a `RangeError`.
113
+ * A fractional count truncates (`2.7` → `2`). A count that never arrived
114
+ * (`NaN`, `null` or unset) renders the reading as the unavailable dash —
115
+ * formatting with a precision the author never chose would let a critical
116
+ * `0.4` pass for a healthy `0`.
112
117
  * @availableWhen valueType==number
113
118
  */
114
119
  fractionDigits: number;
115
120
  /**
116
121
  * Integer digits to reserve / hint (independent of `fractionDigits`).
122
+ * Bounded before use: a fractional count
123
+ * truncates (`2.7` → `2`), anything above 100 — including `Infinity` —
124
+ * caps at 100, and a negative count reserves nothing. A count that never
125
+ * arrived (`NaN`, `null` or unset) renders the reading as the unavailable
126
+ * dash, consistent with `fractionDigits`.
117
127
  * @availableWhen valueType==number
118
128
  */
119
129
  maxDigits: number;
@@ -140,6 +150,22 @@ export declare class ObcReadoutBlock extends LitElement {
140
150
  hidePhase: ReadoutBlockHidePhase;
141
151
  private hasAssignedIcon;
142
152
  private get resolvedValueSize();
153
+ /**
154
+ * `maxDigits` bounded to a usable count. Every width calculation below goes
155
+ * through this rather than the raw property, so an `Infinity` (or absurdly
156
+ * large) value cannot reach `String.prototype.repeat`.
157
+ */
158
+ private get resolvedMaxDigits();
159
+ /**
160
+ * Whether a digit knob failed to arrive (`NaN` / `null` / `undefined`).
161
+ * The reading then renders as the unavailable dash rather than silently
162
+ * formatting with a default the author never chose — a critical `0.4`
163
+ * formatted with a failed `fractionDigits` would otherwise print as a
164
+ * plausible-looking `0`. A missing `maxDigits` dashes too, for consistency:
165
+ * either knob arriving broken means the row's configuration cannot be
166
+ * trusted.
167
+ */
168
+ private get digitCountsMissing();
143
169
  private get numericFormatOptions();
144
170
  /** Widest possible value string for width reservation (e.g. `"000.0"`). */
145
171
  private get reserverText();
@@ -1 +1 @@
1
- {"version":3,"file":"readout-block.d.ts","sourceRoot":"","sources":["../../../src/building-blocks/readout-block/readout-block.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAgD,MAAM,KAAK,CAAC;AAK9E,OAAO,qCAAqC,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,iCAAiC,CAAC;AACzC,OAAO,yCAAyC,CAAC;AACjD,OAAO,EAOL,gBAAgB,EAEjB,MAAM,4DAA4D,CAAC;AACpE,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAC,gBAAgB,EAAC,MAAM,4DAA4D,CAAC;AAE5F;;;;;;GAMG;AACH,oBAAY,mBAAmB;IAC7B,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAED;;;;;GAKG;AACH,oBAAY,gBAAgB;IAC1B,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,KAAK,UAAU;CAChB;AAED;;;;GAIG;AACH,oBAAY,uBAAuB;IACjC,YAAY,kBAAkB;IAC9B,OAAO,YAAY;CACpB;AAED;;;GAGG;AACH,oBAAY,qBAAqB;IAC/B,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,MAAM,WAAW;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBACa,eAAgB,SAAQ,UAAU;IAC7C,oDAAoD;IAC1B,OAAO,EAAE,mBAAmB,CAC1B;IAE5B;;;OAGG;IACuB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAQ;IAE/D;;;;OAIG;IACuB,SAAS,EAAE,gBAAgB,CAC3B;IAE1B,kDAAkD;IACxB,IAAI,EAAE,gBAAgB,CAA0B;IAE1E;;;;OAIG;IACuB,SAAS,CAAC,EAAE,cAAc,CAAC;IAErD,uCAAuC;IACZ,QAAQ,UAAS;IAE5C,6EAA6E;IACnD,MAAM,EAAE,oBAAoB,CACvB;IAE/B,yDAAyD;IAC9B,SAAS,UAAS;IAE7C,8EAA8E;IACnD,OAAO,UAAS;IAE3C;;;OAGG;IACuB,cAAc,SAAK;IAE7C;;;OAGG;IACuB,SAAS,SAAK;IAExC;;;OAGG;IACwB,WAAW,UAAS;IAE/C,sEAAsE;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IAEjD,0EAA0E;IAC/C,GAAG,UAAS;IAEvC,qCAAqC;IACX,OAAO,SAAS;IAE1C,8DAA8D;IACpC,SAAS,EAAE,mBAAmB,CAC5B;IAE5B,oDAAoD;IAC1B,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAKhE,kEAAkE;IACxC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAAS;IAEpE,4EAA4E;IACjD,QAAQ,UAAS;IAE5C,0EAA0E;IAChD,SAAS,EAAE,qBAAqB,CAC7B;IAEpB,OAAO,CAAC,eAAe,CAAS;IAEzC,OAAO,KAAK,iBAAiB,GAY5B;IAED,OAAO,KAAK,oBAAoB,GAS/B;IAED,2EAA2E;IAC3E,OAAO,KAAK,YAAY,GASvB;IAED;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,gBAAgB,CAMtB;IAEF,OAAO,CAAC,UAAU;IAsBlB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;cAiBN,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAYzD,MAAM;IA+Ef,OAAgB,MAAM,0BAA6B;CACpD;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,mBAAmB,EAAE,eAAe,CAAC;KACtC;CACF"}
1
+ {"version":3,"file":"readout-block.d.ts","sourceRoot":"","sources":["../../../src/building-blocks/readout-block/readout-block.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@ import { customElement } from "../../decorator.js";
6
6
  import { ObcTextboxFontWeight, ObcTextboxAlignment, ObcTextboxSize } from "../../components/textbox/textbox.js";
7
7
  import "../../icons/icon-input-right.js";
8
8
  import "../../icons/icon-notification-advice.js";
9
- import { ReadoutValueType, assertReadoutValueType, resolveReadoutNumericValue, resolveReadoutTextValue, READOUT_UNAVAILABLE_DASH, formatNumericValue, readoutFormattedInteger } from "../../navigation-instruments/readout/readout-formatters.js";
9
+ import { ReadoutValueType, resolveReadoutDigitCount, isReadoutDigitCountMissing, assertReadoutValueType, assertReadoutFractionDigits, resolveReadoutNumericValue, resolveReadoutTextValue, READOUT_UNAVAILABLE_DASH, formatNumericValue, readoutFormattedInteger } from "../../navigation-instruments/readout/readout-formatters.js";
10
10
  import { wrapWithAlertFrame } from "../../components/alert-frame/alert-frame.js";
11
11
  var __defProp = Object.defineProperty;
12
12
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -79,23 +79,46 @@ let ObcReadoutBlock = class extends LitElement {
79
79
  return ObcTextboxSize.s;
80
80
  }
81
81
  }
82
+ /**
83
+ * `maxDigits` bounded to a usable count. Every width calculation below goes
84
+ * through this rather than the raw property, so an `Infinity` (or absurdly
85
+ * large) value cannot reach `String.prototype.repeat`.
86
+ */
87
+ get resolvedMaxDigits() {
88
+ return resolveReadoutDigitCount(this.maxDigits);
89
+ }
90
+ /**
91
+ * Whether a digit knob failed to arrive (`NaN` / `null` / `undefined`).
92
+ * The reading then renders as the unavailable dash rather than silently
93
+ * formatting with a default the author never chose — a critical `0.4`
94
+ * formatted with a failed `fractionDigits` would otherwise print as a
95
+ * plausible-looking `0`. A missing `maxDigits` dashes too, for consistency:
96
+ * either knob arriving broken means the row's configuration cannot be
97
+ * trusted.
98
+ */
99
+ get digitCountsMissing() {
100
+ return isReadoutDigitCountMissing(this.fractionDigits) || isReadoutDigitCountMissing(this.maxDigits);
101
+ }
82
102
  get numericFormatOptions() {
83
103
  return {
84
104
  // The unavailable placeholder stays short (`\u2012.\u2012\u2012`) rather than
85
105
  // spelling out every reserved digit position — `maxDigits` already
86
106
  // reserves the width, so it simply sits at the right edge of it.
87
107
  showZeroPadding: false,
88
- minValueLength: this.maxDigits,
89
- fractionDigits: this.fractionDigits
108
+ minValueLength: this.resolvedMaxDigits,
109
+ // A missing precision shapes the placeholder as zero fraction digits (a
110
+ // single dash) — and keeps a runtime `undefined`/`null` out of the
111
+ // options object, which is typed `number` throughout the formatters.
112
+ fractionDigits: isReadoutDigitCountMissing(this.fractionDigits) ? 0 : this.fractionDigits
90
113
  };
91
114
  }
92
115
  /** Widest possible value string for width reservation (e.g. `"000.0"`). */
93
116
  get reserverText() {
94
- const maxDigits = this.maxDigits;
117
+ const maxDigits = this.resolvedMaxDigits;
95
118
  if (maxDigits <= 0) {
96
119
  return "";
97
120
  }
98
- const integer = "0".repeat(Math.max(maxDigits, 1));
121
+ const integer = "0".repeat(maxDigits);
99
122
  return this.fractionDigits > 0 ? `${integer}.${"0".repeat(this.fractionDigits)}` : integer;
100
123
  }
101
124
  /**
@@ -159,18 +182,16 @@ let ObcReadoutBlock = class extends LitElement {
159
182
  willUpdate(changed) {
160
183
  super.willUpdate(changed);
161
184
  assertReadoutValueType("obc-readout-block", this.value, this.valueType);
185
+ assertReadoutFractionDigits("obc-readout-block", this.fractionDigits);
162
186
  }
163
187
  render() {
164
188
  const valueSize = this.resolvedValueSize;
165
189
  const formatOptions = this.numericFormatOptions;
166
190
  const isTextMode = this.valueType === ReadoutValueType.text;
167
- const valueForFormat = resolveReadoutNumericValue(
168
- this.value,
169
- this.valueType
170
- );
191
+ const valueForFormat = this.digitCountsMissing ? void 0 : resolveReadoutNumericValue(this.value, this.valueType);
171
192
  const textValue = resolveReadoutTextValue(this.value, this.valueType);
172
193
  const text = this.off ? this.offText : isTextMode ? textValue ?? READOUT_UNAVAILABLE_DASH : formatNumericValue(valueForFormat, formatOptions);
173
- const hintCount = this.off || isTextMode || !this.hintedZeros || valueForFormat === void 0 || valueForFormat < 0 ? 0 : Math.max(this.maxDigits - readoutFormattedInteger(text), 0);
194
+ const hintCount = this.off || isTextMode || !this.hintedZeros || valueForFormat === void 0 || valueForFormat < 0 ? 0 : Math.max(this.resolvedMaxDigits - readoutFormattedInteger(text), 0);
174
195
  const hinted = hintCount > 0 ? "0".repeat(hintCount) : "";
175
196
  const reserver = isTextMode ? this.spaceReserver ?? "" : this.hintedZeros ? this.reserverText : this.widerReserver(this.spaceReserver, this.reserverText);
176
197
  const block = html`
@@ -1 +1 @@
1
- {"version":3,"file":"readout-block.js","sources":["../../../src/building-blocks/readout-block/readout-block.ts"],"sourcesContent":["import {LitElement, html, nothing, unsafeCSS, type TemplateResult} from 'lit';\nimport {property, state} from 'lit/decorators.js';\nimport {classMap} from 'lit/directives/class-map.js';\nimport componentStyle from './readout-block.css?inline';\nimport {customElement} from '../../decorator.js';\nimport '../../components/textbox/textbox.js';\nimport {\n ObcTextboxSize,\n ObcTextboxFontWeight,\n ObcTextboxAlignment,\n} from '../../components/textbox/textbox.js';\nimport '../../icons/icon-input-right.js';\nimport '../../icons/icon-notification-advice.js';\nimport {\n formatNumericValue,\n readoutFormattedInteger,\n assertReadoutValueType,\n resolveReadoutNumericValue,\n resolveReadoutTextValue,\n READOUT_UNAVAILABLE_DASH,\n ReadoutValueType,\n type ReadoutNumericFormatOptions,\n} from '../../navigation-instruments/readout/readout-formatters.js';\nimport {\n type AlertFrameConfig,\n wrapWithAlertFrame,\n} from '../../components/alert-frame/alert-frame.js';\n\n// Re-exported so consumers can configure typography without a second import path.\nexport {\n ObcTextboxSize,\n ObcTextboxFontWeight,\n ObcTextboxAlignment,\n} from '../../components/textbox/textbox.js';\nexport {ReadoutValueType} from '../../navigation-instruments/readout/readout-formatters.js';\n\n/**\n * Semantic variant of the block. Drives the default marker icon and the\n * colour token; the layout is identical across variants.\n * - `value`: the primary reading (no default marker icon).\n * - `setpoint`: a setpoint reference (default `input-right` marker).\n * - `advice`: an advisory reference (default `notification-advice` marker).\n */\nexport enum ReadoutBlockVariant {\n value = 'value',\n setpoint = 'setpoint',\n advice = 'advice',\n}\n\n/**\n * Density tier of the block — drives icon size, the icon↔number gap, and the\n * degree-column width tier. The number typography size is controlled\n * independently via {@link ObcReadoutBlock.valueSize} so a parent can de-emphasise\n * one block relative to another within the same tier.\n */\nexport enum ReadoutBlockSize {\n small = 'small',\n medium = 'medium',\n large = 'large',\n}\n\n/**\n * Per-block measurement quality, rendered as a non-shifting outline chip.\n * - `low-integrity`: the value is suspect.\n * - `invalid`: the value is invalid.\n */\nexport enum ReadoutBlockDataQuality {\n lowIntegrity = 'low-integrity',\n invalid = 'invalid',\n}\n\n/**\n * Pop-up fade phase for a setpoint block. Driven by the parent's setpoint\n * interaction state machine; only meaningful for `role=\"setpoint\"`.\n */\nexport enum ReadoutBlockHidePhase {\n none = 'none',\n hiding = 'hiding',\n hidden = 'hidden',\n}\n\n/**\n * `<obc-readout-block>` – the most atomic readout primitive: a single\n * cap-height-aligned, width-reservable numeric segment (value / setpoint /\n * advice).\n *\n * It renders one `obc-textbox` number with optional hinted leading zeros, a\n * reservable width, an optional leading marker icon (via the `icon` slot or the\n * role default), an optional trailing degree glyph, an `off`/unavailable text\n * state, per-block data-quality and an optional per-block alert frame.\n *\n * Setting `valueType` to `text` renders `value` verbatim (e.g.\n * `\"Auto\"`) instead of a formatted number.\n *\n * This is a building block used inside `obc-readout-list-item` (and, in a future\n * refactor, inside `obc-readout`); it is not normally used on its own. Colour is\n * inherited from the host context (the parent sets the role colour), so the\n * block stays neutral until placed.\n *\n * @experimental Pilot for the new primitives + per-block options Readout API; the\n * API may change in a future release.\n *\n * @slot icon - Replaces the role's default marker icon.\n *\n * @csspart block - The block container (carries role / tone / data-quality).\n * @csspart block-content - The number + degree group.\n * @csspart block-text - The `obc-textbox` rendering the number.\n * @csspart block-icon - The leading marker-icon container.\n * @csspart degree - The trailing degree-glyph column.\n */\n@customElement('obc-readout-block')\nexport class ObcReadoutBlock extends LitElement {\n /** Semantic variant (value / setpoint / advice). */\n @property({type: String}) variant: ReadoutBlockVariant =\n ReadoutBlockVariant.value;\n\n /**\n * The value; `null`/`undefined` renders a dash. A number by default, or text\n * when {@link valueType} is `text`.\n */\n @property({type: String}) value: number | string | null = null;\n\n /**\n * How {@link value} is interpreted. `number` (default) formats it via\n * `fractionDigits`; `text` renders it verbatim and ignores the numeric\n * format options. Passing text while this is `number` throws.\n */\n @property({type: String}) valueType: ReadoutValueType =\n ReadoutValueType.number;\n\n /** Density tier — icon size, gap, degree tier. */\n @property({type: String}) size: ReadoutBlockSize = ReadoutBlockSize.small;\n\n /**\n * Resolved number-typography size. When unset it is derived from `size`\n * (small→s, medium→m, large→l), so a parent that de-emphasises a block (e.g.\n * a secondary setpoint) can pass a smaller size without changing the tier.\n */\n @property({type: String}) valueSize?: ObcTextboxSize;\n\n /** Accent (in-command) colour tone. */\n @property({type: Boolean}) enhanced = false;\n\n /** Number font weight (regular / semibold / bold); colour is independent. */\n @property({type: String}) weight: ObcTextboxFontWeight =\n ObcTextboxFontWeight.regular;\n\n /** Render the trailing cap-height degree glyph (`°`). */\n @property({type: Boolean}) hasDegree = false;\n\n /** Show the leading marker-icon container (always on for setpoint/advice). */\n @property({type: Boolean}) hasIcon = false;\n\n /**\n * Number of fraction digits.\n * @availableWhen valueType==number\n */\n @property({type: Number}) fractionDigits = 0;\n\n /**\n * Integer digits to reserve / hint (independent of `fractionDigits`).\n * @availableWhen valueType==number\n */\n @property({type: Number}) maxDigits = 0;\n\n /**\n * Render muted leading zeros filling the integer part to `maxDigits`.\n * @availableWhen valueType==number\n */\n @property({type: Boolean}) hintedZeros = false;\n\n /** Explicit longest string to reserve width for (e.g. `\"0000.0\"`). */\n @property({type: String}) spaceReserver?: string;\n\n /** Render `offText` instead of a number (e.g. equipment powered down). */\n @property({type: Boolean}) off = false;\n\n /** Text shown when `off` is true. */\n @property({type: String}) offText = 'OFF';\n\n /** Text alignment of the number within its reserved width. */\n @property({type: String}) alignment: ObcTextboxAlignment =\n ObcTextboxAlignment.Right;\n\n /** Per-block measurement quality (outline chip). */\n @property({type: String}) dataQuality?: ReadoutBlockDataQuality;\n\n // `boolean | …` (not `false | …`): the generated Angular wrapper widens a\n // literal-`false` union to `boolean`. `wrapWithAlertFrame` treats any\n // non-object as \"no frame\", so accepting `boolean` is harmless.\n /** Per-block alert frame; nests inside any parent alert frame. */\n @property({type: Object}) alert: boolean | AlertFrameConfig = false;\n\n /** Setpoint focus (touch) state — only meaningful for `role=\"setpoint\"`. */\n @property({type: Boolean}) touching = false;\n\n /** Setpoint pop-up fade phase — only meaningful for `role=\"setpoint\"`. */\n @property({type: String}) hidePhase: ReadoutBlockHidePhase =\n ReadoutBlockHidePhase.none;\n\n @state() private hasAssignedIcon = false;\n\n private get resolvedValueSize(): ObcTextboxSize {\n if (this.valueSize) {\n return this.valueSize;\n }\n switch (this.size) {\n case ReadoutBlockSize.large:\n return ObcTextboxSize.l;\n case ReadoutBlockSize.medium:\n return ObcTextboxSize.m;\n default:\n return ObcTextboxSize.s;\n }\n }\n\n private get numericFormatOptions(): ReadoutNumericFormatOptions {\n return {\n // The unavailable placeholder stays short (`\\u2012.\\u2012\\u2012`) rather than\n // spelling out every reserved digit position — `maxDigits` already\n // reserves the width, so it simply sits at the right edge of it.\n showZeroPadding: false,\n minValueLength: this.maxDigits,\n fractionDigits: this.fractionDigits,\n };\n }\n\n /** Widest possible value string for width reservation (e.g. `\"000.0\"`). */\n private get reserverText(): string {\n const maxDigits = this.maxDigits;\n if (maxDigits <= 0) {\n return '';\n }\n const integer = '0'.repeat(Math.max(maxDigits, 1));\n return this.fractionDigits > 0\n ? `${integer}.${'0'.repeat(this.fractionDigits)}`\n : integer;\n }\n\n /**\n * Effective width reserver: the wider of the explicit `spaceReserver` and the\n * `maxDigits`/`fractionDigits`-derived reserve, so an explicit reserver can\n * never reserve *less* than the formatted value needs. Under tabular-nums the\n * rendered width is proportional to character count, so \"wider\" compares length.\n */\n private widerReserver(explicit: string | undefined, derived: string): string {\n if (!explicit) {\n return derived;\n }\n if (!derived) {\n return explicit;\n }\n return explicit.length >= derived.length ? explicit : derived;\n }\n\n private dataQualityClasses(): Record<string, boolean> {\n return {\n 'data-low-integrity':\n this.dataQuality === ReadoutBlockDataQuality.lowIntegrity,\n 'data-invalid': this.dataQuality === ReadoutBlockDataQuality.invalid,\n };\n }\n\n private onIconSlotChange = (event: Event): void => {\n // `slotchange` fires after the first render once content is (re)assigned,\n // so it covers the initial state without a firstUpdated re-render.\n this.hasAssignedIcon =\n (event.target as HTMLSlotElement).assignedElements({flatten: true})\n .length > 0;\n };\n\n private renderIcon(): TemplateResult | typeof nothing {\n // Value blocks only show an icon when asked; setpoint/advice always reserve\n // their marker. The role default is rendered as a direct child (so it sizes\n // via `.block-icon obi-*`) and hidden once an icon is assigned to the slot —\n // `{flatten: true}` sees through a forwarded-but-empty parent slot, so the\n // default still shows when the consumer overrides nothing.\n const showIcon = this.variant !== ReadoutBlockVariant.value || this.hasIcon;\n if (!showIcon) {\n return nothing;\n }\n let fallback: TemplateResult | typeof nothing = nothing;\n if (this.variant === ReadoutBlockVariant.setpoint) {\n fallback = html`<obi-input-right></obi-input-right>`;\n } else if (this.variant === ReadoutBlockVariant.advice) {\n fallback = html`<obi-notification-advice></obi-notification-advice>`;\n }\n return html`<span class=\"block-icon\" part=\"block-icon\" aria-hidden=\"true\">\n <slot name=\"icon\" @slotchange=${this.onIconSlotChange}></slot>\n ${this.hasAssignedIcon ? nothing : fallback}\n </span>`;\n }\n\n /**\n * A cap-height `°` column whose width scales with the number size. Inherits the\n * block's colour.\n */\n private renderDegreeGlyph(size: ObcTextboxSize): TemplateResult {\n return html`\n <span\n class=${classMap({\n 'degree-column': true,\n [`degree-${size}`]: true,\n 'degree-inherit': true,\n })}\n part=\"degree\"\n >\n <obc-textbox class=\"degree-glyph\" .size=${size} alignment=\"center\"\n >°</obc-textbox\n >\n </span>\n `;\n }\n\n protected override willUpdate(changed: Map<string, unknown>): void {\n super.willUpdate(changed);\n // Validated on EVERY update, deliberately NOT gated on `value`/`valueType`\n // appearing in `changed`. When this assertion throws, Lit's `performUpdate`\n // catch calls `__markUpdated()`, which clears the changed-properties map. A\n // later update driven by any OTHER property — inside `obc-readout-list`,\n // `align()` writing the shared reservers — would then see no `value` in\n // `changed`, skip the check, and render the invalid value as a plain dash:\n // exactly the silent failure this assertion exists to prevent.\n assertReadoutValueType('obc-readout-block', this.value, this.valueType);\n }\n\n override render() {\n const valueSize = this.resolvedValueSize;\n const formatOptions = this.numericFormatOptions;\n const isTextMode = this.valueType === ReadoutValueType.text;\n const valueForFormat = resolveReadoutNumericValue(\n this.value,\n this.valueType\n );\n const textValue = resolveReadoutTextValue(this.value, this.valueType);\n // Text mode renders verbatim and ignores the numeric format options; a\n // blank / missing text value still falls back to the unavailable dash.\n const text = this.off\n ? this.offText\n : isTextMode\n ? (textValue ?? READOUT_UNAVAILABLE_DASH)\n : formatNumericValue(valueForFormat, formatOptions);\n // Hinted zeros pad the INTEGER part up to `maxDigits`, independent of\n // `fractionDigits` (the decimal point and fraction digits never count toward\n // `maxDigits`). Negative / dashed values are not padded. Example: value 1.2,\n // maxDigits 3, fractionDigits 1 → \"001.2\".\n const hintCount =\n this.off ||\n isTextMode ||\n !this.hintedZeros ||\n valueForFormat === undefined ||\n valueForFormat < 0\n ? 0\n : Math.max(this.maxDigits - readoutFormattedInteger(text), 0);\n const hinted = hintCount > 0 ? '0'.repeat(hintCount) : '';\n // Hinted zeros own the width — they already fill to `maxDigits` — so when\n // `hintedZeros` is enabled an explicit `spaceReserver` is ignored (it has\n // higher priority). Otherwise the wider of the explicit reserver and the\n // `maxDigits`-derived reserve wins.\n // Text mode ignores the `maxDigits`-derived numeric reserve — only an\n // explicit `spaceReserver` still applies.\n const reserver = isTextMode\n ? (this.spaceReserver ?? '')\n : this.hintedZeros\n ? this.reserverText\n : this.widerReserver(this.spaceReserver, this.reserverText);\n\n const block = html`\n <div\n class=${classMap({\n block: true,\n [`block-${this.variant}`]: true,\n [`size-${this.size}`]: true,\n 'tone-enhanced': this.enhanced,\n touching: this.touching,\n 'is-hiding': this.hidePhase === ReadoutBlockHidePhase.hiding,\n 'is-hidden': this.hidePhase === ReadoutBlockHidePhase.hidden,\n ...this.dataQualityClasses(),\n })}\n part=\"block block-${this.variant}\"\n >\n ${this.renderIcon()}\n <span class=\"block-content\" part=\"block-content\">\n <obc-textbox\n class=\"block-text\"\n part=\"block-text\"\n .size=${valueSize}\n .fontWeight=${this.weight}\n .alignment=${this.alignment}\n .tabularNums=${true}\n >\n ${hinted\n ? html`<span class=\"hinted-zero\" aria-hidden=\"true\"\n >${hinted}</span\n >`\n : nothing}${text}\n ${reserver ? html`<span slot=\"length\">${reserver}</span>` : nothing}\n </obc-textbox>\n ${this.hasDegree ? this.renderDegreeGlyph(valueSize) : nothing}\n </span>\n </div>\n `;\n return wrapWithAlertFrame(this.alert ?? false, block);\n }\n\n static override styles = unsafeCSS(componentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-readout-block': ObcReadoutBlock;\n }\n}\n"],"names":["ReadoutBlockVariant","ReadoutBlockSize","ReadoutBlockDataQuality","ReadoutBlockHidePhase"],"mappings":"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"}
1
+ {"version":3,"file":"readout-block.js","sources":["../../../src/building-blocks/readout-block/readout-block.ts"],"sourcesContent":["import {LitElement, html, nothing, unsafeCSS, type TemplateResult} from 'lit';\nimport {property, state} from 'lit/decorators.js';\nimport {classMap} from 'lit/directives/class-map.js';\nimport componentStyle from './readout-block.css?inline';\nimport {customElement} from '../../decorator.js';\nimport '../../components/textbox/textbox.js';\nimport {\n ObcTextboxSize,\n ObcTextboxFontWeight,\n ObcTextboxAlignment,\n} from '../../components/textbox/textbox.js';\nimport '../../icons/icon-input-right.js';\nimport '../../icons/icon-notification-advice.js';\nimport {\n formatNumericValue,\n readoutFormattedInteger,\n assertReadoutValueType,\n assertReadoutFractionDigits,\n isReadoutDigitCountMissing,\n resolveReadoutDigitCount,\n resolveReadoutNumericValue,\n resolveReadoutTextValue,\n READOUT_UNAVAILABLE_DASH,\n ReadoutValueType,\n type ReadoutNumericFormatOptions,\n} from '../../navigation-instruments/readout/readout-formatters.js';\nimport {\n type AlertFrameConfig,\n wrapWithAlertFrame,\n} from '../../components/alert-frame/alert-frame.js';\n\n// Re-exported so consumers can configure typography without a second import path.\nexport {\n ObcTextboxSize,\n ObcTextboxFontWeight,\n ObcTextboxAlignment,\n} from '../../components/textbox/textbox.js';\nexport {ReadoutValueType} from '../../navigation-instruments/readout/readout-formatters.js';\n\n/**\n * Semantic variant of the block. Drives the default marker icon and the\n * colour token; the layout is identical across variants.\n * - `value`: the primary reading (no default marker icon).\n * - `setpoint`: a setpoint reference (default `input-right` marker).\n * - `advice`: an advisory reference (default `notification-advice` marker).\n */\nexport enum ReadoutBlockVariant {\n value = 'value',\n setpoint = 'setpoint',\n advice = 'advice',\n}\n\n/**\n * Density tier of the block — drives icon size, the icon↔number gap, and the\n * degree-column width tier. The number typography size is controlled\n * independently via {@link ObcReadoutBlock.valueSize} so a parent can de-emphasise\n * one block relative to another within the same tier.\n */\nexport enum ReadoutBlockSize {\n small = 'small',\n medium = 'medium',\n large = 'large',\n}\n\n/**\n * Per-block measurement quality, rendered as a non-shifting outline chip.\n * - `low-integrity`: the value is suspect.\n * - `invalid`: the value is invalid.\n */\nexport enum ReadoutBlockDataQuality {\n lowIntegrity = 'low-integrity',\n invalid = 'invalid',\n}\n\n/**\n * Pop-up fade phase for a setpoint block. Driven by the parent's setpoint\n * interaction state machine; only meaningful for `role=\"setpoint\"`.\n */\nexport enum ReadoutBlockHidePhase {\n none = 'none',\n hiding = 'hiding',\n hidden = 'hidden',\n}\n\n/**\n * `<obc-readout-block>` – the most atomic readout primitive: a single\n * cap-height-aligned, width-reservable numeric segment (value / setpoint /\n * advice).\n *\n * It renders one `obc-textbox` number with optional hinted leading zeros, a\n * reservable width, an optional leading marker icon (via the `icon` slot or the\n * role default), an optional trailing degree glyph, an `off`/unavailable text\n * state, per-block data-quality and an optional per-block alert frame.\n *\n * Setting `valueType` to `text` renders `value` verbatim (e.g.\n * `\"Auto\"`) instead of a formatted number.\n *\n * This is a building block used inside `obc-readout-list-item` (and, in a future\n * refactor, inside `obc-readout`); it is not normally used on its own. Colour is\n * inherited from the host context (the parent sets the role colour), so the\n * block stays neutral until placed.\n *\n * @experimental Pilot for the new primitives + per-block options Readout API; the\n * API may change in a future release.\n *\n * @slot icon - Replaces the role's default marker icon.\n *\n * @csspart block - The block container (carries role / tone / data-quality).\n * @csspart block-content - The number + degree group.\n * @csspart block-text - The `obc-textbox` rendering the number.\n * @csspart block-icon - The leading marker-icon container.\n * @csspart degree - The trailing degree-glyph column.\n */\n@customElement('obc-readout-block')\nexport class ObcReadoutBlock extends LitElement {\n /** Semantic variant (value / setpoint / advice). */\n @property({type: String}) variant: ReadoutBlockVariant =\n ReadoutBlockVariant.value;\n\n /**\n * The value; `null`/`undefined` renders a dash. A number by default, or text\n * when {@link valueType} is `text`.\n */\n @property({type: String}) value: number | string | null = null;\n\n /**\n * How {@link value} is interpreted. `number` (default) formats it via\n * `fractionDigits`; `text` renders it verbatim and ignores the numeric\n * format options. Passing text while this is `number` throws.\n */\n @property({type: String}) valueType: ReadoutValueType =\n ReadoutValueType.number;\n\n /** Density tier — icon size, gap, degree tier. */\n @property({type: String}) size: ReadoutBlockSize = ReadoutBlockSize.small;\n\n /**\n * Resolved number-typography size. When unset it is derived from `size`\n * (small→s, medium→m, large→l), so a parent that de-emphasises a block (e.g.\n * a secondary setpoint) can pass a smaller size without changing the tier.\n */\n @property({type: String}) valueSize?: ObcTextboxSize;\n\n /** Accent (in-command) colour tone. */\n @property({type: Boolean}) enhanced = false;\n\n /** Number font weight (regular / semibold / bold); colour is independent. */\n @property({type: String}) weight: ObcTextboxFontWeight =\n ObcTextboxFontWeight.regular;\n\n /** Render the trailing cap-height degree glyph (`°`). */\n @property({type: Boolean}) hasDegree = false;\n\n /** Show the leading marker-icon container (always on for setpoint/advice). */\n @property({type: Boolean}) hasIcon = false;\n\n /**\n * Number of fraction digits. Must be between 0 and 100 — the range\n * `Number.prototype.toFixed` accepts; outside it throws a `RangeError`.\n * A fractional count truncates (`2.7` → `2`). A count that never arrived\n * (`NaN`, `null` or unset) renders the reading as the unavailable dash —\n * formatting with a precision the author never chose would let a critical\n * `0.4` pass for a healthy `0`.\n * @availableWhen valueType==number\n */\n @property({type: Number}) fractionDigits = 0;\n\n /**\n * Integer digits to reserve / hint (independent of `fractionDigits`).\n * Bounded before use: a fractional count\n * truncates (`2.7` → `2`), anything above 100 — including `Infinity` —\n * caps at 100, and a negative count reserves nothing. A count that never\n * arrived (`NaN`, `null` or unset) renders the reading as the unavailable\n * dash, consistent with `fractionDigits`.\n * @availableWhen valueType==number\n */\n @property({type: Number}) maxDigits = 0;\n\n /**\n * Render muted leading zeros filling the integer part to `maxDigits`.\n * @availableWhen valueType==number\n */\n @property({type: Boolean}) hintedZeros = false;\n\n /** Explicit longest string to reserve width for (e.g. `\"0000.0\"`). */\n @property({type: String}) spaceReserver?: string;\n\n /** Render `offText` instead of a number (e.g. equipment powered down). */\n @property({type: Boolean}) off = false;\n\n /** Text shown when `off` is true. */\n @property({type: String}) offText = 'OFF';\n\n /** Text alignment of the number within its reserved width. */\n @property({type: String}) alignment: ObcTextboxAlignment =\n ObcTextboxAlignment.Right;\n\n /** Per-block measurement quality (outline chip). */\n @property({type: String}) dataQuality?: ReadoutBlockDataQuality;\n\n // `boolean | …` (not `false | …`): the generated Angular wrapper widens a\n // literal-`false` union to `boolean`. `wrapWithAlertFrame` treats any\n // non-object as \"no frame\", so accepting `boolean` is harmless.\n /** Per-block alert frame; nests inside any parent alert frame. */\n @property({type: Object}) alert: boolean | AlertFrameConfig = false;\n\n /** Setpoint focus (touch) state — only meaningful for `role=\"setpoint\"`. */\n @property({type: Boolean}) touching = false;\n\n /** Setpoint pop-up fade phase — only meaningful for `role=\"setpoint\"`. */\n @property({type: String}) hidePhase: ReadoutBlockHidePhase =\n ReadoutBlockHidePhase.none;\n\n @state() private hasAssignedIcon = false;\n\n private get resolvedValueSize(): ObcTextboxSize {\n if (this.valueSize) {\n return this.valueSize;\n }\n switch (this.size) {\n case ReadoutBlockSize.large:\n return ObcTextboxSize.l;\n case ReadoutBlockSize.medium:\n return ObcTextboxSize.m;\n default:\n return ObcTextboxSize.s;\n }\n }\n\n /**\n * `maxDigits` bounded to a usable count. Every width calculation below goes\n * through this rather than the raw property, so an `Infinity` (or absurdly\n * large) value cannot reach `String.prototype.repeat`.\n */\n private get resolvedMaxDigits(): number {\n return resolveReadoutDigitCount(this.maxDigits);\n }\n\n /**\n * Whether a digit knob failed to arrive (`NaN` / `null` / `undefined`).\n * The reading then renders as the unavailable dash rather than silently\n * formatting with a default the author never chose — a critical `0.4`\n * formatted with a failed `fractionDigits` would otherwise print as a\n * plausible-looking `0`. A missing `maxDigits` dashes too, for consistency:\n * either knob arriving broken means the row's configuration cannot be\n * trusted.\n */\n private get digitCountsMissing(): boolean {\n return (\n isReadoutDigitCountMissing(this.fractionDigits) ||\n isReadoutDigitCountMissing(this.maxDigits)\n );\n }\n\n private get numericFormatOptions(): ReadoutNumericFormatOptions {\n return {\n // The unavailable placeholder stays short (`\\u2012.\\u2012\\u2012`) rather than\n // spelling out every reserved digit position — `maxDigits` already\n // reserves the width, so it simply sits at the right edge of it.\n showZeroPadding: false,\n minValueLength: this.resolvedMaxDigits,\n // A missing precision shapes the placeholder as zero fraction digits (a\n // single dash) — and keeps a runtime `undefined`/`null` out of the\n // options object, which is typed `number` throughout the formatters.\n fractionDigits: isReadoutDigitCountMissing(this.fractionDigits)\n ? 0\n : this.fractionDigits,\n };\n }\n\n /** Widest possible value string for width reservation (e.g. `\"000.0\"`). */\n private get reserverText(): string {\n const maxDigits = this.resolvedMaxDigits;\n if (maxDigits <= 0) {\n return '';\n }\n const integer = '0'.repeat(maxDigits);\n return this.fractionDigits > 0\n ? `${integer}.${'0'.repeat(this.fractionDigits)}`\n : integer;\n }\n\n /**\n * Effective width reserver: the wider of the explicit `spaceReserver` and the\n * `maxDigits`/`fractionDigits`-derived reserve, so an explicit reserver can\n * never reserve *less* than the formatted value needs. Under tabular-nums the\n * rendered width is proportional to character count, so \"wider\" compares length.\n */\n private widerReserver(explicit: string | undefined, derived: string): string {\n if (!explicit) {\n return derived;\n }\n if (!derived) {\n return explicit;\n }\n return explicit.length >= derived.length ? explicit : derived;\n }\n\n private dataQualityClasses(): Record<string, boolean> {\n return {\n 'data-low-integrity':\n this.dataQuality === ReadoutBlockDataQuality.lowIntegrity,\n 'data-invalid': this.dataQuality === ReadoutBlockDataQuality.invalid,\n };\n }\n\n private onIconSlotChange = (event: Event): void => {\n // `slotchange` fires after the first render once content is (re)assigned,\n // so it covers the initial state without a firstUpdated re-render.\n this.hasAssignedIcon =\n (event.target as HTMLSlotElement).assignedElements({flatten: true})\n .length > 0;\n };\n\n private renderIcon(): TemplateResult | typeof nothing {\n // Value blocks only show an icon when asked; setpoint/advice always reserve\n // their marker. The role default is rendered as a direct child (so it sizes\n // via `.block-icon obi-*`) and hidden once an icon is assigned to the slot —\n // `{flatten: true}` sees through a forwarded-but-empty parent slot, so the\n // default still shows when the consumer overrides nothing.\n const showIcon = this.variant !== ReadoutBlockVariant.value || this.hasIcon;\n if (!showIcon) {\n return nothing;\n }\n let fallback: TemplateResult | typeof nothing = nothing;\n if (this.variant === ReadoutBlockVariant.setpoint) {\n fallback = html`<obi-input-right></obi-input-right>`;\n } else if (this.variant === ReadoutBlockVariant.advice) {\n fallback = html`<obi-notification-advice></obi-notification-advice>`;\n }\n return html`<span class=\"block-icon\" part=\"block-icon\" aria-hidden=\"true\">\n <slot name=\"icon\" @slotchange=${this.onIconSlotChange}></slot>\n ${this.hasAssignedIcon ? nothing : fallback}\n </span>`;\n }\n\n /**\n * A cap-height `°` column whose width scales with the number size. Inherits the\n * block's colour.\n */\n private renderDegreeGlyph(size: ObcTextboxSize): TemplateResult {\n return html`\n <span\n class=${classMap({\n 'degree-column': true,\n [`degree-${size}`]: true,\n 'degree-inherit': true,\n })}\n part=\"degree\"\n >\n <obc-textbox class=\"degree-glyph\" .size=${size} alignment=\"center\"\n >°</obc-textbox\n >\n </span>\n `;\n }\n\n protected override willUpdate(changed: Map<string, unknown>): void {\n super.willUpdate(changed);\n // Validated on EVERY update, deliberately NOT gated on `value`/`valueType`\n // appearing in `changed`. When this assertion throws, Lit's `performUpdate`\n // catch calls `__markUpdated()`, which clears the changed-properties map. A\n // later update driven by any OTHER property — inside `obc-readout-list`,\n // `align()` writing the shared reservers — would then see no `value` in\n // `changed`, skip the check, and render the invalid value as a plain dash:\n // exactly the silent failure this assertion exists to prevent.\n assertReadoutValueType('obc-readout-block', this.value, this.valueType);\n assertReadoutFractionDigits('obc-readout-block', this.fractionDigits);\n }\n\n override render() {\n const valueSize = this.resolvedValueSize;\n const formatOptions = this.numericFormatOptions;\n const isTextMode = this.valueType === ReadoutValueType.text;\n // A missing digit knob makes the reading untrustworthy, so it resolves to\n // \"no reading\" and renders the dash (see `digitCountsMissing`). Text mode\n // is unaffected — it ignores the numeric knobs entirely.\n const valueForFormat = this.digitCountsMissing\n ? undefined\n : resolveReadoutNumericValue(this.value, this.valueType);\n const textValue = resolveReadoutTextValue(this.value, this.valueType);\n // Text mode renders verbatim and ignores the numeric format options; a\n // blank / missing text value still falls back to the unavailable dash.\n const text = this.off\n ? this.offText\n : isTextMode\n ? (textValue ?? READOUT_UNAVAILABLE_DASH)\n : formatNumericValue(valueForFormat, formatOptions);\n // Hinted zeros pad the INTEGER part up to `maxDigits`, independent of\n // `fractionDigits` (the decimal point and fraction digits never count toward\n // `maxDigits`). Negative / dashed values are not padded. Example: value 1.2,\n // maxDigits 3, fractionDigits 1 → \"001.2\".\n const hintCount =\n this.off ||\n isTextMode ||\n !this.hintedZeros ||\n valueForFormat === undefined ||\n valueForFormat < 0\n ? 0\n : Math.max(this.resolvedMaxDigits - readoutFormattedInteger(text), 0);\n const hinted = hintCount > 0 ? '0'.repeat(hintCount) : '';\n // Hinted zeros own the width — they already fill to `maxDigits` — so when\n // `hintedZeros` is enabled an explicit `spaceReserver` is ignored (it has\n // higher priority). Otherwise the wider of the explicit reserver and the\n // `maxDigits`-derived reserve wins.\n // Text mode ignores the `maxDigits`-derived numeric reserve — only an\n // explicit `spaceReserver` still applies.\n const reserver = isTextMode\n ? (this.spaceReserver ?? '')\n : this.hintedZeros\n ? this.reserverText\n : this.widerReserver(this.spaceReserver, this.reserverText);\n\n const block = html`\n <div\n class=${classMap({\n block: true,\n [`block-${this.variant}`]: true,\n [`size-${this.size}`]: true,\n 'tone-enhanced': this.enhanced,\n touching: this.touching,\n 'is-hiding': this.hidePhase === ReadoutBlockHidePhase.hiding,\n 'is-hidden': this.hidePhase === ReadoutBlockHidePhase.hidden,\n ...this.dataQualityClasses(),\n })}\n part=\"block block-${this.variant}\"\n >\n ${this.renderIcon()}\n <span class=\"block-content\" part=\"block-content\">\n <obc-textbox\n class=\"block-text\"\n part=\"block-text\"\n .size=${valueSize}\n .fontWeight=${this.weight}\n .alignment=${this.alignment}\n .tabularNums=${true}\n >\n ${hinted\n ? html`<span class=\"hinted-zero\" aria-hidden=\"true\"\n >${hinted}</span\n >`\n : nothing}${text}\n ${reserver ? html`<span slot=\"length\">${reserver}</span>` : nothing}\n </obc-textbox>\n ${this.hasDegree ? this.renderDegreeGlyph(valueSize) : nothing}\n </span>\n </div>\n `;\n return wrapWithAlertFrame(this.alert ?? false, block);\n }\n\n static override styles = unsafeCSS(componentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-readout-block': ObcReadoutBlock;\n }\n}\n"],"names":["ReadoutBlockVariant","ReadoutBlockSize","ReadoutBlockDataQuality","ReadoutBlockHidePhase"],"mappings":"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"}