@oicl/openbridge-webcomponents 2.0.0-next.102 → 2.0.0-next.104

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 (46) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +583 -145
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +338 -8
  4. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts +22 -2
  5. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts.map +1 -1
  6. package/dist/building-blocks/instrument-radial/instrument-radial.js +61 -37
  7. package/dist/building-blocks/instrument-radial/instrument-radial.js.map +1 -1
  8. package/dist/components/modal-window/modal-window.css.js +4 -2
  9. package/dist/components/modal-window/modal-window.css.js.map +1 -1
  10. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.d.ts +14 -0
  11. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.d.ts.map +1 -1
  12. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.js +30 -11
  13. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.js.map +1 -1
  14. package/dist/navigation-instruments/compass/compass.d.ts +21 -1
  15. package/dist/navigation-instruments/compass/compass.d.ts.map +1 -1
  16. package/dist/navigation-instruments/compass/compass.js +36 -16
  17. package/dist/navigation-instruments/compass/compass.js.map +1 -1
  18. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts.map +1 -1
  19. package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
  20. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js +34 -2
  21. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js.map +1 -1
  22. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts +15 -0
  23. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts.map +1 -1
  24. package/dist/navigation-instruments/gauge-radial/gauge-radial.js +35 -1
  25. package/dist/navigation-instruments/gauge-radial/gauge-radial.js.map +1 -1
  26. package/dist/navigation-instruments/heading/heading.d.ts +19 -1
  27. package/dist/navigation-instruments/heading/heading.d.ts.map +1 -1
  28. package/dist/navigation-instruments/heading/heading.js +34 -16
  29. package/dist/navigation-instruments/heading/heading.js.map +1 -1
  30. package/dist/navigation-instruments/rudder/rudder.d.ts +15 -2
  31. package/dist/navigation-instruments/rudder/rudder.d.ts.map +1 -1
  32. package/dist/navigation-instruments/rudder/rudder.js +36 -26
  33. package/dist/navigation-instruments/rudder/rudder.js.map +1 -1
  34. package/dist/navigation-instruments/speed-gauge/speed-gauge.d.ts +15 -1
  35. package/dist/navigation-instruments/speed-gauge/speed-gauge.d.ts.map +1 -1
  36. package/dist/navigation-instruments/speed-gauge/speed-gauge.js +31 -3
  37. package/dist/navigation-instruments/speed-gauge/speed-gauge.js.map +1 -1
  38. package/dist/navigation-instruments/watch/watch.d.ts +29 -1
  39. package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
  40. package/dist/navigation-instruments/watch/watch.js +52 -38
  41. package/dist/navigation-instruments/watch/watch.js.map +1 -1
  42. package/dist/svghelpers/radial-frame.d.ts +122 -0
  43. package/dist/svghelpers/radial-frame.d.ts.map +1 -0
  44. package/dist/svghelpers/radial-frame.js +259 -0
  45. package/dist/svghelpers/radial-frame.js.map +1 -0
  46. package/package.json +1 -1
@@ -53844,6 +53844,250 @@ function arcContainsAngle(startDeg, endDeg, testDeg) {
53844
53844
  function round4(n3) {
53845
53845
  return Math.round(n3 * 1e4) / 1e4;
53846
53846
  }
53847
+ const OUTER_RING_RADIUS$1 = 368 / 2;
53848
+ const RADIAL_VIEWBOX_BASE = 176;
53849
+ const LABEL_RADIAL_PAD_UNITS = 3;
53850
+ const LABEL_ANCHOR_OFFSET_PX = 9;
53851
+ const LABEL_EDGE_PAD_PX = 4;
53852
+ const DIGIT_WIDTH_PX = 7;
53853
+ const NSWE_LABEL_WIDTH_PX = 16;
53854
+ const NORTH_ARROW_WIDTH_PX = 16;
53855
+ const LABEL_RESERVE_MAX_FRACTION = 0.45;
53856
+ const END_MAXMIN_LABEL_DROP_PX = 21;
53857
+ const SIDE_LABEL_DROP_PX = 13;
53858
+ function measureContainerPx(host) {
53859
+ let width = host.clientWidth;
53860
+ let height = host.clientHeight;
53861
+ if (width === 0 || height === 0) {
53862
+ const box = host.parentElement?.getBoundingClientRect();
53863
+ if (box) {
53864
+ width = box.width;
53865
+ height = box.height;
53866
+ }
53867
+ }
53868
+ return { width, height };
53869
+ }
53870
+ function applyPinnedHostSize(host, frame, wasPinned) {
53871
+ if (frame?.hostWidthPx !== void 0 && frame.hostHeightPx !== void 0) {
53872
+ host.style.width = `${frame.hostWidthPx}px`;
53873
+ host.style.height = `${frame.hostHeightPx}px`;
53874
+ host.style.display = "block";
53875
+ return true;
53876
+ }
53877
+ if (wasPinned) {
53878
+ host.style.removeProperty("width");
53879
+ host.style.removeProperty("height");
53880
+ host.style.removeProperty("display");
53881
+ }
53882
+ return false;
53883
+ }
53884
+ function observeInnerBox(controller, renderRoot) {
53885
+ const box = renderRoot.querySelector("svg, .container");
53886
+ if (box) {
53887
+ controller.observe(box);
53888
+ }
53889
+ }
53890
+ function estimateLabelWidthPx(texts) {
53891
+ let maxChars = 0;
53892
+ for (const t2 of texts) {
53893
+ if (t2 !== void 0 && t2.length > maxChars) {
53894
+ maxChars = t2.length;
53895
+ }
53896
+ }
53897
+ return maxChars * DIGIT_WIDTH_PX;
53898
+ }
53899
+ function clipFractions(clips) {
53900
+ if (!clips) {
53901
+ return { fx: 1, fy: 1 };
53902
+ }
53903
+ const fx = 1 - (clips.left + clips.right) / 100;
53904
+ const fy = 1 - (clips.top + clips.bottom) / 100;
53905
+ return { fx: fx > 0 ? fx : 1, fy: fy > 0 ? fy : 1 };
53906
+ }
53907
+ function buildViewBox(side, clips) {
53908
+ const { fx, fy } = clipFractions(clips);
53909
+ const width = side * fx;
53910
+ const height = side * fy;
53911
+ const x2 = -side / 2 + side * (clips?.left ?? 0) / 100;
53912
+ const y3 = -side / 2 + side * (clips?.top ?? 0) / 100;
53913
+ return {
53914
+ x: x2,
53915
+ y: y3,
53916
+ width,
53917
+ height,
53918
+ viewBox: `${x2} ${y3} ${width} ${height}`,
53919
+ radiusOffset: 0
53920
+ };
53921
+ }
53922
+ function pinnedScale(opts) {
53923
+ if (opts.faceDiameter === void 0) {
53924
+ return void 0;
53925
+ }
53926
+ const scale = opts.faceDiameter / (2 * OUTER_RING_RADIUS$1);
53927
+ return Number.isFinite(scale) && scale > 0 ? scale : void 0;
53928
+ }
53929
+ function effectiveContainer(opts, fx, fy) {
53930
+ const w2 = opts.containerPx?.width ?? 0;
53931
+ const h3 = opts.containerPx?.height ?? 0;
53932
+ if (!Number.isFinite(w2) || !Number.isFinite(h3) || w2 <= 0 || h3 <= 0) {
53933
+ return void 0;
53934
+ }
53935
+ return Math.min(w2 / fx, h3 / fy);
53936
+ }
53937
+ function containerScale(opts, frame) {
53938
+ const w2 = opts.containerPx?.width ?? 0;
53939
+ const h3 = opts.containerPx?.height ?? 0;
53940
+ if (!Number.isFinite(w2) || !Number.isFinite(h3) || w2 <= 0 || h3 <= 0) {
53941
+ return void 0;
53942
+ }
53943
+ const scale = Math.min(w2 / frame.width, h3 / frame.height);
53944
+ return Number.isFinite(scale) && scale > 0 ? scale : void 0;
53945
+ }
53946
+ function withHostPx(frame, pinned, scale, labelReserve, labelsHidden, clipsAdjusted = false) {
53947
+ const result = {
53948
+ ...frame,
53949
+ scale,
53950
+ labelReserve,
53951
+ labelsHidden,
53952
+ clipsAdjusted
53953
+ };
53954
+ if (pinned) {
53955
+ result.hostWidthPx = frame.width * scale;
53956
+ result.hostHeightPx = frame.height * scale;
53957
+ }
53958
+ return result;
53959
+ }
53960
+ function solveSide(opts, labelCostPx, clips) {
53961
+ const baseSide = (RADIAL_VIEWBOX_BASE + opts.basePadding) * 2;
53962
+ const { fx, fy } = clipFractions(clips);
53963
+ const labelSideAt = (scale) => 2 * (OUTER_RING_RADIUS$1 + LABEL_RADIAL_PAD_UNITS + labelCostPx / scale);
53964
+ let side = baseSide;
53965
+ let labelsHidden = false;
53966
+ if (labelCostPx > 0) {
53967
+ const pinned = pinnedScale(opts);
53968
+ if (pinned !== void 0) {
53969
+ const grown = Math.max(baseSide, labelSideAt(pinned));
53970
+ labelsHidden = 2 * (labelCostPx / pinned) / grown > LABEL_RESERVE_MAX_FRACTION;
53971
+ side = labelsHidden ? baseSide : grown;
53972
+ } else {
53973
+ const container = effectiveContainer(opts, fx, fy);
53974
+ if (container === void 0) {
53975
+ side = Math.max(baseSide, labelSideAt(1));
53976
+ } else if (2 * labelCostPx / container > LABEL_RESERVE_MAX_FRACTION) {
53977
+ labelsHidden = true;
53978
+ } else {
53979
+ side = Math.max(
53980
+ baseSide,
53981
+ 2 * (OUTER_RING_RADIUS$1 + LABEL_RADIAL_PAD_UNITS) / (1 - 2 * labelCostPx / container)
53982
+ );
53983
+ }
53984
+ }
53985
+ }
53986
+ return { side, labelsHidden };
53987
+ }
53988
+ function adjustClipsForLabelDrop(clips, side, scale, labelDropPx) {
53989
+ const requiredUnits = labelDropPx / scale;
53990
+ const requiredClipPct = Math.max(0, (0.5 - requiredUnits / side) * 100);
53991
+ const bottom = clips.bottom > requiredClipPct ? requiredClipPct : clips.bottom;
53992
+ const top = clips.top > requiredClipPct ? requiredClipPct : clips.top;
53993
+ if (bottom === clips.bottom && top === clips.top) {
53994
+ return clips;
53995
+ }
53996
+ return { ...clips, top, bottom };
53997
+ }
53998
+ function computeRadialFrame(opts) {
53999
+ const labelWidthPx = opts.labelWidthPx ?? 0;
54000
+ const labelCostPx = labelWidthPx > 0 ? LABEL_ANCHOR_OFFSET_PX + labelWidthPx + LABEL_EDGE_PAD_PX : 0;
54001
+ if (opts.zoomToFitArc && opts.areas && opts.areas.length > 0) {
54002
+ return computeZoomedFrame(opts, labelCostPx);
54003
+ }
54004
+ const baseSide = (RADIAL_VIEWBOX_BASE + opts.basePadding) * 2;
54005
+ let clips = opts.clips;
54006
+ let { side, labelsHidden } = solveSide(opts, labelCostPx, clips);
54007
+ const labelDropPx = opts.labelDropPx ?? 0;
54008
+ if (labelDropPx > 0 && labelCostPx > 0 && !labelsHidden && clips) {
54009
+ for (let i4 = 0; i4 < 2; i4++) {
54010
+ const scale2 = pinnedScale(opts) ?? containerScale(opts, buildViewBox(side, clips)) ?? 1;
54011
+ const adjusted = adjustClipsForLabelDrop(
54012
+ clips,
54013
+ side,
54014
+ scale2,
54015
+ labelDropPx
54016
+ );
54017
+ if (adjusted === clips) {
54018
+ break;
54019
+ }
54020
+ clips = adjusted;
54021
+ const solved = solveSide(opts, labelCostPx, clips);
54022
+ side = solved.side;
54023
+ labelsHidden = solved.labelsHidden;
54024
+ }
54025
+ if (labelsHidden) {
54026
+ clips = opts.clips;
54027
+ const solved = solveSide(opts, labelCostPx, clips);
54028
+ side = solved.side;
54029
+ labelsHidden = solved.labelsHidden;
54030
+ }
54031
+ }
54032
+ const frame = buildViewBox(side, clips);
54033
+ const pinned = pinnedScale(opts);
54034
+ const scale = pinned ?? containerScale(opts, frame) ?? 1;
54035
+ return withHostPx(
54036
+ frame,
54037
+ pinned !== void 0,
54038
+ scale,
54039
+ (side - baseSide) / 2,
54040
+ labelsHidden,
54041
+ clips !== opts.clips
54042
+ );
54043
+ }
54044
+ function computeZoomedFrame(opts, labelCostPx) {
54045
+ let reserve = 0;
54046
+ let labelsHidden = false;
54047
+ let frame = zoomFrameAt(opts, opts.basePadding);
54048
+ if (labelCostPx > 0) {
54049
+ for (let i4 = 0; i4 < 2; i4++) {
54050
+ const passScale = zoomedScale(opts, frame);
54051
+ const reserveUnits = labelCostPx / passScale;
54052
+ if (2 * reserveUnits / Math.max(frame.width, frame.height) > LABEL_RESERVE_MAX_FRACTION) {
54053
+ labelsHidden = true;
54054
+ reserve = 0;
54055
+ frame = zoomFrameAt(opts, opts.basePadding);
54056
+ break;
54057
+ }
54058
+ reserve = reserveUnits;
54059
+ frame = zoomFrameAt(opts, opts.basePadding + reserve);
54060
+ }
54061
+ }
54062
+ const pinned = pinnedZoomedScale(opts, frame);
54063
+ const scale = pinned ?? containerScale(opts, frame) ?? 1;
54064
+ return withHostPx(
54065
+ frame,
54066
+ pinned !== void 0,
54067
+ scale,
54068
+ labelsHidden ? 0 : reserve,
54069
+ labelsHidden
54070
+ );
54071
+ }
54072
+ function zoomFrameAt(opts, extension) {
54073
+ return computeZoomToFitArcFrame({
54074
+ areas: opts.areas,
54075
+ outerRadius: OUTER_RING_RADIUS$1,
54076
+ innerRadius: opts.innerRadius ?? OUTER_RING_RADIUS$1,
54077
+ extension,
54078
+ targetSize: (RADIAL_VIEWBOX_BASE + extension) * 2
54079
+ });
54080
+ }
54081
+ function pinnedZoomedScale(opts, frame) {
54082
+ if (opts.faceDiameter === void 0) {
54083
+ return void 0;
54084
+ }
54085
+ const scale = opts.faceDiameter / (2 * (OUTER_RING_RADIUS$1 + frame.radiusOffset));
54086
+ return Number.isFinite(scale) && scale > 0 ? scale : void 0;
54087
+ }
54088
+ function zoomedScale(opts, frame) {
54089
+ return pinnedZoomedScale(opts, frame) ?? containerScale(opts, frame) ?? 1;
54090
+ }
53847
54091
  var __defProp$vR = Object.defineProperty;
53848
54092
  var __getOwnPropDesc$vU = Object.getOwnPropertyDescriptor;
53849
54093
  var __decorateClass$vV = (decorators, target, key, kind) => {
@@ -53931,6 +54175,8 @@ let ObcWatch = class extends i$4 {
53931
54175
  this._legacyRotationsPerMinute = 0;
53932
54176
  this._resizeController = new h(this, {});
53933
54177
  this._rOff = 0;
54178
+ this._labelsHidden = false;
54179
+ this._hostSizePinned = false;
53934
54180
  }
53935
54181
  set rotationsPerMinute(value) {
53936
54182
  this._legacyRotationsPerMinute = value;
@@ -53952,6 +54198,10 @@ let ObcWatch = class extends i$4 {
53952
54198
  }
53953
54199
  return this._legacyRotationsPerMinute;
53954
54200
  }
54201
+ firstUpdated(changed) {
54202
+ super.firstUpdated(changed);
54203
+ observeInnerBox(this._resizeController, this.renderRoot);
54204
+ }
53955
54205
  willUpdate(changed) {
53956
54206
  super.willUpdate(changed);
53957
54207
  if (this._rotController && (changed.has("rateOfTurnDegreesPerMinute") || changed.has("rotDotAnimationFactor") || changed.has("rotationsPerMinute"))) {
@@ -53976,6 +54226,11 @@ let ObcWatch = class extends i$4 {
53976
54226
  }
53977
54227
  updated(changed) {
53978
54228
  super.updated(changed);
54229
+ this._hostSizePinned = applyPinnedHostSize(
54230
+ this,
54231
+ this.arcFrame ? void 0 : this._ownFrame,
54232
+ this._hostSizePinned
54233
+ );
53979
54234
  const el = this.rotType ? this.renderRoot.querySelector("#rot-spinner") : null;
53980
54235
  if (!el) {
53981
54236
  this._rotController = disposeRotController(this, this._rotController);
@@ -54273,30 +54528,26 @@ let ObcWatch = class extends i$4 {
54273
54528
  });
54274
54529
  }
54275
54530
  getScale({ width, height }) {
54276
- let clientWidth = this.clientWidth;
54277
- let clientHeight = this.clientHeight;
54278
- if (clientWidth === 0 || clientHeight === 0) {
54279
- const box = this.parentElement?.getBoundingClientRect();
54280
- if (box) {
54281
- clientWidth = box.width;
54282
- clientHeight = box.height;
54283
- }
54284
- }
54285
- const scale = Math.min(clientWidth / width, clientHeight / height);
54531
+ const container = measureContainerPx(this);
54532
+ const scale = Math.min(container.width / width, container.height / height);
54286
54533
  if (!Number.isFinite(scale) || scale <= 0) {
54287
54534
  return 1;
54288
54535
  }
54289
54536
  return scale;
54290
54537
  }
54291
- getPadding() {
54292
- if (this.padding !== void 0) {
54293
- return this.padding;
54538
+ /**
54539
+ * Pixel width of the widest outside label, feeding the frame's label
54540
+ * reserve (issue #1021). Explicit `padding` is a hard geometry override
54541
+ * and disables the reserve, preserving legacy consumer output.
54542
+ */
54543
+ getLabelWidthPx() {
54544
+ if (this.padding !== void 0 || this.tickmarksInside) {
54545
+ return 0;
54294
54546
  }
54295
- const hasTickmarksWithText = this.tickmarks.length > 0 && this.tickmarks.some((t2) => t2.text !== void 0);
54296
- if (hasTickmarksWithText && !this.tickmarksInside) {
54297
- return 24 * 2.5;
54547
+ if (this.showLabels) {
54548
+ return NSWE_LABEL_WIDTH_PX;
54298
54549
  }
54299
- return 24;
54550
+ return estimateLabelWidthPx(this.tickmarks.map((t2) => t2.text));
54300
54551
  }
54301
54552
  render() {
54302
54553
  let width;
@@ -54304,31 +54555,33 @@ let ObcWatch = class extends i$4 {
54304
54555
  let viewBox;
54305
54556
  if (this.arcFrame) {
54306
54557
  this._rOff = this.arcFrame.radiusOffset;
54558
+ this._labelsHidden = false;
54559
+ this._ownFrame = void 0;
54307
54560
  width = this.arcFrame.width;
54308
54561
  height = this.arcFrame.height;
54309
54562
  viewBox = this.arcFrame.viewBox;
54310
- } else if (this.zoomToFitArc && this.areas.length > 0) {
54311
- const ext = this.getPadding();
54312
- const targetSize = (176 + ext) * 2;
54313
- const frame = computeZoomToFitArcFrame({
54563
+ } else {
54564
+ const frame = computeRadialFrame({
54565
+ basePadding: this.padding ?? 24,
54566
+ labelWidthPx: this.getLabelWidthPx(),
54567
+ clips: {
54568
+ top: this.clipTop,
54569
+ bottom: this.clipBottom,
54570
+ left: this.clipLeft,
54571
+ right: this.clipRight
54572
+ },
54573
+ containerPx: measureContainerPx(this),
54574
+ faceDiameter: this.faceDiameter,
54575
+ zoomToFitArc: this.zoomToFitArc,
54314
54576
  areas: this.areas,
54315
- outerRadius: OUTER_RING_RADIUS,
54316
- innerRadius: this.innerRingRadius,
54317
- extension: ext,
54318
- targetSize
54577
+ innerRadius: this.innerRingRadius
54319
54578
  });
54320
54579
  this._rOff = frame.radiusOffset;
54580
+ this._labelsHidden = frame.labelsHidden;
54581
+ this._ownFrame = frame;
54321
54582
  width = frame.width;
54322
54583
  height = frame.height;
54323
54584
  viewBox = frame.viewBox;
54324
- } else {
54325
- this._rOff = 0;
54326
- const full = (176 + this.getPadding()) * 2;
54327
- width = full * (1 - this.clipLeft / 100 - this.clipRight / 100);
54328
- height = full * (1 - this.clipTop / 100 - this.clipBottom / 100);
54329
- const left = -full / 2 + full * this.clipLeft / 100;
54330
- const top = -full / 2 + full * this.clipTop / 100;
54331
- viewBox = `${left} ${top} ${width} ${height}`;
54332
54585
  }
54333
54586
  const rOff = this._rOff;
54334
54587
  const scale = this.getScale({ width, height });
@@ -54342,7 +54595,7 @@ let ObcWatch = class extends i$4 {
54342
54595
  size: t2.type,
54343
54596
  style: this.tickmarkStyle,
54344
54597
  scale,
54345
- text: this.showLabels ? void 0 : t2.text,
54598
+ text: this.showLabels || this._labelsHidden ? void 0 : t2.text,
54346
54599
  inside: this.tickmarksInside,
54347
54600
  textRadius,
54348
54601
  rotation: this.rotation,
@@ -54353,9 +54606,11 @@ let ObcWatch = class extends i$4 {
54353
54606
  })
54354
54607
  );
54355
54608
  const advices = this.advices ? this.advices.map((a2) => renderAdvice$3(a2, rOff)) : A;
54356
- const insideLabels = this.tickmarksInside && this.showLabels;
54609
+ const showNsweLabels = this.showLabels && !this._labelsHidden;
54610
+ const showNorthArrow = this.northArrow && !this._labelsHidden;
54611
+ const insideLabels = this.tickmarksInside && showNsweLabels;
54357
54612
  const includeNorth = !this.northArrow;
54358
- const labelPositions = this.showLabels ? getLabelPositions({
54613
+ const labelPositions = showNsweLabels ? getLabelPositions({
54359
54614
  scale,
54360
54615
  inside: this.tickmarksInside,
54361
54616
  innerRadius: this.innerRingRadius + rOff,
@@ -54368,7 +54623,7 @@ let ObcWatch = class extends i$4 {
54368
54623
  innerRadius: this.innerRingRadius + rOff,
54369
54624
  includeNorth
54370
54625
  }) : A;
54371
- const northArrowEl = this.northArrow ? renderNorthArrow({
54626
+ const northArrowEl = showNorthArrow ? renderNorthArrow({
54372
54627
  scale,
54373
54628
  rotation: this.rotation,
54374
54629
  inside: this.northArrowInside ?? this.tickmarksInside
@@ -54651,6 +54906,9 @@ __decorateClass$vV([
54651
54906
  __decorateClass$vV([
54652
54907
  n$3({ type: Number })
54653
54908
  ], ObcWatch.prototype, "padding", 2);
54909
+ __decorateClass$vV([
54910
+ n$3({ type: Number, attribute: "face-diameter" })
54911
+ ], ObcWatch.prototype, "faceDiameter", 2);
54654
54912
  __decorateClass$vV([
54655
54913
  n$3({ type: Array, attribute: false })
54656
54914
  ], ObcWatch.prototype, "areas", 2);
@@ -54781,7 +55039,6 @@ var __decorateClass$vU = (decorators, target, key, kind) => {
54781
55039
  if (kind && result) __defProp$vQ(target, key, result);
54782
55040
  return result;
54783
55041
  };
54784
- const WATCH_DEFAULT_VIEWBOX = 448;
54785
55042
  const NEEDLE_TIP_RADIUS$1 = 160;
54786
55043
  const NEEDLE_TIP_GAP = 5;
54787
55044
  const NEEDLE_WIDTH = 8;
@@ -54850,6 +55107,36 @@ let ObcInstrumentRadial = class extends SetpointMixin(i$4) {
54850
55107
  this.endLabelsMaxMin = false;
54851
55108
  this.zoomToFitArc = false;
54852
55109
  this._radiusOffset = 0;
55110
+ this._lastFrameKey = "";
55111
+ this._resizeController = new h(this, {});
55112
+ this._hostSizePinned = false;
55113
+ }
55114
+ firstUpdated(changed) {
55115
+ super.firstUpdated(changed);
55116
+ observeInnerBox(this._resizeController, this.renderRoot);
55117
+ }
55118
+ /** The frame computed for the current render (viewBox, label reserve …). */
55119
+ get frame() {
55120
+ return this._frame;
55121
+ }
55122
+ updated(changed) {
55123
+ super.updated(changed);
55124
+ this._hostSizePinned = applyPinnedHostSize(
55125
+ this,
55126
+ this._frame,
55127
+ this._hostSizePinned
55128
+ );
55129
+ const frame = this._frame;
55130
+ if (!frame) {
55131
+ return;
55132
+ }
55133
+ const key = `${frame.viewBox}|${frame.labelsHidden}|${frame.hostWidthPx ?? ""}|${frame.hostHeightPx ?? ""}`;
55134
+ if (key !== this._lastFrameKey) {
55135
+ this._lastFrameKey = key;
55136
+ this.dispatchEvent(
55137
+ new CustomEvent("frame-changed", { detail: frame })
55138
+ );
55139
+ }
54853
55140
  }
54854
55141
  get clampedValue() {
54855
55142
  const lowerBound = Math.min(this.minValue, this.maxValue);
@@ -54916,31 +55203,28 @@ let ObcInstrumentRadial = class extends SetpointMixin(i$4) {
54916
55203
  }
54917
55204
  ];
54918
55205
  const watchCircleType = this.type === "needle" ? WatchCircleType.single : WatchCircleType.double;
54919
- const clips = this.safeClips;
54920
- let viewBox;
54921
- if (this.zoomToFitArc) {
54922
- const ext = 48;
54923
- const targetSize = (176 + ext) * 2;
54924
- const frame = computeZoomToFitArcFrame({
54925
- areas,
54926
- outerRadius: OUTER_RING_RADIUS,
54927
- innerRadius: innerRingRadiusFor(watchCircleType),
54928
- extension: ext,
54929
- targetSize
54930
- });
54931
- this._radiusOffset = frame.radiusOffset;
54932
- viewBox = frame.viewBox;
54933
- this._arcFrame = frame;
54934
- } else {
54935
- this._radiusOffset = 0;
54936
- this._arcFrame = void 0;
54937
- const full = WATCH_DEFAULT_VIEWBOX;
54938
- const w2 = full * (1 - clips.left / 100 - clips.right / 100);
54939
- const h3 = full * (1 - clips.top / 100 - clips.bottom / 100);
54940
- const left = -full / 2 + full * clips.left / 100;
54941
- const top = -full / 2 + full * clips.top / 100;
54942
- viewBox = `${left} ${top} ${w2} ${h3}`;
54943
- }
55206
+ const tickmarks = this.tickmarks;
55207
+ const hasHorizontalEndLabels = tickmarks.some((t2) => {
55208
+ if (t2.text === void 0) {
55209
+ return false;
55210
+ }
55211
+ const angle = (t2.angle % 360 + 360) % 360;
55212
+ return Math.abs(angle - 90) < 1 || Math.abs(angle - 270) < 1;
55213
+ });
55214
+ const frame = computeRadialFrame({
55215
+ basePadding: 48,
55216
+ labelWidthPx: this.tickmarksInside ? 0 : estimateLabelWidthPx(tickmarks.map((t2) => t2.text)),
55217
+ labelDropPx: this.tickmarksInside || !hasHorizontalEndLabels ? 0 : this.endLabelsMaxMin ? END_MAXMIN_LABEL_DROP_PX : SIDE_LABEL_DROP_PX,
55218
+ clips: this.zoomToFitArc ? void 0 : this.safeClips,
55219
+ containerPx: measureContainerPx(this),
55220
+ faceDiameter: this.faceDiameter,
55221
+ zoomToFitArc: this.zoomToFitArc,
55222
+ areas,
55223
+ innerRadius: innerRingRadiusFor(watchCircleType)
55224
+ });
55225
+ this._radiusOffset = frame.radiusOffset;
55226
+ this._frame = frame;
55227
+ const shownTickmarks = frame.labelsHidden ? tickmarks.map((t2) => ({ ...t2, text: void 0 })) : tickmarks;
54944
55228
  return b`
54945
55229
  <div class="container">
54946
55230
  <obc-watch
@@ -54952,23 +55236,17 @@ let ObcInstrumentRadial = class extends SetpointMixin(i$4) {
54952
55236
  .angleSetpointAtZeroDeadband=${this.setpointAtZeroDeadband}
54953
55237
  .setpointOverride=${this.setpointOverride}
54954
55238
  .animateSetpoint=${this.animateSetpoint}
54955
- .padding=${48}
54956
- .tickmarks=${this.tickmarks}
55239
+ .tickmarks=${shownTickmarks}
54957
55240
  .tickmarksInside=${this.tickmarksInside}
54958
55241
  .tickmarkStyle=${this.tickmarkStyle}
54959
55242
  .advices=${this._advices}
54960
55243
  .areas=${areas}
54961
55244
  .watchCircleType=${watchCircleType}
54962
55245
  .barAreas=${barAreas}
54963
- .clipTop=${this.zoomToFitArc ? 0 : clips.top}
54964
- .clipBottom=${this.zoomToFitArc ? 0 : clips.bottom}
54965
- .clipLeft=${this.zoomToFitArc ? 0 : clips.left}
54966
- .clipRight=${this.zoomToFitArc ? 0 : clips.right}
54967
55246
  .endLabelsMaxMin=${this.endLabelsMaxMin}
54968
- .zoomToFitArc=${this.zoomToFitArc}
54969
- .arcFrame=${this._arcFrame}
55247
+ .arcFrame=${frame}
54970
55248
  ></obc-watch>
54971
- <svg class="gauge-radial" viewBox=${viewBox}>${this._needle}</svg>
55249
+ <svg class="gauge-radial" viewBox=${frame.viewBox}>${this._needle}</svg>
54972
55250
  </div>
54973
55251
  `;
54974
55252
  }
@@ -55192,6 +55470,9 @@ __decorateClass$vU([
55192
55470
  __decorateClass$vU([
55193
55471
  n$3({ type: Boolean })
55194
55472
  ], ObcInstrumentRadial.prototype, "zoomToFitArc", 2);
55473
+ __decorateClass$vU([
55474
+ n$3({ type: Number, attribute: "face-diameter" })
55475
+ ], ObcInstrumentRadial.prototype, "faceDiameter", 2);
55195
55476
  ObcInstrumentRadial = __decorateClass$vU([
55196
55477
  customElement("obc-instrument-radial")
55197
55478
  ], ObcInstrumentRadial);
@@ -93657,7 +93938,7 @@ const componentStyle$C = i$7`* {
93657
93938
  display: flex;
93658
93939
  flex-direction: column;
93659
93940
  width: 100%;
93660
- height: 407px;
93941
+ max-height: 90vh;
93661
93942
  }
93662
93943
 
93663
93944
  .title-container {
@@ -93716,7 +93997,9 @@ const componentStyle$C = i$7`* {
93716
93997
  }
93717
93998
 
93718
93999
  .content-area {
93719
- flex: 1 0 0;
94000
+ flex: 1 1 auto;
94001
+ min-height: 0;
94002
+ overflow-y: auto;
93720
94003
  }
93721
94004
 
93722
94005
  .action-container {
@@ -208683,6 +208966,8 @@ let ObcAzimuthThruster = class extends i$4 {
208683
208966
  this.bottomPropeller = PropellerType.none;
208684
208967
  this.tickmarkStyle = TickmarkStyle$1.regular;
208685
208968
  this.starboardPortIndicator = false;
208969
+ this._hostSizePinned = false;
208970
+ this._resizeController = new h(this, {});
208686
208971
  }
208687
208972
  willUpdate(changed) {
208688
208973
  super.willUpdate(changed);
@@ -208709,6 +208994,18 @@ let ObcAzimuthThruster = class extends i$4 {
208709
208994
  animateSetpoint: this.animateSetpoint
208710
208995
  });
208711
208996
  }
208997
+ firstUpdated(changed) {
208998
+ super.firstUpdated(changed);
208999
+ observeInnerBox(this._resizeController, this.renderRoot);
209000
+ }
209001
+ updated(changed) {
209002
+ super.updated(changed);
209003
+ this._hostSizePinned = applyPinnedHostSize(
209004
+ this,
209005
+ this._frame,
209006
+ this._hostSizePinned
209007
+ );
209008
+ }
208712
209009
  get angleAdviceRaw() {
208713
209010
  return this.angleAdvices.map((advice) => {
208714
209011
  const triggered = this.angleSetpoint !== void 0 && mapAngle0to360$1(this.angleSetpoint - advice.minAngle) < 180 && mapAngle0to360$1(this.angleSetpoint - advice.maxAngle) > 180;
@@ -208780,19 +209077,21 @@ let ObcAzimuthThruster = class extends i$4 {
208780
209077
  render() {
208781
209078
  const rotateAngle = this.angle;
208782
209079
  const tickmarks = this.getTickmarks();
208783
- let viewBox;
208784
- if (!this.hasLabelSpacer) {
208785
- viewBox = "-192 -192 384 384";
208786
- } else if (this.showLabels && !this.tickmarksInside) {
208787
- viewBox = "-236 -236 472 472";
208788
- } else {
208789
- viewBox = "-200 -200 400 400";
208790
- }
209080
+ const frame = computeRadialFrame({
209081
+ basePadding: this.hasLabelSpacer ? 24 : 16,
209082
+ labelWidthPx: this.hasLabelSpacer && !this.tickmarksInside ? estimateLabelWidthPx(tickmarks.map((t2) => t2.text)) : 0,
209083
+ containerPx: measureContainerPx(this),
209084
+ faceDiameter: this.faceDiameter
209085
+ });
209086
+ this._frame = frame;
209087
+ const shownTickmarks = frame.labelsHidden ? tickmarks.map((t2) => ({ ...t2, text: void 0 })) : tickmarks;
209088
+ const viewBox = frame.viewBox;
208791
209089
  return b`
208792
209090
  <div class="container">
208793
209091
  <obc-watch
208794
209092
  .touching=${this.touching}
208795
- .tickmarks=${tickmarks}
209093
+ .arcFrame=${frame}
209094
+ .tickmarks=${shownTickmarks}
208796
209095
  .state=${this.state}
208797
209096
  .priority=${this.priority}
208798
209097
  .angleSetpoint=${this.angleSetpoint}
@@ -208803,7 +209102,6 @@ let ObcAzimuthThruster = class extends i$4 {
208803
209102
  .animateSetpoint=${this.animateSetpoint}
208804
209103
  .tickmarksInside=${this.tickmarksInside}
208805
209104
  .tickmarkStyle=${this.tickmarkStyle}
208806
- padding=${o$4(!this.hasLabelSpacer ? 16 : void 0)}
208807
209105
  .advices=${this.angleAdviceRaw}
208808
209106
  .starboardPortIndicator=${this.starboardPortIndicator}
208809
209107
  ></obc-watch>
@@ -208949,6 +209247,9 @@ __decorateClass$K([
208949
209247
  __decorateClass$K([
208950
209248
  n$3({ type: Boolean })
208951
209249
  ], ObcAzimuthThruster.prototype, "starboardPortIndicator", 2);
209250
+ __decorateClass$K([
209251
+ n$3({ type: Number, attribute: "face-diameter" })
209252
+ ], ObcAzimuthThruster.prototype, "faceDiameter", 2);
208952
209253
  ObcAzimuthThruster = __decorateClass$K([
208953
209254
  customElement("obc-azimuth-thruster")
208954
209255
  ], ObcAzimuthThruster);
@@ -210977,6 +211278,7 @@ let ObcCompass = class extends i$4 {
210977
211278
  onAnimationEnd: () => this.requestUpdate()
210978
211279
  });
210979
211280
  this._resizeController = new h(this, {});
211281
+ this._hostSizePinned = false;
210980
211282
  }
210981
211283
  willUpdate(changed) {
210982
211284
  super.willUpdate(changed);
@@ -211005,17 +211307,27 @@ let ObcCompass = class extends i$4 {
211005
211307
  get _effectiveRotDegPerMin() {
211006
211308
  return this.rateOfTurnDegreesPerMinute ?? this.rotationsPerMinute;
211007
211309
  }
211008
- getPadding() {
211009
- const size = Math.min(this.clientHeight, this.clientWidth);
211010
- const deltaWidth = 512 - size;
211011
- const steps = deltaWidth / 128;
211012
- let deltaPadding;
211013
- if (deltaWidth > 0) {
211014
- deltaPadding = steps * 48;
211015
- } else {
211016
- deltaPadding = steps * 6;
211310
+ /**
211311
+ * Pixel cost of the outside decor: the always-rendered north-arrow glyph
211312
+ * plus, only while shown, the NSWE labels (both keep a constant on-screen
211313
+ * size via `1/scale` terms). Feeds the frame's width-aware reserve,
211314
+ * replacing the former empirical `72 + delta(clientSize)` padding.
211315
+ * Wind/current symbols at their default radius are covered by the base
211316
+ * padding of 72.
211317
+ */
211318
+ getOutsideDecorPx() {
211319
+ if (this.tickmarksInside) {
211320
+ return 0;
211017
211321
  }
211018
- return 72 + deltaPadding;
211322
+ return NORTH_ARROW_WIDTH_PX + (this.showLabels ? NSWE_LABEL_WIDTH_PX : 0);
211323
+ }
211324
+ updated(changed) {
211325
+ super.updated(changed);
211326
+ this._hostSizePinned = applyPinnedHostSize(
211327
+ this,
211328
+ this._frame,
211329
+ this._hostSizePinned
211330
+ );
211019
211331
  }
211020
211332
  get angleAdviceRaw() {
211021
211333
  return this.headingAdvices.map(({ minAngle, maxAngle, hinted, type }) => {
@@ -211047,22 +211359,27 @@ let ObcCompass = class extends i$4 {
211047
211359
  { angle: 180, type: TickmarkType$1.main },
211048
211360
  { angle: 270, type: TickmarkType$1.main }
211049
211361
  ];
211050
- const padding = this.getPadding();
211051
- const width = (176 + padding) * 2;
211052
- const viewBox = `-${width / 2} -${width / 2} ${width} ${width}`;
211362
+ const frame = computeRadialFrame({
211363
+ basePadding: 72,
211364
+ labelWidthPx: this.getOutsideDecorPx(),
211365
+ containerPx: measureContainerPx(this),
211366
+ faceDiameter: this.faceDiameter
211367
+ });
211368
+ this._frame = frame;
211369
+ const viewBox = frame.viewBox;
211053
211370
  return b`
211054
211371
  <div class="container">
211055
211372
  <obc-watch
211056
211373
  .touching=${this.touching}
211057
- .padding=${padding}
211374
+ .arcFrame=${frame}
211058
211375
  .advices=${this.angleAdviceRaw}
211059
211376
  .tickmarks=${tickmarks}
211060
211377
  .state=${this.state}
211061
211378
  .watchCircleType=${WatchCircleType.triple}
211062
- .showLabels=${this.showLabels}
211379
+ .showLabels=${this.showLabels && !frame.labelsHidden}
211063
211380
  .tickmarksInside=${this.tickmarksInside}
211064
211381
  .crosshairEnabled=${true}
211065
- .northArrow=${true}
211382
+ .northArrow=${!frame.labelsHidden}
211066
211383
  .angleSetpoint=${this.headingSetpoint ?? void 0}
211067
211384
  .newAngleSetpoint=${this.newHeadingSetpoint}
211068
211385
  .atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}
@@ -211248,6 +211565,9 @@ __decorateClass$D([
211248
211565
  __decorateClass$D([
211249
211566
  n$3({ type: Boolean })
211250
211567
  ], ObcCompass.prototype, "tickmarksInside", 2);
211568
+ __decorateClass$D([
211569
+ n$3({ type: Number, attribute: "face-diameter" })
211570
+ ], ObcCompass.prototype, "faceDiameter", 2);
211251
211571
  ObcCompass = __decorateClass$D([
211252
211572
  customElement("obc-compass")
211253
211573
  ], ObcCompass);
@@ -212895,6 +213215,14 @@ const componentStyle$9 = i$7`* {
212895
213215
  height: 100%;
212896
213216
  }
212897
213217
 
213218
+ /* With a pinned face the host shrink-wraps the fixed-size dial, so rows of
213219
+ pinned instruments can sit side by side in flex layouts. */
213220
+
213221
+ :host([face-diameter]) {
213222
+ width: fit-content;
213223
+ height: fit-content;
213224
+ }
213225
+
212898
213226
  .gauge-radial-root {
212899
213227
  /* Readout vertical placement per layout (% of the cropped box), tuned to the
212900
213228
  watch geometry — re-check if the ring radii or the 448 box change. Which
@@ -212915,7 +213243,10 @@ const componentStyle$9 = i$7`* {
212915
213243
  height: auto;
212916
213244
  max-width: 100%;
212917
213245
  max-height: 100%;
212918
- aspect-ratio: 1;
213246
+ /* --gauge-radial-aspect is set from TS only when the frame lowered a crop
213247
+ for the label drop (frame.clipsAdjusted); otherwise the static sector
213248
+ aspect below applies. */
213249
+ aspect-ratio: var(--gauge-radial-aspect, 1);
212919
213250
 
212920
213251
  /* Defaults (centered both ways); overridden by the halign-* / valign-*
212921
213252
  classes. Horizontal alignment uses auto margins (which reference the host
@@ -212931,7 +213262,7 @@ const componentStyle$9 = i$7`* {
212931
213262
  }
212932
213263
 
212933
213264
  .gauge-radial-root.sector-180 {
212934
- aspect-ratio: 448 / 251;
213265
+ aspect-ratio: var(--gauge-radial-aspect, 448 / 251);
212935
213266
  }
212936
213267
 
212937
213268
  .gauge-radial-root.halign-left {
@@ -212964,6 +213295,27 @@ const componentStyle$9 = i$7`* {
212964
213295
  transform: translateY(-100%);
212965
213296
  }
212966
213297
 
213298
+ /* faceDiameter pins the dial to a fixed intrinsic size: the inner
213299
+ instrument sets its own px box and the root shrink-wraps it. The host
213300
+ shrink-wraps too, so there is no slack to align within — neutralize the
213301
+ alignment offsets (declared after the valign rules to win the cascade;
213302
+ \`top: 50%\` would resolve to 0 in the auto-height host anyway while the
213303
+ translateY still applied, shifting the dial up by half its height). */
213304
+
213305
+ .gauge-radial-root.face-pinned {
213306
+ width: fit-content;
213307
+ height: fit-content;
213308
+ max-width: none;
213309
+ max-height: none;
213310
+ aspect-ratio: auto;
213311
+ top: 0;
213312
+ transform: none;
213313
+ }
213314
+
213315
+ .gauge-radial-root.face-pinned.sector-180 {
213316
+ aspect-ratio: auto;
213317
+ }
213318
+
212967
213319
  .gauge-radial-root .gauge-readout-value {
212968
213320
  position: absolute;
212969
213321
  left: 50%;
@@ -213026,6 +213378,9 @@ var __decorateClass$x = (decorators, target, key, kind) => {
213026
213378
  if (kind && result) __defProp$x(target, key, result);
213027
213379
  return result;
213028
213380
  };
213381
+ const READOUT_META_Y = 98.56;
213382
+ const READOUT_META_Y_NEEDLE = 58.24;
213383
+ const READOUT_META_Y_180 = -73.472;
213029
213384
  let ObcGaugeRadial = class extends SetpointMixin(i$4) {
213030
213385
  constructor() {
213031
213386
  super(...arguments);
@@ -213049,6 +213404,28 @@ let ObcGaugeRadial = class extends SetpointMixin(i$4) {
213049
213404
  this.label = "";
213050
213405
  this.unit = "";
213051
213406
  this.fractionDigits = 0;
213407
+ this._onFrameChanged = (e2) => {
213408
+ const frame = e2.detail;
213409
+ const root = this._rootEl;
213410
+ if (!root || !(frame.height > 0)) {
213411
+ return;
213412
+ }
213413
+ const pct = (anchorY) => `${((anchorY - frame.y) / frame.height * 100).toFixed(4)}%`;
213414
+ root.style.setProperty("--readout-meta-top", pct(READOUT_META_Y));
213415
+ root.style.setProperty(
213416
+ "--readout-meta-top-needle",
213417
+ pct(READOUT_META_Y_NEEDLE)
213418
+ );
213419
+ root.style.setProperty("--readout-meta-top-180", pct(READOUT_META_Y_180));
213420
+ if (frame.clipsAdjusted) {
213421
+ root.style.setProperty(
213422
+ "--gauge-radial-aspect",
213423
+ `${frame.width} / ${frame.height}`
213424
+ );
213425
+ } else {
213426
+ root.style.removeProperty("--gauge-radial-aspect");
213427
+ }
213428
+ };
213052
213429
  this.getAngle = (v2) => {
213053
213430
  const { sweep, start } = this.sectorAngles;
213054
213431
  const span = this.maxValue - this.minValue;
@@ -213154,6 +213531,7 @@ let ObcGaugeRadial = class extends SetpointMixin(i$4) {
213154
213531
  <div
213155
213532
  class=${e$1({
213156
213533
  "gauge-radial-root": true,
213534
+ "face-pinned": this.faceDiameter !== void 0,
213157
213535
  "type-needle": this.type === "needle",
213158
213536
  "sector-180": this.sector === "180",
213159
213537
  "sector-90-left": this.sector === "90-left",
@@ -213191,6 +213569,8 @@ let ObcGaugeRadial = class extends SetpointMixin(i$4) {
213191
213569
  .clipLeft=${clips.left}
213192
213570
  .clipRight=${clips.right}
213193
213571
  .endLabelsMaxMin=${this.sector === "180"}
213572
+ .faceDiameter=${this.faceDiameter}
213573
+ @frame-changed=${this._onFrameChanged}
213194
213574
  >
213195
213575
  </obc-instrument-radial>
213196
213576
  ${this.renderReadouts()}
@@ -213259,6 +213639,12 @@ __decorateClass$x([
213259
213639
  __decorateClass$x([
213260
213640
  n$3({ type: Number })
213261
213641
  ], ObcGaugeRadial.prototype, "fractionDigits", 2);
213642
+ __decorateClass$x([
213643
+ n$3({ type: Number, attribute: "face-diameter", reflect: true })
213644
+ ], ObcGaugeRadial.prototype, "faceDiameter", 2);
213645
+ __decorateClass$x([
213646
+ e$3(".gauge-radial-root")
213647
+ ], ObcGaugeRadial.prototype, "_rootEl", 2);
213262
213648
  ObcGaugeRadial = __decorateClass$x([
213263
213649
  customElement("obc-gauge-radial")
213264
213650
  ], ObcGaugeRadial);
@@ -218664,6 +219050,7 @@ let ObcHeading = class extends i$4 {
218664
219050
  onAnimationEnd: () => this.requestUpdate()
218665
219051
  });
218666
219052
  this._resizeController = new h(this, {});
219053
+ this._hostSizePinned = false;
218667
219054
  }
218668
219055
  willUpdate(changed) {
218669
219056
  super.willUpdate(changed);
@@ -218683,17 +219070,25 @@ let ObcHeading = class extends i$4 {
218683
219070
  super.disconnectedCallback();
218684
219071
  this._headingSp.dispose();
218685
219072
  }
218686
- getPadding() {
218687
- const size = Math.min(this.clientHeight, this.clientWidth);
218688
- const deltaWidth = 512 - size;
218689
- const steps = deltaWidth / 128;
218690
- let deltaPadding;
218691
- if (deltaWidth > 0) {
218692
- deltaPadding = steps * 48;
218693
- } else {
218694
- deltaPadding = steps * 6;
219073
+ /**
219074
+ * Pixel cost of the outside decor: the always-rendered north-arrow glyph
219075
+ * plus, only while shown, the NSWE labels (both keep a constant on-screen
219076
+ * size via `1/scale` terms). Feeds the frame's width-aware reserve,
219077
+ * replacing the former empirical `72 + delta(clientSize)` padding.
219078
+ */
219079
+ getOutsideDecorPx() {
219080
+ if (this.tickmarksInside) {
219081
+ return 0;
218695
219082
  }
218696
- return 72 + deltaPadding;
219083
+ return NORTH_ARROW_WIDTH_PX + (this.showLabels ? NSWE_LABEL_WIDTH_PX : 0);
219084
+ }
219085
+ updated(changed) {
219086
+ super.updated(changed);
219087
+ this._hostSizePinned = applyPinnedHostSize(
219088
+ this,
219089
+ this._frame,
219090
+ this._hostSizePinned
219091
+ );
218697
219092
  }
218698
219093
  get angleAdviceRaw() {
218699
219094
  return this.headingAdvices.map(({ minAngle, maxAngle, hinted, type }) => {
@@ -218722,21 +219117,26 @@ let ObcHeading = class extends i$4 {
218722
219117
  { angle: 180, type: TickmarkType$1.main },
218723
219118
  { angle: 270, type: TickmarkType$1.main }
218724
219119
  ];
218725
- const padding = this.getPadding();
218726
- const width = (176 + padding) * 2;
218727
- const viewBox = `-${width / 2} -${width / 2} ${width} ${width}`;
219120
+ const frame = computeRadialFrame({
219121
+ basePadding: 72,
219122
+ labelWidthPx: this.getOutsideDecorPx(),
219123
+ containerPx: measureContainerPx(this),
219124
+ faceDiameter: this.faceDiameter
219125
+ });
219126
+ this._frame = frame;
219127
+ const viewBox = frame.viewBox;
218728
219128
  return b`
218729
219129
  <div class="container">
218730
219130
  <obc-watch
218731
219131
  .touching=${this.touching}
218732
- .padding=${padding}
219132
+ .arcFrame=${frame}
218733
219133
  .advices=${this.angleAdviceRaw}
218734
219134
  .tickmarks=${tickmarks}
218735
219135
  .watchCircleType=${WatchCircleType.single}
218736
- .showLabels=${this.showLabels}
219136
+ .showLabels=${this.showLabels && !frame.labelsHidden}
218737
219137
  .tickmarksInside=${this.tickmarksInside}
218738
219138
  .crosshairEnabled=${true}
218739
- .northArrow=${true}
219139
+ .northArrow=${!frame.labelsHidden}
218740
219140
  .angleSetpoint=${this.headingSetpoint ?? void 0}
218741
219141
  .newAngleSetpoint=${this.newHeadingSetpoint}
218742
219142
  .atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}
@@ -218845,6 +219245,9 @@ __decorateClass$s([
218845
219245
  __decorateClass$s([
218846
219246
  n$3({ type: Boolean })
218847
219247
  ], ObcHeading.prototype, "tickmarksInside", 2);
219248
+ __decorateClass$s([
219249
+ n$3({ type: Number, attribute: "face-diameter" })
219250
+ ], ObcHeading.prototype, "faceDiameter", 2);
218848
219251
  ObcHeading = __decorateClass$s([
218849
219252
  customElement("obc-heading")
218850
219253
  ], ObcHeading);
@@ -223761,6 +224164,20 @@ let ObcRudder = class extends SetpointMixin(i$4) {
223761
224164
  this.advices = [];
223762
224165
  this.zoomToFitArc = false;
223763
224166
  this._radiusOffset = 0;
224167
+ this._hostSizePinned = false;
224168
+ this._resizeController = new h(this, {});
224169
+ }
224170
+ firstUpdated(changed) {
224171
+ super.firstUpdated(changed);
224172
+ observeInnerBox(this._resizeController, this.renderRoot);
224173
+ }
224174
+ updated(changed) {
224175
+ super.updated(changed);
224176
+ this._hostSizePinned = applyPinnedHostSize(
224177
+ this,
224178
+ this._frame,
224179
+ this._hostSizePinned
224180
+ );
223764
224181
  }
223765
224182
  get _needleTransform() {
223766
224183
  const rOff = this._radiusOffset;
@@ -223875,32 +224292,25 @@ let ObcRudder = class extends SetpointMixin(i$4) {
223875
224292
  state
223876
224293
  };
223877
224294
  });
223878
- let overlayViewBox;
223879
- if (this.zoomToFitArc) {
223880
- const ext = 48;
223881
- const targetSize = (176 + ext) * 2;
223882
- const frame = computeZoomToFitArcFrame({
223883
- areas,
223884
- outerRadius: OUTER_RING_RADIUS,
223885
- innerRadius: innerRingRadiusFor(WatchCircleType.double),
223886
- extension: ext,
223887
- targetSize
223888
- });
223889
- overlayViewBox = frame.viewBox;
223890
- this._radiusOffset = frame.radiusOffset;
223891
- this._arcFrame = frame;
223892
- } else {
223893
- overlayViewBox = "-224 -44.8 448 268.8";
223894
- this._radiusOffset = 0;
223895
- this._arcFrame = void 0;
223896
- }
224295
+ const frame = computeRadialFrame({
224296
+ basePadding: 48,
224297
+ labelWidthPx: this.tickmarksInside ? 0 : estimateLabelWidthPx(tickmarks.map((t2) => t2.text)),
224298
+ clips: this.zoomToFitArc ? void 0 : { top: 40, bottom: 0, left: 0, right: 0 },
224299
+ containerPx: measureContainerPx(this),
224300
+ faceDiameter: this.faceDiameter,
224301
+ zoomToFitArc: this.zoomToFitArc,
224302
+ areas,
224303
+ innerRadius: innerRingRadiusFor(WatchCircleType.double)
224304
+ });
224305
+ this._radiusOffset = frame.radiusOffset;
224306
+ this._frame = frame;
224307
+ const shownTickmarks = frame.labelsHidden ? tickmarks.map((t2) => ({ ...t2, text: void 0 })) : tickmarks;
224308
+ const overlayViewBox = frame.viewBox;
223897
224309
  return b`
223898
224310
  <div class="container">
223899
224311
  <obc-watch
223900
224312
  .touching=${this.touching}
223901
- .clipTop=${this.zoomToFitArc ? 0 : 40}
223902
- .zoomToFitArc=${this.zoomToFitArc}
223903
- .arcFrame=${this._arcFrame}
224313
+ .arcFrame=${frame}
223904
224314
  .areas=${areas}
223905
224315
  .angleSetpoint=${setpointAngle}
223906
224316
  .newAngleSetpoint=${this.newSetpoint !== void 0 ? 180 - this.newSetpoint : void 0}
@@ -223908,8 +224318,7 @@ let ObcRudder = class extends SetpointMixin(i$4) {
223908
224318
  .angleSetpointAtZeroDeadband=${this.setpointAtZeroDeadband}
223909
224319
  .setpointOverride=${this.setpointOverride}
223910
224320
  .animateSetpoint=${this.animateSetpoint}
223911
- .padding=${48}
223912
- .tickmarks=${tickmarks}
224321
+ .tickmarks=${shownTickmarks}
223913
224322
  .tickmarksInside=${this.tickmarksInside}
223914
224323
  .tickmarkStyle=${this.tickmarkStyle}
223915
224324
  .watchCircleType=${WatchCircleType.double}
@@ -223972,6 +224381,9 @@ __decorateClass$9([
223972
224381
  __decorateClass$9([
223973
224382
  n$3({ type: Boolean })
223974
224383
  ], ObcRudder.prototype, "zoomToFitArc", 2);
224384
+ __decorateClass$9([
224385
+ n$3({ type: Number, attribute: "face-diameter" })
224386
+ ], ObcRudder.prototype, "faceDiameter", 2);
223975
224387
  ObcRudder = __decorateClass$9([
223976
224388
  customElement("obc-rudder")
223977
224389
  ], ObcRudder);
@@ -224175,8 +224587,22 @@ let ObcSpeedGauge = class extends SetpointMixin(i$4) {
224175
224587
  this.label = "STW";
224176
224588
  this.unit = "KN";
224177
224589
  this.fractionDigits = 1;
224590
+ this._hostSizePinned = false;
224591
+ this._resizeController = new h(this, {});
224178
224592
  this.maxAngle = 180 - 45;
224179
224593
  }
224594
+ firstUpdated(changed) {
224595
+ super.firstUpdated(changed);
224596
+ observeInnerBox(this._resizeController, this.renderRoot);
224597
+ }
224598
+ updated(changed) {
224599
+ super.updated(changed);
224600
+ this._hostSizePinned = applyPinnedHostSize(
224601
+ this,
224602
+ this._frame,
224603
+ this._hostSizePinned
224604
+ );
224605
+ }
224180
224606
  getAngle(v2) {
224181
224607
  return v2 / this.maxSpeed * (180 + 45) - 90;
224182
224608
  }
@@ -224187,6 +224613,15 @@ let ObcSpeedGauge = class extends SetpointMixin(i$4) {
224187
224613
  const barColor = this.priority === Priority.enhanced ? "var(--instrument-enhanced-tertiary-color)" : "var(--instrument-regular-tertiary-color)";
224188
224614
  const setpointAngle = this.setpoint !== void 0 ? this.getAngle(this.setpoint) : void 0;
224189
224615
  const maxDigits = 1;
224616
+ const tickmarks = this.tickmarks;
224617
+ const frame = computeRadialFrame({
224618
+ basePadding: 48,
224619
+ labelWidthPx: this.tickmarksInside ? 0 : estimateLabelWidthPx(tickmarks.map((t2) => t2.text)),
224620
+ containerPx: measureContainerPx(this),
224621
+ faceDiameter: this.faceDiameter
224622
+ });
224623
+ this._frame = frame;
224624
+ const shownTickmarks = frame.labelsHidden ? tickmarks.map((t2) => ({ ...t2, text: void 0 })) : tickmarks;
224190
224625
  return b`
224191
224626
  <div class="container">
224192
224627
  <obc-watch
@@ -224197,8 +224632,8 @@ let ObcSpeedGauge = class extends SetpointMixin(i$4) {
224197
224632
  .angleSetpointAtZeroDeadband=${this.setpointAtZeroDeadband}
224198
224633
  .setpointOverride=${this.setpointOverride}
224199
224634
  .animateSetpoint=${this.animateSetpoint}
224200
- .padding=${48}
224201
- .tickmarks=${this.tickmarks}
224635
+ .arcFrame=${frame}
224636
+ .tickmarks=${shownTickmarks}
224202
224637
  .tickmarksInside=${this.tickmarksInside}
224203
224638
  .tickmarkStyle=${this.tickmarkStyle}
224204
224639
  .advices=${this._advices}
@@ -224219,7 +224654,7 @@ let ObcSpeedGauge = class extends SetpointMixin(i$4) {
224219
224654
  }
224220
224655
  ]}
224221
224656
  ></obc-watch>
224222
- <svg class="rudder" viewBox="-224 -224 448 448">${this.needle}</svg>
224657
+ <svg class="rudder" viewBox=${frame.viewBox}>${this.needle}</svg>
224223
224658
  ${this.hasReadout ? b`
224224
224659
  ${renderInstrumentReadout({
224225
224660
  className: "speed-gauge-value",
@@ -224388,6 +224823,9 @@ __decorateClass$7([
224388
224823
  __decorateClass$7([
224389
224824
  n$3({ type: Number })
224390
224825
  ], ObcSpeedGauge.prototype, "fractionDigits", 2);
224826
+ __decorateClass$7([
224827
+ n$3({ type: Number, attribute: "face-diameter" })
224828
+ ], ObcSpeedGauge.prototype, "faceDiameter", 2);
224391
224829
  ObcSpeedGauge = __decorateClass$7([
224392
224830
  customElement("obc-speed-gauge")
224393
224831
  ], ObcSpeedGauge);