@oicl/openbridge-webcomponents 2.0.0-next.101 → 2.0.0-next.103

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 (77) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +580 -143
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +629 -407
  4. package/dist/bars-graphs/donut-chart/donut-chart.d.ts +18 -16
  5. package/dist/bars-graphs/donut-chart/donut-chart.d.ts.map +1 -1
  6. package/dist/bars-graphs/donut-chart/donut-chart.js.map +1 -1
  7. package/dist/bars-graphs/pie-chart/pie-chart.d.ts +14 -12
  8. package/dist/bars-graphs/pie-chart/pie-chart.d.ts.map +1 -1
  9. package/dist/bars-graphs/pie-chart/pie-chart.js.map +1 -1
  10. package/dist/bars-graphs/polar-chart/polar-chart.d.ts +19 -16
  11. package/dist/bars-graphs/polar-chart/polar-chart.d.ts.map +1 -1
  12. package/dist/bars-graphs/polar-chart/polar-chart.js.map +1 -1
  13. package/dist/bars-graphs/radial-bar-chart/radial-bar-chart.d.ts +8 -8
  14. package/dist/bars-graphs/radial-bar-chart/radial-bar-chart.d.ts.map +1 -1
  15. package/dist/bars-graphs/radial-bar-chart/radial-bar-chart.js.map +1 -1
  16. package/dist/building-blocks/chart-line/chart-line-base.d.ts +8 -25
  17. package/dist/building-blocks/chart-line/chart-line-base.d.ts.map +1 -1
  18. package/dist/building-blocks/chart-line/chart-line-base.js.map +1 -1
  19. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts +22 -2
  20. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts.map +1 -1
  21. package/dist/building-blocks/instrument-radial/instrument-radial.js +61 -37
  22. package/dist/building-blocks/instrument-radial/instrument-radial.js.map +1 -1
  23. package/dist/integration-systems/integration-bar/integration-bar.d.ts +20 -24
  24. package/dist/integration-systems/integration-bar/integration-bar.d.ts.map +1 -1
  25. package/dist/integration-systems/integration-bar/integration-bar.js.map +1 -1
  26. package/dist/integration-systems/integration-button/integration-button.d.ts +15 -13
  27. package/dist/integration-systems/integration-button/integration-button.d.ts.map +1 -1
  28. package/dist/integration-systems/integration-button/integration-button.js.map +1 -1
  29. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.d.ts +14 -0
  30. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.d.ts.map +1 -1
  31. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.js +30 -11
  32. package/dist/navigation-instruments/azimuth-thruster/azimuth-thruster.js.map +1 -1
  33. package/dist/navigation-instruments/compass/compass.d.ts +68 -31
  34. package/dist/navigation-instruments/compass/compass.d.ts.map +1 -1
  35. package/dist/navigation-instruments/compass/compass.js +36 -16
  36. package/dist/navigation-instruments/compass/compass.js.map +1 -1
  37. package/dist/navigation-instruments/compass-flat/compass-flat.d.ts +8 -9
  38. package/dist/navigation-instruments/compass-flat/compass-flat.d.ts.map +1 -1
  39. package/dist/navigation-instruments/compass-flat/compass-flat.js.map +1 -1
  40. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts.map +1 -1
  41. package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
  42. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js +34 -2
  43. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js.map +1 -1
  44. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts +15 -0
  45. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts.map +1 -1
  46. package/dist/navigation-instruments/gauge-radial/gauge-radial.js +35 -1
  47. package/dist/navigation-instruments/gauge-radial/gauge-radial.js.map +1 -1
  48. package/dist/navigation-instruments/gauge-trend/gauge-trend.d.ts +0 -5
  49. package/dist/navigation-instruments/gauge-trend/gauge-trend.d.ts.map +1 -1
  50. package/dist/navigation-instruments/gauge-trend/gauge-trend.js.map +1 -1
  51. package/dist/navigation-instruments/graph-mini/graph-mini.d.ts +1 -1
  52. package/dist/navigation-instruments/graph-mini/graph-mini.d.ts.map +1 -1
  53. package/dist/navigation-instruments/graph-mini/graph-mini.js.map +1 -1
  54. package/dist/navigation-instruments/heading/heading.d.ts +19 -1
  55. package/dist/navigation-instruments/heading/heading.d.ts.map +1 -1
  56. package/dist/navigation-instruments/heading/heading.js +34 -16
  57. package/dist/navigation-instruments/heading/heading.js.map +1 -1
  58. package/dist/navigation-instruments/indicator-graph/indicator-graph.d.ts +1 -1
  59. package/dist/navigation-instruments/indicator-graph/indicator-graph.d.ts.map +1 -1
  60. package/dist/navigation-instruments/indicator-graph/indicator-graph.js.map +1 -1
  61. package/dist/navigation-instruments/rudder/rudder.d.ts +15 -2
  62. package/dist/navigation-instruments/rudder/rudder.d.ts.map +1 -1
  63. package/dist/navigation-instruments/rudder/rudder.js +36 -26
  64. package/dist/navigation-instruments/rudder/rudder.js.map +1 -1
  65. package/dist/navigation-instruments/speed-gauge/speed-gauge.d.ts +15 -1
  66. package/dist/navigation-instruments/speed-gauge/speed-gauge.d.ts.map +1 -1
  67. package/dist/navigation-instruments/speed-gauge/speed-gauge.js +31 -3
  68. package/dist/navigation-instruments/speed-gauge/speed-gauge.js.map +1 -1
  69. package/dist/navigation-instruments/watch/watch.d.ts +45 -17
  70. package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
  71. package/dist/navigation-instruments/watch/watch.js +53 -38
  72. package/dist/navigation-instruments/watch/watch.js.map +1 -1
  73. package/dist/svghelpers/radial-frame.d.ts +122 -0
  74. package/dist/svghelpers/radial-frame.d.ts.map +1 -0
  75. package/dist/svghelpers/radial-frame.js +259 -0
  76. package/dist/svghelpers/radial-frame.js.map +1 -0
  77. 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,10 +54175,13 @@ 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;
53937
54183
  }
54184
+ /** Legacy spin speed of the ROT dot ring, in rotations per minute (sign controls direction; positive = clockwise). */
53938
54185
  get rotationsPerMinute() {
53939
54186
  return this._legacyRotationsPerMinute;
53940
54187
  }
@@ -53951,6 +54198,10 @@ let ObcWatch = class extends i$4 {
53951
54198
  }
53952
54199
  return this._legacyRotationsPerMinute;
53953
54200
  }
54201
+ firstUpdated(changed) {
54202
+ super.firstUpdated(changed);
54203
+ observeInnerBox(this._resizeController, this.renderRoot);
54204
+ }
53954
54205
  willUpdate(changed) {
53955
54206
  super.willUpdate(changed);
53956
54207
  if (this._rotController && (changed.has("rateOfTurnDegreesPerMinute") || changed.has("rotDotAnimationFactor") || changed.has("rotationsPerMinute"))) {
@@ -53975,6 +54226,11 @@ let ObcWatch = class extends i$4 {
53975
54226
  }
53976
54227
  updated(changed) {
53977
54228
  super.updated(changed);
54229
+ this._hostSizePinned = applyPinnedHostSize(
54230
+ this,
54231
+ this.arcFrame ? void 0 : this._ownFrame,
54232
+ this._hostSizePinned
54233
+ );
53978
54234
  const el = this.rotType ? this.renderRoot.querySelector("#rot-spinner") : null;
53979
54235
  if (!el) {
53980
54236
  this._rotController = disposeRotController(this, this._rotController);
@@ -54272,30 +54528,26 @@ let ObcWatch = class extends i$4 {
54272
54528
  });
54273
54529
  }
54274
54530
  getScale({ width, height }) {
54275
- let clientWidth = this.clientWidth;
54276
- let clientHeight = this.clientHeight;
54277
- if (clientWidth === 0 || clientHeight === 0) {
54278
- const box = this.parentElement?.getBoundingClientRect();
54279
- if (box) {
54280
- clientWidth = box.width;
54281
- clientHeight = box.height;
54282
- }
54283
- }
54284
- const scale = Math.min(clientWidth / width, clientHeight / height);
54531
+ const container = measureContainerPx(this);
54532
+ const scale = Math.min(container.width / width, container.height / height);
54285
54533
  if (!Number.isFinite(scale) || scale <= 0) {
54286
54534
  return 1;
54287
54535
  }
54288
54536
  return scale;
54289
54537
  }
54290
- getPadding() {
54291
- if (this.padding !== void 0) {
54292
- 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;
54293
54546
  }
54294
- const hasTickmarksWithText = this.tickmarks.length > 0 && this.tickmarks.some((t2) => t2.text !== void 0);
54295
- if (hasTickmarksWithText && !this.tickmarksInside) {
54296
- return 24 * 2.5;
54547
+ if (this.showLabels) {
54548
+ return NSWE_LABEL_WIDTH_PX;
54297
54549
  }
54298
- return 24;
54550
+ return estimateLabelWidthPx(this.tickmarks.map((t2) => t2.text));
54299
54551
  }
54300
54552
  render() {
54301
54553
  let width;
@@ -54303,31 +54555,33 @@ let ObcWatch = class extends i$4 {
54303
54555
  let viewBox;
54304
54556
  if (this.arcFrame) {
54305
54557
  this._rOff = this.arcFrame.radiusOffset;
54558
+ this._labelsHidden = false;
54559
+ this._ownFrame = void 0;
54306
54560
  width = this.arcFrame.width;
54307
54561
  height = this.arcFrame.height;
54308
54562
  viewBox = this.arcFrame.viewBox;
54309
- } else if (this.zoomToFitArc && this.areas.length > 0) {
54310
- const ext = this.getPadding();
54311
- const targetSize = (176 + ext) * 2;
54312
- 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,
54313
54576
  areas: this.areas,
54314
- outerRadius: OUTER_RING_RADIUS,
54315
- innerRadius: this.innerRingRadius,
54316
- extension: ext,
54317
- targetSize
54577
+ innerRadius: this.innerRingRadius
54318
54578
  });
54319
54579
  this._rOff = frame.radiusOffset;
54580
+ this._labelsHidden = frame.labelsHidden;
54581
+ this._ownFrame = frame;
54320
54582
  width = frame.width;
54321
54583
  height = frame.height;
54322
54584
  viewBox = frame.viewBox;
54323
- } else {
54324
- this._rOff = 0;
54325
- const full = (176 + this.getPadding()) * 2;
54326
- width = full * (1 - this.clipLeft / 100 - this.clipRight / 100);
54327
- height = full * (1 - this.clipTop / 100 - this.clipBottom / 100);
54328
- const left = -full / 2 + full * this.clipLeft / 100;
54329
- const top = -full / 2 + full * this.clipTop / 100;
54330
- viewBox = `${left} ${top} ${width} ${height}`;
54331
54585
  }
54332
54586
  const rOff = this._rOff;
54333
54587
  const scale = this.getScale({ width, height });
@@ -54341,7 +54595,7 @@ let ObcWatch = class extends i$4 {
54341
54595
  size: t2.type,
54342
54596
  style: this.tickmarkStyle,
54343
54597
  scale,
54344
- text: this.showLabels ? void 0 : t2.text,
54598
+ text: this.showLabels || this._labelsHidden ? void 0 : t2.text,
54345
54599
  inside: this.tickmarksInside,
54346
54600
  textRadius,
54347
54601
  rotation: this.rotation,
@@ -54352,9 +54606,11 @@ let ObcWatch = class extends i$4 {
54352
54606
  })
54353
54607
  );
54354
54608
  const advices = this.advices ? this.advices.map((a2) => renderAdvice$3(a2, rOff)) : A;
54355
- 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;
54356
54612
  const includeNorth = !this.northArrow;
54357
- const labelPositions = this.showLabels ? getLabelPositions({
54613
+ const labelPositions = showNsweLabels ? getLabelPositions({
54358
54614
  scale,
54359
54615
  inside: this.tickmarksInside,
54360
54616
  innerRadius: this.innerRingRadius + rOff,
@@ -54367,7 +54623,7 @@ let ObcWatch = class extends i$4 {
54367
54623
  innerRadius: this.innerRingRadius + rOff,
54368
54624
  includeNorth
54369
54625
  }) : A;
54370
- const northArrowEl = this.northArrow ? renderNorthArrow({
54626
+ const northArrowEl = showNorthArrow ? renderNorthArrow({
54371
54627
  scale,
54372
54628
  rotation: this.rotation,
54373
54629
  inside: this.northArrowInside ?? this.tickmarksInside
@@ -54650,6 +54906,9 @@ __decorateClass$vV([
54650
54906
  __decorateClass$vV([
54651
54907
  n$3({ type: Number })
54652
54908
  ], ObcWatch.prototype, "padding", 2);
54909
+ __decorateClass$vV([
54910
+ n$3({ type: Number, attribute: "face-diameter" })
54911
+ ], ObcWatch.prototype, "faceDiameter", 2);
54653
54912
  __decorateClass$vV([
54654
54913
  n$3({ type: Array, attribute: false })
54655
54914
  ], ObcWatch.prototype, "areas", 2);
@@ -54780,7 +55039,6 @@ var __decorateClass$vU = (decorators, target, key, kind) => {
54780
55039
  if (kind && result) __defProp$vQ(target, key, result);
54781
55040
  return result;
54782
55041
  };
54783
- const WATCH_DEFAULT_VIEWBOX = 448;
54784
55042
  const NEEDLE_TIP_RADIUS$1 = 160;
54785
55043
  const NEEDLE_TIP_GAP = 5;
54786
55044
  const NEEDLE_WIDTH = 8;
@@ -54849,6 +55107,36 @@ let ObcInstrumentRadial = class extends SetpointMixin(i$4) {
54849
55107
  this.endLabelsMaxMin = false;
54850
55108
  this.zoomToFitArc = false;
54851
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
+ }
54852
55140
  }
54853
55141
  get clampedValue() {
54854
55142
  const lowerBound = Math.min(this.minValue, this.maxValue);
@@ -54915,31 +55203,28 @@ let ObcInstrumentRadial = class extends SetpointMixin(i$4) {
54915
55203
  }
54916
55204
  ];
54917
55205
  const watchCircleType = this.type === "needle" ? WatchCircleType.single : WatchCircleType.double;
54918
- const clips = this.safeClips;
54919
- let viewBox;
54920
- if (this.zoomToFitArc) {
54921
- const ext = 48;
54922
- const targetSize = (176 + ext) * 2;
54923
- const frame = computeZoomToFitArcFrame({
54924
- areas,
54925
- outerRadius: OUTER_RING_RADIUS,
54926
- innerRadius: innerRingRadiusFor(watchCircleType),
54927
- extension: ext,
54928
- targetSize
54929
- });
54930
- this._radiusOffset = frame.radiusOffset;
54931
- viewBox = frame.viewBox;
54932
- this._arcFrame = frame;
54933
- } else {
54934
- this._radiusOffset = 0;
54935
- this._arcFrame = void 0;
54936
- const full = WATCH_DEFAULT_VIEWBOX;
54937
- const w2 = full * (1 - clips.left / 100 - clips.right / 100);
54938
- const h3 = full * (1 - clips.top / 100 - clips.bottom / 100);
54939
- const left = -full / 2 + full * clips.left / 100;
54940
- const top = -full / 2 + full * clips.top / 100;
54941
- viewBox = `${left} ${top} ${w2} ${h3}`;
54942
- }
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;
54943
55228
  return b`
54944
55229
  <div class="container">
54945
55230
  <obc-watch
@@ -54951,23 +55236,17 @@ let ObcInstrumentRadial = class extends SetpointMixin(i$4) {
54951
55236
  .angleSetpointAtZeroDeadband=${this.setpointAtZeroDeadband}
54952
55237
  .setpointOverride=${this.setpointOverride}
54953
55238
  .animateSetpoint=${this.animateSetpoint}
54954
- .padding=${48}
54955
- .tickmarks=${this.tickmarks}
55239
+ .tickmarks=${shownTickmarks}
54956
55240
  .tickmarksInside=${this.tickmarksInside}
54957
55241
  .tickmarkStyle=${this.tickmarkStyle}
54958
55242
  .advices=${this._advices}
54959
55243
  .areas=${areas}
54960
55244
  .watchCircleType=${watchCircleType}
54961
55245
  .barAreas=${barAreas}
54962
- .clipTop=${this.zoomToFitArc ? 0 : clips.top}
54963
- .clipBottom=${this.zoomToFitArc ? 0 : clips.bottom}
54964
- .clipLeft=${this.zoomToFitArc ? 0 : clips.left}
54965
- .clipRight=${this.zoomToFitArc ? 0 : clips.right}
54966
55246
  .endLabelsMaxMin=${this.endLabelsMaxMin}
54967
- .zoomToFitArc=${this.zoomToFitArc}
54968
- .arcFrame=${this._arcFrame}
55247
+ .arcFrame=${frame}
54969
55248
  ></obc-watch>
54970
- <svg class="gauge-radial" viewBox=${viewBox}>${this._needle}</svg>
55249
+ <svg class="gauge-radial" viewBox=${frame.viewBox}>${this._needle}</svg>
54971
55250
  </div>
54972
55251
  `;
54973
55252
  }
@@ -55191,6 +55470,9 @@ __decorateClass$vU([
55191
55470
  __decorateClass$vU([
55192
55471
  n$3({ type: Boolean })
55193
55472
  ], ObcInstrumentRadial.prototype, "zoomToFitArc", 2);
55473
+ __decorateClass$vU([
55474
+ n$3({ type: Number, attribute: "face-diameter" })
55475
+ ], ObcInstrumentRadial.prototype, "faceDiameter", 2);
55194
55476
  ObcInstrumentRadial = __decorateClass$vU([
55195
55477
  customElement("obc-instrument-radial")
55196
55478
  ], ObcInstrumentRadial);
@@ -208682,6 +208964,8 @@ let ObcAzimuthThruster = class extends i$4 {
208682
208964
  this.bottomPropeller = PropellerType.none;
208683
208965
  this.tickmarkStyle = TickmarkStyle$1.regular;
208684
208966
  this.starboardPortIndicator = false;
208967
+ this._hostSizePinned = false;
208968
+ this._resizeController = new h(this, {});
208685
208969
  }
208686
208970
  willUpdate(changed) {
208687
208971
  super.willUpdate(changed);
@@ -208708,6 +208992,18 @@ let ObcAzimuthThruster = class extends i$4 {
208708
208992
  animateSetpoint: this.animateSetpoint
208709
208993
  });
208710
208994
  }
208995
+ firstUpdated(changed) {
208996
+ super.firstUpdated(changed);
208997
+ observeInnerBox(this._resizeController, this.renderRoot);
208998
+ }
208999
+ updated(changed) {
209000
+ super.updated(changed);
209001
+ this._hostSizePinned = applyPinnedHostSize(
209002
+ this,
209003
+ this._frame,
209004
+ this._hostSizePinned
209005
+ );
209006
+ }
208711
209007
  get angleAdviceRaw() {
208712
209008
  return this.angleAdvices.map((advice) => {
208713
209009
  const triggered = this.angleSetpoint !== void 0 && mapAngle0to360$1(this.angleSetpoint - advice.minAngle) < 180 && mapAngle0to360$1(this.angleSetpoint - advice.maxAngle) > 180;
@@ -208779,19 +209075,21 @@ let ObcAzimuthThruster = class extends i$4 {
208779
209075
  render() {
208780
209076
  const rotateAngle = this.angle;
208781
209077
  const tickmarks = this.getTickmarks();
208782
- let viewBox;
208783
- if (!this.hasLabelSpacer) {
208784
- viewBox = "-192 -192 384 384";
208785
- } else if (this.showLabels && !this.tickmarksInside) {
208786
- viewBox = "-236 -236 472 472";
208787
- } else {
208788
- viewBox = "-200 -200 400 400";
208789
- }
209078
+ const frame = computeRadialFrame({
209079
+ basePadding: this.hasLabelSpacer ? 24 : 16,
209080
+ labelWidthPx: this.hasLabelSpacer && !this.tickmarksInside ? estimateLabelWidthPx(tickmarks.map((t2) => t2.text)) : 0,
209081
+ containerPx: measureContainerPx(this),
209082
+ faceDiameter: this.faceDiameter
209083
+ });
209084
+ this._frame = frame;
209085
+ const shownTickmarks = frame.labelsHidden ? tickmarks.map((t2) => ({ ...t2, text: void 0 })) : tickmarks;
209086
+ const viewBox = frame.viewBox;
208790
209087
  return b`
208791
209088
  <div class="container">
208792
209089
  <obc-watch
208793
209090
  .touching=${this.touching}
208794
- .tickmarks=${tickmarks}
209091
+ .arcFrame=${frame}
209092
+ .tickmarks=${shownTickmarks}
208795
209093
  .state=${this.state}
208796
209094
  .priority=${this.priority}
208797
209095
  .angleSetpoint=${this.angleSetpoint}
@@ -208802,7 +209100,6 @@ let ObcAzimuthThruster = class extends i$4 {
208802
209100
  .animateSetpoint=${this.animateSetpoint}
208803
209101
  .tickmarksInside=${this.tickmarksInside}
208804
209102
  .tickmarkStyle=${this.tickmarkStyle}
208805
- padding=${o$4(!this.hasLabelSpacer ? 16 : void 0)}
208806
209103
  .advices=${this.angleAdviceRaw}
208807
209104
  .starboardPortIndicator=${this.starboardPortIndicator}
208808
209105
  ></obc-watch>
@@ -208948,6 +209245,9 @@ __decorateClass$K([
208948
209245
  __decorateClass$K([
208949
209246
  n$3({ type: Boolean })
208950
209247
  ], ObcAzimuthThruster.prototype, "starboardPortIndicator", 2);
209248
+ __decorateClass$K([
209249
+ n$3({ type: Number, attribute: "face-diameter" })
209250
+ ], ObcAzimuthThruster.prototype, "faceDiameter", 2);
208951
209251
  ObcAzimuthThruster = __decorateClass$K([
208952
209252
  customElement("obc-azimuth-thruster")
208953
209253
  ], ObcAzimuthThruster);
@@ -210976,6 +211276,7 @@ let ObcCompass = class extends i$4 {
210976
211276
  onAnimationEnd: () => this.requestUpdate()
210977
211277
  });
210978
211278
  this._resizeController = new h(this, {});
211279
+ this._hostSizePinned = false;
210979
211280
  }
210980
211281
  willUpdate(changed) {
210981
211282
  super.willUpdate(changed);
@@ -211004,17 +211305,27 @@ let ObcCompass = class extends i$4 {
211004
211305
  get _effectiveRotDegPerMin() {
211005
211306
  return this.rateOfTurnDegreesPerMinute ?? this.rotationsPerMinute;
211006
211307
  }
211007
- getPadding() {
211008
- const size = Math.min(this.clientHeight, this.clientWidth);
211009
- const deltaWidth = 512 - size;
211010
- const steps = deltaWidth / 128;
211011
- let deltaPadding;
211012
- if (deltaWidth > 0) {
211013
- deltaPadding = steps * 48;
211014
- } else {
211015
- deltaPadding = steps * 6;
211308
+ /**
211309
+ * Pixel cost of the outside decor: the always-rendered north-arrow glyph
211310
+ * plus, only while shown, the NSWE labels (both keep a constant on-screen
211311
+ * size via `1/scale` terms). Feeds the frame's width-aware reserve,
211312
+ * replacing the former empirical `72 + delta(clientSize)` padding.
211313
+ * Wind/current symbols at their default radius are covered by the base
211314
+ * padding of 72.
211315
+ */
211316
+ getOutsideDecorPx() {
211317
+ if (this.tickmarksInside) {
211318
+ return 0;
211016
211319
  }
211017
- return 72 + deltaPadding;
211320
+ return NORTH_ARROW_WIDTH_PX + (this.showLabels ? NSWE_LABEL_WIDTH_PX : 0);
211321
+ }
211322
+ updated(changed) {
211323
+ super.updated(changed);
211324
+ this._hostSizePinned = applyPinnedHostSize(
211325
+ this,
211326
+ this._frame,
211327
+ this._hostSizePinned
211328
+ );
211018
211329
  }
211019
211330
  get angleAdviceRaw() {
211020
211331
  return this.headingAdvices.map(({ minAngle, maxAngle, hinted, type }) => {
@@ -211046,22 +211357,27 @@ let ObcCompass = class extends i$4 {
211046
211357
  { angle: 180, type: TickmarkType$1.main },
211047
211358
  { angle: 270, type: TickmarkType$1.main }
211048
211359
  ];
211049
- const padding = this.getPadding();
211050
- const width = (176 + padding) * 2;
211051
- const viewBox = `-${width / 2} -${width / 2} ${width} ${width}`;
211360
+ const frame = computeRadialFrame({
211361
+ basePadding: 72,
211362
+ labelWidthPx: this.getOutsideDecorPx(),
211363
+ containerPx: measureContainerPx(this),
211364
+ faceDiameter: this.faceDiameter
211365
+ });
211366
+ this._frame = frame;
211367
+ const viewBox = frame.viewBox;
211052
211368
  return b`
211053
211369
  <div class="container">
211054
211370
  <obc-watch
211055
211371
  .touching=${this.touching}
211056
- .padding=${padding}
211372
+ .arcFrame=${frame}
211057
211373
  .advices=${this.angleAdviceRaw}
211058
211374
  .tickmarks=${tickmarks}
211059
211375
  .state=${this.state}
211060
211376
  .watchCircleType=${WatchCircleType.triple}
211061
- .showLabels=${this.showLabels}
211377
+ .showLabels=${this.showLabels && !frame.labelsHidden}
211062
211378
  .tickmarksInside=${this.tickmarksInside}
211063
211379
  .crosshairEnabled=${true}
211064
- .northArrow=${true}
211380
+ .northArrow=${!frame.labelsHidden}
211065
211381
  .angleSetpoint=${this.headingSetpoint ?? void 0}
211066
211382
  .newAngleSetpoint=${this.newHeadingSetpoint}
211067
211383
  .atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}
@@ -211247,6 +211563,9 @@ __decorateClass$D([
211247
211563
  __decorateClass$D([
211248
211564
  n$3({ type: Boolean })
211249
211565
  ], ObcCompass.prototype, "tickmarksInside", 2);
211566
+ __decorateClass$D([
211567
+ n$3({ type: Number, attribute: "face-diameter" })
211568
+ ], ObcCompass.prototype, "faceDiameter", 2);
211250
211569
  ObcCompass = __decorateClass$D([
211251
211570
  customElement("obc-compass")
211252
211571
  ], ObcCompass);
@@ -212894,6 +213213,14 @@ const componentStyle$9 = i$7`* {
212894
213213
  height: 100%;
212895
213214
  }
212896
213215
 
213216
+ /* With a pinned face the host shrink-wraps the fixed-size dial, so rows of
213217
+ pinned instruments can sit side by side in flex layouts. */
213218
+
213219
+ :host([face-diameter]) {
213220
+ width: fit-content;
213221
+ height: fit-content;
213222
+ }
213223
+
212897
213224
  .gauge-radial-root {
212898
213225
  /* Readout vertical placement per layout (% of the cropped box), tuned to the
212899
213226
  watch geometry — re-check if the ring radii or the 448 box change. Which
@@ -212914,7 +213241,10 @@ const componentStyle$9 = i$7`* {
212914
213241
  height: auto;
212915
213242
  max-width: 100%;
212916
213243
  max-height: 100%;
212917
- aspect-ratio: 1;
213244
+ /* --gauge-radial-aspect is set from TS only when the frame lowered a crop
213245
+ for the label drop (frame.clipsAdjusted); otherwise the static sector
213246
+ aspect below applies. */
213247
+ aspect-ratio: var(--gauge-radial-aspect, 1);
212918
213248
 
212919
213249
  /* Defaults (centered both ways); overridden by the halign-* / valign-*
212920
213250
  classes. Horizontal alignment uses auto margins (which reference the host
@@ -212930,7 +213260,7 @@ const componentStyle$9 = i$7`* {
212930
213260
  }
212931
213261
 
212932
213262
  .gauge-radial-root.sector-180 {
212933
- aspect-ratio: 448 / 251;
213263
+ aspect-ratio: var(--gauge-radial-aspect, 448 / 251);
212934
213264
  }
212935
213265
 
212936
213266
  .gauge-radial-root.halign-left {
@@ -212963,6 +213293,27 @@ const componentStyle$9 = i$7`* {
212963
213293
  transform: translateY(-100%);
212964
213294
  }
212965
213295
 
213296
+ /* faceDiameter pins the dial to a fixed intrinsic size: the inner
213297
+ instrument sets its own px box and the root shrink-wraps it. The host
213298
+ shrink-wraps too, so there is no slack to align within — neutralize the
213299
+ alignment offsets (declared after the valign rules to win the cascade;
213300
+ \`top: 50%\` would resolve to 0 in the auto-height host anyway while the
213301
+ translateY still applied, shifting the dial up by half its height). */
213302
+
213303
+ .gauge-radial-root.face-pinned {
213304
+ width: fit-content;
213305
+ height: fit-content;
213306
+ max-width: none;
213307
+ max-height: none;
213308
+ aspect-ratio: auto;
213309
+ top: 0;
213310
+ transform: none;
213311
+ }
213312
+
213313
+ .gauge-radial-root.face-pinned.sector-180 {
213314
+ aspect-ratio: auto;
213315
+ }
213316
+
212966
213317
  .gauge-radial-root .gauge-readout-value {
212967
213318
  position: absolute;
212968
213319
  left: 50%;
@@ -213025,6 +213376,9 @@ var __decorateClass$x = (decorators, target, key, kind) => {
213025
213376
  if (kind && result) __defProp$x(target, key, result);
213026
213377
  return result;
213027
213378
  };
213379
+ const READOUT_META_Y = 98.56;
213380
+ const READOUT_META_Y_NEEDLE = 58.24;
213381
+ const READOUT_META_Y_180 = -73.472;
213028
213382
  let ObcGaugeRadial = class extends SetpointMixin(i$4) {
213029
213383
  constructor() {
213030
213384
  super(...arguments);
@@ -213048,6 +213402,28 @@ let ObcGaugeRadial = class extends SetpointMixin(i$4) {
213048
213402
  this.label = "";
213049
213403
  this.unit = "";
213050
213404
  this.fractionDigits = 0;
213405
+ this._onFrameChanged = (e2) => {
213406
+ const frame = e2.detail;
213407
+ const root = this._rootEl;
213408
+ if (!root || !(frame.height > 0)) {
213409
+ return;
213410
+ }
213411
+ const pct = (anchorY) => `${((anchorY - frame.y) / frame.height * 100).toFixed(4)}%`;
213412
+ root.style.setProperty("--readout-meta-top", pct(READOUT_META_Y));
213413
+ root.style.setProperty(
213414
+ "--readout-meta-top-needle",
213415
+ pct(READOUT_META_Y_NEEDLE)
213416
+ );
213417
+ root.style.setProperty("--readout-meta-top-180", pct(READOUT_META_Y_180));
213418
+ if (frame.clipsAdjusted) {
213419
+ root.style.setProperty(
213420
+ "--gauge-radial-aspect",
213421
+ `${frame.width} / ${frame.height}`
213422
+ );
213423
+ } else {
213424
+ root.style.removeProperty("--gauge-radial-aspect");
213425
+ }
213426
+ };
213051
213427
  this.getAngle = (v2) => {
213052
213428
  const { sweep, start } = this.sectorAngles;
213053
213429
  const span = this.maxValue - this.minValue;
@@ -213153,6 +213529,7 @@ let ObcGaugeRadial = class extends SetpointMixin(i$4) {
213153
213529
  <div
213154
213530
  class=${e$1({
213155
213531
  "gauge-radial-root": true,
213532
+ "face-pinned": this.faceDiameter !== void 0,
213156
213533
  "type-needle": this.type === "needle",
213157
213534
  "sector-180": this.sector === "180",
213158
213535
  "sector-90-left": this.sector === "90-left",
@@ -213190,6 +213567,8 @@ let ObcGaugeRadial = class extends SetpointMixin(i$4) {
213190
213567
  .clipLeft=${clips.left}
213191
213568
  .clipRight=${clips.right}
213192
213569
  .endLabelsMaxMin=${this.sector === "180"}
213570
+ .faceDiameter=${this.faceDiameter}
213571
+ @frame-changed=${this._onFrameChanged}
213193
213572
  >
213194
213573
  </obc-instrument-radial>
213195
213574
  ${this.renderReadouts()}
@@ -213258,6 +213637,12 @@ __decorateClass$x([
213258
213637
  __decorateClass$x([
213259
213638
  n$3({ type: Number })
213260
213639
  ], ObcGaugeRadial.prototype, "fractionDigits", 2);
213640
+ __decorateClass$x([
213641
+ n$3({ type: Number, attribute: "face-diameter", reflect: true })
213642
+ ], ObcGaugeRadial.prototype, "faceDiameter", 2);
213643
+ __decorateClass$x([
213644
+ e$3(".gauge-radial-root")
213645
+ ], ObcGaugeRadial.prototype, "_rootEl", 2);
213261
213646
  ObcGaugeRadial = __decorateClass$x([
213262
213647
  customElement("obc-gauge-radial")
213263
213648
  ], ObcGaugeRadial);
@@ -218663,6 +219048,7 @@ let ObcHeading = class extends i$4 {
218663
219048
  onAnimationEnd: () => this.requestUpdate()
218664
219049
  });
218665
219050
  this._resizeController = new h(this, {});
219051
+ this._hostSizePinned = false;
218666
219052
  }
218667
219053
  willUpdate(changed) {
218668
219054
  super.willUpdate(changed);
@@ -218682,17 +219068,25 @@ let ObcHeading = class extends i$4 {
218682
219068
  super.disconnectedCallback();
218683
219069
  this._headingSp.dispose();
218684
219070
  }
218685
- getPadding() {
218686
- const size = Math.min(this.clientHeight, this.clientWidth);
218687
- const deltaWidth = 512 - size;
218688
- const steps = deltaWidth / 128;
218689
- let deltaPadding;
218690
- if (deltaWidth > 0) {
218691
- deltaPadding = steps * 48;
218692
- } else {
218693
- deltaPadding = steps * 6;
219071
+ /**
219072
+ * Pixel cost of the outside decor: the always-rendered north-arrow glyph
219073
+ * plus, only while shown, the NSWE labels (both keep a constant on-screen
219074
+ * size via `1/scale` terms). Feeds the frame's width-aware reserve,
219075
+ * replacing the former empirical `72 + delta(clientSize)` padding.
219076
+ */
219077
+ getOutsideDecorPx() {
219078
+ if (this.tickmarksInside) {
219079
+ return 0;
218694
219080
  }
218695
- return 72 + deltaPadding;
219081
+ return NORTH_ARROW_WIDTH_PX + (this.showLabels ? NSWE_LABEL_WIDTH_PX : 0);
219082
+ }
219083
+ updated(changed) {
219084
+ super.updated(changed);
219085
+ this._hostSizePinned = applyPinnedHostSize(
219086
+ this,
219087
+ this._frame,
219088
+ this._hostSizePinned
219089
+ );
218696
219090
  }
218697
219091
  get angleAdviceRaw() {
218698
219092
  return this.headingAdvices.map(({ minAngle, maxAngle, hinted, type }) => {
@@ -218721,21 +219115,26 @@ let ObcHeading = class extends i$4 {
218721
219115
  { angle: 180, type: TickmarkType$1.main },
218722
219116
  { angle: 270, type: TickmarkType$1.main }
218723
219117
  ];
218724
- const padding = this.getPadding();
218725
- const width = (176 + padding) * 2;
218726
- const viewBox = `-${width / 2} -${width / 2} ${width} ${width}`;
219118
+ const frame = computeRadialFrame({
219119
+ basePadding: 72,
219120
+ labelWidthPx: this.getOutsideDecorPx(),
219121
+ containerPx: measureContainerPx(this),
219122
+ faceDiameter: this.faceDiameter
219123
+ });
219124
+ this._frame = frame;
219125
+ const viewBox = frame.viewBox;
218727
219126
  return b`
218728
219127
  <div class="container">
218729
219128
  <obc-watch
218730
219129
  .touching=${this.touching}
218731
- .padding=${padding}
219130
+ .arcFrame=${frame}
218732
219131
  .advices=${this.angleAdviceRaw}
218733
219132
  .tickmarks=${tickmarks}
218734
219133
  .watchCircleType=${WatchCircleType.single}
218735
- .showLabels=${this.showLabels}
219134
+ .showLabels=${this.showLabels && !frame.labelsHidden}
218736
219135
  .tickmarksInside=${this.tickmarksInside}
218737
219136
  .crosshairEnabled=${true}
218738
- .northArrow=${true}
219137
+ .northArrow=${!frame.labelsHidden}
218739
219138
  .angleSetpoint=${this.headingSetpoint ?? void 0}
218740
219139
  .newAngleSetpoint=${this.newHeadingSetpoint}
218741
219140
  .atAngleSetpoint=${this._headingSp.computeAtSetpoint(this.heading)}
@@ -218844,6 +219243,9 @@ __decorateClass$s([
218844
219243
  __decorateClass$s([
218845
219244
  n$3({ type: Boolean })
218846
219245
  ], ObcHeading.prototype, "tickmarksInside", 2);
219246
+ __decorateClass$s([
219247
+ n$3({ type: Number, attribute: "face-diameter" })
219248
+ ], ObcHeading.prototype, "faceDiameter", 2);
218847
219249
  ObcHeading = __decorateClass$s([
218848
219250
  customElement("obc-heading")
218849
219251
  ], ObcHeading);
@@ -223760,6 +224162,20 @@ let ObcRudder = class extends SetpointMixin(i$4) {
223760
224162
  this.advices = [];
223761
224163
  this.zoomToFitArc = false;
223762
224164
  this._radiusOffset = 0;
224165
+ this._hostSizePinned = false;
224166
+ this._resizeController = new h(this, {});
224167
+ }
224168
+ firstUpdated(changed) {
224169
+ super.firstUpdated(changed);
224170
+ observeInnerBox(this._resizeController, this.renderRoot);
224171
+ }
224172
+ updated(changed) {
224173
+ super.updated(changed);
224174
+ this._hostSizePinned = applyPinnedHostSize(
224175
+ this,
224176
+ this._frame,
224177
+ this._hostSizePinned
224178
+ );
223763
224179
  }
223764
224180
  get _needleTransform() {
223765
224181
  const rOff = this._radiusOffset;
@@ -223874,32 +224290,25 @@ let ObcRudder = class extends SetpointMixin(i$4) {
223874
224290
  state
223875
224291
  };
223876
224292
  });
223877
- let overlayViewBox;
223878
- if (this.zoomToFitArc) {
223879
- const ext = 48;
223880
- const targetSize = (176 + ext) * 2;
223881
- const frame = computeZoomToFitArcFrame({
223882
- areas,
223883
- outerRadius: OUTER_RING_RADIUS,
223884
- innerRadius: innerRingRadiusFor(WatchCircleType.double),
223885
- extension: ext,
223886
- targetSize
223887
- });
223888
- overlayViewBox = frame.viewBox;
223889
- this._radiusOffset = frame.radiusOffset;
223890
- this._arcFrame = frame;
223891
- } else {
223892
- overlayViewBox = "-224 -44.8 448 268.8";
223893
- this._radiusOffset = 0;
223894
- this._arcFrame = void 0;
223895
- }
224293
+ const frame = computeRadialFrame({
224294
+ basePadding: 48,
224295
+ labelWidthPx: this.tickmarksInside ? 0 : estimateLabelWidthPx(tickmarks.map((t2) => t2.text)),
224296
+ clips: this.zoomToFitArc ? void 0 : { top: 40, bottom: 0, left: 0, right: 0 },
224297
+ containerPx: measureContainerPx(this),
224298
+ faceDiameter: this.faceDiameter,
224299
+ zoomToFitArc: this.zoomToFitArc,
224300
+ areas,
224301
+ innerRadius: innerRingRadiusFor(WatchCircleType.double)
224302
+ });
224303
+ this._radiusOffset = frame.radiusOffset;
224304
+ this._frame = frame;
224305
+ const shownTickmarks = frame.labelsHidden ? tickmarks.map((t2) => ({ ...t2, text: void 0 })) : tickmarks;
224306
+ const overlayViewBox = frame.viewBox;
223896
224307
  return b`
223897
224308
  <div class="container">
223898
224309
  <obc-watch
223899
224310
  .touching=${this.touching}
223900
- .clipTop=${this.zoomToFitArc ? 0 : 40}
223901
- .zoomToFitArc=${this.zoomToFitArc}
223902
- .arcFrame=${this._arcFrame}
224311
+ .arcFrame=${frame}
223903
224312
  .areas=${areas}
223904
224313
  .angleSetpoint=${setpointAngle}
223905
224314
  .newAngleSetpoint=${this.newSetpoint !== void 0 ? 180 - this.newSetpoint : void 0}
@@ -223907,8 +224316,7 @@ let ObcRudder = class extends SetpointMixin(i$4) {
223907
224316
  .angleSetpointAtZeroDeadband=${this.setpointAtZeroDeadband}
223908
224317
  .setpointOverride=${this.setpointOverride}
223909
224318
  .animateSetpoint=${this.animateSetpoint}
223910
- .padding=${48}
223911
- .tickmarks=${tickmarks}
224319
+ .tickmarks=${shownTickmarks}
223912
224320
  .tickmarksInside=${this.tickmarksInside}
223913
224321
  .tickmarkStyle=${this.tickmarkStyle}
223914
224322
  .watchCircleType=${WatchCircleType.double}
@@ -223971,6 +224379,9 @@ __decorateClass$9([
223971
224379
  __decorateClass$9([
223972
224380
  n$3({ type: Boolean })
223973
224381
  ], ObcRudder.prototype, "zoomToFitArc", 2);
224382
+ __decorateClass$9([
224383
+ n$3({ type: Number, attribute: "face-diameter" })
224384
+ ], ObcRudder.prototype, "faceDiameter", 2);
223974
224385
  ObcRudder = __decorateClass$9([
223975
224386
  customElement("obc-rudder")
223976
224387
  ], ObcRudder);
@@ -224174,8 +224585,22 @@ let ObcSpeedGauge = class extends SetpointMixin(i$4) {
224174
224585
  this.label = "STW";
224175
224586
  this.unit = "KN";
224176
224587
  this.fractionDigits = 1;
224588
+ this._hostSizePinned = false;
224589
+ this._resizeController = new h(this, {});
224177
224590
  this.maxAngle = 180 - 45;
224178
224591
  }
224592
+ firstUpdated(changed) {
224593
+ super.firstUpdated(changed);
224594
+ observeInnerBox(this._resizeController, this.renderRoot);
224595
+ }
224596
+ updated(changed) {
224597
+ super.updated(changed);
224598
+ this._hostSizePinned = applyPinnedHostSize(
224599
+ this,
224600
+ this._frame,
224601
+ this._hostSizePinned
224602
+ );
224603
+ }
224179
224604
  getAngle(v2) {
224180
224605
  return v2 / this.maxSpeed * (180 + 45) - 90;
224181
224606
  }
@@ -224186,6 +224611,15 @@ let ObcSpeedGauge = class extends SetpointMixin(i$4) {
224186
224611
  const barColor = this.priority === Priority.enhanced ? "var(--instrument-enhanced-tertiary-color)" : "var(--instrument-regular-tertiary-color)";
224187
224612
  const setpointAngle = this.setpoint !== void 0 ? this.getAngle(this.setpoint) : void 0;
224188
224613
  const maxDigits = 1;
224614
+ const tickmarks = this.tickmarks;
224615
+ const frame = computeRadialFrame({
224616
+ basePadding: 48,
224617
+ labelWidthPx: this.tickmarksInside ? 0 : estimateLabelWidthPx(tickmarks.map((t2) => t2.text)),
224618
+ containerPx: measureContainerPx(this),
224619
+ faceDiameter: this.faceDiameter
224620
+ });
224621
+ this._frame = frame;
224622
+ const shownTickmarks = frame.labelsHidden ? tickmarks.map((t2) => ({ ...t2, text: void 0 })) : tickmarks;
224189
224623
  return b`
224190
224624
  <div class="container">
224191
224625
  <obc-watch
@@ -224196,8 +224630,8 @@ let ObcSpeedGauge = class extends SetpointMixin(i$4) {
224196
224630
  .angleSetpointAtZeroDeadband=${this.setpointAtZeroDeadband}
224197
224631
  .setpointOverride=${this.setpointOverride}
224198
224632
  .animateSetpoint=${this.animateSetpoint}
224199
- .padding=${48}
224200
- .tickmarks=${this.tickmarks}
224633
+ .arcFrame=${frame}
224634
+ .tickmarks=${shownTickmarks}
224201
224635
  .tickmarksInside=${this.tickmarksInside}
224202
224636
  .tickmarkStyle=${this.tickmarkStyle}
224203
224637
  .advices=${this._advices}
@@ -224218,7 +224652,7 @@ let ObcSpeedGauge = class extends SetpointMixin(i$4) {
224218
224652
  }
224219
224653
  ]}
224220
224654
  ></obc-watch>
224221
- <svg class="rudder" viewBox="-224 -224 448 448">${this.needle}</svg>
224655
+ <svg class="rudder" viewBox=${frame.viewBox}>${this.needle}</svg>
224222
224656
  ${this.hasReadout ? b`
224223
224657
  ${renderInstrumentReadout({
224224
224658
  className: "speed-gauge-value",
@@ -224387,6 +224821,9 @@ __decorateClass$7([
224387
224821
  __decorateClass$7([
224388
224822
  n$3({ type: Number })
224389
224823
  ], ObcSpeedGauge.prototype, "fractionDigits", 2);
224824
+ __decorateClass$7([
224825
+ n$3({ type: Number, attribute: "face-diameter" })
224826
+ ], ObcSpeedGauge.prototype, "faceDiameter", 2);
224390
224827
  ObcSpeedGauge = __decorateClass$7([
224391
224828
  customElement("obc-speed-gauge")
224392
224829
  ], ObcSpeedGauge);