@fusion.dev/helix-headless 0.162.0 → 0.163.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.
- package/custom-elements.json +342 -15
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +3 -1
- package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -1
- package/dist/canvas/index.d.ts +1 -1
- package/dist/canvas/root/canvas-document.d.ts +33 -0
- package/dist/canvas/root/canvas-root.d.ts +50 -2
- package/dist/canvas/root/canvas-root.js +98 -3
- package/dist/chat/composer/chat-composer.js +1 -1
- package/dist/chat/root/chat-root.d.ts +21 -5
- package/dist/chat/root/chat-root.js +21 -9
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +3 -1
- package/dist/checkbox-group/root/checkbox-group-root.js +2 -1
- package/dist/combobox/root/combobox-root.d.ts +8 -1
- package/dist/combobox/root/combobox-root.js +5 -1
- package/dist/list/root/list-root.d.ts +3 -1
- package/dist/list/root/list-root.js +2 -1
- package/dist/listbox/root/listbox-root.d.ts +2 -1
- package/dist/listbox/root/listbox-root.js +2 -1
- package/dist/map/root/map-root.d.ts +8 -1
- package/dist/map/root/map-root.js +12 -0
- package/dist/menu/item/menu-item.d.ts +38 -7
- package/dist/menu/item/menu-item.js +20 -9
- package/dist/menu/root/menu-root.d.ts +10 -1
- package/dist/menu/root/menu-root.js +8 -3
- package/dist/notifications/group/notification-group.d.ts +4 -1
- package/dist/notifications/group/notification-group.js +5 -3
- package/dist/notifications/root/notifications-root.d.ts +7 -2
- package/dist/notifications/root/notifications-root.js +2 -1
- package/dist/radio/group/radio-group.d.ts +2 -1
- package/dist/radio/group/radio-group.js +2 -1
- package/dist/select/root/select-root.d.ts +8 -1
- package/dist/select/root/select-root.js +5 -1
- package/dist/steps/root/steps-root.d.ts +7 -1
- package/dist/steps/root/steps-root.js +6 -1
- package/dist/tag/group/tag-group.d.ts +3 -1
- package/dist/tag/group/tag-group.js +2 -1
- package/dist/toggle/group/toggle-group.d.ts +2 -1
- package/dist/toggle/group/toggle-group.js +4 -1
- package/dist/tree/root/tree-root.d.ts +2 -1
- package/dist/tree/root/tree-root.js +4 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +162 -24
- package/types/vue.d.ts +40 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { HelixElement, UpgradeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/** One step of a trail written as data, for `items`. */
|
|
4
4
|
export interface BreadcrumbItem {
|
|
@@ -120,6 +120,8 @@ export declare class HelixHeadlessBreadcrumbs extends HelixElement {
|
|
|
120
120
|
private readonly markLast;
|
|
121
121
|
/** New crumbs fold again, as a new `items` does. */
|
|
122
122
|
private readonly handleSlotChange;
|
|
123
|
+
/** Reads a crumb again once it upgrades, so the current one is marked. */
|
|
124
|
+
protected readonly upgradeWatch: UpgradeController;
|
|
123
125
|
/** The crumbs `items` describes, each a crumb of this tier. */
|
|
124
126
|
protected renderItems(): TemplateResult | typeof nothing;
|
|
125
127
|
protected render(): TemplateResult;
|
|
@@ -4,7 +4,7 @@ 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, formatWords, HelixElement, hostAria, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, formatWords, HelixElement, hostAria, UpgradeController, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import { repeat } from 'lit/directives/repeat.js';
|
|
@@ -55,6 +55,7 @@ let HelixHeadlessBreadcrumbs = class HelixHeadlessBreadcrumbs extends HelixEleme
|
|
|
55
55
|
this.expanded = false;
|
|
56
56
|
this.markLast();
|
|
57
57
|
};
|
|
58
|
+
this.upgradeWatch = new UpgradeController(this, this.handleSlotChange);
|
|
58
59
|
}
|
|
59
60
|
static { this.words = 'helix-breadcrumbs'; }
|
|
60
61
|
get slotted() {
|
package/dist/canvas/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { HelixHeadlessCanvasEdge } from './edge/canvas-edge.js';
|
|
2
2
|
export { HelixHeadlessCanvasNode } from './node/canvas-node.js';
|
|
3
|
-
export type { CanvasDocument, CanvasEdge, CanvasEdgeProps, CanvasEnd, CanvasNode, CanvasNodeProps, CanvasPath, CanvasSide, CanvasTone, } from './root/canvas-root.js';
|
|
3
|
+
export type { CanvasDocument, CanvasEdge, CanvasEdgeProps, CanvasEnd, CanvasNode, CanvasNodeProps, CanvasPath, CanvasRoute, CanvasRouteEnds, CanvasSide, CanvasTone, } from './root/canvas-root.js';
|
|
4
4
|
export { fromJsonCanvas, HelixHeadlessCanvas } from './root/canvas-root.js';
|
|
@@ -91,6 +91,39 @@ export declare const port: (node: CanvasNode, side: CanvasSide) => {
|
|
|
91
91
|
};
|
|
92
92
|
/** How a connector travels between the two cards it joins. */
|
|
93
93
|
export type CanvasPath = 'curve' | 'straight' | 'step';
|
|
94
|
+
/** What a board's `edgeRoute` is given for one connection, in the board's own units. */
|
|
95
|
+
export interface CanvasRouteEnds {
|
|
96
|
+
/** The connection. */
|
|
97
|
+
edge: CanvasEdge;
|
|
98
|
+
/** Where it arrives, on the side of the card it arrives at. */
|
|
99
|
+
end: {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
};
|
|
103
|
+
/** The side of the card it leaves. */
|
|
104
|
+
fromSide: CanvasSide;
|
|
105
|
+
/** The route the board would draw: the connection's `path`, or the board's. */
|
|
106
|
+
path: CanvasPath;
|
|
107
|
+
/** Where it leaves, on the side of the card it leaves. */
|
|
108
|
+
start: {
|
|
109
|
+
x: number;
|
|
110
|
+
y: number;
|
|
111
|
+
};
|
|
112
|
+
/** The side of the card it arrives at. */
|
|
113
|
+
toSide: CanvasSide;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Draws a connection's line: its SVG path from `start` to `end`, and where its
|
|
117
|
+
* label sits, the middle of the two ends where it says nothing. Nothing back
|
|
118
|
+
* draws the board's own.
|
|
119
|
+
*/
|
|
120
|
+
export type CanvasRoute = (ends: CanvasRouteEnds) => {
|
|
121
|
+
d: string;
|
|
122
|
+
mid?: {
|
|
123
|
+
x: number;
|
|
124
|
+
y: number;
|
|
125
|
+
} | undefined;
|
|
126
|
+
} | undefined;
|
|
94
127
|
/** A point in the document's own units. */
|
|
95
128
|
type Point = {
|
|
96
129
|
x: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type CanvasView, HelixElement, type ViewportPanDetail, type ViewportWay, type ViewportZoomDetail } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
-
import { type CanvasDocument, type CanvasTone } from './canvas-document.js';
|
|
4
|
-
export type { CanvasDocument, CanvasEdge, CanvasEdgeProps, CanvasEnd, CanvasNode, CanvasNodeProps, CanvasPath, CanvasSide, CanvasTone, } from './canvas-document.js';
|
|
3
|
+
import { type CanvasDocument, type CanvasRoute, type CanvasTone } from './canvas-document.js';
|
|
4
|
+
export type { CanvasDocument, CanvasEdge, CanvasEdgeProps, CanvasEnd, CanvasNode, CanvasNodeProps, CanvasPath, CanvasRoute, CanvasRouteEnds, CanvasSide, CanvasTone, } from './canvas-document.js';
|
|
5
5
|
export { fromJsonCanvas } from './canvas-document.js';
|
|
6
6
|
declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion.dev/helix-core").ViewportElement) & typeof HelixElement;
|
|
7
7
|
/**
|
|
@@ -92,6 +92,17 @@ declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion
|
|
|
92
92
|
* connection is a new element, and React discards an element the board adds to
|
|
93
93
|
* its markup on the next render, so you write the connection.
|
|
94
94
|
*
|
|
95
|
+
* **`edgeRoute` draws the connections' lines your own way**: a function from
|
|
96
|
+
* where a line leaves and arrives, and the sides it does, to its SVG path, for
|
|
97
|
+
* a route such as one that bends around the cards between. What it returns
|
|
98
|
+
* is the line drawn, the line pressed and focused, and the one an arrowhead
|
|
99
|
+
* sits at the end of. Nothing back draws the board's own.
|
|
100
|
+
*
|
|
101
|
+
* Add `minimap` to `controls` for a picture of the whole board in a corner:
|
|
102
|
+
* each card as a box, and a frame around what is on screen. A press or a drag
|
|
103
|
+
* on it brings that place to the middle of the view. It is for a pointer, and
|
|
104
|
+
* a screen reader skips it: the arrow keys and `show()` reach every card.
|
|
105
|
+
*
|
|
95
106
|
* Cards and connections are `helix-canvas-node` and `helix-canvas-edge`
|
|
96
107
|
* elements, so they are in the served markup whichever renderer wrote them. The
|
|
97
108
|
* library fetches nothing, and an array passed to an element is missing from
|
|
@@ -148,6 +159,9 @@ declare const HelixHeadlessCanvas_base: (new (...args: any[]) => import("@fusion
|
|
|
148
159
|
* @csspart label-editor - The field for editing a connection's label or a group's name, placed over that text.
|
|
149
160
|
* @csspart live - A live region that screen readers announce while a connection is being made.
|
|
150
161
|
* @csspart marquee - The box a Shift-drag across the empty board draws, which selects the cards it touches.
|
|
162
|
+
* @csspart minimap - The picture of the whole board, under `controls="minimap"`.
|
|
163
|
+
* @csspart minimap-node - One card in the minimap.
|
|
164
|
+
* @csspart minimap-view - The frame in the minimap around what is on screen.
|
|
151
165
|
* @csspart pan-down - The button that moves the board down.
|
|
152
166
|
* @csspart pan-end - The button that moves the board toward the end of a line of text: right in English.
|
|
153
167
|
* @csspart pan-start - The button that moves the board toward the start of a line of text: left in English.
|
|
@@ -210,6 +224,15 @@ export declare class HelixHeadlessCanvas extends HelixHeadlessCanvas_base {
|
|
|
210
224
|
controlsLabel: string;
|
|
211
225
|
/** What screen readers hear after a copy, with `{count}` for the cards copied. */
|
|
212
226
|
copiedLabel: string;
|
|
227
|
+
/**
|
|
228
|
+
* Draws every connection's line: a function from where it leaves and
|
|
229
|
+
* arrives, and the sides it does, to its SVG path and where its label sits.
|
|
230
|
+
*
|
|
231
|
+
* Absent, or returning nothing for a connection, draws the board's own
|
|
232
|
+
* route. A function that throws draws the board's own too, and the error is
|
|
233
|
+
* reported to the page as an uncaught one. Set it as a property.
|
|
234
|
+
*/
|
|
235
|
+
edgeRoute?: CanvasRoute;
|
|
213
236
|
/** The board's name where `label` is empty. */
|
|
214
237
|
fallbackLabel: string;
|
|
215
238
|
/**
|
|
@@ -702,6 +725,8 @@ export declare class HelixHeadlessCanvas extends HelixHeadlessCanvas_base {
|
|
|
702
725
|
* both its cards are on the board; nothing where either is not.
|
|
703
726
|
*/
|
|
704
727
|
private geometryOf;
|
|
728
|
+
/** One connection's line, by `edgeRoute` where it draws one, and by the board's own where not. */
|
|
729
|
+
private draw;
|
|
705
730
|
/** The card an event came from, wherever inside it the event began. */
|
|
706
731
|
private cardOf;
|
|
707
732
|
/**
|
|
@@ -1244,6 +1269,29 @@ export declare class HelixHeadlessCanvas extends HelixHeadlessCanvas_base {
|
|
|
1244
1269
|
* the card a keyboard reader is on.
|
|
1245
1270
|
*/
|
|
1246
1271
|
private renderDraft;
|
|
1272
|
+
/** The part of the document on screen, in its own units. */
|
|
1273
|
+
private get onScreen();
|
|
1274
|
+
/** What the minimap shows: every card and what is on screen, with room around them. */
|
|
1275
|
+
private get minimapArea();
|
|
1276
|
+
/**
|
|
1277
|
+
* A press or a drag on the minimap, which brings the place under it to the
|
|
1278
|
+
* middle of the view.
|
|
1279
|
+
*
|
|
1280
|
+
* **The point is read through the picture's own scale**: the area is drawn
|
|
1281
|
+
* whole and centered in the box, so the box's spare width or height is split
|
|
1282
|
+
* either side of it.
|
|
1283
|
+
*/
|
|
1284
|
+
private readonly handleMinimapPointer;
|
|
1285
|
+
/**
|
|
1286
|
+
* The minimap, under `controls="minimap"`: each card as a box, and a frame
|
|
1287
|
+
* around what is on screen.
|
|
1288
|
+
*
|
|
1289
|
+
* **A picture for a pointer**, hidden from screen readers and taking no
|
|
1290
|
+
* focus, as a map's own overview is: the arrow keys and `show()` already take
|
|
1291
|
+
* a keyboard to any card, and a second way that says nothing a reader can
|
|
1292
|
+
* use would be a stop that does nothing.
|
|
1293
|
+
*/
|
|
1294
|
+
private renderMinimap;
|
|
1247
1295
|
protected render(): TemplateResult;
|
|
1248
1296
|
}
|
|
1249
1297
|
declare global {
|
|
@@ -733,6 +733,35 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
733
733
|
this.handleSlotChange = () => {
|
|
734
734
|
this.requestUpdate();
|
|
735
735
|
};
|
|
736
|
+
this.handleMinimapPointer = (event) => {
|
|
737
|
+
if (event.type === 'pointermove' && (event.buttons & 1) === 0) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const picture = event.currentTarget;
|
|
741
|
+
if (event.type === 'pointerdown') {
|
|
742
|
+
if (event.button !== 0) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
capturePointer(picture, event.pointerId);
|
|
746
|
+
}
|
|
747
|
+
event.preventDefault();
|
|
748
|
+
const area = this.minimapArea;
|
|
749
|
+
const shown = picture.getBoundingClientRect();
|
|
750
|
+
const scale = Math.min(shown.width / area.width, shown.height / area.height) || 1;
|
|
751
|
+
const point = {
|
|
752
|
+
x: area.x + (event.clientX - shown.left - (shown.width - area.width * scale) / 2) / scale,
|
|
753
|
+
y: area.y + (event.clientY - shown.top - (shown.height - area.height * scale) / 2) / scale,
|
|
754
|
+
};
|
|
755
|
+
const board = extent(this.nodes);
|
|
756
|
+
const { zoom } = this.at;
|
|
757
|
+
this.direct = event.type === 'pointermove';
|
|
758
|
+
this.touched = true;
|
|
759
|
+
this.at = {
|
|
760
|
+
...this.at,
|
|
761
|
+
x: this.box.width / 2 - (point.x - board.x + MARGIN) * zoom,
|
|
762
|
+
y: this.box.height / 2 - (point.y - board.y + MARGIN) * zoom,
|
|
763
|
+
};
|
|
764
|
+
};
|
|
736
765
|
}
|
|
737
766
|
static { this.words = 'helix-canvas'; }
|
|
738
767
|
static { this.shadowRootOptions = {
|
|
@@ -1120,9 +1149,25 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
1120
1149
|
if (!from || !to) {
|
|
1121
1150
|
return undefined;
|
|
1122
1151
|
}
|
|
1123
|
-
const drawn =
|
|
1152
|
+
const drawn = this.draw(edge, onPlane(from, origin), onPlane(to, origin), edge.path ?? this.path ?? 'curve', heads(edge));
|
|
1124
1153
|
return { ...drawn, from, to };
|
|
1125
1154
|
}
|
|
1155
|
+
draw(edge, from, to, path, drawnHeads) {
|
|
1156
|
+
if (this.edgeRoute) {
|
|
1157
|
+
const { end, fromSide, start, toSide } = ends(edge, from, to);
|
|
1158
|
+
let own;
|
|
1159
|
+
try {
|
|
1160
|
+
own = this.edgeRoute({ edge, end, fromSide, path, start, toSide });
|
|
1161
|
+
}
|
|
1162
|
+
catch (error) {
|
|
1163
|
+
globalThis.reportError?.(error);
|
|
1164
|
+
}
|
|
1165
|
+
if (own?.d) {
|
|
1166
|
+
return { d: own.d, mid: own.mid ?? { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 } };
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return connector(edge, from, to, path, drawnHeads);
|
|
1170
|
+
}
|
|
1126
1171
|
cardOf(event) {
|
|
1127
1172
|
return event
|
|
1128
1173
|
.composedPath()
|
|
@@ -2134,7 +2179,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2134
2179
|
if (target) {
|
|
2135
2180
|
const reversed = linking.moving === 'from';
|
|
2136
2181
|
const [from, to] = reversed ? [target, anchor] : [anchor, target];
|
|
2137
|
-
const drawn =
|
|
2182
|
+
const drawn = this.draw({
|
|
2138
2183
|
fromNode: from.id,
|
|
2139
2184
|
fromSide: reversed ? linking.toSide : linking.side,
|
|
2140
2185
|
id: '',
|
|
@@ -2157,6 +2202,53 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2157
2202
|
}
|
|
2158
2203
|
return nothing;
|
|
2159
2204
|
}
|
|
2205
|
+
get onScreen() {
|
|
2206
|
+
const board = extent(this.nodes);
|
|
2207
|
+
const { height, width } = this.box;
|
|
2208
|
+
const { x, y, zoom } = this.at;
|
|
2209
|
+
return {
|
|
2210
|
+
height: height / zoom,
|
|
2211
|
+
width: width / zoom,
|
|
2212
|
+
x: board.x - MARGIN - x / zoom,
|
|
2213
|
+
y: board.y - MARGIN - y / zoom,
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
get minimapArea() {
|
|
2217
|
+
const board = extent(this.nodes);
|
|
2218
|
+
const view = this.onScreen;
|
|
2219
|
+
const x = Math.min(board.x, view.x);
|
|
2220
|
+
const y = Math.min(board.y, view.y);
|
|
2221
|
+
const width = Math.max(board.x + board.width, view.x + view.width) - x;
|
|
2222
|
+
const height = Math.max(board.y + board.height, view.y + view.height) - y;
|
|
2223
|
+
const room = Math.max(width, height) * 0.05;
|
|
2224
|
+
return {
|
|
2225
|
+
height: height + room * 2,
|
|
2226
|
+
width: width + room * 2,
|
|
2227
|
+
x: x - room,
|
|
2228
|
+
y: y - room,
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
renderMinimap() {
|
|
2232
|
+
const nodes = this.nodes;
|
|
2233
|
+
if (!this.drawsControls('minimap') || nodes.length === 0) {
|
|
2234
|
+
return nothing;
|
|
2235
|
+
}
|
|
2236
|
+
const area = this.minimapArea;
|
|
2237
|
+
const view = this.onScreen;
|
|
2238
|
+
return html `<svg
|
|
2239
|
+
part="minimap"
|
|
2240
|
+
aria-hidden="true"
|
|
2241
|
+
height="120"
|
|
2242
|
+
preserveAspectRatio="xMidYMid meet"
|
|
2243
|
+
viewBox=${`${area.x} ${area.y} ${area.width} ${area.height}`}
|
|
2244
|
+
width="160"
|
|
2245
|
+
@pointerdown=${this.handleMinimapPointer}
|
|
2246
|
+
@pointermove=${this.handleMinimapPointer}
|
|
2247
|
+
>
|
|
2248
|
+
${nodes.map((node) => svg `<rect part="minimap-node" data-group=${node.type === 'group' ? '' : nothing} x=${node.x} y=${node.y} width=${node.width} height=${node.height} />`)}
|
|
2249
|
+
${svg `<rect part="minimap-view" x=${view.x} y=${view.y} width=${view.width} height=${view.height} />`}
|
|
2250
|
+
</svg>`;
|
|
2251
|
+
}
|
|
2160
2252
|
render() {
|
|
2161
2253
|
const nodes = this.nodes;
|
|
2162
2254
|
const drawn = extent(nodes);
|
|
@@ -2219,7 +2311,7 @@ let HelixHeadlessCanvas = class HelixHeadlessCanvas extends MixinViewport(HelixE
|
|
|
2219
2311
|
<slot name="empty"></slot>
|
|
2220
2312
|
${this.renderGestures()}
|
|
2221
2313
|
</div>
|
|
2222
|
-
${this.renderControls()}
|
|
2314
|
+
${this.renderControls()} ${this.renderMinimap()}
|
|
2223
2315
|
<span part="live" role="status">${this.said}</span>
|
|
2224
2316
|
<div part="footer">
|
|
2225
2317
|
<slot name="footer"></slot>
|
|
@@ -2255,6 +2347,9 @@ __decorate([
|
|
|
2255
2347
|
words,
|
|
2256
2348
|
property({ attribute: 'copied-label' })
|
|
2257
2349
|
], HelixHeadlessCanvas.prototype, "copiedLabel", void 0);
|
|
2350
|
+
__decorate([
|
|
2351
|
+
property({ attribute: false })
|
|
2352
|
+
], HelixHeadlessCanvas.prototype, "edgeRoute", void 0);
|
|
2258
2353
|
__decorate([
|
|
2259
2354
|
words,
|
|
2260
2355
|
property({ attribute: 'fallback-label' })
|
|
@@ -113,7 +113,7 @@ let HelixHeadlessChatComposer = class HelixHeadlessChatComposer extends HelixFor
|
|
|
113
113
|
}
|
|
114
114
|
const invalid = this.touched && !this.internals.validity.valid;
|
|
115
115
|
this.toggleState('invalid', invalid);
|
|
116
|
-
this.internals.ariaInvalid = invalid ? 'true' :
|
|
116
|
+
this.internals.ariaInvalid = invalid ? 'true' : 'false';
|
|
117
117
|
if (invalid !== this.shownInvalid) {
|
|
118
118
|
this.shownInvalid = invalid;
|
|
119
119
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChildrenController, HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { ChildrenController, HelixElement, UpgradeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import type { HelixHeadlessChatMessage } from '../message/chat-message.js';
|
|
4
4
|
/** One turn handed over as data, which is what `turns` holds. */
|
|
@@ -308,6 +308,11 @@ export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
|
|
|
308
308
|
* never a turn's own text, which a streaming turn rewrites every frame.
|
|
309
309
|
*/
|
|
310
310
|
protected readonly childWatch: ChildrenController;
|
|
311
|
+
/**
|
|
312
|
+
* Hears a message upgrade after it arrived. A message is known by its marker,
|
|
313
|
+
* which an element that has not upgraded lacks, so it was assigned nowhere.
|
|
314
|
+
*/
|
|
315
|
+
protected readonly upgradeWatch: UpgradeController;
|
|
311
316
|
/** Watches the turns' own `state`, which is when one of them settles. */
|
|
312
317
|
private settling;
|
|
313
318
|
/** Watches the turns and the log for size, which scrolling does not report. */
|
|
@@ -512,12 +517,23 @@ export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
|
|
|
512
517
|
* Moves focus to turn `at`, drawing it first where the transcript is
|
|
513
518
|
* windowed and it is not drawn.
|
|
514
519
|
*
|
|
515
|
-
* **A turn takes focus only once a reader moves to it**, so the
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
* to.
|
|
520
|
+
* **A turn takes focus only once a reader moves to it**, so the `tabindex`
|
|
521
|
+
* it needs is written then, long after any server's markup has hydrated,
|
|
522
|
+
* and never on a turn nobody moved to.
|
|
519
523
|
*/
|
|
520
524
|
private focusTurn;
|
|
525
|
+
/** The turn that is a tab stop because a reader moved to it. */
|
|
526
|
+
private stop;
|
|
527
|
+
/**
|
|
528
|
+
* Focuses a turn, making it the transcript's one stop a reader moved to.
|
|
529
|
+
*
|
|
530
|
+
* **`0`, and never `-1`.** Chromium leaves the whole of a shadow host at a
|
|
531
|
+
* negative `tabindex` out of the Tab order, its own controls and what is
|
|
532
|
+
* slotted into it alike, so a turn at `-1` lost its links and its Try again
|
|
533
|
+
* once a reader had paged through it. The stop moves with the reader, and a
|
|
534
|
+
* `tabindex` you wrote on a turn is left as it is.
|
|
535
|
+
*/
|
|
536
|
+
private visit;
|
|
521
537
|
/** The first control in the regions `names` holds, in their order. */
|
|
522
538
|
private firstControlIn;
|
|
523
539
|
/**
|
|
@@ -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
|
var HelixHeadlessChat_1;
|
|
8
|
-
import { ChildrenController, customElement, drawIcon, focusableElements, formatWords, HelixBeforePinEvent, HelixElement, HelixHistoryEvent, HelixPinEvent, hostAria, MixinCollection, onUnlessFalse, requestChange, VirtualizeController, whenHydrated, words, wordsConverter, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { ChildrenController, customElement, drawIcon, focusableElements, formatWords, HelixBeforePinEvent, HelixElement, HelixHistoryEvent, HelixPinEvent, hostAria, MixinCollection, onUnlessFalse, requestChange, UpgradeController, VirtualizeController, whenHydrated, words, wordsConverter, } from '@fusion.dev/helix-core';
|
|
9
9
|
import { arrowDown } from '@fusion.dev/helix-icons';
|
|
10
10
|
import { nothing } from 'lit';
|
|
11
11
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -63,6 +63,7 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
63
63
|
this.said = 0;
|
|
64
64
|
this.words = TURN_WORDS;
|
|
65
65
|
this.childWatch = new ChildrenController(this, () => this.handleSlotChange());
|
|
66
|
+
this.upgradeWatch = new UpgradeController(this, () => this.handleSlotChange());
|
|
66
67
|
this.settling = undefined;
|
|
67
68
|
this.grower = undefined;
|
|
68
69
|
this.watched = [];
|
|
@@ -523,7 +524,9 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
523
524
|
}
|
|
524
525
|
async focusTurn(at) {
|
|
525
526
|
const count = this.fed ? this.turns.length : this.messages.length;
|
|
526
|
-
|
|
527
|
+
const log = this.log;
|
|
528
|
+
const scrolls = log !== null && log !== undefined && log.scrollHeight > log.clientHeight;
|
|
529
|
+
if (at < count - 1 && this.pinned && scrolls) {
|
|
527
530
|
this.requestPinned(false);
|
|
528
531
|
}
|
|
529
532
|
if (this.fed) {
|
|
@@ -536,17 +539,27 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
536
539
|
this.requestUpdate();
|
|
537
540
|
await this.updateComplete;
|
|
538
541
|
}
|
|
539
|
-
drawn()
|
|
542
|
+
const turn = drawn();
|
|
543
|
+
if (turn) {
|
|
544
|
+
this.visit(turn);
|
|
545
|
+
}
|
|
540
546
|
return;
|
|
541
547
|
}
|
|
542
548
|
const message = this.messages[at];
|
|
543
|
-
if (
|
|
544
|
-
|
|
549
|
+
if (message) {
|
|
550
|
+
this.visit(message);
|
|
545
551
|
}
|
|
546
|
-
|
|
547
|
-
|
|
552
|
+
}
|
|
553
|
+
visit(turn) {
|
|
554
|
+
if (this.stop !== turn) {
|
|
555
|
+
this.stop?.removeAttribute('tabindex');
|
|
556
|
+
this.stop = undefined;
|
|
557
|
+
if (!turn.hasAttribute('tabindex')) {
|
|
558
|
+
turn.setAttribute('tabindex', '0');
|
|
559
|
+
this.stop = turn;
|
|
560
|
+
}
|
|
548
561
|
}
|
|
549
|
-
|
|
562
|
+
turn.focus();
|
|
550
563
|
}
|
|
551
564
|
firstControlIn(names) {
|
|
552
565
|
for (const name of names) {
|
|
@@ -581,7 +594,6 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
|
|
|
581
594
|
.turnWords=${this.words}
|
|
582
595
|
.turnPosition=${first + offset + 1}
|
|
583
596
|
.turnCount=${this.turns.length}
|
|
584
|
-
tabindex="-1"
|
|
585
597
|
.failedLabel=${this.failedLabel}
|
|
586
598
|
state=${turn.state ?? nothing}
|
|
587
599
|
>${turn.text}</${message}>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DisabledCascadeController, HelixFormElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { DisabledCascadeController, HelixFormElement, UpgradeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import { HelixHeadlessCheckbox } from '../../checkbox/root/checkbox-root.js';
|
|
4
4
|
import '../../checkbox/index.js';
|
|
@@ -132,6 +132,8 @@ export declare class HelixHeadlessCheckboxGroup extends HelixHeadlessCheckboxGro
|
|
|
132
132
|
private pending;
|
|
133
133
|
/** Watches for boxes arriving inside a wrapper, which no slot reports. */
|
|
134
134
|
private observer;
|
|
135
|
+
/** Reads a box again once it upgrades, so it takes the group's name and value. */
|
|
136
|
+
protected readonly upgradeWatch: UpgradeController;
|
|
135
137
|
/** The value the required check last judged. */
|
|
136
138
|
private judged;
|
|
137
139
|
/** The boxes `member` is added to. */
|
|
@@ -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
|
var HelixHeadlessCheckboxGroup_1;
|
|
8
|
-
import { commaList, customElement, DisabledCascadeController, HelixBeforeChangeEvent, HelixFormElement, hostAria, MixinField, words, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { commaList, customElement, DisabledCascadeController, HelixBeforeChangeEvent, HelixFormElement, hostAria, MixinField, UpgradeController, words, } from '@fusion.dev/helix-core';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
11
11
|
import { html } from 'lit/static-html.js';
|
|
@@ -21,6 +21,7 @@ let HelixHeadlessCheckboxGroup = class HelixHeadlessCheckboxGroup extends MixinF
|
|
|
21
21
|
this.declared = [];
|
|
22
22
|
this.checking = undefined;
|
|
23
23
|
this.pending = false;
|
|
24
|
+
this.upgradeWatch = new UpgradeController(this, undefined, { subtree: true });
|
|
24
25
|
this.watched = new Set();
|
|
25
26
|
this.member = {
|
|
26
27
|
hostUpdated: () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AriaForwardController, HelixFormElement, PopoverController } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, HelixFormElement, PopoverController, UpgradeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import '../../listbox/index.js';
|
|
4
4
|
import '../../tag/index.js';
|
|
@@ -376,6 +376,13 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
|
|
|
376
376
|
*/
|
|
377
377
|
get type(): string;
|
|
378
378
|
private get listbox();
|
|
379
|
+
/**
|
|
380
|
+
* Hears an option upgrade after it arrived. The options are this element's
|
|
381
|
+
* children, which the listbox reaches through a slot and does not watch, so
|
|
382
|
+
* both read them again: the listbox to select the one `value` names, and this
|
|
383
|
+
* element for the words in its field.
|
|
384
|
+
*/
|
|
385
|
+
protected readonly upgradeWatch: UpgradeController;
|
|
379
386
|
protected readonly panel: PopoverController;
|
|
380
387
|
/** The values chosen, in the order they were chosen. */
|
|
381
388
|
private get values();
|
|
@@ -4,7 +4,7 @@ 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 { AriaForwardController, afterLinkFollowed, countLabel, customElement, DelayController, drawAutofill, drawStatus, formatWords, HelixCreateEvent, HelixFilterEvent, HelixFormElement, handedLocale, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, onApple, PopoverController, reportValues, StatusController, submitImplicitly, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, afterLinkFollowed, countLabel, customElement, DelayController, drawAutofill, drawStatus, formatWords, HelixCreateEvent, HelixFilterEvent, HelixFormElement, handedLocale, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, onApple, PopoverController, reportValues, StatusController, submitImplicitly, UpgradeController, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretDown } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -50,6 +50,10 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
50
50
|
this.emptied = false;
|
|
51
51
|
this.opening = undefined;
|
|
52
52
|
this.watcher = undefined;
|
|
53
|
+
this.upgradeWatch = new UpgradeController(this, () => {
|
|
54
|
+
this.listbox?.requestUpdate();
|
|
55
|
+
this.requestUpdate();
|
|
56
|
+
}, { subtree: true });
|
|
53
57
|
this.panel = new PopoverController(this, {
|
|
54
58
|
anchor: () => this.partNamed('field'),
|
|
55
59
|
boundary: () => [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement, KeyboardNavigationController } from '@fusion.dev/helix-core';
|
|
1
|
+
import { HelixElement, KeyboardNavigationController, UpgradeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import { HelixHeadlessListItem } from '../item/list-item.js';
|
|
4
4
|
import { type ListSelectionBehavior, type ListSelectionMode } from '../selection.js';
|
|
@@ -405,6 +405,8 @@ export declare class HelixHeadlessList extends HelixElement {
|
|
|
405
405
|
private askForMore;
|
|
406
406
|
private readonly handleEmptyChange;
|
|
407
407
|
private readonly handleSlotChange;
|
|
408
|
+
/** Reads an item again once it upgrades, so it takes the role the list gives its rows. */
|
|
409
|
+
protected readonly upgradeWatch: UpgradeController;
|
|
408
410
|
}
|
|
409
411
|
declare global {
|
|
410
412
|
interface HTMLElementTagNameMap {
|
|
@@ -4,7 +4,7 @@ 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 { auditName, commaList, customElement, deepActiveElement, drawStatus, focusItemOf, formatWords, HelixActionEvent, HelixElement, HelixLoadMoreEvent, hostAria, inlineStep, KeyboardNavigationController, placeKey, pressedOwnControl, ReorderController, requestReorder, requestValue, rovingStop, StatusController, scrollingAncestor, scrollRoom, selectsAll, typeaheadByWords, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { auditName, commaList, customElement, deepActiveElement, drawStatus, focusItemOf, formatWords, HelixActionEvent, HelixElement, HelixLoadMoreEvent, hostAria, inlineStep, KeyboardNavigationController, placeKey, pressedOwnControl, ReorderController, requestReorder, requestValue, rovingStop, StatusController, scrollingAncestor, scrollRoom, selectsAll, typeaheadByWords, UpgradeController, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { HelixHeadlessListItem } from '../item/list-item.js';
|
|
@@ -138,6 +138,7 @@ let HelixHeadlessList = class HelixHeadlessList extends HelixElement {
|
|
|
138
138
|
this.rowsChanged = true;
|
|
139
139
|
this.requestUpdate();
|
|
140
140
|
};
|
|
141
|
+
this.upgradeWatch = new UpgradeController(this, this.handleSlotChange);
|
|
141
142
|
}
|
|
142
143
|
static { this.words = 'helix-list'; }
|
|
143
144
|
blocked(row) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AriaForwardController, type DataOptionItem, DisabledCascadeController, HelixFormElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, type DataOptionItem, DisabledCascadeController, HelixFormElement, UpgradeController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import { HelixHeadlessOption } from '../../option/root/option-root.js';
|
|
4
4
|
declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusion.dev/helix-core").CollectionElement) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
|
|
@@ -436,6 +436,7 @@ export declare class HelixHeadlessListbox extends HelixHeadlessListbox_base {
|
|
|
436
436
|
* exactly as it moves through a flat one.
|
|
437
437
|
*/
|
|
438
438
|
get options(): HelixHeadlessOption[];
|
|
439
|
+
protected readonly upgradeWatch: UpgradeController;
|
|
439
440
|
/**
|
|
440
441
|
* The elements of one kind in the list: its options, its groups or its
|
|
441
442
|
* rules. Walks the tree, and walks *through* a slot when it meets one.
|
|
@@ -4,7 +4,7 @@ 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 { AriaForwardController, centerInView, chosenValues, columnStep, countLabel, customElement, DelayController, DisabledCascadeController, drawAutofill, endInSight, formatWords, HelixFilterEvent, HelixFormElement, HelixLoadMoreEvent, hostAria, inlineStep, MixinCollection, MixinControllerValue, MixinField, markMatches, OptionWindowController, onApple, pageRows, rangeAdd, reportValues, requestChange, scrollItemIntoView, selectsAll, Typeahead, toggleEvery, typesCharacter, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, centerInView, chosenValues, columnStep, countLabel, customElement, DelayController, DisabledCascadeController, drawAutofill, endInSight, formatWords, HelixFilterEvent, HelixFormElement, HelixLoadMoreEvent, hostAria, inlineStep, MixinCollection, MixinControllerValue, MixinField, markMatches, OptionWindowController, onApple, pageRows, rangeAdd, reportValues, requestChange, scrollItemIntoView, selectsAll, Typeahead, toggleEvery, typesCharacter, UpgradeController, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { magnifyingGlass } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -51,6 +51,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
|
|
|
51
51
|
items: () => this.options,
|
|
52
52
|
});
|
|
53
53
|
this.chosenAgain = undefined;
|
|
54
|
+
this.upgradeWatch = new UpgradeController(this, undefined, { subtree: true });
|
|
54
55
|
this.handleSelection = (event) => {
|
|
55
56
|
if (event.detail.href !== undefined) {
|
|
56
57
|
return;
|
|
@@ -46,7 +46,8 @@ declare const HelixHeadlessMap_base: (new (...args: any[]) => import("@fusion.de
|
|
|
46
46
|
* same `group`, such as a route and its dotted legs, share one tile. A tile
|
|
47
47
|
* sends `helix-before-toggle` from the layer first, so you can store the
|
|
48
48
|
* choice or refuse it, and `helix-toggle` from it once `hidden` holds the
|
|
49
|
-
* choice.
|
|
49
|
+
* choice. Escape closes the menu and returns focus to its button, and the menu
|
|
50
|
+
* closes when focus moves on from it or a press lands outside the map.
|
|
50
51
|
*
|
|
51
52
|
* Imagery, a `helix-map-raster` with `ground`, is a map type and not a tile.
|
|
52
53
|
* The menu opens with a card for each type, Standard, Hybrid, and Satellite,
|
|
@@ -315,6 +316,12 @@ export declare class HelixHeadlessMap extends HelixHeadlessMap_base {
|
|
|
315
316
|
private hiding?;
|
|
316
317
|
/** Closes the layers menu on a press anywhere outside the frame. */
|
|
317
318
|
private readonly handleOutside;
|
|
319
|
+
/**
|
|
320
|
+
* Closes the layers menu when focus moves on from its button and the menu,
|
|
321
|
+
* as a Tab past the last tile does. Focus leaving the page, with nothing
|
|
322
|
+
* focused in its place, leaves the menu as it is.
|
|
323
|
+
*/
|
|
324
|
+
private readonly handleMenuFocusOut;
|
|
318
325
|
/**
|
|
319
326
|
* Keys pressed in the layers menu. Escape closes it and returns to its
|
|
320
327
|
* button, and no key reaches the region, where the arrows would pan the map.
|
|
@@ -82,6 +82,16 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
|
|
|
82
82
|
this.layersOpen = false;
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
|
+
this.handleMenuFocusOut = (event) => {
|
|
86
|
+
const to = event.relatedTarget;
|
|
87
|
+
if (!(this.layersOpen && to)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const owners = this.renderRoot.querySelectorAll('[part="layers-toggle"], [part="layers-menu"]');
|
|
91
|
+
if (![...owners].some((owner) => owner.contains(to))) {
|
|
92
|
+
this.layersOpen = false;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
85
95
|
this.handleMenuKeydown = (event) => {
|
|
86
96
|
event.stopPropagation();
|
|
87
97
|
if (event.key === 'Escape' && this.layersOpen) {
|
|
@@ -402,6 +412,7 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
|
|
|
402
412
|
this.dispatchEvent(new HelixMapPartChangeEvent({ kind: 'menu' }));
|
|
403
413
|
}
|
|
404
414
|
}}
|
|
415
|
+
@focusout=${this.handleMenuFocusOut}
|
|
405
416
|
@keydown=${this.handleMenuKeydown}
|
|
406
417
|
>
|
|
407
418
|
<slot name="layers-icon">
|
|
@@ -416,6 +427,7 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
|
|
|
416
427
|
role="group"
|
|
417
428
|
aria-label=${this.layersLabel}
|
|
418
429
|
?hidden=${!this.layersOpen}
|
|
430
|
+
@focusout=${this.handleMenuFocusOut}
|
|
419
431
|
@keydown=${this.handleMenuKeydown}
|
|
420
432
|
>
|
|
421
433
|
${this.imagery.length > 0 ? this.renderMapTypes() : nothing}
|