@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.56 → 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 (37) 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 +284 -2
  4. package/dist/navigation-instruments/compass-sector/compass-sector.css.js +12 -0
  5. package/dist/navigation-instruments/compass-sector/compass-sector.css.js.map +1 -1
  6. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts +23 -0
  7. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts.map +1 -1
  8. package/dist/navigation-instruments/compass-sector/compass-sector.js +47 -0
  9. package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
  10. package/dist/navigation-instruments/pitch/pitch.d.ts +37 -0
  11. package/dist/navigation-instruments/pitch/pitch.d.ts.map +1 -1
  12. package/dist/navigation-instruments/pitch/pitch.js +130 -62
  13. package/dist/navigation-instruments/pitch/pitch.js.map +1 -1
  14. package/dist/navigation-instruments/pitch-roll/pitch-roll.d.ts +7 -0
  15. package/dist/navigation-instruments/pitch-roll/pitch-roll.d.ts.map +1 -1
  16. package/dist/navigation-instruments/pitch-roll/pitch-roll.js +58 -2
  17. package/dist/navigation-instruments/pitch-roll/pitch-roll.js.map +1 -1
  18. package/dist/navigation-instruments/roll/roll.d.ts +37 -0
  19. package/dist/navigation-instruments/roll/roll.d.ts.map +1 -1
  20. package/dist/navigation-instruments/roll/roll.js +119 -63
  21. package/dist/navigation-instruments/roll/roll.js.map +1 -1
  22. package/dist/navigation-instruments/rot-sector/rot-sector.d.ts +15 -0
  23. package/dist/navigation-instruments/rot-sector/rot-sector.d.ts.map +1 -1
  24. package/dist/navigation-instruments/rot-sector/rot-sector.js +53 -1
  25. package/dist/navigation-instruments/rot-sector/rot-sector.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/navigation-instruments/compass-sector/compass-sector.css +12 -0
  28. package/src/navigation-instruments/compass-sector/compass-sector.stories.ts +7 -0
  29. package/src/navigation-instruments/compass-sector/compass-sector.ts +52 -0
  30. package/src/navigation-instruments/pitch/pitch.stories.ts +23 -1
  31. package/src/navigation-instruments/pitch/pitch.ts +184 -86
  32. package/src/navigation-instruments/pitch-roll/pitch-roll.stories.ts +13 -0
  33. package/src/navigation-instruments/pitch-roll/pitch-roll.ts +80 -13
  34. package/src/navigation-instruments/roll/roll.stories.ts +23 -1
  35. package/src/navigation-instruments/roll/roll.ts +171 -87
  36. package/src/navigation-instruments/rot-sector/rot-sector.stories.ts +15 -0
  37. package/src/navigation-instruments/rot-sector/rot-sector.ts +59 -1
@@ -8,7 +8,11 @@ import {
8
8
  WatchCircleType,
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 ObcRollType {
31
+ /** Single arc scale at the bottom (default). */
32
+ singleScale = 'single-scale',
33
+ /** Bottom scale duplicated to the top as well. */
34
+ dualScale = 'dual-scale',
35
+ }
36
+
37
+ /**
38
+ * `<obc-roll>` — Roll (heel) indicator with a bottom arc scale.
39
+ *
40
+ * Shows `roll` against a watch arc centred at the bottom, with an average-roll
41
+ * band and a rotating indicator. Supports an optional top scale (`dual-scale`),
42
+ * a centre readout (`hasReadout`), and a `regular`/`enhanced` palette. See the
43
+ * individual properties for details.
44
+ *
45
+ * @element obc-roll
46
+ */
26
47
  @customElement('obc-roll')
27
48
  export class ObcRoll extends LitElement {
28
49
  @property({type: Number}) roll = 0;
@@ -33,6 +54,22 @@ export class ObcRoll extends LitElement {
33
54
  @property({type: Number}) maxRollAdvice: number | undefined = undefined;
34
55
  @property({type: Boolean}) triggerRollAdvice = false;
35
56
  @property({type: Boolean}) zoomToFitArc: boolean = false;
57
+ /**
58
+ * When `true`, the centre shows an `<obc-readout>` with the roll value
59
+ * (label `Roll`, unit `DEG`) instead of the horizon line, rotating indicator
60
+ * and vessel. Default `false`.
61
+ */
62
+ @property({type: Boolean}) hasReadout: boolean = false;
63
+ /**
64
+ * `single-scale` shows one arc at the bottom (default); `dual-scale` also
65
+ * shows the scale on the top arc (the indicator's opposite end).
66
+ */
67
+ @property({type: String}) type: ObcRollType = ObcRollType.singleScale;
68
+ /**
69
+ * Colour palette for the scale fill / indicator and the readout value:
70
+ * `regular` (default) or `enhanced`.
71
+ */
72
+ @property({type: String}) priority: Priority = Priority.regular;
36
73
  /**
37
74
  * Half-extent of the watch arc in degrees. The arc spans `180° ± arcAngle`
38
75
  * and roll values are placed at their true position within it. Default
@@ -55,6 +92,18 @@ export class ObcRoll extends LitElement {
55
92
  return Math.max(0, Math.min(2, this.scaleForeImage));
56
93
  }
57
94
 
95
+ private get scaleFillColor(): string {
96
+ return this.priority === Priority.enhanced
97
+ ? 'var(--instrument-enhanced-tertiary-color)'
98
+ : 'var(--instrument-regular-tertiary-color)';
99
+ }
100
+
101
+ private get indicatorColor(): string {
102
+ return this.priority === Priority.enhanced
103
+ ? 'var(--instrument-enhanced-secondary-color)'
104
+ : 'var(--instrument-regular-secondary-color)';
105
+ }
106
+
58
107
  override render() {
59
108
  const arcAngle = normalizeArcAngle(this.arcAngle, 45);
60
109
  // Outer thin-ring complement endpoints. The arc band is centred at
@@ -63,7 +112,7 @@ export class ObcRoll extends LitElement {
63
112
  const x = watchRadius * Math.sin((arcAngle * Math.PI) / 180);
64
113
  const y = watchRadius * Math.cos((arcAngle * Math.PI) / 180);
65
114
 
66
- const areas = [
115
+ const areas: WatchArea[] = [
67
116
  {
68
117
  startAngle: 180 - arcAngle,
69
118
  endAngle: 180 + arcAngle,
@@ -106,108 +155,133 @@ export class ObcRoll extends LitElement {
106
155
  return html`
107
156
  <div class="container">
108
157
  <svg viewBox="${centreViewBox}">
109
- ${svg`
110
- <line
111
- x1="-${watchRadius}"
112
- y1="0"
113
- x2="${watchRadius}"
114
- y2="0"
115
- stroke="var(--instrument-frame-tertiary-color)"
116
- />
117
- <line
118
- x1="0"
119
- y1="0"
120
- y2="${watchRadius - 10}"
121
- x2="0"
122
- stroke="var(--instrument-enhanced-secondary-color)"
123
- transform="${needleTransform}"
124
- />
125
- <g
126
- style="transform: rotate(${this.roll}deg) scale(${vesselScale * this.normalizedScaleForeImage}) translate(-80px, -80px);"
127
- >
128
- ${this.zoomToFitArc ? vesselImages[this.vesselImageFore] : nothing}
129
- </g>
130
- ${
131
- this.zoomToFitArc
132
- ? nothing
133
- : svg`
134
- <path
135
- d="M ${-x} ${y} A ${watchRadius} ${watchRadius} 0 1 1 ${x} ${y}"
136
- fill="none"
137
- stroke="var(--instrument-frame-tertiary-color)"
138
- />
139
- `
140
- }
141
- `}
158
+ ${this.hasReadout
159
+ ? nothing
160
+ : svg`
161
+ <line
162
+ x1="-${watchRadius}"
163
+ y1="0"
164
+ x2="${watchRadius}"
165
+ y2="0"
166
+ stroke="var(--instrument-frame-tertiary-color)"
167
+ />
168
+ <line
169
+ x1="0"
170
+ y1="0"
171
+ y2="${watchRadius - 10}"
172
+ x2="0"
173
+ stroke="${this.indicatorColor}"
174
+ transform="${needleTransform}"
175
+ />
176
+ <g
177
+ style="transform: rotate(${this.roll}deg) scale(${vesselScale * this.normalizedScaleForeImage}) translate(-80px, -80px);"
178
+ >
179
+ ${this.zoomToFitArc ? vesselImages[this.vesselImageFore] : nothing}
180
+ </g>
181
+ `}
182
+ ${this.zoomToFitArc
183
+ ? nothing
184
+ : svg`
185
+ <path
186
+ d="M ${-x} ${y} A ${watchRadius} ${watchRadius} 0 1 1 ${x} ${y}"
187
+ fill="none"
188
+ stroke="var(--instrument-frame-tertiary-color)"
189
+ />
190
+ `}
142
191
  </svg>
143
- <obc-watch
144
- .watchCircleType=${WatchCircleType.double}
145
- .zoomToFitArc=${this.zoomToFitArc}
146
- .arcFrame=${this._arcFrame}
147
- tickmarksInside
148
- .areas=${areas}
149
- .barAreas=${[
150
- {
151
- startAngle: 180 + this.minAvgRoll,
152
- endAngle: 180 + this.maxAvgRoll,
153
- fillColor: 'var(--instrument-enhanced-tertiary-color)',
154
- },
155
- ]}
156
- .needles=${[
157
- {
158
- angle: 180 + this.roll,
159
- fillColor: 'var(--instrument-enhanced-secondary-color)',
160
- strokeColor: 'var(--border-silhouette-color)',
161
- },
162
- ]}
163
- .vessels=${this.zoomToFitArc
164
- ? []
165
- : [
166
- {
167
- size: VesselImageSize.large,
168
- vesselImage: this.vesselImageFore,
169
- transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,
170
- },
171
- ]}
172
- .tickmarks=${[
173
- {
174
- angle: 180,
175
- type: TickmarkType.main,
176
- },
177
- ]}
178
- .advices=${this.advices}
179
- ></obc-watch>
192
+ ${this.renderScale(areas, false)}
193
+ ${this.type === ObcRollType.dualScale
194
+ ? this.renderScale(areas, true)
195
+ : nothing}
196
+ ${this.hasReadout
197
+ ? html`<div class="readout">
198
+ <obc-readout
199
+ .variant=${ReadoutVariant.enhanced}
200
+ .direction=${ReadoutDirection.vertical}
201
+ .hasSetpoint=${false}
202
+ .hasAdvice=${false}
203
+ .value=${this.roll}
204
+ .fractionDigits=${0}
205
+ .valuePriority=${this.priority}
206
+ label="Roll"
207
+ unit="DEG"
208
+ ></obc-readout>
209
+ </div>`
210
+ : nothing}
180
211
  </div>
181
212
  `;
182
213
  }
183
214
 
215
+ // `top` rotates a second watch 180° onto the top arc for dual-scale — a
216
+ // rotation (opposite end of the indicator), not a mirror. A separate watch
217
+ // keeps the zoomed `arcFrame` correct.
218
+ private renderScale(areas: WatchArea[], top: boolean) {
219
+ return html`
220
+ <obc-watch
221
+ class=${top ? 'scale-top' : nothing}
222
+ .priority=${this.priority}
223
+ .watchCircleType=${WatchCircleType.double}
224
+ .zoomToFitArc=${this.zoomToFitArc}
225
+ .arcFrame=${this._arcFrame}
226
+ tickmarksInside
227
+ .areas=${areas}
228
+ .barAreas=${[
229
+ {
230
+ startAngle: 180 + this.minAvgRoll,
231
+ endAngle: 180 + this.maxAvgRoll,
232
+ fillColor: this.scaleFillColor,
233
+ },
234
+ ]}
235
+ .needles=${[
236
+ {
237
+ angle: 180 + this.roll,
238
+ fillColor: this.indicatorColor,
239
+ strokeColor: 'var(--border-silhouette-color)',
240
+ },
241
+ ]}
242
+ .vessels=${top || this.zoomToFitArc || this.hasReadout
243
+ ? []
244
+ : [
245
+ {
246
+ size: VesselImageSize.large,
247
+ vesselImage: this.vesselImageFore,
248
+ transform: `rotate(${this.roll}deg) scale(${this.normalizedScaleForeImage})`,
249
+ },
250
+ ]}
251
+ .tickmarks=${[{angle: 180, type: TickmarkType.main}]}
252
+ .advices=${this.advices}
253
+ ></obc-watch>
254
+ `;
255
+ }
256
+
184
257
  private get advices(): AngleAdviceRaw[] {
185
- const advices = [];
186
- if (this.maxRollAdvice !== undefined) {
187
- const arcAngle = normalizeArcAngle(this.arcAngle, 45);
188
- // Caution band fills the remainder of the arc out to a default 45°
189
- // caution range (clamped to the arc edge).
190
- const outer = Math.min(arcAngle, 45);
191
- const inner = Math.min(this.maxRollAdvice, outer);
192
- const state = this.triggerRollAdvice
193
- ? AdviceState.triggered
194
- : AdviceState.regular;
195
- advices.push({
258
+ if (this.maxRollAdvice === undefined) {
259
+ return [];
260
+ }
261
+ const arcAngle = normalizeArcAngle(this.arcAngle, 45);
262
+ // Caution band fills the remainder of the arc out to a default 45° caution
263
+ // range (clamped to the arc edge).
264
+ const outer = Math.min(arcAngle, 45);
265
+ const inner = Math.min(this.maxRollAdvice, outer);
266
+ const state = this.triggerRollAdvice
267
+ ? AdviceState.triggered
268
+ : AdviceState.regular;
269
+ return [
270
+ {
196
271
  minAngle: 180 - outer,
197
272
  maxAngle: 180 - inner,
198
273
  type: AdviceType.caution,
199
274
  state: state,
200
275
  hideMinTickmark: true,
201
- });
202
- advices.push({
276
+ },
277
+ {
203
278
  minAngle: 180 + inner,
204
279
  maxAngle: 180 + outer,
205
280
  type: AdviceType.caution,
206
281
  state: state,
207
282
  hideMaxTickmark: true,
208
- });
209
- }
210
- return advices;
283
+ },
284
+ ];
211
285
  }
212
286
 
213
287
  static override styles = css`
@@ -228,6 +302,16 @@ export class ObcRoll extends LitElement {
228
302
  width: 100%;
229
303
  height: 100%;
230
304
  }
305
+
306
+ .readout {
307
+ display: flex;
308
+ align-items: center;
309
+ justify-content: center;
310
+ }
311
+
312
+ .scale-top {
313
+ transform: rotate(180deg);
314
+ }
231
315
  `;
232
316
  }
233
317
 
@@ -49,6 +49,21 @@ export const Regular: Story = {
49
49
  },
50
50
  };
51
51
 
52
+ export const WithReadout: Story = {
53
+ args: {
54
+ value: 50,
55
+ hasReadout: true,
56
+ },
57
+ };
58
+
59
+ export const EnhancedWithReadout: Story = {
60
+ args: {
61
+ value: 50,
62
+ priority: Priority.enhanced,
63
+ hasReadout: true,
64
+ },
65
+ };
66
+
52
67
  export const Enhanced: Story = {
53
68
  args: {
54
69
  value: -50,
@@ -1,10 +1,12 @@
1
- import {LitElement, html} from 'lit';
1
+ import {LitElement, html, css, nothing} from 'lit';
2
2
  import {customElement} from '../../decorator.js';
3
3
  import {property} from 'lit/decorators.js';
4
4
  import {AdviceType} from '../watch/advice.js';
5
5
  import {Priority} from '../types.js';
6
6
  import {SetpointMixin} from '../../svghelpers/setpoint-mixin.js';
7
7
  import '../../building-blocks/instrument-radial/instrument-radial.js';
8
+ import '../readout/readout.js';
9
+ import {ReadoutDirection, ReadoutVariant} from '../readout/readout.js';
8
10
  import {TickmarkStyle} from '../watch/tickmark.js';
9
11
 
10
12
  export enum ObcGaugeRadialType {
@@ -140,6 +142,11 @@ export class ObcRotSector extends SetpointMixin(LitElement) {
140
142
  @property({type: Array, attribute: false}) advices: GaugeRadialAdvice[] = [];
141
143
  @property({type: Boolean}) zoomToFitArc: boolean = false;
142
144
  @property({type: Number}) rotArcExtent: number = 60;
145
+ /**
146
+ * When `true`, shows a centered `<obc-readout>` (label `ROT`, unit `DEG/min`)
147
+ * under the arc with the current rate-of-turn value. Default `false`.
148
+ */
149
+ @property({type: Boolean}) hasReadout: boolean = false;
143
150
 
144
151
  getAngle = (v: number): number => {
145
152
  if (!this.maxValue) return 0;
@@ -167,6 +174,23 @@ export class ObcRotSector extends SetpointMixin(LitElement) {
167
174
  return 'var(--instrument-enhanced-tertiary-color)';
168
175
  }
169
176
 
177
+ /**
178
+ * Vertical position of the readout, in % of the host. In the zoomed view the
179
+ * arc's lower edge shifts with `rotArcExtent`, so the position is interpolated
180
+ * between the narrow- and wide-arc anchors to keep a roughly constant gap
181
+ * between the arc and the readout. The static (unzoomed) arc uses a fixed
182
+ * position.
183
+ */
184
+ private get _readoutTopPercent(): number {
185
+ if (!this.zoomToFitArc) {
186
+ return 60;
187
+ }
188
+ const narrowTop = 70; // rotArcExtent ~10
189
+ const wideTop = 66; // rotArcExtent ~60
190
+ const extent = Math.min(60, Math.max(10, this.rotArcExtent));
191
+ return narrowTop + ((wideTop - narrowTop) * (extent - 10)) / (60 - 10);
192
+ }
193
+
170
194
  override render() {
171
195
  const barColor = this._barColor;
172
196
 
@@ -198,6 +222,21 @@ export class ObcRotSector extends SetpointMixin(LitElement) {
198
222
  .zoomToFitArc=${this.zoomToFitArc}
199
223
  >
200
224
  </obc-instrument-radial>
225
+ ${this.hasReadout
226
+ ? html`<div class="readout" style="top: ${this._readoutTopPercent}%">
227
+ <obc-readout
228
+ .variant=${ReadoutVariant.enhanced}
229
+ .direction=${ReadoutDirection.vertical}
230
+ .hasSetpoint=${false}
231
+ .hasAdvice=${false}
232
+ .value=${this.value}
233
+ .fractionDigits=${0}
234
+ .valuePriority=${this.priority}
235
+ label="ROT"
236
+ unit="DEG/min"
237
+ ></obc-readout>
238
+ </div>`
239
+ : nothing}
201
240
  `;
202
241
  }
203
242
 
@@ -218,6 +257,25 @@ export class ObcRotSector extends SetpointMixin(LitElement) {
218
257
 
219
258
  return 'var(--instrument-enhanced-secondary-color)';
220
259
  }
260
+
261
+ static override styles = css`
262
+ :host {
263
+ position: relative;
264
+ display: block;
265
+ height: 100%;
266
+ }
267
+
268
+ .readout {
269
+ position: absolute;
270
+ left: 50%;
271
+ transform: translate(-50%, -50%);
272
+ }
273
+
274
+ /* Center the value over the label/unit row instead of right-aligning it. */
275
+ obc-readout::part(value-wrapper) {
276
+ justify-self: center;
277
+ }
278
+ `;
221
279
  }
222
280
 
223
281
  declare global {