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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +592 -239
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +292 -8
  4. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts +10 -0
  5. package/dist/building-blocks/instrument-radial/instrument-radial.d.ts.map +1 -1
  6. package/dist/building-blocks/instrument-radial/instrument-radial.js +86 -21
  7. package/dist/building-blocks/instrument-radial/instrument-radial.js.map +1 -1
  8. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js +99 -0
  9. package/dist/navigation-instruments/gauge-radial/gauge-radial.css.js.map +1 -0
  10. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts +42 -7
  11. package/dist/navigation-instruments/gauge-radial/gauge-radial.d.ts.map +1 -1
  12. package/dist/navigation-instruments/gauge-radial/gauge-radial.js +178 -31
  13. package/dist/navigation-instruments/gauge-radial/gauge-radial.js.map +1 -1
  14. package/dist/navigation-instruments/readout/readout.css.js +4 -0
  15. package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
  16. package/dist/navigation-instruments/watch/tickmark.d.ts +2 -1
  17. package/dist/navigation-instruments/watch/tickmark.d.ts.map +1 -1
  18. package/dist/navigation-instruments/watch/tickmark.js +24 -4
  19. package/dist/navigation-instruments/watch/tickmark.js.map +1 -1
  20. package/dist/navigation-instruments/watch/watch.d.ts +23 -1
  21. package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
  22. package/dist/navigation-instruments/watch/watch.js +48 -20
  23. package/dist/navigation-instruments/watch/watch.js.map +1 -1
  24. package/package.json +1 -1
@@ -36273,6 +36273,34 @@
36273
36273
  "default": "0",
36274
36274
  "attribute": "clipBottom"
36275
36275
  },
36276
+ {
36277
+ "kind": "field",
36278
+ "name": "clipLeft",
36279
+ "type": {
36280
+ "text": "number"
36281
+ },
36282
+ "default": "0",
36283
+ "attribute": "clipLeft"
36284
+ },
36285
+ {
36286
+ "kind": "field",
36287
+ "name": "clipRight",
36288
+ "type": {
36289
+ "text": "number"
36290
+ },
36291
+ "default": "0",
36292
+ "attribute": "clipRight"
36293
+ },
36294
+ {
36295
+ "kind": "field",
36296
+ "name": "endLabelsMaxMin",
36297
+ "type": {
36298
+ "text": "boolean"
36299
+ },
36300
+ "default": "false",
36301
+ "description": "Place the horizontal end labels (±90°, e.g. min/max) below the tick instead\nof beside it — the \"Max-min\" placement from the radial label model\n(External / Internal / Max-min). See PR #903 / design discussion.",
36302
+ "attribute": "endLabelsMaxMin"
36303
+ },
36276
36304
  {
36277
36305
  "kind": "field",
36278
36306
  "name": "zoomToFitArc",
@@ -36326,6 +36354,33 @@
36326
36354
  "privacy": "private",
36327
36355
  "readonly": true
36328
36356
  },
36357
+ {
36358
+ "kind": "method",
36359
+ "name": "mapAngle",
36360
+ "privacy": "private",
36361
+ "return": {
36362
+ "type": {
36363
+ "text": "number"
36364
+ }
36365
+ },
36366
+ "parameters": [
36367
+ {
36368
+ "name": "value",
36369
+ "type": {
36370
+ "text": "number"
36371
+ }
36372
+ }
36373
+ ]
36374
+ },
36375
+ {
36376
+ "kind": "field",
36377
+ "name": "safeClips",
36378
+ "type": {
36379
+ "text": "Clips"
36380
+ },
36381
+ "privacy": "private",
36382
+ "readonly": true
36383
+ },
36329
36384
  {
36330
36385
  "kind": "field",
36331
36386
  "name": "_derivedNeedleColor",
@@ -36685,6 +36740,31 @@
36685
36740
  "default": "0",
36686
36741
  "fieldName": "clipBottom"
36687
36742
  },
36743
+ {
36744
+ "name": "clipLeft",
36745
+ "type": {
36746
+ "text": "number"
36747
+ },
36748
+ "default": "0",
36749
+ "fieldName": "clipLeft"
36750
+ },
36751
+ {
36752
+ "name": "clipRight",
36753
+ "type": {
36754
+ "text": "number"
36755
+ },
36756
+ "default": "0",
36757
+ "fieldName": "clipRight"
36758
+ },
36759
+ {
36760
+ "name": "endLabelsMaxMin",
36761
+ "type": {
36762
+ "text": "boolean"
36763
+ },
36764
+ "default": "false",
36765
+ "description": "Place the horizontal end labels (±90°, e.g. min/max) below the tick instead\nof beside it — the \"Max-min\" placement from the radial label model\n(External / Internal / Max-min). See PR #903 / design discussion.",
36766
+ "fieldName": "endLabelsMaxMin"
36767
+ },
36688
36768
  {
36689
36769
  "name": "zoomToFitArc",
36690
36770
  "type": {
@@ -73396,7 +73476,7 @@
73396
73476
  "declarations": [
73397
73477
  {
73398
73478
  "kind": "class",
73399
- "description": "`<obc-gauge-radial>` — Configurable radial gauge for generic numeric values.\n\n`ObcGaugeRadial` is a thin wrapper around `<obc-instrument-radial>` that adds\ndomain-independent value-to-angle mapping with automatic range handling for\nboth positive-only and bipolar (negative-to-positive) scales. It inherits a\nfull setpoint property bundle from SetpointMixin, including\nauto at-setpoint detection, dual-marker adjustment preview, and deadband\ntuning — no manual wiring required.\n\n## Features\n\n- **Three display types**: `filled` (solid arc), `bar` (thinner arc), and\n `needle` (pointer indicator) via the `type` property.\n- **Full-range mapping**: The configured `minValue..maxValue` always spans\n the full 270° sweep. Symmetric ranges still place `0` at 12 o'clock.\n- **Setpoint via mixin**: `setpoint`, `newSetpoint`, `touching`,\n `autoAtSetpointDeadband`, `setpointOverride`, and all other setpoint\n properties are provided by `SetpointMixin` and forwarded to the inner\n `<obc-instrument-radial>`.\n- **Advice zones**: Pass an array of GaugeRadialAdvice objects to\n render caution/alert arcs on the gauge.\n\n## Usage Guidelines\n\n- Set `minValue` / `maxValue` to define the scale range.\n- Use `priority` to switch between regular and enhanced color palettes.\n- Provide `primaryTickmarkInterval` and `secondaryTickmarkInterval` to\n control tickmark density.\n- Enable `showLabels` to show numeric labels at primary tickmarks.\n\n## Best Practices\n\n- Prefer `SetpointMixin` properties (`setpoint`, `touching`, etc.) over\n any legacy aliases — the mixin is the single source of truth.\n- Keep domain-specific logic (units, formatting) in the parent view; this\n component is intentionally unit-agnostic.\n\n## Example\n\n```html\n<obc-gauge-radial\n value=\"42\"\n minValue=\"0\"\n maxValue=\"100\"\n type=\"filled\"\n enhanced\n showLabels\n primaryTickmarkInterval=\"25\"\n secondaryTickmarkInterval=\"5\"\n setpoint=\"60\"\n></obc-gauge-radial>\n```",
73479
+ "description": "`<obc-gauge-radial>` — Configurable radial gauge for generic numeric values.\n\n`ObcGaugeRadial` is a thin wrapper around `<obc-instrument-radial>` that adds\ndomain-independent value-to-angle mapping with automatic range handling for\nboth positive-only and bipolar (negative-to-positive) scales. It inherits a\nfull setpoint property bundle from SetpointMixin, including\nauto at-setpoint detection, dual-marker adjustment preview, and deadband\ntuning — no manual wiring required.\n\n## Features\n\n- **Three display types**: `filled` (solid arc), `bar` (thinner arc), and\n `needle` (pointer indicator) via the `type` property.\n- **Sector sweep**: `sector` selects the arc span (`270`, `180`, `90-left`, or `90-right`).\n The configured `minValue..maxValue` always spans the full sector. For the\n centered sectors (`270`, `180`) a symmetric range places `0` at 12 o'clock;\n for `90-left`/`90-right` the range midpoint sits at the middle of the quadrant.\n- **Setpoint via mixin**: `setpoint`, `newSetpoint`, `touching`,\n `autoAtSetpointDeadband`, `setpointOverride`, and all other setpoint\n properties are provided by `SetpointMixin` and forwarded to the inner\n `<obc-instrument-radial>`.\n- **Advice zones**: Pass an array of GaugeRadialAdvice objects to\n render caution/alert arcs on the gauge. Not shown on the `90-left` /\n `90-right` sectors.\n\n## Usage Guidelines\n\n- Set `minValue` / `maxValue` to define the scale range.\n- Use `priority` to switch between regular and enhanced color palettes.\n- Provide `primaryTickmarkInterval` and `secondaryTickmarkInterval` to\n control tickmark density.\n- Enable `showLabels` to show numeric labels at primary tickmarks.\n- Enable `showReadout` with optional `label` and `unit`. Layout depends on `sector`\n and `type`: **270** filled/bar — value at center + a label-only `meta` row in\n the bottom gap (two separate readouts); **180** filled/bar — a single centered\n stack (value + label/unit) at the bottom; **270** needle — bottom stack;\n **180** needle — no readout; **90-left** / **90-right** filled/bar — corner\n readout in a square host; **90** needle — no readout.\n\n## Best Practices\n\n- Prefer `SetpointMixin` properties (`setpoint`, `touching`, etc.) over\n any legacy aliases — the mixin is the single source of truth.\n- Keep domain-specific logic (units, formatting) in the parent view; this\n component is intentionally unit-agnostic.\n\n## Example\n\n```html\n<obc-gauge-radial\n value=\"42\"\n minValue=\"0\"\n maxValue=\"100\"\n type=\"filled\"\n priority=\"enhanced\"\n showLabels\n primaryTickmarkInterval=\"25\"\n secondaryTickmarkInterval=\"5\"\n setpoint=\"60\"\n></obc-gauge-radial>\n```",
73400
73480
  "name": "ObcGaugeRadial",
73401
73481
  "members": [
73402
73482
  {
@@ -73510,24 +73590,109 @@
73510
73590
  "type": {
73511
73591
  "text": "GaugeRadialAdvice[]"
73512
73592
  },
73513
- "default": "[]"
73593
+ "default": "[]",
73594
+ "description": "Caution/alert arcs. Ignored on `sector: 90-left` / `90-right`."
73595
+ },
73596
+ {
73597
+ "kind": "field",
73598
+ "name": "sector",
73599
+ "type": {
73600
+ "text": "GaugeRadialSector"
73601
+ },
73602
+ "attribute": "sector",
73603
+ "reflects": true
73604
+ },
73605
+ {
73606
+ "kind": "field",
73607
+ "name": "showReadout",
73608
+ "type": {
73609
+ "text": "boolean"
73610
+ },
73611
+ "default": "false",
73612
+ "attribute": "showReadout"
73613
+ },
73614
+ {
73615
+ "kind": "field",
73616
+ "name": "label",
73617
+ "type": {
73618
+ "text": "string"
73619
+ },
73620
+ "default": "''",
73621
+ "attribute": "label"
73622
+ },
73623
+ {
73624
+ "kind": "field",
73625
+ "name": "unit",
73626
+ "type": {
73627
+ "text": "string"
73628
+ },
73629
+ "default": "''",
73630
+ "attribute": "unit"
73631
+ },
73632
+ {
73633
+ "kind": "field",
73634
+ "name": "fractionDigits",
73635
+ "type": {
73636
+ "text": "number"
73637
+ },
73638
+ "default": "0",
73639
+ "attribute": "fractionDigits"
73640
+ },
73641
+ {
73642
+ "kind": "field",
73643
+ "name": "sectorAngles",
73644
+ "type": {
73645
+ "text": "{sweep: number; start: number}"
73646
+ },
73647
+ "privacy": "private",
73648
+ "readonly": true
73649
+ },
73650
+ {
73651
+ "kind": "field",
73652
+ "name": "sectorClips",
73653
+ "type": {
73654
+ "text": "{\n top: number;\n bottom: number;\n left: number;\n right: number;\n }"
73655
+ },
73656
+ "privacy": "private",
73657
+ "description": "Per-edge crop (%) of the shared, origin-centered 448 SVG box each sector\nshows. All sectors render at the same natural scale, so the dial stays the\nsame size; the sector only windows a different part (270 whole, 180 wide,\n90 a quadrant).",
73658
+ "readonly": true
73659
+ },
73660
+ {
73661
+ "kind": "field",
73662
+ "name": "isSector90",
73663
+ "type": {
73664
+ "text": "boolean"
73665
+ },
73666
+ "privacy": "private",
73667
+ "readonly": true
73668
+ },
73669
+ {
73670
+ "kind": "field",
73671
+ "name": "getAngle"
73514
73672
  },
73515
73673
  {
73516
73674
  "kind": "method",
73517
- "name": "getAngle",
73675
+ "name": "renderReadout",
73676
+ "privacy": "private",
73518
73677
  "return": {
73519
73678
  "type": {
73520
- "text": "number"
73679
+ "text": "TemplateResult"
73521
73680
  }
73522
73681
  },
73523
73682
  "parameters": [
73524
73683
  {
73525
- "name": "v",
73684
+ "name": "{\n className,\n variant,\n alignment = ReadoutStackVerticalAlignment.vertical,\n withMeta = true,\n labelOnly = false,\n }",
73526
73685
  "type": {
73527
- "text": "number"
73686
+ "text": "{\n className: string;\n variant: ReadoutVariant;\n alignment?: ReadoutStackVerticalAlignment;\n withMeta?: boolean;\n labelOnly?: boolean;\n }"
73528
73687
  }
73529
73688
  }
73530
- ]
73689
+ ],
73690
+ "description": "Renders one gauge readout; `withMeta`/`labelOnly` pick parts."
73691
+ },
73692
+ {
73693
+ "kind": "method",
73694
+ "name": "renderReadouts",
73695
+ "privacy": "private"
73531
73696
  },
73532
73697
  {
73533
73698
  "kind": "field",
@@ -73808,6 +73973,45 @@
73808
73973
  },
73809
73974
  "fieldName": "tickmarkStyle"
73810
73975
  },
73976
+ {
73977
+ "name": "sector",
73978
+ "type": {
73979
+ "text": "GaugeRadialSector"
73980
+ },
73981
+ "fieldName": "sector"
73982
+ },
73983
+ {
73984
+ "name": "showReadout",
73985
+ "type": {
73986
+ "text": "boolean"
73987
+ },
73988
+ "default": "false",
73989
+ "fieldName": "showReadout"
73990
+ },
73991
+ {
73992
+ "name": "label",
73993
+ "type": {
73994
+ "text": "string"
73995
+ },
73996
+ "default": "''",
73997
+ "fieldName": "label"
73998
+ },
73999
+ {
74000
+ "name": "unit",
74001
+ "type": {
74002
+ "text": "string"
74003
+ },
74004
+ "default": "''",
74005
+ "fieldName": "unit"
74006
+ },
74007
+ {
74008
+ "name": "fractionDigits",
74009
+ "type": {
74010
+ "text": "number"
74011
+ },
74012
+ "default": "0",
74013
+ "fieldName": "fractionDigits"
74014
+ },
73811
74015
  {
73812
74016
  "name": "setpoint",
73813
74017
  "type": {
@@ -88337,6 +88541,7 @@
88337
88541
  "text": "number"
88338
88542
  },
88339
88543
  "default": "0",
88544
+ "description": "Top clip, % of height. Ignored when `zoomToFitArc` is true.",
88340
88545
  "attribute": "clipTop"
88341
88546
  },
88342
88547
  {
@@ -88346,8 +88551,39 @@
88346
88551
  "text": "number"
88347
88552
  },
88348
88553
  "default": "0",
88554
+ "description": "Bottom clip, % of height. Ignored when `zoomToFitArc` is true.",
88349
88555
  "attribute": "clipBottom"
88350
88556
  },
88557
+ {
88558
+ "kind": "field",
88559
+ "name": "clipLeft",
88560
+ "type": {
88561
+ "text": "number"
88562
+ },
88563
+ "default": "0",
88564
+ "description": "Left clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`.",
88565
+ "attribute": "clipLeft"
88566
+ },
88567
+ {
88568
+ "kind": "field",
88569
+ "name": "clipRight",
88570
+ "type": {
88571
+ "text": "number"
88572
+ },
88573
+ "default": "0",
88574
+ "description": "Right clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`.",
88575
+ "attribute": "clipRight"
88576
+ },
88577
+ {
88578
+ "kind": "field",
88579
+ "name": "endLabelsMaxMin",
88580
+ "type": {
88581
+ "text": "boolean"
88582
+ },
88583
+ "default": "false",
88584
+ "description": "Place the horizontal end labels (±90°, e.g. min/max) below the tick instead\nof beside it. This is the \"Max-min\" label placement from the radial label\nmodel (External / Internal / Max-min) — see PR #903 / design discussion.\nCurrently only used by the 180° sector of `obc-gauge-radial`.",
88585
+ "attribute": "endLabelsMaxMin"
88586
+ },
88351
88587
  {
88352
88588
  "kind": "field",
88353
88589
  "name": "scaleWindIcon",
@@ -88380,7 +88616,7 @@
88380
88616
  "type": {
88381
88617
  "text": "ZoomToFitArcFrame|undefined"
88382
88618
  },
88383
- "description": "Pre-computed zoom-to-fit arc frame. When set, the watch skips its own `computeZoomToFitArcFrame()` call and uses these values directly. Consumer instruments (e.g. rudder, instrument-radial) should compute the frame once and pass it here to avoid redundant computation."
88619
+ "description": "Pre-computed zoom-to-fit arc frame. When set, the watch skips its own `computeZoomToFitArcFrame()` call and uses these values directly. Consumer instruments (e.g. rudder, instrument-radial) should compute the frame once and pass it here to avoid redundant computation. If you pass `arcFrame`, you own keeping it in sync with `areas` / `watchCircleType` — obc-watch will NOT recompute it, so a stale frame renders stale geometry."
88384
88620
  },
88385
88621
  {
88386
88622
  "kind": "field",
@@ -88536,6 +88772,25 @@
88536
88772
  "privacy": "private",
88537
88773
  "default": "0"
88538
88774
  },
88775
+ {
88776
+ "kind": "method",
88777
+ "name": "_bandRadius",
88778
+ "privacy": "private",
88779
+ "return": {
88780
+ "type": {
88781
+ "text": "number"
88782
+ }
88783
+ },
88784
+ "parameters": [
88785
+ {
88786
+ "name": "base",
88787
+ "type": {
88788
+ "text": "number"
88789
+ }
88790
+ }
88791
+ ],
88792
+ "description": "Radius for a dial-band edge under zoom: additive (`base + _rOff`), keeping\nband thickness constant in SVG units. INVARIANT: every band-edge radius\n(rings, value arc, bars, needles, inside-label `textRadius`) must go through\nthis — mixing additive and multiplicative offsets misaligns ticks, labels,\nadvice masks and arcs. The proportional experiments were removed for this\nreason (PR #903)."
88793
+ },
88539
88794
  {
88540
88795
  "kind": "method",
88541
88796
  "name": "watchCircle",
@@ -88892,6 +89147,7 @@
88892
89147
  "text": "number"
88893
89148
  },
88894
89149
  "default": "0",
89150
+ "description": "Top clip, % of height. Ignored when `zoomToFitArc` is true.",
88895
89151
  "fieldName": "clipTop"
88896
89152
  },
88897
89153
  {
@@ -88900,8 +89156,36 @@
88900
89156
  "text": "number"
88901
89157
  },
88902
89158
  "default": "0",
89159
+ "description": "Bottom clip, % of height. Ignored when `zoomToFitArc` is true.",
88903
89160
  "fieldName": "clipBottom"
88904
89161
  },
89162
+ {
89163
+ "name": "clipLeft",
89164
+ "type": {
89165
+ "text": "number"
89166
+ },
89167
+ "default": "0",
89168
+ "description": "Left clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`.",
89169
+ "fieldName": "clipLeft"
89170
+ },
89171
+ {
89172
+ "name": "clipRight",
89173
+ "type": {
89174
+ "text": "number"
89175
+ },
89176
+ "default": "0",
89177
+ "description": "Right clip, % of width — horizontal counterpart of clipTop/bottom (90° sectors). Ignored when `zoomToFitArc`.",
89178
+ "fieldName": "clipRight"
89179
+ },
89180
+ {
89181
+ "name": "endLabelsMaxMin",
89182
+ "type": {
89183
+ "text": "boolean"
89184
+ },
89185
+ "default": "false",
89186
+ "description": "Place the horizontal end labels (±90°, e.g. min/max) below the tick instead\nof beside it. This is the \"Max-min\" label placement from the radial label\nmodel (External / Internal / Max-min) — see PR #903 / design discussion.\nCurrently only used by the 180° sector of `obc-gauge-radial`.",
89187
+ "fieldName": "endLabelsMaxMin"
89188
+ },
88905
89189
  {
88906
89190
  "name": "scaleWindIcon",
88907
89191
  "type": {
@@ -46,12 +46,22 @@ export declare class ObcInstrumentRadial extends ObcInstrumentRadial_base {
46
46
  advices: GaugeRadialAdvice[];
47
47
  clipTop: number;
48
48
  clipBottom: number;
49
+ clipLeft: number;
50
+ clipRight: number;
51
+ /**
52
+ * Place the horizontal end labels (±90°, e.g. min/max) below the tick instead
53
+ * of beside it — the "Max-min" placement from the radial label model
54
+ * (External / Internal / Max-min). See PR #903 / design discussion.
55
+ */
56
+ endLabelsMaxMin: boolean;
49
57
  zoomToFitArc: boolean;
50
58
  private _radiusOffset;
51
59
  private _arcFrame;
52
60
  private get clampedValue();
53
61
  private get minAngle();
54
62
  private get maxAngle();
63
+ private mapAngle;
64
+ private get safeClips();
55
65
  private get _derivedNeedleColor();
56
66
  private get _derivedBarColor();
57
67
  render(): import('lit-html').TemplateResult<1>;
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-radial.d.ts","sourceRoot":"","sources":["../../../src/building-blocks/instrument-radial/instrument-radial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,UAAU,EAAqB,MAAM,KAAK,CAAC;AAGxD,OAAO,EAEL,UAAU,EAEX,MAAM,8CAA8C,CAAC;AAEtD,OAAO,EAAC,QAAQ,EAAC,MAAM,gDAAgD,CAAC;AAExE,OAAO,EAAC,aAAa,EAAC,MAAM,gDAAgD,CAAC;AAC7E,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,uCAAuC,CAAC;AAWhF,oBAAY,kBAAkB;IAC5B,MAAM,WAAW;IACjB,GAAG,QAAQ;IACX,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;;AAuBD,qBACa,mBAAoB,SAAQ,wBAAyB;IAKtC,KAAK,EAAE,eAAe,CAA0B;IAChD,QAAQ,EAAE,QAAQ,CAAoB;IAEtC,KAAK,SAAK;IACV,QAAQ,SAAO;IACf,QAAQ,SAAK;IACT,QAAQ,EAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,CAAS;IACvD;;;OAGG;IACuB,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAM;IAC3E;;;OAGG;IACuB,yBAAyB,EAAE,MAAM,GAAG,SAAS,CAAM;IAC7E;;;OAGG;IACuB,wBAAwB,EAAE,MAAM,GAAG,SAAS,CAC1D;IACc,IAAI,EAAE,kBAAkB,CACtB;IACF,UAAU,EAAE,kBAAkB,CAC5B;IACD,eAAe,EAAE,OAAO,CAAS;IAClC,aAAa,EAAE,aAAa,CAC9B;IACmB,OAAO,EAAE,iBAAiB,EAAE,CAAM;IACnD,OAAO,EAAE,MAAM,CAAK;IACpB,UAAU,EAAE,MAAM,CAAK;IACtB,YAAY,EAAE,OAAO,CAAS;IAEzD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,SAAS,CAAgC;IAEjD,OAAO,KAAK,YAAY,GAIvB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,mBAAmB,GAU9B;IAED,OAAO,KAAK,gBAAgB,GAe3B;IAEQ,MAAM;IAwFf,OAAO,KAAK,OAAO,GAoBlB;IAED,IAAI,SAAS,IAAI,QAAQ,EAAE,CA6G1B;IAED,OAAO,KAAK,QAAQ,GAoBnB;IAED,OAAgB,MAAM,0BAmCpB;CACH;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,uBAAuB,EAAE,mBAAmB,CAAC;KAC9C;CACF"}
1
+ {"version":3,"file":"instrument-radial.d.ts","sourceRoot":"","sources":["../../../src/building-blocks/instrument-radial/instrument-radial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,UAAU,EAAqB,MAAM,KAAK,CAAC;AAGxD,OAAO,EAEL,UAAU,EAEX,MAAM,8CAA8C,CAAC;AAEtD,OAAO,EAAC,QAAQ,EAAC,MAAM,gDAAgD,CAAC;AAExE,OAAO,EAAC,aAAa,EAAC,MAAM,gDAAgD,CAAC;AAC7E,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,uCAAuC,CAAC;AAehF,oBAAY,kBAAkB;IAC5B,MAAM,WAAW;IACjB,GAAG,QAAQ;IACX,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;;AA8ED,qBACa,mBAAoB,SAAQ,wBAAyB;IAKtC,KAAK,EAAE,eAAe,CAA0B;IAChD,QAAQ,EAAE,QAAQ,CAAoB;IAEtC,KAAK,SAAK;IACV,QAAQ,SAAO;IACf,QAAQ,SAAK;IACT,QAAQ,EAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,CAAS;IACvD;;;OAGG;IACuB,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAM;IAC3E;;;OAGG;IACuB,yBAAyB,EAAE,MAAM,GAAG,SAAS,CAAM;IAC7E;;;OAGG;IACuB,wBAAwB,EAAE,MAAM,GAAG,SAAS,CAC1D;IACc,IAAI,EAAE,kBAAkB,CACtB;IACF,UAAU,EAAE,kBAAkB,CAC5B;IACD,eAAe,EAAE,OAAO,CAAS;IAClC,aAAa,EAAE,aAAa,CAC9B;IACmB,OAAO,EAAE,iBAAiB,EAAE,CAAM;IACnD,OAAO,EAAE,MAAM,CAAK;IACpB,UAAU,EAAE,MAAM,CAAK;IACvB,QAAQ,EAAE,MAAM,CAAK;IACrB,SAAS,EAAE,MAAM,CAAK;IAChD;;;;OAIG;IACwB,eAAe,EAAE,OAAO,CAAS;IACjC,YAAY,EAAE,OAAO,CAAS;IAEzD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,SAAS,CAAgC;IAEjD,OAAO,KAAK,YAAY,GAIvB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAID,OAAO,CAAC,QAAQ;IAWhB,OAAO,KAAK,SAAS,GAOpB;IAED,OAAO,KAAK,mBAAmB,GAU9B;IAED,OAAO,KAAK,gBAAgB,GAe3B;IAEQ,MAAM;IA8Ff,OAAO,KAAK,OAAO,GAsBlB;IAED,IAAI,SAAS,IAAI,QAAQ,EAAE,CA6G1B;IAED,OAAO,KAAK,QAAQ,GAoBnB;IAED,OAAgB,MAAM,0BAmCpB;CACH;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,uBAAuB,EAAE,mBAAmB,CAAC;KAC9C;CACF"}
@@ -17,12 +17,17 @@ var __decorateClass = (decorators, target, key, kind) => {
17
17
  if (kind && result) __defProp(target, key, result);
18
18
  return result;
19
19
  };
20
+ const WATCH_DEFAULT_VIEWBOX = 448;
20
21
  var ObcGaugeRadialType = /* @__PURE__ */ ((ObcGaugeRadialType2) => {
21
22
  ObcGaugeRadialType2["filled"] = "filled";
22
23
  ObcGaugeRadialType2["bar"] = "bar";
23
24
  ObcGaugeRadialType2["needle"] = "needle";
24
25
  return ObcGaugeRadialType2;
25
26
  })(ObcGaugeRadialType || {});
27
+ const NEEDLE_TIP_RADIUS = 160;
28
+ const NEEDLE_TIP_GAP = 5;
29
+ const NEEDLE_WIDTH = 8;
30
+ const NEEDLE_HUB_RADIUS = 16;
26
31
  function rangeIncludesZero(minValue, maxValue) {
27
32
  return minValue <= 0 && maxValue >= 0;
28
33
  }
@@ -38,6 +43,31 @@ function strongerTickmarkType(existing, candidate) {
38
43
  };
39
44
  return priority[candidate] > priority[existing] ? candidate : existing;
40
45
  }
46
+ function clampClipPercent(n) {
47
+ return Number.isFinite(n) ? Math.min(Math.max(n, 0), 100) : 0;
48
+ }
49
+ function normalizeClips(clips) {
50
+ let top = clampClipPercent(clips.top);
51
+ let bottom = clampClipPercent(clips.bottom);
52
+ let left = clampClipPercent(clips.left);
53
+ let right = clampClipPercent(clips.right);
54
+ if (top + bottom >= 100) {
55
+ top = 0;
56
+ bottom = 0;
57
+ }
58
+ if (left + right >= 100) {
59
+ left = 0;
60
+ right = 0;
61
+ }
62
+ return { top, bottom, left, right };
63
+ }
64
+ function defaultGaugeAngle(value, minValue, maxValue) {
65
+ const span = maxValue - minValue;
66
+ if (!Number.isFinite(span) || span <= 0) {
67
+ return -135;
68
+ }
69
+ return (value - minValue) / span * 270 - 135;
70
+ }
41
71
  let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
42
72
  constructor() {
43
73
  super(...arguments);
@@ -57,6 +87,9 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
57
87
  this.advices = [];
58
88
  this.clipTop = 0;
59
89
  this.clipBottom = 0;
90
+ this.clipLeft = 0;
91
+ this.clipRight = 0;
92
+ this.endLabelsMaxMin = false;
60
93
  this.zoomToFitArc = false;
61
94
  this._radiusOffset = 0;
62
95
  }
@@ -66,10 +99,27 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
66
99
  return Math.max(lowerBound, Math.min(this.value, upperBound));
67
100
  }
68
101
  get minAngle() {
69
- return this.getAngle(this.minValue);
102
+ return this.mapAngle(this.minValue);
70
103
  }
71
104
  get maxAngle() {
72
- return this.getAngle(this.maxValue);
105
+ return this.mapAngle(this.maxValue);
106
+ }
107
+ // Map a value to an angle via the consumer's `getAngle`, guarding a missing
108
+ // or non-finite mapping so a misconfigured consumer can't emit NaN geometry.
109
+ mapAngle(value) {
110
+ const fn = this.getAngle;
111
+ const angle = typeof fn === "function" ? fn(value) : defaultGaugeAngle(value, this.minValue, this.maxValue);
112
+ return Number.isFinite(angle) ? angle : 0;
113
+ }
114
+ // Clamped clips, reused for the overlay viewBox and the clips forwarded to
115
+ // obc-watch.
116
+ get safeClips() {
117
+ return normalizeClips({
118
+ top: this.clipTop,
119
+ bottom: this.clipBottom,
120
+ left: this.clipLeft,
121
+ right: this.clipRight
122
+ });
73
123
  }
74
124
  get _derivedNeedleColor() {
75
125
  if (this.state === InstrumentState.loading || this.state === InstrumentState.off) {
@@ -90,12 +140,12 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
90
140
  const barColor = this.barColor ?? this._derivedBarColor;
91
141
  const barStartValue = Math.max(this.minValue, Math.min(0, this.maxValue));
92
142
  const value = this.clampedValue;
93
- const setpointAngle = this.setpoint !== void 0 ? this.getAngle(this.setpoint) : void 0;
94
- const newSetpointAngle = this.newSetpoint !== void 0 ? this.getAngle(this.newSetpoint) : void 0;
143
+ const setpointAngle = this.setpoint !== void 0 ? this.mapAngle(this.setpoint) : void 0;
144
+ const newSetpointAngle = this.newSetpoint !== void 0 ? this.mapAngle(this.newSetpoint) : void 0;
95
145
  const barAreas = this.type === "needle" ? [] : [
96
146
  {
97
- startAngle: this.getAngle(barStartValue),
98
- endAngle: this.getAngle(value),
147
+ startAngle: this.mapAngle(barStartValue),
148
+ endAngle: this.mapAngle(value),
99
149
  fillColor: barColor
100
150
  }
101
151
  ];
@@ -108,6 +158,7 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
108
158
  }
109
159
  ];
110
160
  const watchCircleType = this.type === "needle" ? WatchCircleType.single : WatchCircleType.double;
161
+ const clips = this.safeClips;
111
162
  let viewBox;
112
163
  if (this.zoomToFitArc) {
113
164
  const ext = 48;
@@ -119,16 +170,18 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
119
170
  extension: ext,
120
171
  targetSize
121
172
  });
122
- viewBox = frame.viewBox;
123
173
  this._radiusOffset = frame.radiusOffset;
174
+ viewBox = frame.viewBox;
124
175
  this._arcFrame = frame;
125
176
  } else {
126
177
  this._radiusOffset = 0;
127
178
  this._arcFrame = void 0;
128
- const width = 448;
129
- const height = width * (1 - this.clipTop / 100 - this.clipBottom / 100);
130
- const top = -width / 2 + width * this.clipTop / 100;
131
- viewBox = `${-width / 2} ${top} ${width} ${height}`;
179
+ const full = WATCH_DEFAULT_VIEWBOX;
180
+ const w = full * (1 - clips.left / 100 - clips.right / 100);
181
+ const h = full * (1 - clips.top / 100 - clips.bottom / 100);
182
+ const left = -full / 2 + full * clips.left / 100;
183
+ const top = -full / 2 + full * clips.top / 100;
184
+ viewBox = `${left} ${top} ${w} ${h}`;
132
185
  }
133
186
  return html`
134
187
  <div class="container">
@@ -149,8 +202,11 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
149
202
  .areas=${areas}
150
203
  .watchCircleType=${watchCircleType}
151
204
  .barAreas=${barAreas}
152
- .clipTop=${this.zoomToFitArc ? 0 : this.clipTop}
153
- .clipBottom=${this.zoomToFitArc ? 0 : this.clipBottom}
205
+ .clipTop=${this.zoomToFitArc ? 0 : clips.top}
206
+ .clipBottom=${this.zoomToFitArc ? 0 : clips.bottom}
207
+ .clipLeft=${this.zoomToFitArc ? 0 : clips.left}
208
+ .clipRight=${this.zoomToFitArc ? 0 : clips.right}
209
+ .endLabelsMaxMin=${this.endLabelsMaxMin}
154
210
  .zoomToFitArc=${this.zoomToFitArc}
155
211
  .arcFrame=${this._arcFrame}
156
212
  ></obc-watch>
@@ -166,14 +222,14 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
166
222
  const rOff = this._radiusOffset;
167
223
  const value = this.clampedValue;
168
224
  if (this.type === "needle") {
169
- return svg`<g transform="rotate(${this.getAngle(value)}) translate(-256, -256)">
170
- <circle cx="256" cy="256" r="14" fill=${needleColor}/>
171
- <rect x="250" y="${96 - rOff}" width="12" height="${192 + rOff}" rx="6" fill=${needleColor}/>
172
- <rect x="252" y="${98 - rOff}" width="8" height="${188 + rOff}" rx="4" stroke=${needleColor} fill=${needleColor} stroke-width="4"/>
225
+ const tipY = 256 - (NEEDLE_TIP_RADIUS - NEEDLE_TIP_GAP) - rOff;
226
+ return svg`<g transform="rotate(${this.mapAngle(value)}) translate(-256, -256)">
227
+ <rect x="${256 - NEEDLE_WIDTH / 2}" y="${tipY}" width="${NEEDLE_WIDTH}" height="${256 - tipY}" rx="${NEEDLE_WIDTH / 2}" fill=${needleColor} stroke=${needleColor}/>
228
+ <circle cx="256" cy="256" r="${NEEDLE_HUB_RADIUS}" fill=${needleColor}/>
173
229
  </g>
174
230
  `;
175
231
  } else {
176
- return svg`<g transform="rotate(${this.getAngle(value)}) translate(-256, -256)">
232
+ return svg`<g transform="rotate(${this.mapAngle(value)}) translate(-256, -256)">
177
233
  <rect x="252" y="${96 - rOff}" width="8" height="48" rx="4" fill=${needleColor} stroke="var(--border-silhouette-color)"/>
178
234
  </g>
179
235
  `;
@@ -196,7 +252,7 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
196
252
  return;
197
253
  }
198
254
  tickmarksByValue.set(normalizedValue, {
199
- angle: this.getAngle(normalizedValue),
255
+ angle: this.mapAngle(normalizedValue),
200
256
  type,
201
257
  text
202
258
  });
@@ -256,8 +312,8 @@ let ObcInstrumentRadial = class extends SetpointMixin(LitElement) {
256
312
  get _advices() {
257
313
  const value = this.clampedValue;
258
314
  return this.advices.map((advice) => {
259
- const minAngle = this.getAngle(advice.minValue);
260
- const maxAngle = this.getAngle(advice.maxValue);
315
+ const minAngle = this.mapAngle(advice.minValue);
316
+ const maxAngle = this.mapAngle(advice.maxValue);
261
317
  let state = advice.hinted ? AdviceState.hinted : AdviceState.regular;
262
318
  if (value >= advice.minValue && value <= advice.maxValue) {
263
319
  state = AdviceState.triggered;
@@ -366,6 +422,15 @@ __decorateClass([
366
422
  __decorateClass([
367
423
  property({ type: Number })
368
424
  ], ObcInstrumentRadial.prototype, "clipBottom", 2);
425
+ __decorateClass([
426
+ property({ type: Number })
427
+ ], ObcInstrumentRadial.prototype, "clipLeft", 2);
428
+ __decorateClass([
429
+ property({ type: Number })
430
+ ], ObcInstrumentRadial.prototype, "clipRight", 2);
431
+ __decorateClass([
432
+ property({ type: Boolean })
433
+ ], ObcInstrumentRadial.prototype, "endLabelsMaxMin", 2);
369
434
  __decorateClass([
370
435
  property({ type: Boolean })
371
436
  ], ObcInstrumentRadial.prototype, "zoomToFitArc", 2);