@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.55 → 2.0.0-next.57

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.
Files changed (41) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +395 -123
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +326 -2
  4. package/dist/automation/automation-tank/automation-tank.d.ts +11 -0
  5. package/dist/automation/automation-tank/automation-tank.d.ts.map +1 -1
  6. package/dist/automation/automation-tank/automation-tank.js.map +1 -1
  7. package/dist/navigation-instruments/compass-sector/compass-sector.css.js +12 -0
  8. package/dist/navigation-instruments/compass-sector/compass-sector.css.js.map +1 -1
  9. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts +23 -0
  10. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts.map +1 -1
  11. package/dist/navigation-instruments/compass-sector/compass-sector.js +47 -0
  12. package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
  13. package/dist/navigation-instruments/pitch/pitch.d.ts +37 -0
  14. package/dist/navigation-instruments/pitch/pitch.d.ts.map +1 -1
  15. package/dist/navigation-instruments/pitch/pitch.js +130 -62
  16. package/dist/navigation-instruments/pitch/pitch.js.map +1 -1
  17. package/dist/navigation-instruments/pitch-roll/pitch-roll.d.ts +7 -0
  18. package/dist/navigation-instruments/pitch-roll/pitch-roll.d.ts.map +1 -1
  19. package/dist/navigation-instruments/pitch-roll/pitch-roll.js +58 -2
  20. package/dist/navigation-instruments/pitch-roll/pitch-roll.js.map +1 -1
  21. package/dist/navigation-instruments/roll/roll.d.ts +37 -0
  22. package/dist/navigation-instruments/roll/roll.d.ts.map +1 -1
  23. package/dist/navigation-instruments/roll/roll.js +119 -63
  24. package/dist/navigation-instruments/roll/roll.js.map +1 -1
  25. package/dist/navigation-instruments/rot-sector/rot-sector.d.ts +15 -0
  26. package/dist/navigation-instruments/rot-sector/rot-sector.d.ts.map +1 -1
  27. package/dist/navigation-instruments/rot-sector/rot-sector.js +53 -1
  28. package/dist/navigation-instruments/rot-sector/rot-sector.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/automation/automation-tank/automation-tank.ts +11 -0
  31. package/src/navigation-instruments/compass-sector/compass-sector.css +12 -0
  32. package/src/navigation-instruments/compass-sector/compass-sector.stories.ts +7 -0
  33. package/src/navigation-instruments/compass-sector/compass-sector.ts +52 -0
  34. package/src/navigation-instruments/pitch/pitch.stories.ts +23 -1
  35. package/src/navigation-instruments/pitch/pitch.ts +184 -86
  36. package/src/navigation-instruments/pitch-roll/pitch-roll.stories.ts +13 -0
  37. package/src/navigation-instruments/pitch-roll/pitch-roll.ts +80 -13
  38. package/src/navigation-instruments/roll/roll.stories.ts +23 -1
  39. package/src/navigation-instruments/roll/roll.ts +171 -87
  40. package/src/navigation-instruments/rot-sector/rot-sector.stories.ts +15 -0
  41. package/src/navigation-instruments/rot-sector/rot-sector.ts +59 -1
@@ -8,7 +8,11 @@ import {
8
8
  OUTER_RING_RADIUS,
9
9
  innerRingRadiusFor,
10
10
  vesselImages,
11
+ type WatchArea,
11
12
  } from '../watch/watch.js';
13
+ import '../readout/readout.js';
14
+ import {ReadoutDirection, ReadoutVariant} from '../readout/readout.js';
15
+ import {Priority} from '../types.js';
12
16
  import {TickmarkType} from '../watch/tickmark.js';
13
17
  import {AdviceState, AdviceType, AngleAdviceRaw} from '../watch/advice.js';
14
18
  import {customElement} from '../../decorator.js';
@@ -23,6 +27,23 @@ const watchRadius = OUTER_RING_RADIUS;
23
27
  /** Half-side of the centre overlay viewBox in SVG units. */
24
28
  const CENTRE_HALF = 200;
25
29
 
30
+ export enum ObcPitchType {
31
+ /** Single arc scale on the right (default). */
32
+ singleScale = 'single-scale',
33
+ /** Right scale duplicated to the opposite (left) arc as well. */
34
+ dualScale = 'dual-scale',
35
+ }
36
+
37
+ /**
38
+ * `<obc-pitch>` — Pitch (trim) indicator with a side arc scale.
39
+ *
40
+ * Shows `pitch` against a watch arc centred on the right, with an average-pitch
41
+ * band and a rotating indicator. Supports an optional opposite-side scale
42
+ * (`dual-scale`), a centre readout (`hasReadout`), and a `regular`/`enhanced`
43
+ * palette.
44
+ *
45
+ * @element obc-pitch
46
+ */
26
47
  @customElement('obc-pitch')
27
48
  export class ObcPitch extends LitElement {
28
49
  @property({type: Number}) pitch = 0;
@@ -32,6 +53,22 @@ export class ObcPitch extends LitElement {
32
53
  @property({type: Number}) maxPitchAdvice: number | undefined = undefined;
33
54
  @property({type: Boolean}) triggerPitchAdvice = false;
34
55
  @property({type: Boolean}) zoomToFitArc: boolean = false;
56
+ /**
57
+ * When `true`, the centre shows an `<obc-readout>` with the pitch value
58
+ * (label `Pitch`, unit `DEG`) instead of the horizon line, rotating indicator
59
+ * and vessel. Default `false`.
60
+ */
61
+ @property({type: Boolean}) hasReadout: boolean = false;
62
+ /**
63
+ * `single-scale` shows one arc on the right (default); `dual-scale` also
64
+ * shows the scale on the opposite (left) arc (the indicator's opposite end).
65
+ */
66
+ @property({type: String}) type: ObcPitchType = ObcPitchType.singleScale;
67
+ /**
68
+ * Colour palette for the scale fill / indicator and the readout value:
69
+ * `regular` (default) or `enhanced`.
70
+ */
71
+ @property({type: String}) priority: Priority = Priority.regular;
35
72
  /**
36
73
  * Half-extent of the watch arc in degrees. The arc spans `90° ± arcAngle`
37
74
  * and pitch values are placed at their true position within it. Default
@@ -47,6 +84,18 @@ export class ObcPitch extends LitElement {
47
84
 
48
85
  private _arcFrame: ZoomToFitArcFrame | undefined;
49
86
 
87
+ private get scaleFillColor(): string {
88
+ return this.priority === Priority.enhanced
89
+ ? 'var(--instrument-enhanced-tertiary-color)'
90
+ : 'var(--instrument-regular-tertiary-color)';
91
+ }
92
+
93
+ private get indicatorColor(): string {
94
+ return this.priority === Priority.enhanced
95
+ ? 'var(--instrument-enhanced-secondary-color)'
96
+ : 'var(--instrument-regular-secondary-color)';
97
+ }
98
+
50
99
  override render() {
51
100
  const arcAngle = normalizeArcAngle(this.arcAngle, 45);
52
101
  // Outer thin-ring complement endpoints. The arc band is centred at
@@ -55,7 +104,7 @@ export class ObcPitch extends LitElement {
55
104
  const x = watchRadius * Math.cos((arcAngle * Math.PI) / 180);
56
105
  const y = watchRadius * Math.sin((arcAngle * Math.PI) / 180);
57
106
 
58
- const areas = [
107
+ const areas: WatchArea[] = [
59
108
  {
60
109
  startAngle: 90 - arcAngle,
61
110
  endAngle: 90 + arcAngle,
@@ -98,107 +147,146 @@ export class ObcPitch extends LitElement {
98
147
  return html`
99
148
  <div class="container">
100
149
  <svg viewBox="${centreViewBox}">
101
- ${svg`
102
- <line
103
- x1="-${watchRadius}"
104
- y1="0"
105
- x2="${watchRadius}"
106
- y2="0"
107
- stroke="var(--instrument-frame-tertiary-color)"
108
- />
109
- <line
110
- x1="0"
111
- y1="0"
112
- x2="${watchRadius - 10}"
113
- y2="0"
114
- stroke="var(--instrument-enhanced-secondary-color)"
115
- transform="${needleTransform}"
116
- />
117
- <g
118
- style="transform: rotate(${this.pitch}deg) scale(${vesselScale}) translate(-80px, -80px);"
119
- >
120
- ${vesselImages[this.vesselImageSide]}
121
- </g>
122
- ${
123
- this.zoomToFitArc
124
- ? nothing
125
- : svg`
126
- <path
127
- d="M ${x} ${y} A ${watchRadius} ${watchRadius} 0 1 1 ${x} ${-y}"
128
- fill="none"
129
- stroke="var(--instrument-frame-tertiary-color)"
130
- />
131
- `
132
- }
133
- `}
150
+ ${this.hasReadout
151
+ ? nothing
152
+ : svg`
153
+ <line
154
+ x1="-${watchRadius}"
155
+ y1="0"
156
+ x2="${watchRadius}"
157
+ y2="0"
158
+ stroke="var(--instrument-frame-tertiary-color)"
159
+ />
160
+ <line
161
+ x1="0"
162
+ y1="0"
163
+ x2="${watchRadius - 10}"
164
+ y2="0"
165
+ stroke="${this.indicatorColor}"
166
+ transform="${needleTransform}"
167
+ />
168
+ <g
169
+ style="transform: rotate(${this.pitch}deg) scale(${vesselScale}) translate(-80px, -80px);"
170
+ >
171
+ ${this.zoomToFitArc ? vesselImages[this.vesselImageSide] : nothing}
172
+ </g>
173
+ `}
174
+ ${this.zoomToFitArc
175
+ ? nothing
176
+ : this.type === ObcPitchType.dualScale
177
+ ? svg`
178
+ <path
179
+ d="M ${x} ${-y} A ${watchRadius} ${watchRadius} 0 0 0 ${-x} ${-y}"
180
+ fill="none"
181
+ stroke="var(--instrument-frame-tertiary-color)"
182
+ />
183
+ <path
184
+ d="M ${x} ${y} A ${watchRadius} ${watchRadius} 0 0 1 ${-x} ${y}"
185
+ fill="none"
186
+ stroke="var(--instrument-frame-tertiary-color)"
187
+ />
188
+ `
189
+ : svg`
190
+ <path
191
+ d="M ${x} ${y} A ${watchRadius} ${watchRadius} 0 1 1 ${x} ${-y}"
192
+ fill="none"
193
+ stroke="var(--instrument-frame-tertiary-color)"
194
+ />
195
+ `}
134
196
  </svg>
135
- <obc-watch
136
- .watchCircleType=${WatchCircleType.double}
137
- .zoomToFitArc=${this.zoomToFitArc}
138
- .arcFrame=${this._arcFrame}
139
- .areas=${areas}
140
- .barAreas=${[
141
- {
142
- startAngle: 90 + this.minAvgPitch,
143
- endAngle: 90 + this.maxAvgPitch,
144
- fillColor: 'var(--instrument-enhanced-tertiary-color)',
145
- },
146
- ]}
147
- .needles=${[
148
- {
149
- angle: 90 + this.pitch,
150
- fillColor: 'var(--instrument-enhanced-secondary-color)',
151
- strokeColor: 'var(--border-silhouette-color)',
152
- },
153
- ]}
154
- .vessels=${this.zoomToFitArc
155
- ? []
156
- : [
157
- {
158
- size: VesselImageSize.large,
159
- vesselImage: this.vesselImageSide,
160
- transform: `rotate(${this.pitch}deg)`,
161
- },
162
- ]}
163
- .tickmarks=${[
164
- {
165
- angle: 90,
166
- type: TickmarkType.main,
167
- },
168
- ]}
169
- .advices=${this.advices}
170
- ></obc-watch>
197
+ ${this.renderScale(areas, false)}
198
+ ${this.type === ObcPitchType.dualScale
199
+ ? this.renderScale(areas, true)
200
+ : nothing}
201
+ ${this.hasReadout
202
+ ? html`<div class="readout">
203
+ <obc-readout
204
+ .variant=${ReadoutVariant.enhanced}
205
+ .direction=${ReadoutDirection.vertical}
206
+ .hasSetpoint=${false}
207
+ .hasAdvice=${false}
208
+ .value=${this.pitch}
209
+ .fractionDigits=${0}
210
+ .valuePriority=${this.priority}
211
+ label="Pitch"
212
+ unit="DEG"
213
+ ></obc-readout>
214
+ </div>`
215
+ : nothing}
171
216
  </div>
172
217
  `;
173
218
  }
174
219
 
220
+ // `opposite` rotates a second watch 180° onto the opposite (left) arc for
221
+ // dual-scale — a rotation (opposite end of the indicator), not a mirror. A
222
+ // separate watch keeps the zoomed `arcFrame` correct.
223
+ private renderScale(areas: WatchArea[], opposite: boolean) {
224
+ return html`
225
+ <obc-watch
226
+ class=${opposite ? 'scale-opposite' : nothing}
227
+ .priority=${this.priority}
228
+ .watchCircleType=${WatchCircleType.double}
229
+ .zoomToFitArc=${this.zoomToFitArc}
230
+ .arcFrame=${this._arcFrame}
231
+ tickmarksInside
232
+ .areas=${areas}
233
+ .barAreas=${[
234
+ {
235
+ startAngle: 90 + this.minAvgPitch,
236
+ endAngle: 90 + this.maxAvgPitch,
237
+ fillColor: this.scaleFillColor,
238
+ },
239
+ ]}
240
+ .needles=${[
241
+ {
242
+ angle: 90 + this.pitch,
243
+ fillColor: this.indicatorColor,
244
+ strokeColor: 'var(--border-silhouette-color)',
245
+ },
246
+ ]}
247
+ .vessels=${opposite || this.zoomToFitArc || this.hasReadout
248
+ ? []
249
+ : [
250
+ {
251
+ size: VesselImageSize.large,
252
+ vesselImage: this.vesselImageSide,
253
+ transform: `rotate(${this.pitch}deg)`,
254
+ },
255
+ ]}
256
+ .tickmarks=${[{angle: 90, type: TickmarkType.main}]}
257
+ .advices=${this.advices}
258
+ ></obc-watch>
259
+ `;
260
+ }
261
+
175
262
  private get advices(): AngleAdviceRaw[] {
176
- const advices = [];
177
- if (this.maxPitchAdvice !== undefined) {
178
- const arcAngle = normalizeArcAngle(this.arcAngle, 45);
179
- // Caution band fills the remainder of the arc out to a default 30°
180
- // caution range (clamped to the arc edge).
181
- const outer = Math.min(arcAngle, 30);
182
- const inner = Math.min(this.maxPitchAdvice, outer);
183
- const state = this.triggerPitchAdvice
184
- ? AdviceState.triggered
185
- : AdviceState.regular;
186
- advices.push({
263
+ if (this.maxPitchAdvice === undefined) {
264
+ return [];
265
+ }
266
+ const arcAngle = normalizeArcAngle(this.arcAngle, 45);
267
+ // Caution band fills the remainder of the arc out to a default 30° caution
268
+ // range (clamped to the arc edge).
269
+ const outer = Math.min(arcAngle, 30);
270
+ const inner = Math.min(this.maxPitchAdvice, outer);
271
+ const state = this.triggerPitchAdvice
272
+ ? AdviceState.triggered
273
+ : AdviceState.regular;
274
+ return [
275
+ {
187
276
  minAngle: 90 - outer,
188
277
  maxAngle: 90 - inner,
189
278
  type: AdviceType.caution,
190
279
  state: state,
191
280
  hideMinTickmark: true,
192
- });
193
- advices.push({
281
+ },
282
+ {
194
283
  minAngle: 90 + inner,
195
284
  maxAngle: 90 + outer,
196
285
  type: AdviceType.caution,
197
286
  state: state,
198
287
  hideMaxTickmark: true,
199
- });
200
- }
201
- return advices;
288
+ },
289
+ ];
202
290
  }
203
291
 
204
292
  static override styles = css`
@@ -219,6 +307,16 @@ export class ObcPitch extends LitElement {
219
307
  width: 100%;
220
308
  height: 100%;
221
309
  }
310
+
311
+ .readout {
312
+ display: flex;
313
+ align-items: center;
314
+ justify-content: center;
315
+ }
316
+
317
+ .scale-opposite {
318
+ transform: rotate(180deg);
319
+ }
222
320
  `;
223
321
  }
224
322
 
@@ -61,6 +61,19 @@ export const Primary: Story = {
61
61
  args: {},
62
62
  };
63
63
 
64
+ export const WithReadout: Story = {
65
+ args: {
66
+ hasReadout: true,
67
+ },
68
+ };
69
+
70
+ export const ZoomedInWithReadout: Story = {
71
+ args: {
72
+ zoomToFitArc: true,
73
+ hasReadout: true,
74
+ },
75
+ };
76
+
64
77
  export const Rov: Story = {
65
78
  args: {
66
79
  vesselImageSide: VesselImage.rovSideFaded,
@@ -14,6 +14,8 @@ import {TickmarkType} from '../watch/tickmark.js';
14
14
  import {AdviceState, AdviceType, AngleAdviceRaw} from '../watch/advice.js';
15
15
  import {customElement} from '../../decorator.js';
16
16
  import {Priority} from '../types.js';
17
+ import '../readout/readout.js';
18
+ import {ReadoutDirection, ReadoutVariant} from '../readout/readout.js';
17
19
  import {
18
20
  computeZoomToFitArcFrame,
19
21
  normalizeArcAngle,
@@ -65,6 +67,11 @@ export class ObcPitchRoll extends LitElement {
65
67
  PitchRollPriorityElement.pitch,
66
68
  PitchRollPriorityElement.roll,
67
69
  ];
70
+ /**
71
+ * When `true`, the centre shows two stacked `<obc-readout>`s (pitch above
72
+ * roll) instead of the vessel images. Default `false`.
73
+ */
74
+ @property({type: Boolean}) hasReadout: boolean = false;
68
75
  @property({type: Boolean}) zoomToFitArc: boolean = false;
69
76
  /**
70
77
  * Half-extent of each of the four watch arcs in degrees, measured from the
@@ -156,7 +163,9 @@ export class ObcPitchRoll extends LitElement {
156
163
  return html`
157
164
  <div class="container">
158
165
  <svg viewBox="${overlayViewBox}">
159
- ${svg`
166
+ ${this.hasReadout
167
+ ? nothing
168
+ : svg`
160
169
  <line
161
170
  x1="-150"
162
171
  y1="0"
@@ -179,10 +188,47 @@ export class ObcPitchRoll extends LitElement {
179
188
  ${this.zoomToFitArc
180
189
  ? this.renderZoomedArcs(pitchReq, rollReq)
181
190
  : this.renderFullWatch(areas)}
191
+ ${this.hasReadout
192
+ ? html`<div class="readout">
193
+ <div class="readout-group">
194
+ ${this.renderReadout(
195
+ this.pitch,
196
+ 'Pitch',
197
+ PitchRollPriorityElement.pitch
198
+ )}
199
+ <div class="readout-divider"></div>
200
+ ${this.renderReadout(
201
+ this.roll,
202
+ 'Roll',
203
+ PitchRollPriorityElement.roll
204
+ )}
205
+ </div>
206
+ </div>`
207
+ : nothing}
182
208
  </div>
183
209
  `;
184
210
  }
185
211
 
212
+ private renderReadout(
213
+ value: number,
214
+ label: string,
215
+ element: PitchRollPriorityElement
216
+ ) {
217
+ return html`
218
+ <obc-readout
219
+ .variant=${ReadoutVariant.enhanced}
220
+ .direction=${ReadoutDirection.vertical}
221
+ .hasSetpoint=${false}
222
+ .hasAdvice=${false}
223
+ .value=${value}
224
+ .fractionDigits=${0}
225
+ .valuePriority=${this.priorityFor(element)}
226
+ label=${label}
227
+ unit="DEG"
228
+ ></obc-readout>
229
+ `;
230
+ }
231
+
186
232
  /**
187
233
  * Zoomed-arc layer: four CSS-rotated `<obc-watch>` instances, each
188
234
  * containing a single arc rendered at the watch's natural top
@@ -579,18 +625,20 @@ export class ObcPitchRoll extends LitElement {
579
625
  strokeColor: 'var(--border-silhouette-color)',
580
626
  },
581
627
  ]}
582
- .vessels=${[
583
- {
584
- size: VesselImageSize.large,
585
- vesselImage: this.vesselImageSide,
586
- transform: `rotate(${this.pitch}deg)`,
587
- },
588
- {
589
- size: VesselImageSize.large,
590
- vesselImage: this.vesselImageFore,
591
- transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,
592
- },
593
- ]}
628
+ .vessels=${this.hasReadout
629
+ ? []
630
+ : [
631
+ {
632
+ size: VesselImageSize.large,
633
+ vesselImage: this.vesselImageSide,
634
+ transform: `rotate(${this.pitch}deg)`,
635
+ },
636
+ {
637
+ size: VesselImageSize.large,
638
+ vesselImage: this.vesselImageFore,
639
+ transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,
640
+ },
641
+ ]}
594
642
  .tickmarks=${[
595
643
  {angle: 0, type: TickmarkType.main},
596
644
  {angle: 90, type: TickmarkType.main},
@@ -697,6 +745,25 @@ export class ObcPitchRoll extends LitElement {
697
745
  width: 100%;
698
746
  height: 100%;
699
747
  }
748
+
749
+ .readout {
750
+ display: flex;
751
+ align-items: center;
752
+ justify-content: center;
753
+ }
754
+
755
+ .readout-group {
756
+ display: flex;
757
+ flex-direction: column;
758
+ align-items: center;
759
+ width: fit-content;
760
+ }
761
+
762
+ .readout-divider {
763
+ align-self: stretch;
764
+ height: 1px;
765
+ background: var(--border-divider-color);
766
+ }
700
767
  `;
701
768
  }
702
769
 
@@ -1,6 +1,7 @@
1
1
  import type {Meta, StoryObj} from '@storybook/web-components-vite';
2
- import {ObcRoll} from './roll.js';
2
+ import {ObcRoll, ObcRollType} from './roll.js';
3
3
  import './roll.js';
4
+ import {Priority} from '../types.js';
4
5
  import {widthDecorator} from '../../storybook-util.js';
5
6
  import {VesselImage} from '../watch/vessel.js';
6
7
  import {foreVessels} from '../watch/vessels/storybook-helper.js';
@@ -26,6 +27,8 @@ const meta: Meta<typeof ObcRoll> = {
26
27
  control: 'select',
27
28
  options: foreVessels,
28
29
  },
30
+ type: {control: 'select', options: Object.values(ObcRollType)},
31
+ priority: {control: 'select', options: Object.values(Priority)},
29
32
  },
30
33
  decorators: [widthDecorator],
31
34
  } satisfies Meta<ObcRoll>;
@@ -59,3 +62,22 @@ export const ZoomedInNarrow: Story = {
59
62
  maxRollAdvice: 10,
60
63
  },
61
64
  };
65
+
66
+ export const WithReadout: Story = {
67
+ args: {
68
+ hasReadout: true,
69
+ },
70
+ };
71
+
72
+ export const DualScale: Story = {
73
+ args: {
74
+ type: ObcRollType.dualScale,
75
+ hasReadout: true,
76
+ },
77
+ };
78
+
79
+ export const Enhanced: Story = {
80
+ args: {
81
+ priority: Priority.enhanced,
82
+ },
83
+ };