@neuravision/construct 2.0.2 → 2.1.1
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/CHANGELOG.md +272 -0
- package/README.md +148 -152
- package/behaviors/combobox.js +447 -0
- package/behaviors/drawer.js +18 -0
- package/behaviors/dropdown.js +296 -0
- package/behaviors/index.d.ts +333 -0
- package/behaviors/index.js +9 -0
- package/behaviors/internal/dialog-controller.js +351 -0
- package/behaviors/internal/dom.js +377 -0
- package/behaviors/internal/overlay.js +241 -0
- package/behaviors/internal/popup-layer.js +188 -0
- package/behaviors/modal.js +17 -0
- package/behaviors/popover.js +269 -0
- package/behaviors/select-menu.js +326 -0
- package/behaviors/tabs.js +230 -0
- package/behaviors/toggle-group.js +234 -0
- package/behaviors/tooltip.js +233 -0
- package/components/README.md +279 -83
- package/components/_shared.css +11 -10
- package/components/_shell-shared.css +20 -4
- package/components/app-shell-v2.css +21 -14
- package/components/app-shell.css +23 -14
- package/components/badge.css +15 -0
- package/components/button.css +103 -15
- package/components/card.css +53 -8
- package/components/checkbox.css +3 -2
- package/components/chip.css +1 -1
- package/components/datepicker.css +3 -1
- package/components/datum.css +204 -0
- package/components/index.css +5 -0
- package/components/meter.css +182 -0
- package/components/navbar.css +88 -3
- package/components/slider.css +2 -1
- package/components/status.css +107 -0
- package/components/switch.css +4 -3
- package/components/tooltip.css +4 -0
- package/docs/architecture.md +79 -0
- package/docs/behaviors.md +101 -0
- package/docs/fonts.md +70 -0
- package/docs/governance.md +85 -0
- package/docs/guidelines.md +42 -23
- package/examples/react/README.md +20 -0
- package/examples/react/SettingsModal.jsx +66 -0
- package/examples/react/construct.css +5 -0
- package/fonts.css +10 -0
- package/foundations.css +8 -1
- package/package.json +72 -21
- package/schemas/components.schema.json +107 -0
- package/schemas/examples/theme.example.json +27 -0
- package/schemas/primitives.schema.json +135 -0
- package/schemas/semantic.schema.json +223 -0
- package/schemas/theme.schema.json +63 -0
- package/scripts/build-tokens.mjs +1417 -0
- package/scripts/construct-theme.mjs +43 -0
- package/scripts/validate-tokens.mjs +25 -0
- package/standalone/accordion.css +3 -0
- package/standalone/alert.css +3 -0
- package/standalone/app-shell-v2.css +4 -0
- package/standalone/app-shell.css +4 -0
- package/standalone/avatar.css +3 -0
- package/standalone/badge.css +3 -0
- package/standalone/banner.css +3 -0
- package/standalone/breadcrumbs.css +3 -0
- package/standalone/button.css +3 -0
- package/standalone/card.css +3 -0
- package/standalone/chart.css +3 -0
- package/standalone/checkbox.css +3 -0
- package/standalone/chip.css +3 -0
- package/standalone/combobox.css +3 -0
- package/standalone/core.css +4 -0
- package/standalone/data-table.css +3 -0
- package/standalone/datepicker.css +3 -0
- package/standalone/datum.css +3 -0
- package/standalone/divider.css +3 -0
- package/standalone/drawer.css +3 -0
- package/standalone/dropdown.css +3 -0
- package/standalone/empty-state.css +3 -0
- package/standalone/field.css +3 -0
- package/standalone/file-upload.css +3 -0
- package/standalone/icon.css +3 -0
- package/standalone/input.css +3 -0
- package/standalone/list.css +3 -0
- package/standalone/meter.css +3 -0
- package/standalone/modal.css +3 -0
- package/standalone/navbar.css +3 -0
- package/standalone/pagination.css +3 -0
- package/standalone/popover.css +3 -0
- package/standalone/progress-bar.css +3 -0
- package/standalone/select-menu.css +3 -0
- package/standalone/select.css +3 -0
- package/standalone/sidebar.css +3 -0
- package/standalone/skeleton.css +3 -0
- package/standalone/skip-link.css +3 -0
- package/standalone/slider.css +3 -0
- package/standalone/spinner.css +3 -0
- package/standalone/status.css +3 -0
- package/standalone/switch.css +3 -0
- package/standalone/table.css +3 -0
- package/standalone/tabs.css +3 -0
- package/standalone/textarea.css +3 -0
- package/standalone/toast.css +3 -0
- package/standalone/toggle-group.css +3 -0
- package/standalone/toolbar.css +3 -0
- package/standalone/tooltip.css +3 -0
- package/standalone/tree.css +3 -0
- package/tokens/README.md +130 -126
- package/tokens/components.json +54 -0
- package/tokens/primitives.json +268 -0
- package/tokens/semantic.dark.json +93 -0
- package/tokens/semantic.high-contrast.json +93 -0
- package/tokens/semantic.light.json +178 -0
- package/tokens/tokens.css +532 -28
- package/tokens/tokens.js +335 -25
- package/tokens/tokens.json +764 -21
- package/tokens/tokens.ts +335 -25
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertDescendants,
|
|
3
|
+
assertElement,
|
|
4
|
+
assertElements,
|
|
5
|
+
assertFunction,
|
|
6
|
+
assertSameDocument,
|
|
7
|
+
assertUniqueElements,
|
|
8
|
+
boundaryEnabledIndex,
|
|
9
|
+
createAttributeStore,
|
|
10
|
+
createControllerLifecycle,
|
|
11
|
+
createDisposables,
|
|
12
|
+
createTypeahead,
|
|
13
|
+
dispatch,
|
|
14
|
+
focusElement,
|
|
15
|
+
getItemValue,
|
|
16
|
+
isDisabled,
|
|
17
|
+
nextEnabledIndex,
|
|
18
|
+
} from './internal/dom.js';
|
|
19
|
+
import { createPopupLayer } from './internal/popup-layer.js';
|
|
20
|
+
|
|
21
|
+
const controllers = new WeakMap();
|
|
22
|
+
|
|
23
|
+
const MENU_ITEM_SELECTOR = [
|
|
24
|
+
'[role="menuitem"]',
|
|
25
|
+
'[role="menuitemcheckbox"]',
|
|
26
|
+
'[role="menuitemradio"]',
|
|
27
|
+
'[data-ct-menu-item]',
|
|
28
|
+
].join(',');
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a menu-button controller with roving focus, typeahead, dismissal, and selection events.
|
|
32
|
+
*/
|
|
33
|
+
export function createDropdownController(options) {
|
|
34
|
+
if (!options || typeof options !== 'object') {
|
|
35
|
+
throw new TypeError('Dropdown controller options are required.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const trigger = assertElement(options.trigger, 'options.trigger');
|
|
39
|
+
const existing = controllers.get(trigger);
|
|
40
|
+
if (existing) return existing;
|
|
41
|
+
const menu = assertElement(options.menu, 'options.menu');
|
|
42
|
+
const root = assertElement(options.root ?? menu.closest('.ct-dropdown') ?? menu, 'options.root');
|
|
43
|
+
const items = options.items == null
|
|
44
|
+
? [...menu.querySelectorAll(MENU_ITEM_SELECTOR)]
|
|
45
|
+
: assertElements(options.items, 'options.items');
|
|
46
|
+
if (!items.length) throw new TypeError('Dropdown controller requires at least one menu item.');
|
|
47
|
+
if (trigger.ownerDocument !== menu.ownerDocument) {
|
|
48
|
+
throw new TypeError('Dropdown trigger and menu must belong to the same document.');
|
|
49
|
+
}
|
|
50
|
+
assertSameDocument([root], trigger.ownerDocument, 'options.root');
|
|
51
|
+
assertUniqueElements(items, 'options.items');
|
|
52
|
+
assertSameDocument(items, trigger.ownerDocument, 'options.items');
|
|
53
|
+
assertDescendants(items, menu, 'options.items');
|
|
54
|
+
assertFunction(options.onSelect, 'options.onSelect');
|
|
55
|
+
assertFunction(options.onOpen, 'options.onOpen');
|
|
56
|
+
assertFunction(options.onClose, 'options.onClose');
|
|
57
|
+
|
|
58
|
+
const attributes = createAttributeStore();
|
|
59
|
+
const lifecycle = createControllerLifecycle('Dropdown');
|
|
60
|
+
const disposables = createDisposables();
|
|
61
|
+
const typeahead = createTypeahead({ timeout: options.typeaheadTimeout });
|
|
62
|
+
let controller;
|
|
63
|
+
let focusIndex = -1;
|
|
64
|
+
let pendingFocus = 'first';
|
|
65
|
+
|
|
66
|
+
attributes.set(menu, 'role', 'menu');
|
|
67
|
+
if (trigger.tagName === 'BUTTON' && !trigger.hasAttribute('type')) {
|
|
68
|
+
attributes.set(trigger, 'type', 'button');
|
|
69
|
+
}
|
|
70
|
+
items.forEach((item) => {
|
|
71
|
+
if (!item.hasAttribute('role')) attributes.set(item, 'role', 'menuitem');
|
|
72
|
+
attributes.set(item, 'tabindex', '-1');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
function enabledBoundary(fromEnd = false) {
|
|
76
|
+
return boundaryEnabledIndex(items, fromEnd);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function setHighlighted(index, { focus = true } = {}) {
|
|
80
|
+
if (index < 0 || index >= items.length || isDisabled(items[index])) return false;
|
|
81
|
+
focusIndex = index;
|
|
82
|
+
items.forEach((item, itemIndex) => {
|
|
83
|
+
attributes.set(item, 'data-highlighted', itemIndex === index ? '' : null);
|
|
84
|
+
});
|
|
85
|
+
if (focus) focusElement(items[index]);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function clearHighlight() {
|
|
90
|
+
focusIndex = -1;
|
|
91
|
+
items.forEach((item) => attributes.set(item, 'data-highlighted', null));
|
|
92
|
+
typeahead.reset();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const layer = createPopupLayer({
|
|
96
|
+
attributes,
|
|
97
|
+
closeOnEscape: options.closeOnEscape !== false,
|
|
98
|
+
closeOnFocusOutside: true,
|
|
99
|
+
closeOnOutside: options.closeOnOutside !== false,
|
|
100
|
+
disposables,
|
|
101
|
+
getController: () => controller,
|
|
102
|
+
hasPopup: 'menu',
|
|
103
|
+
handlesTab: true,
|
|
104
|
+
idPrefix: 'ct-dropdown-menu',
|
|
105
|
+
initialOpen: !isDisabled(trigger) && (options.open ?? root.getAttribute('data-state') === 'open'),
|
|
106
|
+
popup: menu,
|
|
107
|
+
stateElement: root,
|
|
108
|
+
trigger,
|
|
109
|
+
onOpen(detail, checkpoint) {
|
|
110
|
+
const index = pendingFocus === 'last' ? enabledBoundary(true) : enabledBoundary();
|
|
111
|
+
if (index >= 0) setHighlighted(index);
|
|
112
|
+
checkpoint();
|
|
113
|
+
options.onOpen?.(detail);
|
|
114
|
+
},
|
|
115
|
+
onClose(detail) {
|
|
116
|
+
clearHighlight();
|
|
117
|
+
options.onClose?.(detail);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
function open(reason = 'programmatic', { focus = 'first' } = {}) {
|
|
122
|
+
return lifecycle.run('open', (checkpoint) => {
|
|
123
|
+
if (!['first', 'last'].includes(focus)) {
|
|
124
|
+
throw new TypeError("focus must be 'first' or 'last'.");
|
|
125
|
+
}
|
|
126
|
+
if (isDisabled(trigger)) return false;
|
|
127
|
+
pendingFocus = focus;
|
|
128
|
+
const changed = layer.open(reason);
|
|
129
|
+
checkpoint();
|
|
130
|
+
if (!changed && layer.isOpen) {
|
|
131
|
+
const index = focus === 'last' ? enabledBoundary(true) : enabledBoundary();
|
|
132
|
+
if (index >= 0) setHighlighted(index);
|
|
133
|
+
}
|
|
134
|
+
return changed;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function selectItem(index, reason) {
|
|
139
|
+
return lifecycle.run('select', (checkpoint) => {
|
|
140
|
+
const item = items[index];
|
|
141
|
+
if (!item || isDisabled(item)) return false;
|
|
142
|
+
|
|
143
|
+
const detail = {
|
|
144
|
+
controller,
|
|
145
|
+
index,
|
|
146
|
+
item,
|
|
147
|
+
reason,
|
|
148
|
+
value: getItemValue(item, index),
|
|
149
|
+
};
|
|
150
|
+
const allowed = dispatch(menu, 'ct:beforeselect', detail, true);
|
|
151
|
+
checkpoint();
|
|
152
|
+
if (!allowed) return false;
|
|
153
|
+
|
|
154
|
+
const role = item.getAttribute('role');
|
|
155
|
+
if (role === 'menuitemcheckbox') {
|
|
156
|
+
attributes.set(item, 'aria-checked', String(item.getAttribute('aria-checked') !== 'true'));
|
|
157
|
+
} else if (role === 'menuitemradio') {
|
|
158
|
+
const group = item.closest('[role="group"]') ?? menu;
|
|
159
|
+
items.forEach((candidate) => {
|
|
160
|
+
if (
|
|
161
|
+
candidate.getAttribute('role') === 'menuitemradio' &&
|
|
162
|
+
(candidate.closest('[role="group"]') ?? menu) === group
|
|
163
|
+
) {
|
|
164
|
+
attributes.set(candidate, 'aria-checked', String(candidate === item));
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
options.onSelect?.(detail);
|
|
170
|
+
checkpoint();
|
|
171
|
+
dispatch(menu, 'ct:select', detail);
|
|
172
|
+
checkpoint();
|
|
173
|
+
const keepOpen = item.hasAttribute('data-ct-keep-open');
|
|
174
|
+
const defaultClose = role !== 'menuitemcheckbox';
|
|
175
|
+
if (!keepOpen && (options.closeOnSelect ?? defaultClose)) layer.close('select');
|
|
176
|
+
return true;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function onMenuKeyDown(event) {
|
|
181
|
+
const current = items.indexOf(event.target);
|
|
182
|
+
if (current < 0) return;
|
|
183
|
+
|
|
184
|
+
if (event.key === 'Tab') {
|
|
185
|
+
close('tab');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let next = -1;
|
|
190
|
+
if (event.key === 'ArrowDown') next = nextEnabledIndex(items, current, 1, true);
|
|
191
|
+
else if (event.key === 'ArrowUp') next = nextEnabledIndex(items, current, -1, true);
|
|
192
|
+
else if (event.key === 'Home' || event.key === 'End') {
|
|
193
|
+
next = enabledBoundary(event.key === 'End');
|
|
194
|
+
} else if (event.key === 'Enter' || event.key === ' ') {
|
|
195
|
+
event.preventDefault();
|
|
196
|
+
selectItem(current, 'keyboard');
|
|
197
|
+
return;
|
|
198
|
+
} else {
|
|
199
|
+
next = typeahead.find(event, items, current);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (next >= 0) {
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
setHighlighted(next);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function close(reason = 'programmatic', closeOptions) {
|
|
209
|
+
return lifecycle.run('close', (checkpoint) => {
|
|
210
|
+
const changed = layer.close(reason, closeOptions);
|
|
211
|
+
checkpoint();
|
|
212
|
+
return changed;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function toggle(reason = 'programmatic') {
|
|
217
|
+
return lifecycle.run('toggle', (checkpoint) => {
|
|
218
|
+
if (isDisabled(trigger)) return false;
|
|
219
|
+
const changed = layer.toggle(reason);
|
|
220
|
+
checkpoint();
|
|
221
|
+
return changed;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
disposables.listen(trigger, 'click', (event) => {
|
|
226
|
+
event.preventDefault();
|
|
227
|
+
if (isDisabled(trigger)) return;
|
|
228
|
+
pendingFocus = 'first';
|
|
229
|
+
toggle('trigger');
|
|
230
|
+
});
|
|
231
|
+
disposables.listen(trigger, 'keydown', (event) => {
|
|
232
|
+
if (isDisabled(trigger)) {
|
|
233
|
+
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(event.key)) event.preventDefault();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
237
|
+
event.preventDefault();
|
|
238
|
+
open('keyboard', { focus: event.key === 'ArrowUp' ? 'last' : 'first' });
|
|
239
|
+
} else if ((event.key === 'Enter' || event.key === ' ') && !layer.isOpen) {
|
|
240
|
+
event.preventDefault();
|
|
241
|
+
open('keyboard');
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
disposables.listen(menu, 'keydown', onMenuKeyDown);
|
|
245
|
+
items.forEach((item, index) => {
|
|
246
|
+
disposables.listen(item, 'focus', () => setHighlighted(index, { focus: false }));
|
|
247
|
+
disposables.listen(item, 'click', (event) => {
|
|
248
|
+
if (isDisabled(item)) {
|
|
249
|
+
event.preventDefault();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
selectItem(index, 'pointer');
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
controller = Object.freeze({
|
|
257
|
+
get trigger() {
|
|
258
|
+
return trigger;
|
|
259
|
+
},
|
|
260
|
+
get root() {
|
|
261
|
+
return root;
|
|
262
|
+
},
|
|
263
|
+
get menu() {
|
|
264
|
+
return menu;
|
|
265
|
+
},
|
|
266
|
+
get items() {
|
|
267
|
+
return [...items];
|
|
268
|
+
},
|
|
269
|
+
get isOpen() {
|
|
270
|
+
return layer.isOpen;
|
|
271
|
+
},
|
|
272
|
+
open,
|
|
273
|
+
close,
|
|
274
|
+
toggle,
|
|
275
|
+
select(target, reason = 'programmatic') {
|
|
276
|
+
lifecycle.assertAlive();
|
|
277
|
+
const index = typeof target === 'number' ? target : items.indexOf(target);
|
|
278
|
+
if (!Number.isInteger(index) || index < 0 || index >= items.length) {
|
|
279
|
+
throw new RangeError('Menu item is not managed by this controller.');
|
|
280
|
+
}
|
|
281
|
+
return selectItem(index, reason);
|
|
282
|
+
},
|
|
283
|
+
destroy() {
|
|
284
|
+
if (!lifecycle.destroy()) return;
|
|
285
|
+
layer.destroy();
|
|
286
|
+
typeahead.reset();
|
|
287
|
+
disposables.dispose();
|
|
288
|
+
attributes.restore();
|
|
289
|
+
controllers.delete(trigger);
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
controllers.set(trigger, controller);
|
|
294
|
+
layer.activateInitialOpen();
|
|
295
|
+
return controller;
|
|
296
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
export type ElementCollection = Element | Iterable<Element> | ArrayLike<Element>;
|
|
2
|
+
export type ControllerReason = string;
|
|
3
|
+
export type InitialFocus = Element | string | (() => Element | null) | null;
|
|
4
|
+
|
|
5
|
+
export interface CloseOptions {
|
|
6
|
+
restoreFocus?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface OpenCloseDetail<TController> {
|
|
10
|
+
controller: TController;
|
|
11
|
+
reason: ControllerReason;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DialogController {
|
|
15
|
+
readonly container: Element;
|
|
16
|
+
readonly dialog: Element;
|
|
17
|
+
readonly trigger: Element | null;
|
|
18
|
+
readonly isOpen: boolean;
|
|
19
|
+
open(reason?: ControllerReason): boolean;
|
|
20
|
+
close(reason?: ControllerReason, options?: CloseOptions): boolean;
|
|
21
|
+
toggle(reason?: ControllerReason): boolean;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DialogControllerOptions {
|
|
26
|
+
container: Element;
|
|
27
|
+
dialog?: Element;
|
|
28
|
+
trigger?: Element | null;
|
|
29
|
+
backdrop?: Element | false;
|
|
30
|
+
closeButtons?: ElementCollection;
|
|
31
|
+
initialFocus?: InitialFocus;
|
|
32
|
+
open?: boolean;
|
|
33
|
+
triggerAction?: 'open' | 'toggle';
|
|
34
|
+
closeOnEscape?: boolean;
|
|
35
|
+
closeOnBackdrop?: boolean;
|
|
36
|
+
trapFocus?: boolean;
|
|
37
|
+
returnFocus?: boolean;
|
|
38
|
+
lockScroll?: boolean;
|
|
39
|
+
onOpen?(detail: OpenCloseDetail<DialogController>): void;
|
|
40
|
+
onClose?(detail: OpenCloseDetail<DialogController>): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createModalController(options: DialogControllerOptions): DialogController;
|
|
44
|
+
export function createDrawerController(options: DialogControllerOptions): DialogController;
|
|
45
|
+
|
|
46
|
+
export interface TabsSelectDetail {
|
|
47
|
+
controller: TabsController;
|
|
48
|
+
index: number;
|
|
49
|
+
tab: Element;
|
|
50
|
+
panel: Element;
|
|
51
|
+
reason: ControllerReason;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface TabsControllerOptions {
|
|
55
|
+
tablist: Element;
|
|
56
|
+
tabs?: ElementCollection;
|
|
57
|
+
panels?: ElementCollection;
|
|
58
|
+
orientation?: 'horizontal' | 'vertical';
|
|
59
|
+
activation?: 'automatic' | 'manual';
|
|
60
|
+
loop?: boolean;
|
|
61
|
+
selectedIndex?: number;
|
|
62
|
+
onSelect?(detail: TabsSelectDetail): void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface TabsController {
|
|
66
|
+
readonly tablist: Element;
|
|
67
|
+
readonly tabs: Element[];
|
|
68
|
+
readonly panels: Element[];
|
|
69
|
+
readonly selectedIndex: number;
|
|
70
|
+
select(
|
|
71
|
+
target: number | Element,
|
|
72
|
+
options?: { focus?: boolean; reason?: ControllerReason },
|
|
73
|
+
): boolean;
|
|
74
|
+
destroy(): void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function createTabsController(options: TabsControllerOptions): TabsController;
|
|
78
|
+
|
|
79
|
+
export interface ToggleGroupValueChangeDetail {
|
|
80
|
+
controller: ToggleGroupController;
|
|
81
|
+
item: Element | null;
|
|
82
|
+
previousValue: string | string[] | null;
|
|
83
|
+
value: string | string[] | null;
|
|
84
|
+
reason: ControllerReason;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface ToggleGroupControllerBaseOptions {
|
|
88
|
+
root: Element;
|
|
89
|
+
items?: ElementCollection;
|
|
90
|
+
orientation?: 'horizontal' | 'vertical';
|
|
91
|
+
loop?: boolean;
|
|
92
|
+
allowEmpty?: boolean;
|
|
93
|
+
onValueChange?(detail: ToggleGroupValueChangeDetail): void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface SingleToggleGroupControllerOptions extends ToggleGroupControllerBaseOptions {
|
|
97
|
+
type?: 'single';
|
|
98
|
+
value?: string | number | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface MultipleToggleGroupControllerOptions extends ToggleGroupControllerBaseOptions {
|
|
102
|
+
type: 'multiple';
|
|
103
|
+
value?: Iterable<string | number> | null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type ToggleGroupControllerOptions =
|
|
107
|
+
| SingleToggleGroupControllerOptions
|
|
108
|
+
| MultipleToggleGroupControllerOptions;
|
|
109
|
+
|
|
110
|
+
export interface ToggleGroupControllerBase {
|
|
111
|
+
readonly root: Element;
|
|
112
|
+
readonly items: Element[];
|
|
113
|
+
toggle(target: number | Element, reason?: ControllerReason): boolean;
|
|
114
|
+
destroy(): void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface SingleToggleGroupController extends ToggleGroupControllerBase {
|
|
118
|
+
readonly value: string | null;
|
|
119
|
+
setValue(value: string | number | null, reason?: ControllerReason): boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface MultipleToggleGroupController extends ToggleGroupControllerBase {
|
|
123
|
+
readonly value: string[];
|
|
124
|
+
setValue(value: Iterable<string | number> | null, reason?: ControllerReason): boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type ToggleGroupController = SingleToggleGroupController | MultipleToggleGroupController;
|
|
128
|
+
|
|
129
|
+
export function createToggleGroupController(
|
|
130
|
+
options: MultipleToggleGroupControllerOptions,
|
|
131
|
+
): MultipleToggleGroupController;
|
|
132
|
+
export function createToggleGroupController(
|
|
133
|
+
options: SingleToggleGroupControllerOptions,
|
|
134
|
+
): SingleToggleGroupController;
|
|
135
|
+
|
|
136
|
+
export interface DropdownSelectDetail {
|
|
137
|
+
controller: DropdownController;
|
|
138
|
+
index: number;
|
|
139
|
+
item: Element;
|
|
140
|
+
value: string;
|
|
141
|
+
reason: ControllerReason;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface DropdownControllerOptions {
|
|
145
|
+
root?: Element;
|
|
146
|
+
trigger: Element;
|
|
147
|
+
menu: Element;
|
|
148
|
+
items?: ElementCollection;
|
|
149
|
+
open?: boolean;
|
|
150
|
+
closeOnEscape?: boolean;
|
|
151
|
+
closeOnOutside?: boolean;
|
|
152
|
+
closeOnSelect?: boolean;
|
|
153
|
+
typeaheadTimeout?: number;
|
|
154
|
+
onOpen?(detail: OpenCloseDetail<DropdownController>): void;
|
|
155
|
+
onClose?(detail: OpenCloseDetail<DropdownController>): void;
|
|
156
|
+
onSelect?(detail: DropdownSelectDetail): void;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface DropdownController {
|
|
160
|
+
readonly root: Element;
|
|
161
|
+
readonly trigger: Element;
|
|
162
|
+
readonly menu: Element;
|
|
163
|
+
readonly items: Element[];
|
|
164
|
+
readonly isOpen: boolean;
|
|
165
|
+
open(reason?: ControllerReason, options?: { focus?: 'first' | 'last' }): boolean;
|
|
166
|
+
close(reason?: ControllerReason, options?: CloseOptions): boolean;
|
|
167
|
+
toggle(reason?: ControllerReason): boolean;
|
|
168
|
+
select(target: number | Element, reason?: ControllerReason): boolean;
|
|
169
|
+
destroy(): void;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function createDropdownController(options: DropdownControllerOptions): DropdownController;
|
|
173
|
+
|
|
174
|
+
export interface SelectMenuValueChangeDetail {
|
|
175
|
+
controller: SelectMenuController;
|
|
176
|
+
index: number;
|
|
177
|
+
option: Element;
|
|
178
|
+
previousValue: string | null;
|
|
179
|
+
value: string;
|
|
180
|
+
reason: ControllerReason;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface SelectMenuControllerOptions {
|
|
184
|
+
root?: Element;
|
|
185
|
+
trigger: Element;
|
|
186
|
+
listbox: Element;
|
|
187
|
+
options?: ElementCollection;
|
|
188
|
+
valueElement?: Element;
|
|
189
|
+
value?: string | number | null;
|
|
190
|
+
open?: boolean;
|
|
191
|
+
closeOnEscape?: boolean;
|
|
192
|
+
closeOnOutside?: boolean;
|
|
193
|
+
typeaheadTimeout?: number;
|
|
194
|
+
getOptionLabel?(option: Element, index: number): string;
|
|
195
|
+
getOptionValue?(option: Element, index: number): string | number;
|
|
196
|
+
onOpen?(detail: OpenCloseDetail<SelectMenuController>): void;
|
|
197
|
+
onClose?(detail: OpenCloseDetail<SelectMenuController>): void;
|
|
198
|
+
onValueChange?(detail: SelectMenuValueChangeDetail): void;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface SelectMenuController {
|
|
202
|
+
readonly root: Element;
|
|
203
|
+
readonly trigger: Element;
|
|
204
|
+
readonly listbox: Element;
|
|
205
|
+
readonly options: Element[];
|
|
206
|
+
readonly isOpen: boolean;
|
|
207
|
+
readonly value: string | null;
|
|
208
|
+
open(reason?: ControllerReason): boolean;
|
|
209
|
+
close(reason?: ControllerReason, options?: CloseOptions): boolean;
|
|
210
|
+
toggle(reason?: ControllerReason): boolean;
|
|
211
|
+
select(target: number | Element, reason?: ControllerReason): boolean;
|
|
212
|
+
setValue(value: string | number, reason?: ControllerReason): boolean;
|
|
213
|
+
destroy(): void;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function createSelectMenuController(
|
|
217
|
+
options: SelectMenuControllerOptions,
|
|
218
|
+
): SelectMenuController;
|
|
219
|
+
|
|
220
|
+
export interface ComboboxQueryChangeDetail {
|
|
221
|
+
controller: ComboboxController;
|
|
222
|
+
query: string;
|
|
223
|
+
results: Element[];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export interface ComboboxValueChangeDetail {
|
|
227
|
+
controller: ComboboxController;
|
|
228
|
+
index: number;
|
|
229
|
+
option: Element | null;
|
|
230
|
+
previousValue: string | null;
|
|
231
|
+
value: string | null;
|
|
232
|
+
reason: ControllerReason;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface ComboboxControllerOptions {
|
|
236
|
+
root?: Element;
|
|
237
|
+
input: HTMLInputElement | HTMLTextAreaElement;
|
|
238
|
+
listbox: Element;
|
|
239
|
+
toggleButton?: Element;
|
|
240
|
+
status?: Element;
|
|
241
|
+
options?: ElementCollection;
|
|
242
|
+
value?: string | number | null;
|
|
243
|
+
open?: boolean;
|
|
244
|
+
autocomplete?: 'list' | 'none';
|
|
245
|
+
filter?: boolean | ((option: Element, query: string, index: number) => boolean);
|
|
246
|
+
openOnFocus?: boolean;
|
|
247
|
+
openOnInput?: boolean;
|
|
248
|
+
closeOnEscape?: boolean;
|
|
249
|
+
closeOnOutside?: boolean;
|
|
250
|
+
clearSelectionOnInput?: boolean;
|
|
251
|
+
setInputValue?: boolean;
|
|
252
|
+
getOptionLabel?(option: Element, index: number): string;
|
|
253
|
+
getOptionValue?(option: Element, index: number): string | number;
|
|
254
|
+
formatStatus?(count: number, query: string): string;
|
|
255
|
+
onOpen?(detail: OpenCloseDetail<ComboboxController>): void;
|
|
256
|
+
onClose?(detail: OpenCloseDetail<ComboboxController>): void;
|
|
257
|
+
onQueryChange?(detail: ComboboxQueryChangeDetail): void;
|
|
258
|
+
onValueChange?(detail: ComboboxValueChangeDetail): void;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export interface ComboboxController {
|
|
262
|
+
readonly root: Element;
|
|
263
|
+
readonly input: HTMLInputElement | HTMLTextAreaElement;
|
|
264
|
+
readonly listbox: Element;
|
|
265
|
+
readonly options: Element[];
|
|
266
|
+
readonly isOpen: boolean;
|
|
267
|
+
readonly query: string;
|
|
268
|
+
readonly value: string | null;
|
|
269
|
+
open(reason?: ControllerReason): boolean;
|
|
270
|
+
close(reason?: ControllerReason, options?: CloseOptions): boolean;
|
|
271
|
+
toggle(reason?: ControllerReason): boolean;
|
|
272
|
+
select(target: number | Element, reason?: ControllerReason): boolean;
|
|
273
|
+
setValue(value: string | number | null, reason?: ControllerReason): boolean;
|
|
274
|
+
refresh(): Element[];
|
|
275
|
+
destroy(): void;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function createComboboxController(options: ComboboxControllerOptions): ComboboxController;
|
|
279
|
+
|
|
280
|
+
export interface TooltipControllerOptions {
|
|
281
|
+
root?: Element;
|
|
282
|
+
trigger: Element;
|
|
283
|
+
tooltip: Element;
|
|
284
|
+
open?: boolean;
|
|
285
|
+
openDelay?: number;
|
|
286
|
+
closeDelay?: number;
|
|
287
|
+
onOpen?(detail: OpenCloseDetail<TooltipController>): void;
|
|
288
|
+
onClose?(detail: OpenCloseDetail<TooltipController>): void;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface TooltipController {
|
|
292
|
+
readonly root: Element;
|
|
293
|
+
readonly trigger: Element;
|
|
294
|
+
readonly tooltip: Element;
|
|
295
|
+
readonly isOpen: boolean;
|
|
296
|
+
open(reason?: ControllerReason): boolean;
|
|
297
|
+
close(reason?: ControllerReason): boolean;
|
|
298
|
+
destroy(): void;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function createTooltipController(options: TooltipControllerOptions): TooltipController;
|
|
302
|
+
|
|
303
|
+
export interface PopoverControllerOptions {
|
|
304
|
+
root?: Element;
|
|
305
|
+
trigger: Element;
|
|
306
|
+
popover: Element;
|
|
307
|
+
closeButtons?: ElementCollection;
|
|
308
|
+
initialFocus?: InitialFocus;
|
|
309
|
+
open?: boolean;
|
|
310
|
+
openOnFocus?: boolean;
|
|
311
|
+
openOnHover?: boolean;
|
|
312
|
+
focusOnOpen?: boolean;
|
|
313
|
+
openDelay?: number;
|
|
314
|
+
closeDelay?: number;
|
|
315
|
+
closeOnEscape?: boolean;
|
|
316
|
+
closeOnOutside?: boolean;
|
|
317
|
+
closeOnFocusOutside?: boolean;
|
|
318
|
+
onOpen?(detail: OpenCloseDetail<PopoverController>): void;
|
|
319
|
+
onClose?(detail: OpenCloseDetail<PopoverController>): void;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export interface PopoverController {
|
|
323
|
+
readonly root: Element;
|
|
324
|
+
readonly trigger: Element;
|
|
325
|
+
readonly popover: Element;
|
|
326
|
+
readonly isOpen: boolean;
|
|
327
|
+
open(reason?: ControllerReason, options?: { focus?: boolean }): boolean;
|
|
328
|
+
close(reason?: ControllerReason, options?: CloseOptions): boolean;
|
|
329
|
+
toggle(reason?: ControllerReason): boolean;
|
|
330
|
+
destroy(): void;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function createPopoverController(options: PopoverControllerOptions): PopoverController;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { createComboboxController } from './combobox.js';
|
|
2
|
+
export { createDrawerController } from './drawer.js';
|
|
3
|
+
export { createDropdownController } from './dropdown.js';
|
|
4
|
+
export { createModalController } from './modal.js';
|
|
5
|
+
export { createPopoverController } from './popover.js';
|
|
6
|
+
export { createSelectMenuController } from './select-menu.js';
|
|
7
|
+
export { createTabsController } from './tabs.js';
|
|
8
|
+
export { createToggleGroupController } from './toggle-group.js';
|
|
9
|
+
export { createTooltipController } from './tooltip.js';
|