@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
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
/** What dismissed an overlay. */
|
|
3
|
+
export type OverlayDismissReason = 'escape' | 'outside';
|
|
4
|
+
export interface OverlayDismissOptions {
|
|
5
|
+
/** Whether there is an open overlay for Escape to close. */
|
|
6
|
+
isOpen: () => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Close the overlay. Runs before focus is returned, so a host can record the
|
|
9
|
+
* dismissal first (e.g. to keep a focus-to-open trigger from re-opening).
|
|
10
|
+
*/
|
|
11
|
+
onDismiss: (reason: OverlayDismissReason) => void;
|
|
12
|
+
/** Where focus goes after an Escape dismissal — usually the trigger. */
|
|
13
|
+
returnFocusTo?: () => HTMLElement | null | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Dismisses an overlay the two standard ways: Escape (closing it and returning
|
|
17
|
+
* focus to its trigger) and a pointer press outside the host.
|
|
18
|
+
*
|
|
19
|
+
* **Escape** is not a global listener: the host routes its own `keydown`
|
|
20
|
+
* through `handleKeydown`, so the key only counts while focus is inside the
|
|
21
|
+
* component, and a host with a richer key handler (a listbox) decides where in
|
|
22
|
+
* that handler Escape is checked. It returns whether it dismissed, so the host
|
|
23
|
+
* can `preventDefault` or stop there.
|
|
24
|
+
*
|
|
25
|
+
* **Outside press** is a `pointerdown` listener on the document, present only
|
|
26
|
+
* between `start()` and `stop()` — a host decides when an outside press
|
|
27
|
+
* should matter (a nav dropdown only once pinned; a listbox whenever open).
|
|
28
|
+
* The press is tested against the event's composed path, so presses inside the
|
|
29
|
+
* host's shadow root count as inside. Like `LayoutChangeController`, the
|
|
30
|
+
* intent survives a disconnect and the listener returns on reconnect.
|
|
31
|
+
*/
|
|
32
|
+
export declare class OverlayDismissController implements ReactiveController {
|
|
33
|
+
private host;
|
|
34
|
+
private options;
|
|
35
|
+
private listening;
|
|
36
|
+
private wantsOutside;
|
|
37
|
+
constructor(host: ReactiveControllerHost & HTMLElement, options: OverlayDismissOptions);
|
|
38
|
+
/** Start closing on a pointer press outside the host. Idempotent. */
|
|
39
|
+
start(): void;
|
|
40
|
+
/** Stop closing on outside presses. Idempotent. */
|
|
41
|
+
stop(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Dismiss on Escape while open, then return focus. Returns `true` when the
|
|
44
|
+
* event dismissed the overlay.
|
|
45
|
+
*/
|
|
46
|
+
handleKeydown(event: KeyboardEvent): boolean;
|
|
47
|
+
hostConnected(): void;
|
|
48
|
+
hostDisconnected(): void;
|
|
49
|
+
private handleDocumentPointerDown;
|
|
50
|
+
private listen;
|
|
51
|
+
private unlisten;
|
|
52
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* Breathing room kept between an overlay and the viewport edge.
|
|
4
|
+
*/
|
|
5
|
+
export declare const overlayViewportGutter = 16;
|
|
6
|
+
/** Which edge of its anchor an overlay lines up with. */
|
|
7
|
+
export type OverlayAlign = 'start' | 'end';
|
|
8
|
+
/** Vertical side of the anchor an overlay hangs from. */
|
|
9
|
+
export type OverlaySide = 'bottom' | 'top';
|
|
10
|
+
/**
|
|
11
|
+
* Where an overlay hangs: its side of the anchor, and the anchor edge it lines
|
|
12
|
+
* up with before any shift (`-end` for a "⋯" trigger at the end of a row).
|
|
13
|
+
*/
|
|
14
|
+
export type OverlayPlacement = `${OverlaySide}-${OverlayAlign}`;
|
|
15
|
+
/**
|
|
16
|
+
* Where to put an overlay that is `position: fixed` against the viewport —
|
|
17
|
+
* for example one shown in the top layer with the Popover API, which escapes
|
|
18
|
+
* every ancestor's overflow clipping and stacking context but is no longer
|
|
19
|
+
* laid out relative to its anchor.
|
|
20
|
+
*/
|
|
21
|
+
export interface OverlayViewportPosition {
|
|
22
|
+
/** Distance from the viewport's left edge, shift included. */
|
|
23
|
+
left: number;
|
|
24
|
+
/** Distance from the viewport's top edge; set when hanging below. */
|
|
25
|
+
top: number | null;
|
|
26
|
+
/** Distance from the viewport's bottom edge; set when flipped above. */
|
|
27
|
+
bottom: number | null;
|
|
28
|
+
/** The anchor's width, for overlays that match it. */
|
|
29
|
+
anchorWidth: number;
|
|
30
|
+
}
|
|
31
|
+
export interface OverlayPosition {
|
|
32
|
+
/** The side the overlay hangs from, after any vertical flip. */
|
|
33
|
+
placement: OverlayPlacement;
|
|
34
|
+
/**
|
|
35
|
+
* Horizontal shift, measured from the start-aligned position under (or
|
|
36
|
+
* above) the anchor. See `computeOverlayShift`. This is all an overlay
|
|
37
|
+
* positioned against its anchor (`position: absolute`) needs.
|
|
38
|
+
*/
|
|
39
|
+
shift: number;
|
|
40
|
+
/** The same position in viewport coordinates, for a `fixed` overlay. */
|
|
41
|
+
viewport: OverlayViewportPosition;
|
|
42
|
+
}
|
|
43
|
+
export interface OverlaySize {
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
}
|
|
47
|
+
type AnchorRect = Pick<DOMRect, 'left' | 'width' | 'top' | 'bottom'>;
|
|
48
|
+
/** Room between the chosen panel edge and the viewport gutter. */
|
|
49
|
+
export declare const computeOverlayAvailableHeight: (viewport: OverlayViewportPosition, viewportHeight: number, gutter?: number) => number;
|
|
50
|
+
/**
|
|
51
|
+
* Viewport coordinates for a `fixed` overlay. A flipped overlay is pinned by
|
|
52
|
+
* its bottom edge, so it stays against the anchor however tall it renders.
|
|
53
|
+
*/
|
|
54
|
+
export declare const computeViewportPosition: (anchor: AnchorRect, { placement, shift }: Pick<OverlayPosition, "placement" | "shift">, viewportHeight: number, offset?: number) => OverlayViewportPosition;
|
|
55
|
+
/**
|
|
56
|
+
* Horizontal shift for an overlay, measured from the start-aligned position
|
|
57
|
+
* under its anchor. Hanging from the anchor's start edge is the default; if
|
|
58
|
+
* that would spill past the viewport, the overlay flips to end on the anchor's
|
|
59
|
+
* far edge instead. Only when *that* would leave the viewport too — an overlay
|
|
60
|
+
* wider than the room on either side of its anchor — does it give up the
|
|
61
|
+
* anchor and sit on the gutter, where a fluid width has already shrunk it to
|
|
62
|
+
* fit.
|
|
63
|
+
*
|
|
64
|
+
* `align: 'end'` mirrors this: the overlay ends on the anchor's end edge,
|
|
65
|
+
* flips to start-aligned when that would spill past the left gutter, and sits
|
|
66
|
+
* on the right gutter when neither fits.
|
|
67
|
+
*/
|
|
68
|
+
export declare const computeOverlayShift: (anchor: AnchorRect, overlayWidth: number, viewportWidth: number, gutter?: number, align?: OverlayAlign) => number;
|
|
69
|
+
/**
|
|
70
|
+
* Vertical side for an overlay. Below the anchor is the default; it flips
|
|
71
|
+
* above only when it doesn't fit below and does fit above. When it fits on
|
|
72
|
+
* neither side it takes whichever has more room, so a long list scrolls inside
|
|
73
|
+
* the larger space rather than the smaller one.
|
|
74
|
+
*/
|
|
75
|
+
export declare const computeOverlayPlacement: (anchor: AnchorRect, overlayHeight: number, viewportHeight: number, { gutter, offset, align, }?: {
|
|
76
|
+
gutter?: number;
|
|
77
|
+
offset?: number;
|
|
78
|
+
align?: OverlayAlign;
|
|
79
|
+
}) => OverlayPlacement;
|
|
80
|
+
/**
|
|
81
|
+
* Size an overlay renders at. A closed overlay is usually `display: none`, so
|
|
82
|
+
* lay it out just long enough to read it — the browser cannot paint between
|
|
83
|
+
* the two writes, so nothing flashes. The overlay's own inline style is
|
|
84
|
+
* restored afterwards.
|
|
85
|
+
*/
|
|
86
|
+
export declare const measureOverlay: (overlay: HTMLElement) => OverlaySize;
|
|
87
|
+
export interface OverlayPositionOptions {
|
|
88
|
+
/** The element the overlay hangs from. Defaults to the host. */
|
|
89
|
+
anchor?: () => Element | null | undefined;
|
|
90
|
+
/** The overlay element to measure. */
|
|
91
|
+
overlay: () => HTMLElement | null | undefined;
|
|
92
|
+
/** Write a computed position to the overlay (CSS custom properties, state). */
|
|
93
|
+
apply: (position: OverlayPosition) => void;
|
|
94
|
+
/** Undo `apply` when the overlay closes. */
|
|
95
|
+
clear?: () => void;
|
|
96
|
+
/**
|
|
97
|
+
* Runs before the overlay is measured, with the anchor's box. A `fixed`
|
|
98
|
+
* overlay that sizes itself from its anchor (rather than from a containing
|
|
99
|
+
* block) sets that size here, so the measurement sees its real width.
|
|
100
|
+
*/
|
|
101
|
+
beforeMeasure?: (anchor: DOMRect) => void;
|
|
102
|
+
/** Viewport gutter in px. Defaults to `overlayViewportGutter`. */
|
|
103
|
+
gutter?: number;
|
|
104
|
+
/** Gap between anchor and overlay, counted against the room on each side. */
|
|
105
|
+
offset?: number;
|
|
106
|
+
/**
|
|
107
|
+
* Flip above the anchor when there isn't room below. Off, the overlay always
|
|
108
|
+
* hangs below.
|
|
109
|
+
*/
|
|
110
|
+
flip?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* The anchor edge the overlay lines up with: `start` (default) or `end`.
|
|
113
|
+
* Read on every measurement, so a host can change it while open.
|
|
114
|
+
*/
|
|
115
|
+
align?: () => OverlayAlign;
|
|
116
|
+
/**
|
|
117
|
+
* Also re-measure on scroll (any scroll container, captured) and window
|
|
118
|
+
* resize, coalesced to one measurement per frame. Width changes are always
|
|
119
|
+
* tracked through the shared layout observer; this adds the height and
|
|
120
|
+
* scroll changes a vertical flip depends on.
|
|
121
|
+
*/
|
|
122
|
+
trackViewport?: boolean;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Positions an overlay against its anchor and the viewport while it is open:
|
|
126
|
+
* start-aligned (or, with `align`, end-aligned) under the anchor, shifted to
|
|
127
|
+
* stay inside the viewport gutter,
|
|
128
|
+
* and optionally flipped above when there isn't room below.
|
|
129
|
+
*
|
|
130
|
+
* Each position is reported two ways: as a `shift` for an overlay laid out
|
|
131
|
+
* against its anchor (`position: absolute` in a shared containing block), and
|
|
132
|
+
* as `viewport` coordinates for a `fixed` overlay, such as one in the top
|
|
133
|
+
* layer. A top-layer overlay should also set `trackViewport`, since it no
|
|
134
|
+
* longer moves with the page on its own.
|
|
135
|
+
*
|
|
136
|
+
* `start()` measures and subscribes to layout changes; `stop()` unsubscribes
|
|
137
|
+
* and calls `clear`, so the next open always starts from a measurement rather
|
|
138
|
+
* than a stale one. `update()` re-measures on demand — call it before opening
|
|
139
|
+
* if the overlay must land in place on its first frame.
|
|
140
|
+
*
|
|
141
|
+
* The subscription is the shared observer in `helpers/layout-change.ts` (the
|
|
142
|
+
* document width and the host's own box), plus scroll/resize listeners when
|
|
143
|
+
* `trackViewport` is on. Like `LayoutChangeController`, the intent survives a
|
|
144
|
+
* disconnect: a host moved in the DOM while open resubscribes on reconnect.
|
|
145
|
+
*/
|
|
146
|
+
export declare class OverlayPositionController implements ReactiveController {
|
|
147
|
+
private host;
|
|
148
|
+
private options;
|
|
149
|
+
private unsubscribe?;
|
|
150
|
+
private wantsUpdates;
|
|
151
|
+
private frame?;
|
|
152
|
+
private _position?;
|
|
153
|
+
constructor(host: ReactiveControllerHost & HTMLElement, options: OverlayPositionOptions);
|
|
154
|
+
/** The last applied position, or `undefined` while stopped. */
|
|
155
|
+
get position(): OverlayPosition | undefined;
|
|
156
|
+
/** Measure now and keep measuring on layout changes. Idempotent. */
|
|
157
|
+
start(): void;
|
|
158
|
+
/** Stop measuring and clear the applied position. Idempotent. */
|
|
159
|
+
stop(): void;
|
|
160
|
+
/** Measure and apply the position once. */
|
|
161
|
+
update: () => void;
|
|
162
|
+
hostConnected(): void;
|
|
163
|
+
hostDisconnected(): void;
|
|
164
|
+
private scheduleUpdate;
|
|
165
|
+
private subscribe;
|
|
166
|
+
private unsubscribeAll;
|
|
167
|
+
}
|
|
168
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
/** Which arrow keys move between items. */
|
|
3
|
+
export type RovingFocusOrientation = 'vertical' | 'horizontal' | 'both';
|
|
4
|
+
export interface RovingFocusOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The items, in order — every element that could take focus, including any
|
|
7
|
+
* the controller should skip. Read on each key press, so a list that
|
|
8
|
+
* re-renders is never stale.
|
|
9
|
+
*/
|
|
10
|
+
items: () => readonly HTMLElement[];
|
|
11
|
+
/**
|
|
12
|
+
* Items arrow keys and typeahead pass over, such as separators. Defaults to
|
|
13
|
+
* `role="separator"`. Disabled items are *not* skipped: in the APG menu,
|
|
14
|
+
* toolbar and tablist patterns they stay focusable so they can be
|
|
15
|
+
* discovered, and the host refuses to activate them.
|
|
16
|
+
*/
|
|
17
|
+
isSkipped?: (item: HTMLElement) => boolean;
|
|
18
|
+
/** Arrow keys that move focus. Defaults to `vertical` (↑ ↓). */
|
|
19
|
+
orientation?: RovingFocusOrientation;
|
|
20
|
+
/** Whether moving past either end wraps around. Defaults to `true`. */
|
|
21
|
+
wrap?: boolean;
|
|
22
|
+
/** Text matched by typeahead. Defaults to the trimmed `textContent`. */
|
|
23
|
+
labelOf?: (item: HTMLElement) => string;
|
|
24
|
+
/** Whether typing a character moves to a matching item. Defaults to `true`. */
|
|
25
|
+
typeahead?: boolean;
|
|
26
|
+
/** How long typed characters accumulate. Defaults to `typeaheadTimeout`. */
|
|
27
|
+
typeaheadTimeout?: number;
|
|
28
|
+
/** Runs after focus lands on an item through the controller or a `focusin`. */
|
|
29
|
+
onFocusChange?: (item: HTMLElement, index: number) => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Roving focus over a group of items — the keyboard model shared by the
|
|
33
|
+
* WAI-ARIA APG menu, toolbar and tablist patterns. DOM focus moves between the
|
|
34
|
+
* items themselves (unlike a listbox's `aria-activedescendant`), and exactly
|
|
35
|
+
* one item is the group's tab stop (`tabindex="0"`, the rest `-1`).
|
|
36
|
+
*
|
|
37
|
+
* Keys are not a global listener: the host routes its own `keydown` through
|
|
38
|
+
* `handleKeydown`, which returns whether it handled the key so the host can
|
|
39
|
+
* `preventDefault` or stop there — the same contract as
|
|
40
|
+
* `OverlayDismissController`. Arrow keys (per `orientation`), Home and End
|
|
41
|
+
* move; printable characters run typeahead (first-letter match, refined while
|
|
42
|
+
* typing within the timeout). Space is only taken while a typeahead search is
|
|
43
|
+
* active, so a host can still use it to activate.
|
|
44
|
+
*
|
|
45
|
+
* Activation is the host's job, so this controller has no notion of
|
|
46
|
+
* "disabled": disabled items are focused like any other, and the host checks
|
|
47
|
+
* `aria-disabled` before acting.
|
|
48
|
+
*
|
|
49
|
+
* Focus that arrives some other way — a click, or a host focusing an item on
|
|
50
|
+
* hover — is picked up through `focusin`, so the tab stop and `activeIndex`
|
|
51
|
+
* always follow the focused item.
|
|
52
|
+
*/
|
|
53
|
+
export declare class RovingFocusController implements ReactiveController {
|
|
54
|
+
private host;
|
|
55
|
+
private options;
|
|
56
|
+
private readonly typeahead;
|
|
57
|
+
private current;
|
|
58
|
+
constructor(host: ReactiveControllerHost & HTMLElement, options: RovingFocusOptions);
|
|
59
|
+
/** Index of the current item among `items()`, or `-1`. */
|
|
60
|
+
get activeIndex(): number;
|
|
61
|
+
/** The current item — the tab stop — or `null`. */
|
|
62
|
+
get activeItem(): HTMLElement | null;
|
|
63
|
+
/** Whether characters are being collected into a typeahead search. */
|
|
64
|
+
get typeaheadActive(): boolean;
|
|
65
|
+
/** Focus the first item that is not skipped. Returns it, or `null`. */
|
|
66
|
+
focusFirst(): HTMLElement | null;
|
|
67
|
+
/** Focus the last item that is not skipped. Returns it, or `null`. */
|
|
68
|
+
focusLast(): HTMLElement | null;
|
|
69
|
+
/** Focus `item` (ignored if it is skipped or not an item). */
|
|
70
|
+
focusItem(item: HTMLElement): HTMLElement | null;
|
|
71
|
+
/**
|
|
72
|
+
* Make one item the tab stop (the current one, else the first) and every
|
|
73
|
+
* other item `tabindex="-1"`. Skipped items get no `tabindex` at all: a
|
|
74
|
+
* focusable separator reads as a slider. Call after the items render.
|
|
75
|
+
*/
|
|
76
|
+
syncTabStops(): void;
|
|
77
|
+
/** Forget the current item and any typeahead in progress. */
|
|
78
|
+
reset(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Move focus for an arrow, Home, End or typeahead key. Returns `true` when
|
|
81
|
+
* the key was handled.
|
|
82
|
+
*/
|
|
83
|
+
handleKeydown(event: KeyboardEvent): boolean;
|
|
84
|
+
hostConnected(): void;
|
|
85
|
+
hostDisconnected(): void;
|
|
86
|
+
private navigable;
|
|
87
|
+
private isTypeaheadKey;
|
|
88
|
+
/**
|
|
89
|
+
* The item a navigation key moves to; `null` when the key is handled but
|
|
90
|
+
* focus stays (no wrap at an end), `undefined` when it is not a navigation
|
|
91
|
+
* key.
|
|
92
|
+
*/
|
|
93
|
+
private targetFor;
|
|
94
|
+
/** One item on from the current one, wrapping if allowed. */
|
|
95
|
+
private step;
|
|
96
|
+
private focusAt;
|
|
97
|
+
private setCurrent;
|
|
98
|
+
private handleFocusIn;
|
|
99
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SizeType } from '../props/size/size.props.js';
|
|
2
|
+
/**
|
|
3
|
+
* The labelling contract between `dsgn-field` and a form control.
|
|
4
|
+
*
|
|
5
|
+
* `aria-labelledby` and `aria-describedby` cannot reference IDs across shadow
|
|
6
|
+
* roots, so Field cannot point its slotted control at a label it renders
|
|
7
|
+
* itself. Instead Field writes these properties onto the control, and the
|
|
8
|
+
* control renders the label, hint and error inside its own shadow root, next
|
|
9
|
+
* to the native element they describe, and wires the ARIA there.
|
|
10
|
+
*
|
|
11
|
+
* A control adopts the contract by declaring these reactive properties and
|
|
12
|
+
* calling `markFieldControl` on its class.
|
|
13
|
+
*/
|
|
14
|
+
export interface FieldControl extends HTMLElement {
|
|
15
|
+
label: string;
|
|
16
|
+
hint: string;
|
|
17
|
+
error: string;
|
|
18
|
+
required: boolean;
|
|
19
|
+
/** Set by Field from a non-empty `error`; a control may also set it alone. */
|
|
20
|
+
invalid: boolean;
|
|
21
|
+
size: SizeType;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Brands a control class as implementing `FieldControl`. Kept off the class
|
|
25
|
+
* body so the brand never surfaces as a public member in the custom elements
|
|
26
|
+
* manifest or the generated React wrapper.
|
|
27
|
+
*/
|
|
28
|
+
export declare const markFieldControl: (ctor: CustomElementConstructor) => void;
|
|
29
|
+
export declare const isFieldControl: (element: Element) => element is FieldControl;
|
|
30
|
+
/**
|
|
31
|
+
* `aria-describedby` value for a control: the error is read before the hint,
|
|
32
|
+
* and only IDs of rendered elements are included. `undefined` when there is
|
|
33
|
+
* nothing to reference, so the attribute is omitted rather than left empty.
|
|
34
|
+
*/
|
|
35
|
+
export declare const describedBy: (...ids: Array<string | false | null | undefined>) => string | undefined;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard navigation over a flat list of items, some of them disabled — the
|
|
3
|
+
* shared part of the WAI-ARIA APG listbox and menu patterns. Indices are into
|
|
4
|
+
* the full list; `-1` means "no item".
|
|
5
|
+
*/
|
|
6
|
+
export interface NavigableItem {
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The next enabled index from `from` in direction `dir`, without wrapping.
|
|
11
|
+
* Stays on `from` when there is no enabled item that way.
|
|
12
|
+
*/
|
|
13
|
+
export declare const stepEnabled: (items: readonly NavigableItem[], from: number, dir: 1 | -1) => number;
|
|
14
|
+
/** The first enabled index, or `-1`. */
|
|
15
|
+
export declare const firstEnabled: (items: readonly NavigableItem[]) => number;
|
|
16
|
+
/** The last enabled index, or `-1`. */
|
|
17
|
+
export declare const lastEnabled: (items: readonly NavigableItem[]) => number;
|
|
18
|
+
/**
|
|
19
|
+
* Whether a key event types a character, so it should feed typeahead rather
|
|
20
|
+
* than act as a command.
|
|
21
|
+
*/
|
|
22
|
+
export declare const isPrintableKey: (event: KeyboardEvent) => boolean;
|
|
23
|
+
/** How long typed characters keep accumulating into one search. */
|
|
24
|
+
export declare const typeaheadTimeout = 500;
|
|
25
|
+
/**
|
|
26
|
+
* Accumulates typed characters for typeahead and finds the matching item.
|
|
27
|
+
*
|
|
28
|
+
* A single character searches from the item after `from`, so pressing the
|
|
29
|
+
* same letter repeatedly cycles through the items that start with it. Longer
|
|
30
|
+
* buffers search from `from` itself, so typing "sv" refines the match on "s"
|
|
31
|
+
* instead of skipping past it. The search wraps and skips disabled items.
|
|
32
|
+
*/
|
|
33
|
+
export declare class TypeaheadBuffer {
|
|
34
|
+
private readonly timeout;
|
|
35
|
+
private buffer;
|
|
36
|
+
private timer?;
|
|
37
|
+
constructor(timeout?: number);
|
|
38
|
+
/** Whether characters are currently being collected. */
|
|
39
|
+
get active(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Add `char` to the buffer and return the matching index, or `-1`.
|
|
42
|
+
* `from` is the current item; `-1` starts the search at the top. With
|
|
43
|
+
* `cycle: false` the search always includes `from` itself, which is what a
|
|
44
|
+
* closed select wants: typing the selected option's letter keeps it.
|
|
45
|
+
*/
|
|
46
|
+
search<T extends NavigableItem>(items: readonly T[], char: string, from: number, labelOf: (item: T) => string, { cycle }?: {
|
|
47
|
+
cycle?: boolean;
|
|
48
|
+
}): number;
|
|
49
|
+
clear(): void;
|
|
50
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The control size scale shared by form controls and `dsgn-field`, so a Field
|
|
3
|
+
* and the control inside it always agree on what `sm`, `md` and `lg` mean.
|
|
4
|
+
*/
|
|
5
|
+
export declare const sizeValues: {
|
|
6
|
+
readonly SM: "sm";
|
|
7
|
+
readonly MD: "md";
|
|
8
|
+
readonly LG: "lg";
|
|
9
|
+
};
|
|
10
|
+
export type SizeType = (typeof sizeValues)[keyof typeof sizeValues];
|
|
11
|
+
export declare const sizeProps: import("lit").PropertyDeclaration<unknown, unknown>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { OverlayPlacement } from '../controllers/OverlayPositionController.js';
|
|
2
|
+
/**
|
|
3
|
+
* Class lists shared by the floating panels — `dsgn-select`'s listbox and
|
|
4
|
+
* `dsgn-dropdown-menu`'s menu — and the rows inside them, so the two read as
|
|
5
|
+
* one overlay layer.
|
|
6
|
+
*
|
|
7
|
+
* A component that imports this module has it added to its Tailwind sources
|
|
8
|
+
* by the build (see `sharedStyleSources` in `build/build-component.ts`), so
|
|
9
|
+
* every class named here must stay a literal string.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Position written on the host by the position controller and inherited by
|
|
13
|
+
* the panel (a top-layer element still inherits through the DOM tree).
|
|
14
|
+
* `shift` places the anchored fallback; the rest place a top-layer panel in
|
|
15
|
+
* viewport coordinates.
|
|
16
|
+
*/
|
|
17
|
+
export declare const overlayPositionProperties: {
|
|
18
|
+
readonly shift: "--dsgn-overlay-shift";
|
|
19
|
+
readonly left: "--dsgn-overlay-left";
|
|
20
|
+
readonly top: "--dsgn-overlay-top";
|
|
21
|
+
readonly bottom: "--dsgn-overlay-bottom";
|
|
22
|
+
readonly width: "--dsgn-overlay-width";
|
|
23
|
+
readonly maxHeight: "--dsgn-overlay-max-height";
|
|
24
|
+
};
|
|
25
|
+
/** Gap between trigger and panel; matches `calc(100% + 4px)` below. */
|
|
26
|
+
export declare const overlayPanelOffset = 4;
|
|
27
|
+
export interface OverlayPanelState {
|
|
28
|
+
/** Displayed — stays true through the exit animation. */
|
|
29
|
+
visible: boolean;
|
|
30
|
+
/** Open — false while the exit animation runs. */
|
|
31
|
+
open: boolean;
|
|
32
|
+
/** Shown in the top layer rather than anchored in the component. */
|
|
33
|
+
topLayer: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The panel surface: popover background, border, radius, padding, shadow and
|
|
37
|
+
* item gap, positioned for its mode and placement. Width is the caller's.
|
|
38
|
+
*/
|
|
39
|
+
export declare const overlayPanelClasses: ({ visible, open, topLayer }: OverlayPanelState, placement: OverlayPlacement) => string[];
|
|
40
|
+
/**
|
|
41
|
+
* A row in a panel: layout, radius and the shared highlight. Mouse hover and
|
|
42
|
+
* keyboard highlight are the same state — hovering moves the active row — so
|
|
43
|
+
* one style covers both. Height, text size and gap are the caller's.
|
|
44
|
+
*
|
|
45
|
+
* `tone` replaces the enabled text colour and highlight fill (a destructive
|
|
46
|
+
* menu item), so two colour utilities never compete on one row.
|
|
47
|
+
*/
|
|
48
|
+
export declare const overlayItemClasses: ({ active, disabled, tone, }: {
|
|
49
|
+
active: boolean;
|
|
50
|
+
disabled: boolean;
|
|
51
|
+
tone?: {
|
|
52
|
+
text: string;
|
|
53
|
+
activeFill: string;
|
|
54
|
+
};
|
|
55
|
+
}) => string[];
|
|
@@ -16,6 +16,12 @@ export declare class Button extends Base {
|
|
|
16
16
|
* submission the way a native submit button does.
|
|
17
17
|
*/
|
|
18
18
|
static formAssociated: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* `focus()` on the host moves focus to the inner control, so a component
|
|
21
|
+
* that returns focus to a slotted trigger (a menu closing on Escape) lands
|
|
22
|
+
* on the real button.
|
|
23
|
+
*/
|
|
24
|
+
static shadowRootOptions: ShadowRootInit;
|
|
19
25
|
constructor();
|
|
20
26
|
/**
|
|
21
27
|
* Surface theme variant (light or dark)
|
|
@@ -97,6 +103,20 @@ export declare class Button extends Base {
|
|
|
97
103
|
*/
|
|
98
104
|
name: string;
|
|
99
105
|
value: string;
|
|
106
|
+
/**
|
|
107
|
+
* Forwarded to the inner `<button>`, which is the element assistive
|
|
108
|
+
* technology sees: the button opens a popup of this type (`menu`, `listbox`,
|
|
109
|
+
* `dialog`…). A popup owner such as `dsgn-dropdown-menu` sets it as a
|
|
110
|
+
* property, so it lands on the real control rather than on the host. Ignored
|
|
111
|
+
* in link mode.
|
|
112
|
+
*/
|
|
113
|
+
ariaHasPopup: string;
|
|
114
|
+
/**
|
|
115
|
+
* Forwarded to the inner `<button>`: whether the popup it controls is open
|
|
116
|
+
* (`'true'`/`'false'`, empty for none). An expanded ghost button keeps its
|
|
117
|
+
* hover fill, so an open menu's trigger stays marked. Ignored in link mode.
|
|
118
|
+
*/
|
|
119
|
+
ariaExpanded: string;
|
|
100
120
|
/**
|
|
101
121
|
* Disabled state inherited from an ancestor `<fieldset>`/form owner, kept
|
|
102
122
|
* separate from the authored `disabled` property. Writing the inherited state
|
|
@@ -544,6 +544,16 @@ var styles2 = css2`/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.co
|
|
|
544
544
|
opacity: 50%;
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
|
+
.aria-expanded\\:bg-button-ghost-bg-hover {
|
|
548
|
+
&[aria-expanded="true"] {
|
|
549
|
+
background-color: var(--color-button-ghost-bg-hover);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
.aria-expanded\\:text-button-ghost-text-hover {
|
|
553
|
+
&[aria-expanded="true"] {
|
|
554
|
+
color: var(--color-button-ghost-text-hover);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
547
557
|
.motion-safe\\:animate-spin {
|
|
548
558
|
@media (prefers-reduced-motion: no-preference) {
|
|
549
559
|
animation: var(--animate-spin);
|
|
@@ -821,6 +831,8 @@ var appearanceClasses = {
|
|
|
821
831
|
"text-button-ghost-text",
|
|
822
832
|
"hover:text-button-ghost-text-hover",
|
|
823
833
|
"hover:bg-button-ghost-bg-hover",
|
|
834
|
+
"aria-expanded:text-button-ghost-text-hover",
|
|
835
|
+
"aria-expanded:bg-button-ghost-bg-hover",
|
|
824
836
|
"border-none"
|
|
825
837
|
],
|
|
826
838
|
"mono-solid": [
|
|
@@ -890,9 +902,15 @@ var classNames = ({
|
|
|
890
902
|
};
|
|
891
903
|
|
|
892
904
|
// components/Button/src/Button.ts
|
|
905
|
+
var optional = (value) => value || nothing;
|
|
906
|
+
|
|
893
907
|
class Button extends Base {
|
|
894
908
|
static styles = [Base.styles, Button_default];
|
|
895
909
|
static formAssociated = true;
|
|
910
|
+
static shadowRootOptions = {
|
|
911
|
+
...Base.shadowRootOptions,
|
|
912
|
+
delegatesFocus: true
|
|
913
|
+
};
|
|
896
914
|
#internals;
|
|
897
915
|
constructor() {
|
|
898
916
|
super();
|
|
@@ -910,6 +928,8 @@ class Button extends Base {
|
|
|
910
928
|
this.loading = false;
|
|
911
929
|
this.name = "";
|
|
912
930
|
this.value = "";
|
|
931
|
+
this.ariaHasPopup = "";
|
|
932
|
+
this.ariaExpanded = "";
|
|
913
933
|
this._formDisabled = false;
|
|
914
934
|
this.#internals = this.attachInternals();
|
|
915
935
|
}
|
|
@@ -1040,6 +1060,8 @@ class Button extends Base {
|
|
|
1040
1060
|
aria-disabled=${this.loading ? "true" : nothing}
|
|
1041
1061
|
aria-busy=${ariaBusy}
|
|
1042
1062
|
aria-label=${ariaLabel}
|
|
1063
|
+
aria-haspopup=${optional(this.ariaHasPopup)}
|
|
1064
|
+
aria-expanded=${optional(this.ariaExpanded)}
|
|
1043
1065
|
@click=${this._handleClick}
|
|
1044
1066
|
>
|
|
1045
1067
|
${content}
|
|
@@ -1092,6 +1114,12 @@ __legacyDecorateClassTS([
|
|
|
1092
1114
|
__legacyDecorateClassTS([
|
|
1093
1115
|
property({ type: String, reflect: true })
|
|
1094
1116
|
], Button.prototype, "value", undefined);
|
|
1117
|
+
__legacyDecorateClassTS([
|
|
1118
|
+
property({ type: String, attribute: "aria-haspopup" })
|
|
1119
|
+
], Button.prototype, "ariaHasPopup", undefined);
|
|
1120
|
+
__legacyDecorateClassTS([
|
|
1121
|
+
property({ type: String, attribute: "aria-expanded" })
|
|
1122
|
+
], Button.prototype, "ariaExpanded", undefined);
|
|
1095
1123
|
__legacyDecorateClassTS([
|
|
1096
1124
|
state()
|
|
1097
1125
|
], Button.prototype, "_formDisabled", undefined);
|