@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.152 → 2.0.0-next.153
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 +1 -1
- package/bundle/openbridge-webcomponents.bundle.js +4717 -4485
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +374 -52
- package/dist/alert-severity.d.ts +19 -1
- package/dist/alert-severity.d.ts.map +1 -1
- package/dist/alert-severity.js +48 -2
- package/dist/alert-severity.js.map +1 -1
- package/dist/components/alert-button/alert-button.css.js +7 -1513
- package/dist/components/alert-button/alert-button.css.js.map +1 -1
- package/dist/components/alert-button/alert-button.d.ts +40 -96
- package/dist/components/alert-button/alert-button.d.ts.map +1 -1
- package/dist/components/alert-button/alert-button.js +40 -107
- package/dist/components/alert-button/alert-button.js.map +1 -1
- package/dist/components/alert-button-item/alert-button-item.css.js +1570 -0
- package/dist/components/alert-button-item/alert-button-item.css.js.map +1 -0
- package/dist/components/alert-button-item/alert-button-item.d.ts +134 -0
- package/dist/components/alert-button-item/alert-button-item.d.ts.map +1 -0
- package/dist/components/alert-button-item/alert-button-item.js +210 -0
- package/dist/components/alert-button-item/alert-button-item.js.map +1 -0
- package/dist/components/alert-counter-item/alert-counter-item.css.js +25 -0
- package/dist/components/alert-counter-item/alert-counter-item.css.js.map +1 -0
- package/dist/components/alert-counter-item/alert-counter-item.d.ts +77 -0
- package/dist/components/alert-counter-item/alert-counter-item.d.ts.map +1 -0
- package/dist/components/alert-counter-item/alert-counter-item.js +87 -0
- package/dist/components/alert-counter-item/alert-counter-item.js.map +1 -0
- package/dist/components/alert-frame/alert-frame.css.js +23 -27
- package/dist/components/alert-frame/alert-frame.css.js.map +1 -1
- package/dist/components/alert-frame/alert-frame.d.ts +7 -6
- package/dist/components/alert-frame/alert-frame.d.ts.map +1 -1
- package/dist/components/alert-frame/alert-frame.js +6 -9
- package/dist/components/alert-frame/alert-frame.js.map +1 -1
- package/dist/components/tree-navigation-item/tree-navigation-item.d.ts +7 -34
- package/dist/components/tree-navigation-item/tree-navigation-item.d.ts.map +1 -1
- package/dist/components/tree-navigation-item/tree-navigation-item.js +3 -32
- package/dist/components/tree-navigation-item/tree-navigation-item.js.map +1 -1
- package/dist/generated/locales/es-419.d.ts +11 -3
- package/dist/generated/locales/es-419.d.ts.map +1 -1
- package/dist/generated/locales/es-419.js +11 -3
- package/dist/generated/locales/es-419.js.map +1 -1
- package/dist/generated/locales/fi-FI.d.ts +8 -0
- package/dist/generated/locales/fi-FI.d.ts.map +1 -1
- package/dist/generated/locales/fi-FI.js +8 -0
- package/dist/generated/locales/fi-FI.js.map +1 -1
- package/dist/navigation-instruments/readout/readout.css.js +4 -29
- package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
- package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
- package/dist/navigation-instruments/readout/readout.js +1 -9
- package/dist/navigation-instruments/readout/readout.js.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js +5 -34
- 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.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js +1 -9
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
- package/dist/palettes/blinking.d.ts +6 -2
- package/dist/palettes/blinking.d.ts.map +1 -1
- package/dist/palettes/blinking.js +6 -2
- package/dist/palettes/blinking.js.map +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/alert-severity.spec.ts +81 -2
- package/src/alert-severity.ts +74 -0
- package/src/components/alert-button/alert-button.css +7 -196
- package/src/components/alert-button/alert-button.spec.ts +119 -68
- package/src/components/alert-button/alert-button.stories.ts +24 -0
- package/src/components/alert-button/alert-button.ts +49 -211
- package/src/components/alert-button-item/alert-button-item.css +242 -0
- package/src/components/alert-button-item/alert-button-item.spec.ts +299 -0
- package/src/components/alert-button-item/alert-button-item.stories.ts +178 -0
- package/src/components/alert-button-item/alert-button-item.ts +289 -0
- package/src/components/alert-counter-item/alert-counter-item.css +15 -0
- package/src/components/alert-counter-item/alert-counter-item.spec.ts +91 -0
- package/src/components/alert-counter-item/alert-counter-item.stories.ts +66 -0
- package/src/components/alert-counter-item/alert-counter-item.ts +128 -0
- package/src/components/alert-frame/alert-frame.css +23 -27
- package/src/components/alert-frame/alert-frame.spec.ts +19 -1
- package/src/components/alert-frame/alert-frame.stories.ts +47 -0
- package/src/components/alert-frame/alert-frame.ts +16 -17
- package/src/components/tree-navigation-item/tree-navigation-item.ts +10 -60
- package/src/generated/locales/es-419.ts +11 -3
- package/src/generated/locales/fi-FI.ts +8 -0
- package/src/navigation-instruments/readout/readout.css +4 -29
- package/src/navigation-instruments/readout/readout.ts +1 -9
- package/src/navigation-instruments/readout-list-item/readout-list-item.css +5 -33
- package/src/navigation-instruments/readout-list-item/readout-list-item.ts +1 -9
- package/src/palettes/blinking.spec.ts +38 -23
- package/src/palettes/blinking.ts +12 -4
- package/src/types.ts +16 -0
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
position: relative;
|
|
25
25
|
width: 100%;
|
|
26
26
|
height: 100%;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
);
|
|
27
|
+
/* The stroke's centreline follows this radius, like a Figma centre stroke. */
|
|
28
|
+
border-radius: var(--ui-components-button-border-radius);
|
|
30
29
|
outline: var(--thickness) solid var(--bg-color);
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/* Centre stroke: frames on touching components share one edge. */
|
|
31
|
+
outline-offset: calc(-1 * var(--thickness) / 2);
|
|
32
|
+
/* +2 px while on, 1 px each side, both thicknesses (Figma Flash=yes: 2 → 4 px). */
|
|
33
33
|
--blink-width: 2px;
|
|
34
34
|
&.thickness-small {
|
|
35
35
|
--thickness: 2px;
|
|
@@ -62,7 +62,9 @@
|
|
|
62
62
|
--flash-transition: 50ms linear;
|
|
63
63
|
|
|
64
64
|
&.flash-effect-outline-eased {
|
|
65
|
-
transition:
|
|
65
|
+
transition:
|
|
66
|
+
outline-width var(--flash-transition),
|
|
67
|
+
outline-offset var(--flash-transition);
|
|
66
68
|
|
|
67
69
|
.flap {
|
|
68
70
|
transition: box-shadow var(--flash-transition);
|
|
@@ -88,14 +90,10 @@
|
|
|
88
90
|
&.unacked-active,
|
|
89
91
|
&.unacked-rectified {
|
|
90
92
|
outline-width: calc(var(--thickness) + var(--blink-width-dynamic));
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
&.unacked-rectified {
|
|
98
|
-
/* The dashed stroke is centred, so it moves out by half its growth. */
|
|
93
|
+
outline-offset: calc(
|
|
94
|
+
-1 * (var(--thickness) + var(--blink-width-dynamic)) / 2
|
|
95
|
+
);
|
|
96
|
+
/* The stroke grows on both sides, so its outer edge moves by half. */
|
|
99
97
|
--flap-grow: calc(var(--blink-width-dynamic) / 2);
|
|
100
98
|
}
|
|
101
99
|
|
|
@@ -160,9 +158,9 @@
|
|
|
160
158
|
gap: 2px;
|
|
161
159
|
|
|
162
160
|
&.small {
|
|
163
|
-
top: calc(-1 * var(--thickness));
|
|
161
|
+
top: calc(-1 * var(--thickness) / 2);
|
|
164
162
|
right: calc(
|
|
165
|
-
-1 * var(--app-components-alert-frame-icon-size) -
|
|
163
|
+
-1 * var(--app-components-alert-frame-icon-size) -
|
|
166
164
|
var(--ui-components-badge-padding) * 2
|
|
167
165
|
);
|
|
168
166
|
}
|
|
@@ -272,10 +270,10 @@
|
|
|
272
270
|
|
|
273
271
|
&.large {
|
|
274
272
|
flex-direction: column;
|
|
275
|
-
top: calc(-1 * var(--thickness));
|
|
276
|
-
bottom: calc(-1 * var(--thickness));
|
|
273
|
+
top: calc(-1 * var(--thickness) / 2);
|
|
274
|
+
bottom: calc(-1 * var(--thickness) / 2);
|
|
277
275
|
right: calc(
|
|
278
|
-
-1 * var(--app-components-alert-frame-icon-size) + var(--thickness)
|
|
276
|
+
-1 * var(--app-components-alert-frame-icon-size) + var(--thickness) -
|
|
279
277
|
var(--ui-components-badge-padding) * 2
|
|
280
278
|
);
|
|
281
279
|
border-radius: 0px
|
|
@@ -285,13 +283,12 @@
|
|
|
285
283
|
|
|
286
284
|
&.bottom,
|
|
287
285
|
&.top {
|
|
288
|
-
left: calc(-1 * var(--thickness));
|
|
289
|
-
right: calc(-1 * var(--thickness));
|
|
286
|
+
left: calc(-1 * var(--thickness) / 2);
|
|
287
|
+
right: calc(-1 * var(--thickness) / 2);
|
|
290
288
|
padding: var(--app-components-alert-frame-large-flip-padding-vertical)
|
|
291
289
|
var(--app-components-alert-frame-large-flip-4px-padding-horizontal);
|
|
292
290
|
bottom: calc(
|
|
293
|
-
-1 * var(--
|
|
294
|
-
var(--global-typography-ui-label-line-height) -
|
|
291
|
+
-1 * var(--global-typography-ui-label-line-height) -
|
|
295
292
|
var(--app-components-alert-frame-large-flip-padding-vertical) * 2
|
|
296
293
|
);
|
|
297
294
|
border-radius: 0px 0px var(--button-border-radius-bottom-right)
|
|
@@ -308,7 +305,7 @@
|
|
|
308
305
|
.text-size-large & {
|
|
309
306
|
@mixin font-body;
|
|
310
307
|
bottom: calc(
|
|
311
|
-
var(--thickness)
|
|
308
|
+
var(--thickness) - var(--global-typography-ui-body-line-height) -
|
|
312
309
|
var(--app-components-alert-frame-large-flip-padding-vertical) * 2
|
|
313
310
|
);
|
|
314
311
|
|
|
@@ -321,8 +318,7 @@
|
|
|
321
318
|
&.top {
|
|
322
319
|
bottom: unset !important;
|
|
323
320
|
top: calc(
|
|
324
|
-
-1 * var(--
|
|
325
|
-
var(--global-typography-ui-label-line-height) -
|
|
321
|
+
-1 * var(--global-typography-ui-label-line-height) -
|
|
326
322
|
var(--app-components-alert-frame-large-flip-padding-vertical) * 2
|
|
327
323
|
);
|
|
328
324
|
border-radius: var(--button-border-radius-bottom-right)
|
|
@@ -330,7 +326,7 @@
|
|
|
330
326
|
|
|
331
327
|
.text-size-large & {
|
|
332
328
|
top: calc(
|
|
333
|
-
var(--thickness)
|
|
329
|
+
var(--thickness) - var(--global-typography-ui-body-line-height) -
|
|
334
330
|
var(--app-components-alert-frame-large-flip-padding-vertical) * 2
|
|
335
331
|
);
|
|
336
332
|
}
|
|
@@ -221,6 +221,21 @@ describe('obc-alert-frame flashing lifecycle', () => {
|
|
|
221
221
|
expect(midway).toBeLessThan(4);
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
+
it('centres the stroke on the frame edge and grows it 1 px each side while on', async () => {
|
|
225
|
+
const el = await setup(ObcAlertFrameMode.unackedActive);
|
|
226
|
+
const wrapper = el.shadowRoot!.querySelector('.wrapper') as HTMLElement;
|
|
227
|
+
const outline = () => {
|
|
228
|
+
const style = getComputedStyle(wrapper);
|
|
229
|
+
return [style.outlineWidth, style.outlineOffset];
|
|
230
|
+
};
|
|
231
|
+
const [flash] = el.getAnimations();
|
|
232
|
+
flash.pause();
|
|
233
|
+
flash.currentTime = 100;
|
|
234
|
+
expect(outline()).toEqual(['4px', '-2px']);
|
|
235
|
+
flash.currentTime = 500;
|
|
236
|
+
expect(outline()).toEqual(['2px', '-1px']);
|
|
237
|
+
});
|
|
238
|
+
|
|
224
239
|
describe('rectified dash', () => {
|
|
225
240
|
it('draws the 12/6 dash as one svg path whose stroke width flashes', async () => {
|
|
226
241
|
const el = await setup(ObcAlertFrameMode.unackedRectified);
|
|
@@ -263,7 +278,10 @@ describe('obc-alert-frame flashing lifecycle', () => {
|
|
|
263
278
|
|
|
264
279
|
const sharp = path().getAttribute('d')!;
|
|
265
280
|
expect(sharp).not.toBe(rounded);
|
|
266
|
-
|
|
281
|
+
// The sharp corner sits on the wrapper corner: the svg is shifted by -pad.
|
|
282
|
+
expect(sharp.startsWith('M2 2 H')).toBe(true);
|
|
283
|
+
const svg = el.shadowRoot!.querySelector('svg.dash') as SVGSVGElement;
|
|
284
|
+
expect(svg.style.getPropertyValue('--dash-pad')).toBe('2px');
|
|
267
285
|
});
|
|
268
286
|
|
|
269
287
|
it('removes the svg when the mode leaves unacked-rectified', async () => {
|
|
@@ -290,6 +290,53 @@ export const CriticalUnacked: Story = {
|
|
|
290
290
|
},
|
|
291
291
|
};
|
|
292
292
|
|
|
293
|
+
const ADJACENT_FRAMES: {
|
|
294
|
+
status: AlertType;
|
|
295
|
+
mode: ObcAlertFrameMode;
|
|
296
|
+
thickness: ObcAlertFrameThickness;
|
|
297
|
+
}[] = [
|
|
298
|
+
{
|
|
299
|
+
status: AlertType.Warning,
|
|
300
|
+
mode: ObcAlertFrameMode.unackedActive,
|
|
301
|
+
thickness: ObcAlertFrameThickness.Small,
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
status: AlertType.Alarm,
|
|
305
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
306
|
+
thickness: ObcAlertFrameThickness.Small,
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
status: AlertType.Caution,
|
|
310
|
+
mode: ObcAlertFrameMode.ackedActive,
|
|
311
|
+
thickness: ObcAlertFrameThickness.Large,
|
|
312
|
+
},
|
|
313
|
+
];
|
|
314
|
+
|
|
315
|
+
export const AdjacentFrames: Story = {
|
|
316
|
+
parameters: {
|
|
317
|
+
docs: {
|
|
318
|
+
description: {
|
|
319
|
+
story:
|
|
320
|
+
'Frames on touching components share one edge: every stroke is centred on the edge of its component. The top frame is in its on phase, so its wider stroke shows on both sides of the thinner one below it.',
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
render: () =>
|
|
325
|
+
html`<div style="display: grid; width: 240px; padding: 16px">
|
|
326
|
+
${ADJACENT_FRAMES.map(
|
|
327
|
+
(frame) =>
|
|
328
|
+
html`<div style="position: relative; height: 56px">
|
|
329
|
+
<obc-alert-frame
|
|
330
|
+
.type=${ObcAlertFrameType.Regular}
|
|
331
|
+
.status=${frame.status}
|
|
332
|
+
.mode=${frame.mode}
|
|
333
|
+
.thickness=${frame.thickness}
|
|
334
|
+
></obc-alert-frame>
|
|
335
|
+
</div>`
|
|
336
|
+
)}
|
|
337
|
+
</div>`,
|
|
338
|
+
};
|
|
339
|
+
|
|
293
340
|
export const FlashComparison: Story = {
|
|
294
341
|
tags: ['skip-test'],
|
|
295
342
|
render: () => {
|
|
@@ -66,10 +66,10 @@ export enum ObcAlertFrameMode {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* How the frame renders the on phase of a flash: `outline` grows the
|
|
70
|
-
*
|
|
71
|
-
* way over a 50 ms transition. The eased variant exists for
|
|
72
|
-
* and may be removed (#1224).
|
|
69
|
+
* How the frame renders the on phase of a flash: `outline` grows the centred
|
|
70
|
+
* stroke by 2 px, 1 px on each side, in one step (the design); `outline-eased`
|
|
71
|
+
* grows it the same way over a 50 ms transition. The eased variant exists for
|
|
72
|
+
* design evaluation and may be removed (#1224).
|
|
73
73
|
*/
|
|
74
74
|
export enum ObcAlertFrameFlashEffect {
|
|
75
75
|
Outline = 'outline',
|
|
@@ -94,7 +94,7 @@ interface DashBox {
|
|
|
94
94
|
radii: RoundedRect['radii'];
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
/**
|
|
97
|
+
/** On-phase stroke growth; the svg leaves half of stroke plus growth around the box. */
|
|
98
98
|
const DASH_FLASH_GROWTH_PX = 2;
|
|
99
99
|
|
|
100
100
|
function sameDashBox(a: DashBox, b: DashBox | undefined): boolean {
|
|
@@ -130,6 +130,7 @@ export interface AlertFrameConfig {
|
|
|
130
130
|
* - `large-side-flip`: Adds a larger, vertical side flap with a status icon and optional custom icon.
|
|
131
131
|
* - `bottom-flip`: Adds a bottom flap with a status icon, label, and timer slots.
|
|
132
132
|
* - **Thickness options:** Choose between `small` (thin border) and `large` (thick border) for visual emphasis.
|
|
133
|
+
* - **Centred stroke:** The stroke straddles the frame edge, half over the content and half outside, so frames on touching components share one edge. While flashing it grows by 1 px on each side.
|
|
133
134
|
* - **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`).
|
|
134
135
|
* - **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.
|
|
135
136
|
* - **Content wrapping:** When `wrapContent` is true, the frame wraps and sizes itself to its slotted content rather than overlaying a fixed region.
|
|
@@ -202,7 +203,7 @@ export interface AlertFrameConfig {
|
|
|
202
203
|
* (critical/alarm/high fast, warning/medium slow, low very slow, every rectified alert very
|
|
203
204
|
* slow, caution and diagnostic fixed), `fast`, `slow`, `very-slow` force a tempo, `fixed`
|
|
204
205
|
* never flashes. Acknowledged frames are always steady.
|
|
205
|
-
* @property flashEffect - `outline` (default) grows the outline by 2 px while on in one step;
|
|
206
|
+
* @property flashEffect - `outline` (default) grows the outline by 2 px, 1 px each side, while on in one step;
|
|
206
207
|
* `outline-eased` grows it over a 50 ms transition, a design-evaluation option.
|
|
207
208
|
* @slot - Default slot for main alert content.
|
|
208
209
|
* @slot icon - Custom icon for the flap (large-side-flip, bottom-flip).
|
|
@@ -327,18 +328,16 @@ export class ObcAlertFrame extends LitElement {
|
|
|
327
328
|
if (this.mode !== ObcAlertFrameMode.unackedRectified || !box) {
|
|
328
329
|
return nothing;
|
|
329
330
|
}
|
|
330
|
-
const pad = box.thickness + DASH_FLASH_GROWTH_PX;
|
|
331
|
-
//
|
|
332
|
-
//
|
|
331
|
+
const pad = (box.thickness + DASH_FLASH_GROWTH_PX) / 2;
|
|
332
|
+
// The centreline is the wrapper edge, as for the solid outline. A second,
|
|
333
|
+
// wider path for the on phase would have longer corner arcs and its
|
|
334
|
+
// dashes would drift around the frame.
|
|
333
335
|
const centreline: RoundedRect = {
|
|
334
|
-
x: pad
|
|
335
|
-
y: pad
|
|
336
|
-
width: box.width
|
|
337
|
-
height: box.height
|
|
338
|
-
|
|
339
|
-
radii: box.radii.map((r) =>
|
|
340
|
-
r > 0 ? r + box.thickness / 2 : 0
|
|
341
|
-
) as RoundedRect['radii'],
|
|
336
|
+
x: pad,
|
|
337
|
+
y: pad,
|
|
338
|
+
width: box.width,
|
|
339
|
+
height: box.height,
|
|
340
|
+
radii: box.radii,
|
|
342
341
|
};
|
|
343
342
|
const width = box.width + 2 * pad;
|
|
344
343
|
const height = box.height + 2 * pad;
|
|
@@ -8,7 +8,8 @@ import '../../icons/icon-chevron-right-google.js';
|
|
|
8
8
|
import '../../icons/icon-alert-header-aggregated-iec.js';
|
|
9
9
|
import '../../icons/icon-alert-header-group-iec.js';
|
|
10
10
|
import '../badge/badge.js';
|
|
11
|
-
import {
|
|
11
|
+
import type {AlertCounts} from '../../types.js';
|
|
12
|
+
import {rankAlertCounts, type RankedAlertCount} from '../../alert-severity.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Guide line drawn for one indentation column. Normally computed by
|
|
@@ -43,36 +44,16 @@ export enum TreeTerminalType {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
/**
|
|
46
|
-
* Per-severity alert counts for a tree row's trailing badge(s)
|
|
47
|
-
*
|
|
48
|
-
* level severities `level-critical`, `level-high`, `level-medium`, `level-low`,
|
|
49
|
-
* and `level-diagnostic`, plus the IEC severities `alarm`, `warning`, and
|
|
50
|
-
* `caution`. Badges are ordered and aggregated by the shared
|
|
51
|
-
* `ALERT_SEVERITY_PRIORITY` ranking, most to least severe.
|
|
47
|
+
* Per-severity alert counts for a tree row's trailing badge(s): the active
|
|
48
|
+
* alerts at or beneath the row, ordered by `rankAlertCounts`.
|
|
52
49
|
*
|
|
53
50
|
* Set `combine` to collapse the counts into a single badge showing the total,
|
|
54
51
|
* styled as the highest category present; otherwise one badge is rendered per
|
|
55
52
|
* non-zero count.
|
|
56
53
|
*/
|
|
57
|
-
export interface TreeNavigationItemAlerts {
|
|
54
|
+
export interface TreeNavigationItemAlerts extends AlertCounts {
|
|
58
55
|
/** Collapse all counts into one badge: total count, highest-severity style. */
|
|
59
56
|
combine?: boolean;
|
|
60
|
-
/** Number of level-critical alerts. */
|
|
61
|
-
countLevelCritical?: number;
|
|
62
|
-
/** Number of alarm alerts. */
|
|
63
|
-
countAlarm?: number;
|
|
64
|
-
/** Number of level-high alerts. */
|
|
65
|
-
countLevelHigh?: number;
|
|
66
|
-
/** Number of warning alerts. */
|
|
67
|
-
countWarning?: number;
|
|
68
|
-
/** Number of level-medium alerts. */
|
|
69
|
-
countLevelMedium?: number;
|
|
70
|
-
/** Number of caution alerts. */
|
|
71
|
-
countCaution?: number;
|
|
72
|
-
/** Number of level-low alerts. */
|
|
73
|
-
countLevelLow?: number;
|
|
74
|
-
/** Number of level-diagnostic alerts. */
|
|
75
|
-
countLevelDiagnostic?: number;
|
|
76
57
|
}
|
|
77
58
|
|
|
78
59
|
/**
|
|
@@ -177,8 +158,8 @@ export class ObcTreeNavigationItem extends LitElement {
|
|
|
177
158
|
* every count at 0) for a row with no alerts. See {@link TreeNavigationItemAlerts}.
|
|
178
159
|
*
|
|
179
160
|
* - When `combine` is true, a single badge is shown: its number is the sum
|
|
180
|
-
* of all counts and its severity is the highest category present
|
|
181
|
-
*
|
|
161
|
+
* of all counts and its severity is the highest category present, ranked
|
|
162
|
+
* by `ALERT_SEVERITY_PRIORITY`.
|
|
182
163
|
* - Otherwise one badge is shown per count greater than 0, ordered most to
|
|
183
164
|
* least severe and spaced by the alert-counter spacing token.
|
|
184
165
|
*/
|
|
@@ -193,40 +174,9 @@ export class ObcTreeNavigationItem extends LitElement {
|
|
|
193
174
|
this.wrapperElement?.focus(options);
|
|
194
175
|
}
|
|
195
176
|
|
|
196
|
-
/**
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
*
|
|
200
|
-
* - No `alerts`, or every count 0 → no badges.
|
|
201
|
-
* - `combine` → a single pair: the summed count typed as the highest
|
|
202
|
-
* category that has any alerts.
|
|
203
|
-
* - Otherwise → one pair per count greater than 0.
|
|
204
|
-
*/
|
|
205
|
-
private get alertBadges(): {type: AlertType; count: number}[] {
|
|
206
|
-
const alerts = this.alerts;
|
|
207
|
-
if (!alerts) return [];
|
|
208
|
-
const countByType: Partial<Record<AlertType, number>> = {
|
|
209
|
-
[AlertType.LevelCritical]: alerts.countLevelCritical ?? 0,
|
|
210
|
-
[AlertType.Alarm]: alerts.countAlarm ?? 0,
|
|
211
|
-
[AlertType.LevelHigh]: alerts.countLevelHigh ?? 0,
|
|
212
|
-
[AlertType.Warning]: alerts.countWarning ?? 0,
|
|
213
|
-
[AlertType.LevelMedium]: alerts.countLevelMedium ?? 0,
|
|
214
|
-
[AlertType.Caution]: alerts.countCaution ?? 0,
|
|
215
|
-
[AlertType.LevelLow]: alerts.countLevelLow ?? 0,
|
|
216
|
-
[AlertType.LevelDiagnostic]: alerts.countLevelDiagnostic ?? 0,
|
|
217
|
-
};
|
|
218
|
-
// Order (and, when combining, rank) by the shared severity priority,
|
|
219
|
-
// keeping only the severities this component exposes.
|
|
220
|
-
const ranked = ALERT_SEVERITY_PRIORITY.filter(
|
|
221
|
-
(type) => type in countByType
|
|
222
|
-
).map((type) => ({type, count: countByType[type] ?? 0}));
|
|
223
|
-
if (alerts.combine) {
|
|
224
|
-
const total = ranked.reduce((sum, b) => sum + b.count, 0);
|
|
225
|
-
const highest = ranked.find((b) => b.count > 0);
|
|
226
|
-
if (!highest) return [];
|
|
227
|
-
return [{type: highest.type, count: total}];
|
|
228
|
-
}
|
|
229
|
-
return ranked.filter((b) => b.count > 0);
|
|
177
|
+
/** The badge(s) to render from `alerts`, ranked by `rankAlertCounts`. */
|
|
178
|
+
private get alertBadges(): RankedAlertCount[] {
|
|
179
|
+
return this.alerts ? rankAlertCounts(this.alerts, this.alerts.combine) : [];
|
|
230
180
|
}
|
|
231
181
|
|
|
232
182
|
/** A root-level row has no ancestor columns, so it draws no connector lines. */
|
|
@@ -13,10 +13,20 @@
|
|
|
13
13
|
's8119af112f965c78': `Brillantez`,
|
|
14
14
|
'sa7f8e003ff63ef9f': `No alertas activas`,
|
|
15
15
|
'se0955919920ee87d': `Día`,
|
|
16
|
+
'scd93bdcb7863611a': `Alarm`,
|
|
17
|
+
's34be76c6b1eadbef': `Warning`,
|
|
18
|
+
'sf11564cc75a8d5a6': `Caution`,
|
|
19
|
+
'sa45f3300b97dabe8': `Critical`,
|
|
20
|
+
's9deeefd89ca8a81d': `High`,
|
|
21
|
+
'sac6bdc71f750ac96': `Medium`,
|
|
22
|
+
's24f3a319b88552c1': `Low`,
|
|
23
|
+
'sca9e13f23f0c433e': `Diagnostic`,
|
|
24
|
+
'sf268637d594dacb2': `Shelved`,
|
|
25
|
+
's88d1c1e15ca1aade': `Alerts`,
|
|
26
|
+
's68395a4c26a9e8ac': `Silence`,
|
|
16
27
|
'sf993bb199fefbe04': `All`,
|
|
17
28
|
's01b603dddbaba7b6': `Unacked`,
|
|
18
29
|
'se92d11374fcdf768': `No unacknowledged alerts`,
|
|
19
|
-
'sf268637d594dacb2': `Shelved`,
|
|
20
30
|
's2a2488656f97b746': `No shelved alerts`,
|
|
21
31
|
'sf59d9197d2e50c93': `Blocked`,
|
|
22
32
|
's61a9ee872da3a0cd': `No blocked alerts`,
|
|
@@ -26,8 +36,6 @@
|
|
|
26
36
|
's5ba9818f6a39054c': `Go to the 'Alert list' for more details or to manage existing alerts.`,
|
|
27
37
|
'sa45a194b58837e4f': `Active`,
|
|
28
38
|
'sa6ef04345891fb38': `ACK visible`,
|
|
29
|
-
's68395a4c26a9e8ac': `Silence`,
|
|
30
|
-
's88d1c1e15ca1aade': `Alerts`,
|
|
31
39
|
's3cf89cb47fdde7e9': `Link`,
|
|
32
40
|
's47278872f6826da8': `Dusk`,
|
|
33
41
|
's9e34794bcfdc6665': `Bright`,
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
'sf993bb199fefbe04': `Kaikki`,
|
|
42
42
|
'sfa32c219a0764262': `VAHVISTA`,
|
|
43
43
|
'sfbc33dd48f23752c': `Tunnistetunnus`,
|
|
44
|
+
'scd93bdcb7863611a': `Alarm`,
|
|
45
|
+
's34be76c6b1eadbef': `Warning`,
|
|
46
|
+
'sf11564cc75a8d5a6': `Caution`,
|
|
47
|
+
'sa45f3300b97dabe8': `Critical`,
|
|
48
|
+
's9deeefd89ca8a81d': `High`,
|
|
49
|
+
'sac6bdc71f750ac96': `Medium`,
|
|
50
|
+
's24f3a319b88552c1': `Low`,
|
|
51
|
+
'sca9e13f23f0c433e': `Diagnostic`,
|
|
44
52
|
'sa45a194b58837e4f': `Active`,
|
|
45
53
|
'sc5a5b268010fc312': `Audio muted`,
|
|
46
54
|
's740deeebbea89396': `Audio on`,
|
|
@@ -173,42 +173,17 @@ obc-readout-block {
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
/* ---------- Value alert overlay ----------
|
|
176
|
-
*
|
|
177
|
-
* the
|
|
178
|
-
* (padding − stroke/2) on each axis so the alert-frame's outline (drawn
|
|
179
|
-
* outside its box) lands CENTRED on the 4px/2px padding line. */
|
|
176
|
+
* Reserves no space, so toggling the frame never shifts content. The box sits
|
|
177
|
+
* on the 4px/2px padding line, where obc-alert-frame centres its stroke. */
|
|
180
178
|
.value-alert-overlay {
|
|
181
179
|
--_readout-value-frame-padding-horizontal: 4px;
|
|
182
180
|
--_readout-value-frame-padding-vertical: 2px;
|
|
183
|
-
/* Must match obc-alert-frame's ACTUAL outline width so the stroke stays
|
|
184
|
-
centred on the 4px/2px line. `thickness-small` is hard-coded to 2px in
|
|
185
|
-
alert-frame.css, so use a literal 2px here; `thickness-large` is handled
|
|
186
|
-
below. */
|
|
187
|
-
--_readout-value-frame-stroke: 2px;
|
|
188
181
|
position: absolute;
|
|
189
|
-
inset-inline: calc(
|
|
190
|
-
|
|
191
|
-
(
|
|
192
|
-
var(--_readout-value-frame-padding-horizontal) -
|
|
193
|
-
var(--_readout-value-frame-stroke) / 2
|
|
194
|
-
)
|
|
195
|
-
);
|
|
196
|
-
inset-block: calc(
|
|
197
|
-
-1 *
|
|
198
|
-
(
|
|
199
|
-
var(--_readout-value-frame-padding-vertical) -
|
|
200
|
-
var(--_readout-value-frame-stroke) / 2
|
|
201
|
-
)
|
|
202
|
-
);
|
|
182
|
+
inset-inline: calc(-1 * var(--_readout-value-frame-padding-horizontal));
|
|
183
|
+
inset-block: calc(-1 * var(--_readout-value-frame-padding-vertical));
|
|
203
184
|
pointer-events: none;
|
|
204
185
|
}
|
|
205
186
|
|
|
206
|
-
.value-alert-overlay.thickness-large {
|
|
207
|
-
--_readout-value-frame-stroke: var(
|
|
208
|
-
--global-size-spacing-border-weight-alertframe
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
187
|
/* ---------- Meta zone (label + unit) ----------
|
|
213
188
|
* `.meta` is a row hosting the optional leading icon and `.meta-labels`; the
|
|
214
189
|
* inline/stacked arrangement applies to `.meta-labels` (the label/unit pair). */
|
|
@@ -851,15 +851,7 @@ export class ObcReadout extends LitElement {
|
|
|
851
851
|
}
|
|
852
852
|
const thickness = alert.thickness ?? ObcAlertFrameThickness.Small;
|
|
853
853
|
return html`
|
|
854
|
-
<div
|
|
855
|
-
class=${classMap({
|
|
856
|
-
'value-alert-overlay': true,
|
|
857
|
-
// The outward offset is thickness-dependent (see the CSS): large frames
|
|
858
|
-
// draw a wider outline, so the box must sit further out to stay centred.
|
|
859
|
-
'thickness-large': thickness === ObcAlertFrameThickness.Large,
|
|
860
|
-
})}
|
|
861
|
-
aria-hidden="true"
|
|
862
|
-
>
|
|
854
|
+
<div class="value-alert-overlay" aria-hidden="true">
|
|
863
855
|
<obc-alert-frame
|
|
864
856
|
part="value-alert-frame"
|
|
865
857
|
.type=${alert.type ?? ObcAlertFrameType.Regular}
|
|
@@ -181,47 +181,19 @@ obc-readout-block {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
/* ---------- Value alert overlay ----------
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* outline (drawn outside its box) lands CENTRED on the 4px/2px padding line
|
|
188
|
-
* (half inside / half outside), matching Figma 58:10120. */
|
|
184
|
+
* Reserves no space, so the frame never changes the row height or the column
|
|
185
|
+
* alignment. The box sits on the 4px/2px padding line, where obc-alert-frame
|
|
186
|
+
* centres its stroke (Figma 58:10120). */
|
|
189
187
|
.value-alert-overlay {
|
|
190
188
|
--_obc-readout-value-frame-padding-horizontal: 4px;
|
|
191
189
|
--_obc-readout-value-frame-padding-vertical: 2px;
|
|
192
|
-
/* Must match obc-alert-frame's ACTUAL outline width so the stroke stays centred
|
|
193
|
-
on the 4px/2px line. `thickness-small` is hard-coded to 2px in
|
|
194
|
-
alert-frame.css (it does NOT use the themed stroke-width token), so use a
|
|
195
|
-
literal 2px here; `thickness-large` is handled below. */
|
|
196
|
-
--_obc-readout-value-frame-stroke: 2px;
|
|
197
190
|
position: absolute;
|
|
198
|
-
inset-inline: calc(
|
|
199
|
-
|
|
200
|
-
(
|
|
201
|
-
var(--_obc-readout-value-frame-padding-horizontal) -
|
|
202
|
-
var(--_obc-readout-value-frame-stroke) / 2
|
|
203
|
-
)
|
|
204
|
-
);
|
|
205
|
-
inset-block: calc(
|
|
206
|
-
-1 *
|
|
207
|
-
(
|
|
208
|
-
var(--_obc-readout-value-frame-padding-vertical) -
|
|
209
|
-
var(--_obc-readout-value-frame-stroke) / 2
|
|
210
|
-
)
|
|
211
|
-
);
|
|
191
|
+
inset-inline: calc(-1 * var(--_obc-readout-value-frame-padding-horizontal));
|
|
192
|
+
inset-block: calc(-1 * var(--_obc-readout-value-frame-padding-vertical));
|
|
212
193
|
/* Display-only: never intercept clicks on a clickable row. */
|
|
213
194
|
pointer-events: none;
|
|
214
195
|
}
|
|
215
196
|
|
|
216
|
-
/* thickness=large: obc-alert-frame's large outline uses the alert-frame border
|
|
217
|
-
weight token, so the overlay's outward offset must use the same width to keep
|
|
218
|
-
the stroke centred on the padding line. */
|
|
219
|
-
.value-alert-overlay.thickness-large {
|
|
220
|
-
--_obc-readout-value-frame-stroke: var(
|
|
221
|
-
--global-size-spacing-border-weight-alertframe
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
197
|
/* The readout building blocks (advice / setpoint / value) now render inside
|
|
226
198
|
`obc-readout-block`; their layout / colour / hinted-zero / data-quality CSS
|
|
227
199
|
lives there. The list-item only bridges the public colour vars (see top) and
|
|
@@ -926,15 +926,7 @@ export class ObcReadoutListItem extends LitElement {
|
|
|
926
926
|
}
|
|
927
927
|
const thickness = alert.thickness ?? ObcAlertFrameThickness.Small;
|
|
928
928
|
return html`
|
|
929
|
-
<div
|
|
930
|
-
class=${classMap({
|
|
931
|
-
'value-alert-overlay': true,
|
|
932
|
-
// The outward offset is thickness-dependent (see the CSS): large frames
|
|
933
|
-
// draw a wider outline, so the box must sit further out to stay centred.
|
|
934
|
-
'thickness-large': thickness === ObcAlertFrameThickness.Large,
|
|
935
|
-
})}
|
|
936
|
-
aria-hidden="true"
|
|
937
|
-
>
|
|
929
|
+
<div class="value-alert-overlay" aria-hidden="true">
|
|
938
930
|
<obc-alert-frame
|
|
939
931
|
part="value-alert-frame"
|
|
940
932
|
.type=${alert.type ?? ObcAlertFrameType.Regular}
|
|
@@ -24,14 +24,14 @@ describe('installFlashing', () => {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it.each(FLASH_TEMPOS)(
|
|
27
|
-
'%s runs on for its on-time, off for
|
|
27
|
+
'%s runs on for its on-time, then off for its off-time, from the document origin',
|
|
28
28
|
(tempo) => {
|
|
29
29
|
const el = host();
|
|
30
30
|
const cancel = installFlashing(el, tempo);
|
|
31
31
|
const [anim] = el.getAnimations();
|
|
32
32
|
const effect = anim.effect as KeyframeEffect;
|
|
33
33
|
const timing = effect.getTiming();
|
|
34
|
-
expect(timing.duration).toBe(FLASH_ON_MS[tempo] + FLASH_OFF_MS);
|
|
34
|
+
expect(timing.duration).toBe(FLASH_ON_MS[tempo] + FLASH_OFF_MS[tempo]);
|
|
35
35
|
expect(timing.iterations).toBe(Infinity);
|
|
36
36
|
const frames = effect.getKeyframes();
|
|
37
37
|
expect(frames).toHaveLength(3);
|
|
@@ -46,32 +46,47 @@ describe('installFlashing', () => {
|
|
|
46
46
|
}
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
-
it('
|
|
49
|
+
it('follows the tempo table: on for 400 ms, off for 400, 1200 or 2800 ms', () => {
|
|
50
|
+
expect(
|
|
51
|
+
FLASH_TEMPOS.map((tempo) => [FLASH_ON_MS[tempo], FLASH_OFF_MS[tempo]])
|
|
52
|
+
).toEqual([
|
|
53
|
+
[400, 400],
|
|
54
|
+
[400, 1200],
|
|
55
|
+
[400, 2800],
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('nests the cycles so every tempo lights up together', () => {
|
|
50
60
|
expect(flashPeriodMs(FlashingSpeed.Fast)).toBe(800);
|
|
51
61
|
expect(flashPeriodMs(FlashingSpeed.Slow)).toBe(1600);
|
|
52
62
|
expect(flashPeriodMs(FlashingSpeed.VerySlow)).toBe(3200);
|
|
53
63
|
});
|
|
54
64
|
|
|
55
|
-
it(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
it.each(FLASH_TEMPOS)(
|
|
66
|
+
'%s drives the on/off variables in step, on phase first',
|
|
67
|
+
(tempo) => {
|
|
68
|
+
const el = host();
|
|
69
|
+
installFlashing(el, tempo);
|
|
70
|
+
const [anim] = el.getAnimations();
|
|
71
|
+
const read = (phase: 'on' | 'off') =>
|
|
72
|
+
getComputedStyle(el)
|
|
73
|
+
.getPropertyValue(flashVariable(tempo, phase))
|
|
74
|
+
.trim();
|
|
75
|
+
const period = flashPeriodMs(tempo);
|
|
76
|
+
anim.pause();
|
|
77
|
+
for (const [time, lit] of [
|
|
78
|
+
[100, true],
|
|
79
|
+
[500, false],
|
|
80
|
+
[period - 100, false],
|
|
81
|
+
[period + 100, true],
|
|
82
|
+
] as const) {
|
|
83
|
+
anim.currentTime = time;
|
|
84
|
+
expect([time, read('on'), read('off')]).toEqual(
|
|
85
|
+
lit ? [time, '1', '0'] : [time, '0', '1']
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
);
|
|
75
90
|
|
|
76
91
|
it('blinkingAll installs one animation per tempo and cancels them together', () => {
|
|
77
92
|
const el = host();
|