diff --git a/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/analog-valve/analog-valve.stories.ts/alert-frame-with-icon-auto.png b/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/analog-valve/analog-valve.stories.ts/alert-frame-with-icon-auto.png new file mode 100644 index 000000000..cb212f9e1 Binary files /dev/null and b/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/analog-valve/analog-valve.stories.ts/alert-frame-with-icon-auto.png differ diff --git a/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/automation-button/automation-button.stories.ts/valve-alert-button-positioning-auto.png b/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/automation-button/automation-button.stories.ts/valve-alert-button-positioning-auto.png new file mode 100644 index 000000000..6b4d575ee Binary files /dev/null and b/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/automation-button/automation-button.stories.ts/valve-alert-button-positioning-auto.png differ diff --git a/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/automation-tank/automation-tank.stories.ts/with-alert-bottom-flip-auto.png b/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/automation-tank/automation-tank.stories.ts/with-alert-bottom-flip-auto.png new file mode 100644 index 000000000..553bce9ba Binary files /dev/null and b/packages/openbridge-webcomponents/__vis__/linux/__baselines__/automation/automation-tank/automation-tank.stories.ts/with-alert-bottom-flip-auto.png differ diff --git a/packages/openbridge-webcomponents/src/automation/analog-valve/analog-valve.stories.ts b/packages/openbridge-webcomponents/src/automation/analog-valve/analog-valve.stories.ts index d05d509c3..796731dac 100644 --- a/packages/openbridge-webcomponents/src/automation/analog-valve/analog-valve.stories.ts +++ b/packages/openbridge-webcomponents/src/automation/analog-valve/analog-valve.stories.ts @@ -1,10 +1,12 @@ import type {Meta, StoryObj} from '@storybook/web-vite'; +import {html} from 'lit'; import {ObcAnalogValve} from './analog-valve.js'; import {AutomationButtonReadoutPosition} from '../automation-button/automation-button.js'; import {AutomationButtonReadoutStackSize} from '../../components/automation-button-readout-stack/automation-button-readout-stack.js'; import './analog-valve.js'; import {crossDecorator} from '../../storybook-util.js'; import '../automation-badge/automation-badge.js'; +import '../../icons/icon-pressure.js'; import {argTypesAbstractAutomationButtonPassiveRound} from '../automation-button/abstract-automation-button-storybook-helpers.js'; import { AutomationButtonBadgeAlert, @@ -12,6 +14,7 @@ import { AutomationButtonBadgeControl, AutomationButtonBadgeInterlock, } from '../automation-button/abstract-automation-button.js'; +import {ObcAlertFrameType} from '../../components/alert-frame/alert-frame.js'; const meta: Meta = { title: 'Automation/Automation Devices/Analog Valve', @@ -60,3 +63,28 @@ export const WithBadges: Story = { badgeCommandLocked: AutomationButtonBadgeCommandLocked.CommandLocked, }, }; + +export const AlertFrameWithIcon: Story = { + args: { + open: true, + value: 20, + alert: true, + alertFrameType: ObcAlertFrameType.LargeSideFlip, + showAlertIcon: true, + }, + render: (args) => html` + + + + `, +}; diff --git a/packages/openbridge-webcomponents/src/automation/automation-button/abstract-automation-button.ts b/packages/openbridge-webcomponents/src/automation/automation-button/abstract-automation-button.ts index 61f41bf99..b86f41bdc 100644 --- a/packages/openbridge-webcomponents/src/automation/automation-button/abstract-automation-button.ts +++ b/packages/openbridge-webcomponents/src/automation/automation-button/abstract-automation-button.ts @@ -56,6 +56,38 @@ export enum AutomationButtonBadgeCommandLocked { CommandLocked = 'command-locked', } +/** + * Abstract base class for automation device buttons (e.g. valves, pumps, + * tanks). Subclasses provide the device symbol through the `icon` getter and + * its on/off state through `_on`, while this class renders the underlying + * `` and forwards the shared slots and properties. + * + * ### Alert frame slots + * + * When `alert` is enabled the button is wrapped in an ``. The + * alert frame can show a custom icon, label and timer, depending on the + * selected `alertFrameType`. These are exposed using the same slot names as + * `` and are forwarded all the way down to the alert + * frame, for every `positioning` value: + * + * | Slot Name | Renders When... | Purpose | + * |--------------|--------------------------------------------------------------|-------------------------------------------------------------------------| + * | alert-icon | `alert` and `showAlertIcon` and `alertFrameType` in [`large-side-flip`, `bottom-flip`, `top-flip`] | Custom icon shown in the alert frame flap, in addition to the alert category icon. | + * | alert-label | `alert` and `alertFrameType` in [`bottom-flip`, `top-flip`] | Label text shown in the alert frame flap. | + * | alert-timer | `alert` and `alertFrameType` in [`bottom-flip`, `top-flip`] | Timer / clock shown in the alert frame flap. | + * + * The slot content is remapped on its way down: `alert-icon` + * (`obc-automation-button`) -> `icon` (`obc-alert-frame`), and likewise for + * the label and timer slots. + * + * @slot alert-icon - Custom icon shown in the alert frame flap (requires `showAlertIcon` and a flap variant that supports an icon). + * @slot alert-label - Label text shown in the alert frame flap (`bottom-flip`/`top-flip`). + * @slot alert-timer - Timer / clock shown in the alert frame flap (`bottom-flip`/`top-flip`). + * @slot badge-top-right - Custom badge in the top-right corner (overrides `badgeAlert`). + * @slot badge-top-left - Custom badge in the top-left corner (overrides `badgeControl`). + * @slot badge-bottom-left - Custom badge in the bottom-left corner (overrides `badgeInterlock`). + * @slot badge-bottom-right - Custom badge in the bottom-right corner (overrides `badgeCommandLocked`). + */ export class ObcAbstractAutomationButton extends LitElement { @property({type: Boolean, attribute: false}) showReadoutStack: boolean = true; /** @availableWhen showReadoutStack==true */ @@ -262,6 +294,9 @@ export class ObcAbstractAutomationButton extends LitElement { .positioning=${this.positioning} > ${this.icon} + + + + + +
Label
+
00:45
+
`; + }, +}; + export const ValveBadges: Story = { render(args) { const readouts: AutomationButtonReadoutStack[] = []; diff --git a/packages/openbridge-webcomponents/src/automation/automation-button/automation-button.ts b/packages/openbridge-webcomponents/src/automation/automation-button/automation-button.ts index 2814631c5..0bb0ade0f 100644 --- a/packages/openbridge-webcomponents/src/automation/automation-button/automation-button.ts +++ b/packages/openbridge-webcomponents/src/automation/automation-button/automation-button.ts @@ -18,7 +18,6 @@ import { ObcAlertFrameThickness, ObcAlertFrameType, ObcAlertFrameMode, - wrapWithAlertFrame, } from '../../components/alert-frame/alert-frame.js'; import {AlertType} from '../../types.js'; import {customElement} from '../../decorator.js'; @@ -196,19 +195,7 @@ export class ObcAutomationButton extends LitElement { ` : nothing} ${this.alert && this.positioning === AutomationButtonPositioning.point - ? html` - - - - ` + ? this.renderAlertFrame(nothing, true) : nothing} `); @@ -218,19 +205,9 @@ export class ObcAutomationButton extends LitElement { if (this.positioning === AutomationButtonPositioning.point) { return html`
${content}
`; } - const innerContent = wrapWithAlertFrame( - this.alert - ? { - type: this.alertFrameType, - thickness: this.alertFrameThickness, - status: this.alertFrameStatus, - mode: this.alertFrameMode, - showIcon: this.showAlertIcon, - showAlertCategoryIcon: this.showAlertCategoryIcon, - } - : false, - content - ); + const innerContent = this.alert + ? this.renderAlertFrame(content, false) + : content; if (this.positioning === AutomationButtonPositioning.symbol) { return html`
` together with the `alert-icon`, + * `alert-label` and `alert-timer` slots so that slotted content reaches the + * frame regardless of the `positioning`. When `overlay` is true the frame is + * an absolutely-positioned overlay (used by `point` positioning) and + * `content` is empty; otherwise the frame wraps `content` (used by `symbol` + * and `button` positioning). + */ + private renderAlertFrame( + content: HTMLTemplateResult | typeof nothing, + overlay: boolean + ): HTMLTemplateResult { + return html` + + + + ${content} + `; + } + static override styles = unsafeCSS(compentStyle); private get effectiveVariant(): AutomationButtonVariant { diff --git a/packages/openbridge-webcomponents/src/automation/automation-tank/automation-tank.stories.ts b/packages/openbridge-webcomponents/src/automation/automation-tank/automation-tank.stories.ts index 661623b08..5f3a0041b 100644 --- a/packages/openbridge-webcomponents/src/automation/automation-tank/automation-tank.stories.ts +++ b/packages/openbridge-webcomponents/src/automation/automation-tank/automation-tank.stories.ts @@ -10,6 +10,8 @@ import { } from './automation-tank.js'; import './automation-tank.js'; import {html} from 'lit'; +import '../../icons/icon-tank.js'; +import '../../icons/icon-timer-google.js'; import {crossDecorator} from '../../storybook-util.js'; import {AdviceType} from '../../navigation-instruments/watch/advice.js'; import type {LinearAdvice} from '../../building-blocks/instrument-linear/advice.js'; @@ -484,6 +486,48 @@ export const WithAlertAlarm: Story = { `, }; +export const WithAlertBottomFlip: Story = { + args: { + type: TankType.atmospheric, + alert: true, + alertFrameStatus: ObcAlertFrameStatus.Alarm, + alertFrameType: ObcAlertFrameType.BottomFlip, + showAlertIcon: true, + }, + render: (args) => html` + + + Level alarm + + + `, +}; + /** * Warning-status alert on a horizontal atmospheric tank — demonstrates the * ring tracks the tank's host orientation correctly.