@fusion.dev/helix-headless 0.102.0 → 0.103.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.
@@ -55506,6 +55506,26 @@
55506
55506
  }
55507
55507
  ],
55508
55508
  "members": [
55509
+ {
55510
+ "kind": "field",
55511
+ "name": "controls",
55512
+ "type": {
55513
+ "text": "string | undefined"
55514
+ },
55515
+ "description": "Which of the controls this frame draws, as a list of what to keep.\n\n**A list rather than a switch, because the overlap is not symmetric.**\nEvery map library draws zoom controls and none of them draws pan buttons:\nMapLibre's navigation control is zoom and a compass, Leaflet's is zoom,\nGoogle's is zoom. So a consumer slotting one of those has our zoom twice\nover and our pan pad not at all, and a single switch would make them choose\nbetween a duplicate and nothing.\n\n`none` draws none of them, which is the right answer where the slotted map\nalready has controls a reader can find: what this family argues for is that\n*a* findable control exists, never that it is ours. The methods stay public\neither way, so a consumer drawing their own chrome can still drive the\nframe.\n\nAbsent is all three, which is this library's rule that a resting value is an\nattribute's absence, and it is what the stylesheet asks about: the layout\nrules are written against `[controls]:not([controls~=\"zoom\"])` rather than\nthe bare negation, or a frame that named no list at all would be laid out as\nthough it had dropped everything.",
55516
+ "attribute": "controls",
55517
+ "reflects": true
55518
+ },
55519
+ {
55520
+ "kind": "field",
55521
+ "name": "controlsPlacement",
55522
+ "type": {
55523
+ "text": "'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | undefined"
55524
+ },
55525
+ "description": "Which corner the controls sit in.\n\n**Configurable because there is no safe default**, and this is a collision\nrather than a taste: MapLibre puts its own controls at the top end, Leaflet\nand OpenLayers at the top start, Google's zoom at the bottom end. Whichever\ncorner this took would land on somebody's, which is why every one of those\nlibraries made it a setting too.\n\nLogical corners rather than left and right, so a right-to-left page moves\nthem without a consumer writing a second rule.\n\nAbsent is the top end, which is this library's rule that a resting value is\nan attribute's absence: a default that reflected would write an attribute\nonto every host that no server sent and nobody asked for.",
55526
+ "attribute": "controls-placement",
55527
+ "reflects": true
55528
+ },
55509
55529
  {
55510
55530
  "kind": "field",
55511
55531
  "name": "fullscreenLabel",
@@ -55610,6 +55630,25 @@
55610
55630
  "readonly": true,
55611
55631
  "default": "new IdleController( this, () => this.idleDelay, (resting) => { this.resting = resting }, )"
55612
55632
  },
55633
+ {
55634
+ "kind": "method",
55635
+ "name": "draws",
55636
+ "privacy": "private",
55637
+ "return": {
55638
+ "type": {
55639
+ "text": "boolean"
55640
+ }
55641
+ },
55642
+ "parameters": [
55643
+ {
55644
+ "name": "group",
55645
+ "type": {
55646
+ "text": "'pan' | 'zoom' | 'fullscreen'"
55647
+ }
55648
+ }
55649
+ ],
55650
+ "description": "Whether a named group of controls is one this frame draws."
55651
+ },
55613
55652
  {
55614
55653
  "kind": "method",
55615
55654
  "name": "toggleFullscreen",
@@ -55691,6 +55730,17 @@
55691
55730
  ],
55692
55731
  "description": "What one pan button is called, with the way it goes filled in.\n\nLeft and right rather than start and end, because the label is read aloud\nand a reader knows which way left is. The event keeps the reading-order\nname, which is the one a right-to-left page needs."
55693
55732
  },
55733
+ {
55734
+ "kind": "method",
55735
+ "name": "renderControls",
55736
+ "privacy": "private",
55737
+ "return": {
55738
+ "type": {
55739
+ "text": "TemplateResult | typeof nothing"
55740
+ }
55741
+ },
55742
+ "description": "The controls, or nothing where a consumer kept none of them.\n\n**Written out rather than generated**, because a part name behind an\ninterpolation is a part the manifest cannot see: it would publish `pan-`\nand a consumer's `::part(pan-up)` would match nothing. The same cost\n`drawIcon` names for `Trend`."
55743
+ },
55694
55744
  {
55695
55745
  "kind": "field",
55696
55746
  "name": "tier",
@@ -56035,6 +56085,22 @@
56035
56085
  }
56036
56086
  ],
56037
56087
  "attributes": [
56088
+ {
56089
+ "name": "controls",
56090
+ "type": {
56091
+ "text": "string | undefined"
56092
+ },
56093
+ "description": "Which of the controls this frame draws, as a list of what to keep.\n\n**A list rather than a switch, because the overlap is not symmetric.**\nEvery map library draws zoom controls and none of them draws pan buttons:\nMapLibre's navigation control is zoom and a compass, Leaflet's is zoom,\nGoogle's is zoom. So a consumer slotting one of those has our zoom twice\nover and our pan pad not at all, and a single switch would make them choose\nbetween a duplicate and nothing.\n\n`none` draws none of them, which is the right answer where the slotted map\nalready has controls a reader can find: what this family argues for is that\n*a* findable control exists, never that it is ours. The methods stay public\neither way, so a consumer drawing their own chrome can still drive the\nframe.\n\nAbsent is all three, which is this library's rule that a resting value is an\nattribute's absence, and it is what the stylesheet asks about: the layout\nrules are written against `[controls]:not([controls~=\"zoom\"])` rather than\nthe bare negation, or a frame that named no list at all would be laid out as\nthough it had dropped everything.",
56094
+ "fieldName": "controls"
56095
+ },
56096
+ {
56097
+ "name": "controls-placement",
56098
+ "type": {
56099
+ "text": "'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | undefined"
56100
+ },
56101
+ "description": "Which corner the controls sit in.\n\n**Configurable because there is no safe default**, and this is a collision\nrather than a taste: MapLibre puts its own controls at the top end, Leaflet\nand OpenLayers at the top start, Google's zoom at the bottom end. Whichever\ncorner this took would land on somebody's, which is why every one of those\nlibraries made it a setting too.\n\nLogical corners rather than left and right, so a right-to-left page moves\nthem without a consumer writing a second rule.\n\nAbsent is the top end, which is this library's rule that a resting value is\nan attribute's absence: a default that reflected would write an attribute\nonto every host that no server sent and nobody asked for.",
56102
+ "fieldName": "controlsPlacement"
56103
+ },
56038
56104
  {
56039
56105
  "name": "fullscreen-label",
56040
56106
  "type": {
@@ -2,6 +2,8 @@ import { HelixElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  /** Which way a press or a key asked the map to go. */
4
4
  type Way = 'up' | 'down' | 'start' | 'end';
5
+ /** Where the controls sit, in the corner words the rest of the library uses. */
6
+ export type MapControlsPlacement = 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
5
7
  /**
6
8
  * @summary A frame around somebody else's map.
7
9
  *
@@ -59,6 +61,46 @@ type Way = 'up' | 'down' | 'start' | 'end';
59
61
  * @cssstate idle - While the reader has been still long enough to hide the chrome.
60
62
  */
61
63
  export declare class HelixHeadlessMap extends HelixElement {
64
+ /**
65
+ * Which of the controls this frame draws, as a list of what to keep.
66
+ *
67
+ * **A list rather than a switch, because the overlap is not symmetric.**
68
+ * Every map library draws zoom controls and none of them draws pan buttons:
69
+ * MapLibre's navigation control is zoom and a compass, Leaflet's is zoom,
70
+ * Google's is zoom. So a consumer slotting one of those has our zoom twice
71
+ * over and our pan pad not at all, and a single switch would make them choose
72
+ * between a duplicate and nothing.
73
+ *
74
+ * `none` draws none of them, which is the right answer where the slotted map
75
+ * already has controls a reader can find: what this family argues for is that
76
+ * *a* findable control exists, never that it is ours. The methods stay public
77
+ * either way, so a consumer drawing their own chrome can still drive the
78
+ * frame.
79
+ *
80
+ * Absent is all three, which is this library's rule that a resting value is an
81
+ * attribute's absence, and it is what the stylesheet asks about: the layout
82
+ * rules are written against `[controls]:not([controls~="zoom"])` rather than
83
+ * the bare negation, or a frame that named no list at all would be laid out as
84
+ * though it had dropped everything.
85
+ */
86
+ controls?: string;
87
+ /**
88
+ * Which corner the controls sit in.
89
+ *
90
+ * **Configurable because there is no safe default**, and this is a collision
91
+ * rather than a taste: MapLibre puts its own controls at the top end, Leaflet
92
+ * and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
93
+ * corner this took would land on somebody's, which is why every one of those
94
+ * libraries made it a setting too.
95
+ *
96
+ * Logical corners rather than left and right, so a right-to-left page moves
97
+ * them without a consumer writing a second rule.
98
+ *
99
+ * Absent is the top end, which is this library's rule that a resting value is
100
+ * an attribute's absence: a default that reflected would write an attribute
101
+ * onto every host that no server sent and nobody asked for.
102
+ */
103
+ controlsPlacement?: MapControlsPlacement;
62
104
  /** What the control that fills the screen is called. */
63
105
  fullscreenLabel: string;
64
106
  /**
@@ -100,6 +142,8 @@ export declare class HelixHeadlessMap extends HelixElement {
100
142
  private filled;
101
143
  private readonly fullscreen;
102
144
  private readonly idle;
145
+ /** Whether a named group of controls is one this frame draws. */
146
+ private draws;
103
147
  /** Fills the screen with the frame, or comes back. */
104
148
  toggleFullscreen(): void;
105
149
  /** Asks for the map to move, which the consumer answers. */
@@ -132,6 +176,15 @@ export declare class HelixHeadlessMap extends HelixElement {
132
176
  * name, which is the one a right-to-left page needs.
133
177
  */
134
178
  private named;
179
+ /**
180
+ * The controls, or nothing where a consumer kept none of them.
181
+ *
182
+ * **Written out rather than generated**, because a part name behind an
183
+ * interpolation is a part the manifest cannot see: it would publish `pan-`
184
+ * and a consumer's `::part(pan-up)` would match nothing. The same cost
185
+ * `drawIcon` names for `Trend`.
186
+ */
187
+ private renderControls;
135
188
  protected render(): TemplateResult;
136
189
  }
137
190
  declare global {
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { customElement, FullscreenController, HelixElement, HelixMapPanEvent, HelixMapZoomEvent, hostAria, IdleController, inlineStep, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
8
+ import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  let HelixHeadlessMap = class HelixHeadlessMap extends HelixElement {
11
11
  constructor() {
@@ -53,6 +53,9 @@ let HelixHeadlessMap = class HelixHeadlessMap extends HelixElement {
53
53
  }
54
54
  };
55
55
  }
56
+ draws(group) {
57
+ return this.controls === undefined ? true : this.controls.split(/\s+/).includes(group);
58
+ }
56
59
  toggleFullscreen() {
57
60
  void this.fullscreen.toggle();
58
61
  }
@@ -76,6 +79,49 @@ let HelixHeadlessMap = class HelixHeadlessMap extends HelixElement {
76
79
  const spoken = way === 'start' ? 'left' : way === 'end' ? 'right' : way;
77
80
  return this.panLabel.replace('{way}', spoken);
78
81
  }
82
+ renderControls() {
83
+ const pan = this.draws('pan');
84
+ const zoom = this.draws('zoom');
85
+ const fullscreen = this.draws('fullscreen');
86
+ if (!pan && !zoom && !fullscreen) {
87
+ return nothing;
88
+ }
89
+ return html `<div part="controls" role="group" aria-label="Map controls">
90
+ ${pan
91
+ ? html `<button part="pan-up" aria-label=${this.named('up')} type="button" @click=${() => this.pan('up')}>
92
+ ↑
93
+ </button>
94
+ <button part="pan-start" aria-label=${this.named('start')} type="button" @click=${() => this.pan('start')}>
95
+ ←
96
+ </button>
97
+ <button part="pan-end" aria-label=${this.named('end')} type="button" @click=${() => this.pan('end')}>
98
+ →
99
+ </button>
100
+ <button part="pan-down" aria-label=${this.named('down')} type="button" @click=${() => this.pan('down')}>
101
+ ↓
102
+ </button>`
103
+ : nothing}
104
+ ${zoom
105
+ ? html `<button part="zoom-in" aria-label=${this.zoomInLabel} type="button" @click=${() => this.zoom(1)}>
106
+ +
107
+ </button>
108
+ <button part="zoom-out" aria-label=${this.zoomOutLabel} type="button" @click=${() => this.zoom(-1)}>
109
+ −
110
+ </button>`
111
+ : nothing}
112
+ ${fullscreen
113
+ ? html `<button
114
+ part="fullscreen"
115
+ aria-label=${this.fullscreenLabel}
116
+ aria-pressed=${this.filled ? 'true' : 'false'}
117
+ type="button"
118
+ @click=${this.toggleFullscreen}
119
+ >
120
+ ⤢
121
+ </button>`
122
+ : nothing}
123
+ </div>`;
124
+ }
79
125
  render() {
80
126
  return html `<div
81
127
  part="region"
@@ -90,44 +136,18 @@ let HelixHeadlessMap = class HelixHeadlessMap extends HelixElement {
90
136
  <div part="viewport">
91
137
  <slot></slot>
92
138
  </div>
93
- <div part="controls" role="group" aria-label="Map controls">
94
- <!-- **Written out rather than generated in a loop**, because a part name
95
- behind an interpolation is a part the manifest cannot see: it would
96
- publish \`pan-\` and a consumer's \`::part(pan-up)\` would match
97
- nothing. The same cost \`drawIcon\` names for \`Trend\`. -->
98
- <button part="pan-up" aria-label=${this.named('up')} type="button" @click=${() => this.pan('up')}>
99
- ↑
100
- </button>
101
- <button part="pan-start" aria-label=${this.named('start')} type="button" @click=${() => this.pan('start')}>
102
- ←
103
- </button>
104
- <button part="pan-end" aria-label=${this.named('end')} type="button" @click=${() => this.pan('end')}>
105
- →
106
- </button>
107
- <button part="pan-down" aria-label=${this.named('down')} type="button" @click=${() => this.pan('down')}>
108
- ↓
109
- </button>
110
- <button part="zoom-in" aria-label=${this.zoomInLabel} type="button" @click=${() => this.zoom(1)}>
111
- +
112
- </button>
113
- <button part="zoom-out" aria-label=${this.zoomOutLabel} type="button" @click=${() => this.zoom(-1)}>
114
- −
115
- </button>
116
- <button
117
- part="fullscreen"
118
- aria-label=${this.fullscreenLabel}
119
- aria-pressed=${this.filled ? 'true' : 'false'}
120
- type="button"
121
- @click=${this.toggleFullscreen}
122
- >
123
- ⤢
124
- </button>
125
- </div>
139
+ ${this.renderControls()}
126
140
  <slot name="footer"></slot>
127
141
  <slot name="attribution"></slot>
128
142
  </div>`;
129
143
  }
130
144
  };
145
+ __decorate([
146
+ property({ reflect: true })
147
+ ], HelixHeadlessMap.prototype, "controls", void 0);
148
+ __decorate([
149
+ property({ attribute: 'controls-placement', reflect: true })
150
+ ], HelixHeadlessMap.prototype, "controlsPlacement", void 0);
131
151
  __decorate([
132
152
  property({ attribute: 'fullscreen-label' })
133
153
  ], HelixHeadlessMap.prototype, "fullscreenLabel", void 0);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-headless",
3
3
  "description": "Headless component tier. Structure, accessibility, state, and form participation. No styling.",
4
- "version": "0.102.0",
4
+ "version": "0.103.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -46,13 +46,13 @@
46
46
  },
47
47
  "bugs": "https://github.com/project-fusion/fusion/issues",
48
48
  "dependencies": {
49
- "@fusion.dev/helix-core": "0.102.0",
50
- "@fusion.dev/helix-icons": "0.102.0",
49
+ "@fusion.dev/helix-core": "0.103.0",
50
+ "@fusion.dev/helix-icons": "0.103.0",
51
51
  "uqr": "^0.1.3"
52
52
  },
53
53
  "devDependencies": {
54
- "@fusion.dev/helix-cem": "0.102.0",
55
- "@fusion.dev/helix-testing": "0.102.0",
54
+ "@fusion.dev/helix-cem": "0.103.0",
55
+ "@fusion.dev/helix-testing": "0.103.0",
56
56
  "@fusion.dev/scripts": "0.0.0-dev",
57
57
  "@happy-dom/global-registrator": "^20.0.0",
58
58
  "@lit-labs/ssr": "^4.1.0",
package/types/jsx.d.ts CHANGED
@@ -9396,6 +9396,57 @@ export type HelixHeadlessMapHelixMapZoomElementEvent =
9396
9396
  HelixHeadlessMapElementEvent<HelixMapZoomEvent>;
9397
9397
 
9398
9398
  export type HelixHeadlessMapProps = {
9399
+ /** Which of the controls this frame draws, as a list of what to keep.
9400
+
9401
+ **A list rather than a switch, because the overlap is not symmetric.**
9402
+ Every map library draws zoom controls and none of them draws pan buttons:
9403
+ MapLibre's navigation control is zoom and a compass, Leaflet's is zoom,
9404
+ Google's is zoom. So a consumer slotting one of those has our zoom twice
9405
+ over and our pan pad not at all, and a single switch would make them choose
9406
+ between a duplicate and nothing.
9407
+
9408
+ `none` draws none of them, which is the right answer where the slotted map
9409
+ already has controls a reader can find: what this family argues for is that
9410
+ *a* findable control exists, never that it is ours. The methods stay public
9411
+ either way, so a consumer drawing their own chrome can still drive the
9412
+ frame.
9413
+
9414
+ Absent is all three, which is this library's rule that a resting value is an
9415
+ attribute's absence, and it is what the stylesheet asks about: the layout
9416
+ rules are written against `[controls]:not([controls~="zoom"])` rather than
9417
+ the bare negation, or a frame that named no list at all would be laid out as
9418
+ though it had dropped everything. */
9419
+ controls?: (HelixHeadlessMap["controls"]) | undefined;
9420
+ /** Which corner the controls sit in.
9421
+
9422
+ **Configurable because there is no safe default**, and this is a collision
9423
+ rather than a taste: MapLibre puts its own controls at the top end, Leaflet
9424
+ and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
9425
+ corner this took would land on somebody's, which is why every one of those
9426
+ libraries made it a setting too.
9427
+
9428
+ Logical corners rather than left and right, so a right-to-left page moves
9429
+ them without a consumer writing a second rule.
9430
+
9431
+ Absent is the top end, which is this library's rule that a resting value is
9432
+ an attribute's absence: a default that reflected would write an attribute
9433
+ onto every host that no server sent and nobody asked for. */
9434
+ "controls-placement"?: (HelixHeadlessMap["controlsPlacement"]) | undefined;
9435
+ /** Which corner the controls sit in.
9436
+
9437
+ **Configurable because there is no safe default**, and this is a collision
9438
+ rather than a taste: MapLibre puts its own controls at the top end, Leaflet
9439
+ and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
9440
+ corner this took would land on somebody's, which is why every one of those
9441
+ libraries made it a setting too.
9442
+
9443
+ Logical corners rather than left and right, so a right-to-left page moves
9444
+ them without a consumer writing a second rule.
9445
+
9446
+ Absent is the top end, which is this library's rule that a resting value is
9447
+ an attribute's absence: a default that reflected would write an attribute
9448
+ onto every host that no server sent and nobody asked for. */
9449
+ controlsPlacement?: (HelixHeadlessMap["controlsPlacement"]) | undefined;
9399
9450
  /** What the control that fills the screen is called. */
9400
9451
  "fullscreen-label"?: (HelixHeadlessMap["fullscreenLabel"]) | undefined;
9401
9452
  /** What the control that fills the screen is called. */
@@ -9452,6 +9503,57 @@ multiply by their own. */
9452
9503
  };
9453
9504
 
9454
9505
  export type HelixHeadlessMapSolidJsProps = {
9506
+ /** Which of the controls this frame draws, as a list of what to keep.
9507
+
9508
+ **A list rather than a switch, because the overlap is not symmetric.**
9509
+ Every map library draws zoom controls and none of them draws pan buttons:
9510
+ MapLibre's navigation control is zoom and a compass, Leaflet's is zoom,
9511
+ Google's is zoom. So a consumer slotting one of those has our zoom twice
9512
+ over and our pan pad not at all, and a single switch would make them choose
9513
+ between a duplicate and nothing.
9514
+
9515
+ `none` draws none of them, which is the right answer where the slotted map
9516
+ already has controls a reader can find: what this family argues for is that
9517
+ *a* findable control exists, never that it is ours. The methods stay public
9518
+ either way, so a consumer drawing their own chrome can still drive the
9519
+ frame.
9520
+
9521
+ Absent is all three, which is this library's rule that a resting value is an
9522
+ attribute's absence, and it is what the stylesheet asks about: the layout
9523
+ rules are written against `[controls]:not([controls~="zoom"])` rather than
9524
+ the bare negation, or a frame that named no list at all would be laid out as
9525
+ though it had dropped everything. */
9526
+ "prop:controls"?: (HelixHeadlessMap["controls"]) | undefined;
9527
+ /** Which corner the controls sit in.
9528
+
9529
+ **Configurable because there is no safe default**, and this is a collision
9530
+ rather than a taste: MapLibre puts its own controls at the top end, Leaflet
9531
+ and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
9532
+ corner this took would land on somebody's, which is why every one of those
9533
+ libraries made it a setting too.
9534
+
9535
+ Logical corners rather than left and right, so a right-to-left page moves
9536
+ them without a consumer writing a second rule.
9537
+
9538
+ Absent is the top end, which is this library's rule that a resting value is
9539
+ an attribute's absence: a default that reflected would write an attribute
9540
+ onto every host that no server sent and nobody asked for. */
9541
+ "attr:controls-placement"?: (HelixHeadlessMap["controlsPlacement"]) | undefined;
9542
+ /** Which corner the controls sit in.
9543
+
9544
+ **Configurable because there is no safe default**, and this is a collision
9545
+ rather than a taste: MapLibre puts its own controls at the top end, Leaflet
9546
+ and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
9547
+ corner this took would land on somebody's, which is why every one of those
9548
+ libraries made it a setting too.
9549
+
9550
+ Logical corners rather than left and right, so a right-to-left page moves
9551
+ them without a consumer writing a second rule.
9552
+
9553
+ Absent is the top end, which is this library's rule that a resting value is
9554
+ an attribute's absence: a default that reflected would write an attribute
9555
+ onto every host that no server sent and nobody asked for. */
9556
+ "prop:controlsPlacement"?: (HelixHeadlessMap["controlsPlacement"]) | undefined;
9455
9557
  /** What the control that fills the screen is called. */
9456
9558
  "attr:fullscreen-label"?: (HelixHeadlessMap["fullscreenLabel"]) | undefined;
9457
9559
  /** What the control that fills the screen is called. */
@@ -27514,6 +27616,40 @@ export type CustomElements = {
27514
27616
  *
27515
27617
  * Component attributes and properties that can be applied to the element or by using JavaScript.
27516
27618
  *
27619
+ * - `controls`: Which of the controls this frame draws, as a list of what to keep.
27620
+ *
27621
+ * **A list rather than a switch, because the overlap is not symmetric.**
27622
+ * Every map library draws zoom controls and none of them draws pan buttons:
27623
+ * MapLibre's navigation control is zoom and a compass, Leaflet's is zoom,
27624
+ * Google's is zoom. So a consumer slotting one of those has our zoom twice
27625
+ * over and our pan pad not at all, and a single switch would make them choose
27626
+ * between a duplicate and nothing.
27627
+ *
27628
+ * `none` draws none of them, which is the right answer where the slotted map
27629
+ * already has controls a reader can find: what this family argues for is that
27630
+ * *a* findable control exists, never that it is ours. The methods stay public
27631
+ * either way, so a consumer drawing their own chrome can still drive the
27632
+ * frame.
27633
+ *
27634
+ * Absent is all three, which is this library's rule that a resting value is an
27635
+ * attribute's absence, and it is what the stylesheet asks about: the layout
27636
+ * rules are written against `[controls]:not([controls~="zoom"])` rather than
27637
+ * the bare negation, or a frame that named no list at all would be laid out as
27638
+ * though it had dropped everything.
27639
+ * - `controls-placement`/`controlsPlacement`: Which corner the controls sit in.
27640
+ *
27641
+ * **Configurable because there is no safe default**, and this is a collision
27642
+ * rather than a taste: MapLibre puts its own controls at the top end, Leaflet
27643
+ * and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
27644
+ * corner this took would land on somebody's, which is why every one of those
27645
+ * libraries made it a setting too.
27646
+ *
27647
+ * Logical corners rather than left and right, so a right-to-left page moves
27648
+ * them without a consumer writing a second rule.
27649
+ *
27650
+ * Absent is the top end, which is this library's rule that a resting value is
27651
+ * an attribute's absence: a default that reflected would write an attribute
27652
+ * onto every host that no server sent and nobody asked for.
27517
27653
  * - `fullscreen-label`/`fullscreenLabel`: What the control that fills the screen is called.
27518
27654
  * - `idle-delay`/`idleDelay`: How long the reader may be still before the chrome hides, in milliseconds.
27519
27655
  *
@@ -45982,6 +46118,40 @@ export type CustomElementsSolidJs = {
45982
46118
  *
45983
46119
  * Component attributes and properties that can be applied to the element or by using JavaScript.
45984
46120
  *
46121
+ * - `controls`: Which of the controls this frame draws, as a list of what to keep.
46122
+ *
46123
+ * **A list rather than a switch, because the overlap is not symmetric.**
46124
+ * Every map library draws zoom controls and none of them draws pan buttons:
46125
+ * MapLibre's navigation control is zoom and a compass, Leaflet's is zoom,
46126
+ * Google's is zoom. So a consumer slotting one of those has our zoom twice
46127
+ * over and our pan pad not at all, and a single switch would make them choose
46128
+ * between a duplicate and nothing.
46129
+ *
46130
+ * `none` draws none of them, which is the right answer where the slotted map
46131
+ * already has controls a reader can find: what this family argues for is that
46132
+ * *a* findable control exists, never that it is ours. The methods stay public
46133
+ * either way, so a consumer drawing their own chrome can still drive the
46134
+ * frame.
46135
+ *
46136
+ * Absent is all three, which is this library's rule that a resting value is an
46137
+ * attribute's absence, and it is what the stylesheet asks about: the layout
46138
+ * rules are written against `[controls]:not([controls~="zoom"])` rather than
46139
+ * the bare negation, or a frame that named no list at all would be laid out as
46140
+ * though it had dropped everything.
46141
+ * - `controls-placement`/`controlsPlacement`: Which corner the controls sit in.
46142
+ *
46143
+ * **Configurable because there is no safe default**, and this is a collision
46144
+ * rather than a taste: MapLibre puts its own controls at the top end, Leaflet
46145
+ * and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
46146
+ * corner this took would land on somebody's, which is why every one of those
46147
+ * libraries made it a setting too.
46148
+ *
46149
+ * Logical corners rather than left and right, so a right-to-left page moves
46150
+ * them without a consumer writing a second rule.
46151
+ *
46152
+ * Absent is the top end, which is this library's rule that a resting value is
46153
+ * an attribute's absence: a default that reflected would write an attribute
46154
+ * onto every host that no server sent and nobody asked for.
45985
46155
  * - `fullscreen-label`/`fullscreenLabel`: What the control that fills the screen is called.
45986
46156
  * - `idle-delay`/`idleDelay`: How long the reader may be still before the chrome hides, in milliseconds.
45987
46157
  *
package/types/vue.d.ts CHANGED
@@ -4808,6 +4808,57 @@ export type HelixHeadlessMapHelixMapZoomElementEvent =
4808
4808
  HelixHeadlessMapElementEvent<HelixMapZoomEvent>;
4809
4809
 
4810
4810
  export type HelixHeadlessMapVueProps = {
4811
+ /** Which of the controls this frame draws, as a list of what to keep.
4812
+
4813
+ **A list rather than a switch, because the overlap is not symmetric.**
4814
+ Every map library draws zoom controls and none of them draws pan buttons:
4815
+ MapLibre's navigation control is zoom and a compass, Leaflet's is zoom,
4816
+ Google's is zoom. So a consumer slotting one of those has our zoom twice
4817
+ over and our pan pad not at all, and a single switch would make them choose
4818
+ between a duplicate and nothing.
4819
+
4820
+ `none` draws none of them, which is the right answer where the slotted map
4821
+ already has controls a reader can find: what this family argues for is that
4822
+ *a* findable control exists, never that it is ours. The methods stay public
4823
+ either way, so a consumer drawing their own chrome can still drive the
4824
+ frame.
4825
+
4826
+ Absent is all three, which is this library's rule that a resting value is an
4827
+ attribute's absence, and it is what the stylesheet asks about: the layout
4828
+ rules are written against `[controls]:not([controls~="zoom"])` rather than
4829
+ the bare negation, or a frame that named no list at all would be laid out as
4830
+ though it had dropped everything. */
4831
+ controls?: (HelixHeadlessMap["controls"]) | undefined;
4832
+ /** Which corner the controls sit in.
4833
+
4834
+ **Configurable because there is no safe default**, and this is a collision
4835
+ rather than a taste: MapLibre puts its own controls at the top end, Leaflet
4836
+ and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
4837
+ corner this took would land on somebody's, which is why every one of those
4838
+ libraries made it a setting too.
4839
+
4840
+ Logical corners rather than left and right, so a right-to-left page moves
4841
+ them without a consumer writing a second rule.
4842
+
4843
+ Absent is the top end, which is this library's rule that a resting value is
4844
+ an attribute's absence: a default that reflected would write an attribute
4845
+ onto every host that no server sent and nobody asked for. */
4846
+ "controls-placement"?: (HelixHeadlessMap["controlsPlacement"]) | undefined;
4847
+ /** Which corner the controls sit in.
4848
+
4849
+ **Configurable because there is no safe default**, and this is a collision
4850
+ rather than a taste: MapLibre puts its own controls at the top end, Leaflet
4851
+ and OpenLayers at the top start, Google's zoom at the bottom end. Whichever
4852
+ corner this took would land on somebody's, which is why every one of those
4853
+ libraries made it a setting too.
4854
+
4855
+ Logical corners rather than left and right, so a right-to-left page moves
4856
+ them without a consumer writing a second rule.
4857
+
4858
+ Absent is the top end, which is this library's rule that a resting value is
4859
+ an attribute's absence: a default that reflected would write an attribute
4860
+ onto every host that no server sent and nobody asked for. */
4861
+ controlsPlacement?: (HelixHeadlessMap["controlsPlacement"]) | undefined;
4811
4862
  /** What the control that fills the screen is called. */
4812
4863
  "fullscreen-label"?: (HelixHeadlessMap["fullscreenLabel"]) | undefined;
4813
4864
  /** What the control that fills the screen is called. */