@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
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
import type {Meta, StoryObj} from '@storybook/web-components-vite';
|
|
2
|
+
import {LitElement, html, nothing, type TemplateResult} from 'lit';
|
|
3
|
+
import {property, state} from 'lit/decorators.js';
|
|
4
|
+
import {repeat} from 'lit/directives/repeat.js';
|
|
5
|
+
import {
|
|
6
|
+
ObcAlertFrameFlashEffect,
|
|
7
|
+
ObcAlertFrameMode,
|
|
8
|
+
ObcAlertFrameType,
|
|
9
|
+
} from './alert-frame.js';
|
|
10
|
+
import './alert-frame.js';
|
|
11
|
+
import '../alert-button/alert-button.js';
|
|
12
|
+
import '../alert-icon/alert-icon.js';
|
|
13
|
+
import '../alert-menu/alert-menu.js';
|
|
14
|
+
import {ObcAlertMenuItemStatus} from '../alert-menu-item/alert-menu-item.js';
|
|
15
|
+
import '../alert-menu-item/alert-menu-item.js';
|
|
16
|
+
import {ButtonVariant} from '../button/button.js';
|
|
17
|
+
import '../button/button.js';
|
|
18
|
+
import '../card/card.js';
|
|
19
|
+
import '../clock/clock.js';
|
|
20
|
+
import '../top-bar/top-bar.js';
|
|
21
|
+
import {ObcTopbarMessageItemType} from '../topbar-message-item/topbar-message-item.js';
|
|
22
|
+
import '../topbar-message-item/topbar-message-item.js';
|
|
23
|
+
import '../../navigation-instruments/readout-list-item/readout-list-item.js';
|
|
24
|
+
import {requiresAcknowledgement} from '../../alert-severity.js';
|
|
25
|
+
import {ALERT_SEVERITY_PRIORITY, AlertType} from '../../types.js';
|
|
26
|
+
|
|
27
|
+
interface ScenarioReadout {
|
|
28
|
+
label: string;
|
|
29
|
+
unit: string;
|
|
30
|
+
/** Value while the alert condition is present. */
|
|
31
|
+
alerting: number;
|
|
32
|
+
normal: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface AlertState {
|
|
36
|
+
present: boolean;
|
|
37
|
+
active: boolean;
|
|
38
|
+
acknowledged: boolean;
|
|
39
|
+
/** Timeline second the alert was raised; negative when raised before the start. */
|
|
40
|
+
raisedAt: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface ScenarioPanel {
|
|
44
|
+
id: string;
|
|
45
|
+
title: string;
|
|
46
|
+
alertType: AlertType;
|
|
47
|
+
alertTitle: string;
|
|
48
|
+
alertDescription: string;
|
|
49
|
+
frameType: ObcAlertFrameType;
|
|
50
|
+
readouts: ScenarioReadout[];
|
|
51
|
+
initial: AlertState;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
enum ScenarioChange {
|
|
55
|
+
Raise = 'raise',
|
|
56
|
+
Rectify = 'rectify',
|
|
57
|
+
Clear = 'clear',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface TimelineStep {
|
|
61
|
+
at: number;
|
|
62
|
+
panel: string;
|
|
63
|
+
change: ScenarioChange;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const raised = (raisedAt: number): AlertState => ({
|
|
67
|
+
present: true,
|
|
68
|
+
active: true,
|
|
69
|
+
acknowledged: false,
|
|
70
|
+
raisedAt,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const rectified = (raisedAt: number): AlertState => ({
|
|
74
|
+
present: true,
|
|
75
|
+
active: false,
|
|
76
|
+
acknowledged: false,
|
|
77
|
+
raisedAt,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const acknowledged = (raisedAt: number): AlertState => ({
|
|
81
|
+
present: true,
|
|
82
|
+
active: true,
|
|
83
|
+
acknowledged: true,
|
|
84
|
+
raisedAt,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const QUIET: AlertState = {
|
|
88
|
+
present: false,
|
|
89
|
+
active: false,
|
|
90
|
+
acknowledged: false,
|
|
91
|
+
raisedAt: 0,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* At the start every design level (critical, alarm, warning, caution) shows
|
|
96
|
+
* one solid and one dashed frame, and one alarm is acknowledged.
|
|
97
|
+
*/
|
|
98
|
+
const PANELS: ScenarioPanel[] = [
|
|
99
|
+
{
|
|
100
|
+
id: 'fire',
|
|
101
|
+
title: 'Fire detection',
|
|
102
|
+
alertType: AlertType.LevelCritical,
|
|
103
|
+
alertTitle: 'Fire in engine room',
|
|
104
|
+
alertDescription: 'Smoke and heat detectors triggered in zone 3',
|
|
105
|
+
frameType: ObcAlertFrameType.BottomFlip,
|
|
106
|
+
readouts: [
|
|
107
|
+
{label: 'Zone 3 temperature', unit: '°C', alerting: 86, normal: 31},
|
|
108
|
+
{label: 'Smoke density', unit: '%', alerting: 42, normal: 0},
|
|
109
|
+
],
|
|
110
|
+
initial: raised(-95),
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'switchboard',
|
|
114
|
+
title: 'Main switchboard',
|
|
115
|
+
alertType: AlertType.LevelCritical,
|
|
116
|
+
alertTitle: 'Blackout on bus B',
|
|
117
|
+
alertDescription: 'Bus B supplied from the emergency generator',
|
|
118
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
119
|
+
readouts: [
|
|
120
|
+
{label: 'Bus A', unit: 'V', alerting: 440, normal: 440},
|
|
121
|
+
{label: 'Bus B', unit: 'V', alerting: 0, normal: 438},
|
|
122
|
+
],
|
|
123
|
+
initial: rectified(-240),
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'generator',
|
|
127
|
+
title: 'Generator 2',
|
|
128
|
+
alertType: AlertType.Alarm,
|
|
129
|
+
alertTitle: 'Generator 2 overload',
|
|
130
|
+
alertDescription: 'Load above 95 % of rated power',
|
|
131
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
132
|
+
readouts: [
|
|
133
|
+
{label: 'Load', unit: '%', alerting: 97, normal: 78},
|
|
134
|
+
{label: 'Output', unit: 'kW', alerting: 1940, normal: 1560},
|
|
135
|
+
],
|
|
136
|
+
initial: raised(-60),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'tank',
|
|
140
|
+
title: 'Service tank',
|
|
141
|
+
alertType: AlertType.Alarm,
|
|
142
|
+
alertTitle: 'Service tank level low',
|
|
143
|
+
alertDescription: 'Fuel level below 30 %',
|
|
144
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
145
|
+
readouts: [
|
|
146
|
+
{label: 'Level', unit: '%', alerting: 27, normal: 34},
|
|
147
|
+
{label: 'Consumption', unit: 'L/h', alerting: 210, normal: 210},
|
|
148
|
+
],
|
|
149
|
+
initial: rectified(-420),
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: 'ballast',
|
|
153
|
+
title: 'Ballast pump 1',
|
|
154
|
+
alertType: AlertType.Alarm,
|
|
155
|
+
alertTitle: 'Ballast pump 1 tripped',
|
|
156
|
+
alertDescription: 'Motor overcurrent protection released',
|
|
157
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
158
|
+
readouts: [
|
|
159
|
+
{label: 'Motor current', unit: 'A', alerting: 0, normal: 42},
|
|
160
|
+
{label: 'Flow', unit: 'm³/h', alerting: 0, normal: 180},
|
|
161
|
+
],
|
|
162
|
+
initial: acknowledged(-300),
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: 'cooling',
|
|
166
|
+
title: 'Main engine',
|
|
167
|
+
alertType: AlertType.Warning,
|
|
168
|
+
alertTitle: 'Cooling water temperature high',
|
|
169
|
+
alertDescription: 'Jacket water outlet above 90 °C',
|
|
170
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
171
|
+
readouts: [
|
|
172
|
+
{label: 'Cooling water', unit: '°C', alerting: 92, normal: 84},
|
|
173
|
+
{label: 'Speed', unit: 'rpm', alerting: 84, normal: 84},
|
|
174
|
+
],
|
|
175
|
+
initial: raised(-150),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: 'bilge',
|
|
179
|
+
title: 'Bilge',
|
|
180
|
+
alertType: AlertType.Warning,
|
|
181
|
+
alertTitle: 'Bilge level high',
|
|
182
|
+
alertDescription: 'Engine room bilge well 2',
|
|
183
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
184
|
+
readouts: [
|
|
185
|
+
{label: 'Well 2 level', unit: '%', alerting: 71, normal: 12},
|
|
186
|
+
{label: 'Well 3 level', unit: '%', alerting: 9, normal: 9},
|
|
187
|
+
],
|
|
188
|
+
initial: rectified(-380),
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'steering',
|
|
192
|
+
title: 'Steering gear',
|
|
193
|
+
alertType: AlertType.Warning,
|
|
194
|
+
alertTitle: 'Steering pump 2 oil level low',
|
|
195
|
+
alertDescription: 'Hydraulic oil below the refill mark',
|
|
196
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
197
|
+
readouts: [
|
|
198
|
+
{label: 'Pump 2 oil level', unit: '%', alerting: 18, normal: 64},
|
|
199
|
+
{label: 'Rudder', unit: '°', alerting: 3, normal: 3},
|
|
200
|
+
],
|
|
201
|
+
initial: QUIET,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'gnss',
|
|
205
|
+
title: 'GNSS',
|
|
206
|
+
alertType: AlertType.Caution,
|
|
207
|
+
alertTitle: 'GNSS 2 signal degraded',
|
|
208
|
+
alertDescription: 'Position accuracy reduced',
|
|
209
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
210
|
+
readouts: [
|
|
211
|
+
{label: 'Satellites', unit: 'sats', alerting: 6, normal: 14},
|
|
212
|
+
{label: 'Accuracy', unit: 'm', alerting: 12, normal: 2},
|
|
213
|
+
],
|
|
214
|
+
initial: raised(-40),
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: 'freshwater',
|
|
218
|
+
title: 'Fresh water',
|
|
219
|
+
alertType: AlertType.Caution,
|
|
220
|
+
alertTitle: 'Fresh water pressure low',
|
|
221
|
+
alertDescription: 'Hydrophore pressure below 300 kPa',
|
|
222
|
+
frameType: ObcAlertFrameType.SmallSideFlip,
|
|
223
|
+
readouts: [
|
|
224
|
+
{label: 'Pressure', unit: 'kPa', alerting: 280, normal: 390},
|
|
225
|
+
{label: 'Tank level', unit: '%', alerting: 72, normal: 72},
|
|
226
|
+
],
|
|
227
|
+
initial: rectified(-200),
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
const PANEL_BY_ID = new Map(PANELS.map((panel) => [panel.id, panel]));
|
|
232
|
+
|
|
233
|
+
const TIMELINE: TimelineStep[] = [
|
|
234
|
+
{at: 5, panel: 'steering', change: ScenarioChange.Raise},
|
|
235
|
+
{at: 10, panel: 'generator', change: ScenarioChange.Rectify},
|
|
236
|
+
{at: 15, panel: 'gnss', change: ScenarioChange.Rectify},
|
|
237
|
+
{at: 20, panel: 'cooling', change: ScenarioChange.Rectify},
|
|
238
|
+
{at: 25, panel: 'freshwater', change: ScenarioChange.Clear},
|
|
239
|
+
{at: 30, panel: 'gnss', change: ScenarioChange.Clear},
|
|
240
|
+
{at: 35, panel: 'ballast', change: ScenarioChange.Rectify},
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
const TIMELINE_LENGTH_S = 40;
|
|
244
|
+
|
|
245
|
+
const CHANGE_LABEL: Record<ScenarioChange, string> = {
|
|
246
|
+
[ScenarioChange.Raise]: 'raises an alert',
|
|
247
|
+
[ScenarioChange.Rectify]: 'rectifies',
|
|
248
|
+
[ScenarioChange.Clear]: 'clears',
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const START_UTC_MS = Date.UTC(2026, 8, 14, 9, 41, 0);
|
|
252
|
+
|
|
253
|
+
function clockDate(second: number): string {
|
|
254
|
+
return new Date(START_UTC_MS + second * 1000).toISOString();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function clockTime(second: number): string {
|
|
258
|
+
return clockDate(second).slice(11, 19);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function elapsedTime(seconds: number): string {
|
|
262
|
+
const minutes = String(Math.floor(seconds / 60)).padStart(2, '0');
|
|
263
|
+
return `${minutes}:${String(seconds % 60).padStart(2, '0')}`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function initialAlerts(): Record<string, AlertState> {
|
|
267
|
+
return Object.fromEntries(
|
|
268
|
+
PANELS.map((panel) => [panel.id, {...panel.initial}])
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Acknowledged and rectified alerts are gone, so they have no frame. */
|
|
273
|
+
function frameMode(alert: AlertState): ObcAlertFrameMode | undefined {
|
|
274
|
+
if (!alert.present) {
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
if (!alert.active) {
|
|
278
|
+
return ObcAlertFrameMode.unackedRectified;
|
|
279
|
+
}
|
|
280
|
+
return alert.acknowledged
|
|
281
|
+
? ObcAlertFrameMode.ackedActive
|
|
282
|
+
: ObcAlertFrameMode.unackedActive;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function needsAcknowledgement(
|
|
286
|
+
panel: ScenarioPanel,
|
|
287
|
+
alert: AlertState
|
|
288
|
+
): boolean {
|
|
289
|
+
return (
|
|
290
|
+
alert.present &&
|
|
291
|
+
!alert.acknowledged &&
|
|
292
|
+
requiresAcknowledgement(panel.alertType)
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function menuStatus(
|
|
297
|
+
panel: ScenarioPanel,
|
|
298
|
+
alert: AlertState
|
|
299
|
+
): ObcAlertMenuItemStatus {
|
|
300
|
+
if (!requiresAcknowledgement(panel.alertType)) {
|
|
301
|
+
return ObcAlertMenuItemStatus.Caution;
|
|
302
|
+
}
|
|
303
|
+
return alert.acknowledged
|
|
304
|
+
? ObcAlertMenuItemStatus.Acknowledged
|
|
305
|
+
: ObcAlertMenuItemStatus.Unacknowledged;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* `<alert-flash-scenario-story>` – Live alert scenario for judging the flash
|
|
310
|
+
* tempos and frame effects together.
|
|
311
|
+
*
|
|
312
|
+
* Ten panels in alert frames, the top bar message and alert button, and the
|
|
313
|
+
* alert menu read one alert state. A 40-second timeline raises, rectifies and
|
|
314
|
+
* clears alerts, then restarts; ACK in the top bar or the menu acknowledges.
|
|
315
|
+
*
|
|
316
|
+
* @property flashEffect - Flash effect of every frame in the scenario.
|
|
317
|
+
* @property runTimeline - Advances the timeline once a second; off holds the
|
|
318
|
+
* current state.
|
|
319
|
+
* @property showAlertMenu - Shows the alert menu beside the panels; the alert
|
|
320
|
+
* button toggles it.
|
|
321
|
+
*/
|
|
322
|
+
class AlertFlashScenarioStory extends LitElement {
|
|
323
|
+
@property({type: String}) flashEffect = ObcAlertFrameFlashEffect.Outline;
|
|
324
|
+
@property({type: Boolean, attribute: false}) runTimeline = true;
|
|
325
|
+
@property({type: Boolean, attribute: false}) showAlertMenu = true;
|
|
326
|
+
|
|
327
|
+
@state() private second = 0;
|
|
328
|
+
@state() private alerts = initialAlerts();
|
|
329
|
+
|
|
330
|
+
private timer?: ReturnType<typeof setInterval>;
|
|
331
|
+
|
|
332
|
+
override createRenderRoot() {
|
|
333
|
+
return this;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
override connectedCallback() {
|
|
337
|
+
super.connectedCallback();
|
|
338
|
+
this.syncTimer();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
override disconnectedCallback() {
|
|
342
|
+
super.disconnectedCallback();
|
|
343
|
+
this.syncTimer();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
override updated() {
|
|
347
|
+
this.syncTimer();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
tick() {
|
|
351
|
+
const next = this.second + 1;
|
|
352
|
+
if (next >= TIMELINE_LENGTH_S) {
|
|
353
|
+
this.restart();
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this.second = next;
|
|
357
|
+
for (const step of TIMELINE.filter((s) => s.at === next)) {
|
|
358
|
+
this.apply(step);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
restart() {
|
|
363
|
+
this.second = 0;
|
|
364
|
+
this.alerts = initialAlerts();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
acknowledge(id: string) {
|
|
368
|
+
const alert = this.alerts[id];
|
|
369
|
+
this.alerts = {
|
|
370
|
+
...this.alerts,
|
|
371
|
+
[id]: alert.active
|
|
372
|
+
? {...alert, acknowledged: true}
|
|
373
|
+
: {...alert, present: false},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private apply({panel, change}: TimelineStep) {
|
|
378
|
+
const alert = this.alerts[panel];
|
|
379
|
+
let next: AlertState;
|
|
380
|
+
switch (change) {
|
|
381
|
+
case ScenarioChange.Raise:
|
|
382
|
+
next = raised(this.second);
|
|
383
|
+
break;
|
|
384
|
+
case ScenarioChange.Rectify:
|
|
385
|
+
next = alert.acknowledged
|
|
386
|
+
? {...alert, present: false}
|
|
387
|
+
: {...alert, active: false};
|
|
388
|
+
break;
|
|
389
|
+
case ScenarioChange.Clear:
|
|
390
|
+
next = {...alert, present: false};
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
this.alerts = {...this.alerts, [panel]: next};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private syncTimer() {
|
|
397
|
+
const wanted = this.runTimeline && this.isConnected;
|
|
398
|
+
if (wanted && !this.timer) {
|
|
399
|
+
this.timer = setInterval(() => this.tick(), 1000);
|
|
400
|
+
} else if (!wanted && this.timer) {
|
|
401
|
+
clearInterval(this.timer);
|
|
402
|
+
this.timer = undefined;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** Pending acknowledgements first, then by severity, newest first. */
|
|
407
|
+
private visiblePanels(): ScenarioPanel[] {
|
|
408
|
+
const rank = (panel: ScenarioPanel) =>
|
|
409
|
+
ALERT_SEVERITY_PRIORITY.indexOf(panel.alertType);
|
|
410
|
+
const pending = (panel: ScenarioPanel) =>
|
|
411
|
+
needsAcknowledgement(panel, this.alerts[panel.id]);
|
|
412
|
+
return PANELS.filter((panel) => this.alerts[panel.id].present).sort(
|
|
413
|
+
(a, b) =>
|
|
414
|
+
Number(pending(b)) - Number(pending(a)) ||
|
|
415
|
+
rank(a) - rank(b) ||
|
|
416
|
+
this.alerts[b.id].raisedAt - this.alerts[a.id].raisedAt
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private renderIcon(panel: ScenarioPanel, slot: string): TemplateResult {
|
|
421
|
+
const alert = this.alerts[panel.id];
|
|
422
|
+
return html`<obc-alert-icon
|
|
423
|
+
slot=${slot}
|
|
424
|
+
.alertType=${panel.alertType}
|
|
425
|
+
.active=${alert.active}
|
|
426
|
+
.acknowledged=${alert.acknowledged}
|
|
427
|
+
></obc-alert-icon>`;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private renderMessage(top: ScenarioPanel | undefined): TemplateResult {
|
|
431
|
+
if (!top) {
|
|
432
|
+
return html`<obc-topbar-message-item
|
|
433
|
+
slot="alerts"
|
|
434
|
+
.type=${ObcTopbarMessageItemType.Inactive}
|
|
435
|
+
></obc-topbar-message-item>`;
|
|
436
|
+
}
|
|
437
|
+
const alert = this.alerts[top.id];
|
|
438
|
+
return html`<obc-topbar-message-item
|
|
439
|
+
slot="alerts"
|
|
440
|
+
.type=${needsAcknowledgement(top, alert)
|
|
441
|
+
? ObcTopbarMessageItemType.WithButton
|
|
442
|
+
: ObcTopbarMessageItemType.Simple}
|
|
443
|
+
@action-click=${() => this.acknowledge(top.id)}
|
|
444
|
+
>
|
|
445
|
+
${this.renderIcon(top, 'primary-icon')}
|
|
446
|
+
<div slot="title">${top.alertTitle}</div>
|
|
447
|
+
<div slot="description">${top.alertDescription}</div>
|
|
448
|
+
<div slot="time">${clockTime(alert.raisedAt)}</div>
|
|
449
|
+
<div slot="action-text">ACK</div>
|
|
450
|
+
</obc-topbar-message-item>`;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private renderTopBar(visible: ScenarioPanel[]): TemplateResult {
|
|
454
|
+
const top = visible[0];
|
|
455
|
+
return html`<obc-top-bar
|
|
456
|
+
.appTitle=${'Machinery'}
|
|
457
|
+
.pageName=${'Alert flash scenario'}
|
|
458
|
+
.showClock=${true}
|
|
459
|
+
>
|
|
460
|
+
${this.renderMessage(top)}
|
|
461
|
+
<obc-alert-button
|
|
462
|
+
slot="alerts"
|
|
463
|
+
.nAlerts=${visible.length}
|
|
464
|
+
.alertType=${top?.alertType}
|
|
465
|
+
.counter=${true}
|
|
466
|
+
.blinking=${visible.some((panel) =>
|
|
467
|
+
needsAcknowledgement(panel, this.alerts[panel.id])
|
|
468
|
+
)}
|
|
469
|
+
@click-alert=${() => (this.showAlertMenu = !this.showAlertMenu)}
|
|
470
|
+
></obc-alert-button>
|
|
471
|
+
<obc-clock slot="clock" .date=${clockDate(this.second)}></obc-clock>
|
|
472
|
+
</obc-top-bar>`;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private renderPanel(panel: ScenarioPanel): TemplateResult {
|
|
476
|
+
const alert = this.alerts[panel.id];
|
|
477
|
+
const mode = frameMode(alert);
|
|
478
|
+
const alerting = alert.present && alert.active;
|
|
479
|
+
const card = html`<obc-card>
|
|
480
|
+
<span slot="title">${panel.title}</span>
|
|
481
|
+
${panel.readouts.map(
|
|
482
|
+
(readout) =>
|
|
483
|
+
html`<obc-readout-list-item
|
|
484
|
+
.label=${readout.label}
|
|
485
|
+
.unit=${readout.unit}
|
|
486
|
+
.value=${alerting ? readout.alerting : readout.normal}
|
|
487
|
+
></obc-readout-list-item>`
|
|
488
|
+
)}
|
|
489
|
+
</obc-card>`;
|
|
490
|
+
if (!mode) {
|
|
491
|
+
return html`<div>${card}</div>`;
|
|
492
|
+
}
|
|
493
|
+
return html`<obc-alert-frame
|
|
494
|
+
.type=${panel.frameType}
|
|
495
|
+
.status=${panel.alertType}
|
|
496
|
+
.mode=${mode}
|
|
497
|
+
.flashEffect=${this.flashEffect}
|
|
498
|
+
.wrapContent=${true}
|
|
499
|
+
.fullWidth=${true}
|
|
500
|
+
>
|
|
501
|
+
${card}
|
|
502
|
+
${panel.frameType === ObcAlertFrameType.BottomFlip
|
|
503
|
+
? html`<div slot="label">${panel.alertTitle}</div>
|
|
504
|
+
<div slot="timer">
|
|
505
|
+
${elapsedTime(this.second - alert.raisedAt)}
|
|
506
|
+
</div>`
|
|
507
|
+
: nothing}
|
|
508
|
+
</obc-alert-frame>`;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
private renderMenu(visible: ScenarioPanel[]): TemplateResult {
|
|
512
|
+
const pending = visible.filter((panel) =>
|
|
513
|
+
needsAcknowledgement(panel, this.alerts[panel.id])
|
|
514
|
+
);
|
|
515
|
+
return html`<obc-alert-menu
|
|
516
|
+
.hasShelved=${false}
|
|
517
|
+
.showSilenceButton=${false}
|
|
518
|
+
.showAlertListButton=${false}
|
|
519
|
+
.canAckAll=${pending.length > 0}
|
|
520
|
+
@ack-all-visible-click=${() =>
|
|
521
|
+
pending.forEach((panel) => this.acknowledge(panel.id))}
|
|
522
|
+
>
|
|
523
|
+
${repeat(
|
|
524
|
+
visible,
|
|
525
|
+
(panel) => panel.id,
|
|
526
|
+
(panel) =>
|
|
527
|
+
html`<obc-alert-menu-item
|
|
528
|
+
.status=${menuStatus(panel, this.alerts[panel.id])}
|
|
529
|
+
.title=${panel.alertTitle}
|
|
530
|
+
.description=${panel.alertDescription}
|
|
531
|
+
.time=${clockTime(this.alerts[panel.id].raisedAt)}
|
|
532
|
+
@ack-click=${() => this.acknowledge(panel.id)}
|
|
533
|
+
>
|
|
534
|
+
${this.renderIcon(panel, 'alert-icon')}
|
|
535
|
+
</obc-alert-menu-item>`
|
|
536
|
+
)}
|
|
537
|
+
</obc-alert-menu>`;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
private renderStatus(): TemplateResult {
|
|
541
|
+
const next = TIMELINE.find((step) => step.at > this.second);
|
|
542
|
+
const upcoming = next
|
|
543
|
+
? `${PANEL_BY_ID.get(next.panel)?.title} ${CHANGE_LABEL[next.change]} at ${next.at} s`
|
|
544
|
+
: `restarts at ${TIMELINE_LENGTH_S} s`;
|
|
545
|
+
return html`<div class="afs-status">
|
|
546
|
+
<span>
|
|
547
|
+
${this.runTimeline ? 'Timeline' : 'Timeline paused at'} ${this.second} s
|
|
548
|
+
of ${TIMELINE_LENGTH_S} s · next: ${upcoming}
|
|
549
|
+
</span>
|
|
550
|
+
<obc-button .variant=${ButtonVariant.flat} @click=${() => this.restart()}
|
|
551
|
+
>Restart</obc-button
|
|
552
|
+
>
|
|
553
|
+
</div>`;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
override render() {
|
|
557
|
+
const visible = this.visiblePanels();
|
|
558
|
+
return html`
|
|
559
|
+
<style>
|
|
560
|
+
.afs {
|
|
561
|
+
display: flex;
|
|
562
|
+
flex-direction: column;
|
|
563
|
+
height: 100vh;
|
|
564
|
+
background: var(--container-background-color);
|
|
565
|
+
font-family: var(--global-typography-font-family);
|
|
566
|
+
}
|
|
567
|
+
.afs-body {
|
|
568
|
+
display: flex;
|
|
569
|
+
flex: 1;
|
|
570
|
+
gap: 16px;
|
|
571
|
+
/* lets the grid scroll inside the viewport instead of growing the page */
|
|
572
|
+
min-height: 0;
|
|
573
|
+
padding: 16px;
|
|
574
|
+
}
|
|
575
|
+
.afs-main {
|
|
576
|
+
display: flex;
|
|
577
|
+
flex: 1;
|
|
578
|
+
flex-direction: column;
|
|
579
|
+
gap: 8px;
|
|
580
|
+
min-width: 0;
|
|
581
|
+
}
|
|
582
|
+
.afs-status {
|
|
583
|
+
display: flex;
|
|
584
|
+
align-items: center;
|
|
585
|
+
justify-content: space-between;
|
|
586
|
+
gap: 16px;
|
|
587
|
+
font-size: var(--global-typography-ui-label-font-size);
|
|
588
|
+
line-height: var(--global-typography-ui-label-line-height);
|
|
589
|
+
color: var(--element-neutral-color);
|
|
590
|
+
}
|
|
591
|
+
.afs-grid {
|
|
592
|
+
display: grid;
|
|
593
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
594
|
+
/* the side and bottom flaps sit outside their frames */
|
|
595
|
+
gap: 48px 40px;
|
|
596
|
+
align-content: start;
|
|
597
|
+
overflow: auto;
|
|
598
|
+
padding: 8px 32px 40px 8px;
|
|
599
|
+
}
|
|
600
|
+
</style>
|
|
601
|
+
<div class="afs">
|
|
602
|
+
${this.renderTopBar(visible)}
|
|
603
|
+
<div class="afs-body">
|
|
604
|
+
<div class="afs-main">
|
|
605
|
+
${this.renderStatus()}
|
|
606
|
+
<div class="afs-grid">
|
|
607
|
+
${PANELS.map((panel) => this.renderPanel(panel))}
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
${this.showAlertMenu ? this.renderMenu(visible) : nothing}
|
|
611
|
+
</div>
|
|
612
|
+
</div>
|
|
613
|
+
`;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (!customElements.get('alert-flash-scenario-story')) {
|
|
618
|
+
customElements.define('alert-flash-scenario-story', AlertFlashScenarioStory);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
interface ScenarioArgs {
|
|
622
|
+
flashEffect: ObcAlertFrameFlashEffect;
|
|
623
|
+
runTimeline: boolean;
|
|
624
|
+
showAlertMenu: boolean;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const meta: Meta<ScenarioArgs> = {
|
|
628
|
+
title: 'UI Components/Message and Alerts/Alert Flash Scenario',
|
|
629
|
+
tags: ['skip-test'],
|
|
630
|
+
parameters: {
|
|
631
|
+
layout: 'fullscreen',
|
|
632
|
+
docs: {
|
|
633
|
+
description: {
|
|
634
|
+
component:
|
|
635
|
+
'A live scenario for comparing the frame flash effects in context. ' +
|
|
636
|
+
'At the start every alert level shows a solid (active) and a dashed ' +
|
|
637
|
+
'(rectified) frame; a 40-second timeline then raises, rectifies and ' +
|
|
638
|
+
'clears alerts and restarts. ACK in the top bar message or the alert ' +
|
|
639
|
+
'menu acknowledges. `flashEffect` switches every frame between the ' +
|
|
640
|
+
'instant step and the eased growth; the icons and the alert button ' +
|
|
641
|
+
'flash on the same tempo table either way.',
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
args: {
|
|
646
|
+
flashEffect: ObcAlertFrameFlashEffect.Outline,
|
|
647
|
+
runTimeline: true,
|
|
648
|
+
showAlertMenu: true,
|
|
649
|
+
},
|
|
650
|
+
argTypes: {
|
|
651
|
+
flashEffect: {
|
|
652
|
+
options: Object.values(ObcAlertFrameFlashEffect),
|
|
653
|
+
control: {type: 'radio'},
|
|
654
|
+
},
|
|
655
|
+
runTimeline: {control: {type: 'boolean'}},
|
|
656
|
+
showAlertMenu: {control: {type: 'boolean'}},
|
|
657
|
+
},
|
|
658
|
+
render: (args) =>
|
|
659
|
+
html`<alert-flash-scenario-story
|
|
660
|
+
.flashEffect=${args.flashEffect}
|
|
661
|
+
.runTimeline=${args.runTimeline}
|
|
662
|
+
.showAlertMenu=${args.showAlertMenu}
|
|
663
|
+
></alert-flash-scenario-story>`,
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
export default meta;
|
|
667
|
+
type Story = StoryObj<ScenarioArgs>;
|
|
668
|
+
|
|
669
|
+
export const Scenario: Story = {};
|