@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.86 → 2.0.0-next.88

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.
@@ -1 +1 @@
1
- {"version":3,"file":"gauge-radial.js","sources":["../../../src/navigation-instruments/gauge-radial/gauge-radial.ts"],"sourcesContent":["import {LitElement, html, nothing, unsafeCSS, type TemplateResult} from 'lit';\nimport {classMap} from 'lit/directives/class-map.js';\nimport componentStyle from './gauge-radial.css?inline';\nimport {customElement} from '../../decorator.js';\nimport {property} from 'lit/decorators.js';\nimport {AdviceType} from '../watch/advice.js';\nimport {InstrumentState, Priority} from '../types.js';\nimport {SetpointMixin} from '../../svghelpers/setpoint-mixin.js';\nimport '../../building-blocks/instrument-radial/instrument-radial.js';\nimport {TickmarkStyle} from '../watch/tickmark.js';\nimport {renderInstrumentReadout} from '../readout/instrument-readout.js';\nimport {\n ReadoutStackVerticalAlignment,\n ReadoutVariant,\n} from '../readout/readout.js';\n\nexport enum ObcGaugeRadialType {\n filled = 'filled',\n bar = 'bar',\n needle = 'needle',\n}\n\nexport enum GaugeRadialSector {\n deg270 = '270',\n deg180 = '180',\n deg90Left = '90-left',\n deg90Right = '90-right',\n}\n\nexport interface GaugeRadialAdvice {\n minValue: number;\n maxValue: number;\n type: AdviceType;\n hinted: boolean;\n}\n\n/**\n * `<obc-gauge-radial>` — Configurable radial gauge for generic numeric values.\n *\n * `ObcGaugeRadial` is a thin wrapper around `<obc-instrument-radial>` that adds\n * domain-independent value-to-angle mapping with automatic range handling for\n * both positive-only and bipolar (negative-to-positive) scales. It inherits a\n * full setpoint property bundle from {@link SetpointMixin}, including\n * auto at-setpoint detection, dual-marker adjustment preview, and deadband\n * tuning — no manual wiring required.\n *\n * ## Features\n *\n * - **Three display types**: `filled` (solid arc), `bar` (thinner arc), and\n * `needle` (pointer indicator) via the `type` property.\n * - **Sector sweep**: `sector` selects the arc span (`270`, `180`, `90-left`, or `90-right`).\n * The configured `minValue..maxValue` always spans the full sector. For the\n * centered sectors (`270`, `180`) a symmetric range places `0` at 12 o'clock;\n * for `90-left`/`90-right` the range midpoint sits at the middle of the quadrant.\n * - **Setpoint via mixin**: `setpoint`, `newSetpoint`, `touching`,\n * `autoAtSetpointDeadband`, `setpointOverride`, and all other setpoint\n * properties are provided by `SetpointMixin` and forwarded to the inner\n * `<obc-instrument-radial>`.\n * - **Advice zones**: Pass an array of {@link GaugeRadialAdvice} objects to\n * render caution/alert arcs on the gauge. Not shown on the `90-left` /\n * `90-right` sectors.\n *\n * ## Usage Guidelines\n *\n * - Set `minValue` / `maxValue` to define the scale range.\n * - Use `priority` to switch between regular and enhanced color palettes.\n * - Provide `primaryTickmarkInterval` and `secondaryTickmarkInterval` to\n * control tickmark density.\n * - Enable `showLabels` to show numeric labels at primary tickmarks.\n * - Enable `hasReadout` with optional `label` and `unit`. Layout depends on `sector`\n * and `type`: **270** filled/bar — value at center + a label-only `meta` row in\n * the bottom gap (two separate readouts); **180** filled/bar — a single centered\n * stack (value + label/unit) at the bottom; **270** needle — bottom stack;\n * **180** needle — no readout; **90-left** / **90-right** filled/bar — corner\n * readout in a square host; **90** needle — no readout.\n *\n * ## Best Practices\n *\n * - Prefer `SetpointMixin` properties (`setpoint`, `touching`, etc.) over\n * any legacy aliases — the mixin is the single source of truth.\n * - Keep domain-specific logic (units, formatting) in the parent view; this\n * component is intentionally unit-agnostic.\n *\n * ## Example\n *\n * ```html\n * <obc-gauge-radial\n * value=\"42\"\n * minValue=\"0\"\n * maxValue=\"100\"\n * type=\"filled\"\n * priority=\"enhanced\"\n * showLabels\n * primaryTickmarkInterval=\"25\"\n * secondaryTickmarkInterval=\"5\"\n * setpoint=\"60\"\n * ></obc-gauge-radial>\n * ```\n *\n * @element obc-gauge-radial\n * @typedef {import('./gauge-radial.js').GaugeRadialAdvice} GaugeRadialAdvice\n */\n@customElement('obc-gauge-radial')\nexport class ObcGaugeRadial extends SetpointMixin(LitElement) {\n @property({type: Number}) value = 0;\n @property({type: Number}) maxValue = 100;\n @property({type: Number}) minValue = 0;\n @property({type: Boolean}) showLabels: boolean = false;\n @property({type: Number}) primaryTickmarkInterval = 50;\n @property({type: Number}) secondaryTickmarkInterval = 10;\n /**\n * Interval for tertiary tickmarks in value units.\n * When undefined or <= 0, no tertiary tickmarks are shown.\n */\n @property({type: Number}) tertiaryTickmarkInterval: number | undefined =\n undefined;\n @property({type: String}) state: InstrumentState = InstrumentState.active;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: String}) type: ObcGaugeRadialType =\n ObcGaugeRadialType.filled;\n @property({type: Boolean}) tickmarksInside: boolean = false;\n @property({type: String}) tickmarkStyle: TickmarkStyle =\n TickmarkStyle.regular;\n /** Caution/alert arcs. Ignored on `sector: 90-left` / `90-right`. */\n @property({type: Array, attribute: false}) advices: GaugeRadialAdvice[] = [];\n @property({type: String, reflect: true}) sector: GaugeRadialSector =\n GaugeRadialSector.deg270;\n /**\n * When `true`, shows the centre `<obc-readout>`(s) with the current value\n * (and optional `label`/`unit`). Layout depends on `sector` and `type`.\n * Default `false`.\n */\n @property({type: Boolean}) hasReadout = false;\n @property({type: String}) label = '';\n @property({type: String}) unit = '';\n @property({type: Number}) fractionDigits = 0;\n\n private get sectorAngles(): {sweep: number; start: number} {\n switch (this.sector) {\n case GaugeRadialSector.deg180:\n return {sweep: 180, start: -90};\n case GaugeRadialSector.deg90Left:\n return {sweep: 90, start: -90};\n case GaugeRadialSector.deg90Right:\n return {sweep: 90, start: 0};\n case GaugeRadialSector.deg270:\n default:\n return {sweep: 270, start: -135};\n }\n }\n\n /**\n * Per-edge crop (%) of the shared, origin-centered 448 SVG box each sector\n * shows. All sectors render at the same natural scale, so the dial stays the\n * same size; the sector only windows a different part (270 whole, 180 wide,\n * 90 a quadrant).\n */\n private get sectorClips(): {\n top: number;\n bottom: number;\n left: number;\n right: number;\n } {\n switch (this.sector) {\n case GaugeRadialSector.deg180:\n return {top: 0, bottom: 44, left: 0, right: 0};\n case GaugeRadialSector.deg90Left:\n return {top: 0, bottom: 45, left: 0, right: 45};\n case GaugeRadialSector.deg90Right:\n return {top: 0, bottom: 45, left: 45, right: 0};\n case GaugeRadialSector.deg270:\n default:\n return {top: 0, bottom: 0, left: 0, right: 0};\n }\n }\n\n private get isSector90(): boolean {\n return (\n this.sector === GaugeRadialSector.deg90Left ||\n this.sector === GaugeRadialSector.deg90Right\n );\n }\n\n // Arrow form so `this` binds when passed as `.getAngle=${this.getAngle}`\n // to <obc-instrument-radial>. Do not convert to a method.\n getAngle = (v: number): number => {\n const {sweep, start} = this.sectorAngles;\n const span = this.maxValue - this.minValue;\n if (!Number.isFinite(span) || span <= 0) {\n return start;\n }\n\n return ((v - this.minValue) / span) * sweep + start;\n };\n\n /** Renders one gauge readout; `withMeta`/`labelOnly` pick parts. */\n private renderReadout({\n className,\n variant,\n alignment = ReadoutStackVerticalAlignment.vertical,\n withMeta = true,\n labelOnly = false,\n }: {\n className: string;\n variant: ReadoutVariant;\n alignment?: ReadoutStackVerticalAlignment;\n withMeta?: boolean;\n labelOnly?: boolean;\n }): TemplateResult {\n // `labelOnly` already means \"no value\", so derive it instead of passing both.\n const withValue = !labelOnly;\n return renderInstrumentReadout({\n className,\n variant,\n alignment,\n labelOnly,\n value: withValue ? this.value : undefined,\n valuePriority: withValue ? this.priority : undefined,\n fractionDigits: this.fractionDigits,\n label: withMeta ? this.label : '',\n unit: withMeta ? this.unit : '',\n });\n }\n\n private renderReadouts() {\n if (!this.hasReadout) {\n return nothing;\n }\n\n const isNeedle = this.type === ObcGaugeRadialType.needle;\n const is90 = this.isSector90;\n const is180 = this.sector === GaugeRadialSector.deg180;\n\n if (isNeedle && (is180 || is90)) {\n return nothing;\n }\n\n // 90 filled/bar: enhanced corner readout (bold value + label/unit).\n if (is90) {\n return this.renderReadout({\n className: 'gauge-readout-meta',\n variant: ReadoutVariant.enhanced,\n });\n }\n\n // 270 needle and 180 filled/bar: centered stack (value weight follows `priority`).\n if (isNeedle || is180) {\n return this.renderReadout({\n className: 'gauge-readout-meta',\n variant: ReadoutVariant.stack,\n alignment: ReadoutStackVerticalAlignment.center,\n });\n }\n\n // 270 filled/bar: the value sits at the circle center and the label/unit row\n // lower in the bottom gap, so they are two separately-positioned readouts.\n return html`\n ${this.renderReadout({\n className: 'gauge-readout-value',\n variant: ReadoutVariant.enhanced,\n withMeta: false,\n })}\n ${this.label || this.unit\n ? this.renderReadout({\n className: 'gauge-readout-meta',\n variant: ReadoutVariant.stack,\n alignment: ReadoutStackVerticalAlignment.center,\n labelOnly: true,\n })\n : nothing}\n `;\n }\n\n override render() {\n const clips = this.sectorClips;\n return html`\n <div\n class=${classMap({\n 'gauge-radial-root': true,\n 'type-needle': this.type === ObcGaugeRadialType.needle,\n 'sector-180': this.sector === GaugeRadialSector.deg180,\n 'sector-90-left': this.sector === GaugeRadialSector.deg90Left,\n 'sector-90-right': this.sector === GaugeRadialSector.deg90Right,\n })}\n >\n <obc-instrument-radial\n .value=${this.value}\n .state=${this.state}\n .priority=${this.priority}\n .setpoint=${this.setpoint}\n .newSetpoint=${this.newSetpoint}\n .setpointAtZeroDeadband=${this.setpointAtZeroDeadband}\n .setpointOverride=${this.setpointOverride}\n .touching=${this.touching}\n .autoAtSetpoint=${this.autoAtSetpoint}\n .autoAtSetpointDeadband=${this.autoAtSetpointDeadband}\n .animateSetpoint=${this.animateSetpoint}\n .maxValue=${this.maxValue}\n .minValue=${this.minValue}\n .getAngle=${this.getAngle}\n .showLabels=${this.showLabels}\n .primaryTickmarkInterval=${this.primaryTickmarkInterval}\n .secondaryTickmarkInterval=${this.secondaryTickmarkInterval}\n .tertiaryTickmarkInterval=${this.tertiaryTickmarkInterval}\n .type=${this.type}\n .needleType=${this.type}\n .tickmarksInside=${this.tickmarksInside}\n .tickmarkStyle=${this.tickmarkStyle}\n .advices=${this.isSector90 ? [] : this.advices}\n .clipTop=${clips.top}\n .clipBottom=${clips.bottom}\n .clipLeft=${clips.left}\n .clipRight=${clips.right}\n .endLabelsMaxMin=${this.sector === GaugeRadialSector.deg180}\n >\n </obc-instrument-radial>\n ${this.renderReadouts()}\n </div>\n `;\n }\n\n static override styles = unsafeCSS(componentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-gauge-radial': ObcGaugeRadial;\n }\n}\n"],"names":["ObcGaugeRadialType","GaugeRadialSector"],"mappings":"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"}
1
+ {"version":3,"file":"gauge-radial.js","sources":["../../../src/navigation-instruments/gauge-radial/gauge-radial.ts"],"sourcesContent":["import {LitElement, html, nothing, unsafeCSS, type TemplateResult} from 'lit';\nimport {classMap} from 'lit/directives/class-map.js';\nimport componentStyle from './gauge-radial.css?inline';\nimport {customElement} from '../../decorator.js';\nimport {property} from 'lit/decorators.js';\nimport {AdviceType} from '../watch/advice.js';\nimport {InstrumentState, Priority} from '../types.js';\nimport {SetpointMixin} from '../../svghelpers/setpoint-mixin.js';\nimport '../../building-blocks/instrument-radial/instrument-radial.js';\nimport {TickmarkStyle} from '../watch/tickmark.js';\nimport {renderInstrumentReadout} from '../readout/instrument-readout.js';\nimport {\n ReadoutStackVerticalAlignment,\n ReadoutVariant,\n} from '../readout/readout.js';\n\nexport enum ObcGaugeRadialType {\n filled = 'filled',\n bar = 'bar',\n needle = 'needle',\n}\n\nexport enum GaugeRadialSector {\n deg270 = '270',\n deg180 = '180',\n deg90Left = '90-left',\n deg90Right = '90-right',\n}\n\nexport enum GaugeRadialHorizontalAlignment {\n left = 'left',\n center = 'center',\n right = 'right',\n}\n\nexport enum GaugeRadialVerticalAlignment {\n top = 'top',\n center = 'center',\n bottom = 'bottom',\n}\n\nexport interface GaugeRadialAdvice {\n minValue: number;\n maxValue: number;\n type: AdviceType;\n hinted: boolean;\n}\n\n/**\n * `<obc-gauge-radial>` — Configurable radial gauge for generic numeric values.\n *\n * `ObcGaugeRadial` is a thin wrapper around `<obc-instrument-radial>` that adds\n * domain-independent value-to-angle mapping with automatic range handling for\n * both positive-only and bipolar (negative-to-positive) scales. It inherits a\n * full setpoint property bundle from {@link SetpointMixin}, including\n * auto at-setpoint detection, dual-marker adjustment preview, and deadband\n * tuning — no manual wiring required.\n *\n * ## Features\n *\n * - **Three display types**: `filled` (solid arc), `bar` (thinner arc), and\n * `needle` (pointer indicator) via the `type` property.\n * - **Sector sweep**: `sector` selects the arc span (`270`, `180`, `90-left`, or `90-right`).\n * The configured `minValue..maxValue` always spans the full sector. For the\n * centered sectors (`270`, `180`) a symmetric range places `0` at 12 o'clock;\n * for `90-left`/`90-right` the range midpoint sits at the middle of the quadrant.\n * - **Setpoint via mixin**: `setpoint`, `newSetpoint`, `touching`,\n * `autoAtSetpointDeadband`, `setpointOverride`, and all other setpoint\n * properties are provided by `SetpointMixin` and forwarded to the inner\n * `<obc-instrument-radial>`.\n * - **Advice zones**: Pass an array of {@link GaugeRadialAdvice} objects to\n * render caution/alert arcs on the gauge. Not shown on the `90-left` /\n * `90-right` sectors.\n * - **Alignment**: The host always fills its container and the dial shrinks to\n * fit the smaller of the available width/height, so a short, wide (or tall,\n * narrow) container leaves slack on one axis. `horizontalAlignment`\n * (`left` | `center` | `right`) and `verticalAlignment`\n * (`top` | `center` | `bottom`) position the dial within that slack; both\n * default to `center`.\n *\n * ## Usage Guidelines\n *\n * - Set `minValue` / `maxValue` to define the scale range.\n * - Use `priority` to switch between regular and enhanced color palettes.\n * - Provide `primaryTickmarkInterval` and `secondaryTickmarkInterval` to\n * control tickmark density.\n * - Enable `showLabels` to show numeric labels at primary tickmarks.\n * - Enable `hasReadout` with optional `label` and `unit`. Layout depends on `sector`\n * and `type`: **270** filled/bar — value at center + a label-only `meta` row in\n * the bottom gap (two separate readouts); **180** filled/bar — a single centered\n * stack (value + label/unit) at the bottom; **270** needle — bottom stack;\n * **180** needle — no readout; **90-left** / **90-right** filled/bar — corner\n * readout in a square host; **90** needle — no readout.\n *\n * ## Best Practices\n *\n * - Prefer `SetpointMixin` properties (`setpoint`, `touching`, etc.) over\n * any legacy aliases — the mixin is the single source of truth.\n * - Keep domain-specific logic (units, formatting) in the parent view; this\n * component is intentionally unit-agnostic.\n *\n * ## Example\n *\n * ```html\n * <obc-gauge-radial\n * value=\"42\"\n * minValue=\"0\"\n * maxValue=\"100\"\n * type=\"filled\"\n * priority=\"enhanced\"\n * showLabels\n * primaryTickmarkInterval=\"25\"\n * secondaryTickmarkInterval=\"5\"\n * setpoint=\"60\"\n * ></obc-gauge-radial>\n * ```\n *\n * @element obc-gauge-radial\n * @typedef {import('./gauge-radial.js').GaugeRadialAdvice} GaugeRadialAdvice\n */\n@customElement('obc-gauge-radial')\nexport class ObcGaugeRadial extends SetpointMixin(LitElement) {\n @property({type: Number}) value = 0;\n @property({type: Number}) maxValue = 100;\n @property({type: Number}) minValue = 0;\n @property({type: Boolean}) showLabels: boolean = false;\n @property({type: Number}) primaryTickmarkInterval = 50;\n @property({type: Number}) secondaryTickmarkInterval = 10;\n /**\n * Interval for tertiary tickmarks in value units.\n * When undefined or <= 0, no tertiary tickmarks are shown.\n */\n @property({type: Number}) tertiaryTickmarkInterval: number | undefined =\n undefined;\n @property({type: String}) state: InstrumentState = InstrumentState.active;\n @property({type: String}) priority: Priority = Priority.regular;\n @property({type: String}) type: ObcGaugeRadialType =\n ObcGaugeRadialType.filled;\n @property({type: Boolean}) tickmarksInside: boolean = false;\n @property({type: String}) tickmarkStyle: TickmarkStyle =\n TickmarkStyle.regular;\n /** Caution/alert arcs. Ignored on `sector: 90-left` / `90-right`. */\n @property({type: Array, attribute: false}) advices: GaugeRadialAdvice[] = [];\n @property({type: String, reflect: true}) sector: GaugeRadialSector =\n GaugeRadialSector.deg270;\n /**\n * Horizontal placement of the dial when the host is wider than the dial\n * (e.g. a short, wide container shrinks the dial to fit the height, leaving\n * horizontal slack). Default `center`.\n */\n @property({type: String})\n horizontalAlignment: GaugeRadialHorizontalAlignment =\n GaugeRadialHorizontalAlignment.center;\n /**\n * Vertical placement of the dial when the host is taller than the dial\n * (e.g. a tall, narrow container shrinks the dial to fit the width, leaving\n * vertical slack). Default `center`.\n */\n @property({type: String}) verticalAlignment: GaugeRadialVerticalAlignment =\n GaugeRadialVerticalAlignment.center;\n /**\n * When `true`, shows the centre `<obc-readout>`(s) with the current value\n * (and optional `label`/`unit`). Layout depends on `sector` and `type`.\n * Default `false`.\n */\n @property({type: Boolean}) hasReadout = false;\n @property({type: String}) label = '';\n @property({type: String}) unit = '';\n @property({type: Number}) fractionDigits = 0;\n\n private get sectorAngles(): {sweep: number; start: number} {\n switch (this.sector) {\n case GaugeRadialSector.deg180:\n return {sweep: 180, start: -90};\n case GaugeRadialSector.deg90Left:\n return {sweep: 90, start: -90};\n case GaugeRadialSector.deg90Right:\n return {sweep: 90, start: 0};\n case GaugeRadialSector.deg270:\n default:\n return {sweep: 270, start: -135};\n }\n }\n\n /**\n * Per-edge crop (%) of the shared, origin-centered 448 SVG box each sector\n * shows. All sectors render at the same natural scale, so the dial stays the\n * same size; the sector only windows a different part (270 whole, 180 wide,\n * 90 a quadrant).\n */\n private get sectorClips(): {\n top: number;\n bottom: number;\n left: number;\n right: number;\n } {\n switch (this.sector) {\n case GaugeRadialSector.deg180:\n return {top: 0, bottom: 44, left: 0, right: 0};\n case GaugeRadialSector.deg90Left:\n return {top: 0, bottom: 45, left: 0, right: 45};\n case GaugeRadialSector.deg90Right:\n return {top: 0, bottom: 45, left: 45, right: 0};\n case GaugeRadialSector.deg270:\n default:\n return {top: 0, bottom: 0, left: 0, right: 0};\n }\n }\n\n private get isSector90(): boolean {\n return (\n this.sector === GaugeRadialSector.deg90Left ||\n this.sector === GaugeRadialSector.deg90Right\n );\n }\n\n // Arrow form so `this` binds when passed as `.getAngle=${this.getAngle}`\n // to <obc-instrument-radial>. Do not convert to a method.\n getAngle = (v: number): number => {\n const {sweep, start} = this.sectorAngles;\n const span = this.maxValue - this.minValue;\n if (!Number.isFinite(span) || span <= 0) {\n return start;\n }\n\n return ((v - this.minValue) / span) * sweep + start;\n };\n\n /** Renders one gauge readout; `withMeta`/`labelOnly` pick parts. */\n private renderReadout({\n className,\n variant,\n alignment = ReadoutStackVerticalAlignment.vertical,\n withMeta = true,\n labelOnly = false,\n }: {\n className: string;\n variant: ReadoutVariant;\n alignment?: ReadoutStackVerticalAlignment;\n withMeta?: boolean;\n labelOnly?: boolean;\n }): TemplateResult {\n // `labelOnly` already means \"no value\", so derive it instead of passing both.\n const withValue = !labelOnly;\n return renderInstrumentReadout({\n className,\n variant,\n alignment,\n labelOnly,\n value: withValue ? this.value : undefined,\n valuePriority: withValue ? this.priority : undefined,\n fractionDigits: this.fractionDigits,\n label: withMeta ? this.label : '',\n unit: withMeta ? this.unit : '',\n });\n }\n\n private renderReadouts() {\n if (!this.hasReadout) {\n return nothing;\n }\n\n const isNeedle = this.type === ObcGaugeRadialType.needle;\n const is90 = this.isSector90;\n const is180 = this.sector === GaugeRadialSector.deg180;\n\n if (isNeedle && (is180 || is90)) {\n return nothing;\n }\n\n // 90 filled/bar: enhanced corner readout (bold value + label/unit).\n if (is90) {\n return this.renderReadout({\n className: 'gauge-readout-meta',\n variant: ReadoutVariant.enhanced,\n });\n }\n\n // 270 needle and 180 filled/bar: centered stack (value weight follows `priority`).\n if (isNeedle || is180) {\n return this.renderReadout({\n className: 'gauge-readout-meta',\n variant: ReadoutVariant.stack,\n alignment: ReadoutStackVerticalAlignment.center,\n });\n }\n\n // 270 filled/bar: the value sits at the circle center and the label/unit row\n // lower in the bottom gap, so they are two separately-positioned readouts.\n return html`\n ${this.renderReadout({\n className: 'gauge-readout-value',\n variant: ReadoutVariant.enhanced,\n withMeta: false,\n })}\n ${this.label || this.unit\n ? this.renderReadout({\n className: 'gauge-readout-meta',\n variant: ReadoutVariant.stack,\n alignment: ReadoutStackVerticalAlignment.center,\n labelOnly: true,\n })\n : nothing}\n `;\n }\n\n override render() {\n const clips = this.sectorClips;\n return html`\n <div\n class=${classMap({\n 'gauge-radial-root': true,\n 'type-needle': this.type === ObcGaugeRadialType.needle,\n 'sector-180': this.sector === GaugeRadialSector.deg180,\n 'sector-90-left': this.sector === GaugeRadialSector.deg90Left,\n 'sector-90-right': this.sector === GaugeRadialSector.deg90Right,\n [`halign-${this.horizontalAlignment}`]: true,\n [`valign-${this.verticalAlignment}`]: true,\n })}\n >\n <obc-instrument-radial\n .value=${this.value}\n .state=${this.state}\n .priority=${this.priority}\n .setpoint=${this.setpoint}\n .newSetpoint=${this.newSetpoint}\n .setpointAtZeroDeadband=${this.setpointAtZeroDeadband}\n .setpointOverride=${this.setpointOverride}\n .touching=${this.touching}\n .autoAtSetpoint=${this.autoAtSetpoint}\n .autoAtSetpointDeadband=${this.autoAtSetpointDeadband}\n .animateSetpoint=${this.animateSetpoint}\n .maxValue=${this.maxValue}\n .minValue=${this.minValue}\n .getAngle=${this.getAngle}\n .showLabels=${this.showLabels}\n .primaryTickmarkInterval=${this.primaryTickmarkInterval}\n .secondaryTickmarkInterval=${this.secondaryTickmarkInterval}\n .tertiaryTickmarkInterval=${this.tertiaryTickmarkInterval}\n .type=${this.type}\n .needleType=${this.type}\n .tickmarksInside=${this.tickmarksInside}\n .tickmarkStyle=${this.tickmarkStyle}\n .advices=${this.isSector90 ? [] : this.advices}\n .clipTop=${clips.top}\n .clipBottom=${clips.bottom}\n .clipLeft=${clips.left}\n .clipRight=${clips.right}\n .endLabelsMaxMin=${this.sector === GaugeRadialSector.deg180}\n >\n </obc-instrument-radial>\n ${this.renderReadouts()}\n </div>\n `;\n }\n\n static override styles = unsafeCSS(componentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-gauge-radial': ObcGaugeRadial;\n }\n}\n"],"names":["ObcGaugeRadialType","GaugeRadialSector","GaugeRadialHorizontalAlignment","GaugeRadialVerticalAlignment"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oicl/openbridge-webcomponents-full-bundle",
3
- "version": "2.0.0-next.86",
3
+ "version": "2.0.0-next.88",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -68,13 +68,13 @@ export enum TankOrientation {
68
68
  /**
69
69
  * Host positioning model.
70
70
  *
71
- * - `point` (default): the host has fixed default dimensions (per
71
+ * - `point`: the host has fixed default dimensions (per
72
72
  * orientation and compact / static variant) and a P&ID anchor — the
73
73
  * visual content is shifted with `translateX(-50%)` and (in non-compact)
74
74
  * `top: -20px` so the tank's top-center aligns with the host's top-left
75
75
  * placement coordinate. Use this when dropping the tank onto a P&ID
76
76
  * canvas at a pipe-grid coordinate.
77
- * - `button`: the host fills its parent container (100% × 100%) with no
77
+ * - `button` (default): the host fills its parent container (100% × 100%) with no
78
78
  * anchor offset. Use this when embedding the tank inside a sized layout
79
79
  * slot — the parent controls the footprint and the tank renders
80
80
  * responsively inside it, just like a regular button. Compact / static
@@ -4,45 +4,68 @@
4
4
  height: 100%;
5
5
  }
6
6
 
7
- /* Host takes each sector's crop aspect (see `sectorClips`): 270/90 square, 180
8
- wide so the dial is the same size at any width.
7
+ .gauge-radial-root {
8
+ /* Readout vertical placement per layout (% of the cropped box), tuned to the
9
+ watch geometry — re-check if the ring radii or the 448 box change. Which
10
+ layout uses which is in renderReadouts(). */
11
+ --readout-meta-top: 72%;
12
+ --readout-meta-top-needle: 63%;
13
+ --readout-meta-top-180: 60%;
14
+
15
+ position: relative;
9
16
 
10
- `width`/`height: auto` + `max-width`/`max-height: 100%` makes the host the
11
- largest box of the sector's aspect ratio that fits inside *both* the
12
- available width and height. Using `height: auto` alone only honours the
13
- width, so in a short, wide (e.g. flex) container the square would grow as
14
- tall as it is wide and overflow see issue #992. */
15
- :host([sector="270"]),
16
- :host([sector="90-left"]),
17
- :host([sector="90-right"]) {
17
+ /* The dial is the largest box of its sector's aspect ratio that fits inside
18
+ *both* the available width and height of the (full-size) host. `width: auto`
19
+ stretches the block to the host width, then `aspect-ratio` + `max-height`
20
+ transfer the constraint back, shrinking it to fit a short host too — see
21
+ issue #992. The host always fills its container, so the gauge can be
22
+ positioned within the leftover space via the alignment classes below. */
18
23
  width: auto;
19
24
  height: auto;
20
25
  max-width: 100%;
21
26
  max-height: 100%;
22
27
  aspect-ratio: 1;
23
- }
24
28
 
25
- /* 448 / 251 = the 180° crop's aspect (448 wide × 448·(1 44%) tall); keep in
26
- sync with `sectorClips` bottom=44 in gauge-radial.ts. */
27
- :host([sector="180"]) {
28
- width: auto;
29
- height: auto;
30
- max-width: 100%;
31
- max-height: 100%;
32
- aspect-ratio: 448 / 251;
33
- }
29
+ /* Defaults (centered both ways); overridden by the halign-* / valign-*
30
+ classes. Horizontal alignment uses auto margins (which reference the host
31
+ width); vertical alignment uses a relative `top` offset (which references
32
+ the definite host height) plus a self-sized `translateY`. */
33
+ margin-left: auto;
34
+ margin-right: auto;
35
+ top: 50%;
36
+ transform: translateY(-50%);
34
37
 
35
- .gauge-radial-root {
36
- /* Readout vertical placement per layout (% of the cropped host), tuned to the
37
- watch geometry — re-check if the ring radii or the 448 box change. Which
38
- layout uses which is in renderReadouts(). */
39
- --readout-meta-top: 72%;
40
- --readout-meta-top-needle: 63%;
41
- --readout-meta-top-180: 60%;
38
+ /* 448 / 251 = the 180° crop's aspect (448 wide × 448·(1 − 44%) tall); keep in
39
+ sync with `sectorClips` bottom=44 in gauge-radial.ts. */
40
+ &.sector-180 {
41
+ aspect-ratio: 448 / 251;
42
+ }
42
43
 
43
- position: relative;
44
- width: 100%;
45
- height: 100%;
44
+ &.halign-left {
45
+ margin-left: 0;
46
+ margin-right: auto;
47
+ }
48
+ &.halign-center {
49
+ margin-left: auto;
50
+ margin-right: auto;
51
+ }
52
+ &.halign-right {
53
+ margin-left: auto;
54
+ margin-right: 0;
55
+ }
56
+
57
+ &.valign-top {
58
+ top: 0;
59
+ transform: translateY(0);
60
+ }
61
+ &.valign-center {
62
+ top: 50%;
63
+ transform: translateY(-50%);
64
+ }
65
+ &.valign-bottom {
66
+ top: 100%;
67
+ transform: translateY(-100%);
68
+ }
46
69
 
47
70
  .gauge-readout-value {
48
71
  position: absolute;
@@ -1,6 +1,8 @@
1
1
  import type {Meta, StoryObj} from '@storybook/web-components-vite';
2
2
  import {
3
+ GaugeRadialHorizontalAlignment,
3
4
  GaugeRadialSector,
5
+ GaugeRadialVerticalAlignment,
4
6
  ObcGaugeRadial,
5
7
  ObcGaugeRadialType,
6
8
  } from './gauge-radial.js';
@@ -28,6 +30,14 @@ const meta = {
28
30
  height: {control: {type: 'range', min: 100, max: 1000, step: 1}},
29
31
  type: {control: 'select', options: Object.values(ObcGaugeRadialType)},
30
32
  sector: {control: 'select', options: Object.values(GaugeRadialSector)},
33
+ horizontalAlignment: {
34
+ control: 'select',
35
+ options: Object.values(GaugeRadialHorizontalAlignment),
36
+ },
37
+ verticalAlignment: {
38
+ control: 'select',
39
+ options: Object.values(GaugeRadialVerticalAlignment),
40
+ },
31
41
  priority: {control: 'select', options: Object.values(Priority)},
32
42
  state: {control: 'select', options: Object.values(InstrumentState)},
33
43
  tickmarkStyle: {
@@ -259,6 +269,46 @@ export const ShortWideContainer180: Story = {
259
269
  },
260
270
  };
261
271
 
272
+ export const ShortWideAlignedLeft: Story = {
273
+ args: {
274
+ ...sectorStoryArgs,
275
+ sector: GaugeRadialSector.deg270,
276
+ width: 600,
277
+ height: 160,
278
+ horizontalAlignment: GaugeRadialHorizontalAlignment.left,
279
+ },
280
+ };
281
+
282
+ export const ShortWideAlignedRight: Story = {
283
+ args: {
284
+ ...sectorStoryArgs,
285
+ sector: GaugeRadialSector.deg270,
286
+ width: 600,
287
+ height: 160,
288
+ horizontalAlignment: GaugeRadialHorizontalAlignment.right,
289
+ },
290
+ };
291
+
292
+ export const TallNarrowAlignedTop: Story = {
293
+ args: {
294
+ ...sectorStoryArgs,
295
+ sector: GaugeRadialSector.deg270,
296
+ width: 200,
297
+ height: 500,
298
+ verticalAlignment: GaugeRadialVerticalAlignment.top,
299
+ },
300
+ };
301
+
302
+ export const TallNarrowAlignedBottom: Story = {
303
+ args: {
304
+ ...sectorStoryArgs,
305
+ sector: GaugeRadialSector.deg270,
306
+ width: 200,
307
+ height: 500,
308
+ verticalAlignment: GaugeRadialVerticalAlignment.bottom,
309
+ },
310
+ };
311
+
262
312
  const readoutStoryArgs = {
263
313
  value: 123,
264
314
  minValue: 0,
@@ -27,6 +27,18 @@ export enum GaugeRadialSector {
27
27
  deg90Right = '90-right',
28
28
  }
29
29
 
30
+ export enum GaugeRadialHorizontalAlignment {
31
+ left = 'left',
32
+ center = 'center',
33
+ right = 'right',
34
+ }
35
+
36
+ export enum GaugeRadialVerticalAlignment {
37
+ top = 'top',
38
+ center = 'center',
39
+ bottom = 'bottom',
40
+ }
41
+
30
42
  export interface GaugeRadialAdvice {
31
43
  minValue: number;
32
44
  maxValue: number;
@@ -59,6 +71,12 @@ export interface GaugeRadialAdvice {
59
71
  * - **Advice zones**: Pass an array of {@link GaugeRadialAdvice} objects to
60
72
  * render caution/alert arcs on the gauge. Not shown on the `90-left` /
61
73
  * `90-right` sectors.
74
+ * - **Alignment**: The host always fills its container and the dial shrinks to
75
+ * fit the smaller of the available width/height, so a short, wide (or tall,
76
+ * narrow) container leaves slack on one axis. `horizontalAlignment`
77
+ * (`left` | `center` | `right`) and `verticalAlignment`
78
+ * (`top` | `center` | `bottom`) position the dial within that slack; both
79
+ * default to `center`.
62
80
  *
63
81
  * ## Usage Guidelines
64
82
  *
@@ -125,6 +143,21 @@ export class ObcGaugeRadial extends SetpointMixin(LitElement) {
125
143
  @property({type: Array, attribute: false}) advices: GaugeRadialAdvice[] = [];
126
144
  @property({type: String, reflect: true}) sector: GaugeRadialSector =
127
145
  GaugeRadialSector.deg270;
146
+ /**
147
+ * Horizontal placement of the dial when the host is wider than the dial
148
+ * (e.g. a short, wide container shrinks the dial to fit the height, leaving
149
+ * horizontal slack). Default `center`.
150
+ */
151
+ @property({type: String})
152
+ horizontalAlignment: GaugeRadialHorizontalAlignment =
153
+ GaugeRadialHorizontalAlignment.center;
154
+ /**
155
+ * Vertical placement of the dial when the host is taller than the dial
156
+ * (e.g. a tall, narrow container shrinks the dial to fit the width, leaving
157
+ * vertical slack). Default `center`.
158
+ */
159
+ @property({type: String}) verticalAlignment: GaugeRadialVerticalAlignment =
160
+ GaugeRadialVerticalAlignment.center;
128
161
  /**
129
162
  * When `true`, shows the centre `<obc-readout>`(s) with the current value
130
163
  * (and optional `label`/`unit`). Layout depends on `sector` and `type`.
@@ -281,6 +314,8 @@ export class ObcGaugeRadial extends SetpointMixin(LitElement) {
281
314
  'sector-180': this.sector === GaugeRadialSector.deg180,
282
315
  'sector-90-left': this.sector === GaugeRadialSector.deg90Left,
283
316
  'sector-90-right': this.sector === GaugeRadialSector.deg90Right,
317
+ [`halign-${this.horizontalAlignment}`]: true,
318
+ [`valign-${this.verticalAlignment}`]: true,
284
319
  })}
285
320
  >
286
321
  <obc-instrument-radial