@ds-gn/ui 0.10.0 → 0.11.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/README.md +4 -4
- package/custom-elements.json +5511 -2144
- package/dist/common/controllers/OverlayDismissController.d.ts +52 -0
- package/dist/common/controllers/OverlayPositionController.d.ts +168 -0
- package/dist/common/controllers/RovingFocusController.d.ts +99 -0
- package/dist/common/helpers/field-control.d.ts +35 -0
- package/dist/common/helpers/listbox-navigation.d.ts +50 -0
- package/dist/common/props/size/size.props.d.ts +11 -0
- package/dist/common/styles/overlay-panel.styles.d.ts +55 -0
- package/dist/components/Button/src/Button.d.ts +20 -0
- package/dist/components/Button/src/Button.js +28 -0
- package/dist/components/DropdownMenu/src/DropdownMenu.d.ts +150 -0
- package/dist/components/DropdownMenu/src/DropdownMenu.js +1550 -0
- package/dist/components/DropdownMenu/src/DropdownMenu.playground.d.ts +2 -0
- package/dist/components/DropdownMenu/src/DropdownMenu.props.d.ts +47 -0
- package/dist/components/DropdownMenu/src/DropdownMenu.props.js +54 -0
- package/dist/components/DropdownMenu/src/DropdownMenu.styles.d.ts +20 -0
- package/dist/components/Field/src/Field.d.ts +39 -0
- package/dist/components/Field/src/Field.js +203 -0
- package/dist/components/Field/src/Field.playground.d.ts +2 -0
- package/dist/components/Field/src/Field.styles.d.ts +6 -0
- package/dist/components/Icon/src/Icon.d.ts +5 -0
- package/dist/components/Icon/src/Icon.js +3 -0
- package/dist/components/Icon/src/icons/arrow-down.d.ts +1 -0
- package/dist/components/Icon/src/icons/arrow-down.js +6 -0
- package/dist/components/Icon/src/icons/arrow-up.d.ts +1 -0
- package/dist/components/Icon/src/icons/arrow-up.js +6 -0
- package/dist/components/Icon/src/icons/chevrons-up-down.d.ts +1 -0
- package/dist/components/Icon/src/icons/chevrons-up-down.js +6 -0
- package/dist/components/Icon/src/icons/cookie.d.ts +1 -0
- package/dist/components/Icon/src/icons/cookie.js +6 -0
- package/dist/components/Icon/src/icons/icon-names.d.ts +1 -1
- package/dist/components/Icon/src/icons/icon-names.js +4 -0
- package/dist/components/Icon/src/icons/index.d.ts +4 -0
- package/dist/components/Icon/src/icons/index.js +4 -0
- package/dist/components/Input/src/Input.d.ts +37 -0
- package/dist/components/Input/src/Input.js +300 -143
- package/dist/components/Input/src/Input.props.d.ts +2 -1
- package/dist/components/Input/src/Input.props.js +12 -6
- package/dist/components/Input/src/Input.styles.d.ts +12 -4
- package/dist/components/Modal/src/Modal.d.ts +1 -0
- package/dist/components/Modal/src/Modal.js +1 -0
- package/dist/components/Nav/src/Nav.js +2 -1
- package/dist/components/NavLink/src/NavLink.d.ts +14 -30
- package/dist/components/NavLink/src/NavLink.js +201 -65
- package/dist/components/Select/src/Select.d.ts +157 -0
- package/dist/components/Select/src/Select.js +1766 -0
- package/dist/components/Select/src/Select.playground.d.ts +2 -0
- package/dist/components/Select/src/Select.props.d.ts +19 -0
- package/dist/components/Select/src/Select.props.js +40 -0
- package/dist/components/Select/src/Select.styles.d.ts +30 -0
- package/dist/components/Table/src/Table.d.ts +93 -0
- package/dist/components/Table/src/Table.js +1105 -0
- package/dist/components/Table/src/Table.playground.d.ts +2 -0
- package/dist/components/Table/src/Table.props.d.ts +53 -0
- package/dist/components/Table/src/Table.props.js +18 -0
- package/dist/components/Table/src/Table.styles.d.ts +33 -0
- package/dist/components/Toaster/src/Toaster.d.ts +174 -0
- package/dist/components/Toaster/src/Toaster.js +1489 -0
- package/dist/components/Toaster/src/Toaster.playground.d.ts +2 -0
- package/dist/components/Toaster/src/Toaster.props.d.ts +14 -0
- package/dist/components/Toaster/src/Toaster.props.js +81 -0
- package/dist/components/Toaster/src/Toaster.styles.d.ts +52 -0
- package/dist/components/Toaster/src/toast.d.ts +115 -0
- package/dist/components/Toaster/src/toast.js +74 -0
- package/dist/playground/types.d.ts +27 -0
- package/dist/playground-manifest.d.ts +3 -2
- package/dist/playground-manifest.js +507 -7
- package/dist/react/DropdownMenu.d.ts +8 -0
- package/dist/react/DropdownMenu.js +13 -0
- package/dist/react/Field.d.ts +6 -0
- package/dist/react/Field.js +13 -0
- package/dist/react/Select.d.ts +8 -0
- package/dist/react/Select.js +13 -0
- package/dist/react/Table.d.ts +8 -0
- package/dist/react/Table.js +13 -0
- package/dist/react/Toaster.d.ts +8 -0
- package/dist/react/Toaster.js +13 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +6 -0
- package/package.json +52 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SizeType } from '../../../common/props/size/size.props.js';
|
|
1
2
|
export declare const inputTypeValues: {
|
|
2
3
|
readonly TEXT: "text";
|
|
3
4
|
readonly EMAIL: "email";
|
|
@@ -9,5 +10,5 @@ export declare const inputSizeValues: {
|
|
|
9
10
|
readonly MD: "md";
|
|
10
11
|
readonly LG: "lg";
|
|
11
12
|
};
|
|
12
|
-
export type InputSizeType =
|
|
13
|
+
export type InputSizeType = SizeType;
|
|
13
14
|
export declare const inputSizeProps: import("lit").PropertyDeclaration<unknown, unknown>;
|
|
@@ -21,18 +21,24 @@ var enumCreator = (acceptableValues, propName, type, other = {}) => {
|
|
|
21
21
|
};
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
// common/props/size/size.props.ts
|
|
25
|
+
var sizeValues = {
|
|
26
|
+
SM: "sm",
|
|
27
|
+
MD: "md",
|
|
28
|
+
LG: "lg"
|
|
29
|
+
};
|
|
30
|
+
var sizeProps = enumCreator(Object.values(sizeValues), "size", String, {
|
|
31
|
+
reflect: true
|
|
32
|
+
});
|
|
33
|
+
|
|
24
34
|
// components/Input/src/Input.props.ts
|
|
25
35
|
var inputTypeValues = {
|
|
26
36
|
TEXT: "text",
|
|
27
37
|
EMAIL: "email"
|
|
28
38
|
};
|
|
29
39
|
var inputTypeProps = enumCreator(Object.values(inputTypeValues), "type", String, { reflect: true });
|
|
30
|
-
var inputSizeValues =
|
|
31
|
-
|
|
32
|
-
MD: "md",
|
|
33
|
-
LG: "lg"
|
|
34
|
-
};
|
|
35
|
-
var inputSizeProps = enumCreator(Object.values(inputSizeValues), "size", String, { reflect: true });
|
|
40
|
+
var inputSizeValues = sizeValues;
|
|
41
|
+
var inputSizeProps = sizeProps;
|
|
36
42
|
export {
|
|
37
43
|
inputSizeProps,
|
|
38
44
|
inputSizeValues,
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { type InputSizeType } from './Input.props.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Visual state of the control. Exactly one applies, so the border and
|
|
4
|
+
* background utilities never compete: disabled wins over read-only, which wins
|
|
5
|
+
* over invalid.
|
|
6
|
+
*/
|
|
7
|
+
export type InputState = 'default' | 'invalid' | 'readonly' | 'disabled';
|
|
8
|
+
export declare const classNames: (size: InputSizeType, state: InputState) => string;
|
|
3
9
|
export declare const wrapperClasses: () => string;
|
|
4
|
-
export declare const labelClasses: () => string;
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const
|
|
10
|
+
export declare const labelClasses: (size: InputSizeType, disabled: boolean) => string;
|
|
11
|
+
export declare const requiredClasses: () => string;
|
|
12
|
+
export declare const hintClasses: (size: InputSizeType) => string;
|
|
13
|
+
export declare const errorClasses: (size: InputSizeType) => string;
|
|
14
|
+
export declare const errorIconClasses: () => string;
|
|
@@ -22,6 +22,7 @@ import { type ModalCloseReasonType, type ModalMotionType, type ModalPlacementTyp
|
|
|
22
22
|
* @slot description - Supporting text referenced by `aria-describedby`.
|
|
23
23
|
* @slot footer - Actions, typically buttons.
|
|
24
24
|
* @slot close - A close control; clicking anything slotted here requests a close.
|
|
25
|
+
* @slot toaster - Not for authors: an open page's `dsgn-toaster` moves its region here while the modal is open, because a modal dialog makes everything outside it inert.
|
|
25
26
|
*
|
|
26
27
|
* @csspart dialog - Native dialog element.
|
|
27
28
|
* @csspart close-button - Built-in close button.
|
|
@@ -144,7 +144,8 @@ var styles2 = css2`/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.co
|
|
|
144
144
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
145
145
|
--default-transition-duration: 150ms;
|
|
146
146
|
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
147
|
-
--radius-
|
|
147
|
+
--radius-control: var(--dsgn-src-radius-control, 0.375rem);
|
|
148
|
+
--radius-input: var(--dsgn-src-radius-input, var(--radius-control));
|
|
148
149
|
--color-accent: inherit;
|
|
149
150
|
--color-outline: inherit;
|
|
150
151
|
--color-page: inherit;
|
|
@@ -91,13 +91,21 @@ export declare class NavLink extends Base {
|
|
|
91
91
|
*/
|
|
92
92
|
private breakpointController;
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
95
|
-
* the
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
94
|
+
* Panels are positioned against their own trigger and the viewport, not
|
|
95
|
+
* against the nav, so every column count uses the same rule. The shift is
|
|
96
|
+
* measured against the viewport, so a change in the room around the trigger
|
|
97
|
+
* invalidates it: an open panel would otherwise keep the offset the old width
|
|
98
|
+
* earned it and hang off whichever edge the new width put it over. The
|
|
99
|
+
* controller only tracks layout while a panel is open. The default 16px
|
|
100
|
+
* gutter matches the `2rem` the styles subtract from `100vw` for the panel's
|
|
101
|
+
* max width, so a panel squeezed to its widest still lands on the gutter.
|
|
102
|
+
*/
|
|
103
|
+
private overlayPosition;
|
|
104
|
+
/**
|
|
105
|
+
* Escape closes any open panel and returns focus to the trigger; a press
|
|
106
|
+
* outside closes a pinned one (the listener only exists while pinned).
|
|
99
107
|
*/
|
|
100
|
-
private
|
|
108
|
+
private overlayDismiss;
|
|
101
109
|
connectedCallback(): void;
|
|
102
110
|
/**
|
|
103
111
|
* Resolve the `rel` attribute. For `target="_blank"` links, always include
|
|
@@ -105,7 +113,6 @@ export declare class NavLink extends Base {
|
|
|
105
113
|
* new-tab nav link never exposes `window.opener`.
|
|
106
114
|
*/
|
|
107
115
|
private get computedRel();
|
|
108
|
-
disconnectedCallback(): void;
|
|
109
116
|
updated(changedProperties: Map<string | number | symbol, unknown>): void;
|
|
110
117
|
private updateDropdownState;
|
|
111
118
|
private handleDropdownSlotChange;
|
|
@@ -128,29 +135,6 @@ export declare class NavLink extends Base {
|
|
|
128
135
|
*/
|
|
129
136
|
private setExpanded;
|
|
130
137
|
private toggleDropdown;
|
|
131
|
-
private handleDocumentPointerDown;
|
|
132
|
-
private clearDropdownAlignment;
|
|
133
|
-
/**
|
|
134
|
-
* Width the dropdown panel renders at. A closed panel is `display: none`, so
|
|
135
|
-
* lay it out just long enough to read it — the browser cannot paint between
|
|
136
|
-
* the two writes below, so nothing flashes.
|
|
137
|
-
*/
|
|
138
|
-
private measureDropdownWidth;
|
|
139
|
-
/**
|
|
140
|
-
* Horizontal shift for the panel, measured from the left-aligned position
|
|
141
|
-
* under its trigger. Hanging right of the trigger is the default; if that
|
|
142
|
-
* would spill past the viewport, the panel flips to hang left instead, with
|
|
143
|
-
* its right edge on the trigger's. Only when *that* would leave the viewport
|
|
144
|
-
* too — a panel wider than the room on either side of its trigger — does it
|
|
145
|
-
* give up the anchor and sit on the gutter, where its fluid width has already
|
|
146
|
-
* shrunk it to fit.
|
|
147
|
-
*/
|
|
148
|
-
private dropdownShift;
|
|
149
|
-
/**
|
|
150
|
-
* Panels are positioned against their own trigger and the viewport, not
|
|
151
|
-
* against the nav, so every column count uses the same rule.
|
|
152
|
-
*/
|
|
153
|
-
private updateDropdownAlignment;
|
|
154
138
|
/**
|
|
155
139
|
* Hover-to-open applies only where the desktop trigger is rendered (>= sm)
|
|
156
140
|
* and only on a device that really hovers.
|
|
@@ -91,6 +91,60 @@ class Base extends LitElement {
|
|
|
91
91
|
}
|
|
92
92
|
defineElement("dsgn-base", Base);
|
|
93
93
|
|
|
94
|
+
// common/controllers/OverlayDismissController.ts
|
|
95
|
+
class OverlayDismissController {
|
|
96
|
+
constructor(host, options) {
|
|
97
|
+
this.listening = false;
|
|
98
|
+
this.wantsOutside = false;
|
|
99
|
+
this.handleDocumentPointerDown = (event) => {
|
|
100
|
+
if (!event.composedPath().includes(this.host)) {
|
|
101
|
+
this.options.onDismiss("outside");
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
this.host = host;
|
|
105
|
+
this.options = options;
|
|
106
|
+
host.addController(this);
|
|
107
|
+
}
|
|
108
|
+
start() {
|
|
109
|
+
this.wantsOutside = true;
|
|
110
|
+
this.listen();
|
|
111
|
+
}
|
|
112
|
+
stop() {
|
|
113
|
+
this.wantsOutside = false;
|
|
114
|
+
this.unlisten();
|
|
115
|
+
}
|
|
116
|
+
handleKeydown(event) {
|
|
117
|
+
if (event.key !== "Escape" || !this.options.isOpen()) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
this.options.onDismiss("escape");
|
|
121
|
+
this.options.returnFocusTo?.()?.focus();
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
hostConnected() {
|
|
125
|
+
if (this.wantsOutside) {
|
|
126
|
+
this.listen();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
hostDisconnected() {
|
|
130
|
+
this.unlisten();
|
|
131
|
+
}
|
|
132
|
+
listen() {
|
|
133
|
+
if (this.listening || typeof document === "undefined") {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this.listening = true;
|
|
137
|
+
document.addEventListener("pointerdown", this.handleDocumentPointerDown);
|
|
138
|
+
}
|
|
139
|
+
unlisten() {
|
|
140
|
+
if (!this.listening) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.listening = false;
|
|
144
|
+
document.removeEventListener("pointerdown", this.handleDocumentPointerDown);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
94
148
|
// common/helpers/layout-change.ts
|
|
95
149
|
var listenersByTarget = new Map;
|
|
96
150
|
var observer;
|
|
@@ -152,21 +206,118 @@ var observeLayoutChanges = (target, listener) => {
|
|
|
152
206
|
};
|
|
153
207
|
};
|
|
154
208
|
|
|
155
|
-
// common/controllers/
|
|
156
|
-
|
|
157
|
-
|
|
209
|
+
// common/controllers/OverlayPositionController.ts
|
|
210
|
+
var overlayViewportGutter = 16;
|
|
211
|
+
var computeViewportPosition = (anchor, { placement, shift }, viewportHeight, offset = 0) => {
|
|
212
|
+
const below = placement.startsWith("bottom");
|
|
213
|
+
return {
|
|
214
|
+
left: anchor.left + shift,
|
|
215
|
+
top: below ? anchor.bottom + offset : null,
|
|
216
|
+
bottom: below ? null : viewportHeight - anchor.top + offset,
|
|
217
|
+
anchorWidth: anchor.width
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
var computeOverlayShift = (anchor, overlayWidth, viewportWidth, gutter = overlayViewportGutter, align = "start") => {
|
|
221
|
+
const viewportRight = viewportWidth - gutter;
|
|
222
|
+
const startFits = anchor.left + overlayWidth <= viewportRight;
|
|
223
|
+
const endAligned = anchor.width - overlayWidth;
|
|
224
|
+
const endFits = anchor.left + endAligned >= gutter;
|
|
225
|
+
if (!overlayWidth) {
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
if (align === "end") {
|
|
229
|
+
if (endFits)
|
|
230
|
+
return endAligned;
|
|
231
|
+
return startFits ? 0 : viewportRight - overlayWidth - anchor.left;
|
|
232
|
+
}
|
|
233
|
+
if (startFits)
|
|
234
|
+
return 0;
|
|
235
|
+
return endFits ? endAligned : gutter - anchor.left;
|
|
236
|
+
};
|
|
237
|
+
var computeOverlayPlacement = (anchor, overlayHeight, viewportHeight, {
|
|
238
|
+
gutter = overlayViewportGutter,
|
|
239
|
+
offset = 0,
|
|
240
|
+
align = "start"
|
|
241
|
+
} = {}) => {
|
|
242
|
+
const below = viewportHeight - gutter - anchor.bottom - offset;
|
|
243
|
+
const above = anchor.top - gutter - offset;
|
|
244
|
+
const flips = !!overlayHeight && overlayHeight > below && (overlayHeight <= above || above > below);
|
|
245
|
+
return `${flips ? "top" : "bottom"}-${align}`;
|
|
246
|
+
};
|
|
247
|
+
var sizeOf = (overlay) => {
|
|
248
|
+
if (overlay.offsetWidth) {
|
|
249
|
+
return { width: overlay.offsetWidth, height: overlay.offsetHeight };
|
|
250
|
+
}
|
|
251
|
+
const { width, height } = overlay.getBoundingClientRect();
|
|
252
|
+
return { width, height };
|
|
253
|
+
};
|
|
254
|
+
var measureOverlay = (overlay) => {
|
|
255
|
+
const open = sizeOf(overlay);
|
|
256
|
+
if (open.width) {
|
|
257
|
+
return open;
|
|
258
|
+
}
|
|
259
|
+
const inline = overlay.style.cssText;
|
|
260
|
+
overlay.style.display = "block";
|
|
261
|
+
overlay.style.visibility = "hidden";
|
|
262
|
+
const size = sizeOf(overlay);
|
|
263
|
+
overlay.style.cssText = inline;
|
|
264
|
+
return size;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
class OverlayPositionController {
|
|
268
|
+
constructor(host, options) {
|
|
158
269
|
this.wantsUpdates = false;
|
|
270
|
+
this.update = () => {
|
|
271
|
+
const anchor = this.options.anchor?.() ?? this.host;
|
|
272
|
+
const overlay = this.options.overlay();
|
|
273
|
+
if (!anchor || !overlay) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
277
|
+
this.options.beforeMeasure?.(anchorRect);
|
|
278
|
+
const { width, height } = measureOverlay(overlay);
|
|
279
|
+
const gutter = this.options.gutter ?? overlayViewportGutter;
|
|
280
|
+
const offset = this.options.offset ?? 0;
|
|
281
|
+
const align = this.options.align?.() ?? "start";
|
|
282
|
+
const shift = computeOverlayShift(anchorRect, width, window.innerWidth, gutter, align);
|
|
283
|
+
const placement = this.options.flip ? computeOverlayPlacement(anchorRect, height, window.innerHeight, {
|
|
284
|
+
gutter,
|
|
285
|
+
offset,
|
|
286
|
+
align
|
|
287
|
+
}) : `bottom-${align}`;
|
|
288
|
+
this._position = {
|
|
289
|
+
shift,
|
|
290
|
+
placement,
|
|
291
|
+
viewport: computeViewportPosition(anchorRect, { placement, shift }, window.innerHeight, offset)
|
|
292
|
+
};
|
|
293
|
+
this.options.apply(this._position);
|
|
294
|
+
};
|
|
295
|
+
this.scheduleUpdate = () => {
|
|
296
|
+
if (this.frame !== undefined) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
this.frame = requestAnimationFrame(() => {
|
|
300
|
+
this.frame = undefined;
|
|
301
|
+
this.update();
|
|
302
|
+
});
|
|
303
|
+
};
|
|
159
304
|
this.host = host;
|
|
160
|
-
this.
|
|
305
|
+
this.options = options;
|
|
161
306
|
host.addController(this);
|
|
162
307
|
}
|
|
163
|
-
|
|
308
|
+
get position() {
|
|
309
|
+
return this._position;
|
|
310
|
+
}
|
|
311
|
+
start() {
|
|
164
312
|
this.wantsUpdates = true;
|
|
165
313
|
this.subscribe();
|
|
314
|
+
this.update();
|
|
166
315
|
}
|
|
167
|
-
|
|
316
|
+
stop() {
|
|
168
317
|
this.wantsUpdates = false;
|
|
169
|
-
this.
|
|
318
|
+
this.unsubscribeAll();
|
|
319
|
+
this._position = undefined;
|
|
320
|
+
this.options.clear?.();
|
|
170
321
|
}
|
|
171
322
|
hostConnected() {
|
|
172
323
|
if (this.wantsUpdates) {
|
|
@@ -174,17 +325,33 @@ class LayoutChangeController {
|
|
|
174
325
|
}
|
|
175
326
|
}
|
|
176
327
|
hostDisconnected() {
|
|
177
|
-
this.
|
|
328
|
+
this.unsubscribeAll();
|
|
178
329
|
}
|
|
179
330
|
subscribe() {
|
|
180
331
|
if (this.unsubscribe) {
|
|
181
332
|
return;
|
|
182
333
|
}
|
|
183
|
-
|
|
334
|
+
const stopLayout = observeLayoutChanges(this.host, this.update);
|
|
335
|
+
if (!this.options.trackViewport || typeof window === "undefined") {
|
|
336
|
+
this.unsubscribe = stopLayout;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const listenerOptions = { capture: true, passive: true };
|
|
340
|
+
window.addEventListener("scroll", this.scheduleUpdate, listenerOptions);
|
|
341
|
+
window.addEventListener("resize", this.scheduleUpdate, listenerOptions);
|
|
342
|
+
this.unsubscribe = () => {
|
|
343
|
+
stopLayout();
|
|
344
|
+
window.removeEventListener("scroll", this.scheduleUpdate, true);
|
|
345
|
+
window.removeEventListener("resize", this.scheduleUpdate, true);
|
|
346
|
+
};
|
|
184
347
|
}
|
|
185
|
-
|
|
348
|
+
unsubscribeAll() {
|
|
186
349
|
this.unsubscribe?.();
|
|
187
350
|
this.unsubscribe = undefined;
|
|
351
|
+
if (this.frame !== undefined) {
|
|
352
|
+
cancelAnimationFrame(this.frame);
|
|
353
|
+
this.frame = undefined;
|
|
354
|
+
}
|
|
188
355
|
}
|
|
189
356
|
}
|
|
190
357
|
|
|
@@ -1554,7 +1721,6 @@ var overviewClasses = () => clsx_default("col-span-full", "inline-flex", "w-full
|
|
|
1554
1721
|
var navLinkDropdownId = 0;
|
|
1555
1722
|
var dropdownShiftProperty = "--dsgn-nav-link-dropdown-shift";
|
|
1556
1723
|
var supportsHover = () => typeof window === "undefined" || typeof window.matchMedia !== "function" ? true : window.matchMedia("(hover: hover)").matches;
|
|
1557
|
-
var dropdownViewportGutter = 16;
|
|
1558
1724
|
|
|
1559
1725
|
class NavLink extends Base {
|
|
1560
1726
|
constructor() {
|
|
@@ -1575,7 +1741,22 @@ class NavLink extends Base {
|
|
|
1575
1741
|
this.keyboardDismissed = false;
|
|
1576
1742
|
this.dropdownId = `dsgn-nav-link-dropdown-${++navLinkDropdownId}`;
|
|
1577
1743
|
this.breakpointController = new BreakpointController(this);
|
|
1578
|
-
this.
|
|
1744
|
+
this.overlayPosition = new OverlayPositionController(this, {
|
|
1745
|
+
overlay: () => this.shadowRoot?.querySelector(`#${this.dropdownId}`),
|
|
1746
|
+
apply: ({ shift }) => this.style.setProperty(dropdownShiftProperty, `${shift}px`),
|
|
1747
|
+
clear: () => this.style.removeProperty(dropdownShiftProperty)
|
|
1748
|
+
});
|
|
1749
|
+
this.overlayDismiss = new OverlayDismissController(this, {
|
|
1750
|
+
isOpen: () => this.expanded && this.hasDropdown,
|
|
1751
|
+
onDismiss: (reason) => {
|
|
1752
|
+
this.expanded = false;
|
|
1753
|
+
this.pinned = false;
|
|
1754
|
+
if (reason === "escape") {
|
|
1755
|
+
this.keyboardDismissed = true;
|
|
1756
|
+
}
|
|
1757
|
+
},
|
|
1758
|
+
returnFocusTo: () => this.shadowRoot?.querySelector("button, a")
|
|
1759
|
+
});
|
|
1579
1760
|
this.handleDropdownSlotChange = () => {
|
|
1580
1761
|
this.updateDropdownState();
|
|
1581
1762
|
};
|
|
@@ -1585,17 +1766,11 @@ class NavLink extends Base {
|
|
|
1585
1766
|
this.toggleDropdown = () => {
|
|
1586
1767
|
this.pinned = !this.pinned;
|
|
1587
1768
|
if (this.pinned) {
|
|
1588
|
-
this.
|
|
1769
|
+
this.overlayPosition.update();
|
|
1589
1770
|
}
|
|
1590
1771
|
this.keyboardDismissed = false;
|
|
1591
1772
|
this.setExpanded(this.pinned);
|
|
1592
1773
|
};
|
|
1593
|
-
this.handleDocumentPointerDown = (event) => {
|
|
1594
|
-
if (!event.composedPath().includes(this)) {
|
|
1595
|
-
this.pinned = false;
|
|
1596
|
-
this.expanded = false;
|
|
1597
|
-
}
|
|
1598
|
-
};
|
|
1599
1774
|
this.handlePointerEnter = () => {
|
|
1600
1775
|
if (!this.hoverOpensDropdown) {
|
|
1601
1776
|
return;
|
|
@@ -1603,7 +1778,7 @@ class NavLink extends Base {
|
|
|
1603
1778
|
this.keyboardDismissed = false;
|
|
1604
1779
|
this.updateDropdownState();
|
|
1605
1780
|
if (this.hasDropdown) {
|
|
1606
|
-
this.
|
|
1781
|
+
this.overlayPosition.update();
|
|
1607
1782
|
this.setExpanded(true);
|
|
1608
1783
|
}
|
|
1609
1784
|
};
|
|
@@ -1619,7 +1794,7 @@ class NavLink extends Base {
|
|
|
1619
1794
|
}
|
|
1620
1795
|
this.updateDropdownState();
|
|
1621
1796
|
if (this.hasDropdown) {
|
|
1622
|
-
this.
|
|
1797
|
+
this.overlayPosition.update();
|
|
1623
1798
|
this.setExpanded(true);
|
|
1624
1799
|
}
|
|
1625
1800
|
};
|
|
@@ -1635,12 +1810,7 @@ class NavLink extends Base {
|
|
|
1635
1810
|
});
|
|
1636
1811
|
};
|
|
1637
1812
|
this.handleKeydown = (event) => {
|
|
1638
|
-
|
|
1639
|
-
this.expanded = false;
|
|
1640
|
-
this.pinned = false;
|
|
1641
|
-
this.keyboardDismissed = true;
|
|
1642
|
-
this.shadowRoot?.querySelector("button, a")?.focus();
|
|
1643
|
-
}
|
|
1813
|
+
this.overlayDismiss.handleKeydown(event);
|
|
1644
1814
|
};
|
|
1645
1815
|
}
|
|
1646
1816
|
static styles = [Base.styles, NavLink_default];
|
|
@@ -1663,26 +1833,21 @@ class NavLink extends Base {
|
|
|
1663
1833
|
tokens.add("noreferrer");
|
|
1664
1834
|
return Array.from(tokens).join(" ");
|
|
1665
1835
|
}
|
|
1666
|
-
disconnectedCallback() {
|
|
1667
|
-
document.removeEventListener("pointerdown", this.handleDocumentPointerDown);
|
|
1668
|
-
super.disconnectedCallback();
|
|
1669
|
-
}
|
|
1670
1836
|
updated(changedProperties) {
|
|
1671
1837
|
super.updated(changedProperties);
|
|
1672
1838
|
this.updateTheme(changedProperties, this.surface);
|
|
1673
1839
|
if (changedProperties.has("pinned")) {
|
|
1674
1840
|
if (this.pinned) {
|
|
1675
|
-
|
|
1841
|
+
this.overlayDismiss.start();
|
|
1676
1842
|
} else {
|
|
1677
|
-
|
|
1843
|
+
this.overlayDismiss.stop();
|
|
1678
1844
|
}
|
|
1679
1845
|
}
|
|
1680
1846
|
if (changedProperties.has("expanded")) {
|
|
1681
1847
|
if (this.expanded) {
|
|
1682
|
-
this.
|
|
1848
|
+
this.overlayPosition.start();
|
|
1683
1849
|
} else {
|
|
1684
|
-
this.
|
|
1685
|
-
this.clearDropdownAlignment();
|
|
1850
|
+
this.overlayPosition.stop();
|
|
1686
1851
|
}
|
|
1687
1852
|
}
|
|
1688
1853
|
}
|
|
@@ -1729,35 +1894,6 @@ class NavLink extends Base {
|
|
|
1729
1894
|
this.expanded = open;
|
|
1730
1895
|
this.emit("dsgn-nav-link-toggle", { open });
|
|
1731
1896
|
}
|
|
1732
|
-
clearDropdownAlignment() {
|
|
1733
|
-
this.style.removeProperty(dropdownShiftProperty);
|
|
1734
|
-
}
|
|
1735
|
-
measureDropdownWidth() {
|
|
1736
|
-
const panel = this.shadowRoot?.querySelector(`#${this.dropdownId}`);
|
|
1737
|
-
if (!panel) {
|
|
1738
|
-
return 0;
|
|
1739
|
-
}
|
|
1740
|
-
const openWidth = panel.getBoundingClientRect().width;
|
|
1741
|
-
if (openWidth) {
|
|
1742
|
-
return openWidth;
|
|
1743
|
-
}
|
|
1744
|
-
panel.style.cssText = "display:block;visibility:hidden";
|
|
1745
|
-
const width = panel.getBoundingClientRect().width;
|
|
1746
|
-
panel.style.cssText = "";
|
|
1747
|
-
return width;
|
|
1748
|
-
}
|
|
1749
|
-
dropdownShift(triggerRect) {
|
|
1750
|
-
const panelWidth = this.measureDropdownWidth();
|
|
1751
|
-
const viewportRight = window.innerWidth - dropdownViewportGutter;
|
|
1752
|
-
if (!panelWidth || triggerRect.left + panelWidth <= viewportRight) {
|
|
1753
|
-
return 0;
|
|
1754
|
-
}
|
|
1755
|
-
const flipped = triggerRect.width - panelWidth;
|
|
1756
|
-
return triggerRect.left + flipped >= dropdownViewportGutter ? flipped : dropdownViewportGutter - triggerRect.left;
|
|
1757
|
-
}
|
|
1758
|
-
updateDropdownAlignment() {
|
|
1759
|
-
this.style.setProperty(dropdownShiftProperty, `${this.dropdownShift(this.getBoundingClientRect())}px`);
|
|
1760
|
-
}
|
|
1761
1897
|
get hoverOpensDropdown() {
|
|
1762
1898
|
return this.breakpointController.isAtLeast("sm") && supportsHover();
|
|
1763
1899
|
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import '../../../types/global.d.ts';
|
|
2
|
+
import { type CSSResultGroup } from 'lit';
|
|
3
|
+
import { Base } from '../../../common/Base/Base.js';
|
|
4
|
+
import { type SurfaceType } from '../../../common/props/surface/surface.props.js';
|
|
5
|
+
import { type SelectOption, type SelectSizeType } from './Select.props.js';
|
|
6
|
+
/**
|
|
7
|
+
* Form-associated single-value select, following the WAI-ARIA APG select-only
|
|
8
|
+
* combobox: a `<button role="combobox">` trigger that keeps DOM focus while
|
|
9
|
+
* the open `role="listbox"` is navigated through `aria-activedescendant`.
|
|
10
|
+
*
|
|
11
|
+
* Renders its own label, hint and error inside the shadow root, next to the
|
|
12
|
+
* trigger they describe (error read before hint). Inside a `<dsgn-field>`,
|
|
13
|
+
* Field supplies `label`, `hint`, `error`, `required`, `invalid` and `size`;
|
|
14
|
+
* see `common/helpers/field-control.ts`.
|
|
15
|
+
*
|
|
16
|
+
* The listbox is positioned against the trigger and the viewport: below by
|
|
17
|
+
* default, flipped above when there isn't room, and shifted to keep a 16px
|
|
18
|
+
* viewport gutter. It opens in the top layer (`popover="manual"`), so an
|
|
19
|
+
* ancestor's `overflow` or stacking context cannot clip or cover it; where the
|
|
20
|
+
* Popover API is missing it falls back to absolute positioning inside the
|
|
21
|
+
* component. Dismissal stays with the component (not popover light-dismiss),
|
|
22
|
+
* because focus never leaves the trigger.
|
|
23
|
+
*
|
|
24
|
+
* @fires dsgn-change - Fired when the user picks a different option; `detail` is `{ value, name }`. Not fired when the same option is picked again, or when `value` is set programmatically.
|
|
25
|
+
*
|
|
26
|
+
* @csspart label - The visible `<label>`.
|
|
27
|
+
* @csspart required-indicator - The `*` appended to the label when `required`. Hidden from assistive technology; `aria-required` on the trigger is what is announced.
|
|
28
|
+
* @csspart trigger - The combobox button.
|
|
29
|
+
* @csspart listbox - The floating option list.
|
|
30
|
+
* @csspart option - Each option in the list.
|
|
31
|
+
* @csspart hint - Persistent hint text below the trigger.
|
|
32
|
+
* @csspart error - Error message, icon and text, below the hint.
|
|
33
|
+
*/
|
|
34
|
+
export declare class Select extends Base {
|
|
35
|
+
#private;
|
|
36
|
+
static styles: CSSResultGroup;
|
|
37
|
+
static formAssociated: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* `focus()` on the host (and a click on a `<label>` pointing at it) moves
|
|
40
|
+
* focus to the trigger.
|
|
41
|
+
*/
|
|
42
|
+
static shadowRootOptions: ShadowRootInit;
|
|
43
|
+
constructor();
|
|
44
|
+
surface: SurfaceType;
|
|
45
|
+
size: SelectSizeType;
|
|
46
|
+
/**
|
|
47
|
+
* The choices, in order. Set as a property, or as a JSON attribute in
|
|
48
|
+
* plain HTML.
|
|
49
|
+
*/
|
|
50
|
+
options: SelectOption[];
|
|
51
|
+
/**
|
|
52
|
+
* The selected option's `value`, or `''` for none. Not reflected, matching
|
|
53
|
+
* `dsgn-input`: the value at first render is the default `form.reset()`
|
|
54
|
+
* returns to.
|
|
55
|
+
*/
|
|
56
|
+
value: string;
|
|
57
|
+
/**
|
|
58
|
+
* Field name under which the value is submitted. Reflected because the
|
|
59
|
+
* browser builds a form's entry list from the `name` content attribute.
|
|
60
|
+
*/
|
|
61
|
+
name: string;
|
|
62
|
+
/** Shown in the trigger while no option is selected. */
|
|
63
|
+
placeholder: string;
|
|
64
|
+
label: string;
|
|
65
|
+
/**
|
|
66
|
+
* Accessible name for a select with no visible `label`. Ignored when `label`
|
|
67
|
+
* is set, so the visible text and the announced name cannot disagree.
|
|
68
|
+
*/
|
|
69
|
+
ariaLabel: string;
|
|
70
|
+
/** Supporting text below the trigger. Stays visible alongside an error. */
|
|
71
|
+
hint: string;
|
|
72
|
+
/** Error message. A non-empty value also marks the select invalid. */
|
|
73
|
+
error: string;
|
|
74
|
+
/** Marks the select invalid without a message. */
|
|
75
|
+
invalid: boolean;
|
|
76
|
+
disabled: boolean;
|
|
77
|
+
required: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Disabled state inherited from an ancestor `<fieldset>`, kept apart from
|
|
80
|
+
* the authored `disabled` so re-enabling the fieldset is never stuck (the
|
|
81
|
+
* LL-130 fix; see `dsgn-input`).
|
|
82
|
+
*/
|
|
83
|
+
private _formDisabled;
|
|
84
|
+
/** Whether the listbox is open (drives `aria-expanded`). */
|
|
85
|
+
private _open;
|
|
86
|
+
/** Whether the listbox is displayed — stays true through the exit animation. */
|
|
87
|
+
private _visible;
|
|
88
|
+
/** Index of the highlighted option, or -1. */
|
|
89
|
+
private _activeIndex;
|
|
90
|
+
private _placement;
|
|
91
|
+
private _trigger;
|
|
92
|
+
private _listbox;
|
|
93
|
+
/** The value `form.reset()` restores, captured at first render. */
|
|
94
|
+
private _defaultValue;
|
|
95
|
+
private readonly _typeahead;
|
|
96
|
+
private readonly _motion;
|
|
97
|
+
/** Invalidates a pending exit when the listbox reopens. */
|
|
98
|
+
private _exitRun;
|
|
99
|
+
/** Fixed at construction: the listbox's rendering mode never changes. */
|
|
100
|
+
private readonly _topLayer;
|
|
101
|
+
/** Whether this component has the listbox showing in the top layer. */
|
|
102
|
+
private _inTopLayer;
|
|
103
|
+
private _listboxScrollTop;
|
|
104
|
+
private readonly _position;
|
|
105
|
+
private readonly _dismiss;
|
|
106
|
+
private get _isDisabled();
|
|
107
|
+
private get _isInvalid();
|
|
108
|
+
private get _selectedIndex();
|
|
109
|
+
private get _state();
|
|
110
|
+
/** The selected option if it is enabled, otherwise the first enabled one. */
|
|
111
|
+
private get _openingIndex();
|
|
112
|
+
private _openAt;
|
|
113
|
+
private _close;
|
|
114
|
+
private _pick;
|
|
115
|
+
/** Space types into an active typeahead search instead of opening/picking. */
|
|
116
|
+
private _isTypeahead;
|
|
117
|
+
private _search;
|
|
118
|
+
/** Returns whether the key was handled (and its default should be blocked). */
|
|
119
|
+
private _handleClosedKey;
|
|
120
|
+
private _handleOpenKey;
|
|
121
|
+
private _handleKeydown;
|
|
122
|
+
/** Firefox activates a button on Space keyup; the keydown already acted. */
|
|
123
|
+
private _handleKeyup;
|
|
124
|
+
private _handleTriggerClick;
|
|
125
|
+
private _handleOptionClick;
|
|
126
|
+
private _handleOptionEnter;
|
|
127
|
+
/** Keeps focus on the trigger when the listbox is pressed. */
|
|
128
|
+
private _keepFocus;
|
|
129
|
+
private _handleBlur;
|
|
130
|
+
private _syncValidity;
|
|
131
|
+
formResetCallback(): void;
|
|
132
|
+
formDisabledCallback(disabled: boolean): void;
|
|
133
|
+
formStateRestoreCallback(restored: string | File | FormData | null): void;
|
|
134
|
+
firstUpdated(): void;
|
|
135
|
+
disconnectedCallback(): void;
|
|
136
|
+
willUpdate(changedProperties: Map<string | number | symbol, unknown>): void;
|
|
137
|
+
updated(changedProperties: Map<string | number | symbol, unknown>): void;
|
|
138
|
+
private _showListbox;
|
|
139
|
+
private _hideListbox;
|
|
140
|
+
private _handleOpenChange;
|
|
141
|
+
private _scrollActiveIntoView;
|
|
142
|
+
private _renderLabel;
|
|
143
|
+
private _renderHint;
|
|
144
|
+
private _renderError;
|
|
145
|
+
private _renderOption;
|
|
146
|
+
/** Name sources shared by the trigger and the listbox. */
|
|
147
|
+
private get _labelledBy();
|
|
148
|
+
private get _fallbackName();
|
|
149
|
+
private _renderTrigger;
|
|
150
|
+
private _renderListbox;
|
|
151
|
+
render(): import("lit").TemplateResult<1>;
|
|
152
|
+
}
|
|
153
|
+
declare global {
|
|
154
|
+
interface HTMLElementTagNameMap {
|
|
155
|
+
'dsgn-select': Select;
|
|
156
|
+
}
|
|
157
|
+
}
|