@oicl/openbridge-webcomponents 0.0.15-dev-20240916185711 → 0.0.15-dev-20240923190011

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. package/.storybook/main.ts +8 -0
  2. package/.storybook/preview.ts +2 -0
  3. package/__snapshots__/building-blocks-watch--advice.png +0 -0
  4. package/__snapshots__/building-blocks-watch-flat--primary.png +0 -0
  5. package/__snapshots__/navigation-instruments-azimuth-thruster--in-command.png +0 -0
  6. package/__snapshots__/navigation-instruments-azimuth-thruster-labeled--large.png +0 -0
  7. package/__snapshots__/navigation-instruments-azimuth-thruster-labeled--medium.png +0 -0
  8. package/__snapshots__/navigation-instruments-azimuth-thruster-labeled--no-command.png +0 -0
  9. package/__snapshots__/navigation-instruments-compass--primary.png +0 -0
  10. package/__snapshots__/navigation-instruments-compass-flat--primary.png +0 -0
  11. package/__snapshots__/navigation-instruments-compass-flat--with-fov-indicator.png +0 -0
  12. package/custom-elements.json +968 -7
  13. package/dist/navigation-instruments/compass/arrow.d.ts +7 -0
  14. package/dist/navigation-instruments/compass/arrow.d.ts.map +1 -0
  15. package/dist/navigation-instruments/compass/arrow.js +59 -0
  16. package/dist/navigation-instruments/compass/arrow.js.map +1 -0
  17. package/dist/navigation-instruments/compass/compass.d.ts +23 -0
  18. package/dist/navigation-instruments/compass/compass.d.ts.map +1 -0
  19. package/dist/navigation-instruments/compass/compass.js +139 -0
  20. package/dist/navigation-instruments/compass/compass.js.map +1 -0
  21. package/dist/navigation-instruments/compass/radial-tickmark.d.ts +4 -0
  22. package/dist/navigation-instruments/compass/radial-tickmark.d.ts.map +1 -0
  23. package/dist/navigation-instruments/compass/radial-tickmark.js +69 -0
  24. package/dist/navigation-instruments/compass/radial-tickmark.js.map +1 -0
  25. package/dist/navigation-instruments/compass-flat/compass-flat.css.js +29 -0
  26. package/dist/navigation-instruments/compass-flat/compass-flat.css.js.map +1 -0
  27. package/dist/navigation-instruments/compass-flat/compass-flat.d.ts +45 -0
  28. package/dist/navigation-instruments/compass-flat/compass-flat.d.ts.map +1 -0
  29. package/dist/navigation-instruments/compass-flat/compass-flat.js +223 -0
  30. package/dist/navigation-instruments/compass-flat/compass-flat.js.map +1 -0
  31. package/dist/navigation-instruments/thruster/advice.d.ts.map +1 -1
  32. package/dist/navigation-instruments/thruster/advice.js +9 -5
  33. package/dist/navigation-instruments/thruster/advice.js.map +1 -1
  34. package/dist/navigation-instruments/watch/advice.js +1 -1
  35. package/dist/navigation-instruments/watch/advice.js.map +1 -1
  36. package/dist/navigation-instruments/watch/label.d.ts +3 -0
  37. package/dist/navigation-instruments/watch/label.d.ts.map +1 -0
  38. package/dist/navigation-instruments/watch/label.js +68 -0
  39. package/dist/navigation-instruments/watch/label.js.map +1 -0
  40. package/dist/navigation-instruments/watch/watch.css.js +15 -14
  41. package/dist/navigation-instruments/watch/watch.css.js.map +1 -1
  42. package/dist/navigation-instruments/watch/watch.d.ts +3 -0
  43. package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
  44. package/dist/navigation-instruments/watch/watch.js +34 -1
  45. package/dist/navigation-instruments/watch/watch.js.map +1 -1
  46. package/dist/navigation-instruments/watch-flat/tickmark-flat.d.ts +20 -0
  47. package/dist/navigation-instruments/watch-flat/tickmark-flat.d.ts.map +1 -0
  48. package/dist/navigation-instruments/watch-flat/tickmark-flat.js +53 -0
  49. package/dist/navigation-instruments/watch-flat/tickmark-flat.js.map +1 -0
  50. package/dist/navigation-instruments/watch-flat/watch-flat.css.js +32 -0
  51. package/dist/navigation-instruments/watch-flat/watch-flat.css.js.map +1 -0
  52. package/dist/navigation-instruments/watch-flat/watch-flat.d.ts +29 -0
  53. package/dist/navigation-instruments/watch-flat/watch-flat.d.ts.map +1 -0
  54. package/dist/navigation-instruments/watch-flat/watch-flat.js +184 -0
  55. package/dist/navigation-instruments/watch-flat/watch-flat.js.map +1 -0
  56. package/dist/svghelpers/rectangular.d.ts +1 -0
  57. package/dist/svghelpers/rectangular.d.ts.map +1 -1
  58. package/dist/svghelpers/rectangular.js +3 -2
  59. package/dist/svghelpers/rectangular.js.map +1 -1
  60. package/package.json +16 -11
  61. package/src/navigation-instruments/compass/arrow.ts +61 -0
  62. package/src/navigation-instruments/compass/compass.stories.ts +37 -0
  63. package/src/navigation-instruments/compass/compass.ts +132 -0
  64. package/src/navigation-instruments/compass/radial-tickmark.ts +77 -0
  65. package/src/navigation-instruments/compass-flat/compass-flat.css +23 -0
  66. package/src/navigation-instruments/compass-flat/compass-flat.stories.ts +35 -0
  67. package/src/navigation-instruments/compass-flat/compass-flat.ts +221 -0
  68. package/src/navigation-instruments/thruster/advice.ts +9 -5
  69. package/src/navigation-instruments/watch/advice.ts +1 -1
  70. package/src/navigation-instruments/watch/label.ts +69 -0
  71. package/src/navigation-instruments/watch/watch.css +7 -7
  72. package/src/navigation-instruments/watch/watch.ts +30 -1
  73. package/src/navigation-instruments/watch-flat/tickmark-flat.ts +62 -0
  74. package/src/navigation-instruments/watch-flat/watch-flat.css +19 -0
  75. package/src/navigation-instruments/watch-flat/watch-flat.stories.ts +17 -0
  76. package/src/navigation-instruments/watch-flat/watch-flat.ts +148 -0
  77. package/src/svghelpers/rectangular.ts +6 -3
@@ -0,0 +1,184 @@
1
+ import { unsafeCSS, LitElement, svg, html } from "lit";
2
+ import { property, customElement } from "lit/decorators.js";
3
+ import compentStyle from "./watch-flat.css.js";
4
+ import { tickmark, TickmarkStyle } from "./tickmark-flat.js";
5
+ import { rect } from "../../svghelpers/rectangular.js";
6
+ var __defProp = Object.defineProperty;
7
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
+ var __decorateClass = (decorators, target, key, kind) => {
9
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
10
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
11
+ if (decorator = decorators[i])
12
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
13
+ if (kind && result)
14
+ __defProp(target, key, result);
15
+ return result;
16
+ };
17
+ let ObcWatchFlat = class extends LitElement {
18
+ constructor() {
19
+ super(...arguments);
20
+ this.width = 352;
21
+ this.height = 72;
22
+ this.padding = 0;
23
+ this.rotation = 0;
24
+ this.tickmarkSpacing = 0;
25
+ this.tickmarks = [];
26
+ this.labels = [];
27
+ this.FOVIndicator = [];
28
+ this.trackHeight = 2 / 3 * this.height;
29
+ this.ticksHeight = this.height - this.trackHeight;
30
+ this.borderRadius = 8;
31
+ }
32
+ renderClipPath(offsetY = 0) {
33
+ return svg`
34
+ <clipPath id="frameClipPath${offsetY === 0 ? "" : "Tickmarks"}">
35
+ <rect x="${-this.width / 2}" y="${-this.height / 2 + offsetY}"
36
+ width="${this.width}" height="${this.height}"
37
+ rx="${this.borderRadius}" />
38
+ </clipPath>
39
+ `;
40
+ }
41
+ renderLabelMask() {
42
+ return svg`
43
+ <mask id="labelMask">
44
+ <rect x="${-this.width / 2}" y="${-70}"
45
+ width="${this.width}" height="${32}"
46
+ />
47
+ <linearGradient id="fadeGradient" gradientUnits="userSpaceOnUse"
48
+ x1="${-this.width / 2}" y1="0" x2="${this.width / 2}" y2="0">
49
+ <stop offset="0%" style="stop-color:black; stop-opacity:1;" />
50
+ <stop offset="10%" style="stop-color:white; stop-opacity:1;" />
51
+ <stop offset="50%" style="stop-color:white; stop-opacity:1;" />
52
+ <stop offset="90%" style="stop-color:white; stop-opacity:1;" />
53
+ <stop offset="100%" style="stop-color:black; stop-opacity:1;" />
54
+ </linearGradient>
55
+ <rect x="${-this.width / 2}" y="${-70}"
56
+ width="${this.width}" height="${32}"
57
+ fill="url(#fadeGradient)" />
58
+ </mask>
59
+ `;
60
+ }
61
+ renderLabels(scale) {
62
+ const labels = [];
63
+ for (const l of this.labels) {
64
+ labels.push(
65
+ svg`<g transform="translate(${-this.rotation * this.tickmarkSpacing}, ${-6 / scale})">
66
+ <text x=${l.x} y=${l.y} class="label" fill=${"var(--instrument-tick-mark-secondary-color)"}>
67
+ ${l.text}
68
+ </text>
69
+ </g>`
70
+ );
71
+ }
72
+ return labels;
73
+ }
74
+ watchFace() {
75
+ const strokeWidth = 1;
76
+ return svg`
77
+ ${this.renderClipPath()}
78
+ ${this.renderClipPath(-40)}
79
+ <g clip-path="url(#frameClipPath)">
80
+ ${rect("frame-track", {
81
+ width: this.width,
82
+ height: this.trackHeight,
83
+ y: this.height / 2 - this.trackHeight,
84
+ strokeWidth,
85
+ strokeColor: "var(--instrument-frame-secondary-color)",
86
+ strokePosition: "inside",
87
+ fillColor: "var(--instrument-frame-secondary-color)",
88
+ borderRadius: 0
89
+ })}
90
+ ${rect("frame-ticks", {
91
+ width: this.width,
92
+ height: this.ticksHeight,
93
+ y: this.height / 2 - this.trackHeight - this.ticksHeight,
94
+ strokeWidth,
95
+ strokeColor: "var(--instrument-frame-primary-color)",
96
+ strokePosition: "inside",
97
+ fillColor: "var(--instrument-frame-primary-color)",
98
+ borderRadius: 0
99
+ })}
100
+ </g>
101
+ ${rect("frame-outline", {
102
+ width: this.width,
103
+ height: this.height,
104
+ strokeWidth,
105
+ strokeColor: "var(--instrument-frame-tertiary-color)",
106
+ strokePosition: "inside",
107
+ fillColor: "none",
108
+ borderRadius: this.borderRadius
109
+ })}
110
+ `;
111
+ }
112
+ render() {
113
+ const width = (this.width / 2 + this.padding) * 2;
114
+ const viewBox = `-${width / 2} -${this.height / 2} ${width} ${this.height}`;
115
+ const scale = this.clientWidth / width;
116
+ return html`
117
+ <svg
118
+ width="100%"
119
+ height="100%"
120
+ viewBox=${viewBox}
121
+ style="--scale: ${scale}"
122
+ >
123
+ ${this.watchFace()} ${this.renderLabelMask()} ${this.FOVIndicator}
124
+
125
+ <g clip-path="url(#frameClipPath)">
126
+ ${this.tickmarks.map(
127
+ (t) => svg`
128
+ <g transform="translate(${-this.rotation * this.tickmarkSpacing}, 0)">
129
+ ${tickmark(t.angle, t.type, TickmarkStyle.hinted)}
130
+ </g>
131
+ `
132
+ )}
133
+ </g>
134
+
135
+ <g mask="url(#labelMask)">
136
+ ${this.renderLabels(scale)}
137
+ </svg>
138
+ `;
139
+ }
140
+ };
141
+ ObcWatchFlat.styles = unsafeCSS(compentStyle);
142
+ __decorateClass([
143
+ property({ type: Number })
144
+ ], ObcWatchFlat.prototype, "width", 2);
145
+ __decorateClass([
146
+ property({ type: Number })
147
+ ], ObcWatchFlat.prototype, "height", 2);
148
+ __decorateClass([
149
+ property({ type: Number })
150
+ ], ObcWatchFlat.prototype, "padding", 2);
151
+ __decorateClass([
152
+ property({ type: Number })
153
+ ], ObcWatchFlat.prototype, "rotation", 2);
154
+ __decorateClass([
155
+ property({ type: Number })
156
+ ], ObcWatchFlat.prototype, "tickmarkSpacing", 2);
157
+ __decorateClass([
158
+ property({ type: Number })
159
+ ], ObcWatchFlat.prototype, "angleSetpoint", 2);
160
+ __decorateClass([
161
+ property({ type: Array, attribute: false })
162
+ ], ObcWatchFlat.prototype, "tickmarks", 2);
163
+ __decorateClass([
164
+ property({ type: Array, attribute: false })
165
+ ], ObcWatchFlat.prototype, "labels", 2);
166
+ __decorateClass([
167
+ property({ type: Array, attribute: false })
168
+ ], ObcWatchFlat.prototype, "FOVIndicator", 2);
169
+ __decorateClass([
170
+ property({ type: Number })
171
+ ], ObcWatchFlat.prototype, "trackHeight", 2);
172
+ __decorateClass([
173
+ property({ type: Number })
174
+ ], ObcWatchFlat.prototype, "ticksHeight", 2);
175
+ __decorateClass([
176
+ property({ type: Number })
177
+ ], ObcWatchFlat.prototype, "borderRadius", 2);
178
+ ObcWatchFlat = __decorateClass([
179
+ customElement("obc-watch-flat")
180
+ ], ObcWatchFlat);
181
+ export {
182
+ ObcWatchFlat
183
+ };
184
+ //# sourceMappingURL=watch-flat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch-flat.js","sources":["../../../src/navigation-instruments/watch-flat/watch-flat.ts"],"sourcesContent":["import {LitElement, SVGTemplateResult, html, svg, unsafeCSS} from 'lit';\nimport {customElement, property} from 'lit/decorators.js';\nimport compentStyle from './watch-flat.css?inline';\nimport {Tickmark, TickmarkStyle, tickmark} from './tickmark-flat';\nimport {rect} from '../../svghelpers/rectangular';\nimport {Label} from '../compass-flat/compass-flat';\n\n@customElement('obc-watch-flat')\nexport class ObcWatchFlat extends LitElement {\n @property({type: Number}) width = 352;\n @property({type: Number}) height = 72;\n @property({type: Number}) padding = 0;\n @property({type: Number}) rotation = 0;\n @property({type: Number}) tickmarkSpacing = 0;\n @property({type: Number}) angleSetpoint: number | undefined;\n @property({type: Array, attribute: false}) tickmarks: Tickmark[] = [];\n @property({type: Array, attribute: false}) labels: Label[] = [];\n @property({type: Array, attribute: false}) FOVIndicator: SVGTemplateResult[] =\n [];\n @property({type: Number}) trackHeight = (2 / 3) * this.height;\n @property({type: Number}) ticksHeight = this.height - this.trackHeight;\n @property({type: Number}) borderRadius = 8;\n\n private renderClipPath(offsetY: number = 0): SVGTemplateResult {\n return svg`\n <clipPath id=\"frameClipPath${offsetY === 0 ? '' : 'Tickmarks'}\">\n <rect x=\"${-this.width / 2}\" y=\"${-this.height / 2 + offsetY}\" \n width=\"${this.width}\" height=\"${this.height}\" \n rx=\"${this.borderRadius}\" />\n </clipPath>\n `;\n }\n\n private renderLabelMask(): SVGTemplateResult {\n return svg`\n <mask id=\"labelMask\">\n <rect x=\"${-this.width / 2}\" y=\"${-70}\" \n width=\"${this.width}\" height=\"${32}\"\n />\n <linearGradient id=\"fadeGradient\" gradientUnits=\"userSpaceOnUse\"\n x1=\"${-this.width / 2}\" y1=\"0\" x2=\"${this.width / 2}\" y2=\"0\">\n <stop offset=\"0%\" style=\"stop-color:black; stop-opacity:1;\" />\n <stop offset=\"10%\" style=\"stop-color:white; stop-opacity:1;\" />\n <stop offset=\"50%\" style=\"stop-color:white; stop-opacity:1;\" />\n <stop offset=\"90%\" style=\"stop-color:white; stop-opacity:1;\" />\n <stop offset=\"100%\" style=\"stop-color:black; stop-opacity:1;\" />\n </linearGradient>\n <rect x=\"${-this.width / 2}\" y=\"${-70}\" \n width=\"${this.width}\" height=\"${32}\"\n fill=\"url(#fadeGradient)\" />\n </mask>\n `;\n }\n\n private renderLabels(scale: number): SVGTemplateResult[] {\n const labels: SVGTemplateResult[] = [];\n\n for (const l of this.labels) {\n labels.push(\n svg`<g transform=\"translate(${-this.rotation * this.tickmarkSpacing}, ${-6 / scale})\">\n <text x=${l.x} y=${l.y} class=\"label\" fill=${'var(--instrument-tick-mark-secondary-color)'}>\n ${l.text}\n </text>\n </g>`\n );\n }\n\n return labels;\n }\n\n private watchFace(): SVGTemplateResult {\n const strokeWidth = 1;\n\n return svg`\n ${this.renderClipPath()}\n ${this.renderClipPath(-40)}\n <g clip-path=\"url(#frameClipPath)\">\n ${rect('frame-track', {\n width: this.width,\n height: this.trackHeight,\n y: this.height / 2 - this.trackHeight,\n strokeWidth: strokeWidth,\n strokeColor: 'var(--instrument-frame-secondary-color)',\n strokePosition: 'inside',\n fillColor: 'var(--instrument-frame-secondary-color)',\n borderRadius: 0,\n })}\n ${rect('frame-ticks', {\n width: this.width,\n height: this.ticksHeight,\n y: this.height / 2 - this.trackHeight - this.ticksHeight,\n strokeWidth: strokeWidth,\n strokeColor: 'var(--instrument-frame-primary-color)',\n strokePosition: 'inside',\n fillColor: 'var(--instrument-frame-primary-color)',\n borderRadius: 0,\n })}\n </g>\n ${rect('frame-outline', {\n width: this.width,\n height: this.height,\n strokeWidth: strokeWidth,\n strokeColor: 'var(--instrument-frame-tertiary-color)',\n strokePosition: 'inside',\n fillColor: 'none',\n borderRadius: this.borderRadius,\n })}\n `;\n }\n\n override render() {\n const width = (this.width / 2 + this.padding) * 2;\n const viewBox = `-${width / 2} -${this.height / 2} ${width} ${this.height}`;\n const scale = this.clientWidth / width;\n\n return html`\n <svg\n width=\"100%\"\n height=\"100%\"\n viewBox=${viewBox}\n style=\"--scale: ${scale}\"\n >\n ${this.watchFace()} ${this.renderLabelMask()} ${this.FOVIndicator}\n\n <g clip-path=\"url(#frameClipPath)\">\n ${this.tickmarks.map(\n (t) => svg`\n <g transform=\"translate(${-this.rotation * this.tickmarkSpacing}, 0)\">\n ${tickmark(t.angle, t.type, TickmarkStyle.hinted)}\n </g>\n `\n )}\n </g>\n\n <g mask=\"url(#labelMask)\">\n ${this.renderLabels(scale)}\n </svg>\n `;\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-watch-flat': ObcWatchFlat;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -6,5 +6,6 @@ export declare function rect(id: string, data: {
6
6
  fillColor: string;
7
7
  borderRadius: number;
8
8
  strokePosition: 'inside' | 'outside';
9
+ y?: number;
9
10
  }): import("lit-html").TemplateResult<2> | null;
10
11
  //# sourceMappingURL=rectangular.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rectangular.d.ts","sourceRoot":"","sources":["../../src/svghelpers/rectangular.ts"],"names":[],"mappings":"AAEA,wBAAgB,IAAI,CAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,QAAQ,GAAG,SAAS,CAAC;CACtC,+CA0BF"}
1
+ {"version":3,"file":"rectangular.d.ts","sourceRoot":"","sources":["../../src/svghelpers/rectangular.ts"],"names":[],"mappings":"AAEA,wBAAgB,IAAI,CAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,QAAQ,GAAG,SAAS,CAAC;IACrC,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ,+CA4BF"}
@@ -1,13 +1,14 @@
1
1
  import { svg } from "lit";
2
2
  function rect(id, data) {
3
+ const yPosition = data.y !== void 0 ? data.y : -data.height / 2;
3
4
  if (data.strokePosition === "inside") {
4
5
  return svg`
5
6
  <defs>
6
7
  <clipPath id="clip${id}">
7
- <rect id=${id} x=${-data.width / 2} y=${-data.height / 2} width=${data.width} height=${data.height} rx=${data.borderRadius} vector-effect="non-scaling-stroke" />
8
+ <rect id=${id} x=${-data.width / 2} y=${yPosition} width=${data.width} height=${data.height} rx=${data.borderRadius} vector-effect="non-scaling-stroke" />
8
9
  </clipPath>
9
10
  </defs>
10
- <rect id=${id} x=${-data.width / 2} y=${-data.height / 2} width=${data.width} height=${data.height} rx=${data.borderRadius} vector-effect="non-scaling-stroke" stroke=${data.strokeColor} stroke-width=${data.strokeWidth * 2} fill=${data.fillColor} clip-path="url(#clip${id})"/>
11
+ <rect id=${id} x=${-data.width / 2} y=${yPosition} width=${data.width} height=${data.height} rx=${data.borderRadius} vector-effect="non-scaling-stroke" stroke=${data.strokeColor} stroke-width=${data.strokeWidth * 2} fill=${data.fillColor} clip-path="url(#clip${id})"/>
11
12
  `;
12
13
  } else {
13
14
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"rectangular.js","sources":["../../src/svghelpers/rectangular.ts"],"sourcesContent":["import {svg} from 'lit';\n\nexport function rect(\n id: string,\n data: {\n width: number;\n height: number;\n strokeWidth: number;\n strokeColor: string;\n fillColor: string;\n borderRadius: number;\n strokePosition: 'inside' | 'outside';\n }\n) {\n if (data.strokePosition === 'inside') {\n return svg`\n\t\t<defs>\n\t\t\t<clipPath id=\"clip${id}\">\n\t\t\t\t<rect id=${id} x=${-data.width / 2} y=${-data.height / 2} width=${\n data.width\n } height=${data.height} rx=${\n data.borderRadius\n } vector-effect=\"non-scaling-stroke\" />\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<rect id=${id} x=${-data.width / 2} y=${-data.height / 2} width=${\n data.width\n } height=${data.height} rx=${\n data.borderRadius\n } vector-effect=\"non-scaling-stroke\" stroke=${\n data.strokeColor\n } stroke-width=${data.strokeWidth * 2} fill=${\n data.fillColor\n } clip-path=\"url(#clip${id})\"/>\n\t\t `;\n } else {\n return null;\n }\n}\n"],"names":[],"mappings":";AAEgB,SAAA,KACd,IACA,MASA;AACI,MAAA,KAAK,mBAAmB,UAAU;AAC7B,WAAA;AAAA;AAAA,uBAEY,EAAE;AAAA,eACV,EAAE,MAAM,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,UAClD,KAAK,KACP,WAAW,KAAK,MAAM,OACpB,KAAK,YACP;AAAA;AAAA;AAAA,aAGK,EAAE,MAAM,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,UACpD,KAAK,KACP,WAAW,KAAK,MAAM,OACpB,KAAK,YACP,8CACE,KAAK,WACP,kBAAkB,KAAK,cAAc,CAAC,SACpC,KAAK,SACP,wBAAwB,EAAE;AAAA;AAAA,EAAA,OAErB;AACE,WAAA;AAAA,EACT;AACF;"}
1
+ {"version":3,"file":"rectangular.js","sources":["../../src/svghelpers/rectangular.ts"],"sourcesContent":["import {svg} from 'lit';\n\nexport function rect(\n id: string,\n data: {\n width: number;\n height: number;\n strokeWidth: number;\n strokeColor: string;\n fillColor: string;\n borderRadius: number;\n strokePosition: 'inside' | 'outside';\n y?: number;\n }\n) {\n const yPosition = data.y !== undefined ? data.y : -data.height / 2;\n\n if (data.strokePosition === 'inside') {\n return svg`\n\t\t<defs>\n\t\t\t<clipPath id=\"clip${id}\">\n\t\t\t\t<rect id=${id} x=${-data.width / 2} y=${yPosition} width=${\n data.width\n } height=${data.height} rx=${\n data.borderRadius\n } vector-effect=\"non-scaling-stroke\" />\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<rect id=${id} x=${-data.width / 2} y=${yPosition} width=${\n data.width\n } height=${data.height} rx=${\n data.borderRadius\n } vector-effect=\"non-scaling-stroke\" stroke=${\n data.strokeColor\n } stroke-width=${data.strokeWidth * 2} fill=${\n data.fillColor\n } clip-path=\"url(#clip${id})\"/> \n\t\t `;\n } else {\n return null;\n }\n}\n"],"names":[],"mappings":";AAEgB,SAAA,KACd,IACA,MAUA;AACM,QAAA,YAAY,KAAK,MAAM,SAAY,KAAK,IAAI,CAAC,KAAK,SAAS;AAE7D,MAAA,KAAK,mBAAmB,UAAU;AAC7B,WAAA;AAAA;AAAA,uBAEY,EAAE;AAAA,eACV,EAAE,MAAM,CAAC,KAAK,QAAQ,CAAC,MAAM,SAAS,UAC3C,KAAK,KACP,WAAW,KAAK,MAAM,OACpB,KAAK,YACP;AAAA;AAAA;AAAA,aAGK,EAAE,MAAM,CAAC,KAAK,QAAQ,CAAC,MAAM,SAAS,UAC7C,KAAK,KACP,WAAW,KAAK,MAAM,OACpB,KAAK,YACP,8CACE,KAAK,WACP,kBAAkB,KAAK,cAAc,CAAC,SACpC,KAAK,SACP,wBAAwB,EAAE;AAAA;AAAA,EAAA,OAErB;AACE,WAAA;AAAA,EACT;AACF;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oicl/openbridge-webcomponents",
3
- "version": "0.0.15-dev-20240916185711",
3
+ "version": "0.0.15-dev-20240923190011",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,23 +35,28 @@
35
35
  "lit": "^3.1.0"
36
36
  },
37
37
  "devDependencies": {
38
+ "@chromatic-com/storybook": "^1.7.0",
38
39
  "@custom-elements-manifest/analyzer": "^0.9.0",
39
40
  "@lit-labs/cli": "^0.6.4",
40
41
  "@lit-labs/gen-wrapper-react": "^0.3.2",
41
42
  "@lit-labs/gen-wrapper-vue": "^0.3.3",
42
43
  "@open-wc/lit-helpers": "^0.7.0",
43
- "@storybook/addon-essentials": "^8.1.11",
44
- "@storybook/addon-interactions": "^8.1.11",
45
- "@storybook/addon-links": "^8.1.11",
46
- "@storybook/addon-storysource": "^8.1.11",
47
- "@storybook/addon-themes": "^8.1.11",
48
- "@storybook/blocks": "^8.1.11",
49
- "@storybook/test": "^8.1.11",
44
+ "@playwright/test": "^1.46.1",
45
+ "@storybook/addon-essentials": "^8.2.9",
46
+ "@storybook/addon-interactions": "^8.2.9",
47
+ "@storybook/addon-links": "^8.2.9",
48
+ "@storybook/addon-storysource": "^8.2.9",
49
+ "@storybook/addon-themes": "^8.2.9",
50
+ "@storybook/blocks": "^8.2.9",
51
+ "@storybook/manager-api": "^8.2.9",
52
+ "@storybook/test": "^8.2.9",
50
53
  "@storybook/test-runner": "^0.17.0",
51
- "@storybook/web-components": "^8.1.11",
52
- "@storybook/web-components-vite": "^8.1.11",
54
+ "@storybook/theming": "^8.2.9",
55
+ "@storybook/web-components": "^8.2.9",
56
+ "@storybook/web-components-vite": "^8.2.9",
53
57
  "@topcli/prompts": "^1.8.0",
54
58
  "@types/jest-image-snapshot": "^6",
59
+ "@types/node": "^22.4.1",
55
60
  "concurrently": "^8.2.2",
56
61
  "dotenv": "^16.3.1",
57
62
  "eslint": "^8.56.0",
@@ -67,7 +72,7 @@
67
72
  "prettier": "^3.1.1",
68
73
  "release-it": "^17.1.1",
69
74
  "rollup-plugin-postcss-lit": "^2.1.0",
70
- "storybook": "^8.1.11",
75
+ "storybook": "^8.2.9",
71
76
  "tsx": "^4.7.1",
72
77
  "typescript": "^5.3.3",
73
78
  "vite": "^5.2.7",
@@ -0,0 +1,61 @@
1
+ import {SVGTemplateResult, svg} from 'lit';
2
+
3
+ export enum ArrowStyle {
4
+ HDG = 'HDG',
5
+ COG = 'COG',
6
+ }
7
+
8
+ export function arrow(
9
+ style: ArrowStyle,
10
+ angle: number
11
+ ): SVGTemplateResult | SVGTemplateResult[] {
12
+ const colorName = 'var(--instrument-enhanced-secondary-color)';
13
+
14
+ if (style === ArrowStyle.HDG) {
15
+ return svg`
16
+ <g transform="translate(-32, -${256}) rotate(${angle}, 32, 256)">
17
+ <rect x="28" y="118" width="8" height="139" rx="4" fill="${colorName}" />
18
+ <rect x="30.0039" y="256" width="4" height="156" rx="2" fill="${colorName}" />
19
+ <circle cx="32" cy="256" r="8" fill="${colorName}" />
20
+ <mask
21
+ id="mask0_262_65165"
22
+ style="mask-type:luminance"
23
+ maskUnits="userSpaceOnUse"
24
+ x="8"
25
+ y="94"
26
+ width="48"
27
+ height="50"
28
+ >
29
+ <path
30
+ d="M13.2833 140.564L32 96L50.7167 140.564C51.4569 142.326 49.54 144.023 47.8805 143.075L32 136L16.1195 143.075C14.46 144.023 12.5432 142.326 13.2833 140.564Z"
31
+ fill="white"
32
+ stroke="black"
33
+ />
34
+ </mask>
35
+ <g mask="url(#mask0_262_65165)">
36
+ <path
37
+ d="M13.2833 140.564L32 96L50.7167 140.564C51.4569 142.326 49.54 144.023 47.8805 143.075L32 136L16.1195 143.075C14.46 144.023 12.5432 142.326 13.2833 140.564Z"
38
+ fill="${colorName}"
39
+ />
40
+ </g>
41
+ </g>
42
+ `;
43
+ } else if (style === ArrowStyle.COG) {
44
+ return svg`
45
+ <g transform="translate(-32, -${256}) rotate(${angle}, 32, 256)">
46
+ <path
47
+ fill-rule="evenodd"
48
+ clip-rule="evenodd"
49
+ d="M13.2833 140.564C12.5431 142.326 14.46 144.023 16.1195 143.075L32 136L47.8805 143.075C49.54 144.023 51.4568 142.326 50.7167 140.564L32 96L13.2833 140.564ZM32 106.33L19.2545 136.676L32 131.393L44.7455 136.676L32 106.33ZM49.865 139.602L49.8625 139.6L49.865 139.602Z"
50
+ fill="${colorName}"
51
+ />
52
+ <rect x="30" y="133" width="4" height="124" rx="2" fill="${colorName}" />
53
+ <circle cx="32" cy="256" r="4" fill="${colorName}" />
54
+ </g>
55
+ `;
56
+ } else {
57
+ return [];
58
+ }
59
+
60
+ // return [...shaft, circle, arrowTip];
61
+ }
@@ -0,0 +1,37 @@
1
+ import type {Meta, StoryObj} from '@storybook/web-components';
2
+ import {ObcCompass} from './compass';
3
+ import './compass';
4
+ import {beta6Decorator, widthDecorator} from '../../storybook-util';
5
+ import {AdviceType} from '../watch/advice';
6
+
7
+ const meta: Meta<typeof ObcCompass> = {
8
+ title: 'Navigation Instruments/Compass',
9
+ tags: ['autodocs'],
10
+ component: 'obc-compass',
11
+ args: {
12
+ width: 512,
13
+ heading: 311,
14
+ courseOverGround: 338,
15
+ headingAdvices: [
16
+ {
17
+ minAngle: 20,
18
+ maxAngle: 50,
19
+ type: AdviceType.advice,
20
+ hinted: false,
21
+ },
22
+ ],
23
+ },
24
+ argTypes: {
25
+ width: {control: {type: 'range', min: 32, max: 1028, step: 1}},
26
+ heading: {control: {type: 'range', min: 0, max: 360, step: 1}},
27
+ courseOverGround: {control: {type: 'range', min: 0, max: 360, step: 1}},
28
+ },
29
+ decorators: [widthDecorator, beta6Decorator],
30
+ } satisfies Meta<ObcCompass>;
31
+
32
+ export default meta;
33
+ type Story = StoryObj<ObcCompass>;
34
+
35
+ export const Primary: Story = {
36
+ args: {},
37
+ };
@@ -0,0 +1,132 @@
1
+ import {LitElement, css, html} from 'lit';
2
+ import {customElement, property} from 'lit/decorators.js';
3
+ import '../watch/watch';
4
+ import {Tickmark, TickmarkType} from '../watch/tickmark';
5
+ import {arrow, ArrowStyle} from './arrow';
6
+ import {
7
+ AdviceState,
8
+ AdviceType,
9
+ AngleAdvice,
10
+ AngleAdviceRaw,
11
+ } from '../watch/advice';
12
+ import {radialTickmarks} from './radial-tickmark';
13
+
14
+ @customElement('obc-compass')
15
+ export class ObcCompass extends LitElement {
16
+ @property({type: Number}) heading = 0;
17
+ @property({type: Number}) courseOverGround = 0;
18
+ @property({type: Number}) padding = 48;
19
+ @property({type: Array, attribute: false}) headingAdvices: AngleAdvice[] = [];
20
+ @property({type: Number}) containerWidth = 0;
21
+
22
+ private resizeObserver: ResizeObserver = new ResizeObserver((entries) => {
23
+ for (const entry of entries) {
24
+ this.containerWidth = entry.contentRect.width;
25
+ this.adjustPadding();
26
+ }
27
+ });
28
+
29
+ override connectedCallback() {
30
+ super.connectedCallback();
31
+ this.resizeObserver.observe(this);
32
+ }
33
+
34
+ override disconnectedCallback() {
35
+ super.disconnectedCallback();
36
+ this.resizeObserver.unobserve(this);
37
+ }
38
+
39
+ private adjustPadding() {
40
+ const deltaWidth = 512 - this.containerWidth;
41
+ const steps = deltaWidth / 128;
42
+ let deltaPadding = 0;
43
+ if (deltaWidth > 0) {
44
+ deltaPadding = steps * 48;
45
+ } else {
46
+ deltaPadding = steps * 6;
47
+ }
48
+
49
+ this.padding = 72 + deltaPadding;
50
+ }
51
+
52
+ private get angleAdviceRaw(): AngleAdviceRaw[] {
53
+ return this.headingAdvices.map(({minAngle, maxAngle, hinted, type}) => {
54
+ const state =
55
+ this.heading >= minAngle && this.heading <= maxAngle
56
+ ? AdviceState.triggered
57
+ : hinted
58
+ ? AdviceState.hinted
59
+ : AdviceState.regular;
60
+ return {minAngle, maxAngle, type, state};
61
+ });
62
+ }
63
+
64
+ override render() {
65
+ const tickmarks: Tickmark[] = [
66
+ {angle: 0, type: TickmarkType.main},
67
+ {angle: 90, type: TickmarkType.main},
68
+ {angle: 180, type: TickmarkType.main},
69
+ {angle: 270, type: TickmarkType.main},
70
+ ];
71
+
72
+ const rt = this.headingAdvices.map(({minAngle, maxAngle, type}) =>
73
+ radialTickmarks(
74
+ minAngle,
75
+ maxAngle,
76
+ type === AdviceType.caution ? TickmarkType.secondary : undefined
77
+ )
78
+ );
79
+
80
+ const width = (176 + this.padding) * 2;
81
+ const viewBox = `-${width / 2} -${width / 2} ${width} ${width}`;
82
+
83
+ return html`
84
+ <div class="container">
85
+ <obc-watch
86
+ .padding=${this.padding}
87
+ .advices=${this.angleAdviceRaw}
88
+ .tickmarks=${tickmarks}
89
+ .labelFrameEnabled=${true}
90
+ .crosshairEnabled=${true}
91
+ >
92
+ </obc-watch>
93
+ <svg viewBox="${viewBox}">
94
+ ${rt} ${arrow(ArrowStyle.HDG, this.heading)}
95
+ ${arrow(ArrowStyle.COG, this.courseOverGround)}
96
+ </svg>
97
+ </div>
98
+ `;
99
+ }
100
+
101
+ static override styles = css`
102
+ * {
103
+ box-sizing: border-box;
104
+ }
105
+
106
+ .container {
107
+ position: relative;
108
+ width: 100%;
109
+ height: 100%;
110
+ }
111
+
112
+ .container > * {
113
+ position: absolute;
114
+ top: 0;
115
+ left: 0;
116
+ width: 100%;
117
+ height: 100%;
118
+ }
119
+
120
+ :host {
121
+ display: block;
122
+ width: 100%;
123
+ height: 100%;
124
+ }
125
+ `;
126
+ }
127
+
128
+ declare global {
129
+ interface HTMLElementTagNameMap {
130
+ 'obc-compass': ObcCompass;
131
+ }
132
+ }
@@ -0,0 +1,77 @@
1
+ import {SVGTemplateResult, svg} from 'lit';
2
+ import {TickmarkType, TickmarkStyle, tickmarkColor} from '../watch/tickmark';
3
+
4
+ export function radialTickmarks(
5
+ minAngle: number,
6
+ maxAngle: number,
7
+ type: TickmarkType | undefined
8
+ ): SVGTemplateResult[] {
9
+ if (type === TickmarkType.main || type === TickmarkType.tertiary) {
10
+ throw new Error(
11
+ 'Only secondary tickmarks or undefined tickmarks (dots) are supported'
12
+ );
13
+ }
14
+
15
+ const origin = {x: 0, y: 0};
16
+ const radius = 320 / 2;
17
+ const strokeWidth = '1.2';
18
+ const margin = 1.5;
19
+ const colorName = tickmarkColor(TickmarkStyle.hinted);
20
+ const tickWidth = type === TickmarkType.secondary ? 4 : 1;
21
+ const tickmarks: SVGTemplateResult[] = [];
22
+
23
+ const sinMin = Math.sin((minAngle * Math.PI) / 180);
24
+ const cosMin = Math.cos((minAngle * Math.PI) / 180);
25
+ const sinMax = Math.sin((maxAngle * Math.PI) / 180);
26
+ const cosMax = Math.cos((maxAngle * Math.PI) / 180);
27
+
28
+ const deltaIncrement = tickWidth * margin;
29
+
30
+ for (let deltaR = 0; deltaR <= radius; deltaR += deltaIncrement) {
31
+ const xMin = origin.x + sinMin * deltaR;
32
+ const yMin = origin.y - cosMin * deltaR;
33
+ const xMax = origin.x + sinMax * deltaR;
34
+ const yMax = origin.y - cosMax * deltaR;
35
+
36
+ if (type === undefined) {
37
+ const size = 1;
38
+ tickmarks.push(
39
+ svg`<rect
40
+ x=${xMin - size / 2}
41
+ y=${yMin - size / 2}
42
+ width=${size}
43
+ height=${size}
44
+ fill=${colorName}
45
+ transform="rotate(${minAngle} ${xMin} ${yMin})"
46
+ vector-effect="non-scaling-stroke"/>`
47
+ );
48
+ tickmarks.push(
49
+ svg`<rect
50
+ x=${xMax - size / 2}
51
+ y=${yMax - size / 2}
52
+ width=${size}
53
+ height=${size}
54
+ fill=${colorName}
55
+ transform="rotate(${maxAngle} ${xMax} ${yMax})"
56
+ vector-effect="non-scaling-stroke"/>`
57
+ );
58
+ } else {
59
+ const currentRadius = Math.min(deltaR + tickWidth, radius);
60
+ const x2Min = origin.x + sinMin * currentRadius;
61
+ const y2Min = origin.y - cosMin * currentRadius;
62
+ const x2Max = origin.x + sinMax * currentRadius;
63
+ const y2Max = origin.y - cosMax * currentRadius;
64
+
65
+ tickmarks.push(
66
+ svg`<line x1=${xMin} y1=${yMin} x2=${x2Min} y2=${y2Min} stroke=${colorName} stroke-width=${strokeWidth} vector-effect="non-scaling-stroke"/>`
67
+ );
68
+ tickmarks.push(
69
+ svg`<line x1=${xMax} y1=${yMax} x2=${x2Max} y2=${y2Max} stroke=${colorName} stroke-width=${strokeWidth} vector-effect="non-scaling-stroke"/>`
70
+ );
71
+
72
+ if (currentRadius >= radius) break;
73
+ }
74
+ }
75
+
76
+ return tickmarks;
77
+ }
@@ -0,0 +1,23 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ .container {
6
+ position: relative;
7
+ width: 100%;
8
+ height: 100%;
9
+ }
10
+
11
+ .container > * {
12
+ position: absolute;
13
+ top: 0;
14
+ left: 0;
15
+ width: 100%;
16
+ height: 100%;
17
+ }
18
+
19
+ :host {
20
+ display: block;
21
+ width: 100%;
22
+ height: 100%;
23
+ }
@@ -0,0 +1,35 @@
1
+ import type {Meta, StoryObj} from '@storybook/web-components';
2
+ import {ObcCompassFlat} from './compass-flat';
3
+ import './compass-flat';
4
+ import {beta6Decorator, widthDecorator} from '../../storybook-util';
5
+ import {LabelPosition} from './compass-flat';
6
+
7
+ const meta: Meta<typeof ObcCompassFlat> = {
8
+ title: 'Navigation Instruments/Compass flat',
9
+ tags: ['autodocs'],
10
+ component: 'obc-compass-flat',
11
+ args: {
12
+ width: 512,
13
+ heading: 45,
14
+ courseOverGround: 50,
15
+ FOVIndicator: false,
16
+ minFOV: 90,
17
+ },
18
+ argTypes: {
19
+ width: {control: {type: 'range', min: 32, max: 1028, step: 1}},
20
+ heading: {control: {type: 'range', min: 0, max: 360, step: 1}},
21
+ courseOverGround: {control: {type: 'range', min: 0, max: 360, step: 1}},
22
+ },
23
+ decorators: [widthDecorator, beta6Decorator],
24
+ } satisfies Meta<ObcCompassFlat>;
25
+
26
+ export default meta;
27
+ type Story = StoryObj<ObcCompassFlat>;
28
+
29
+ export const Primary: Story = {};
30
+
31
+ export const WithFOVIndicator: Story = {
32
+ args: {
33
+ FOVIndicator: true,
34
+ },
35
+ };