@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.151 → 2.0.0-next.152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/vitest.setup.ts +12 -1
- package/bundle/openbridge-webcomponents.bundle.js +989 -319
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +1514 -77
- package/dist/alert-severity.d.ts +12 -9
- package/dist/alert-severity.d.ts.map +1 -1
- package/dist/alert-severity.js +30 -38
- package/dist/alert-severity.js.map +1 -1
- package/dist/building-blocks/bar-horizontal/bar-horizontal.d.ts +6 -0
- package/dist/building-blocks/bar-horizontal/bar-horizontal.d.ts.map +1 -1
- package/dist/building-blocks/bar-horizontal/bar-horizontal.js +6 -1
- package/dist/building-blocks/bar-horizontal/bar-horizontal.js.map +1 -1
- package/dist/building-blocks/bar-vertical/bar-vertical.d.ts +6 -0
- package/dist/building-blocks/bar-vertical/bar-vertical.d.ts.map +1 -1
- package/dist/building-blocks/bar-vertical/bar-vertical.js +6 -1
- package/dist/building-blocks/bar-vertical/bar-vertical.js.map +1 -1
- package/dist/building-blocks/chart-line/chart-line-base.d.ts +129 -0
- package/dist/building-blocks/chart-line/chart-line-base.d.ts.map +1 -1
- package/dist/building-blocks/chart-line/chart-line-base.js +464 -79
- package/dist/building-blocks/chart-line/chart-line-base.js.map +1 -1
- package/dist/building-blocks/external-scale/external-scale.d.ts +11 -0
- package/dist/building-blocks/external-scale/external-scale.d.ts.map +1 -1
- package/dist/building-blocks/external-scale/external-scale.js +45 -12
- package/dist/building-blocks/external-scale/external-scale.js.map +1 -1
- package/dist/charthelpers/index.d.ts +1 -0
- package/dist/charthelpers/index.d.ts.map +1 -1
- package/dist/charthelpers/index.js +7 -1
- package/dist/charthelpers/index.js.map +1 -1
- package/dist/charthelpers/range-labels.d.ts +55 -0
- package/dist/charthelpers/range-labels.d.ts.map +1 -0
- package/dist/charthelpers/range-labels.js +36 -0
- package/dist/charthelpers/range-labels.js.map +1 -0
- package/dist/components/alert-button/alert-button.css.js +13 -21
- package/dist/components/alert-button/alert-button.css.js.map +1 -1
- package/dist/components/alert-button/alert-button.d.ts +9 -4
- package/dist/components/alert-button/alert-button.d.ts.map +1 -1
- package/dist/components/alert-button/alert-button.js +24 -15
- package/dist/components/alert-button/alert-button.js.map +1 -1
- package/dist/components/alert-frame/alert-frame.css.js +109 -33
- package/dist/components/alert-frame/alert-frame.css.js.map +1 -1
- package/dist/components/alert-frame/alert-frame.d.ts +34 -7
- package/dist/components/alert-frame/alert-frame.d.ts.map +1 -1
- package/dist/components/alert-frame/alert-frame.js +134 -30
- package/dist/components/alert-frame/alert-frame.js.map +1 -1
- package/dist/components/alert-icon/alert-icon.d.ts +10 -7
- package/dist/components/alert-icon/alert-icon.d.ts.map +1 -1
- package/dist/components/alert-icon/alert-icon.js +37 -60
- package/dist/components/alert-icon/alert-icon.js.map +1 -1
- package/dist/components/alert-menu-item/alert-menu-item.css.js +1 -4
- package/dist/components/alert-menu-item/alert-menu-item.css.js.map +1 -1
- package/dist/components/date-item/date-item.css.js +3 -3
- package/dist/components/keyboard-full/keyboard-full.css.js +4 -4
- package/dist/components/message-menu-item/message-menu-item.css.js +2 -6
- package/dist/components/message-menu-item/message-menu-item.css.js.map +1 -1
- package/dist/components/sequence-toolbar/sequence-toolbar.css.js +1 -2
- package/dist/components/sequence-toolbar/sequence-toolbar.css.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/navigation-instruments/gauge-trend/gauge-trend.d.ts +6 -0
- package/dist/navigation-instruments/gauge-trend/gauge-trend.d.ts.map +1 -1
- package/dist/navigation-instruments/gauge-trend/gauge-trend.js +8 -0
- package/dist/navigation-instruments/gauge-trend/gauge-trend.js.map +1 -1
- package/dist/navigation-instruments/instrument-field/instrument-field.css.js +2 -2
- package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
- package/dist/navigation-instruments/readout/readout.js +2 -1
- package/dist/navigation-instruments/readout/readout.js.map +1 -1
- 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 +2 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
- package/dist/openbridge.css +19 -4
- package/dist/palettes/blinking.d.ts +14 -4
- package/dist/palettes/blinking.d.ts.map +1 -1
- package/dist/palettes/blinking.js +41 -35
- package/dist/palettes/blinking.js.map +1 -1
- package/dist/palettes/flashing-controller.d.ts +21 -0
- package/dist/palettes/flashing-controller.d.ts.map +1 -0
- package/dist/palettes/flashing-controller.js +45 -0
- package/dist/palettes/flashing-controller.js.map +1 -0
- package/dist/svghelpers/index.d.ts +1 -0
- package/dist/svghelpers/index.d.ts.map +1 -1
- package/dist/svghelpers/index.js +2 -0
- package/dist/svghelpers/index.js.map +1 -1
- package/dist/svghelpers/rounded-rect.d.ts +15 -0
- package/dist/svghelpers/rounded-rect.d.ts.map +1 -0
- package/dist/svghelpers/rounded-rect.js +29 -0
- package/dist/svghelpers/rounded-rect.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/script/check-css-variables.ts +2 -15
- package/src/alert-severity.spec.ts +56 -0
- package/src/alert-severity.ts +45 -43
- package/src/bars-graphs/area-graph/area-graph.stories.ts +235 -1
- package/src/bars-graphs/line-graph/line-graph.stories.ts +24 -0
- package/src/building-blocks/bar-horizontal/bar-horizontal.ts +7 -0
- package/src/building-blocks/bar-vertical/bar-vertical.ts +7 -0
- package/src/building-blocks/chart-line/chart-line-base.spec.ts +767 -0
- package/src/building-blocks/chart-line/chart-line-base.stories.ts +124 -0
- package/src/building-blocks/chart-line/chart-line-base.ts +671 -97
- package/src/building-blocks/chart-line/chart-sizing-battleground.stories.ts +17 -0
- package/src/building-blocks/external-scale/external-scale.spec.ts +154 -0
- package/src/building-blocks/external-scale/external-scale.ts +78 -20
- package/src/charthelpers/index.ts +1 -0
- package/src/charthelpers/range-labels.spec.ts +46 -0
- package/src/charthelpers/range-labels.ts +93 -0
- package/src/components/alert-button/alert-button.css +10 -23
- package/src/components/alert-button/alert-button.spec.ts +56 -15
- package/src/components/alert-button/alert-button.stories.ts +5 -1
- package/src/components/alert-button/alert-button.ts +33 -24
- package/src/components/alert-frame/alert-flash-scenario.stories.ts +669 -0
- package/src/components/alert-frame/alert-frame.css +106 -33
- package/src/components/alert-frame/alert-frame.spec.ts +176 -17
- package/src/components/alert-frame/alert-frame.stories.ts +61 -0
- package/src/components/alert-frame/alert-frame.ts +179 -41
- package/src/components/alert-icon/alert-icon.spec.ts +82 -27
- package/src/components/alert-icon/alert-icon.stories.ts +7 -1
- package/src/components/alert-icon/alert-icon.ts +48 -74
- package/src/components/alert-menu-item/alert-menu-item.css +1 -4
- package/src/components/date-item/date-item.css +3 -3
- package/src/components/keyboard-full/keyboard-full.css +4 -4
- package/src/components/message-menu-item/message-menu-item.css +2 -6
- package/src/components/sequence-toolbar/sequence-toolbar.css +1 -2
- package/src/navigation-instruments/gauge-trend/gauge-trend.stories.ts +29 -0
- package/src/navigation-instruments/gauge-trend/gauge-trend.ts +11 -0
- package/src/navigation-instruments/instrument-field/instrument-field.css +2 -2
- package/src/navigation-instruments/readout/readout.ts +2 -1
- package/src/navigation-instruments/readout-list-item/readout-list-item.ts +2 -1
- package/src/palettes/blinking.spec.ts +83 -0
- package/src/palettes/blinking.ts +47 -35
- package/src/palettes/flashing-controller.ts +62 -0
- package/src/palettes/manual.css +18 -4
- package/src/svghelpers/index.ts +1 -0
- package/src/svghelpers/rounded-rect.spec.ts +47 -0
- package/src/svghelpers/rounded-rect.ts +42 -0
- package/src/types.ts +20 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
TemplateResult,
|
|
7
7
|
HTMLTemplateResult,
|
|
8
8
|
} from 'lit';
|
|
9
|
-
import {property} from 'lit/decorators.js';
|
|
9
|
+
import {property, query, state} from 'lit/decorators.js';
|
|
10
10
|
import compentStyle from './alert-frame.css?inline';
|
|
11
11
|
import {classMap} from 'lit/directives/class-map.js';
|
|
12
12
|
import '../../icons/icon-alarm-badge.js';
|
|
@@ -15,12 +15,21 @@ import '../../icons/icon-caution-badge.js';
|
|
|
15
15
|
import '../../icons/icon-critical-badge.js';
|
|
16
16
|
import './diagnostic-badge.js';
|
|
17
17
|
import {customElement} from '../../decorator.js';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
AlertType,
|
|
20
|
+
FlashingSpeed,
|
|
21
|
+
type ResolvedFlashingSpeed,
|
|
22
|
+
} from '../../types.js';
|
|
19
23
|
import {
|
|
20
24
|
getAlertBadgeComponent,
|
|
21
25
|
AlertBadgeComponent,
|
|
22
26
|
} from '../../alert-severity.js';
|
|
23
|
-
import {
|
|
27
|
+
import {FlashingController} from '../../palettes/flashing-controller.js';
|
|
28
|
+
import {
|
|
29
|
+
roundedRectPath,
|
|
30
|
+
type RoundedRect,
|
|
31
|
+
} from '../../svghelpers/rounded-rect.js';
|
|
32
|
+
import {AlertFlashPhase, resolveFlashingSpeed} from '../../alert-severity.js';
|
|
24
33
|
|
|
25
34
|
export {AlertType as ObcAlertFrameStatus} from '../../types.js';
|
|
26
35
|
|
|
@@ -56,6 +65,17 @@ export enum ObcAlertFrameMode {
|
|
|
56
65
|
unackedRectified = 'unacked-rectified',
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
/**
|
|
69
|
+
* How the frame renders the on phase of a flash: `outline` grows the outline
|
|
70
|
+
* outward by 2 px in one step (the design); `outline-eased` grows it the same
|
|
71
|
+
* way over a 50 ms transition. The eased variant exists for design evaluation
|
|
72
|
+
* and may be removed (#1224).
|
|
73
|
+
*/
|
|
74
|
+
export enum ObcAlertFrameFlashEffect {
|
|
75
|
+
Outline = 'outline',
|
|
76
|
+
OutlineEased = 'outline-eased',
|
|
77
|
+
}
|
|
78
|
+
|
|
59
79
|
/**
|
|
60
80
|
* Text size options for flip-flap typography.
|
|
61
81
|
* - `regular`: Standard text size (default).
|
|
@@ -66,11 +86,33 @@ export enum AlertFrameTextSize {
|
|
|
66
86
|
Large = 'large',
|
|
67
87
|
}
|
|
68
88
|
|
|
89
|
+
/** Wrapper geometry the dashed overlay is drawn from; measured, never derived from props. */
|
|
90
|
+
interface DashBox {
|
|
91
|
+
width: number;
|
|
92
|
+
height: number;
|
|
93
|
+
thickness: number;
|
|
94
|
+
radii: RoundedRect['radii'];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Room the svg leaves around the box for the stroke's on-phase growth. */
|
|
98
|
+
const DASH_FLASH_GROWTH_PX = 2;
|
|
99
|
+
|
|
100
|
+
function sameDashBox(a: DashBox, b: DashBox | undefined): boolean {
|
|
101
|
+
return (
|
|
102
|
+
b !== undefined &&
|
|
103
|
+
a.width === b.width &&
|
|
104
|
+
a.height === b.height &&
|
|
105
|
+
a.thickness === b.thickness &&
|
|
106
|
+
a.radii.every((r, i) => r === b.radii[i])
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
69
110
|
export interface AlertFrameConfig {
|
|
70
111
|
type?: ObcAlertFrameType;
|
|
71
112
|
thickness?: ObcAlertFrameThickness;
|
|
72
113
|
status?: AlertType;
|
|
73
114
|
mode?: ObcAlertFrameMode;
|
|
115
|
+
flashingSpeed?: FlashingSpeed;
|
|
74
116
|
textSize?: AlertFrameTextSize;
|
|
75
117
|
showIcon?: boolean;
|
|
76
118
|
showAlertCategoryIcon?: boolean;
|
|
@@ -89,7 +131,7 @@ export interface AlertFrameConfig {
|
|
|
89
131
|
* - `bottom-flip`: Adds a bottom flap with a status icon, label, and timer slots.
|
|
90
132
|
* - **Thickness options:** Choose between `small` (thin border) and `large` (thick border) for visual emphasis.
|
|
91
133
|
* - **Status indication:** Displays different color schemes and icons for the legacy statuses (`alarm`, `warning`, `caution`) and the level statuses (`level-critical`, `level-high`, `level-medium`, `level-low`, `level-diagnostic`).
|
|
92
|
-
* - **Acknowledgement mode:** The `mode` property reflects the alert lifecycle state — `acked-active` (default), `unacked-active
|
|
134
|
+
* - **Acknowledgement mode:** The `mode` property reflects the alert lifecycle state — `acked-active` (default, steady), `unacked-active` (flashes) and `unacked-rectified` (dashed, flashes) — and `flashingSpeed` picks the tempo.
|
|
93
135
|
* - **Content wrapping:** When `wrapContent` is true, the frame wraps and sizes itself to its slotted content rather than overlaying a fixed region.
|
|
94
136
|
* - **Customizable corners:** Each corner can be set to a sharp (non-rounded) edge for integration with other UI elements.
|
|
95
137
|
* - **Slot-based content:** Supports custom icons, labels, and timers in flap variants via named slots.
|
|
@@ -156,6 +198,12 @@ export interface AlertFrameConfig {
|
|
|
156
198
|
* @property sharpEdgeTopRight - If true, the top-right corner will be sharp (not rounded).
|
|
157
199
|
* @property sharpEdgeBottomLeft - If true, the bottom-left corner will be sharp (not rounded).
|
|
158
200
|
* @property sharpEdgeBottomRight - If true, the bottom-right corner will be sharp (not rounded).
|
|
201
|
+
* @property flashingSpeed - Flash tempo: `default` resolves from the alert type and mode
|
|
202
|
+
* (critical/alarm/high fast, warning/medium slow, low very slow, every rectified alert very
|
|
203
|
+
* slow, caution and diagnostic fixed), `fast`, `slow`, `very-slow` force a tempo, `fixed`
|
|
204
|
+
* never flashes. Acknowledged frames are always steady.
|
|
205
|
+
* @property flashEffect - `outline` (default) grows the outline by 2 px while on in one step;
|
|
206
|
+
* `outline-eased` grows it over a 50 ms transition, a design-evaluation option.
|
|
159
207
|
* @slot - Default slot for main alert content.
|
|
160
208
|
* @slot icon - Custom icon for the flap (large-side-flip, bottom-flip).
|
|
161
209
|
* @slot label - Label text for the bottom flap (bottom-flip only).
|
|
@@ -202,6 +250,129 @@ export class ObcAlertFrame extends LitElement {
|
|
|
202
250
|
@property({type: String}) mode: ObcAlertFrameMode =
|
|
203
251
|
ObcAlertFrameMode.ackedActive;
|
|
204
252
|
|
|
253
|
+
@property({type: String}) flashingSpeed: FlashingSpeed =
|
|
254
|
+
FlashingSpeed.Default;
|
|
255
|
+
|
|
256
|
+
@property({type: String}) flashEffect: ObcAlertFrameFlashEffect =
|
|
257
|
+
ObcAlertFrameFlashEffect.Outline;
|
|
258
|
+
|
|
259
|
+
protected readonly flashing = new FlashingController(
|
|
260
|
+
this,
|
|
261
|
+
() => this.resolvedFlashingSpeed
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
@state() private dashBox?: DashBox;
|
|
265
|
+
|
|
266
|
+
@query('.wrapper') private wrapper?: HTMLElement;
|
|
267
|
+
|
|
268
|
+
private dashObserver?: ResizeObserver;
|
|
269
|
+
|
|
270
|
+
private measureDash = (): void => {
|
|
271
|
+
const wrapper = this.wrapper;
|
|
272
|
+
if (!wrapper) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const style = getComputedStyle(wrapper);
|
|
276
|
+
const px = (value: string) => parseFloat(value) || 0;
|
|
277
|
+
const box: DashBox = {
|
|
278
|
+
width: wrapper.offsetWidth,
|
|
279
|
+
height: wrapper.offsetHeight,
|
|
280
|
+
thickness: px(style.getPropertyValue('--thickness')),
|
|
281
|
+
radii: [
|
|
282
|
+
px(style.borderTopLeftRadius),
|
|
283
|
+
px(style.borderTopRightRadius),
|
|
284
|
+
px(style.borderBottomRightRadius),
|
|
285
|
+
px(style.borderBottomLeftRadius),
|
|
286
|
+
],
|
|
287
|
+
};
|
|
288
|
+
// Setting state from updated() re-renders; only do it for a real change.
|
|
289
|
+
if (!sameDashBox(box, this.dashBox)) {
|
|
290
|
+
this.dashBox = box;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
override connectedCallback() {
|
|
295
|
+
super.connectedCallback();
|
|
296
|
+
if (this.hasUpdated) {
|
|
297
|
+
this.requestUpdate();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
override updated() {
|
|
302
|
+
const wantsDash =
|
|
303
|
+
this.mode === ObcAlertFrameMode.unackedRectified && this.isConnected;
|
|
304
|
+
if (wantsDash && !this.dashObserver && this.wrapper) {
|
|
305
|
+
this.dashObserver = new ResizeObserver(this.measureDash);
|
|
306
|
+
this.dashObserver.observe(this.wrapper);
|
|
307
|
+
}
|
|
308
|
+
if (wantsDash) {
|
|
309
|
+
// thickness and sharpEdge* change the radii without a resize.
|
|
310
|
+
this.measureDash();
|
|
311
|
+
}
|
|
312
|
+
if (!wantsDash && this.dashObserver) {
|
|
313
|
+
this.dashObserver.disconnect();
|
|
314
|
+
this.dashObserver = undefined;
|
|
315
|
+
this.dashBox = undefined;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
override disconnectedCallback() {
|
|
320
|
+
super.disconnectedCallback();
|
|
321
|
+
this.dashObserver?.disconnect();
|
|
322
|
+
this.dashObserver = undefined;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private renderDash(): TemplateResult | typeof nothing {
|
|
326
|
+
const box = this.dashBox;
|
|
327
|
+
if (this.mode !== ObcAlertFrameMode.unackedRectified || !box) {
|
|
328
|
+
return nothing;
|
|
329
|
+
}
|
|
330
|
+
const pad = box.thickness + DASH_FLASH_GROWTH_PX;
|
|
331
|
+
// A second, wider path for the on phase would have longer corner arcs
|
|
332
|
+
// and its dashes would drift around the frame.
|
|
333
|
+
const centreline: RoundedRect = {
|
|
334
|
+
x: pad - box.thickness / 2,
|
|
335
|
+
y: pad - box.thickness / 2,
|
|
336
|
+
width: box.width + box.thickness,
|
|
337
|
+
height: box.height + box.thickness,
|
|
338
|
+
// A radius of 0 is a sharp edge and must stay square.
|
|
339
|
+
radii: box.radii.map((r) =>
|
|
340
|
+
r > 0 ? r + box.thickness / 2 : 0
|
|
341
|
+
) as RoundedRect['radii'],
|
|
342
|
+
};
|
|
343
|
+
const width = box.width + 2 * pad;
|
|
344
|
+
const height = box.height + 2 * pad;
|
|
345
|
+
return html`<svg
|
|
346
|
+
class="dash"
|
|
347
|
+
aria-hidden="true"
|
|
348
|
+
width=${width}
|
|
349
|
+
height=${height}
|
|
350
|
+
viewBox="0 0 ${width} ${height}"
|
|
351
|
+
style="--dash-pad: ${pad}px"
|
|
352
|
+
>
|
|
353
|
+
<path d=${roundedRectPath(centreline)}></path>
|
|
354
|
+
</svg>`;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
get resolvedFlashingSpeed(): ResolvedFlashingSpeed {
|
|
358
|
+
switch (this.mode) {
|
|
359
|
+
case ObcAlertFrameMode.unackedActive:
|
|
360
|
+
return resolveFlashingSpeed(
|
|
361
|
+
this.flashingSpeed,
|
|
362
|
+
this.status,
|
|
363
|
+
AlertFlashPhase.Active
|
|
364
|
+
);
|
|
365
|
+
case ObcAlertFrameMode.unackedRectified:
|
|
366
|
+
return resolveFlashingSpeed(
|
|
367
|
+
this.flashingSpeed,
|
|
368
|
+
this.status,
|
|
369
|
+
AlertFlashPhase.Rectified
|
|
370
|
+
);
|
|
371
|
+
default:
|
|
372
|
+
return FlashingSpeed.Fixed;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
205
376
|
@property({type: Boolean, reflect: true}) wrapContent: boolean = false;
|
|
206
377
|
|
|
207
378
|
@property({type: Boolean, reflect: true}) fullWidth: boolean = false;
|
|
@@ -237,50 +408,16 @@ export class ObcAlertFrame extends LitElement {
|
|
|
237
408
|
'sharp-edge-bottom-left': this.sharpEdgeBottomLeft,
|
|
238
409
|
'sharp-edge-bottom-right': this.sharpEdgeBottomRight,
|
|
239
410
|
[this.mode]: true,
|
|
411
|
+
['flash-' + this.resolvedFlashingSpeed]: true,
|
|
412
|
+
['flash-effect-' + this.flashEffect]: true,
|
|
240
413
|
})}
|
|
241
414
|
>
|
|
242
415
|
<slot></slot>
|
|
243
|
-
${this.flap()}
|
|
416
|
+
${this.renderDash()} ${this.flap()}
|
|
244
417
|
</div>
|
|
245
418
|
`;
|
|
246
419
|
}
|
|
247
420
|
|
|
248
|
-
private _blinkAnimationCancel?: () => void;
|
|
249
|
-
|
|
250
|
-
private syncBlinking() {
|
|
251
|
-
if (
|
|
252
|
-
this.mode !== ObcAlertFrameMode.unackedActive &&
|
|
253
|
-
this._blinkAnimationCancel
|
|
254
|
-
) {
|
|
255
|
-
this._blinkAnimationCancel();
|
|
256
|
-
this._blinkAnimationCancel = undefined;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (
|
|
260
|
-
this.mode === ObcAlertFrameMode.unackedActive &&
|
|
261
|
-
!this._blinkAnimationCancel
|
|
262
|
-
) {
|
|
263
|
-
this._blinkAnimationCancel = blinkingAll(this);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
override connectedCallback() {
|
|
268
|
-
super.connectedCallback();
|
|
269
|
-
if (this.hasUpdated) {
|
|
270
|
-
this.syncBlinking();
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
override updated() {
|
|
275
|
-
this.syncBlinking();
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
override disconnectedCallback() {
|
|
279
|
-
super.disconnectedCallback();
|
|
280
|
-
this._blinkAnimationCancel?.();
|
|
281
|
-
this._blinkAnimationCancel = undefined;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
421
|
private flap() {
|
|
285
422
|
if (this.type === ObcAlertFrameType.Regular) {
|
|
286
423
|
return nothing;
|
|
@@ -383,6 +520,7 @@ export function wrapWithAlertFrame(
|
|
|
383
520
|
.thickness=${options.thickness ?? ObcAlertFrameThickness.Small}
|
|
384
521
|
.status=${options.status ?? AlertType.Alarm}
|
|
385
522
|
.mode=${options.mode ?? ObcAlertFrameMode.ackedActive}
|
|
523
|
+
.flashingSpeed=${options.flashingSpeed ?? FlashingSpeed.Default}
|
|
386
524
|
.showIcon=${options.showIcon ?? false}
|
|
387
525
|
.showAlertCategoryIcon=${options.showAlertCategoryIcon ?? true}
|
|
388
526
|
.wrapContent=${true}
|
|
@@ -3,74 +3,129 @@ import './alert-icon.js';
|
|
|
3
3
|
import {ObcAlertIcon} from './alert-icon.js';
|
|
4
4
|
import {render} from 'vitest-browser-lit';
|
|
5
5
|
import {html} from 'lit';
|
|
6
|
-
import {AlertType} from '../../types.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import {AlertType, FlashingSpeed} from '../../types.js';
|
|
7
|
+
|
|
8
|
+
function durations(el: HTMLElement): number[] {
|
|
9
|
+
return el
|
|
10
|
+
.getAnimations()
|
|
11
|
+
.map((a) => (a.effect as KeyframeEffect).getTiming().duration as number);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('obc-alert-icon flashing lifecycle', () => {
|
|
15
|
+
async function setup(
|
|
16
|
+
props: Partial<
|
|
17
|
+
Pick<
|
|
18
|
+
ObcAlertIcon,
|
|
19
|
+
'alertType' | 'acknowledged' | 'active' | 'flashingSpeed'
|
|
20
|
+
>
|
|
21
|
+
> = {}
|
|
22
|
+
) {
|
|
10
23
|
const screen = render(
|
|
11
24
|
html`<obc-alert-icon
|
|
12
|
-
.alertType=${AlertType.Alarm}
|
|
13
|
-
.acknowledged=${acknowledged}
|
|
25
|
+
.alertType=${props.alertType ?? AlertType.Alarm}
|
|
26
|
+
.acknowledged=${props.acknowledged ?? false}
|
|
27
|
+
.active=${props.active}
|
|
28
|
+
.flashingSpeed=${props.flashingSpeed ?? FlashingSpeed.Default}
|
|
14
29
|
></obc-alert-icon>`
|
|
15
30
|
);
|
|
16
|
-
const el = screen.
|
|
17
|
-
'obc-alert-icon'
|
|
18
|
-
) as ObcAlertIcon;
|
|
31
|
+
const el = screen.container.querySelector('obc-alert-icon') as ObcAlertIcon;
|
|
19
32
|
await el.updateComplete;
|
|
20
33
|
return el;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
|
-
|
|
24
|
-
return el.getAnimations();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
it('blinks for an unacknowledged blinking alert type', async () => {
|
|
36
|
+
it('flashes fast for an unacknowledged alarm', async () => {
|
|
28
37
|
const el = await setup();
|
|
29
38
|
|
|
30
|
-
expect(
|
|
39
|
+
expect(durations(el)).toEqual([800]);
|
|
40
|
+
expect(el.shadowRoot!.querySelector('.wrapper.flash-fast')).not.toBeNull();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('flashes slow for a warning and very slow for level-low', async () => {
|
|
44
|
+
expect(durations(await setup({alertType: AlertType.Warning}))).toEqual([
|
|
45
|
+
1600,
|
|
46
|
+
]);
|
|
47
|
+
expect(durations(await setup({alertType: AlertType.LevelLow}))).toEqual([
|
|
48
|
+
3200,
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('flashes very slow while rectified and unacknowledged', async () => {
|
|
53
|
+
expect(durations(await setup({active: false}))).toEqual([3200]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('never flashes caution or diagnostic', async () => {
|
|
57
|
+
expect(durations(await setup({alertType: AlertType.Caution}))).toEqual([]);
|
|
58
|
+
expect(
|
|
59
|
+
durations(await setup({alertType: AlertType.LevelDiagnostic}))
|
|
60
|
+
).toEqual([]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('stays static for caution and diagnostic even with an explicit speed', async () => {
|
|
64
|
+
for (const alertType of [AlertType.Caution, AlertType.LevelDiagnostic]) {
|
|
65
|
+
const el = await setup({alertType, flashingSpeed: FlashingSpeed.Fast});
|
|
66
|
+
expect(el.getAnimations()).toHaveLength(0);
|
|
67
|
+
expect(el.shadowRoot!.querySelector('.wrapper .a')).toBeNull();
|
|
68
|
+
expect(el.shadowRoot!.querySelector('.wrapper')!.children).toHaveLength(
|
|
69
|
+
1
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('honours an explicit flashingSpeed unless acknowledged', async () => {
|
|
75
|
+
expect(durations(await setup({flashingSpeed: FlashingSpeed.Slow}))).toEqual(
|
|
76
|
+
[1600]
|
|
77
|
+
);
|
|
78
|
+
expect(
|
|
79
|
+
durations(await setup({flashingSpeed: FlashingSpeed.Fixed}))
|
|
80
|
+
).toEqual([]);
|
|
81
|
+
expect(
|
|
82
|
+
durations(
|
|
83
|
+
await setup({flashingSpeed: FlashingSpeed.Fast, acknowledged: true})
|
|
84
|
+
)
|
|
85
|
+
).toEqual([]);
|
|
31
86
|
});
|
|
32
87
|
|
|
33
|
-
it('does not
|
|
34
|
-
const el = await setup(true);
|
|
88
|
+
it('does not flash once acknowledged', async () => {
|
|
89
|
+
const el = await setup({acknowledged: true});
|
|
35
90
|
|
|
36
|
-
expect(
|
|
91
|
+
expect(el.getAnimations()).toHaveLength(0);
|
|
37
92
|
});
|
|
38
93
|
|
|
39
|
-
it('stops
|
|
94
|
+
it('stops flashing when the alert is acknowledged', async () => {
|
|
40
95
|
const el = await setup();
|
|
41
|
-
expect(
|
|
96
|
+
expect(el.getAnimations().length).toBeGreaterThan(0);
|
|
42
97
|
|
|
43
98
|
el.acknowledged = true;
|
|
44
99
|
await el.updateComplete;
|
|
45
100
|
|
|
46
|
-
expect(
|
|
101
|
+
expect(el.getAnimations()).toHaveLength(0);
|
|
47
102
|
});
|
|
48
103
|
|
|
49
|
-
it('resumes
|
|
104
|
+
it('resumes flashing after disconnect and reconnect', async () => {
|
|
50
105
|
const el = await setup();
|
|
51
106
|
const parent = el.parentElement!;
|
|
52
|
-
const initial =
|
|
107
|
+
const initial = el.getAnimations().length;
|
|
53
108
|
expect(initial).toBeGreaterThan(0);
|
|
54
109
|
|
|
55
110
|
parent.removeChild(el);
|
|
56
|
-
expect(
|
|
111
|
+
expect(el.getAnimations()).toHaveLength(0);
|
|
57
112
|
|
|
58
113
|
// Reconnect without touching any property, so no Lit update is scheduled.
|
|
59
114
|
parent.appendChild(el);
|
|
60
115
|
await el.updateComplete;
|
|
61
116
|
|
|
62
|
-
expect(
|
|
117
|
+
expect(el.getAnimations()).toHaveLength(initial);
|
|
63
118
|
});
|
|
64
119
|
|
|
65
120
|
it('does not accumulate animations across repeated updates', async () => {
|
|
66
121
|
const el = await setup();
|
|
67
|
-
const initial =
|
|
122
|
+
const initial = el.getAnimations().length;
|
|
68
123
|
|
|
69
124
|
el.active = true;
|
|
70
125
|
await el.updateComplete;
|
|
71
126
|
el.silenced = true;
|
|
72
127
|
await el.updateComplete;
|
|
73
128
|
|
|
74
|
-
expect(
|
|
129
|
+
expect(el.getAnimations()).toHaveLength(initial);
|
|
75
130
|
});
|
|
76
131
|
});
|
|
@@ -2,7 +2,7 @@ import type {Meta, StoryObj} from '@storybook/web-components-vite';
|
|
|
2
2
|
import {ObcAlertIcon} from './alert-icon.js';
|
|
3
3
|
import './alert-icon.js';
|
|
4
4
|
import {html} from 'lit';
|
|
5
|
-
import {AlertType} from '../../types.js';
|
|
5
|
+
import {AlertType, FlashingSpeed} from '../../types.js';
|
|
6
6
|
|
|
7
7
|
const meta: Meta<typeof ObcAlertIcon> = {
|
|
8
8
|
title: 'Application Components/Alerts/Alert Icon',
|
|
@@ -13,8 +13,13 @@ const meta: Meta<typeof ObcAlertIcon> = {
|
|
|
13
13
|
acknowledged: false,
|
|
14
14
|
active: true,
|
|
15
15
|
outline: false,
|
|
16
|
+
flashingSpeed: FlashingSpeed.Default,
|
|
16
17
|
},
|
|
17
18
|
argTypes: {
|
|
19
|
+
flashingSpeed: {
|
|
20
|
+
options: Object.values(FlashingSpeed),
|
|
21
|
+
control: {type: 'select'},
|
|
22
|
+
},
|
|
18
23
|
type: {
|
|
19
24
|
control: {type: 'select', options: Object.values(AlertType)},
|
|
20
25
|
},
|
|
@@ -31,6 +36,7 @@ const meta: Meta<typeof ObcAlertIcon> = {
|
|
|
31
36
|
.type=${args.type}
|
|
32
37
|
.acknowledged=${args.acknowledged}
|
|
33
38
|
.active=${args.active}
|
|
39
|
+
.flashingSpeed=${args.flashingSpeed}
|
|
34
40
|
></obc-alert-icon>
|
|
35
41
|
</div>`,
|
|
36
42
|
} satisfies Meta<ObcAlertIcon>;
|
|
@@ -3,11 +3,15 @@ import {property} from 'lit/decorators.js';
|
|
|
3
3
|
import {classMap} from 'lit/directives/class-map.js';
|
|
4
4
|
import '../../icons/icon-caution-color-iec.js';
|
|
5
5
|
import {customElement} from '../../decorator.js';
|
|
6
|
-
import {AlertType} from '../../types.js';
|
|
7
6
|
import {
|
|
8
|
-
|
|
7
|
+
AlertType,
|
|
8
|
+
FlashingSpeed,
|
|
9
|
+
type ResolvedFlashingSpeed,
|
|
10
|
+
} from '../../types.js';
|
|
11
|
+
import {
|
|
12
|
+
AlertFlashPhase,
|
|
9
13
|
getBamAlertTypeForBlinking,
|
|
10
|
-
|
|
14
|
+
resolveFlashingSpeed,
|
|
11
15
|
} from '../../alert-severity.js';
|
|
12
16
|
import '../../icons/icon-alarm-badge-outline.js';
|
|
13
17
|
import '../../icons/icon-warning-badge-outline.js';
|
|
@@ -50,7 +54,7 @@ import {
|
|
|
50
54
|
import {lowSilencedA, lowSilencedB} from './icons/icon-low-silenced.js';
|
|
51
55
|
import {lowRectifiedA, lowRectifiedB} from './icons/icon-low-rectified.js';
|
|
52
56
|
import {lowAcknowledged} from './icons/icon-low-acknowledged.js';
|
|
53
|
-
import {
|
|
57
|
+
import {FlashingController} from '../../palettes/flashing-controller.js';
|
|
54
58
|
|
|
55
59
|
enum AlertIconState {
|
|
56
60
|
Silenced = 'silenced',
|
|
@@ -156,7 +160,7 @@ const mapping = {
|
|
|
156
160
|
*
|
|
157
161
|
* ## Features
|
|
158
162
|
* - **Multiple Icon Types:** Supports all alarm and warning types.
|
|
159
|
-
* - **
|
|
163
|
+
* - **Flashing:** Two SVG layers alternate at the design tempo (fast for critical/alarm/high, slow for warning/medium, very slow for low and for every rectified alert); acknowledged icons are steady.
|
|
160
164
|
* - **Adaptive Styling:** Applies different CSS variables for alarm and warning types to allow for distinct visual cues (e.g., color, blink timing).
|
|
161
165
|
* - **Scalable:** Designed to fit any container size; scales with its parent element.
|
|
162
166
|
*
|
|
@@ -179,6 +183,9 @@ const mapping = {
|
|
|
179
183
|
* <obc-alert-icon .alarmType=${alarm.type} .alarmStatus=${alarm.status}></obc-alert-icon>
|
|
180
184
|
* ```
|
|
181
185
|
*
|
|
186
|
+
* @property flashingSpeed - Flash tempo: `default` resolves from the alert type and state,
|
|
187
|
+
* `fast`, `slow`, `very-slow` force a tempo, `fixed` never flashes. Acknowledged icons are
|
|
188
|
+
* always steady.
|
|
182
189
|
* @availableWhen acknowledged type in [Alarm, Warning, LevelCritical, LevelHigh, LevelMedium, LevelLow]
|
|
183
190
|
* @availableWhen active type in [Alarm, Warning, LevelCritical, LevelHigh, LevelMedium, LevelLow]
|
|
184
191
|
* @stable
|
|
@@ -192,6 +199,26 @@ export class ObcAlertIcon extends LitElement {
|
|
|
192
199
|
@property({type: Boolean}) active?: boolean;
|
|
193
200
|
@property({type: Boolean}) silenced?: boolean;
|
|
194
201
|
|
|
202
|
+
@property({type: String}) flashingSpeed: FlashingSpeed =
|
|
203
|
+
FlashingSpeed.Default;
|
|
204
|
+
|
|
205
|
+
protected readonly flashing = new FlashingController(
|
|
206
|
+
this,
|
|
207
|
+
() => this.resolvedFlashingSpeed
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
get resolvedFlashingSpeed(): ResolvedFlashingSpeed {
|
|
211
|
+
// Caution and diagnostic have no flashing glyph pair, whatever the speed.
|
|
212
|
+
if (this.acknowledged || !this.icon) {
|
|
213
|
+
return FlashingSpeed.Fixed;
|
|
214
|
+
}
|
|
215
|
+
return resolveFlashingSpeed(
|
|
216
|
+
this.flashingSpeed,
|
|
217
|
+
this._effectiveType,
|
|
218
|
+
this.active === false ? AlertFlashPhase.Rectified : AlertFlashPhase.Active
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
195
222
|
private get _effectiveType(): AlertType {
|
|
196
223
|
if (this.type) {
|
|
197
224
|
return this.type;
|
|
@@ -319,19 +346,11 @@ export class ObcAlertIcon extends LitElement {
|
|
|
319
346
|
if (!this._effectiveType) {
|
|
320
347
|
return html`<div>No alarm</div>`;
|
|
321
348
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
throw new Error('No icon found');
|
|
326
|
-
}
|
|
327
|
-
const blinkMode = getAlertBlinkMode(this._effectiveType);
|
|
349
|
+
const tempo = this.resolvedFlashingSpeed;
|
|
350
|
+
const icons = this.icon;
|
|
351
|
+
if (tempo !== FlashingSpeed.Fixed && icons) {
|
|
328
352
|
return html`
|
|
329
|
-
<div
|
|
330
|
-
class=${classMap({
|
|
331
|
-
wrapper: true,
|
|
332
|
-
[`blink-${blinkMode}`]: true,
|
|
333
|
-
})}
|
|
334
|
-
>
|
|
353
|
+
<div class=${classMap({wrapper: true, [`flash-${tempo}`]: true})}>
|
|
335
354
|
<span class="a">${icons.a}</span>
|
|
336
355
|
<span class="b">${icons.b}</span>
|
|
337
356
|
</div>
|
|
@@ -340,43 +359,6 @@ export class ObcAlertIcon extends LitElement {
|
|
|
340
359
|
return html`<div class="wrapper">${this.renderStaticIcon()}</div>`;
|
|
341
360
|
}
|
|
342
361
|
|
|
343
|
-
private _blinkAnimationCancel?: () => void;
|
|
344
|
-
|
|
345
|
-
private syncBlinking(): void {
|
|
346
|
-
if (
|
|
347
|
-
this._blinkAnimationCancel &&
|
|
348
|
-
!supportsBlinking(this._effectiveType, this.acknowledged ?? false)
|
|
349
|
-
) {
|
|
350
|
-
this._blinkAnimationCancel();
|
|
351
|
-
this._blinkAnimationCancel = undefined;
|
|
352
|
-
}
|
|
353
|
-
if (
|
|
354
|
-
!this._blinkAnimationCancel &&
|
|
355
|
-
supportsBlinking(this._effectiveType, this.acknowledged ?? false)
|
|
356
|
-
) {
|
|
357
|
-
this._blinkAnimationCancel = blinkingAll(this);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
override connectedCallback(): void {
|
|
362
|
-
super.connectedCallback();
|
|
363
|
-
if (this.hasUpdated) {
|
|
364
|
-
this.syncBlinking();
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
override updated(): void {
|
|
369
|
-
this.syncBlinking();
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
override disconnectedCallback(): void {
|
|
373
|
-
super.disconnectedCallback();
|
|
374
|
-
if (this._blinkAnimationCancel) {
|
|
375
|
-
this._blinkAnimationCancel();
|
|
376
|
-
this._blinkAnimationCancel = undefined;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
362
|
static override styles = css`
|
|
381
363
|
.wrapper {
|
|
382
364
|
height: 100%;
|
|
@@ -402,36 +384,28 @@ export class ObcAlertIcon extends LitElement {
|
|
|
402
384
|
top: 0;
|
|
403
385
|
left: 0;
|
|
404
386
|
}
|
|
405
|
-
&.
|
|
406
|
-
opacity: var(--
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
&.blink-alarm .b {
|
|
410
|
-
opacity: var(--alarm-blink-off);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
&.blink-critical .a {
|
|
414
|
-
opacity: var(--critical-blink-on);
|
|
387
|
+
&.flash-fast .a {
|
|
388
|
+
opacity: var(--flash-fast-on);
|
|
415
389
|
}
|
|
416
390
|
|
|
417
|
-
&.
|
|
418
|
-
opacity: var(--
|
|
391
|
+
&.flash-fast .b {
|
|
392
|
+
opacity: var(--flash-fast-off);
|
|
419
393
|
}
|
|
420
394
|
|
|
421
|
-
&.
|
|
422
|
-
opacity: var(--
|
|
395
|
+
&.flash-slow .a {
|
|
396
|
+
opacity: var(--flash-slow-on);
|
|
423
397
|
}
|
|
424
398
|
|
|
425
|
-
&.
|
|
426
|
-
opacity: var(--
|
|
399
|
+
&.flash-slow .b {
|
|
400
|
+
opacity: var(--flash-slow-off);
|
|
427
401
|
}
|
|
428
402
|
|
|
429
|
-
&.
|
|
430
|
-
opacity: var(--
|
|
403
|
+
&.flash-very-slow .a {
|
|
404
|
+
opacity: var(--flash-very-slow-on);
|
|
431
405
|
}
|
|
432
406
|
|
|
433
|
-
&.
|
|
434
|
-
opacity: var(--
|
|
407
|
+
&.flash-very-slow .b {
|
|
408
|
+
opacity: var(--flash-very-slow-off);
|
|
435
409
|
}
|
|
436
410
|
}
|
|
437
411
|
`;
|