@digdir/designsystemet-web 1.18.0 → 1.19.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/README.md +13 -15
- package/dist/cjs/_vendors/@oddbird/popover-polyfill/dist/popover-fn.cjs +4 -4
- package/dist/cjs/_vendors/@oddbird/popover-polyfill/dist/popover-fn.cjs.map +1 -1
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -1
- package/dist/cjs/breadcrumbs/breadcrumbs.cjs +1 -1
- package/dist/cjs/breadcrumbs/breadcrumbs.cjs.map +1 -1
- package/dist/cjs/clickdelegatefor/clickdelegatefor.cjs +1 -1
- package/dist/cjs/clickdelegatefor/clickdelegatefor.cjs.map +1 -1
- package/dist/cjs/dialog/dialog.cjs +1 -1
- package/dist/cjs/dialog/dialog.cjs.map +1 -1
- package/dist/cjs/error-summary/error-summary.cjs +1 -2
- package/dist/cjs/error-summary/error-summary.cjs.map +1 -1
- package/dist/cjs/field/field.cjs +1 -1
- package/dist/cjs/field/field.cjs.map +1 -1
- package/dist/cjs/fieldset/fieldset.cjs +1 -1
- package/dist/cjs/fieldset/fieldset.cjs.map +1 -1
- package/dist/cjs/focusgroup/focusgroup.cjs +2 -0
- package/dist/cjs/focusgroup/focusgroup.cjs.map +1 -0
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/invokers/invokers.cjs.map +1 -1
- package/dist/cjs/packages/web/package.cjs +2 -0
- package/dist/cjs/packages/web/package.cjs.map +1 -0
- package/dist/cjs/pagination/pagination.cjs +1 -1
- package/dist/cjs/pagination/pagination.cjs.map +1 -1
- package/dist/cjs/popover/popover.cjs +1 -1
- package/dist/cjs/popover/popover.cjs.map +1 -1
- package/dist/cjs/readonly/readonly.cjs +1 -1
- package/dist/cjs/readonly/readonly.cjs.map +1 -1
- package/dist/cjs/suggestion/suggestion.cjs +1 -1
- package/dist/cjs/suggestion/suggestion.cjs.map +1 -1
- package/dist/cjs/toggle-group/toggle-group.cjs +1 -1
- package/dist/cjs/toggle-group/toggle-group.cjs.map +1 -1
- package/dist/cjs/tooltip/tooltip.cjs +1 -1
- package/dist/cjs/tooltip/tooltip.cjs.map +1 -1
- package/dist/cjs/utils/utils.cjs +1 -1
- package/dist/cjs/utils/utils.cjs.map +1 -1
- package/dist/custom-elements.json +187 -2
- package/dist/esm/_vendors/@oddbird/popover-polyfill/dist/popover-fn.js +4 -4
- package/dist/esm/_vendors/@oddbird/popover-polyfill/dist/popover-fn.js.map +1 -1
- package/dist/esm/breadcrumbs/breadcrumbs.js +1 -1
- package/dist/esm/breadcrumbs/breadcrumbs.js.map +1 -1
- package/dist/esm/clickdelegatefor/clickdelegatefor.js +1 -1
- package/dist/esm/clickdelegatefor/clickdelegatefor.js.map +1 -1
- package/dist/esm/dialog/dialog.js +1 -1
- package/dist/esm/dialog/dialog.js.map +1 -1
- package/dist/esm/error-summary/error-summary.js +1 -2
- package/dist/esm/error-summary/error-summary.js.map +1 -1
- package/dist/esm/field/field.js +1 -1
- package/dist/esm/field/field.js.map +1 -1
- package/dist/esm/fieldset/fieldset.js +1 -1
- package/dist/esm/fieldset/fieldset.js.map +1 -1
- package/dist/esm/focusgroup/focusgroup.js +2 -0
- package/dist/esm/focusgroup/focusgroup.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/invokers/invokers.js.map +1 -1
- package/dist/esm/packages/web/package.js +2 -0
- package/dist/esm/packages/web/package.js.map +1 -0
- package/dist/esm/pagination/pagination.js +1 -1
- package/dist/esm/pagination/pagination.js.map +1 -1
- package/dist/esm/popover/popover.js +1 -1
- package/dist/esm/popover/popover.js.map +1 -1
- package/dist/esm/readonly/readonly.js +1 -1
- package/dist/esm/readonly/readonly.js.map +1 -1
- package/dist/esm/suggestion/suggestion.js +1 -1
- package/dist/esm/suggestion/suggestion.js.map +1 -1
- package/dist/esm/toggle-group/toggle-group.js +1 -1
- package/dist/esm/toggle-group/toggle-group.js.map +1 -1
- package/dist/esm/tooltip/tooltip.js +1 -1
- package/dist/esm/tooltip/tooltip.js.map +1 -1
- package/dist/esm/utils/utils.js +1 -1
- package/dist/esm/utils/utils.js.map +1 -1
- package/dist/index.d.ts +18 -22
- package/dist/index.js +636 -263
- package/dist/umd/index.js +16 -11
- package/dist/umd/index.js.map +1 -1
- package/package.json +15 -10
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,13 @@ import { autoUpdate, computePosition, flip, limitShift, offset, shift, size } fr
|
|
|
3
3
|
import { UHTMLComboboxElement } from "@u-elements/u-combobox";
|
|
4
4
|
import * as UTabs from "@u-elements/u-tabs";
|
|
5
5
|
export * from "@u-elements/u-datalist";
|
|
6
|
+
//#region package.json
|
|
7
|
+
var version = "1.19.1";
|
|
8
|
+
//#endregion
|
|
6
9
|
//#region src/utils/utils.ts
|
|
10
|
+
const ARIA_DESC = "aria-description";
|
|
11
|
+
const ARIA_LABEL = "aria-label";
|
|
12
|
+
const ARIA_LABELLEDBY = "aria-labelledby";
|
|
7
13
|
const QUICK_EVENT = {
|
|
8
14
|
passive: true,
|
|
9
15
|
capture: true
|
|
@@ -27,7 +33,7 @@ const warn = (message, ...args) => !isBrowser() || window.dsWarnings === false |
|
|
|
27
33
|
* @param value A valid attribute value or null to remove attribute
|
|
28
34
|
*/
|
|
29
35
|
const attr = (el, name, value) => {
|
|
30
|
-
if (value === void 0) return el.getAttribute(name)
|
|
36
|
+
if (value === void 0) return el.getAttribute(name);
|
|
31
37
|
if (value === null) el.removeAttribute(name);
|
|
32
38
|
else if (el.getAttribute(name) !== value) el.setAttribute(name, value);
|
|
33
39
|
return null;
|
|
@@ -51,10 +57,45 @@ const STRIP_QUOTES = /^["']|["']$/g;
|
|
|
51
57
|
const attrOrCSS = (el, name) => {
|
|
52
58
|
let value = attr(el, name);
|
|
53
59
|
if (!value) value = getCSSProp(el, `--_ds-${name}`).replace(STRIP_QUOTES, "").trim();
|
|
54
|
-
if (!value) warn(`Missing ${name} on:`, el);
|
|
55
60
|
return value || null;
|
|
56
61
|
};
|
|
57
62
|
/**
|
|
63
|
+
* getRoot
|
|
64
|
+
* @description Helper for better compatibility
|
|
65
|
+
* @param node The target node
|
|
66
|
+
* @return The shadow root or document
|
|
67
|
+
*/
|
|
68
|
+
const getRoot = (node) => {
|
|
69
|
+
const root = node?.getRootNode?.() || node?.ownerDocument;
|
|
70
|
+
if (root instanceof Document || root instanceof ShadowRoot) return root;
|
|
71
|
+
return node?.ownerDocument || document;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* getComposedTarget
|
|
75
|
+
* @description Helper to get the composed target of an event, supporting ShadowDOM rendering
|
|
76
|
+
* @param event The event
|
|
77
|
+
* @return The target Element
|
|
78
|
+
*/
|
|
79
|
+
const getComposedTarget = (e) => {
|
|
80
|
+
const el = e.target?.shadowRoot && e.composedPath()[0] || e.target;
|
|
81
|
+
return el?.nodeType === 1 ? el : null;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* getComposedPath
|
|
85
|
+
* @description Helper to get the full composed path even if listener is bound to a ShadowRoot (unlike event.composedPath())
|
|
86
|
+
* @param el The element to start traversing from
|
|
87
|
+
* @return Set of nodes in the path
|
|
88
|
+
*/
|
|
89
|
+
const getComposedPath = (el) => {
|
|
90
|
+
const path = /* @__PURE__ */ new Set();
|
|
91
|
+
while (el) {
|
|
92
|
+
path.add(el);
|
|
93
|
+
if (el.nodeType === 11) el = el.host;
|
|
94
|
+
else el = el.assignedSlot || el.parentNode;
|
|
95
|
+
}
|
|
96
|
+
return path;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
58
99
|
* on
|
|
59
100
|
* @param el The Element to use as EventTarget
|
|
60
101
|
* @param types A space separated string of event types
|
|
@@ -75,6 +116,7 @@ const off = (el, ...rest) => {
|
|
|
75
116
|
const [types, ...options] = rest;
|
|
76
117
|
for (const type of types.split(" ")) el.removeEventListener(type, ...options);
|
|
77
118
|
};
|
|
119
|
+
const HOT_RELOAD_KEY = `_dsHotReloadCleanup${version}`;
|
|
78
120
|
/**
|
|
79
121
|
* onHotReload
|
|
80
122
|
* @description Runs a callback when window is loaded in browser, and ensures cleanup when hot-reloading
|
|
@@ -83,9 +125,9 @@ const off = (el, ...rest) => {
|
|
|
83
125
|
*/
|
|
84
126
|
const onHotReload = (key, setup) => {
|
|
85
127
|
if (!isBrowser()) return;
|
|
86
|
-
if (!window
|
|
87
|
-
window
|
|
88
|
-
window
|
|
128
|
+
if (!window[HOT_RELOAD_KEY]) window[HOT_RELOAD_KEY] = /* @__PURE__ */ new Map();
|
|
129
|
+
for (const cleanup of window[HOT_RELOAD_KEY]?.get(key) || []) cleanup();
|
|
130
|
+
window[HOT_RELOAD_KEY]?.set(key, setup());
|
|
89
131
|
};
|
|
90
132
|
/**
|
|
91
133
|
* MutationObserver wrapper with automatic cleanup
|
|
@@ -153,60 +195,86 @@ const announceMount = () => {
|
|
|
153
195
|
onHotReload("announce", () => [on(document, "focus mouseover", announceMount, QUICK_EVENT)]);
|
|
154
196
|
//#endregion
|
|
155
197
|
//#region src/clickdelegatefor/clickdelegatefor.ts
|
|
156
|
-
const CLASS_HOVER = ":click-delegate-hover";
|
|
157
198
|
const ATTR_CLICKDELEGATEFOR = "data-clickdelegatefor";
|
|
158
|
-
const
|
|
159
|
-
const
|
|
199
|
+
const CLASS_HOVER = ":click-delegate-hover";
|
|
200
|
+
const SKIP = /* @__PURE__ */ new Set([
|
|
201
|
+
"A",
|
|
202
|
+
"BUTTON",
|
|
203
|
+
"DETAILS",
|
|
204
|
+
"DIALOG",
|
|
205
|
+
"INPUT",
|
|
206
|
+
"LABEL",
|
|
207
|
+
"SELECT",
|
|
208
|
+
"TEXTAREA"
|
|
209
|
+
]);
|
|
160
210
|
const handleClickDelegateFor = (event) => {
|
|
161
211
|
const isNewTab = event.button === 1 || event.metaKey || event.ctrlKey;
|
|
212
|
+
const target = getComposedTarget(event);
|
|
162
213
|
const delegateTarget = event.button < 2 && getDelegateTarget(event);
|
|
163
|
-
if (!delegateTarget || delegateTarget.contains(
|
|
214
|
+
if (!delegateTarget || delegateTarget.contains(target)) return;
|
|
164
215
|
if (isNewTab && delegateTarget instanceof HTMLAnchorElement) return window.open(delegateTarget.href, void 0, delegateTarget.rel);
|
|
165
216
|
event.stopImmediatePropagation();
|
|
166
|
-
delegateTarget.click();
|
|
217
|
+
if (delegateTarget.nodeName !== "LABEL") delegateTarget.click();
|
|
167
218
|
};
|
|
168
219
|
let HOVER;
|
|
169
|
-
|
|
220
|
+
let TARGET$1;
|
|
221
|
+
const handleMouseMove = (event) => {
|
|
222
|
+
const target = getComposedTarget(event);
|
|
223
|
+
if (!target || target === TARGET$1) return;
|
|
224
|
+
TARGET$1 = target;
|
|
170
225
|
const delegateTarget = getDelegateTarget(event);
|
|
171
226
|
if (HOVER === delegateTarget) return;
|
|
172
227
|
if (HOVER) HOVER.classList.remove(CLASS_HOVER);
|
|
173
228
|
if (delegateTarget) delegateTarget.classList.add(CLASS_HOVER);
|
|
174
229
|
HOVER = delegateTarget;
|
|
175
230
|
};
|
|
176
|
-
const getDelegateTarget = (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
231
|
+
const getDelegateTarget = (event) => {
|
|
232
|
+
let interactive;
|
|
233
|
+
for (let el of event.composedPath()) {
|
|
234
|
+
if (el.nodeType !== 1) continue;
|
|
235
|
+
if (el.nodeName === "LABEL") el = el.control || el;
|
|
236
|
+
const id = el.getAttribute(ATTR_CLICKDELEGATEFOR);
|
|
237
|
+
if (id) {
|
|
238
|
+
const to = getRoot(el).getElementById(id);
|
|
239
|
+
return to && !(interactive && interactive !== to) && !to.disabled && !to.readOnly ? to : void 0;
|
|
240
|
+
}
|
|
241
|
+
if (!interactive && isInteractive(el)) interactive = el;
|
|
242
|
+
}
|
|
181
243
|
};
|
|
182
|
-
|
|
244
|
+
const isInteractive = (el) => el.isContentEditable || el.popover || SKIP.has(el.nodeName) || attr(el, "role") === "button";
|
|
245
|
+
onHotReload("clickdelegatefor", () => [on(window, "click auxclick", handleClickDelegateFor, true), on(document, "mousemove", handleMouseMove, QUICK_EVENT)]);
|
|
183
246
|
//#endregion
|
|
184
247
|
//#region src/dialog/dialog.ts
|
|
185
248
|
let DOWN_INSIDE = false;
|
|
186
|
-
const handleClosedbyAny = (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
249
|
+
const handleClosedbyAny = (event) => {
|
|
250
|
+
const { type, clientX: x = 0, clientY: y = 0 } = event;
|
|
251
|
+
const el = getComposedTarget(event);
|
|
252
|
+
if (el && type === "pointerdown") for (const dialog of getComposedPath(el)) {
|
|
253
|
+
if (dialog.nodeName !== "DIALOG") continue;
|
|
254
|
+
const r = dialog.getBoundingClientRect();
|
|
255
|
+
DOWN_INSIDE = r.top <= y && y <= r.bottom && r.left <= x && x <= r.right;
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
const isClose = el?.nodeName === "DIALOG" && !DOWN_INSIDE && attr(el, "closedby") === "any";
|
|
192
260
|
DOWN_INSIDE = false;
|
|
193
|
-
if (isClose)
|
|
261
|
+
if (isClose) setTimeout(close, 0, el);
|
|
194
262
|
}
|
|
195
263
|
};
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
264
|
+
const close = (dialog) => dialog.open && dialog.close();
|
|
265
|
+
const MODAL = "show-modal";
|
|
266
|
+
const NON_MODAL = "--show-non-modal";
|
|
267
|
+
const handleAriaAttributes = (event) => {
|
|
268
|
+
for (const el of event.composedPath()) {
|
|
269
|
+
const command = el.nodeType === 1 && attr(el, "command");
|
|
270
|
+
if (command === MODAL || command === NON_MODAL) return attr(el, "aria-haspopup", "dialog");
|
|
271
|
+
}
|
|
199
272
|
};
|
|
200
|
-
const handleCommand = ({ command, target }) => command ===
|
|
273
|
+
const handleCommand = ({ command, target }) => command === NON_MODAL && target instanceof HTMLDialogElement && target.show();
|
|
201
274
|
onHotReload("dialog", () => [
|
|
202
275
|
on(document, "command", handleCommand, QUICK_EVENT),
|
|
203
|
-
on(document, "
|
|
204
|
-
|
|
205
|
-
attributeFilter: ["command"],
|
|
206
|
-
attributes: true,
|
|
207
|
-
childList: true,
|
|
208
|
-
subtree: true
|
|
209
|
-
})
|
|
276
|
+
on(document, "focus", handleAriaAttributes, QUICK_EVENT),
|
|
277
|
+
on(document, "pointerdown pointerup", handleClosedbyAny, QUICK_EVENT)
|
|
210
278
|
]);
|
|
211
279
|
//#endregion
|
|
212
280
|
//#region src/fieldset/fieldset.ts
|
|
@@ -214,7 +282,7 @@ const FIELDSETS = isBrowser() ? document.getElementsByTagName("fieldset") : [];
|
|
|
214
282
|
const handleFieldsetMutations = () => {
|
|
215
283
|
for (const el of FIELDSETS) {
|
|
216
284
|
if (el.hasAttribute("aria-labelledby")) continue;
|
|
217
|
-
attr(el,
|
|
285
|
+
attr(el, ARIA_LABELLEDBY, `${useId(el.querySelector("legend"))} ${useId(el.querySelector(":scope > :is([data-field=\"description\"],legend + p)"))}`.trim() || null);
|
|
218
286
|
}
|
|
219
287
|
};
|
|
220
288
|
onHotReload("fieldset", () => [onMutation(document, handleFieldsetMutations, {
|
|
@@ -222,6 +290,187 @@ onHotReload("fieldset", () => [onMutation(document, handleFieldsetMutations, {
|
|
|
222
290
|
subtree: true
|
|
223
291
|
})]);
|
|
224
292
|
//#endregion
|
|
293
|
+
//#region src/focusgroup/focusgroup.ts
|
|
294
|
+
const ATTR_GROUP = "focusgroup";
|
|
295
|
+
const PROP_GROUP = "focusGroup";
|
|
296
|
+
const ATTR_START = `${ATTR_GROUP}start`;
|
|
297
|
+
const GROUPS = /* @__PURE__ */ new WeakMap();
|
|
298
|
+
const ROOTS$1 = /* @__PURE__ */ new Map();
|
|
299
|
+
const ROLES = {
|
|
300
|
+
listbox: {
|
|
301
|
+
block: true,
|
|
302
|
+
wrap: false,
|
|
303
|
+
items: "option"
|
|
304
|
+
},
|
|
305
|
+
menu: {
|
|
306
|
+
block: true,
|
|
307
|
+
wrap: true,
|
|
308
|
+
items: "menuitem"
|
|
309
|
+
},
|
|
310
|
+
menubar: {
|
|
311
|
+
block: false,
|
|
312
|
+
wrap: true,
|
|
313
|
+
items: "menuitem"
|
|
314
|
+
},
|
|
315
|
+
radiogroup: {
|
|
316
|
+
block: null,
|
|
317
|
+
wrap: true,
|
|
318
|
+
items: "radio"
|
|
319
|
+
},
|
|
320
|
+
tablist: {
|
|
321
|
+
block: false,
|
|
322
|
+
wrap: true,
|
|
323
|
+
items: "tab"
|
|
324
|
+
},
|
|
325
|
+
toolbar: {
|
|
326
|
+
block: false,
|
|
327
|
+
wrap: false,
|
|
328
|
+
items: null
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
let IS_TAB = false;
|
|
332
|
+
const IS_CONFLICT = /* @__PURE__ */ new Set([
|
|
333
|
+
"AUDIO",
|
|
334
|
+
"VIDEO",
|
|
335
|
+
"TEXTAREA",
|
|
336
|
+
"SELECT",
|
|
337
|
+
"date",
|
|
338
|
+
"datetime-local",
|
|
339
|
+
"email",
|
|
340
|
+
"month",
|
|
341
|
+
"number",
|
|
342
|
+
"password",
|
|
343
|
+
"range",
|
|
344
|
+
"search",
|
|
345
|
+
"tel",
|
|
346
|
+
"text",
|
|
347
|
+
"time",
|
|
348
|
+
"url",
|
|
349
|
+
"week"
|
|
350
|
+
]);
|
|
351
|
+
const IS_SUPPORTED = isBrowser() && (ATTR_GROUP in HTMLElement.prototype || PROP_GROUP in HTMLElement.prototype);
|
|
352
|
+
const setIsTab = (state) => IS_TAB = state;
|
|
353
|
+
const handleKeydown$2 = (e) => {
|
|
354
|
+
if (e.defaultPrevented || e.altKey || e.metaKey || e.ctrlKey) return;
|
|
355
|
+
const isTab = e.key === "Tab";
|
|
356
|
+
const isBlock = e.key === "ArrowUp" || e.key === "ArrowDown";
|
|
357
|
+
const isArrow = isBlock || e.key === "ArrowLeft" || e.key === "ArrowRight";
|
|
358
|
+
if (!isTab && !isArrow && e.key !== "Home" && e.key !== "End") return;
|
|
359
|
+
if (isTab) {
|
|
360
|
+
setIsTab(true);
|
|
361
|
+
setTimeout(setIsTab, 100, false);
|
|
362
|
+
}
|
|
363
|
+
const target = getComposedTarget(e);
|
|
364
|
+
if (!target || isConflict(target)) return;
|
|
365
|
+
let group = getGroup(getComposedPath(target));
|
|
366
|
+
if (group?.role && group?.el === target) group = getGroup(getComposedPath(group.el.parentNode));
|
|
367
|
+
if (!group?.role) return;
|
|
368
|
+
const items = getItems(group.el, target);
|
|
369
|
+
let next = 0;
|
|
370
|
+
if (isTab) return setTimeout(setTab, 0, items, setTab(items));
|
|
371
|
+
if (e.key === "Home") next = 0;
|
|
372
|
+
else if (e.key === "End") next = items.length - 1;
|
|
373
|
+
else {
|
|
374
|
+
const { direction: dir, writingMode: mode } = getComputedStyle(target);
|
|
375
|
+
const isFlipped = mode.startsWith("vertical");
|
|
376
|
+
const isReverse = isFlipped ? mode === "vertical-rl" : dir === "rtl";
|
|
377
|
+
const isForward = e.key === "ArrowDown" || e.key === "ArrowRight";
|
|
378
|
+
const moveBlock = isBlock !== isFlipped;
|
|
379
|
+
const moveForward = isForward !== (!isBlock && isReverse);
|
|
380
|
+
if ((group.block ?? moveBlock) !== moveBlock) return;
|
|
381
|
+
next = items.indexOf(target) + (moveForward ? 1 : -1);
|
|
382
|
+
if (group.wrap) next = (items.length + next) % items.length;
|
|
383
|
+
else next = Math.max(0, Math.min(next, items.length - 1));
|
|
384
|
+
}
|
|
385
|
+
if (items[next] !== target) e.preventDefault();
|
|
386
|
+
items[next]?.focus?.();
|
|
387
|
+
};
|
|
388
|
+
const handleFocus = (e) => {
|
|
389
|
+
const target = getComposedTarget(e);
|
|
390
|
+
const path = getComposedPath(target);
|
|
391
|
+
for (const [el, off] of ROOTS$1) if (!path.has(el) && ROOTS$1.delete(el)) off();
|
|
392
|
+
for (const el of path) if (el.nodeType === 11 && !ROOTS$1.has(el)) ROOTS$1.set(el, on(el, "focus", handleFocus, QUICK_EVENT));
|
|
393
|
+
if (e.target?.shadowRoot) return;
|
|
394
|
+
const group = getGroup(path);
|
|
395
|
+
if (!group?.role) return;
|
|
396
|
+
if (IS_TAB && !isConflict(group.focus)) {
|
|
397
|
+
const segment = getSegment(getItems(group.el, null), target);
|
|
398
|
+
const start = group.memory && segment.includes(group.focus) && group.focus || segment.find((el) => el?.hasAttribute(ATTR_START)) || segment[0];
|
|
399
|
+
if (start !== target) return start?.focus?.();
|
|
400
|
+
}
|
|
401
|
+
group.focus = target;
|
|
402
|
+
};
|
|
403
|
+
const getGroup = (path) => {
|
|
404
|
+
for (const el of path) {
|
|
405
|
+
if (el.nodeType !== Node.ELEMENT_NODE) continue;
|
|
406
|
+
const key = el.getAttribute(ATTR_GROUP);
|
|
407
|
+
if (key !== null) {
|
|
408
|
+
let group = GROUPS.get(el);
|
|
409
|
+
if (group?.key === key) return group;
|
|
410
|
+
group = parseGroup(el, key);
|
|
411
|
+
GROUPS.set(el, group);
|
|
412
|
+
for (const item of getItems(el)) if (item && !item.hasAttribute("role")) attr(item, "role", group.items);
|
|
413
|
+
if (!el.hasAttribute("role")) attr(el, "role", group.role);
|
|
414
|
+
return group;
|
|
415
|
+
}
|
|
416
|
+
if (isTopLayer(el)) return;
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
const parseGroup = (el, key) => {
|
|
420
|
+
const opts = new Set(key.toLowerCase().split(" "));
|
|
421
|
+
const role = [...opts].find((t) => t in ROLES);
|
|
422
|
+
const base = ROLES[role];
|
|
423
|
+
const x = opts.has("inline");
|
|
424
|
+
const y = opts.has("block");
|
|
425
|
+
return {
|
|
426
|
+
key,
|
|
427
|
+
el,
|
|
428
|
+
role,
|
|
429
|
+
block: x && y ? null : y || (x ? false : base?.block),
|
|
430
|
+
focus: null,
|
|
431
|
+
items: base?.items,
|
|
432
|
+
memory: !opts.has("nomemory"),
|
|
433
|
+
wrap: opts.has("wrap") || (opts.has("nowrap") ? false : base?.wrap)
|
|
434
|
+
};
|
|
435
|
+
};
|
|
436
|
+
const getItems = (root, keep, items = [], isNested = false) => {
|
|
437
|
+
const children = root?.nodeName === "SLOT" ? root.assignedElements({ flatten: true }) : (root?.shadowRoot || root)?.children;
|
|
438
|
+
for (let i = 0, l = children?.length || i; children && i < l; i++) {
|
|
439
|
+
const el = children[i];
|
|
440
|
+
if (el.nodeName === "SLOT") getItems(el, keep, items, isNested);
|
|
441
|
+
else if (isKeyboardReachable(el) && !isTopLayer(el) && isVisible(el)) {
|
|
442
|
+
const group = el.getAttribute(ATTR_GROUP);
|
|
443
|
+
if (el === keep || !isNested && group !== "none" && isFocusable$1(el)) items.push(el);
|
|
444
|
+
else if (group !== null && keep === null) items.push(null);
|
|
445
|
+
else getItems(el, keep, items, isNested || group !== null);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return items;
|
|
449
|
+
};
|
|
450
|
+
const getSegment = (acc, item) => {
|
|
451
|
+
const at = acc.indexOf(item);
|
|
452
|
+
const to = acc.indexOf(null, at);
|
|
453
|
+
return acc.slice(acc.lastIndexOf(null, at) + 1, to === -1 ? void 0 : to);
|
|
454
|
+
};
|
|
455
|
+
const isKeyboardReachable = (el) => !el.inert && !el.hidden && !el.disabled;
|
|
456
|
+
const isTopLayer = (el) => el?.nodeName === "DIALOG" || el?.hasAttribute("popover");
|
|
457
|
+
const isVisible = isBrowser() && typeof Element.prototype.checkVisibility === "function" ? (el) => el.checkVisibility() : (el) => el.offsetParent !== null;
|
|
458
|
+
const isFocusable$1 = (el) => el.isContentEditable || el.tabIndex >= 0 && !el.disabled;
|
|
459
|
+
const isConflict = (el) => el?.isContentEditable || IS_CONFLICT.has(el?.nodeName) || el?.nodeName === "INPUT" && IS_CONFLICT.has(el.type);
|
|
460
|
+
const setTab = (items, prevs) => Array.from(items, (item, index) => {
|
|
461
|
+
const prev = item?.getAttribute("tabindex");
|
|
462
|
+
if (item) attr(item, "tabindex", prevs ? prevs[index] : "-1");
|
|
463
|
+
return prev;
|
|
464
|
+
});
|
|
465
|
+
if (!IS_SUPPORTED) onHotReload(ATTR_GROUP, () => [
|
|
466
|
+
on(document, "keydown", handleKeydown$2),
|
|
467
|
+
on(document, "focus", handleFocus, QUICK_EVENT),
|
|
468
|
+
() => {
|
|
469
|
+
for (const [, off] of ROOTS$1) off();
|
|
470
|
+
ROOTS$1.clear();
|
|
471
|
+
}
|
|
472
|
+
]);
|
|
473
|
+
//#endregion
|
|
225
474
|
//#region ../../node_modules/.pnpm/invokers-polyfill@1.0.3/node_modules/invokers-polyfill/invoker.js
|
|
226
475
|
function isSupported$1() {
|
|
227
476
|
return typeof HTMLButtonElement !== "undefined" && "command" in HTMLButtonElement.prototype && "source" in ((globalThis.CommandEvent || {}).prototype || {});
|
|
@@ -450,14 +699,17 @@ function apply$1() {
|
|
|
450
699
|
//#region src/invokers/invokers.ts
|
|
451
700
|
if (isBrowser() && !isSupported$1()) apply$1();
|
|
452
701
|
//#endregion
|
|
453
|
-
//#region ../../node_modules/.pnpm/@oddbird+popover-polyfill@0.
|
|
702
|
+
//#region ../../node_modules/.pnpm/@oddbird+popover-polyfill@0.7.1/node_modules/@oddbird/popover-polyfill/dist/popover-fn.js
|
|
454
703
|
var ToggleEvent = class extends Event {
|
|
455
704
|
oldState;
|
|
456
705
|
newState;
|
|
457
|
-
constructor(type,
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
706
|
+
constructor(type, init = {}) {
|
|
707
|
+
const eventInit = Object.assign({}, init);
|
|
708
|
+
delete eventInit.oldState;
|
|
709
|
+
delete eventInit.newState;
|
|
710
|
+
super(type, eventInit);
|
|
711
|
+
this.oldState = String(init.oldState || "");
|
|
712
|
+
this.newState = String(init.newState || "");
|
|
461
713
|
}
|
|
462
714
|
};
|
|
463
715
|
var popoverToggleTaskQueue = /* @__PURE__ */ new WeakMap();
|
|
@@ -575,7 +827,6 @@ function topMostPopoverAncestor(newPopover, list) {
|
|
|
575
827
|
if (!candidate) return;
|
|
576
828
|
let okNesting = false;
|
|
577
829
|
let candidateAncestor = null;
|
|
578
|
-
let candidatePosition = null;
|
|
579
830
|
while (!okNesting) {
|
|
580
831
|
candidateAncestor = nearestInclusiveOpenPopover(candidate) || null;
|
|
581
832
|
if (candidateAncestor === null) return;
|
|
@@ -583,7 +834,7 @@ function topMostPopoverAncestor(newPopover, list) {
|
|
|
583
834
|
if (newPopover.popover === "hint" || candidateAncestor.popover === "auto") okNesting = true;
|
|
584
835
|
if (!okNesting) candidate = candidateAncestor.parentElement;
|
|
585
836
|
}
|
|
586
|
-
candidatePosition = popoverPositions.get(candidateAncestor);
|
|
837
|
+
const candidatePosition = popoverPositions.get(candidateAncestor) ?? null;
|
|
587
838
|
if (topMostPopoverAncestor2 === null || popoverPositions.get(topMostPopoverAncestor2) < candidatePosition) topMostPopoverAncestor2 = candidateAncestor;
|
|
588
839
|
}
|
|
589
840
|
checkAncestor(newPopover.parentElement || getRootNode(newPopover));
|
|
@@ -782,9 +1033,14 @@ function setInvokerAriaExpanded(el, force = false) {
|
|
|
782
1033
|
}
|
|
783
1034
|
}
|
|
784
1035
|
var ShadowRoot2 = globalThis.ShadowRoot || function() {};
|
|
1036
|
+
var DEFAULT_LAYER_NAME = "popover-polyfill";
|
|
785
1037
|
function isSupported() {
|
|
786
1038
|
return typeof HTMLElement !== "undefined" && typeof HTMLElement.prototype === "object" && "popover" in HTMLElement.prototype;
|
|
787
1039
|
}
|
|
1040
|
+
function isPolyfilled() {
|
|
1041
|
+
var _a;
|
|
1042
|
+
return Boolean(((_a = document.body) == null ? void 0 : _a.showPopover) && !/native code/i.test(document.body.showPopover.toString()));
|
|
1043
|
+
}
|
|
788
1044
|
function patchSelectorFn(object, name, mapper) {
|
|
789
1045
|
const original = object[name];
|
|
790
1046
|
Object.defineProperty(object, name, { value(selector) {
|
|
@@ -795,10 +1051,12 @@ var nonEscapedPopoverSelector = /(^|[^\\]):popover-open\b/g;
|
|
|
795
1051
|
function hasLayerSupport() {
|
|
796
1052
|
return typeof globalThis.CSSLayerBlockRule === "function";
|
|
797
1053
|
}
|
|
798
|
-
function getStyles() {
|
|
1054
|
+
function getStyles(layerName) {
|
|
1055
|
+
var _a;
|
|
799
1056
|
const useLayer = hasLayerSupport();
|
|
1057
|
+
const layerNameEscaped = (layerName ?? ((_a = window.POPOVER_POLYFILL_OPTIONS) == null ? void 0 : _a.layerName) ?? DEFAULT_LAYER_NAME).split(".").map(CSS.escape).join(".");
|
|
800
1058
|
return `
|
|
801
|
-
${useLayer ?
|
|
1059
|
+
${useLayer ? `@layer ${layerNameEscaped} {` : ""}
|
|
802
1060
|
:where([popover]) {
|
|
803
1061
|
position: fixed;
|
|
804
1062
|
z-index: 2147483647;
|
|
@@ -862,8 +1120,8 @@ ${useLayer ? "}" : ""}
|
|
|
862
1120
|
`;
|
|
863
1121
|
}
|
|
864
1122
|
var popoverStyleSheet = null;
|
|
865
|
-
function injectStyles(root) {
|
|
866
|
-
const styles = getStyles();
|
|
1123
|
+
function injectStyles(root, layerName) {
|
|
1124
|
+
const styles = getStyles(layerName);
|
|
867
1125
|
if (popoverStyleSheet === null) try {
|
|
868
1126
|
popoverStyleSheet = new CSSStyleSheet();
|
|
869
1127
|
popoverStyleSheet.replaceSync(styles);
|
|
@@ -877,8 +1135,9 @@ function injectStyles(root) {
|
|
|
877
1135
|
else root.prepend(sheet);
|
|
878
1136
|
} else root.adoptedStyleSheets = [popoverStyleSheet, ...root.adoptedStyleSheets];
|
|
879
1137
|
}
|
|
880
|
-
function apply() {
|
|
1138
|
+
function apply(opts) {
|
|
881
1139
|
if (typeof window === "undefined") return;
|
|
1140
|
+
const layerName = opts == null ? void 0 : opts.layerName;
|
|
882
1141
|
window.ToggleEvent = window.ToggleEvent || ToggleEvent;
|
|
883
1142
|
function rewriteSelector(selector) {
|
|
884
1143
|
if (selector == null ? void 0 : selector.includes(":popover-open")) selector = selector.replace(nonEscapedPopoverSelector, "$1.\\:popover-open");
|
|
@@ -939,7 +1198,7 @@ function apply() {
|
|
|
939
1198
|
writable: true,
|
|
940
1199
|
value(options) {
|
|
941
1200
|
const shadowRoot = originalAttachShadow.call(this, options);
|
|
942
|
-
injectStyles(shadowRoot);
|
|
1201
|
+
injectStyles(shadowRoot, layerName);
|
|
943
1202
|
return shadowRoot;
|
|
944
1203
|
}
|
|
945
1204
|
} });
|
|
@@ -950,7 +1209,7 @@ function apply() {
|
|
|
950
1209
|
writable: true,
|
|
951
1210
|
value() {
|
|
952
1211
|
const internals = originalAttachInternals.call(this);
|
|
953
|
-
if (internals.shadowRoot) injectStyles(internals.shadowRoot);
|
|
1212
|
+
if (internals.shadowRoot) injectStyles(internals.shadowRoot, layerName);
|
|
954
1213
|
return internals;
|
|
955
1214
|
}
|
|
956
1215
|
} });
|
|
@@ -1032,23 +1291,25 @@ function apply() {
|
|
|
1032
1291
|
root.addEventListener("pointerup", lightDismissOpenPopovers);
|
|
1033
1292
|
};
|
|
1034
1293
|
addEventListeners(document);
|
|
1035
|
-
injectStyles(document);
|
|
1294
|
+
injectStyles(document, layerName);
|
|
1036
1295
|
}
|
|
1037
1296
|
//#endregion
|
|
1038
1297
|
//#region src/popover/popover.ts
|
|
1039
|
-
if (isBrowser() && !isSupported()) apply();
|
|
1298
|
+
if (isBrowser() && !isSupported() && !isPolyfilled()) apply({ layerName: "ds.base" });
|
|
1299
|
+
const ROOTS = /* @__PURE__ */ new Map();
|
|
1040
1300
|
const ATTR_PLACE = "data-placement";
|
|
1041
1301
|
const ATTR_AUTO = "data-autoplacement";
|
|
1042
1302
|
const POPOVERS = /* @__PURE__ */ new Map();
|
|
1043
|
-
|
|
1044
|
-
|
|
1303
|
+
const handleToggle = (e) => toggle(getComposedTarget(e), e.newState, e.oldState, e.source || e.detail);
|
|
1304
|
+
function toggle(el, newState, oldState, source) {
|
|
1045
1305
|
const float = el instanceof HTMLElement && attr(el, "popover") !== null && getCSSProp(el, "--_ds-floating");
|
|
1046
|
-
|
|
1047
|
-
if (newState === "
|
|
1306
|
+
const prev = POPOVERS.get(el);
|
|
1307
|
+
if (newState === "open" && prev && prev.source === source) return;
|
|
1308
|
+
prev?.cleanup();
|
|
1309
|
+
if (newState === "closed" || !float) return;
|
|
1048
1310
|
if (!source) {
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
source = el.id && root?.querySelector?.(css) || void 0;
|
|
1311
|
+
const css = el.id && `[popovertarget="${el.id}"],[commandfor="${el.id}"]`;
|
|
1312
|
+
source = css && getRoot(el).querySelector(css) || void 0;
|
|
1052
1313
|
}
|
|
1053
1314
|
if (!source || source === el || oldState && oldState === newState) return;
|
|
1054
1315
|
el.style.scrollMarginBottom = `var(--_ds-floating-arrow-size)`;
|
|
@@ -1081,19 +1342,66 @@ function handleToggle(event) {
|
|
|
1081
1342
|
]
|
|
1082
1343
|
};
|
|
1083
1344
|
const unfloat = autoUpdate(source, el, async () => {
|
|
1084
|
-
if (!source?.isConnected) return POPOVERS.get(el)?.();
|
|
1345
|
+
if (!source?.isConnected) return POPOVERS.get(el)?.cleanup();
|
|
1085
1346
|
const { x, y } = await computePosition(source, el, options);
|
|
1086
1347
|
el.style.translate = `${x}px ${y}px`;
|
|
1087
1348
|
});
|
|
1088
|
-
POPOVERS.set(el,
|
|
1349
|
+
POPOVERS.set(el, {
|
|
1350
|
+
source,
|
|
1351
|
+
cleanup: () => POPOVERS.delete(el) && unfloat()
|
|
1352
|
+
});
|
|
1089
1353
|
}
|
|
1090
1354
|
let IS_SCROLL;
|
|
1091
|
-
const handleScrollbar = (
|
|
1092
|
-
if (type === "
|
|
1093
|
-
if (type === "scroll" && IS_SCROLL === false) IS_SCROLL = true;
|
|
1094
|
-
if (type === "
|
|
1355
|
+
const handleScrollbar = (e) => {
|
|
1356
|
+
if (e.type === "pointerdown") IS_SCROLL = false;
|
|
1357
|
+
if (e.type === "scroll" && IS_SCROLL === false) IS_SCROLL = true;
|
|
1358
|
+
if (e.type === "pointerup" && IS_SCROLL) for (const [popover] of POPOVERS) popover.showPopover();
|
|
1359
|
+
};
|
|
1360
|
+
const handleClick$1 = (e) => {
|
|
1361
|
+
for (const [root, off] of ROOTS) if (!root.host?.isConnected && ROOTS.delete(root)) off();
|
|
1362
|
+
const root = getRoot(getComposedTarget(e));
|
|
1363
|
+
if (root instanceof ShadowRoot && !ROOTS.has(root)) ROOTS.set(root, on(root, "toggle", handleToggle, QUICK_EVENT));
|
|
1095
1364
|
};
|
|
1096
|
-
onHotReload("popover", () =>
|
|
1365
|
+
onHotReload("popover", () => {
|
|
1366
|
+
const isFunction = (fn) => typeof fn === "function";
|
|
1367
|
+
const togglePopover = HTMLElement.prototype.togglePopover;
|
|
1368
|
+
const showPopover = HTMLElement.prototype.showPopover;
|
|
1369
|
+
const hidePopover = HTMLElement.prototype.hidePopover;
|
|
1370
|
+
if (isFunction(togglePopover)) HTMLElement.prototype.togglePopover = function(opt) {
|
|
1371
|
+
const isOpen = this.matches(":popover-open");
|
|
1372
|
+
const isBool = typeof opt === "boolean";
|
|
1373
|
+
const prev = isOpen ? "open" : "closed";
|
|
1374
|
+
const next = (isBool ? opt : opt?.force ?? !isOpen) ? "open" : "closed";
|
|
1375
|
+
const source = isBool ? void 0 : opt?.source;
|
|
1376
|
+
const result = togglePopover.call(this, opt);
|
|
1377
|
+
toggle(this, next, prev, source);
|
|
1378
|
+
return result;
|
|
1379
|
+
};
|
|
1380
|
+
if (isFunction(showPopover)) HTMLElement.prototype.showPopover = function(opt) {
|
|
1381
|
+
const prev = this.matches(":popover-open") ? "open" : "closed";
|
|
1382
|
+
const result = showPopover.call(this, opt);
|
|
1383
|
+
toggle(this, "open", prev, opt?.source);
|
|
1384
|
+
return result;
|
|
1385
|
+
};
|
|
1386
|
+
if (isFunction(hidePopover)) HTMLElement.prototype.hidePopover = function() {
|
|
1387
|
+
const prev = this.matches(":popover-open") ? "open" : "closed";
|
|
1388
|
+
const result = hidePopover.call(this);
|
|
1389
|
+
toggle(this, "closed", prev);
|
|
1390
|
+
return result;
|
|
1391
|
+
};
|
|
1392
|
+
return [
|
|
1393
|
+
on(document, "click", handleClick$1, QUICK_EVENT),
|
|
1394
|
+
on(document, "pointerdown pointerup scroll", handleScrollbar, QUICK_EVENT),
|
|
1395
|
+
on(document, "toggle ds-toggle-source", handleToggle, QUICK_EVENT),
|
|
1396
|
+
() => {
|
|
1397
|
+
HTMLElement.prototype.togglePopover = togglePopover;
|
|
1398
|
+
HTMLElement.prototype.showPopover = showPopover;
|
|
1399
|
+
HTMLElement.prototype.hidePopover = hidePopover;
|
|
1400
|
+
for (const [, off] of ROOTS) off();
|
|
1401
|
+
ROOTS.clear();
|
|
1402
|
+
}
|
|
1403
|
+
];
|
|
1404
|
+
});
|
|
1097
1405
|
const arrowPseudo = () => ({
|
|
1098
1406
|
name: "arrowPseudo",
|
|
1099
1407
|
fn(data) {
|
|
@@ -1109,89 +1417,104 @@ const arrowPseudo = () => ({
|
|
|
1109
1417
|
});
|
|
1110
1418
|
//#endregion
|
|
1111
1419
|
//#region src/readonly/readonly.ts
|
|
1112
|
-
const isReadOnly = (el) => (el
|
|
1113
|
-
const
|
|
1114
|
-
if (e.key
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
const all = document.querySelectorAll(`input[name="${e.target.name}"]`);
|
|
1120
|
-
const move = e.key?.match(/Arrow(Right|Down)/) ? 1 : -1;
|
|
1121
|
-
all[(all.length + [...all].indexOf(e.target) + move) % all.length]?.focus();
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1420
|
+
const isReadOnly = (el) => attr(el, "readonly") !== null || attr(el, "aria-readonly") === "true";
|
|
1421
|
+
const handleKeydown$1 = (e) => {
|
|
1422
|
+
if (!(e.key === "Tab" || e.altKey || e.ctrlKey || e.metaKey) || e.key?.startsWith("Arrow")) handleSelect(e);
|
|
1423
|
+
};
|
|
1424
|
+
const handleSelect = (e) => {
|
|
1425
|
+
const el = getComposedTarget(e);
|
|
1426
|
+
if (el?.nodeName === "SELECT" && isReadOnly(el)) e.preventDefault();
|
|
1124
1427
|
};
|
|
1125
1428
|
const handleClick = (e) => {
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1429
|
+
for (let el of e.composedPath()) {
|
|
1430
|
+
if (el?.nodeName === "LABEL") el = el.control;
|
|
1431
|
+
if (el?.nodeName === "INPUT" || el?.nodeName === "SELECT") {
|
|
1432
|
+
if (isReadOnly(el)) {
|
|
1433
|
+
e.stopImmediatePropagation();
|
|
1434
|
+
e.preventDefault();
|
|
1435
|
+
el[el.nodeName === "SELECT" ? "blur" : "focus"]();
|
|
1436
|
+
requestAnimationFrame(() => el.isConnected && el.focus());
|
|
1437
|
+
}
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1130
1440
|
}
|
|
1131
1441
|
};
|
|
1132
|
-
const handleMouseDown = (e) => {
|
|
1133
|
-
if (e.target instanceof HTMLSelectElement && isReadOnly(e.target)) e.preventDefault();
|
|
1134
|
-
};
|
|
1135
1442
|
onHotReload("readonly", () => [
|
|
1136
|
-
on(document, "keydown",
|
|
1137
|
-
on(document, "click", handleClick),
|
|
1138
|
-
on(document, "mousedown",
|
|
1443
|
+
on(document, "keydown", handleKeydown$1),
|
|
1444
|
+
on(document, "click", handleClick, true),
|
|
1445
|
+
on(document, "mousedown", handleSelect, true)
|
|
1139
1446
|
]);
|
|
1140
1447
|
//#endregion
|
|
1141
1448
|
//#region src/toggle-group/toggle-group.ts
|
|
1142
|
-
const
|
|
1143
|
-
const ARIA_LABEL$1 = "aria-label";
|
|
1449
|
+
const ATTR_FOCUSGROUP = "focusgroup";
|
|
1144
1450
|
const ATTR_TOGGLEGROUP = "data-toggle-group";
|
|
1145
1451
|
const SELECTOR_TOGGLEGROUP = `[${ATTR_TOGGLEGROUP}]`;
|
|
1146
|
-
|
|
1147
|
-
|
|
1452
|
+
let IS_ARROW;
|
|
1453
|
+
const handleMutations$1 = (root, records) => {
|
|
1454
|
+
for (const r of records || [null]) if (r?.attributeName === ATTR_TOGGLEGROUP) deprecate(r.target);
|
|
1455
|
+
else if (!r || r.addedNodes.length) {
|
|
1456
|
+
const scope = r?.target || root;
|
|
1457
|
+
for (const el of scope.querySelectorAll(SELECTOR_TOGGLEGROUP)) deprecate(el);
|
|
1458
|
+
}
|
|
1148
1459
|
};
|
|
1149
|
-
const
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
if (!attr(group, ARIA_LABEL$1) && !attr(group, ARIA_LABELLEDBY)) attr(group, ARIA_LABEL$1, attrOrCSS(group, ATTR_TOGGLEGROUP));
|
|
1154
|
-
if (key === "Enter") el.click();
|
|
1155
|
-
if (key?.startsWith("Arrow")) {
|
|
1156
|
-
event.preventDefault?.();
|
|
1157
|
-
const inputs = [...group.getElementsByTagName("input")];
|
|
1158
|
-
const index = inputs.indexOf(el);
|
|
1159
|
-
const move = key.match(/Arrow(Right|Down)/) ? 1 : -1;
|
|
1160
|
-
let nextIndex = index;
|
|
1161
|
-
for (let i = 0; i < inputs.length; i++) {
|
|
1162
|
-
nextIndex = (inputs.length + nextIndex + move) % inputs.length;
|
|
1163
|
-
if (!inputs[nextIndex]?.disabled) {
|
|
1164
|
-
inputs[nextIndex]?.focus();
|
|
1165
|
-
break;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1460
|
+
const handleRadioClick = (e) => {
|
|
1461
|
+
if (IS_ARROW && getRadioInFocusGroup(e)) {
|
|
1462
|
+
e.preventDefault();
|
|
1463
|
+
e.stopImmediatePropagation();
|
|
1168
1464
|
}
|
|
1169
1465
|
};
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
}
|
|
1466
|
+
const resetIsArrow = () => IS_ARROW = false;
|
|
1467
|
+
const handleKeydown = (e) => {
|
|
1468
|
+
IS_ARROW = e.key?.startsWith("Arrow");
|
|
1469
|
+
if (e.key === "Enter") getRadioInFocusGroup(e)?.click();
|
|
1470
|
+
if (IS_ARROW) setTimeout(resetIsArrow, 0);
|
|
1471
|
+
};
|
|
1472
|
+
const getRadioInFocusGroup = (e) => {
|
|
1473
|
+
const el = getComposedTarget(e);
|
|
1474
|
+
if (el?.nodeName !== "INPUT" || el.type !== "radio" || !el.name) return;
|
|
1475
|
+
for (const group of getComposedPath(el)) {
|
|
1476
|
+
if (group.nodeType !== 1 || !group.hasAttribute(ATTR_FOCUSGROUP)) continue;
|
|
1477
|
+
if (!attr(group, ATTR_FOCUSGROUP)?.includes("radiogroup")) return;
|
|
1478
|
+
return el;
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
const deprecate = (el) => {
|
|
1482
|
+
if (el.hasAttribute(ATTR_FOCUSGROUP)) return;
|
|
1483
|
+
const label = attrOrCSS(el, ATTR_TOGGLEGROUP);
|
|
1484
|
+
const labelledby = attr(el, ARIA_LABELLEDBY)?.trim();
|
|
1485
|
+
const message = `Please use ${ATTR_FOCUSGROUP}="radiogroup" and ${labelledby ? ARIA_LABELLEDBY : ARIA_LABEL}="${labelledby || label}" instead of deprecated ${ATTR_TOGGLEGROUP} on:`;
|
|
1486
|
+
attr(el, ARIA_LABEL, labelledby ? null : label);
|
|
1487
|
+
attr(el, ATTR_FOCUSGROUP, "radiogroup");
|
|
1488
|
+
warn(message, el);
|
|
1489
|
+
};
|
|
1490
|
+
onHotReload("toggle-group", () => [
|
|
1491
|
+
on(document, "click", handleRadioClick, true),
|
|
1492
|
+
on(document, "keydown", handleKeydown),
|
|
1493
|
+
onMutation(document, handleMutations$1, {
|
|
1494
|
+
attributeFilter: [ATTR_TOGGLEGROUP],
|
|
1495
|
+
attributes: true,
|
|
1496
|
+
childList: true,
|
|
1497
|
+
subtree: true
|
|
1498
|
+
})
|
|
1499
|
+
]);
|
|
1176
1500
|
//#endregion
|
|
1177
1501
|
//#region src/tooltip/tooltip.ts
|
|
1178
1502
|
let TIP;
|
|
1179
|
-
let
|
|
1180
|
-
let
|
|
1181
|
-
let
|
|
1182
|
-
let
|
|
1183
|
-
const IS_IOS = isBrowser() && /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
1184
|
-
const ATTR_TOOLTIP = "data-tooltip";
|
|
1503
|
+
let TIMER;
|
|
1504
|
+
let TARGET;
|
|
1505
|
+
let OPEN;
|
|
1506
|
+
let LAST_HIDE = 0;
|
|
1185
1507
|
const ATTR_COLOR = "data-color";
|
|
1186
|
-
const ARIA_LABEL = "aria-label";
|
|
1187
|
-
const ARIA_DESC = "aria-description";
|
|
1188
|
-
const SELECTOR_COLOR = `[${ATTR_COLOR}]`;
|
|
1189
|
-
const SELECTOR_TOOLTIP = `[${ATTR_TOOLTIP}]`;
|
|
1190
1508
|
const ATTR_SCHEME = "data-color-scheme";
|
|
1191
|
-
const
|
|
1192
|
-
const
|
|
1509
|
+
const ATTR_SIZE = "data-size";
|
|
1510
|
+
const ATTR_TOOLTIP = "data-tooltip";
|
|
1193
1511
|
const DELAY_HOVER = 300;
|
|
1194
1512
|
const DELAY_SKIP = 300;
|
|
1513
|
+
const IS_IOS = isBrowser() && /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
1514
|
+
const SELECTOR_COLOR = `[${ATTR_COLOR}]`;
|
|
1515
|
+
const SELECTOR_SCHEME = `[${ATTR_SCHEME}]`;
|
|
1516
|
+
const SELECTOR_SIZE = `[${ATTR_SIZE}]`;
|
|
1517
|
+
const SELECTOR_TOOLTIP = `[${ATTR_TOOLTIP}]`;
|
|
1195
1518
|
/**
|
|
1196
1519
|
* setTooltipElement
|
|
1197
1520
|
* @description Allows setting a custom tooltip element. It does not need to, and should not, be injected to document.body, as we inject on hover to ensure React hydration works as expected.
|
|
@@ -1199,75 +1522,83 @@ const DELAY_SKIP = 300;
|
|
|
1199
1522
|
*/
|
|
1200
1523
|
const setTooltipElement = (el) => {
|
|
1201
1524
|
if (el && !(el instanceof HTMLElement)) warn("setTooltipElement expects an HTMLElement, got: ", el);
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
SOURCE = void 0;
|
|
1205
|
-
IS_HOVERING = false;
|
|
1525
|
+
hide();
|
|
1526
|
+
LAST_HIDE = 0;
|
|
1206
1527
|
TIP = el || void 0;
|
|
1207
1528
|
};
|
|
1208
|
-
const
|
|
1209
|
-
for (const el of
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
if (
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
const isChanged = isCurrent && text && TIP?.textContent !== text;
|
|
1221
|
-
if (isCurrent && isChanged) {
|
|
1222
|
-
if (TIP) TIP.textContent = text;
|
|
1223
|
-
if (document.activeElement === el) announce(text);
|
|
1529
|
+
const initTooltips = (scope = document) => {
|
|
1530
|
+
for (const el of scope?.querySelectorAll(SELECTOR_TOOLTIP) || []) setupText(el);
|
|
1531
|
+
};
|
|
1532
|
+
const handleMutations = (_, records) => {
|
|
1533
|
+
if (!records) return initTooltips();
|
|
1534
|
+
for (const r of records) {
|
|
1535
|
+
if (r.target === TIP) continue;
|
|
1536
|
+
if (r.attributeName === ATTR_TOOLTIP) setupText(r.target);
|
|
1537
|
+
else for (const el of r.addedNodes) {
|
|
1538
|
+
if (el.nodeType !== 1) continue;
|
|
1539
|
+
if (el.hasAttribute(ATTR_TOOLTIP)) setupText(el);
|
|
1540
|
+
else initTooltips(el);
|
|
1224
1541
|
}
|
|
1225
1542
|
}
|
|
1226
1543
|
};
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
if (
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
}
|
|
1237
|
-
if (
|
|
1238
|
-
|
|
1239
|
-
|
|
1544
|
+
const setupText = (el, canAnnounce = true) => {
|
|
1545
|
+
let text = attrOrCSS(el, ATTR_TOOLTIP) || "";
|
|
1546
|
+
if (text[0] === "#") text = getRoot(el).getElementById(text.slice(1))?.textContent?.trim() || "";
|
|
1547
|
+
if (text !== (el.getAttribute("aria-label") || el.getAttribute("aria-description"))) {
|
|
1548
|
+
const hasText = attr(el, "role") !== "img" && el.textContent?.trim();
|
|
1549
|
+
attr(el, ATTR_TOOLTIP, text);
|
|
1550
|
+
attr(el, ARIA_LABEL, hasText ? null : text);
|
|
1551
|
+
attr(el, ARIA_DESC, hasText ? text : null);
|
|
1552
|
+
if (el.tabIndex === -1) warn("Missing tabindex=\"0\" attribute on: ", el);
|
|
1553
|
+
}
|
|
1554
|
+
if (el === OPEN && TIP?.textContent !== text) {
|
|
1555
|
+
if (TIP) TIP.textContent = text;
|
|
1556
|
+
if (text && canAnnounce && document.activeElement === el) announce(text);
|
|
1240
1557
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1558
|
+
};
|
|
1559
|
+
const handleInterest = (e) => {
|
|
1560
|
+
const target = getComposedTarget(e);
|
|
1561
|
+
if (!target || TARGET === target || TIP?.contains(target)) return;
|
|
1562
|
+
TARGET = target;
|
|
1563
|
+
const source = TARGET?.closest?.(SELECTOR_TOOLTIP) || void 0;
|
|
1564
|
+
if (OPEN === source) return;
|
|
1565
|
+
if (OPEN) hide();
|
|
1566
|
+
OPEN = source;
|
|
1567
|
+
if (!source) return;
|
|
1568
|
+
if (e.type === "focus" || IS_IOS || DELAY_SKIP > Date.now() - LAST_HIDE) return show();
|
|
1569
|
+
if (e.type === "mousemove") TIMER = setTimeout(show, DELAY_HOVER);
|
|
1570
|
+
};
|
|
1571
|
+
const show = () => {
|
|
1572
|
+
if (!OPEN) return hide();
|
|
1243
1573
|
if (!TIP) TIP = tag("div", { class: "ds-tooltip" });
|
|
1244
1574
|
if (!TIP.isConnected) document.body.appendChild(TIP);
|
|
1245
|
-
const color =
|
|
1246
|
-
const scheme =
|
|
1575
|
+
const color = OPEN.closest(SELECTOR_COLOR);
|
|
1576
|
+
const scheme = OPEN.closest(SELECTOR_SCHEME);
|
|
1577
|
+
const size = OPEN.closest(SELECTOR_SIZE);
|
|
1247
1578
|
const isReset = color !== scheme && color?.contains(scheme);
|
|
1248
|
-
clearTimeout(SKIP_TIMER);
|
|
1249
1579
|
attr(TIP, "popover", "manual");
|
|
1250
1580
|
attr(TIP, ATTR_SCHEME, scheme?.getAttribute(ATTR_SCHEME) || null);
|
|
1251
1581
|
attr(TIP, ATTR_COLOR, isReset && color?.getAttribute(ATTR_COLOR) || null);
|
|
1252
|
-
TIP
|
|
1582
|
+
attr(TIP, ATTR_SIZE, size?.getAttribute(ATTR_SIZE) || null);
|
|
1583
|
+
setupText(OPEN, false);
|
|
1253
1584
|
TIP.showPopover();
|
|
1254
|
-
TIP.dispatchEvent(new CustomEvent("ds-toggle-source", {
|
|
1255
|
-
|
|
1256
|
-
|
|
1585
|
+
TIP.dispatchEvent(new CustomEvent("ds-toggle-source", {
|
|
1586
|
+
bubbles: true,
|
|
1587
|
+
composed: true,
|
|
1588
|
+
detail: OPEN
|
|
1589
|
+
}));
|
|
1257
1590
|
};
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
if (
|
|
1261
|
-
if (!event)
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
SKIP_TIMER = setTimeout(handleClose, DELAY_SKIP);
|
|
1265
|
-
}
|
|
1591
|
+
const hide = (event) => {
|
|
1592
|
+
if (event?.type === "keydown" && event?.key !== "Escape") return;
|
|
1593
|
+
if (OPEN && TIP?.isConnected && TIP.popover) TIP.hidePopover();
|
|
1594
|
+
if (!event) LAST_HIDE = Date.now();
|
|
1595
|
+
clearTimeout(TIMER);
|
|
1596
|
+
OPEN = void 0;
|
|
1266
1597
|
};
|
|
1267
1598
|
onHotReload("tooltip", () => [
|
|
1268
|
-
on(document, "
|
|
1269
|
-
on(document, "
|
|
1270
|
-
onMutation(document,
|
|
1599
|
+
on(document, "focus mousemove", handleInterest, QUICK_EVENT),
|
|
1600
|
+
on(document, "keydown", hide, QUICK_EVENT),
|
|
1601
|
+
onMutation(document, handleMutations, {
|
|
1271
1602
|
attributeFilter: [ATTR_TOOLTIP],
|
|
1272
1603
|
attributes: true,
|
|
1273
1604
|
childList: true,
|
|
@@ -1276,18 +1607,19 @@ onHotReload("tooltip", () => [
|
|
|
1276
1607
|
]);
|
|
1277
1608
|
//#endregion
|
|
1278
1609
|
//#region src/breadcrumbs/breadcrumbs.ts
|
|
1279
|
-
const ATTR_LABEL$1 = "aria-label";
|
|
1280
1610
|
var DSBreadcrumbsElement = class extends DSElement {
|
|
1281
1611
|
_items;
|
|
1282
|
-
_label =
|
|
1612
|
+
_label = {
|
|
1613
|
+
key: ARIA_LABEL,
|
|
1614
|
+
value: null
|
|
1615
|
+
};
|
|
1283
1616
|
_unresize;
|
|
1284
1617
|
_unmutate;
|
|
1285
1618
|
static get observedAttributes() {
|
|
1286
|
-
return [
|
|
1619
|
+
return [ARIA_LABEL, ARIA_LABELLEDBY];
|
|
1287
1620
|
}
|
|
1288
1621
|
connectedCallback() {
|
|
1289
1622
|
const resize = debounce(() => render$3(this), 100);
|
|
1290
|
-
this._label = attrOrCSS(this, ATTR_LABEL$1);
|
|
1291
1623
|
this._items = this.getElementsByTagName("a");
|
|
1292
1624
|
this._unresize = on(window, "resize", resize);
|
|
1293
1625
|
this._unmutate = onMutation(this, render$3, {
|
|
@@ -1295,10 +1627,8 @@ var DSBreadcrumbsElement = class extends DSElement {
|
|
|
1295
1627
|
subtree: true
|
|
1296
1628
|
});
|
|
1297
1629
|
}
|
|
1298
|
-
attributeChangedCallback(
|
|
1299
|
-
if (
|
|
1300
|
-
this._label = next;
|
|
1301
|
-
render$3(this);
|
|
1630
|
+
attributeChangedCallback() {
|
|
1631
|
+
if (this._unmutate) render$3(this);
|
|
1302
1632
|
}
|
|
1303
1633
|
disconnectedCallback() {
|
|
1304
1634
|
this._unresize?.();
|
|
@@ -1311,8 +1641,18 @@ const render$3 = (self) => {
|
|
|
1311
1641
|
const lastItemInList = lastItem?.parentElement === self ? null : lastItem;
|
|
1312
1642
|
const isListHidden = !lastItemInList?.offsetHeight;
|
|
1313
1643
|
attr(self, "role", isListHidden ? null : "navigation");
|
|
1314
|
-
attr(self, ATTR_LABEL$1, isListHidden ? null : self._label);
|
|
1315
1644
|
for (const item of self._items || []) attr(item, "aria-current", item === lastItemInList ? "page" : null);
|
|
1645
|
+
const label = attrOrCSS(self, ARIA_LABEL)?.trim();
|
|
1646
|
+
const labelledby = attr(self, ARIA_LABELLEDBY)?.trim();
|
|
1647
|
+
const prevLabel = label || labelledby;
|
|
1648
|
+
if (prevLabel) self._label.value = prevLabel;
|
|
1649
|
+
if (prevLabel) self._label.key = labelledby ? ARIA_LABELLEDBY : ARIA_LABEL;
|
|
1650
|
+
const nextLabel = isListHidden ? null : self._label.value;
|
|
1651
|
+
if (prevLabel !== nextLabel) {
|
|
1652
|
+
attr(self, ARIA_LABELLEDBY, null);
|
|
1653
|
+
attr(self, ARIA_LABEL, null);
|
|
1654
|
+
attr(self, self._label.key, nextLabel);
|
|
1655
|
+
}
|
|
1316
1656
|
};
|
|
1317
1657
|
customElements.define("ds-breadcrumbs", DSBreadcrumbsElement);
|
|
1318
1658
|
//#endregion
|
|
@@ -1339,11 +1679,11 @@ var DSErrorSummaryElement = class extends DSElement {
|
|
|
1339
1679
|
}
|
|
1340
1680
|
};
|
|
1341
1681
|
const render$2 = (self) => {
|
|
1342
|
-
const label = attr(self,
|
|
1343
|
-
const labelledBy = attr(self,
|
|
1682
|
+
const label = attr(self, ARIA_LABEL)?.trim();
|
|
1683
|
+
const labelledBy = attr(self, ARIA_LABELLEDBY)?.trim();
|
|
1344
1684
|
const heading = self.querySelector("h2,h3,h4,h5,h6");
|
|
1345
|
-
if (heading && !label && !labelledBy) attr(self,
|
|
1346
|
-
if (!heading && !label && !labelledBy) warn("Missing accessible name on:", self,
|
|
1685
|
+
if (heading && !label && !labelledBy) attr(self, ARIA_LABELLEDBY, useId(heading));
|
|
1686
|
+
if (!heading && !label && !labelledBy) warn("Missing accessible name on:", self, `\nAdd a heading (h2–h6), or set ${ARIA_LABEL} or ${ARIA_LABELLEDBY} to provide an accessible name for screen readers.`);
|
|
1347
1687
|
};
|
|
1348
1688
|
customElements.define("ds-error-summary", DSErrorSummaryElement);
|
|
1349
1689
|
//#endregion
|
|
@@ -1352,32 +1692,22 @@ const ATTR_INVALID = "aria-invalid";
|
|
|
1352
1692
|
const ATTR_DESCRIBEDBY = "aria-describedby";
|
|
1353
1693
|
const ATTR_INDETERMINATE = "data-indeterminate";
|
|
1354
1694
|
const COUNTER_DEBOUNCE = isWindows() ? 800 : 200;
|
|
1355
|
-
const COUNTS = /* @__PURE__ */ new WeakMap();
|
|
1356
|
-
const FIELDS = /* @__PURE__ */ new Map();
|
|
1357
|
-
const VALIDATIONS = /* @__PURE__ */ new WeakSet();
|
|
1358
1695
|
const WARNING_MULTIPLE_INPUTS = `Fields should only have one input element. Use <fieldset> to group multiple fields:`;
|
|
1359
|
-
const handleFieldMutations = (_doc, records = []) => {
|
|
1360
|
-
for (const { target } of records) {
|
|
1361
|
-
const isFieldset = target instanceof HTMLFieldSetElement;
|
|
1362
|
-
for (const [field] of FIELDS) if (isFieldset ? target.contains(field) : field.contains(target)) handleFieldMutation(field);
|
|
1363
|
-
}
|
|
1364
|
-
};
|
|
1365
1696
|
const handleFieldMutation = (field) => {
|
|
1366
1697
|
const labels = [];
|
|
1367
1698
|
const nextDescs = [];
|
|
1368
|
-
const prevDescs =
|
|
1369
|
-
let input;
|
|
1370
|
-
let counter;
|
|
1699
|
+
const prevDescs = field._descs || [];
|
|
1371
1700
|
let hasValidation = false;
|
|
1372
1701
|
let invalid = false;
|
|
1702
|
+
if (field._input?.hidden) field._input = void 0;
|
|
1373
1703
|
for (const el of field.getElementsByTagName("*")) {
|
|
1374
1704
|
if (el instanceof HTMLLabelElement) labels.push(el);
|
|
1375
1705
|
if (el.hidden) continue;
|
|
1376
|
-
if (isInputLike(el)) if (
|
|
1377
|
-
else
|
|
1706
|
+
if (isInputLike(el)) if (field._input?.isConnected && field._input !== el) warn(WARNING_MULTIPLE_INPUTS, field);
|
|
1707
|
+
else field._input = el;
|
|
1378
1708
|
else {
|
|
1379
1709
|
const type = el.getAttribute("data-field");
|
|
1380
|
-
if (type === "counter")
|
|
1710
|
+
if (type === "counter") field._counter = el;
|
|
1381
1711
|
if (type === "validation") {
|
|
1382
1712
|
nextDescs.unshift(useId(el));
|
|
1383
1713
|
hasValidation = true;
|
|
@@ -1385,9 +1715,9 @@ const handleFieldMutation = (field) => {
|
|
|
1385
1715
|
} else if (type) nextDescs.push(useId(el));
|
|
1386
1716
|
}
|
|
1387
1717
|
}
|
|
1388
|
-
if (!
|
|
1389
|
-
|
|
1390
|
-
for (const label of labels) attr(label, "for", useId(input));
|
|
1718
|
+
if (!field._input?.isConnected) return;
|
|
1719
|
+
const input = field._input;
|
|
1720
|
+
for (const label of labels) attr(label, "for", useId(input) || null);
|
|
1391
1721
|
const fieldsetValidation = field.closest("fieldset")?.querySelector(":scope > [data-field=\"validation\"]");
|
|
1392
1722
|
if (fieldsetValidation && !fieldsetValidation?.hidden) {
|
|
1393
1723
|
hasValidation = true;
|
|
@@ -1399,69 +1729,76 @@ const handleFieldMutation = (field) => {
|
|
|
1399
1729
|
if (input.type === "radio" || input.type === "checkbox") attr(field, "data-clickdelegatefor", useId(input));
|
|
1400
1730
|
const keep = (attr(input, ATTR_DESCRIBEDBY)?.trim().split(/\s+/))?.filter((id) => !prevDescs.includes(id)) || [];
|
|
1401
1731
|
attr(input, ATTR_DESCRIBEDBY, [...nextDescs, ...keep].join(" ") || null);
|
|
1402
|
-
|
|
1403
|
-
const hadValidation =
|
|
1732
|
+
field._descs = nextDescs;
|
|
1733
|
+
const hadValidation = field._validation;
|
|
1404
1734
|
if (hasValidation && !hadValidation && !input.hasAttribute(ATTR_INVALID)) {
|
|
1405
1735
|
attr(input, ATTR_INVALID, "true");
|
|
1406
|
-
|
|
1736
|
+
field._validation = true;
|
|
1407
1737
|
} else if (!hasValidation && hadValidation) {
|
|
1408
1738
|
attr(input, ATTR_INVALID, null);
|
|
1409
|
-
|
|
1739
|
+
field._validation = void 0;
|
|
1410
1740
|
}
|
|
1411
|
-
|
|
1741
|
+
field.handleEvent();
|
|
1412
1742
|
};
|
|
1413
1743
|
const TEXTS = {
|
|
1414
1744
|
over: "%d tegn for mye",
|
|
1415
1745
|
under: "%d tegn igjen"
|
|
1416
1746
|
};
|
|
1417
|
-
const handleFieldInput = (e) => {
|
|
1418
|
-
const input = e.target || e;
|
|
1419
|
-
const counter = COUNTS.get(input);
|
|
1420
|
-
if (counter?.isConnected) {
|
|
1421
|
-
const count = (Number(attr(counter, "data-limit")) || 0) - input.value.length;
|
|
1422
|
-
const state = count < 0 ? "over" : "under";
|
|
1423
|
-
const label = (attrOrCSS(counter, `data-${state}`) || TEXTS[state])?.replace("%d", `${Math.abs(count)}`);
|
|
1424
|
-
attr(counter, "data-label", label);
|
|
1425
|
-
attr(counter, "data-state", state);
|
|
1426
|
-
attr(counter, "data-color", count < 0 ? "danger" : null);
|
|
1427
|
-
if (e.type === "input" && label) debouncedCounterLiveRegion(input, label);
|
|
1428
|
-
}
|
|
1429
|
-
if (input instanceof HTMLTextAreaElement) {
|
|
1430
|
-
input.style.setProperty("--_ds-field-sizing", "auto");
|
|
1431
|
-
input.style.setProperty("--_ds-field-sizing", `${input.scrollHeight}px`);
|
|
1432
|
-
}
|
|
1433
|
-
};
|
|
1434
1747
|
const debouncedCounterLiveRegion = debounce((input, text) => {
|
|
1435
1748
|
if (document.activeElement === input) announce(text);
|
|
1436
1749
|
}, COUNTER_DEBOUNCE);
|
|
1437
1750
|
const isInvalid = (el) => el.getAttribute("data-color") !== "success";
|
|
1438
1751
|
const isInputLike = (el) => el instanceof HTMLElement && "validity" in el && !(el instanceof HTMLButtonElement) && el.type !== "hidden";
|
|
1439
1752
|
var DSFieldElement = class extends DSElement {
|
|
1753
|
+
_counter;
|
|
1754
|
+
_descs;
|
|
1755
|
+
_input;
|
|
1756
|
+
_validation;
|
|
1757
|
+
_unevents;
|
|
1758
|
+
_unmutate;
|
|
1440
1759
|
connectedCallback() {
|
|
1441
|
-
|
|
1442
|
-
handleFieldMutation(this)
|
|
1760
|
+
this._unevents = on(this, "input", this, QUICK_EVENT);
|
|
1761
|
+
this._unmutate = onMutation(this, () => handleFieldMutation(this), {
|
|
1762
|
+
attributeFilter: [
|
|
1763
|
+
"data-field",
|
|
1764
|
+
"data-limit",
|
|
1765
|
+
"hidden",
|
|
1766
|
+
"id",
|
|
1767
|
+
"value",
|
|
1768
|
+
ATTR_INDETERMINATE
|
|
1769
|
+
],
|
|
1770
|
+
attributes: true,
|
|
1771
|
+
childList: true,
|
|
1772
|
+
subtree: true
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
handleEvent(event) {
|
|
1776
|
+
const { _counter, _input } = this;
|
|
1777
|
+
if (_counter?.isConnected && _input) {
|
|
1778
|
+
const count = (Number(attr(_counter, "data-limit")) || 0) - _input.value.length;
|
|
1779
|
+
const state = count < 0 ? "over" : "under";
|
|
1780
|
+
const label = (attrOrCSS(_counter, `data-${state}`) || TEXTS[state])?.replace("%d", `${Math.abs(count)}`);
|
|
1781
|
+
if (!label) warn(`Missing data-${state} on:`, _counter);
|
|
1782
|
+
attr(_counter, "data-label", label);
|
|
1783
|
+
attr(_counter, "data-state", state);
|
|
1784
|
+
attr(_counter, "data-color", count < 0 ? "danger" : null);
|
|
1785
|
+
if (event?.type === "input" && label) debouncedCounterLiveRegion(_input, label);
|
|
1786
|
+
}
|
|
1787
|
+
if (_input instanceof HTMLTextAreaElement) {
|
|
1788
|
+
_input.style.setProperty("--_ds-field-sizing", "auto");
|
|
1789
|
+
_input.style.setProperty("--_ds-field-sizing", `${_input.scrollHeight}px`);
|
|
1790
|
+
}
|
|
1443
1791
|
}
|
|
1444
1792
|
disconnectedCallback() {
|
|
1445
|
-
|
|
1793
|
+
this._unevents?.();
|
|
1794
|
+
this._unmutate?.();
|
|
1795
|
+
this._unevents = this._unmutate = void 0;
|
|
1796
|
+
this._input = this._counter = this._descs = this._validation = void 0;
|
|
1446
1797
|
}
|
|
1447
1798
|
};
|
|
1448
1799
|
customElements.define("ds-field", DSFieldElement);
|
|
1449
|
-
onHotReload("field", () => [on(document, "input", handleFieldInput, QUICK_EVENT), onMutation(document, handleFieldMutations, {
|
|
1450
|
-
attributeFilter: [
|
|
1451
|
-
"data-field",
|
|
1452
|
-
"data-limit",
|
|
1453
|
-
"hidden",
|
|
1454
|
-
"id",
|
|
1455
|
-
"value",
|
|
1456
|
-
ATTR_INDETERMINATE
|
|
1457
|
-
],
|
|
1458
|
-
attributes: true,
|
|
1459
|
-
childList: true,
|
|
1460
|
-
subtree: true
|
|
1461
|
-
})]);
|
|
1462
1800
|
//#endregion
|
|
1463
1801
|
//#region src/pagination/pagination.ts
|
|
1464
|
-
const ATTR_LABEL = "aria-label";
|
|
1465
1802
|
const ATTR_CURRENT = "data-current";
|
|
1466
1803
|
const ATTR_TOTAL = "data-total";
|
|
1467
1804
|
const ATTR_HREF = "data-href";
|
|
@@ -1479,7 +1816,8 @@ var DSPaginationElement = class extends DSElement {
|
|
|
1479
1816
|
_render;
|
|
1480
1817
|
static get observedAttributes() {
|
|
1481
1818
|
return [
|
|
1482
|
-
|
|
1819
|
+
ARIA_LABEL,
|
|
1820
|
+
ARIA_LABELLEDBY,
|
|
1483
1821
|
ATTR_CURRENT,
|
|
1484
1822
|
ATTR_TOTAL,
|
|
1485
1823
|
ATTR_HREF
|
|
@@ -1490,7 +1828,10 @@ var DSPaginationElement = class extends DSElement {
|
|
|
1490
1828
|
const current = attr(this, ATTR_CURRENT);
|
|
1491
1829
|
if (current && !total) warn(`Missing ${ATTR_TOTAL} attribute on:`, this);
|
|
1492
1830
|
if (total && !current) warn(`Missing ${ATTR_CURRENT} attribute on:`, this);
|
|
1493
|
-
|
|
1831
|
+
const label = attrOrCSS(this, ARIA_LABEL);
|
|
1832
|
+
const labelledby = attr(this, ARIA_LABELLEDBY)?.trim();
|
|
1833
|
+
if (label || labelledby) attr(this, ARIA_LABEL, labelledby ? null : label);
|
|
1834
|
+
else warn(`Missing ${ARIA_LABEL} on:`, this);
|
|
1494
1835
|
attr(this, "role", "navigation");
|
|
1495
1836
|
this._unmutate = onMutation(this, render$1, {
|
|
1496
1837
|
childList: true,
|
|
@@ -1520,7 +1861,7 @@ const render$1 = (self) => {
|
|
|
1520
1861
|
items.forEach((item, i) => {
|
|
1521
1862
|
const page = i ? items[i + 1] ? pages[i - 1]?.page : next : prev;
|
|
1522
1863
|
attr(item, "aria-current", pages[i - 1]?.current ? "true" : null);
|
|
1523
|
-
attr(item,
|
|
1864
|
+
attr(item, ARIA_LABEL, `${page ?? "hidden"}`);
|
|
1524
1865
|
attr(item, "role", page ? null : "none");
|
|
1525
1866
|
attr(item, "tabindex", page ? null : "-1");
|
|
1526
1867
|
if (item instanceof HTMLButtonElement) attr(item, "value", `${page}`);
|
|
@@ -1540,32 +1881,66 @@ const getSteps = (now, max, show = Number.POSITIVE_INFINITY) => {
|
|
|
1540
1881
|
customElements.define("ds-pagination", DSPaginationElement);
|
|
1541
1882
|
//#endregion
|
|
1542
1883
|
//#region src/suggestion/suggestion.ts
|
|
1884
|
+
const ATTR_EMPTY = "data-empty";
|
|
1885
|
+
const ATTR_CREATE = "data-create";
|
|
1543
1886
|
var DSSuggestionElement = class extends UHTMLComboboxElement {
|
|
1544
1887
|
_unmutate;
|
|
1545
1888
|
connectedCallback() {
|
|
1546
1889
|
super.connectedCallback();
|
|
1547
1890
|
this._unmutate = onMutation(this, render, { childList: true });
|
|
1891
|
+
on(this, "comboboxafterselect input", handleEmpty, QUICK_EVENT);
|
|
1548
1892
|
on(this, "toggle", polyfillToggleSource, QUICK_EVENT);
|
|
1549
1893
|
}
|
|
1550
1894
|
disconnectedCallback() {
|
|
1551
1895
|
super.disconnectedCallback();
|
|
1552
1896
|
this._unmutate?.();
|
|
1553
1897
|
this._unmutate = void 0;
|
|
1898
|
+
off(this, "comboboxafterselect input", handleEmpty, QUICK_EVENT);
|
|
1554
1899
|
off(this, "toggle", polyfillToggleSource, QUICK_EVENT);
|
|
1555
1900
|
}
|
|
1556
1901
|
};
|
|
1557
1902
|
const render = (self) => {
|
|
1558
1903
|
const { control, list } = self;
|
|
1559
1904
|
const datalist = list || self.querySelector("u-datalist");
|
|
1560
|
-
if (control
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1905
|
+
if (control) {
|
|
1906
|
+
attr(control, "popovertarget", list ? useId(list) : null);
|
|
1907
|
+
if (!control.placeholder) attr(control, "placeholder", " ");
|
|
1908
|
+
}
|
|
1909
|
+
if (datalist) {
|
|
1910
|
+
attr(datalist, "popover", "manual");
|
|
1911
|
+
attr(datalist, "data-is-floating", "true");
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
const handleEmpty = ({ currentTarget: self }) => {
|
|
1915
|
+
const { creatable, control, options } = self;
|
|
1916
|
+
if (!options) return;
|
|
1917
|
+
const value = control?.value.trim() || "";
|
|
1918
|
+
const query = value.toLowerCase();
|
|
1919
|
+
let empty;
|
|
1920
|
+
let exists = !value;
|
|
1921
|
+
for (const opt of options) {
|
|
1922
|
+
if (!empty && opt.hasAttribute(ATTR_EMPTY)) empty = opt;
|
|
1923
|
+
else if (!exists && opt.label?.toLowerCase() === query) exists = true;
|
|
1924
|
+
if (exists && empty) break;
|
|
1925
|
+
}
|
|
1926
|
+
if (!empty) return;
|
|
1927
|
+
empty.hidden = exists;
|
|
1928
|
+
empty.label = value;
|
|
1929
|
+
empty.value = creatable ? value : "";
|
|
1930
|
+
if (!creatable || empty.textContent) return;
|
|
1931
|
+
const text = attrOrCSS(empty, ATTR_EMPTY);
|
|
1932
|
+
if (!text) warn(`Missing ${ATTR_EMPTY} value on:`, empty);
|
|
1933
|
+
else attr(empty, ATTR_EMPTY, text);
|
|
1934
|
+
attr(empty, ATTR_CREATE, text?.replace("{value}", value));
|
|
1564
1935
|
};
|
|
1565
1936
|
const polyfillToggleSource = (event) => {
|
|
1566
1937
|
const self = event.currentTarget;
|
|
1567
1938
|
const detail = event.newState === "open" && self.control;
|
|
1568
|
-
if (detail) self.list?.dispatchEvent(new CustomEvent("ds-toggle-source", {
|
|
1939
|
+
if (detail) self.list?.dispatchEvent(new CustomEvent("ds-toggle-source", {
|
|
1940
|
+
bubbles: true,
|
|
1941
|
+
composed: true,
|
|
1942
|
+
detail
|
|
1943
|
+
}));
|
|
1569
1944
|
};
|
|
1570
1945
|
customElements.define("ds-suggestion", DSSuggestionElement);
|
|
1571
1946
|
//#endregion
|
|
@@ -1579,6 +1954,4 @@ customElements.define("ds-tablist", DSTabListElement);
|
|
|
1579
1954
|
customElements.define("ds-tab", DSTabElement);
|
|
1580
1955
|
customElements.define("ds-tabpanel", DSTabPanelElement);
|
|
1581
1956
|
//#endregion
|
|
1582
|
-
export { DSBreadcrumbsElement, DSErrorSummaryElement, DSFieldElement, DSPaginationElement, DSSuggestionElement, DSTabElement, DSTabListElement, DSTabPanelElement, DSTabsElement, pagination, setTooltipElement };
|
|
1583
|
-
|
|
1584
|
-
//# sourceMappingURL=index.js.map
|
|
1957
|
+
export { DSBreadcrumbsElement, DSErrorSummaryElement, DSFieldElement, DSPaginationElement, DSSuggestionElement, DSTabElement, DSTabListElement, DSTabPanelElement, DSTabsElement, initTooltips, pagination, setTooltipElement };
|