@fusion.dev/helix-headless 0.104.1 → 0.106.0

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 (167) hide show
  1. package/custom-elements.json +10386 -3813
  2. package/dist/accordion/item/accordion-item.d.ts +17 -16
  3. package/dist/accordion/root/accordion-root.d.ts +13 -17
  4. package/dist/announcement/root/announcement-root.d.ts +24 -31
  5. package/dist/app-header/root/app-header-root.d.ts +14 -14
  6. package/dist/area-chart/root/area-chart-root.d.ts +29 -48
  7. package/dist/avatar/group/avatar-group.d.ts +6 -8
  8. package/dist/avatar/root/avatar-root.d.ts +11 -11
  9. package/dist/badge/root/badge-root.d.ts +14 -15
  10. package/dist/bar-chart/root/bar-chart-root.d.ts +40 -44
  11. package/dist/blockquote/root/blockquote-root.d.ts +5 -7
  12. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +25 -33
  13. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +12 -11
  14. package/dist/button/root/button-root.d.ts +16 -17
  15. package/dist/button-group/root/button-group-root.d.ts +9 -10
  16. package/dist/callout/root/callout-root.d.ts +10 -15
  17. package/dist/canvas/edge/canvas-edge.d.ts +21 -21
  18. package/dist/canvas/node/canvas-node.d.ts +43 -45
  19. package/dist/canvas/root/canvas-root.d.ts +100 -99
  20. package/dist/card/root/card-root.d.ts +10 -12
  21. package/dist/carousel/root/carousel-root.d.ts +32 -33
  22. package/dist/chat/composer/chat-composer.d.ts +24 -26
  23. package/dist/chat/message/chat-message.d.ts +30 -33
  24. package/dist/chat/root/chat-root.d.ts +63 -80
  25. package/dist/checkbox/root/checkbox-root.d.ts +30 -32
  26. package/dist/checkbox-group/root/checkbox-group-root.d.ts +25 -25
  27. package/dist/code/root/code-root.d.ts +6 -10
  28. package/dist/code-block/root/code-block-root.d.ts +35 -48
  29. package/dist/color-input/root/color-input-root.d.ts +62 -56
  30. package/dist/color-input/root/color-input-root.js +3 -0
  31. package/dist/color-picker/root/color-picker-root.d.ts +61 -49
  32. package/dist/color-picker/root/color-picker-root.js +4 -0
  33. package/dist/combobox/root/combobox-root.d.ts +52 -53
  34. package/dist/command/root/command-root.d.ts +40 -40
  35. package/dist/command/root/command-root.js +1 -0
  36. package/dist/copy-button/root/copy-button-root.d.ts +24 -26
  37. package/dist/data-table/root/data-table-root.d.ts +106 -107
  38. package/dist/date-input/root/date-input-root.d.ts +54 -53
  39. package/dist/date-picker/root/date-picker-root.d.ts +25 -21
  40. package/dist/description-list/item/description-list-item.d.ts +7 -7
  41. package/dist/description-list/root/description-list-root.d.ts +10 -15
  42. package/dist/dialog/root/dialog-root.d.ts +22 -19
  43. package/dist/divider/root/divider-root.d.ts +12 -13
  44. package/dist/donut-chart/root/donut-chart-root.d.ts +19 -20
  45. package/dist/drawer/root/drawer-root.d.ts +19 -18
  46. package/dist/drilldown/item/drilldown-item.d.ts +28 -28
  47. package/dist/drilldown/root/drilldown-root.d.ts +22 -28
  48. package/dist/element-events.generated.js +19 -1
  49. package/dist/empty/root/empty-root.d.ts +16 -19
  50. package/dist/file-input/root/file-input-root.d.ts +32 -32
  51. package/dist/filter-input/chip/filter-chip.d.ts +36 -39
  52. package/dist/filter-input/field/filter-field.d.ts +25 -31
  53. package/dist/filter-input/menu/filter-menu.d.ts +50 -47
  54. package/dist/filter-input/operator/filter-operator.d.ts +15 -20
  55. package/dist/filter-input/root/filter-input-root.d.ts +80 -98
  56. package/dist/format-date/root/format-date-root.d.ts +19 -27
  57. package/dist/heatmap/root/heatmap-root.d.ts +40 -41
  58. package/dist/icon/root/icon-root.d.ts +21 -23
  59. package/dist/icon-button/root/icon-button-root.d.ts +12 -14
  60. package/dist/image/root/image-root.d.ts +30 -30
  61. package/dist/input/root/input-root.d.ts +36 -35
  62. package/dist/kbd/root/kbd-root.d.ts +13 -15
  63. package/dist/legend/root/legend-root.d.ts +18 -16
  64. package/dist/lightbox/item/lightbox-item.d.ts +13 -15
  65. package/dist/lightbox/root/lightbox-root.d.ts +40 -41
  66. package/dist/line-chart/root/line-chart-root.d.ts +20 -29
  67. package/dist/list/item/list-item.d.ts +17 -22
  68. package/dist/list/root/list-root.d.ts +6 -7
  69. package/dist/listbox/root/listbox-root.d.ts +42 -41
  70. package/dist/loader/root/loader-root.d.ts +16 -20
  71. package/dist/map/area/map-area.d.ts +48 -0
  72. package/dist/map/area/map-area.js +40 -0
  73. package/dist/map/buildings/map-buildings.d.ts +45 -0
  74. package/dist/map/buildings/map-buildings.js +37 -0
  75. package/dist/map/contours/map-contours.d.ts +61 -0
  76. package/dist/map/contours/map-contours.js +54 -0
  77. package/dist/map/index.d.ts +14 -0
  78. package/dist/map/index.js +10 -0
  79. package/dist/map/landcover/map-landcover.d.ts +47 -0
  80. package/dist/map/landcover/map-landcover.js +37 -0
  81. package/dist/map/marker/map-marker.d.ts +120 -0
  82. package/dist/map/marker/map-marker.js +109 -0
  83. package/dist/map/points/map-points.d.ts +139 -0
  84. package/dist/map/points/map-points.js +161 -0
  85. package/dist/map/position/map-position.d.ts +70 -0
  86. package/dist/map/position/map-position.js +89 -0
  87. package/dist/map/raster/map-raster.d.ts +84 -0
  88. package/dist/map/raster/map-raster.js +96 -0
  89. package/dist/map/root/map-root.d.ts +269 -52
  90. package/dist/map/root/map-root.js +473 -5
  91. package/dist/map/route/map-route.d.ts +73 -0
  92. package/dist/map/route/map-route.js +52 -0
  93. package/dist/map/terrain/map-terrain.d.ts +68 -0
  94. package/dist/map/terrain/map-terrain.js +58 -0
  95. package/dist/megamenu/root/megamenu-root.d.ts +27 -25
  96. package/dist/menu/item/menu-item.d.ts +30 -31
  97. package/dist/menu/root/menu-root.d.ts +25 -29
  98. package/dist/meter/root/meter-root.d.ts +22 -21
  99. package/dist/navigation/root/navigation-root.d.ts +13 -16
  100. package/dist/notifications/group/notification-group.d.ts +27 -28
  101. package/dist/notifications/notification/notification.d.ts +35 -42
  102. package/dist/notifications/root/notifications-root.d.ts +22 -24
  103. package/dist/number-input/root/number-input-root.d.ts +46 -50
  104. package/dist/object-inspector/root/object-inspector-root.d.ts +46 -58
  105. package/dist/option/group/option-group.d.ts +11 -11
  106. package/dist/option/root/option-root.d.ts +28 -33
  107. package/dist/outline/root/outline-root.d.ts +18 -17
  108. package/dist/pagination/root/pagination-root.d.ts +37 -39
  109. package/dist/pagination/size/pagination-size.d.ts +37 -43
  110. package/dist/pagination/summary/pagination-summary.d.ts +12 -14
  111. package/dist/pin-input/root/pin-input-root.d.ts +32 -38
  112. package/dist/popover/root/popover-root.d.ts +16 -21
  113. package/dist/progress/root/progress-root.d.ts +16 -14
  114. package/dist/qr-code/root/qr-code-root.d.ts +22 -25
  115. package/dist/quote/root/quote-root.d.ts +5 -6
  116. package/dist/radio/group/radio-group.d.ts +26 -28
  117. package/dist/radio/root/radio-root.d.ts +24 -28
  118. package/dist/rating/root/rating-root.d.ts +27 -30
  119. package/dist/scatter-chart/root/scatter-chart-root.d.ts +43 -55
  120. package/dist/scroll-area/root/scroll-area-root.d.ts +15 -21
  121. package/dist/select/root/select-root.d.ts +41 -44
  122. package/dist/skeleton/root/skeleton-root.d.ts +7 -8
  123. package/dist/skip-link/root/skip-link-root.d.ts +21 -28
  124. package/dist/slider/root/slider-root.d.ts +36 -39
  125. package/dist/sparkline/root/sparkline-root.d.ts +22 -28
  126. package/dist/speed-dial/item/speed-dial-item.d.ts +21 -21
  127. package/dist/speed-dial/root/speed-dial-root.d.ts +25 -27
  128. package/dist/splitter/root/splitter-root.d.ts +37 -41
  129. package/dist/stat/group/stat-group.d.ts +4 -4
  130. package/dist/stat/root/stat-root.d.ts +8 -9
  131. package/dist/steps/root/steps-root.d.ts +11 -14
  132. package/dist/steps/step/step.d.ts +22 -29
  133. package/dist/switch/root/switch-root.d.ts +20 -24
  134. package/dist/table/cell/table-cell.d.ts +18 -8
  135. package/dist/table/cell/table-cell.js +5 -1
  136. package/dist/table/column/table-column.d.ts +62 -55
  137. package/dist/table/root/table-root.d.ts +16 -17
  138. package/dist/table/row/table-row.d.ts +3 -3
  139. package/dist/tabs/panel/tab-panel.d.ts +3 -3
  140. package/dist/tabs/root/tabs-root.d.ts +19 -17
  141. package/dist/tabs/tab/tab.d.ts +6 -6
  142. package/dist/tag/root/tag-root.d.ts +11 -12
  143. package/dist/textarea/root/textarea-root.d.ts +29 -32
  144. package/dist/time-input/root/time-input-root.d.ts +70 -69
  145. package/dist/time-picker/root/time-picker-root.d.ts +58 -66
  146. package/dist/timeline/item/timeline-item.d.ts +18 -24
  147. package/dist/timeline/root/timeline-root.d.ts +13 -21
  148. package/dist/toast/item/toast-item.d.ts +21 -22
  149. package/dist/toast/root/toast-root.d.ts +18 -23
  150. package/dist/toggle/group/toggle-group.d.ts +23 -25
  151. package/dist/toggle/root/toggle-root.d.ts +8 -8
  152. package/dist/toolbar/root/toolbar-root.d.ts +7 -10
  153. package/dist/tooltip/root/tooltip-root.d.ts +41 -47
  154. package/dist/tour/root/tour-root.d.ts +24 -26
  155. package/dist/tour/stop/tour-stop.d.ts +39 -48
  156. package/dist/tree/item/tree-item.d.ts +23 -25
  157. package/dist/tree/root/tree-root.d.ts +13 -12
  158. package/dist/trend/root/trend-root.d.ts +18 -19
  159. package/dist/video/root/video-root.d.ts +108 -101
  160. package/dist/video-playlist/item/video-playlist-item.d.ts +23 -28
  161. package/dist/video-playlist/menu/video-playlist-menu.d.ts +17 -21
  162. package/dist/video-playlist/root/video-playlist-root.d.ts +21 -25
  163. package/dist/virtual-list/root/virtual-list-root.d.ts +27 -26
  164. package/package.json +5 -5
  165. package/types/jsx.d.ts +14011 -11983
  166. package/types/svelte.d.ts +10 -0
  167. package/types/vue.d.ts +2143 -1497
@@ -4,13 +4,423 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement, HelixMapPanEvent, HelixMapZoomEvent, hostAria, MixinViewport, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
9
- import { property } from 'lit/decorators.js';
7
+ import { customElement, HelixActivateEvent, HelixElement, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, hostAria, KeyboardNavigationController, MixinViewport, } from '@fusion.dev/helix-core';
8
+ import { buildings, cloudRain, dotsNine, leaf, mountains, path, polygon, stack, target, } from '@fusion.dev/helix-icons';
9
+ import { html, nothing } from 'lit';
10
+ import { property, state } from 'lit/decorators.js';
11
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
+ const MAP_TYPES = ['standard', 'hybrid', 'satellite'];
13
+ const iconOf = (layer) => {
14
+ switch (layer?.layerKind) {
15
+ case 'route':
16
+ return path;
17
+ case 'area':
18
+ return polygon;
19
+ case 'points':
20
+ return dotsNine;
21
+ case 'buildings':
22
+ return buildings;
23
+ case 'terrain':
24
+ return mountains;
25
+ case 'contours':
26
+ return target;
27
+ case 'landcover':
28
+ return leaf;
29
+ case 'raster':
30
+ return cloudRain;
31
+ default:
32
+ return stack;
33
+ }
34
+ };
35
+ const ROTATE_STEP = 15;
36
+ const TILT_STEP = 10;
37
+ const TILTED = 50;
10
38
  let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement) {
11
39
  constructor() {
12
40
  super(...arguments);
41
+ this.bearing = 0;
42
+ this.cluster = false;
43
+ this.faceNorthLabel = 'Face north';
44
+ this.hybridLabel = 'Hybrid';
13
45
  this.label = '';
46
+ this.layersLabel = 'Map layers';
47
+ this.mapPreviews = {};
48
+ this.mapTypeLabel = 'Map type';
49
+ this.pitch = 0;
50
+ this.rotateLeftLabel = 'Rotate left';
51
+ this.rotateRightLabel = 'Rotate right';
52
+ this.satelliteLabel = 'Satellite';
53
+ this.standardLabel = 'Standard';
54
+ this.tiltLabel = 'Show in 3D';
55
+ this.layersOpen = false;
56
+ this.handleOutside = (event) => {
57
+ if (!event.composedPath().includes(this)) {
58
+ this.layersOpen = false;
59
+ }
60
+ };
61
+ this.handleMenuKeydown = (event) => {
62
+ event.stopPropagation();
63
+ if (event.key === 'Escape' && this.layersOpen) {
64
+ this.layersOpen = false;
65
+ this.renderRoot.querySelector('[part="layers-toggle"]')?.focus();
66
+ }
67
+ };
68
+ this.handlePartChange = (event) => {
69
+ if (event.detail.kind !== 'place') {
70
+ this.requestUpdate();
71
+ }
72
+ };
73
+ this.stop = 0;
74
+ this.keys = new KeyboardNavigationController(this, {
75
+ items: () => this.markers.map((marker, at) => ({
76
+ contains: (node) => node !== null && (marker.contains(node) || marker.shadowRoot?.contains(node) === true),
77
+ disabled: marker.gatheredBy !== undefined,
78
+ focusItem: () => {
79
+ this.stop = at;
80
+ marker.focus();
81
+ },
82
+ select: () => { },
83
+ selected: false,
84
+ })),
85
+ origin: 'focus',
86
+ selects: () => false,
87
+ });
88
+ this.handleFocusIn = (event) => {
89
+ const marker = this.markerOf(event);
90
+ const at = marker ? this.markers.indexOf(marker) : -1;
91
+ if (at !== -1) {
92
+ this.stop = at;
93
+ }
94
+ };
95
+ this.handleClick = (event) => {
96
+ const marker = this.markerOf(event);
97
+ if (marker) {
98
+ this.activate(marker);
99
+ }
100
+ };
101
+ this.handleMarkerKeydown = (event) => {
102
+ const marker = this.markerOf(event);
103
+ if (!marker || (event.key !== 'Enter' && event.key !== ' ')) {
104
+ return;
105
+ }
106
+ event.preventDefault();
107
+ if (event.key === 'Enter' && !event.repeat) {
108
+ this.activate(marker);
109
+ }
110
+ };
111
+ this.handleMarkerKeyup = (event) => {
112
+ const marker = this.markerOf(event);
113
+ if (marker && event.key === ' ') {
114
+ event.preventDefault();
115
+ this.activate(marker);
116
+ }
117
+ };
118
+ this.handleSlotChange = () => {
119
+ this.requestUpdate();
120
+ };
121
+ }
122
+ get layers() {
123
+ return this.knownChildren.filter((child) => child.isMapLayer === true);
124
+ }
125
+ get imagery() {
126
+ return this.layers.filter((layer) => layer.layerKind === 'raster' && layer.ground === true);
127
+ }
128
+ nameOf(type) {
129
+ return type === 'hybrid'
130
+ ? this.hybridLabel
131
+ : type === 'satellite'
132
+ ? this.satelliteLabel
133
+ : this.standardLabel;
134
+ }
135
+ showLayers(layers, shown) {
136
+ for (const layer of layers) {
137
+ layer.hidden = !shown;
138
+ }
139
+ this.requestUpdate();
140
+ }
141
+ get layerEntries() {
142
+ const entries = [];
143
+ const groups = new Map();
144
+ const imagery = this.imagery;
145
+ for (const layer of this.layers.filter((one) => !imagery.includes(one))) {
146
+ const had = layer.group ? groups.get(layer.group) : undefined;
147
+ if (had) {
148
+ had.push(layer);
149
+ continue;
150
+ }
151
+ const entry = [layer];
152
+ entries.push(entry);
153
+ if (layer.group) {
154
+ groups.set(layer.group, entry);
155
+ }
156
+ }
157
+ return entries;
158
+ }
159
+ get markers() {
160
+ return this.knownChildren.filter((child) => child.isMapMarker === true);
161
+ }
162
+ markerOf(event) {
163
+ return event
164
+ .composedPath()
165
+ .find((one) => one.isMapMarker === true);
166
+ }
167
+ activate(marker) {
168
+ if (marker.gathered.length > 1) {
169
+ marker.dispatchEvent(new HelixMapClusterEvent({ labels: [...marker.gathered] }));
170
+ return;
171
+ }
172
+ this.dispatchEvent(new HelixActivateEvent({ value: marker.name || marker.label || '' }));
173
+ }
174
+ connectedCallback() {
175
+ super.connectedCallback();
176
+ this.addEventListener(HelixMapPartChangeEvent.eventName, this.handlePartChange);
177
+ this.addEventListener('click', this.handleClick);
178
+ this.addEventListener('focusin', this.handleFocusIn);
179
+ this.addEventListener('keydown', this.handleMarkerKeydown);
180
+ this.addEventListener('keyup', this.handleMarkerKeyup);
181
+ this.hiding = new MutationObserver(() => this.requestUpdate());
182
+ this.hiding.observe(this, { attributeFilter: ['hidden'], attributes: true, subtree: true });
183
+ }
184
+ disconnectedCallback() {
185
+ super.disconnectedCallback();
186
+ this.hiding?.disconnect();
187
+ document.removeEventListener('pointerdown', this.handleOutside);
188
+ this.removeEventListener(HelixMapPartChangeEvent.eventName, this.handlePartChange);
189
+ this.removeEventListener('click', this.handleClick);
190
+ this.removeEventListener('focusin', this.handleFocusIn);
191
+ this.removeEventListener('keydown', this.handleMarkerKeydown);
192
+ this.removeEventListener('keyup', this.handleMarkerKeyup);
193
+ }
194
+ updated(changed) {
195
+ super.updated(changed);
196
+ const photographed = this.mapType !== undefined;
197
+ for (const layer of this.imagery) {
198
+ if (layer.hidden === photographed) {
199
+ layer.hidden = !photographed;
200
+ }
201
+ }
202
+ if (changed.has('mapType')) {
203
+ this.dispatchEvent(new HelixMapPartChangeEvent({ kind: 'layer' }));
204
+ }
205
+ if (changed.has('layersOpen')) {
206
+ if (this.layersOpen) {
207
+ document.addEventListener('pointerdown', this.handleOutside);
208
+ }
209
+ else {
210
+ document.removeEventListener('pointerdown', this.handleOutside);
211
+ }
212
+ }
213
+ const markers = this.markers;
214
+ const on = markers[Math.min(this.stop, markers.length - 1)];
215
+ const stop = on?.gatheredBy ?? on;
216
+ for (const marker of markers) {
217
+ marker.current = marker === stop;
218
+ }
219
+ if (changed.has('cluster')) {
220
+ this.dispatchEvent(new HelixMapPartChangeEvent({ kind: 'place' }));
221
+ }
222
+ }
223
+ rotate(by) {
224
+ this.dispatchEvent(new HelixMapRotateEvent({ by }));
225
+ }
226
+ faceNorth() {
227
+ this.dispatchEvent(new HelixMapRotateEvent({ to: 0 }));
228
+ }
229
+ tilt(by) {
230
+ this.dispatchEvent(new HelixMapTiltEvent({ by }));
231
+ }
232
+ tiltTo(to) {
233
+ this.dispatchEvent(new HelixMapTiltEvent({ to }));
234
+ }
235
+ renderLayersMenu() {
236
+ const layers = this.layers;
237
+ if (!this.drawsControls('layers') || layers.length === 0) {
238
+ return nothing;
239
+ }
240
+ const entries = this.layerEntries;
241
+ return html `<button
242
+ part="layers-toggle"
243
+ aria-controls="layers-menu"
244
+ aria-expanded=${this.layersOpen ? 'true' : 'false'}
245
+ aria-label=${this.layersLabel}
246
+ type="button"
247
+ @click=${() => {
248
+ this.layersOpen = !this.layersOpen;
249
+ if (this.layersOpen) {
250
+ this.dispatchEvent(new HelixMapPartChangeEvent({ kind: 'menu' }));
251
+ }
252
+ }}
253
+ @keydown=${this.handleMenuKeydown}
254
+ >
255
+ <svg aria-hidden="true" fill="currentColor" height="16" viewBox=${stack.viewBox} width="16">
256
+ ${unsafeSVG(stack.content)}
257
+ </svg>
258
+ </button>
259
+ <div
260
+ part="layers-menu"
261
+ id="layers-menu"
262
+ role="group"
263
+ aria-label=${this.layersLabel}
264
+ ?hidden=${!this.layersOpen}
265
+ @keydown=${this.handleMenuKeydown}
266
+ >
267
+ ${this.imagery.length > 0 ? this.renderMapTypes() : nothing}
268
+ ${entries.length > 0
269
+ ? html `<div part="layer-choices">
270
+ ${entries.map((entry) => {
271
+ const shown = entry.some((layer) => !layer.hidden);
272
+ return html `<button
273
+ part="layer-choice"
274
+ aria-pressed=${shown ? 'true' : 'false'}
275
+ type="button"
276
+ @click=${() => this.showLayers(entry, !shown)}
277
+ >
278
+ <svg part="layer-icon" aria-hidden="true" fill="currentColor" viewBox=${iconOf(entry[0]).viewBox}>
279
+ ${unsafeSVG(iconOf(entry[0]).content)}
280
+ </svg>
281
+ <span part="layer-name">${entry[0]?.label ?? ''}</span>
282
+ </button>`;
283
+ })}
284
+ </div>`
285
+ : nothing}
286
+ </div>`;
287
+ }
288
+ renderMapTypes() {
289
+ const chosen = this.mapType ?? 'standard';
290
+ return html `<div part="map-types" role="radiogroup" aria-label=${this.mapTypeLabel}>
291
+ ${MAP_TYPES.map((type) => {
292
+ const preview = this.mapPreviews[type];
293
+ return html `<label part="map-type">
294
+ <input
295
+ type="radio"
296
+ name="map-type"
297
+ .checked=${chosen === type}
298
+ @change=${() => {
299
+ this.mapType = type === 'standard' ? undefined : type;
300
+ }}
301
+ />
302
+ ${preview
303
+ ? html `<img part="map-type-preview" alt="" src=${preview} />`
304
+ : html `<span part="map-type-preview"></span>`}
305
+ <span part="map-type-name">${this.nameOf(type)}</span>
306
+ </label>`;
307
+ })}
308
+ </div>`;
309
+ }
310
+ renderCompass(pan, rotate) {
311
+ return html `<div part="compass">
312
+ ${rotate
313
+ ? html `<button part="rotate-left" aria-label=${this.rotateLeftLabel} type="button" @click=${() => this.rotate(-ROTATE_STEP)}>
314
+ ↺
315
+ </button>`
316
+ : nothing}
317
+ ${pan
318
+ ? html `<button part="pan-up" aria-label=${this.panLabel('up')} type="button" @click=${() => this.pan('up')}>
319
+ ${this.panGlyph('up')}
320
+ </button>`
321
+ : nothing}
322
+ ${rotate
323
+ ? html `<button part="rotate-right" aria-label=${this.rotateRightLabel} type="button" @click=${() => this.rotate(ROTATE_STEP)}>
324
+ ↻
325
+ </button>`
326
+ : nothing}
327
+ ${pan
328
+ ? html `<button part="pan-start" aria-label=${this.panLabel('start')} type="button" @click=${() => this.pan('start')}>
329
+ ${this.panGlyph('start')}
330
+ </button>`
331
+ : nothing}
332
+ <button part="face-north" aria-label=${this.faceNorthLabel} type="button" style=${`--map-needle-turn: ${-this.bearing}deg`} @click=${() => this.faceNorth()}>
333
+ <svg part="needle" aria-hidden="true" viewBox="0 0 16 16">
334
+ <path data-end="north" d="M8 1.5 10.5 8h-5Z" />
335
+ <path data-end="south" d="M8 14.5 5.5 8h5Z" />
336
+ </svg>
337
+ </button>
338
+ ${pan
339
+ ? html `<button part="pan-end" aria-label=${this.panLabel('end')} type="button" @click=${() => this.pan('end')}>
340
+ ${this.panGlyph('end')}
341
+ </button>`
342
+ : nothing}
343
+ ${pan
344
+ ? html `<button part="pan-down" aria-label=${this.panLabel('down')} type="button" @click=${() => this.pan('down')}>
345
+ ${this.panGlyph('down')}
346
+ </button>`
347
+ : nothing}
348
+ </div>`;
349
+ }
350
+ renderControls() {
351
+ const pan = this.drawsControls('pan');
352
+ const zoom = this.drawsControls('zoom');
353
+ const rotate = this.drawsControls('rotate');
354
+ const tilt = this.drawsControls('tilt');
355
+ const fullscreen = this.drawsControls('fullscreen');
356
+ const menu = this.renderLayersMenu();
357
+ if (!pan && !zoom && !rotate && !tilt && !fullscreen && menu === nothing) {
358
+ return nothing;
359
+ }
360
+ return html `<div part="controls" role="group" aria-label=${this.controlsLabel ?? this.restingControlsLabel}>
361
+ ${fullscreen
362
+ ? html `<button
363
+ part="fullscreen"
364
+ aria-label=${this.fullscreenLabel}
365
+ aria-pressed=${this.filled ? 'true' : 'false'}
366
+ type="button"
367
+ @click=${this.toggleFullscreen}
368
+ >
369
+ ⤢
370
+ </button>`
371
+ : nothing}
372
+ ${menu}
373
+ ${tilt
374
+ ? html `<button
375
+ part="tilt"
376
+ aria-label=${this.tiltLabel}
377
+ aria-pressed=${this.pitch > 0 ? 'true' : 'false'}
378
+ type="button"
379
+ @click=${() => this.tiltTo(this.pitch > 0 ? 0 : TILTED)}
380
+ >
381
+ 3D
382
+ </button>`
383
+ : nothing}
384
+ ${zoom
385
+ ? html `<button part="zoom-in" aria-label=${this.zoomInLabel} type="button" @click=${() => this.zoom(1)}>
386
+ +
387
+ </button>
388
+ <button part="zoom-out" aria-label=${this.zoomOutLabel} type="button" @click=${() => this.zoom(-1)}>
389
+ −
390
+ </button>`
391
+ : nothing}
392
+ ${this.drawsCompass ? this.renderCompass(pan, rotate) : nothing}
393
+ </div>`;
394
+ }
395
+ get restingControls() {
396
+ return 'zoom compass fullscreen';
397
+ }
398
+ get drawsCompass() {
399
+ return ['compass', 'pan', 'rotate'].some((group) => this.drawsControls(group));
400
+ }
401
+ get keyShortcuts() {
402
+ const keys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Plus', 'Minus'];
403
+ if (this.drawsCompass) {
404
+ keys.push('Shift+ArrowLeft', 'Shift+ArrowRight');
405
+ }
406
+ if (this.drawsControls('tilt')) {
407
+ keys.push('Shift+ArrowUp', 'Shift+ArrowDown');
408
+ }
409
+ return keys.join(' ');
410
+ }
411
+ handleMoreKeys(event) {
412
+ if (!event.shiftKey) {
413
+ return false;
414
+ }
415
+ if (this.drawsCompass && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
416
+ this.rotate(event.key === 'ArrowLeft' ? -ROTATE_STEP : ROTATE_STEP);
417
+ return true;
418
+ }
419
+ if (this.drawsControls('tilt') && (event.key === 'ArrowUp' || event.key === 'ArrowDown')) {
420
+ this.tilt(event.key === 'ArrowUp' ? TILT_STEP : -TILT_STEP);
421
+ return true;
422
+ }
423
+ return false;
14
424
  }
15
425
  panEvent(detail) {
16
426
  return new HelixMapPanEvent(detail);
@@ -27,12 +437,21 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
27
437
  this.toggleState('fullscreen', this.filled);
28
438
  this.toggleState('idle', this.resting);
29
439
  }
440
+ renderLayers() {
441
+ const layers = this.layers.filter((layer) => !layer.hidden);
442
+ if (layers.length === 0) {
443
+ return nothing;
444
+ }
445
+ return html `<ul part="layers" aria-label=${this.layersLabel}>
446
+ ${layers.map((layer) => html `<li part="layer">${layer.label ?? ''}${layer.description ? `, ${layer.description}` : ''}</li>`)}
447
+ </ul>`;
448
+ }
30
449
  render() {
31
450
  return html `<div
32
451
  part="region"
33
452
  role="group"
34
453
  aria-label=${this.label || 'Map'}
35
- aria-keyshortcuts="ArrowUp ArrowDown ArrowLeft ArrowRight Plus Minus"
454
+ aria-keyshortcuts=${this.keyShortcuts}
36
455
  tabindex="0"
37
456
  @focusin=${this.idle.stir}
38
457
  @keydown=${this.handleKeydown}
@@ -43,8 +462,9 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
43
462
  <slot name="header"></slot>
44
463
  </div>
45
464
  <div part="viewport">
46
- <slot></slot>
465
+ <slot @slotchange=${this.handleSlotChange}></slot>
47
466
  </div>
467
+ ${this.renderLayers()}
48
468
  ${this.renderControls()}
49
469
  <div part="footer">
50
470
  <slot name="footer"></slot>
@@ -55,9 +475,57 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
55
475
  </div>`;
56
476
  }
57
477
  };
478
+ __decorate([
479
+ property({ type: Number })
480
+ ], HelixHeadlessMap.prototype, "bearing", void 0);
481
+ __decorate([
482
+ property({ type: Boolean })
483
+ ], HelixHeadlessMap.prototype, "cluster", void 0);
484
+ __decorate([
485
+ property({ attribute: 'face-north-label' })
486
+ ], HelixHeadlessMap.prototype, "faceNorthLabel", void 0);
487
+ __decorate([
488
+ property({ attribute: 'hybrid-label' })
489
+ ], HelixHeadlessMap.prototype, "hybridLabel", void 0);
58
490
  __decorate([
59
491
  property()
60
492
  ], HelixHeadlessMap.prototype, "label", void 0);
493
+ __decorate([
494
+ property({ attribute: 'layers-label' })
495
+ ], HelixHeadlessMap.prototype, "layersLabel", void 0);
496
+ __decorate([
497
+ state()
498
+ ], HelixHeadlessMap.prototype, "mapPreviews", void 0);
499
+ __decorate([
500
+ property({ attribute: 'map-type', reflect: true })
501
+ ], HelixHeadlessMap.prototype, "mapType", void 0);
502
+ __decorate([
503
+ property({ attribute: 'map-type-label' })
504
+ ], HelixHeadlessMap.prototype, "mapTypeLabel", void 0);
505
+ __decorate([
506
+ property({ type: Number })
507
+ ], HelixHeadlessMap.prototype, "pitch", void 0);
508
+ __decorate([
509
+ property({ attribute: 'rotate-left-label' })
510
+ ], HelixHeadlessMap.prototype, "rotateLeftLabel", void 0);
511
+ __decorate([
512
+ property({ attribute: 'rotate-right-label' })
513
+ ], HelixHeadlessMap.prototype, "rotateRightLabel", void 0);
514
+ __decorate([
515
+ property({ attribute: 'satellite-label' })
516
+ ], HelixHeadlessMap.prototype, "satelliteLabel", void 0);
517
+ __decorate([
518
+ property({ attribute: 'standard-label' })
519
+ ], HelixHeadlessMap.prototype, "standardLabel", void 0);
520
+ __decorate([
521
+ property({ attribute: 'tilt-label' })
522
+ ], HelixHeadlessMap.prototype, "tiltLabel", void 0);
523
+ __decorate([
524
+ state()
525
+ ], HelixHeadlessMap.prototype, "layersOpen", void 0);
526
+ __decorate([
527
+ state()
528
+ ], HelixHeadlessMap.prototype, "stop", void 0);
61
529
  HelixHeadlessMap = __decorate([
62
530
  customElement('helix-headless-map')
63
531
  ], HelixHeadlessMap);
@@ -0,0 +1,73 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { nothing, type PropertyValues } from 'lit';
3
+ /** A place a line passes through or a shape is drawn around, in degrees. */
4
+ export interface MapPoint {
5
+ lat: number;
6
+ lng: number;
7
+ }
8
+ /**
9
+ * `points` as it is written: `lat,lng` pairs separated by spaces, the order a
10
+ * marker's own `lat` and `lng` read in. A pair that is not two numbers is
11
+ * dropped rather than drawn at the origin, **an empty side included**:
12
+ * `Number('')` is 0, so `30.1,` from a record with no longitude was a vertex
13
+ * off West Africa.
14
+ */
15
+ export declare const pointsOf: (value: string | null) => MapPoint[];
16
+ /** `points` read from its attribute, which is written and never reflected. */
17
+ export declare const pointsConverter: {
18
+ fromAttribute: typeof pointsOf;
19
+ };
20
+ /**
21
+ * @summary A line on a map, such as a route between stops.
22
+ *
23
+ * @description
24
+ * `connectMap` draws the route as a layer of the map, under its place names,
25
+ * and redraws it when its points, progress, or color change. The frame lists
26
+ * each route by its `label` and `description` for screen readers.
27
+ *
28
+ * @since 0.105.0
29
+ * @status experimental
30
+ * @category media
31
+ * @aliases Map line, Path, Polyline
32
+ * @documentation https://fusion.dev/helix/components/map
33
+ *
34
+ * @event {HelixMapPartChangeEvent} helix-map-part-change - The route's points, progress, name, or color changed. Used by `connectMap`; pages do not need to listen for it.
35
+ */
36
+ export declare class HelixHeadlessMapRoute extends HelixElement {
37
+ /**
38
+ * More about the route, such as its distance or time. Read after its label
39
+ * in the frame's list.
40
+ */
41
+ description?: string;
42
+ /**
43
+ * A name this layer shares with other layers, such as a route and its
44
+ * dotted legs. The layers menu shows layers in the same group as one entry,
45
+ * named by the first of them, and its checkbox shows or hides them all.
46
+ */
47
+ group?: string;
48
+ /** Marks this as a layer for the frame and the connector, whatever its tag. */
49
+ readonly isMapLayer = true;
50
+ /** @internal Which kind of layer, which is how the connector draws it. */
51
+ readonly layerKind: 'area' | 'route';
52
+ /** The route's name, used in the frame's list. */
53
+ label?: string;
54
+ /**
55
+ * The points the line passes through, in order. Write the attribute as
56
+ * `lat,lng` pairs separated by spaces, or set the property to an array of `{
57
+ * lat, lng }`.
58
+ */
59
+ points?: MapPoint[];
60
+ /**
61
+ * How much of the route has been traveled, from 0 to 1. The traveled part is
62
+ * drawn in a lighter color.
63
+ */
64
+ progress?: number;
65
+ /** Tells the frame and the connector the line moved, was renamed or was retoned. */
66
+ protected updated(changed: PropertyValues): void;
67
+ protected render(): typeof nothing;
68
+ }
69
+ declare global {
70
+ interface HTMLElementTagNameMap {
71
+ 'helix-headless-map-route': HelixHeadlessMapRoute;
72
+ }
73
+ }
@@ -0,0 +1,52 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { customElement, HelixElement, HelixMapPartChangeEvent } from '@fusion.dev/helix-core';
8
+ import { nothing } from 'lit';
9
+ import { property } from 'lit/decorators.js';
10
+ export const pointsOf = (value) => (value ?? '')
11
+ .trim()
12
+ .split(/\s+/)
13
+ .filter(Boolean)
14
+ .map((pair) => pair.split(','))
15
+ .filter((pair) => pair.length === 2 && pair.every((one) => one.trim() !== ''))
16
+ .map((pair) => pair.map(Number))
17
+ .filter((pair) => pair.every((one) => Number.isFinite(one)))
18
+ .map(([lat, lng]) => ({ lat: lat, lng: lng }));
19
+ export const pointsConverter = { fromAttribute: pointsOf };
20
+ let HelixHeadlessMapRoute = class HelixHeadlessMapRoute extends HelixElement {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.isMapLayer = true;
24
+ this.layerKind = 'route';
25
+ }
26
+ updated(changed) {
27
+ super.updated(changed);
28
+ this.dispatchEvent(new HelixMapPartChangeEvent({ kind: 'layer' }));
29
+ }
30
+ render() {
31
+ return nothing;
32
+ }
33
+ };
34
+ __decorate([
35
+ property()
36
+ ], HelixHeadlessMapRoute.prototype, "description", void 0);
37
+ __decorate([
38
+ property()
39
+ ], HelixHeadlessMapRoute.prototype, "group", void 0);
40
+ __decorate([
41
+ property()
42
+ ], HelixHeadlessMapRoute.prototype, "label", void 0);
43
+ __decorate([
44
+ property({ converter: pointsConverter })
45
+ ], HelixHeadlessMapRoute.prototype, "points", void 0);
46
+ __decorate([
47
+ property({ type: Number })
48
+ ], HelixHeadlessMapRoute.prototype, "progress", void 0);
49
+ HelixHeadlessMapRoute = __decorate([
50
+ customElement('helix-headless-map-route')
51
+ ], HelixHeadlessMapRoute);
52
+ export { HelixHeadlessMapRoute };