@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.128 → 2.0.0-next.129
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.
- package/bundle/openbridge-webcomponents.bundle.js +12321 -11895
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +227 -3
- package/dist/building-blocks/readout-block/readout-block.css.js +53 -19
- package/dist/building-blocks/readout-block/readout-block.css.js.map +1 -1
- package/dist/building-blocks/readout-block/readout-block.d.ts +55 -0
- package/dist/building-blocks/readout-block/readout-block.d.ts.map +1 -1
- package/dist/building-blocks/readout-block/readout-block.js +57 -2
- package/dist/building-blocks/readout-block/readout-block.js.map +1 -1
- package/dist/navigation-instruments/readout/readout-shared.d.ts +23 -0
- package/dist/navigation-instruments/readout/readout-shared.d.ts.map +1 -1
- package/dist/navigation-instruments/readout/readout-shared.js +8 -0
- package/dist/navigation-instruments/readout/readout-shared.js.map +1 -1
- package/dist/navigation-instruments/readout/readout.css.js +138 -31
- package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
- package/dist/navigation-instruments/readout/readout.d.ts +63 -3
- package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
- package/dist/navigation-instruments/readout/readout.js +127 -27
- package/dist/navigation-instruments/readout/readout.js.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js +64 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts +77 -2
- package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js +100 -23
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
- package/package.json +1 -1
- package/src/building-blocks/readout-block/readout-block.css +49 -20
- package/src/building-blocks/readout-block/readout-block.stories.ts +54 -0
- package/src/building-blocks/readout-block/readout-block.ts +101 -2
- package/src/navigation-instruments/instrument-field/instrument-field.stories.ts +1150 -180
- package/src/navigation-instruments/readout/readout-shared.spec.ts +50 -0
- package/src/navigation-instruments/readout/readout-shared.ts +33 -0
- package/src/navigation-instruments/readout/readout.css +124 -32
- package/src/navigation-instruments/readout/readout.stories.ts +536 -19
- package/src/navigation-instruments/readout/readout.ts +184 -31
- package/src/navigation-instruments/readout-list-item/readout-list-item.css +55 -1
- package/src/navigation-instruments/readout-list-item/readout-list-item.stories.ts +341 -0
- package/src/navigation-instruments/readout-list-item/readout-list-item.ts +169 -25
|
@@ -6,6 +6,15 @@ 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 "../../icons/icon-notification-advice-active.js";
|
|
10
|
+
import "../../icons/icon-caution-google.js";
|
|
11
|
+
import "../../icons/icon-caution-color-iec.js";
|
|
12
|
+
import "../../icons/icon-warning-unacknowledged-outlined.js";
|
|
13
|
+
import "../../icons/icon-warning-unacknowledged-iec.js";
|
|
14
|
+
import "../../icons/icon-alarm.js";
|
|
15
|
+
import "../../icons/icon-alarm-unacknowledged-iec.js";
|
|
16
|
+
import "../../icons/icon-running.js";
|
|
17
|
+
import "../../icons/icon-running-color-iec.js";
|
|
9
18
|
import { ReadoutValueType, resolveReadoutDigitCount, isReadoutDigitCountMissing, assertReadoutValueType, assertReadoutFractionDigits, resolveReadoutNumericValue, resolveReadoutTextValue, READOUT_UNAVAILABLE_DASH, formatNumericValue, readoutFormattedInteger } from "../../navigation-instruments/readout/readout-formatters.js";
|
|
10
19
|
import { wrapWithAlertFrame } from "../../components/alert-frame/alert-frame.js";
|
|
11
20
|
var __defProp = Object.defineProperty;
|
|
@@ -35,6 +44,16 @@ var ReadoutBlockDataQuality = /* @__PURE__ */ ((ReadoutBlockDataQuality2) => {
|
|
|
35
44
|
ReadoutBlockDataQuality2["invalid"] = "invalid";
|
|
36
45
|
return ReadoutBlockDataQuality2;
|
|
37
46
|
})(ReadoutBlockDataQuality || {});
|
|
47
|
+
var ReadoutAdviceCategory = /* @__PURE__ */ ((ReadoutAdviceCategory2) => {
|
|
48
|
+
ReadoutAdviceCategory2["regular"] = "regular";
|
|
49
|
+
ReadoutAdviceCategory2["optimal"] = "optimal";
|
|
50
|
+
ReadoutAdviceCategory2["eco"] = "eco";
|
|
51
|
+
ReadoutAdviceCategory2["caution"] = "caution";
|
|
52
|
+
ReadoutAdviceCategory2["warning"] = "warning";
|
|
53
|
+
ReadoutAdviceCategory2["alarm"] = "alarm";
|
|
54
|
+
ReadoutAdviceCategory2["running"] = "running";
|
|
55
|
+
return ReadoutAdviceCategory2;
|
|
56
|
+
})(ReadoutAdviceCategory || {});
|
|
38
57
|
var ReadoutBlockHidePhase = /* @__PURE__ */ ((ReadoutBlockHidePhase2) => {
|
|
39
58
|
ReadoutBlockHidePhase2["none"] = "none";
|
|
40
59
|
ReadoutBlockHidePhase2["hiding"] = "hiding";
|
|
@@ -58,6 +77,8 @@ let ObcReadoutBlock = class extends LitElement {
|
|
|
58
77
|
this.off = false;
|
|
59
78
|
this.offText = "OFF";
|
|
60
79
|
this.alignment = ObcTextboxAlignment.Right;
|
|
80
|
+
this.category = "regular";
|
|
81
|
+
this.active = false;
|
|
61
82
|
this.alert = false;
|
|
62
83
|
this.touching = false;
|
|
63
84
|
this.hidePhase = "none";
|
|
@@ -152,13 +173,34 @@ let ObcReadoutBlock = class extends LitElement {
|
|
|
152
173
|
if (this.variant === "setpoint") {
|
|
153
174
|
fallback = html`<obi-input-right></obi-input-right>`;
|
|
154
175
|
} else if (this.variant === "advice") {
|
|
155
|
-
fallback =
|
|
176
|
+
fallback = this.adviceFallbackIcon();
|
|
156
177
|
}
|
|
157
178
|
return html`<span class="block-icon" part="block-icon" aria-hidden="true">
|
|
158
179
|
<slot name="icon" @slotchange=${this.onIconSlotChange}></slot>
|
|
159
180
|
${this.hasAssignedIcon ? nothing : fallback}
|
|
160
181
|
</span>`;
|
|
161
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* The advice marker for the current category / triggered state. Resting
|
|
185
|
+
* categories carry a neutral outline glyph (tinted via `currentColor`);
|
|
186
|
+
* triggered alert categories swap to the filled IEC status icon, whose
|
|
187
|
+
* colours are fixed fills and ignore `currentColor` by design.
|
|
188
|
+
*/
|
|
189
|
+
adviceFallbackIcon() {
|
|
190
|
+
const active = this.active;
|
|
191
|
+
switch (this.category) {
|
|
192
|
+
case "caution":
|
|
193
|
+
return active ? html`<obi-caution-color-iec></obi-caution-color-iec>` : html`<obi-caution-google></obi-caution-google>`;
|
|
194
|
+
case "warning":
|
|
195
|
+
return active ? html`<obi-warning-unacknowledged-iec></obi-warning-unacknowledged-iec>` : html`<obi-warning-unacknowledged-outlined></obi-warning-unacknowledged-outlined>`;
|
|
196
|
+
case "alarm":
|
|
197
|
+
return active ? html`<obi-alarm-unacknowledged-iec></obi-alarm-unacknowledged-iec>` : html`<obi-alarm></obi-alarm>`;
|
|
198
|
+
case "running":
|
|
199
|
+
return active ? html`<obi-running-color-iec></obi-running-color-iec>` : html`<obi-running></obi-running>`;
|
|
200
|
+
default:
|
|
201
|
+
return active ? html`<obi-notification-advice-active></obi-notification-advice-active>` : html`<obi-notification-advice></obi-notification-advice>`;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
162
204
|
/**
|
|
163
205
|
* A cap-height `°` column whose width scales with the number size. Inherits the
|
|
164
206
|
* block's colour.
|
|
@@ -186,6 +228,9 @@ let ObcReadoutBlock = class extends LitElement {
|
|
|
186
228
|
}
|
|
187
229
|
render() {
|
|
188
230
|
const valueSize = this.resolvedValueSize;
|
|
231
|
+
const isAdvice = this.variant === "advice";
|
|
232
|
+
const adviceSemibold = isAdvice && this.active && (this.category === "regular" || this.category === "optimal" || this.category === "eco");
|
|
233
|
+
const weight = adviceSemibold ? ObcTextboxFontWeight.semibold : this.weight;
|
|
189
234
|
const formatOptions = this.numericFormatOptions;
|
|
190
235
|
const isTextMode = this.valueType === ReadoutValueType.text;
|
|
191
236
|
const valueForFormat = this.digitCountsMissing ? void 0 : resolveReadoutNumericValue(this.value, this.valueType);
|
|
@@ -200,6 +245,9 @@ let ObcReadoutBlock = class extends LitElement {
|
|
|
200
245
|
block: true,
|
|
201
246
|
[`block-${this.variant}`]: true,
|
|
202
247
|
[`size-${this.size}`]: true,
|
|
248
|
+
[`value-size-${valueSize}`]: true,
|
|
249
|
+
[`advice-${this.category}`]: isAdvice,
|
|
250
|
+
"advice-active": isAdvice && this.active,
|
|
203
251
|
"tone-enhanced": this.enhanced,
|
|
204
252
|
touching: this.touching,
|
|
205
253
|
"is-hiding": this.hidePhase === "hiding",
|
|
@@ -214,7 +262,7 @@ let ObcReadoutBlock = class extends LitElement {
|
|
|
214
262
|
class="block-text"
|
|
215
263
|
part="block-text"
|
|
216
264
|
.size=${valueSize}
|
|
217
|
-
.fontWeight=${
|
|
265
|
+
.fontWeight=${weight}
|
|
218
266
|
.alignment=${this.alignment}
|
|
219
267
|
.tabularNums=${true}
|
|
220
268
|
>
|
|
@@ -279,6 +327,12 @@ __decorateClass([
|
|
|
279
327
|
__decorateClass([
|
|
280
328
|
property({ type: String })
|
|
281
329
|
], ObcReadoutBlock.prototype, "alignment", 2);
|
|
330
|
+
__decorateClass([
|
|
331
|
+
property({ type: String })
|
|
332
|
+
], ObcReadoutBlock.prototype, "category", 2);
|
|
333
|
+
__decorateClass([
|
|
334
|
+
property({ type: Boolean })
|
|
335
|
+
], ObcReadoutBlock.prototype, "active", 2);
|
|
282
336
|
__decorateClass([
|
|
283
337
|
property({ type: String })
|
|
284
338
|
], ObcReadoutBlock.prototype, "dataQuality", 2);
|
|
@@ -302,6 +356,7 @@ export {
|
|
|
302
356
|
ObcTextboxAlignment,
|
|
303
357
|
ObcTextboxFontWeight,
|
|
304
358
|
ObcTextboxSize,
|
|
359
|
+
ReadoutAdviceCategory,
|
|
305
360
|
ReadoutBlockDataQuality,
|
|
306
361
|
ReadoutBlockHidePhase,
|
|
307
362
|
ReadoutBlockSize,
|
|
@@ -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 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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"}
|
|
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 '../../icons/icon-notification-advice-active.js';\nimport '../../icons/icon-caution-google.js';\nimport '../../icons/icon-caution-color-iec.js';\nimport '../../icons/icon-warning-unacknowledged-outlined.js';\nimport '../../icons/icon-warning-unacknowledged-iec.js';\nimport '../../icons/icon-alarm.js';\nimport '../../icons/icon-alarm-unacknowledged-iec.js';\nimport '../../icons/icon-running.js';\nimport '../../icons/icon-running-color-iec.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 * Semantic category of an advice block (Figma 6.1 \"Readout-block-advice\"\n * Value axis). Drives the default marker icon and — while {@link\n * ObcReadoutBlock.active} — the triggered styling:\n * - `regular` / `optimal` / `eco`: the advice diamond; triggered renders the\n * filled diamond (tinted enhanced-blue / mint for optimal / eco) and a\n * SemiBold number.\n * - `caution` / `warning` / `alarm` / `running`: the matching neutral outline\n * icon; triggered swaps in the filled IEC status icon and the active text\n * colour.\n *\n * TODO(designer): the optimal / eco triggered tint covers the whole diamond\n * glyph — the Figma icon tints only the inner plus, which needs a two-tone\n * icon asset; and the advice \"Enhanced\" state (indent background chip) is not\n * implemented yet. Both flagged as \"may change\" in the 6.1 review.\n */\nexport enum ReadoutAdviceCategory {\n regular = 'regular',\n optimal = 'optimal',\n eco = 'eco',\n caution = 'caution',\n warning = 'warning',\n alarm = 'alarm',\n running = 'running',\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 /**\n * Semantic category of an advice block — picks the default marker icon and\n * the {@link active} styling. Only meaningful for `variant=\"advice\"`.\n * @availableWhen variant==advice\n */\n @property({type: String}) category: ReadoutAdviceCategory =\n ReadoutAdviceCategory.regular;\n\n /**\n * The advice is triggered: the marker swaps to its filled/status icon and\n * the number takes the category's triggered styling (SemiBold for\n * regular/optimal/eco, active text colour for the alert categories). Only\n * meaningful for `variant=\"advice\"`.\n * @availableWhen variant==advice\n */\n @property({type: Boolean}) active = false;\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 = this.adviceFallbackIcon();\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 * The advice marker for the current category / triggered state. Resting\n * categories carry a neutral outline glyph (tinted via `currentColor`);\n * triggered alert categories swap to the filled IEC status icon, whose\n * colours are fixed fills and ignore `currentColor` by design.\n */\n private adviceFallbackIcon(): TemplateResult {\n const active = this.active;\n switch (this.category) {\n case ReadoutAdviceCategory.caution:\n return active\n ? html`<obi-caution-color-iec></obi-caution-color-iec>`\n : html`<obi-caution-google></obi-caution-google>`;\n case ReadoutAdviceCategory.warning:\n return active\n ? html`<obi-warning-unacknowledged-iec></obi-warning-unacknowledged-iec>`\n : html`<obi-warning-unacknowledged-outlined></obi-warning-unacknowledged-outlined>`;\n case ReadoutAdviceCategory.alarm:\n return active\n ? html`<obi-alarm-unacknowledged-iec></obi-alarm-unacknowledged-iec>`\n : html`<obi-alarm></obi-alarm>`;\n case ReadoutAdviceCategory.running:\n return active\n ? html`<obi-running-color-iec></obi-running-color-iec>`\n : html`<obi-running></obi-running>`;\n default:\n // regular / optimal / eco share the advice diamond; the triggered tint\n // is applied via CSS on `.block-icon` (see `advice-*` classes).\n return active\n ? html`<obi-notification-advice-active></obi-notification-advice-active>`\n : html`<obi-notification-advice></obi-notification-advice>`;\n }\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 isAdvice = this.variant === ReadoutBlockVariant.advice;\n // Triggered regular/optimal/eco advice renders SemiBold (Figma 6.1); the\n // alert categories keep the caller's weight and change colour instead.\n const adviceSemibold =\n isAdvice &&\n this.active &&\n (this.category === ReadoutAdviceCategory.regular ||\n this.category === ReadoutAdviceCategory.optimal ||\n this.category === ReadoutAdviceCategory.eco);\n const weight = adviceSemibold ? ObcTextboxFontWeight.semibold : this.weight;\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 [`value-size-${valueSize}`]: true,\n [`advice-${this.category}`]: isAdvice,\n 'advice-active': isAdvice && this.active,\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=${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","ReadoutAdviceCategory","ReadoutBlockHidePhase"],"mappings":"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"}
|
|
@@ -41,6 +41,29 @@ export declare function isDisplayedAtSetpoint(value: number | null, setpoint: nu
|
|
|
41
41
|
export declare function readoutPrimarySize(size: ReadoutBlockSize): ObcTextboxSize;
|
|
42
42
|
/** Secondary (de-emphasised) value-typography size for a density tier. */
|
|
43
43
|
export declare function readoutSecondarySize(size: ReadoutBlockSize): ObcTextboxSize;
|
|
44
|
+
/**
|
|
45
|
+
* The setpoint block's typography size: primary while emphasised (touching /
|
|
46
|
+
* flip-flop away from setpoint) or in the `equal-size` interaction, secondary
|
|
47
|
+
* otherwise.
|
|
48
|
+
*/
|
|
49
|
+
export declare function readoutSetpointSize(options: {
|
|
50
|
+
primary: ObcTextboxSize;
|
|
51
|
+
secondary: ObcTextboxSize;
|
|
52
|
+
emphasized: boolean;
|
|
53
|
+
equalSize: boolean;
|
|
54
|
+
}): ObcTextboxSize;
|
|
55
|
+
/**
|
|
56
|
+
* The value block's typography size: secondary while the setpoint is the
|
|
57
|
+
* focus of attention (touching / flip-flop away from setpoint) — EXCEPT in
|
|
58
|
+
* the `equal-size` interaction, whose whole point is that both blocks hold
|
|
59
|
+
* the primary size; there the value never demotes, not even while touching.
|
|
60
|
+
*/
|
|
61
|
+
export declare function readoutValueSize(options: {
|
|
62
|
+
primary: ObcTextboxSize;
|
|
63
|
+
secondary: ObcTextboxSize;
|
|
64
|
+
setpointEmphasized: boolean;
|
|
65
|
+
equalSize: boolean;
|
|
66
|
+
}): ObcTextboxSize;
|
|
44
67
|
/** Setpoint is SemiBold only while emphasised, otherwise regular weight. */
|
|
45
68
|
export declare function readoutSetpointWeight(emphasized: boolean): ObcTextboxFontWeight;
|
|
46
69
|
/** classMap fragment for a block / source carrying per-block data quality. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readout-shared.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/readout/readout-shared.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,oBAAoB,EACrB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACtB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACrB,2BAA2B,CAY7B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,aAAa,EAAE,2BAA2B,GACzC,OAAO,CAkBT;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,cAAc,CASzE;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,gBAAgB,GAAG,cAAc,CAS3E;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,OAAO,GAClB,oBAAoB,CAItB;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,uBAAuB,GAAG,SAAS,GAC/C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,OAAO,EACxB,aAAa,EAAE,qBAAqB,GACnC,qBAAqB,CAEvB"}
|
|
1
|
+
{"version":3,"file":"readout-shared.d.ts","sourceRoot":"","sources":["../../../src/navigation-instruments/readout/readout-shared.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,oBAAoB,EACrB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACtB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACrB,2BAA2B,CAY7B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,aAAa,EAAE,2BAA2B,GACzC,OAAO,CAkBT;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,cAAc,CASzE;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,gBAAgB,GAAG,cAAc,CAS3E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,EAAE,cAAc,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,cAAc,CAIjB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,EAAE,cAAc,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,cAAc,CAIjB;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,OAAO,GAClB,oBAAoB,CAItB;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,uBAAuB,GAAG,SAAS,GAC/C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,OAAO,EACxB,aAAa,EAAE,qBAAqB,GACnC,qBAAqB,CAEvB"}
|
|
@@ -43,6 +43,12 @@ function readoutSecondarySize(size) {
|
|
|
43
43
|
return ObcTextboxSize.xs;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
+
function readoutSetpointSize(options) {
|
|
47
|
+
return options.emphasized || options.equalSize ? options.primary : options.secondary;
|
|
48
|
+
}
|
|
49
|
+
function readoutValueSize(options) {
|
|
50
|
+
return options.setpointEmphasized && !options.equalSize ? options.secondary : options.primary;
|
|
51
|
+
}
|
|
46
52
|
function readoutSetpointWeight(emphasized) {
|
|
47
53
|
return emphasized ? ObcTextboxFontWeight.semibold : ObcTextboxFontWeight.regular;
|
|
48
54
|
}
|
|
@@ -61,7 +67,9 @@ export {
|
|
|
61
67
|
readoutNumericFormatOptions,
|
|
62
68
|
readoutPrimarySize,
|
|
63
69
|
readoutSecondarySize,
|
|
70
|
+
readoutSetpointSize,
|
|
64
71
|
readoutSetpointWeight,
|
|
72
|
+
readoutValueSize,
|
|
65
73
|
resolveSetpointHidePhase
|
|
66
74
|
};
|
|
67
75
|
//# sourceMappingURL=readout-shared.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readout-shared.js","sources":["../../../src/navigation-instruments/readout/readout-shared.ts"],"sourcesContent":["import {\n ObcTextboxSize,\n ObcTextboxFontWeight,\n} from '../../components/textbox/textbox.js';\nimport {\n ReadoutBlockSize,\n ReadoutBlockDataQuality,\n ReadoutBlockHidePhase,\n} from '../../building-blocks/readout-block/readout-block.js';\nimport {\n formatNumericValue,\n type ReadoutNumericFormatOptions,\n} from './readout-formatters.js';\n\n/**\n * Pure helpers shared by `obc-readout` and `obc-readout-list-item`. The two\n * components are layout siblings built from the same primitives + per-block\n * options API (see #1012); this module keeps their behavioral logic in\n * lock-step until an eventual merge.\n *\n * ## Features\n * - **Numeric formatting:** {@link readoutNumericFormatOptions} builds the\n * shared `maxDigits`/`fractionDigits` format options (never zero-padded).\n * - **Setpoint comparison:** {@link isDisplayedAtSetpoint} compares the\n * DISPLAYED (rounded) value and setpoint strings, not the raw numbers.\n * - **Typography mapping:** {@link readoutPrimarySize} /\n * {@link readoutSecondarySize} map the density tier to the primary /\n * de-emphasised `obc-textbox` size; {@link readoutSetpointWeight} picks the\n * setpoint font weight from its emphasis state.\n * - **Data quality:** {@link readoutDataQualityClasses} produces the classMap\n * fragment for the low-integrity / invalid chip.\n * - **Pop-up hide phase:** {@link resolveSetpointHidePhase} maps the\n * component's deferred-hide state onto the block-level\n * `ReadoutBlockHidePhase`.\n *\n * ## Usage Guidelines\n * These helpers are consumed by the components' private getters — behavioral\n * changes belong here (once), not re-inlined per component.\n *\n * @example\n * const format = readoutNumericFormatOptions(3, 1);\n * const atSetpoint = isDisplayedAtSetpoint(29.99, 30, format);\n */\n\nexport function readoutNumericFormatOptions(\n maxDigits: number,\n fractionDigits: number\n): ReadoutNumericFormatOptions {\n return {\n // Comparison-only (see `isDisplayedAtSetpoint`), which returns early unless\n // both operands are finite numbers — so `formatNumericValue` never reaches\n // its unavailable-dash branch here and the padding would have no effect.\n // `obc-readout-block` sets it to `false` for rendering as well, since the\n // unavailable placeholder is deliberately short rather than filled out to\n // `maxDigits`. Nothing enables it today.\n showZeroPadding: false,\n minValueLength: maxDigits,\n fractionDigits,\n };\n}\n\n/**\n * Whether the value reads as \"at the setpoint\". Compares what is DISPLAYED\n * (rounded to fractionDigits), not the raw values, so e.g. 29.999 and 30 at\n * fractionDigits=0 both read \"30\" → at setpoint.\n */\nexport function isDisplayedAtSetpoint(\n value: number | null,\n setpoint: number | undefined,\n formatOptions: ReadoutNumericFormatOptions\n): boolean {\n if (value === null || setpoint === undefined) {\n return false;\n }\n // An unavailable reading is never \"at\" the setpoint. Callers normalise\n // `value` (via `resolveReadoutNumericValue`) but pass `setpoint` raw, so a\n // non-finite setpoint would otherwise be formatted as the literal \"NaN\" /\n // \"Infinity\" and compared as a string. The comparison result happens to be\n // correct either way — a normalised `value` can never also format to \"NaN\" —\n // but guarding both keeps the two operands symmetric and the invariant below\n // honest.\n if (!Number.isFinite(value) || !Number.isFinite(setpoint)) {\n return false;\n }\n return (\n formatNumericValue(value, formatOptions) ===\n formatNumericValue(setpoint, formatOptions)\n );\n}\n\n/** Primary value-typography size for a density tier. */\nexport function readoutPrimarySize(size: ReadoutBlockSize): ObcTextboxSize {\n switch (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/** Secondary (de-emphasised) value-typography size for a density tier. */\nexport function readoutSecondarySize(size: ReadoutBlockSize): ObcTextboxSize {\n switch (size) {\n case ReadoutBlockSize.large:\n return ObcTextboxSize.s;\n case ReadoutBlockSize.medium:\n return ObcTextboxSize.s;\n default:\n return ObcTextboxSize.xs;\n }\n}\n\n/** Setpoint is SemiBold only while emphasised, otherwise regular weight. */\nexport function readoutSetpointWeight(\n emphasized: boolean\n): ObcTextboxFontWeight {\n return emphasized\n ? ObcTextboxFontWeight.semibold\n : ObcTextboxFontWeight.regular;\n}\n\n/** classMap fragment for a block / source carrying per-block data quality. */\nexport function readoutDataQualityClasses(\n dataQuality: ReadoutBlockDataQuality | undefined\n): Record<string, boolean> {\n return {\n 'data-low-integrity': dataQuality === ReadoutBlockDataQuality.lowIntegrity,\n 'data-invalid': dataQuality === ReadoutBlockDataQuality.invalid,\n };\n}\n\n/**\n * The block-level hide phase for a pop-up setpoint: the component's deferred\n * phase while the value sits at the setpoint, `none` otherwise (value away\n * from the setpoint, or touching).\n */\nexport function resolveSetpointHidePhase(\n popUpAtSetpoint: boolean,\n deferredPhase: ReadoutBlockHidePhase\n): ReadoutBlockHidePhase {\n return popUpAtSetpoint ? deferredPhase : ReadoutBlockHidePhase.none;\n}\n"],"names":[],"mappings":";;;AA4CO,SAAS,4BACd,WACA,gBAC6B;AAC7B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB;AAAA,EAAA;AAEJ;AAOO,SAAS,sBACd,OACA,UACA,eACS;AACT,MAAI,UAAU,QAAQ,aAAa,QAAW;AAC5C,WAAO;AAAA,EACT;AAQA,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,OAAO,SAAS,QAAQ,GAAG;AACzD,WAAO;AAAA,EACT;AACA,SACE,mBAAmB,OAAO,aAAa,MACvC,mBAAmB,UAAU,aAAa;AAE9C;AAGO,SAAS,mBAAmB,MAAwC;AACzE,UAAQ,MAAA;AAAA,IACN,KAAK,iBAAiB;AACpB,aAAO,eAAe;AAAA,IACxB,KAAK,iBAAiB;AACpB,aAAO,eAAe;AAAA,IACxB;AACE,aAAO,eAAe;AAAA,EAAA;AAE5B;AAGO,SAAS,qBAAqB,MAAwC;AAC3E,UAAQ,MAAA;AAAA,IACN,KAAK,iBAAiB;AACpB,aAAO,eAAe;AAAA,IACxB,KAAK,iBAAiB;AACpB,aAAO,eAAe;AAAA,IACxB;AACE,aAAO,eAAe;AAAA,EAAA;AAE5B;AAGO,SAAS,sBACd,YACsB;AACtB,SAAO,aACH,qBAAqB,WACrB,qBAAqB;AAC3B;AAGO,SAAS,0BACd,aACyB;AACzB,SAAO;AAAA,IACL,sBAAsB,gBAAgB,wBAAwB;AAAA,IAC9D,gBAAgB,gBAAgB,wBAAwB;AAAA,EAAA;AAE5D;AAOO,SAAS,yBACd,iBACA,eACuB;AACvB,SAAO,kBAAkB,gBAAgB,sBAAsB;AACjE;"}
|
|
1
|
+
{"version":3,"file":"readout-shared.js","sources":["../../../src/navigation-instruments/readout/readout-shared.ts"],"sourcesContent":["import {\n ObcTextboxSize,\n ObcTextboxFontWeight,\n} from '../../components/textbox/textbox.js';\nimport {\n ReadoutBlockSize,\n ReadoutBlockDataQuality,\n ReadoutBlockHidePhase,\n} from '../../building-blocks/readout-block/readout-block.js';\nimport {\n formatNumericValue,\n type ReadoutNumericFormatOptions,\n} from './readout-formatters.js';\n\n/**\n * Pure helpers shared by `obc-readout` and `obc-readout-list-item`. The two\n * components are layout siblings built from the same primitives + per-block\n * options API (see #1012); this module keeps their behavioral logic in\n * lock-step until an eventual merge.\n *\n * ## Features\n * - **Numeric formatting:** {@link readoutNumericFormatOptions} builds the\n * shared `maxDigits`/`fractionDigits` format options (never zero-padded).\n * - **Setpoint comparison:** {@link isDisplayedAtSetpoint} compares the\n * DISPLAYED (rounded) value and setpoint strings, not the raw numbers.\n * - **Typography mapping:** {@link readoutPrimarySize} /\n * {@link readoutSecondarySize} map the density tier to the primary /\n * de-emphasised `obc-textbox` size; {@link readoutSetpointWeight} picks the\n * setpoint font weight from its emphasis state.\n * - **Data quality:** {@link readoutDataQualityClasses} produces the classMap\n * fragment for the low-integrity / invalid chip.\n * - **Pop-up hide phase:** {@link resolveSetpointHidePhase} maps the\n * component's deferred-hide state onto the block-level\n * `ReadoutBlockHidePhase`.\n *\n * ## Usage Guidelines\n * These helpers are consumed by the components' private getters — behavioral\n * changes belong here (once), not re-inlined per component.\n *\n * @example\n * const format = readoutNumericFormatOptions(3, 1);\n * const atSetpoint = isDisplayedAtSetpoint(29.99, 30, format);\n */\n\nexport function readoutNumericFormatOptions(\n maxDigits: number,\n fractionDigits: number\n): ReadoutNumericFormatOptions {\n return {\n // Comparison-only (see `isDisplayedAtSetpoint`), which returns early unless\n // both operands are finite numbers — so `formatNumericValue` never reaches\n // its unavailable-dash branch here and the padding would have no effect.\n // `obc-readout-block` sets it to `false` for rendering as well, since the\n // unavailable placeholder is deliberately short rather than filled out to\n // `maxDigits`. Nothing enables it today.\n showZeroPadding: false,\n minValueLength: maxDigits,\n fractionDigits,\n };\n}\n\n/**\n * Whether the value reads as \"at the setpoint\". Compares what is DISPLAYED\n * (rounded to fractionDigits), not the raw values, so e.g. 29.999 and 30 at\n * fractionDigits=0 both read \"30\" → at setpoint.\n */\nexport function isDisplayedAtSetpoint(\n value: number | null,\n setpoint: number | undefined,\n formatOptions: ReadoutNumericFormatOptions\n): boolean {\n if (value === null || setpoint === undefined) {\n return false;\n }\n // An unavailable reading is never \"at\" the setpoint. Callers normalise\n // `value` (via `resolveReadoutNumericValue`) but pass `setpoint` raw, so a\n // non-finite setpoint would otherwise be formatted as the literal \"NaN\" /\n // \"Infinity\" and compared as a string. The comparison result happens to be\n // correct either way — a normalised `value` can never also format to \"NaN\" —\n // but guarding both keeps the two operands symmetric and the invariant below\n // honest.\n if (!Number.isFinite(value) || !Number.isFinite(setpoint)) {\n return false;\n }\n return (\n formatNumericValue(value, formatOptions) ===\n formatNumericValue(setpoint, formatOptions)\n );\n}\n\n/** Primary value-typography size for a density tier. */\nexport function readoutPrimarySize(size: ReadoutBlockSize): ObcTextboxSize {\n switch (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/** Secondary (de-emphasised) value-typography size for a density tier. */\nexport function readoutSecondarySize(size: ReadoutBlockSize): ObcTextboxSize {\n switch (size) {\n case ReadoutBlockSize.large:\n return ObcTextboxSize.s;\n case ReadoutBlockSize.medium:\n return ObcTextboxSize.s;\n default:\n return ObcTextboxSize.xs;\n }\n}\n\n/**\n * The setpoint block's typography size: primary while emphasised (touching /\n * flip-flop away from setpoint) or in the `equal-size` interaction, secondary\n * otherwise.\n */\nexport function readoutSetpointSize(options: {\n primary: ObcTextboxSize;\n secondary: ObcTextboxSize;\n emphasized: boolean;\n equalSize: boolean;\n}): ObcTextboxSize {\n return options.emphasized || options.equalSize\n ? options.primary\n : options.secondary;\n}\n\n/**\n * The value block's typography size: secondary while the setpoint is the\n * focus of attention (touching / flip-flop away from setpoint) — EXCEPT in\n * the `equal-size` interaction, whose whole point is that both blocks hold\n * the primary size; there the value never demotes, not even while touching.\n */\nexport function readoutValueSize(options: {\n primary: ObcTextboxSize;\n secondary: ObcTextboxSize;\n setpointEmphasized: boolean;\n equalSize: boolean;\n}): ObcTextboxSize {\n return options.setpointEmphasized && !options.equalSize\n ? options.secondary\n : options.primary;\n}\n\n/** Setpoint is SemiBold only while emphasised, otherwise regular weight. */\nexport function readoutSetpointWeight(\n emphasized: boolean\n): ObcTextboxFontWeight {\n return emphasized\n ? ObcTextboxFontWeight.semibold\n : ObcTextboxFontWeight.regular;\n}\n\n/** classMap fragment for a block / source carrying per-block data quality. */\nexport function readoutDataQualityClasses(\n dataQuality: ReadoutBlockDataQuality | undefined\n): Record<string, boolean> {\n return {\n 'data-low-integrity': dataQuality === ReadoutBlockDataQuality.lowIntegrity,\n 'data-invalid': dataQuality === ReadoutBlockDataQuality.invalid,\n };\n}\n\n/**\n * The block-level hide phase for a pop-up setpoint: the component's deferred\n * phase while the value sits at the setpoint, `none` otherwise (value away\n * from the setpoint, or touching).\n */\nexport function resolveSetpointHidePhase(\n popUpAtSetpoint: boolean,\n deferredPhase: ReadoutBlockHidePhase\n): ReadoutBlockHidePhase {\n return popUpAtSetpoint ? deferredPhase : ReadoutBlockHidePhase.none;\n}\n"],"names":[],"mappings":";;;AA4CO,SAAS,4BACd,WACA,gBAC6B;AAC7B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOL,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB;AAAA,EAAA;AAEJ;AAOO,SAAS,sBACd,OACA,UACA,eACS;AACT,MAAI,UAAU,QAAQ,aAAa,QAAW;AAC5C,WAAO;AAAA,EACT;AAQA,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,OAAO,SAAS,QAAQ,GAAG;AACzD,WAAO;AAAA,EACT;AACA,SACE,mBAAmB,OAAO,aAAa,MACvC,mBAAmB,UAAU,aAAa;AAE9C;AAGO,SAAS,mBAAmB,MAAwC;AACzE,UAAQ,MAAA;AAAA,IACN,KAAK,iBAAiB;AACpB,aAAO,eAAe;AAAA,IACxB,KAAK,iBAAiB;AACpB,aAAO,eAAe;AAAA,IACxB;AACE,aAAO,eAAe;AAAA,EAAA;AAE5B;AAGO,SAAS,qBAAqB,MAAwC;AAC3E,UAAQ,MAAA;AAAA,IACN,KAAK,iBAAiB;AACpB,aAAO,eAAe;AAAA,IACxB,KAAK,iBAAiB;AACpB,aAAO,eAAe;AAAA,IACxB;AACE,aAAO,eAAe;AAAA,EAAA;AAE5B;AAOO,SAAS,oBAAoB,SAKjB;AACjB,SAAO,QAAQ,cAAc,QAAQ,YACjC,QAAQ,UACR,QAAQ;AACd;AAQO,SAAS,iBAAiB,SAKd;AACjB,SAAO,QAAQ,sBAAsB,CAAC,QAAQ,YAC1C,QAAQ,YACR,QAAQ;AACd;AAGO,SAAS,sBACd,YACsB;AACtB,SAAO,aACH,qBAAqB,WACrB,qBAAqB;AAC3B;AAGO,SAAS,0BACd,aACyB;AACzB,SAAO;AAAA,IACL,sBAAsB,gBAAgB,wBAAwB;AAAA,IAC9D,gBAAgB,gBAAgB,wBAAwB;AAAA,EAAA;AAE5D;AAOO,SAAS,yBACd,iBACA,eACuB;AACvB,SAAO,kBAAkB,gBAAgB,sBAAsB;AACjE;"}
|
|
@@ -47,7 +47,7 @@ obc-readout-block {
|
|
|
47
47
|
);
|
|
48
48
|
--obc-readout-block-hinted-color: var(
|
|
49
49
|
--obc-readout-hinted-color,
|
|
50
|
-
var(--element-
|
|
50
|
+
var(--element-disabled-color)
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -269,29 +269,76 @@ obc-readout-block {
|
|
|
269
269
|
);
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
/* ---------- Meta zone (label + unit) ----------
|
|
272
|
+
/* ---------- Meta zone (label + unit) ----------
|
|
273
|
+
* \`.meta\` is a row hosting the optional leading icon and \`.meta-labels\`; the
|
|
274
|
+
* inline/stacked arrangement applies to \`.meta-labels\` (the label/unit pair). */
|
|
273
275
|
|
|
274
276
|
.meta {
|
|
275
277
|
display: inline-flex;
|
|
278
|
+
flex-direction: row;
|
|
279
|
+
align-items: center;
|
|
280
|
+
gap: var(--instrument-components-readout-new-general-regular-icon-gap);
|
|
276
281
|
padding-inline: var(--_readout-trim);
|
|
277
282
|
}
|
|
278
283
|
|
|
279
|
-
.meta.
|
|
284
|
+
.meta .leading-icon {
|
|
285
|
+
display: inline-flex;
|
|
286
|
+
align-items: center;
|
|
287
|
+
justify-content: center;
|
|
288
|
+
flex: none;
|
|
289
|
+
color: var(--element-neutral-color);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.meta .leading-icon ::slotted(*) {
|
|
293
|
+
display: block;
|
|
294
|
+
inline-size: 100%;
|
|
295
|
+
block-size: 100%;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.readout.size-small .meta .leading-icon {
|
|
299
|
+
inline-size: var(
|
|
300
|
+
--instrument-components-readout-new-general-regular-icon-size
|
|
301
|
+
);
|
|
302
|
+
block-size: var(
|
|
303
|
+
--instrument-components-readout-new-general-regular-icon-size
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.readout.size-medium .meta .leading-icon {
|
|
308
|
+
inline-size: var(
|
|
309
|
+
--instrument-components-readout-new-general-medium-icon-size
|
|
310
|
+
);
|
|
311
|
+
block-size: var(--instrument-components-readout-new-general-medium-icon-size);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.readout.size-large .meta .leading-icon {
|
|
315
|
+
inline-size: var(--instrument-components-readout-new-general-large-icon-size);
|
|
316
|
+
block-size: var(--instrument-components-readout-new-general-large-icon-size);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.meta-labels {
|
|
320
|
+
display: inline-flex;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.meta-inline .meta-labels {
|
|
280
324
|
flex-direction: row;
|
|
281
|
-
align
|
|
325
|
+
/* Bottom-align so an \`s\` label and its \`xs\` unit share a baseline (the
|
|
326
|
+
Figma title block is items-end); identical to the old flex-start while
|
|
327
|
+
both were \`xs\`. */
|
|
328
|
+
align-items: flex-end;
|
|
282
329
|
gap: var(--global-size-spacing-target-padding);
|
|
283
330
|
}
|
|
284
331
|
|
|
285
|
-
.meta.meta-
|
|
332
|
+
.meta-stacked .meta-labels {
|
|
286
333
|
flex-direction: column;
|
|
287
334
|
align-items: flex-end;
|
|
288
335
|
}
|
|
289
336
|
|
|
290
|
-
.readout.direction-vertical.alignment-left .meta.meta-
|
|
337
|
+
.readout.direction-vertical.alignment-left .meta-stacked .meta-labels {
|
|
291
338
|
align-items: flex-start;
|
|
292
339
|
}
|
|
293
340
|
|
|
294
|
-
.readout.direction-vertical.alignment-center .meta.meta-
|
|
341
|
+
.readout.direction-vertical.alignment-center .meta-stacked .meta-labels {
|
|
295
342
|
align-items: center;
|
|
296
343
|
}
|
|
297
344
|
|
|
@@ -299,10 +346,13 @@ obc-readout-block {
|
|
|
299
346
|
the row so the label reads first. */
|
|
300
347
|
|
|
301
348
|
.readout.direction-horizontal .meta.meta-stacked {
|
|
302
|
-
align-items: flex-start;
|
|
303
349
|
align-self: center;
|
|
304
350
|
}
|
|
305
351
|
|
|
352
|
+
:is(.readout.direction-horizontal .meta.meta-stacked) .meta-labels {
|
|
353
|
+
align-items: flex-start;
|
|
354
|
+
}
|
|
355
|
+
|
|
306
356
|
.label-only:is(.readout.direction-horizontal .meta.meta-stacked) {
|
|
307
357
|
align-self: flex-start;
|
|
308
358
|
}
|
|
@@ -311,6 +361,21 @@ obc-readout-block {
|
|
|
311
361
|
align-self: flex-end;
|
|
312
362
|
}
|
|
313
363
|
|
|
364
|
+
/* Cap alignment beside the value (Figma 6.1 review): the label's cap TOP and
|
|
365
|
+
the unit's cap BOTTOM line up with the value's cap edges. The stack is
|
|
366
|
+
pinned to the value's container height with space-between — every textbox
|
|
367
|
+
carries a fixed 4px pad above/below its cap, exactly like the value box, so
|
|
368
|
+
pinning the boxes to the container edges aligns the caps for any label/unit
|
|
369
|
+
size. Only when both lines exist: a single line keeps its edge alignment
|
|
370
|
+
from the rules above. */
|
|
371
|
+
|
|
372
|
+
.readout.direction-horizontal
|
|
373
|
+
.meta.meta-stacked:not(.label-only):not(.unit-only)
|
|
374
|
+
.meta-labels {
|
|
375
|
+
block-size: var(--_readout-primary-height);
|
|
376
|
+
justify-content: space-between;
|
|
377
|
+
}
|
|
378
|
+
|
|
314
379
|
.label {
|
|
315
380
|
color: var(--obc-readout-label-color, var(--element-neutral-color));
|
|
316
381
|
}
|
|
@@ -348,6 +413,69 @@ obc-readout-block {
|
|
|
348
413
|
outline-color: var(--alert-invalid-border-color);
|
|
349
414
|
}
|
|
350
415
|
|
|
416
|
+
/* ---------- Source state chip & deviation (Figma 6.1 Readout-block-source)
|
|
417
|
+
* \`enhanced\` renders the amplified chip, \`caution\`/\`warning\` the alert chip.
|
|
418
|
+
* Like the data-quality chip these use \`outline\`, so toggling a state never
|
|
419
|
+
* shifts the layout. TODO(designer): Figma pads the chip 4px; a padding here
|
|
420
|
+
* would shift the layout on live state changes, so the chip hugs the text
|
|
421
|
+
* like the data-quality chip does. */
|
|
422
|
+
|
|
423
|
+
.source-block {
|
|
424
|
+
display: inline-flex;
|
|
425
|
+
flex-direction: column;
|
|
426
|
+
align-items: flex-start;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.source-block.source-state-enhanced,
|
|
430
|
+
.source-block.source-state-caution,
|
|
431
|
+
.source-block.source-state-warning {
|
|
432
|
+
outline: 1px solid;
|
|
433
|
+
border-radius: var(--global-border-radius-border-radius-check);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.source-block.source-state-enhanced {
|
|
437
|
+
background: var(--amplified-enabled-background-color);
|
|
438
|
+
outline-color: var(--amplified-enabled-border-color);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.source-block.source-state-enhanced .source,
|
|
442
|
+
.source-block.source-state-enhanced .source-deviation {
|
|
443
|
+
color: var(--on-amplified-active-color);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.source-block.source-state-caution {
|
|
447
|
+
background: var(--alert-caution-color);
|
|
448
|
+
outline-color: var(--alert-caution-outline-color);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.source-block.source-state-caution .source,
|
|
452
|
+
.source-block.source-state-caution .source-deviation {
|
|
453
|
+
color: var(--on-caution-active-color);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.source-block.source-state-warning {
|
|
457
|
+
background: var(--alert-warning-color);
|
|
458
|
+
outline-color: var(--alert-warning-outline-color);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.source-block.source-state-warning .source,
|
|
462
|
+
.source-block.source-state-warning .source-deviation {
|
|
463
|
+
color: var(--on-warning-active-color);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.source-deviation {
|
|
467
|
+
display: inline-flex;
|
|
468
|
+
align-items: center;
|
|
469
|
+
gap: 2px;
|
|
470
|
+
color: var(--element-inactive-color);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.source-deviation obi-delta {
|
|
474
|
+
display: block;
|
|
475
|
+
inline-size: 12px;
|
|
476
|
+
block-size: 12px;
|
|
477
|
+
}
|
|
478
|
+
|
|
351
479
|
/* ---------- Source row & auto divider ---------- */
|
|
352
480
|
|
|
353
481
|
.source-row {
|
|
@@ -361,11 +489,8 @@ obc-readout-block {
|
|
|
361
489
|
background-color: var(--border-divider-color);
|
|
362
490
|
}
|
|
363
491
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
block-size: 1px;
|
|
367
|
-
margin-block: var(--_readout-divider-pad);
|
|
368
|
-
}
|
|
492
|
+
/* Only the horizontal direction draws a source divider (Figma 6.1: vertical
|
|
493
|
+
readouts have no rule above the source). */
|
|
369
494
|
|
|
370
495
|
.divider.divider-vertical {
|
|
371
496
|
inline-size: 1px;
|
|
@@ -523,9 +648,6 @@ obc-readout-block {
|
|
|
523
648
|
--_readout-value-pad-v: var(
|
|
524
649
|
--instrument-components-readout-new-group-vertical-regular-value-container-padding-vertical
|
|
525
650
|
);
|
|
526
|
-
--_readout-divider-pad: var(
|
|
527
|
-
--instrument-components-readout-new-group-vertical-regular-divider-padding
|
|
528
|
-
);
|
|
529
651
|
--_readout-min-height: var(
|
|
530
652
|
--instrument-components-readout-new-group-vertical-regular-container-min-height
|
|
531
653
|
);
|
|
@@ -551,9 +673,6 @@ obc-readout-block {
|
|
|
551
673
|
--_readout-value-pad-v: var(
|
|
552
674
|
--instrument-components-readout-new-group-vertical-medium-value-container-padding-vertical
|
|
553
675
|
);
|
|
554
|
-
--_readout-divider-pad: var(
|
|
555
|
-
--instrument-components-readout-new-group-vertical-medium-divider-padding
|
|
556
|
-
);
|
|
557
676
|
}
|
|
558
677
|
|
|
559
678
|
.readout.direction-vertical.stacking-inline.size-large {
|
|
@@ -572,9 +691,6 @@ obc-readout-block {
|
|
|
572
691
|
--_readout-value-pad-v: var(
|
|
573
692
|
--instrument-components-readout-new-group-vertical-enhanced-vertical-value-container-padding-vertical
|
|
574
693
|
);
|
|
575
|
-
--_readout-divider-pad: var(
|
|
576
|
-
--instrument-components-readout-new-group-vertical-enhanced-divider-padding
|
|
577
|
-
);
|
|
578
694
|
--_readout-min-height: var(
|
|
579
695
|
--instrument-components-readout-new-group-vertical-enhanced-container-min-height
|
|
580
696
|
);
|
|
@@ -598,9 +714,6 @@ obc-readout-block {
|
|
|
598
714
|
--_readout-value-pad-v: var(
|
|
599
715
|
--instrument-components-readout-new-stack-regular-value-container-padding-vertical
|
|
600
716
|
);
|
|
601
|
-
--_readout-divider-pad: var(
|
|
602
|
-
--instrument-components-readout-new-stack-regular-divider-padding
|
|
603
|
-
);
|
|
604
717
|
--_readout-min-height: var(
|
|
605
718
|
--instrument-components-readout-new-stack-regular-container-min-height
|
|
606
719
|
);
|
|
@@ -622,9 +735,6 @@ obc-readout-block {
|
|
|
622
735
|
--_readout-value-pad-v: var(
|
|
623
736
|
--instrument-components-readout-new-stack-medium-value-container-padding-vertical
|
|
624
737
|
);
|
|
625
|
-
--_readout-divider-pad: var(
|
|
626
|
-
--instrument-components-readout-new-stack-medium-divider-padding
|
|
627
|
-
);
|
|
628
738
|
}
|
|
629
739
|
|
|
630
740
|
.readout.direction-vertical.stacking-stacked.size-large {
|
|
@@ -643,9 +753,6 @@ obc-readout-block {
|
|
|
643
753
|
--_readout-value-pad-v: var(
|
|
644
754
|
--instrument-components-readout-new-stack-enhanced-vertical-value-container-padding-vertical
|
|
645
755
|
);
|
|
646
|
-
--_readout-divider-pad: var(
|
|
647
|
-
--instrument-components-readout-new-stack-enhanced-divider-padding
|
|
648
|
-
);
|
|
649
756
|
--_readout-min-height: var(
|
|
650
757
|
--instrument-components-readout-new-stack-enhanced-container-min-height
|
|
651
758
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readout.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"readout.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|