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

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 (32) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +592 -239
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +292 -8
  4. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts +10 -0
  5. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts.map +1 -1
  6. package/dist/building-blocks/instrument-radial/instrument-radial.js +86 -21
  7. package/dist/building-blocks/instrument-radial/instrument-radial.js.map +1 -1
  8. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js +99 -0
  9. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js.map +1 -0
  10. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts +42 -7
  11. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts.map +1 -1
  12. package/dist/navigation-instruments/gauge-radial/gauge-radial.js +178 -31
  13. package/dist/navigation-instruments/gauge-radial/gauge-radial.js.map +1 -1
  14. package/dist/navigation-instruments/readout/readout.css.js +4 -0
  15. package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
  16. package/dist/navigation-instruments/watch/tickmark.d.ts +2 -1
  17. package/dist/navigation-instruments/watch/tickmark.d.ts.map +1 -1
  18. package/dist/navigation-instruments/watch/tickmark.js +24 -4
  19. package/dist/navigation-instruments/watch/tickmark.js.map +1 -1
  20. package/dist/navigation-instruments/watch/watch.d.ts +23 -1
  21. package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
  22. package/dist/navigation-instruments/watch/watch.js +48 -20
  23. package/dist/navigation-instruments/watch/watch.js.map +1 -1
  24. package/package.json +1 -1
  25. package/src/building-blocks/instrument-radial/instrument-radial.stories.ts +21 -0
  26. package/src/building-blocks/instrument-radial/instrument-radial.ts +118 -21
  27. package/src/navigation-instruments/gauge-radial/gauge-radial.css +85 -0
  28. package/src/navigation-instruments/gauge-radial/gauge-radial.stories.ts +149 -7
  29. package/src/navigation-instruments/gauge-radial/gauge-radial.ts +212 -33
  30. package/src/navigation-instruments/readout/readout.css +4 -0
  31. package/src/navigation-instruments/watch/tickmark.ts +33 -6
  32. package/src/navigation-instruments/watch/watch.ts +60 -25
@@ -176,7 +176,7 @@ const RADIAL_SETPOINT_INWARD_ADJUST = 4;
176
176
  * @property {number|undefined} rateOfTurnDegreesPerMinute - Measured rate of turn in degrees per minute (the maritime/AIS convention, see ES-TRIN 2025/1 Art. 3.02 and ITU-R M.1371). Sign controls direction (positive = starboard/clockwise). When defined, this drives both the dot animation (multiplied by `rotDotAnimationFactor`) and the port/starboard direction sign.
177
177
  * @property {number} rotDotAnimationFactor - Visual amplification factor applied only to the spinning-dot animation (not to bar extent). Default `18` keeps the legacy visual feel (≈1 rpm at 20°/min).
178
178
  * @property {number} rotationsPerMinute - **Deprecated.** Spin speed of the ROT dot ring in rotations per minute. Sign controls direction (positive = clockwise). Use `rateOfTurnDegreesPerMinute` instead.
179
- * @property {ZoomToFitArcFrame|undefined} arcFrame - Pre-computed zoom-to-fit arc frame. When set, the watch skips its own `computeZoomToFitArcFrame()` call and uses these values directly. Consumer instruments (e.g. rudder, instrument-radial) should compute the frame once and pass it here to avoid redundant computation.
179
+ * @property {ZoomToFitArcFrame|undefined} arcFrame - Pre-computed zoom-to-fit arc frame. When set, the watch skips its own `computeZoomToFitArcFrame()` call and uses these values directly. Consumer instruments (e.g. rudder, instrument-radial) should compute the frame once and pass it here to avoid redundant computation. If you pass `arcFrame`, you own keeping it in sync with `areas` / `watchCircleType` — obc-watch will NOT recompute it, so a stale frame renders stale geometry.
180
180
  */
181
181
  @customElement('obc-watch')
182
182
  export class ObcWatch extends LitElement {
@@ -231,8 +231,21 @@ export class ObcWatch extends LitElement {
231
231
  @property({type: Number}) currentSymbolRadius: number | null = null;
232
232
  @property({type: String}) currentColor: string | undefined;
233
233
  @property({type: Boolean}) starboardPortIndicator: boolean = false;
234
- @property({type: Number}) clipTop: number = 0; // in percent of height
235
- @property({type: Number}) clipBottom: number = 0; // in percent of height
234
+ /** Top clip, % of height. Ignored when `zoomToFitArc` is true. */
235
+ @property({type: Number}) clipTop: number = 0;
236
+ /** Bottom clip, % of height. Ignored when `zoomToFitArc` is true. */
237
+ @property({type: Number}) clipBottom: number = 0;
238
+ /** Left clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`. */
239
+ @property({type: Number}) clipLeft: number = 0;
240
+ /** Right clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`. */
241
+ @property({type: Number}) clipRight: number = 0;
242
+ /**
243
+ * Place the horizontal end labels (±90°, e.g. min/max) below the tick instead
244
+ * of beside it. This is the "Max-min" label placement from the radial label
245
+ * model (External / Internal / Max-min) — see PR #903 / design discussion.
246
+ * Currently only used by the 180° sector of `obc-gauge-radial`.
247
+ */
248
+ @property({type: Boolean}) endLabelsMaxMin: boolean = false;
236
249
  @property({type: Number}) scaleWindIcon: number = 1;
237
250
  @property({type: Number}) rotation: number | undefined;
238
251
  @property({type: Boolean}) zoomToFitArc: boolean = false;
@@ -342,6 +355,18 @@ export class ObcWatch extends LitElement {
342
355
 
343
356
  private _rOff = 0;
344
357
 
358
+ /**
359
+ * Radius for a dial-band edge under zoom: additive (`base + _rOff`), keeping
360
+ * band thickness constant in SVG units. INVARIANT: every band-edge radius
361
+ * (rings, value arc, bars, needles, inside-label `textRadius`) must go through
362
+ * this — mixing additive and multiplicative offsets misaligns ticks, labels,
363
+ * advice masks and arcs. The proportional experiments were removed for this
364
+ * reason (PR #903).
365
+ */
366
+ private _bandRadius(base: number): number {
367
+ return base + this._rOff;
368
+ }
369
+
345
370
  private watchCircle(): SVGTemplateResult | SVGTemplateResult[] {
346
371
  const rings = [];
347
372
  if (this.state !== InstrumentState.off) {
@@ -349,18 +374,19 @@ export class ObcWatch extends LitElement {
349
374
  <circle
350
375
  cx="0"
351
376
  cy="0"
352
- r="${172 + this._rOff}"
377
+ r="${this._bandRadius(172)}"
353
378
  stroke="var(--instrument-frame-primary-color)"
354
379
  fill="none"
355
380
  stroke-width="24"
356
381
  />`);
357
382
 
358
383
  if (this.watchCircleType !== WatchCircleType.single) {
359
- const r1 = RING2_RADIUS + this._rOff;
360
- const r2 =
361
- (this.watchCircleType === WatchCircleType.doubleThin
384
+ const r1 = this._bandRadius(RING2_RADIUS);
385
+ const r2 = this._bandRadius(
386
+ this.watchCircleType === WatchCircleType.doubleThin
362
387
  ? RING3B_RADIUS
363
- : RING3_RADIUS) + this._rOff;
388
+ : RING3_RADIUS
389
+ );
364
390
  const r = (r1 + r2) / 2;
365
391
  const strokeWidth = r1 - r2;
366
392
  rings.push(
@@ -372,8 +398,8 @@ export class ObcWatch extends LitElement {
372
398
  );
373
399
  }
374
400
  if (this.watchCircleType === WatchCircleType.triple) {
375
- const r1 = RING3_RADIUS + this._rOff;
376
- const r2 = RING4_RADIUS + this._rOff;
401
+ const r1 = this._bandRadius(RING3_RADIUS);
402
+ const r2 = this._bandRadius(RING4_RADIUS);
377
403
  const r = (r1 + r2) / 2;
378
404
  const strokeWidth = r1 - r2;
379
405
  rings.push(
@@ -389,8 +415,8 @@ export class ObcWatch extends LitElement {
389
415
  const svgPath = roundedArch({
390
416
  startAngle: area.startAngle,
391
417
  endAngle: area.endAngle,
392
- R: OUTER_RING_RADIUS + this._rOff,
393
- r: this.innerRingRadius + this._rOff,
418
+ R: this._bandRadius(OUTER_RING_RADIUS),
419
+ r: this._bandRadius(this.innerRingRadius),
394
420
  roundOutsideCut: area.roundOutsideCut,
395
421
  roundInsideCut: area.roundInsideCut,
396
422
  });
@@ -422,7 +448,7 @@ export class ObcWatch extends LitElement {
422
448
  if (this.state !== InstrumentState.off) {
423
449
  result.push(
424
450
  circle('outerRing', {
425
- radius: OUTER_RING_RADIUS + this._rOff,
451
+ radius: this._bandRadius(OUTER_RING_RADIUS),
426
452
  strokeWidth: 1,
427
453
  strokeColor: 'var(--instrument-frame-tertiary-color)',
428
454
  strokePosition: 'center',
@@ -432,7 +458,7 @@ export class ObcWatch extends LitElement {
432
458
 
433
459
  result.push(svg`
434
460
  ${circle('innerRing', {
435
- radius: this.innerRingRadius + this._rOff,
461
+ radius: this._bandRadius(this.innerRingRadius),
436
462
  strokeWidth: 1,
437
463
  strokeColor: 'var(--instrument-frame-tertiary-color)',
438
464
  strokePosition: 'center',
@@ -442,7 +468,7 @@ export class ObcWatch extends LitElement {
442
468
  } else {
443
469
  result.push(svg`
444
470
  ${circle('innerRing', {
445
- radius: OUTER_RING_RADIUS + this._rOff,
471
+ radius: this._bandRadius(OUTER_RING_RADIUS),
446
472
  strokeWidth: 1,
447
473
  strokeColor: 'var(--instrument-frame-tertiary-color)',
448
474
  strokePosition: 'center',
@@ -476,7 +502,10 @@ export class ObcWatch extends LitElement {
476
502
  return `M 0 0 L ${x1} ${y1} A ${R} ${R} 0 ${largeArc} 1 ${x2} ${y2} Z`;
477
503
  };
478
504
 
479
- const Rm = (OUTER_RING_RADIUS + this.innerRingRadius) / 2 + this._rOff;
505
+ const Rm =
506
+ (this._bandRadius(OUTER_RING_RADIUS) +
507
+ this._bandRadius(this.innerRingRadius)) /
508
+ 2;
480
509
  const gx = (deg: number) => Rm * Math.sin(toRad(deg));
481
510
  const gy = (deg: number) => -Rm * Math.cos(toRad(deg));
482
511
 
@@ -594,8 +623,8 @@ export class ObcWatch extends LitElement {
594
623
  const startAngle = Math.min(bar.startAngle, bar.endAngle);
595
624
  const endAngle = Math.max(bar.startAngle, bar.endAngle);
596
625
  const arc = roundedArch({
597
- r: RING3_RADIUS + this._rOff,
598
- R: RING2_RADIUS + this._rOff,
626
+ r: this._bandRadius(RING3_RADIUS),
627
+ R: this._bandRadius(RING2_RADIUS),
599
628
  startAngle: startAngle,
600
629
  endAngle: endAngle,
601
630
  roundInsideCut: false,
@@ -638,7 +667,7 @@ export class ObcWatch extends LitElement {
638
667
  return svg`
639
668
  <rect
640
669
  transform="rotate(${needle.angle})"
641
- x="-4" y="${-(RING2_RADIUS + this._rOff)}" width="8" height="48" rx="4"
670
+ x="-4" y="${-this._bandRadius(RING2_RADIUS)}" width="8" height="48" rx="4"
642
671
  fill=${needle.fillColor}
643
672
  stroke=${needle.strokeColor}
644
673
  stroke-width="1"
@@ -705,17 +734,22 @@ export class ObcWatch extends LitElement {
705
734
  viewBox = frame.viewBox;
706
735
  } else {
707
736
  this._rOff = 0;
708
- width = (176 + this.getPadding()) * 2;
709
- height = width * (1 - this.clipTop / 100 - this.clipBottom / 100);
710
- const top = -width / 2 + (width * this.clipTop) / 100;
711
- viewBox = `-${width / 2} ${top} ${width} ${height}`;
737
+ const full = (176 + this.getPadding()) * 2;
738
+ width = full * (1 - this.clipLeft / 100 - this.clipRight / 100);
739
+ height = full * (1 - this.clipTop / 100 - this.clipBottom / 100);
740
+ const left = -full / 2 + (full * this.clipLeft) / 100;
741
+ const top = -full / 2 + (full * this.clipTop) / 100;
742
+ viewBox = `${left} ${top} ${width} ${height}`;
712
743
  }
713
744
 
714
745
  const rOff = this._rOff;
715
746
  const scale = this.getScale({width, height});
716
747
  const angleSetpoint = this.renderSetpoint();
717
- const textRadius =
718
- (this.tickmarksInside ? this.innerRingRadius : OUTER_RING_RADIUS) + rOff;
748
+ // Route through _bandRadius so inside labels track the (zoom-shifted) inner
749
+ // band edge; the coupling is intentional (see _bandRadius INVARIANT).
750
+ const textRadius = this.tickmarksInside
751
+ ? this._bandRadius(this.innerRingRadius)
752
+ : this._bandRadius(OUTER_RING_RADIUS);
719
753
  const maxDigits = Math.max(
720
754
  ...this.tickmarks.map((t) => t.text?.length ?? 0)
721
755
  );
@@ -731,6 +765,7 @@ export class ObcWatch extends LitElement {
731
765
  maxDigits,
732
766
  color: t.color,
733
767
  radiusOffset: rOff,
768
+ endLabelsMaxMin: this.endLabelsMaxMin,
734
769
  })
735
770
  );
736
771
  const advices = this.advices