@digdir/designsystemet-web 1.13.1 → 1.13.3

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.
Files changed (71) hide show
  1. package/README.md +12 -1
  2. package/dist/cjs/_vendors/invokers-polyfill/invoker.cjs +2 -0
  3. package/dist/cjs/_vendors/invokers-polyfill/invoker.cjs.map +1 -0
  4. package/dist/cjs/breadcrumbs/breadcrumbs.cjs +1 -1
  5. package/dist/cjs/breadcrumbs/breadcrumbs.cjs.map +1 -1
  6. package/dist/cjs/clickdelegatefor/clickdelegatefor.cjs +1 -1
  7. package/dist/cjs/clickdelegatefor/clickdelegatefor.cjs.map +1 -1
  8. package/dist/cjs/dialog/dialog.cjs +1 -1
  9. package/dist/cjs/dialog/dialog.cjs.map +1 -1
  10. package/dist/cjs/error-summary/error-summary.cjs +1 -1
  11. package/dist/cjs/error-summary/error-summary.cjs.map +1 -1
  12. package/dist/cjs/field/field.cjs +1 -1
  13. package/dist/cjs/field/field.cjs.map +1 -1
  14. package/dist/cjs/fieldset/fieldset.cjs +2 -0
  15. package/dist/cjs/fieldset/fieldset.cjs.map +1 -0
  16. package/dist/cjs/index.cjs +1 -1
  17. package/dist/cjs/index.cjs.map +1 -1
  18. package/dist/cjs/pagination/pagination.cjs +1 -1
  19. package/dist/cjs/pagination/pagination.cjs.map +1 -1
  20. package/dist/cjs/popover/popover.cjs +1 -1
  21. package/dist/cjs/popover/popover.cjs.map +1 -1
  22. package/dist/cjs/readonly/readonly.cjs +1 -1
  23. package/dist/cjs/readonly/readonly.cjs.map +1 -1
  24. package/dist/cjs/suggestion/suggestion.cjs +1 -1
  25. package/dist/cjs/suggestion/suggestion.cjs.map +1 -1
  26. package/dist/cjs/toggle-group/toggle-group.cjs +1 -1
  27. package/dist/cjs/toggle-group/toggle-group.cjs.map +1 -1
  28. package/dist/cjs/tooltip/tooltip.cjs +1 -1
  29. package/dist/cjs/tooltip/tooltip.cjs.map +1 -1
  30. package/dist/cjs/utils/utils.cjs +1 -1
  31. package/dist/cjs/utils/utils.cjs.map +1 -1
  32. package/dist/custom-elements.json +18 -12
  33. package/dist/esm/_vendors/invokers-polyfill/invoker.js +2 -0
  34. package/dist/esm/_vendors/invokers-polyfill/invoker.js.map +1 -0
  35. package/dist/esm/breadcrumbs/breadcrumbs.js +1 -1
  36. package/dist/esm/breadcrumbs/breadcrumbs.js.map +1 -1
  37. package/dist/esm/clickdelegatefor/clickdelegatefor.js +1 -1
  38. package/dist/esm/clickdelegatefor/clickdelegatefor.js.map +1 -1
  39. package/dist/esm/dialog/dialog.js +1 -1
  40. package/dist/esm/dialog/dialog.js.map +1 -1
  41. package/dist/esm/error-summary/error-summary.js +1 -1
  42. package/dist/esm/error-summary/error-summary.js.map +1 -1
  43. package/dist/esm/field/field.js +1 -1
  44. package/dist/esm/field/field.js.map +1 -1
  45. package/dist/esm/fieldset/fieldset.js +2 -0
  46. package/dist/esm/fieldset/fieldset.js.map +1 -0
  47. package/dist/esm/index.js +1 -1
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/pagination/pagination.js +1 -1
  50. package/dist/esm/pagination/pagination.js.map +1 -1
  51. package/dist/esm/popover/popover.js +1 -1
  52. package/dist/esm/popover/popover.js.map +1 -1
  53. package/dist/esm/readonly/readonly.js +1 -1
  54. package/dist/esm/readonly/readonly.js.map +1 -1
  55. package/dist/esm/suggestion/suggestion.js +1 -1
  56. package/dist/esm/suggestion/suggestion.js.map +1 -1
  57. package/dist/esm/toggle-group/toggle-group.js +1 -1
  58. package/dist/esm/toggle-group/toggle-group.js.map +1 -1
  59. package/dist/esm/tooltip/tooltip.js +1 -1
  60. package/dist/esm/tooltip/tooltip.js.map +1 -1
  61. package/dist/esm/utils/utils.js +1 -1
  62. package/dist/esm/utils/utils.js.map +1 -1
  63. package/dist/index.d.ts +115 -93
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +948 -713
  66. package/dist/index.js.map +1 -0
  67. package/dist/umd/index.js +6 -4
  68. package/dist/umd/index.js.map +1 -1
  69. package/package.json +20 -16
  70. package/dist/web.manifest.json +0 -997
  71. package/dist/web.vscode.json +0 -60
package/dist/index.js CHANGED
@@ -1,750 +1,985 @@
1
- // src/index.ts
2
- import { isSupported, apply as polyfillInvokers } from "invokers-polyfill/fn";
3
-
4
- // src/utils/utils.ts
5
- var QUICK_EVENT = { passive: true, capture: true };
6
- var isBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
7
- var isWindows = () => isBrowser() && // @ts-expect-error Typescript has not implemented userAgentData yet https://stackoverflow.com/a/71392474
8
- /^Win/i.test(navigator.userAgentData?.platform || navigator.platform);
9
- var DSElement = typeof HTMLElement === "undefined" ? class {
10
- } : HTMLElement;
1
+ import "@u-elements/u-details/polyfill";
2
+ import { autoUpdate, computePosition, flip, limitShift, offset, shift, size } from "@floating-ui/dom";
3
+ import { UHTMLComboboxElement } from "@u-elements/u-combobox";
4
+ import * as UTabs from "@u-elements/u-tabs";
5
+ export * from "@u-elements/u-datalist";
6
+ //#region ../../node_modules/.pnpm/invokers-polyfill@1.0.2_patch_hash=d5677be15320f04cdc552d82cb4a79242bd1cf8b26bcbb0a13e1153e7bed3b5a/node_modules/invokers-polyfill/invoker.js
7
+ function isSupported() {
8
+ return typeof HTMLButtonElement !== "undefined" && "command" in HTMLButtonElement.prototype && "source" in ((globalThis.CommandEvent || {}).prototype || {});
9
+ }
10
+ function apply() {
11
+ document.addEventListener("invoke", (e) => {
12
+ if (e.type == "invoke" && e.isTrusted) {
13
+ e.stopImmediatePropagation();
14
+ e.preventDefault();
15
+ }
16
+ }, true);
17
+ document.addEventListener("command", (e) => {
18
+ if (e.type == "command" && e.isTrusted) {
19
+ e.stopImmediatePropagation();
20
+ e.preventDefault();
21
+ }
22
+ }, true);
23
+ function enumerate(obj, key, enumerable = true) {
24
+ Object.defineProperty(obj, key, {
25
+ ...Object.getOwnPropertyDescriptor(obj, key),
26
+ enumerable
27
+ });
28
+ }
29
+ function getRootNode(node) {
30
+ if (node && typeof node.getRootNode === "function") return node.getRootNode();
31
+ if (node && node.parentNode) return getRootNode(node.parentNode);
32
+ return node;
33
+ }
34
+ const commandEventSourceElements = /* @__PURE__ */ new WeakMap();
35
+ const commandEventActions = /* @__PURE__ */ new WeakMap();
36
+ class CommandEvent extends Event {
37
+ constructor(type, invokeEventInit = {}) {
38
+ super(type, invokeEventInit);
39
+ const { source, command } = invokeEventInit;
40
+ if (source != null && !(source instanceof Element)) throw new TypeError(`source must be an element`);
41
+ commandEventSourceElements.set(this, source || null);
42
+ commandEventActions.set(this, command !== void 0 ? String(command) : "");
43
+ }
44
+ get [Symbol.toStringTag]() {
45
+ return "CommandEvent";
46
+ }
47
+ get source() {
48
+ if (!commandEventSourceElements.has(this)) throw new TypeError("illegal invocation");
49
+ const source = commandEventSourceElements.get(this);
50
+ if (!(source instanceof Element)) return null;
51
+ const invokerRoot = getRootNode(source);
52
+ if (invokerRoot !== getRootNode(this.target || document)) return invokerRoot.host;
53
+ return source;
54
+ }
55
+ get command() {
56
+ if (!commandEventActions.has(this)) throw new TypeError("illegal invocation");
57
+ return commandEventActions.get(this);
58
+ }
59
+ }
60
+ enumerate(CommandEvent.prototype, "source");
61
+ enumerate(CommandEvent.prototype, "command");
62
+ const invokerAssociatedElements = /* @__PURE__ */ new WeakMap();
63
+ function applyInvokerMixin(ElementClass) {
64
+ Object.defineProperties(ElementClass.prototype, {
65
+ commandForElement: {
66
+ enumerable: true,
67
+ configurable: true,
68
+ set(targetElement) {
69
+ if (targetElement === null) {
70
+ this.removeAttribute("commandfor");
71
+ invokerAssociatedElements.delete(this);
72
+ } else if (!(targetElement instanceof Element)) throw new TypeError(`commandForElement must be an element or null`);
73
+ else {
74
+ this.setAttribute("commandfor", "");
75
+ const targetRootNode = getRootNode(targetElement);
76
+ if (getRootNode(this) === targetRootNode || targetRootNode === this.ownerDocument) invokerAssociatedElements.set(this, targetElement);
77
+ else invokerAssociatedElements.delete(this);
78
+ }
79
+ },
80
+ get() {
81
+ if (this.localName !== "button") return null;
82
+ if (this.disabled) return null;
83
+ if (this.form && this.getAttribute("type") !== "button") {
84
+ console.warn("Element with `commandFor` is a form participant. It should explicitly set `type=button` in order for `commandFor` to work");
85
+ return null;
86
+ }
87
+ const targetElement = invokerAssociatedElements.get(this);
88
+ if (targetElement) if (targetElement.isConnected) return targetElement;
89
+ else {
90
+ invokerAssociatedElements.delete(this);
91
+ return null;
92
+ }
93
+ const root = getRootNode(this);
94
+ const idref = this.getAttribute("commandfor");
95
+ if ((root instanceof Document || root instanceof ShadowRoot) && idref) return root.getElementById(idref) || null;
96
+ return null;
97
+ }
98
+ },
99
+ command: {
100
+ enumerable: true,
101
+ configurable: true,
102
+ get() {
103
+ const value = this.getAttribute("command") || "";
104
+ if (value.startsWith("--")) return value;
105
+ const valueLower = value.toLowerCase();
106
+ switch (valueLower) {
107
+ case "show-modal":
108
+ case "request-close":
109
+ case "close":
110
+ case "toggle-popover":
111
+ case "hide-popover":
112
+ case "show-popover": return valueLower;
113
+ }
114
+ return "";
115
+ },
116
+ set(value) {
117
+ this.setAttribute("command", value);
118
+ }
119
+ }
120
+ });
121
+ }
122
+ const onHandlers = /* @__PURE__ */ new WeakMap();
123
+ Object.defineProperties(HTMLElement.prototype, { oncommand: {
124
+ enumerable: true,
125
+ configurable: true,
126
+ get() {
127
+ oncommandObserver.takeRecords();
128
+ return onHandlers.get(this) || null;
129
+ },
130
+ set(handler) {
131
+ const existing = onHandlers.get(this) || null;
132
+ if (existing) this.removeEventListener("command", existing);
133
+ onHandlers.set(this, typeof handler === "object" || typeof handler === "function" ? handler : null);
134
+ if (typeof handler == "function") this.addEventListener("command", handler);
135
+ }
136
+ } });
137
+ function applyOnCommandHandler(els) {
138
+ for (const el of els) el.oncommand = new Function("event", el.getAttribute("oncommand"));
139
+ }
140
+ const oncommandObserver = new MutationObserver((records) => {
141
+ for (const record of records) {
142
+ const { target } = record;
143
+ if (record.type === "childList") applyOnCommandHandler(target.querySelectorAll("[oncommand]"));
144
+ else applyOnCommandHandler([target]);
145
+ }
146
+ });
147
+ oncommandObserver.observe(document, {
148
+ subtree: true,
149
+ childList: true,
150
+ attributeFilter: ["oncommand"]
151
+ });
152
+ applyOnCommandHandler(document.querySelectorAll("[oncommand]"));
153
+ const processedEvents = /* @__PURE__ */ new WeakSet();
154
+ function handleInvokerActivation(event) {
155
+ if (processedEvents.has(event)) return;
156
+ processedEvents.add(event);
157
+ if (event.defaultPrevented) return;
158
+ if (event.type !== "click") return;
159
+ const source = event.composedPath().find((el) => el.matches?.("button[commandfor], button[command]"));
160
+ if (!source) return;
161
+ if (source.form && source.getAttribute("type") !== "button") {
162
+ event.preventDefault();
163
+ throw new Error("Element with `commandFor` is a form participant. It should explicitly set `type=button` in order for `commandFor` to work. In order for it to act as a Submit button, it must not have command or commandfor attributes");
164
+ }
165
+ if (source.hasAttribute("command") !== source.hasAttribute("commandfor")) {
166
+ const attr = source.hasAttribute("command") ? "command" : "commandfor";
167
+ const missing = source.hasAttribute("command") ? "commandfor" : "command";
168
+ throw new Error(`Element with ${attr} attribute must also have a ${missing} attribute to function.`);
169
+ }
170
+ if (source.command !== "show-popover" && source.command !== "hide-popover" && source.command !== "toggle-popover" && source.command !== "show-modal" && source.command !== "request-close" && source.command !== "close" && !source.command.startsWith("--")) {
171
+ console.warn(`"${source.command}" is not a valid command value. Custom commands must begin with --`);
172
+ return;
173
+ }
174
+ const invokee = source.commandForElement;
175
+ if (!invokee) return;
176
+ const invokeEvent = new CommandEvent("command", {
177
+ command: source.command,
178
+ source,
179
+ cancelable: true
180
+ });
181
+ invokee.dispatchEvent(invokeEvent);
182
+ if (invokeEvent.defaultPrevented) return;
183
+ const command = invokeEvent.command.toLowerCase();
184
+ if (invokee.popover) {
185
+ const canShow = !invokee.matches(":popover-open");
186
+ const shouldShow = canShow && (command === "toggle-popover" || command === "show-popover");
187
+ const shouldHide = !canShow && command === "hide-popover";
188
+ if (shouldShow) invokee.showPopover({ source });
189
+ else if (shouldHide) invokee.hidePopover();
190
+ } else if (invokee.localName === "dialog") {
191
+ const canShow = !invokee.hasAttribute("open");
192
+ if (canShow && command == "show-modal") invokee.showModal();
193
+ else if (!canShow && command == "close") invokee.close(source.value ? source.value : void 0);
194
+ else if (!canShow && command == "request-close") {
195
+ if (!HTMLDialogElement.prototype.requestClose) HTMLDialogElement.prototype.requestClose = function() {
196
+ const cancelEvent = new Event("cancel", { cancelable: true });
197
+ this.dispatchEvent(cancelEvent);
198
+ if (!cancelEvent.defaultPrevented) this.close();
199
+ };
200
+ invokee.requestClose(source.value ? source.value : void 0);
201
+ }
202
+ }
203
+ }
204
+ function setupInvokeListeners(target) {
205
+ target.addEventListener("click", handleInvokerActivation, true);
206
+ }
207
+ function observeShadowRoots(ElementClass, callback) {
208
+ const attachShadow = ElementClass.prototype.attachShadow;
209
+ ElementClass.prototype.attachShadow = function(init) {
210
+ const shadow = attachShadow.call(this, init);
211
+ callback(shadow);
212
+ return shadow;
213
+ };
214
+ const attachInternals = ElementClass.prototype.attachInternals;
215
+ ElementClass.prototype.attachInternals = function() {
216
+ const internals = attachInternals.call(this);
217
+ if (internals.shadowRoot) callback(internals.shadowRoot);
218
+ return internals;
219
+ };
220
+ }
221
+ applyInvokerMixin(HTMLButtonElement);
222
+ observeShadowRoots(HTMLElement, (shadow) => {
223
+ setupInvokeListeners(shadow);
224
+ oncommandObserver.observe(shadow, { attributeFilter: ["oncommand"] });
225
+ applyOnCommandHandler(shadow.querySelectorAll("[oncommand]"));
226
+ });
227
+ setupInvokeListeners(document);
228
+ Object.assign(globalThis, { CommandEvent });
229
+ }
230
+ //#endregion
231
+ //#region src/utils/utils.ts
232
+ const QUICK_EVENT = {
233
+ passive: true,
234
+ capture: true
235
+ };
236
+ const isBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
237
+ const isWindows = () => isBrowser() && /^Win/i.test(navigator.userAgentData?.platform || navigator.platform);
238
+ const DSElement = typeof HTMLElement === "undefined" ? class {} : HTMLElement;
11
239
  function debounce(callback, delay) {
12
- let timer;
13
- return function(...args) {
14
- clearTimeout(timer);
15
- timer = setTimeout(() => callback.apply(this, args), delay);
16
- };
240
+ let timer;
241
+ return function(...args) {
242
+ clearTimeout(timer);
243
+ timer = setTimeout(() => callback.apply(this, args), delay);
244
+ };
17
245
  }
18
- var warn = (message, ...args) => !isBrowser() || window.dsWarnings === false || console.warn(`Designsystemet: ${message}`, ...args);
19
- var attr = (el, name, value) => {
20
- if (value === void 0) return el.getAttribute(name) ?? null;
21
- if (value === null) el.removeAttribute(name);
22
- else if (el.getAttribute(name) !== value) el.setAttribute(name, value);
23
- return null;
24
- };
25
- var STRIP_SURROUNDING_QUOTES = /^["']|["']$/g;
26
- var attrOrCSS = (el, name) => {
27
- let value = attr(el, name);
28
- if (!value) {
29
- const prop = getComputedStyle(el).getPropertyValue(`--_ds-${name}`);
30
- value = prop.replace(STRIP_SURROUNDING_QUOTES, "").trim() || null;
31
- }
32
- if (!value) warn(`Missing ${name} on:`, el);
33
- return value;
34
- };
35
- var on = (el, ...rest) => {
36
- const [types, ...options] = rest;
37
- for (const type of types.split(" ")) el.addEventListener(type, ...options);
38
- return () => off(el, ...rest);
39
- };
40
- var off = (el, ...rest) => {
41
- const [types, ...options] = rest;
42
- for (const type of types.split(" ")) el.removeEventListener(type, ...options);
43
- };
44
- var onHotReload = (key, setup) => {
45
- if (!isBrowser()) return;
46
- if (!window._dsHotReloadCleanup) window._dsHotReloadCleanup = /* @__PURE__ */ new Map();
47
- window._dsHotReloadCleanup?.get(key)?.map((cleanup) => cleanup());
48
- window._dsHotReloadCleanup?.set(key, setup());
49
- };
50
- var SKIP_MUTATIONS = false;
51
- var onMutation = (el, callback, options) => {
52
- let queue = 0;
53
- const onFrame = () => {
54
- if (!el.isConnected) return cleanup();
55
- callback(observer);
56
- observer.takeRecords();
57
- queue = 0;
58
- };
59
- const cleanup = () => observer?.disconnect?.();
60
- const observer = new MutationObserver(() => {
61
- if (!SKIP_MUTATIONS && !queue) queue = requestAnimationFrame(onFrame);
62
- });
63
- observer.observe(el, options);
64
- requestAnimationFrame(onFrame);
65
- return cleanup;
66
- };
67
- var setTextWithoutMutation = (el, text) => {
68
- SKIP_MUTATIONS = true;
69
- el.textContent = text;
70
- requestAnimationFrame(enableMutations);
71
- };
72
- var enableMutations = () => {
73
- SKIP_MUTATIONS = false;
74
- };
75
- var tag = (tagName, attrs) => {
76
- const el = document.createElement(tagName);
77
- if (attrs) for (const [key, val] of Object.entries(attrs)) attr(el, key, val);
78
- return el;
79
- };
80
- var customElements = {
81
- define: (name, instance) => !isBrowser() || window.customElements.get(name) || window.customElements.define(name, instance)
82
- };
83
- var id = 0;
246
+ const warn = (message, ...args) => !isBrowser() || window.dsWarnings === false || console.log(`\x1B[1mDesignsystemet:\x1B[m ${message}`, ...args);
247
+ /**
248
+ * attr
249
+ * @description Utility to quickly get, set and remove attributes
250
+ * @param el The Element to read/write attributes from
251
+ * @param name The attribute name to get, set or remove, or a object to set multiple attributes
252
+ * @param value A valid attribute value or null to remove attribute
253
+ */
254
+ const attr = (el, name, value) => {
255
+ if (value === void 0) return el.getAttribute(name) ?? null;
256
+ if (value === null) el.removeAttribute(name);
257
+ else if (el.getAttribute(name) !== value) el.setAttribute(name, value);
258
+ return null;
259
+ };
260
+ /**
261
+ * getCSSProp
262
+ * @description Retrieves and CSS property value and trims it
263
+ * @param el The Element to read attributes/CSS from
264
+ * @param name Attribute or CSS property to get
265
+ * @return string CSS property value
266
+ */
267
+ const getCSSProp = (el, prop) => getComputedStyle(el).getPropertyValue(prop).trim();
268
+ const STRIP_QUOTES = /^["']|["']$/g;
269
+ /**
270
+ * attrOrCSS
271
+ * @description Retrieves and updates attribute based on attribute or CSS property value
272
+ * @param el The Element to read attributes/CSS from
273
+ * @param name Attribute or CSS property to get
274
+ * @return string attribute or CSS property value
275
+ */
276
+ const attrOrCSS = (el, name) => {
277
+ let value = attr(el, name);
278
+ if (!value) value = getCSSProp(el, `--_ds-${name}`).replace(STRIP_QUOTES, "").trim();
279
+ if (!value) warn(`Missing ${name} on:`, el);
280
+ return value || null;
281
+ };
282
+ /**
283
+ * on
284
+ * @param el The Element to use as EventTarget
285
+ * @param types A space separated string of event types
286
+ * @param listener An event listener function or listener object
287
+ */
288
+ const on = (el, ...rest) => {
289
+ const [types, ...options] = rest;
290
+ for (const type of types.split(" ")) el.addEventListener(type, ...options);
291
+ return () => off(el, ...rest);
292
+ };
293
+ /**
294
+ * off
295
+ * @param el The Element to use as EventTarget
296
+ * @param types A space separated string of event types
297
+ * @param listener An event listener function or listener object
298
+ */
299
+ const off = (el, ...rest) => {
300
+ const [types, ...options] = rest;
301
+ for (const type of types.split(" ")) el.removeEventListener(type, ...options);
302
+ };
303
+ /**
304
+ * onHotReload
305
+ * @description Runs a callback when window is loaded in browser, and ensures cleanup when hot-reloading
306
+ * @param key The key to identify setup and corresponding cleanup
307
+ * @param callback The callback to run when the page is ready
308
+ */
309
+ const onHotReload = (key, setup) => {
310
+ if (!isBrowser()) return;
311
+ if (!window._dsHotReloadCleanup) window._dsHotReloadCleanup = /* @__PURE__ */ new Map();
312
+ window._dsHotReloadCleanup?.get(key)?.map((cleanup) => cleanup());
313
+ window._dsHotReloadCleanup?.set(key, setup());
314
+ };
315
+ /**
316
+ * MutationObserver wrapper with automatic cleanup
317
+ * @return new MutaionObserver
318
+ */
319
+ const onMutation = (el, callback, options) => {
320
+ const cleanup = () => observer.disconnect();
321
+ const observer = new MutationObserver((records) => {
322
+ if (!isBrowser() || !el.isConnected) return cleanup();
323
+ callback(el, records);
324
+ });
325
+ callback(el);
326
+ observer.observe(el, options);
327
+ return cleanup;
328
+ };
329
+ /**
330
+ * tag
331
+ * @description creates element and assigns properties
332
+ * @param tagName The tagname of element to create
333
+ * @param attrs Optional attributes to add to the element
334
+ * @param text Optional text content to add to the element
335
+ * @return HTMLElement with props
336
+ */
337
+ const tag = (tagName, attrs) => {
338
+ const el = document.createElement(tagName);
339
+ if (attrs) for (const [key, val] of Object.entries(attrs)) attr(el, key, val);
340
+ return el;
341
+ };
342
+ /**
343
+ * customElements.define
344
+ * @description Defines a customElement if running in browser and if not already registered
345
+ * Scoped/named "customElements.define" so @custom-elements-manifest/analyzer can find tag names
346
+ */
347
+ const customElements = { define: (name, instance) => !isBrowser() || window.customElements.get(name) || window.customElements.define(name, instance) };
348
+ let id = 0;
84
349
  function useId(el) {
85
- if (!isBrowser()) return `:ds:${++id}`;
86
- if (!window.dsUseId) window.dsUseId = 0;
87
- if (el && !el.id) el.id = `:ds:${++window.dsUseId}`;
88
- return el?.id || "";
350
+ if (!isBrowser()) return `:ds:${++id}`;
351
+ if (!window.dsUseId) window.dsUseId = 0;
352
+ if (el && !el.id) el.id = `:ds:${++window.dsUseId}`;
353
+ return el?.id || "";
89
354
  }
90
- var LIVE_EL;
91
- var LIVE_FIX = 0;
92
- var LIVE_CLEAR = 0;
93
- var announce = (text) => {
94
- clearTimeout(LIVE_CLEAR);
95
- if (LIVE_EL)
96
- setTextWithoutMutation(LIVE_EL, `${text}${LIVE_FIX++ % 2 ? "\xA0" : ""}`);
97
- if (text) LIVE_CLEAR = setTimeout(announce, 2e3, "");
98
- };
99
- var announceMount = () => {
100
- if (document.readyState !== "complete") return;
101
- if (!LIVE_EL) {
102
- LIVE_EL = tag("div", { "aria-live": "assertive" });
103
- LIVE_EL.style.overflow = "hidden";
104
- LIVE_EL.style.position = "fixed";
105
- LIVE_EL.style.whiteSpace = "nowrap";
106
- LIVE_EL.style.width = "1px";
107
- }
108
- if (!LIVE_EL.isConnected) document.body.appendChild(LIVE_EL);
109
- };
110
- onHotReload("announce", () => [
111
- on(document, "focus mouseover", announceMount, QUICK_EVENT)
112
- ]);
113
-
114
- // src/index.ts
115
- import "@u-elements/u-details/polyfill";
116
-
117
- // src/clickdelegatefor/clickdelegatefor.ts
118
- var CLASS_HOVER = ":click-delegate-hover";
119
- var ATTR_CLICKDELEGATEFOR = "data-clickdelegatefor";
120
- var SELECTOR_CLICKDELEGATEFOR = `[${ATTR_CLICKDELEGATEFOR}]`;
121
- var SELECTOR_SKIP = 'a,button,label,input,select,textarea,details,dialog,[role="button"],[popover],[contenteditable]';
122
- var handleClickDelegateFor = (event) => {
123
- const isNewTab = event.button === 1 || event.metaKey || event.ctrlKey;
124
- const isUserLeftOrMiddleClick = event.isTrusted && event.button < 2;
125
- const delegateTarget = isUserLeftOrMiddleClick && getDelegateTarget(event);
126
- if (!delegateTarget || delegateTarget.contains(event.target)) return;
127
- if (isNewTab && delegateTarget instanceof HTMLAnchorElement)
128
- return window.open(delegateTarget.href, void 0, delegateTarget.rel);
129
- event.stopImmediatePropagation();
130
- delegateTarget.click();
131
- };
132
- var HOVER;
133
- var handleMouseOver = (event) => {
134
- const delegateTarget = getDelegateTarget(event);
135
- if (HOVER === delegateTarget) return;
136
- if (HOVER) HOVER.classList.remove(CLASS_HOVER);
137
- if (delegateTarget) delegateTarget.classList.add(CLASS_HOVER);
138
- HOVER = delegateTarget;
139
- };
140
- var getDelegateTarget = ({ target: el }) => {
141
- const scope = el instanceof Element ? el.closest(SELECTOR_CLICKDELEGATEFOR) : null;
142
- const id2 = scope?.getAttribute(ATTR_CLICKDELEGATEFOR);
143
- const target = id2 && document.getElementById(id2) || void 0;
144
- const skip = target && el.closest(SELECTOR_SKIP);
145
- return (!skip || skip === target) && !target?.disabled ? target : void 0;
146
- };
147
- onHotReload("clickdelegatefor", () => [
148
- on(window, "click auxclick", handleClickDelegateFor, true),
149
- // Use capture to ensure we run before other click listeners
150
- on(document, "mouseover", handleMouseOver, QUICK_EVENT)
151
- // Use passive for better performance
152
- ]);
153
-
154
- // src/dialog/dialog.ts
155
- var DOWN_INSIDE = false;
156
- var handleClosedbyAny = ({
157
- type,
158
- target: el,
159
- clientX: x = 0,
160
- clientY: y = 0
161
- }) => {
162
- if (type === "pointerdown") {
163
- const r = el?.closest?.("dialog")?.getBoundingClientRect();
164
- const isInside = r && r.top <= y && y <= r.bottom && r.left <= x && x <= r.right;
165
- DOWN_INSIDE = !!isInside;
166
- } else {
167
- const isDialog = el instanceof HTMLDialogElement;
168
- const isClose = isDialog && !DOWN_INSIDE && attr(el, "closedby") === "any";
169
- DOWN_INSIDE = false;
170
- if (isClose) requestAnimationFrame(() => el.open && el.close());
171
- }
172
- };
173
- var handleAriaAttributes = () => {
174
- for (const btn of document.querySelectorAll('button[command="show-modal"]'))
175
- attr(btn, "aria-haspopup", "dialog");
176
- };
177
- var handleCommand = ({ command, target }) => command === "--show-non-modal" && target instanceof HTMLDialogElement && target.show();
355
+ /**
356
+ * @description Based off speak function from [U-elements](https://github.com/u-elements/u-elements/blob/main/packages/utils.ts#L210)
357
+ * @param text The text to announce
358
+ */
359
+ let LIVE_EL;
360
+ let LIVE_FIX = 0;
361
+ let LIVE_CLEAR = 0;
362
+ const announce = (text) => {
363
+ clearTimeout(LIVE_CLEAR);
364
+ if (LIVE_EL) LIVE_EL.textContent = `${text}${LIVE_FIX++ % 2 ? "\xA0" : ""}`;
365
+ if (text) LIVE_CLEAR = setTimeout(announce, 2e3, "");
366
+ };
367
+ const announceMount = () => {
368
+ if (document.readyState !== "complete") return;
369
+ if (!LIVE_EL) {
370
+ LIVE_EL = tag("div", { "aria-live": "assertive" });
371
+ LIVE_EL.style.overflow = "hidden";
372
+ LIVE_EL.style.position = "fixed";
373
+ LIVE_EL.style.whiteSpace = "nowrap";
374
+ LIVE_EL.style.width = "1px";
375
+ }
376
+ if (!LIVE_EL.isConnected) document.body.appendChild(LIVE_EL);
377
+ };
378
+ onHotReload("announce", () => [on(document, "focus mouseover", announceMount, QUICK_EVENT)]);
379
+ //#endregion
380
+ //#region src/clickdelegatefor/clickdelegatefor.ts
381
+ const CLASS_HOVER = ":click-delegate-hover";
382
+ const ATTR_CLICKDELEGATEFOR = "data-clickdelegatefor";
383
+ const SELECTOR_CLICKDELEGATEFOR = `[${ATTR_CLICKDELEGATEFOR}]`;
384
+ const SELECTOR_SKIP = "a,button,label,input,select,textarea,details,dialog,[role=\"button\"],[popover],[contenteditable]";
385
+ const handleClickDelegateFor = (event) => {
386
+ const isNewTab = event.button === 1 || event.metaKey || event.ctrlKey;
387
+ const delegateTarget = event.button < 2 && getDelegateTarget(event);
388
+ if (!delegateTarget || delegateTarget.contains(event.target)) return;
389
+ if (isNewTab && delegateTarget instanceof HTMLAnchorElement) return window.open(delegateTarget.href, void 0, delegateTarget.rel);
390
+ event.stopImmediatePropagation();
391
+ delegateTarget.click();
392
+ };
393
+ let HOVER;
394
+ const handleMouseOver = (event) => {
395
+ const delegateTarget = getDelegateTarget(event);
396
+ if (HOVER === delegateTarget) return;
397
+ if (HOVER) HOVER.classList.remove(CLASS_HOVER);
398
+ if (delegateTarget) delegateTarget.classList.add(CLASS_HOVER);
399
+ HOVER = delegateTarget;
400
+ };
401
+ const getDelegateTarget = ({ target: el }) => {
402
+ const id = (el instanceof Element ? el.closest(SELECTOR_CLICKDELEGATEFOR) : null)?.getAttribute(ATTR_CLICKDELEGATEFOR);
403
+ const target = id && document.getElementById(id) || void 0;
404
+ const skip = target && el.closest(SELECTOR_SKIP);
405
+ return (!skip || skip === target) && !target?.disabled ? target : void 0;
406
+ };
407
+ onHotReload("clickdelegatefor", () => [on(window, "click auxclick", handleClickDelegateFor, true), on(document, "mouseover", handleMouseOver, QUICK_EVENT)]);
408
+ //#endregion
409
+ //#region src/dialog/dialog.ts
410
+ let DOWN_INSIDE = false;
411
+ const handleClosedbyAny = ({ type, target: el, clientX: x = 0, clientY: y = 0 }) => {
412
+ if (type === "pointerdown") {
413
+ const r = el?.closest?.("dialog")?.getBoundingClientRect();
414
+ DOWN_INSIDE = !!(r && r.top <= y && y <= r.bottom && r.left <= x && x <= r.right);
415
+ } else {
416
+ const isClose = el instanceof HTMLDialogElement && !DOWN_INSIDE && attr(el, "closedby") === "any";
417
+ DOWN_INSIDE = false;
418
+ if (isClose) requestAnimationFrame(() => el.open && el.close());
419
+ }
420
+ };
421
+ const BUTTONS = isBrowser() ? document.getElementsByTagName("button") : [];
422
+ const handleAriaAttributes$2 = () => {
423
+ for (const btn of BUTTONS) if (btn.getAttribute("command")?.endsWith("-modal")) btn.setAttribute("aria-haspopup", "dialog");
424
+ };
425
+ const handleCommand = ({ command, target }) => command === "--show-non-modal" && target instanceof HTMLDialogElement && target.show();
178
426
  onHotReload("dialog", () => [
179
- on(document, "command", handleCommand, QUICK_EVENT),
180
- on(document, "pointerdown pointerup", handleClosedbyAny, QUICK_EVENT),
181
- onMutation(document, handleAriaAttributes, {
182
- attributeFilter: ["command"],
183
- attributes: true,
184
- childList: true,
185
- subtree: true
186
- })
427
+ on(document, "command", handleCommand, QUICK_EVENT),
428
+ on(document, "pointerdown pointerup", handleClosedbyAny, QUICK_EVENT),
429
+ onMutation(document, handleAriaAttributes$2, {
430
+ attributeFilter: ["command"],
431
+ attributes: true,
432
+ childList: true,
433
+ subtree: true
434
+ })
187
435
  ]);
188
-
189
- // src/popover/popover.ts
190
- import {
191
- autoUpdate,
192
- computePosition,
193
- flip,
194
- limitShift,
195
- offset,
196
- shift,
197
- size
198
- } from "@floating-ui/dom";
199
- var ATTR_PLACE = "data-placement";
200
- var ATTR_AUTO = "data-autoplacement";
201
- var POPOVERS = /* @__PURE__ */ new Map();
436
+ //#endregion
437
+ //#region src/fieldset/fieldset.ts
438
+ const FIELDSETS = isBrowser() ? document.getElementsByTagName("fieldset") : [];
439
+ const handleFieldsetMutations = () => {
440
+ for (const el of FIELDSETS) {
441
+ if (el.hasAttribute("aria-labelledby")) continue;
442
+ attr(el, "aria-labelledby", `${useId(el.querySelector("legend"))} ${useId(el.querySelector(":scope > :is([data-field=\"description\"],legend + p)"))}`.trim() || null);
443
+ }
444
+ };
445
+ onHotReload("fieldset", () => [onMutation(document, handleFieldsetMutations, {
446
+ childList: true,
447
+ subtree: true
448
+ })]);
449
+ //#endregion
450
+ //#region src/popover/popover.ts
451
+ const ATTR_PLACE = "data-placement";
452
+ const ATTR_AUTO = "data-autoplacement";
453
+ const POPOVERS = /* @__PURE__ */ new Map();
202
454
  function handleToggle(event) {
203
- let { newState, oldState, target: el, source = event.detail } = event;
204
- const isPopover = el instanceof HTMLElement && attr(el, "popover") !== null;
205
- const float = isPopover && getCSSProp(el, "--_ds-floating");
206
- if (!float) return;
207
- if (newState === "closed") return POPOVERS.get(el)?.();
208
- if (!source) {
209
- const root = el.getRootNode();
210
- const css = `[popovertarget="${el.id}"],[commandfor="${el.id}"]`;
211
- source = el.id && root?.querySelector?.(css) || void 0;
212
- }
213
- if (!source || source === el || oldState && oldState === newState) return;
214
- const padding = 10;
215
- const overscroll = getCSSProp(el, "--_ds-floating-overscroll");
216
- const placement = attr(el, ATTR_PLACE) || attr(source, ATTR_PLACE) || float;
217
- const auto = attr(el, ATTR_AUTO) || attr(source, ATTR_AUTO);
218
- const arrowSize = parseFloat(getComputedStyle(el, "::before").height) || 0;
219
- const shiftProp = placement.match(/left|right/gi) ? "Height" : "Width";
220
- const shiftLimit = source[`offset${shiftProp}`] / 2 + arrowSize;
221
- if (placement === "none") return;
222
- const options = {
223
- strategy: "absolute",
224
- placement,
225
- middleware: [
226
- offset(arrowSize || 0),
227
- // Add space for arrow or default to 8px
228
- shift({
229
- padding,
230
- limiter: limitShift({ offset: { mainAxis: shiftLimit } })
231
- // Prevent from shifing away from source
232
- }),
233
- arrowPseudo(),
234
- ...auto !== "false" ? [flip({ padding, crossAxis: false })] : [],
235
- ...overscroll ? [
236
- size({
237
- apply({ availableHeight }) {
238
- if (overscroll === "fit")
239
- el.style.width = `${source.clientWidth}px`;
240
- el.style.maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;
241
- }
242
- })
243
- ] : []
244
- ]
245
- };
246
- const unfloat = autoUpdate(source, el, async () => {
247
- if (!source?.isConnected) return POPOVERS.get(el)?.();
248
- const { x, y } = await computePosition(source, el, options);
249
- el.style.translate = `${x}px ${y}px`;
250
- });
251
- POPOVERS.set(el, () => POPOVERS.delete(el) && unfloat());
455
+ let { newState, oldState, target: el, source = event.detail } = event;
456
+ const float = el instanceof HTMLElement && attr(el, "popover") !== null && getCSSProp(el, "--_ds-floating");
457
+ if (!float) return;
458
+ if (newState === "closed") return POPOVERS.get(el)?.();
459
+ if (!source) {
460
+ const root = el.getRootNode();
461
+ const css = `[popovertarget="${el.id}"],[commandfor="${el.id}"]`;
462
+ source = el.id && root?.querySelector?.(css) || void 0;
463
+ }
464
+ if (!source || source === el || oldState && oldState === newState) return;
465
+ const padding = 10;
466
+ const overscroll = getCSSProp(el, "--_ds-floating-overscroll");
467
+ const placement = attr(el, ATTR_PLACE) || attr(source, ATTR_PLACE) || float;
468
+ const auto = attr(el, ATTR_AUTO) || attr(source, ATTR_AUTO);
469
+ const arrowSize = parseFloat(getComputedStyle(el, "::before").height) || 0;
470
+ const shiftProp = placement.match(/left|right/gi) ? "Height" : "Width";
471
+ const shiftLimit = source[`offset${shiftProp}`] / 2 + arrowSize;
472
+ if (placement === "none") return;
473
+ const options = {
474
+ strategy: "absolute",
475
+ placement,
476
+ middleware: [
477
+ offset(arrowSize || 0),
478
+ shift({
479
+ padding,
480
+ limiter: limitShift({ offset: { mainAxis: shiftLimit } })
481
+ }),
482
+ arrowPseudo(),
483
+ ...auto !== "false" ? [flip({
484
+ padding,
485
+ crossAxis: false
486
+ })] : [],
487
+ ...overscroll ? [size({ apply({ availableHeight }) {
488
+ if (overscroll === "fit") el.style.width = `${source.clientWidth}px`;
489
+ el.style.maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;
490
+ } })] : []
491
+ ]
492
+ };
493
+ const unfloat = autoUpdate(source, el, async () => {
494
+ if (!source?.isConnected) return POPOVERS.get(el)?.();
495
+ const { x, y } = await computePosition(source, el, options);
496
+ el.style.translate = `${x}px ${y}px`;
497
+ });
498
+ POPOVERS.set(el, () => POPOVERS.delete(el) && unfloat());
252
499
  }
253
- var IS_SCROLL;
254
- var handleScrollbar = ({ type }) => {
255
- if (type === "mousedown") IS_SCROLL = false;
256
- if (type === "scroll" && IS_SCROLL === false) IS_SCROLL = true;
257
- if (type === "mouseup" && IS_SCROLL)
258
- for (const [popover] of POPOVERS) popover.showPopover();
259
- };
260
- onHotReload("popover", () => [
261
- on(document, "mousedown scroll mouseup", handleScrollbar, true),
262
- on(document, "toggle ds-toggle-source", handleToggle, QUICK_EVENT)
263
- // Use capture since the toggle event does not bubble
264
- ]);
265
- var getCSSProp = (el, prop) => getComputedStyle(el).getPropertyValue(prop).trim();
266
- var arrowPseudo = () => ({
267
- name: "arrowPseudo",
268
- fn(data) {
269
- const target = data.elements.floating;
270
- const source = data.rects.reference;
271
- const x = `${Math.round(source.width / 2 + source.x - data.x)}px`;
272
- const y = `${Math.round(source.height / 2 + source.y - data.y)}px`;
273
- target.style.setProperty("--_ds-floating-arrow-x", x);
274
- target.style.setProperty("--_ds-floating-arrow-y", y);
275
- attr(target, "data-floating", data.placement);
276
- return data;
277
- }
500
+ let IS_SCROLL;
501
+ const handleScrollbar = ({ type }) => {
502
+ if (type === "mousedown") IS_SCROLL = false;
503
+ if (type === "scroll" && IS_SCROLL === false) IS_SCROLL = true;
504
+ if (type === "mouseup" && IS_SCROLL) for (const [popover] of POPOVERS) popover.showPopover();
505
+ };
506
+ onHotReload("popover", () => [on(document, "mousedown scroll mouseup", handleScrollbar, true), on(document, "toggle ds-toggle-source", handleToggle, QUICK_EVENT)]);
507
+ const arrowPseudo = () => ({
508
+ name: "arrowPseudo",
509
+ fn(data) {
510
+ const target = data.elements.floating;
511
+ const source = data.rects.reference;
512
+ const x = `${Math.round(source.width / 2 + source.x - data.x)}px`;
513
+ const y = `${Math.round(source.height / 2 + source.y - data.y)}px`;
514
+ target.style.setProperty("--_ds-floating-arrow-x", x);
515
+ target.style.setProperty("--_ds-floating-arrow-y", y);
516
+ attr(target, "data-floating", data.placement);
517
+ return data;
518
+ }
278
519
  });
279
-
280
- // src/readonly/readonly.ts
281
- var isReadOnly = (el) => (el instanceof HTMLSelectElement || el instanceof HTMLInputElement) && (el.hasAttribute("readonly") || attr(el, "aria-readonly") === "true");
282
- var handleKeyDown = (e) => {
283
- if (e.key !== "Tab" && isReadOnly(e.target)) {
284
- e.preventDefault();
285
- if (e.key?.startsWith("Arrow") && attr(e.target, "type") === "radio") {
286
- const all = document.querySelectorAll(`input[name="${e.target.name}"]`);
287
- const move = e.key?.match(/Arrow(Right|Down)/) ? 1 : -1;
288
- const next = all.length + [...all].indexOf(e.target) + move;
289
- all[next % all.length]?.focus();
290
- }
291
- }
292
- };
293
- var handleClick = (e) => {
294
- const input = e.target?.closest?.("label")?.control || e.target;
295
- if (isReadOnly(input)) {
296
- e.preventDefault();
297
- input.focus();
298
- }
299
- };
300
- var handleMouseDown = (e) => {
301
- if (e.target instanceof HTMLSelectElement && isReadOnly(e.target))
302
- e.preventDefault();
520
+ //#endregion
521
+ //#region src/readonly/readonly.ts
522
+ const isReadOnly = (el) => (el instanceof HTMLSelectElement || el instanceof HTMLInputElement) && (el.hasAttribute("readonly") || attr(el, "aria-readonly") === "true");
523
+ const handleKeyDown = (e) => {
524
+ if (e.key !== "Tab" && isReadOnly(e.target)) {
525
+ const isArrow = e.key?.startsWith("Arrow");
526
+ const isModifier = e.altKey || e.ctrlKey || e.metaKey;
527
+ if (isArrow || !isModifier) e.preventDefault();
528
+ if (isArrow && attr(e.target, "type") === "radio") {
529
+ const all = document.querySelectorAll(`input[name="${e.target.name}"]`);
530
+ const move = e.key?.match(/Arrow(Right|Down)/) ? 1 : -1;
531
+ all[(all.length + [...all].indexOf(e.target) + move) % all.length]?.focus();
532
+ }
533
+ }
534
+ };
535
+ const handleClick = (e) => {
536
+ const input = e.target?.closest?.("label")?.control || e.target;
537
+ if (isReadOnly(input)) {
538
+ e.preventDefault();
539
+ input.focus();
540
+ }
541
+ };
542
+ const handleMouseDown = (e) => {
543
+ if (e.target instanceof HTMLSelectElement && isReadOnly(e.target)) e.preventDefault();
303
544
  };
304
545
  onHotReload("readonly", () => [
305
- on(document, "keydown", handleKeyDown),
306
- on(document, "click", handleClick),
307
- // click needed for <label> and <input>
308
- on(document, "mousedown", handleMouseDown)
309
- // mousedown needed for <select>
310
- ]);
311
-
312
- // src/toggle-group/toggle-group.ts
313
- var ATTR_TOGGLEGROUP = "data-toggle-group";
314
- var SELECTOR_TOGGLEGROUP = `[${ATTR_TOGGLEGROUP}]`;
315
- var handleAriaAttributes2 = debounce(() => {
316
- for (const group of document.querySelectorAll(SELECTOR_TOGGLEGROUP))
317
- attr(group, "aria-label", attrOrCSS(group, ATTR_TOGGLEGROUP));
318
- }, 0);
319
- var handleKeydown = (event) => {
320
- const group = event.target instanceof HTMLInputElement && event.target.closest(SELECTOR_TOGGLEGROUP);
321
- if (!group) return;
322
- if (event.key === "Enter") event.target.click();
323
- if (event.key?.startsWith("Arrow")) {
324
- event.preventDefault?.();
325
- const inputs = [...group.getElementsByTagName("input")];
326
- const index = inputs.indexOf(event.target);
327
- const move = event.key.match(/Arrow(Right|Down)/) ? 1 : -1;
328
- let nextIndex = index;
329
- for (let i = 0; i < inputs.length; i++) {
330
- nextIndex = (inputs.length + nextIndex + move) % inputs.length;
331
- if (!inputs[nextIndex]?.disabled) {
332
- inputs[nextIndex]?.focus();
333
- break;
334
- }
335
- }
336
- }
337
- };
338
- onHotReload("toggle-group", () => [
339
- on(document, "keydown", handleKeydown),
340
- onMutation(document, handleAriaAttributes2, {
341
- attributeFilter: [ATTR_TOGGLEGROUP],
342
- attributes: true,
343
- childList: true,
344
- subtree: true
345
- })
546
+ on(document, "keydown", handleKeyDown),
547
+ on(document, "click", handleClick),
548
+ on(document, "mousedown", handleMouseDown)
346
549
  ]);
347
-
348
- // src/tooltip/tooltip.ts
349
- var TIP;
350
- var SOURCE;
351
- var HOVER_TIMER = 0;
352
- var SKIP_TIMER = 0;
353
- var IS_IOS = isBrowser() && /iPad|iPhone|iPod/.test(navigator.userAgent);
354
- var ATTR_TOOLTIP = "data-tooltip";
355
- var ATTR_COLOR = "data-color";
356
- var ARIA_LABEL = "aria-label";
357
- var ARIA_DESC = "aria-description";
358
- var SELECTOR_COLOR = `[${ATTR_COLOR}]`;
359
- var SELECTOR_TOOLTIP = `[${ATTR_TOOLTIP}]`;
360
- var ATTR_SCHEME = "data-color-scheme";
361
- var SELECTOR_SCHEME = `[${ATTR_SCHEME}]`;
362
- var SELECTOR_INTERACTIVE = "a,button,input,label,select,textarea,[tabindex]";
363
- var DELAY_HOVER = 300;
364
- var DELAY_SKIP = 300;
365
- var setTooltipElement = (el) => {
366
- if (el && !(el instanceof HTMLElement))
367
- warn("setTooltipElement expects an HTMLElement, got: ", el);
368
- TIP = el || void 0;
369
- };
370
- var handleAriaAttributes3 = debounce(() => {
371
- for (const el of document.querySelectorAll(SELECTOR_TOOLTIP)) {
372
- const aria = el.getAttribute(ARIA_LABEL) || el.getAttribute(ARIA_DESC);
373
- const text = el.getAttribute(ATTR_TOOLTIP) || attrOrCSS(el, ATTR_TOOLTIP);
374
- if (aria !== text) {
375
- const hasText = attr(el, "role") !== "img" && el.textContent?.trim();
376
- attr(el, ATTR_TOOLTIP, text);
377
- attr(el, ARIA_LABEL, hasText ? null : text);
378
- attr(el, ARIA_DESC, hasText ? text : null);
379
- if (!el.matches(SELECTOR_INTERACTIVE))
380
- warn('Missing tabindex="0" attribute on: ', el);
381
- }
382
- const isCurrent = el === SOURCE && TIP?.matches(":popover-open");
383
- const isChanged = isCurrent && text && TIP?.textContent !== text;
384
- if (isCurrent && isChanged) {
385
- if (TIP) setTextWithoutMutation(TIP, text);
386
- if (document.activeElement === el) announce(text);
387
- }
388
- }
389
- }, 0);
390
- var handleInterest = ({ type, target }) => {
391
- clearTimeout(HOVER_TIMER);
392
- if (target === TIP) return;
393
- if (type === "mouseover" && !SOURCE && !IS_IOS) {
394
- HOVER_TIMER = setTimeout(handleInterest, DELAY_HOVER, { target });
395
- return;
396
- }
397
- const source = target?.closest?.(`[${ATTR_TOOLTIP}]`);
398
- if (source === SOURCE) return;
399
- if (!source) return hideTooltip();
400
- if (!TIP) TIP = tag("div", { class: "ds-tooltip" });
401
- if (!TIP.isConnected) document.body.appendChild(TIP);
402
- const color = source.closest(SELECTOR_COLOR);
403
- const scheme = source.closest(SELECTOR_SCHEME);
404
- const isReset = color !== scheme && color?.contains(scheme);
405
- clearTimeout(SKIP_TIMER);
406
- attr(TIP, "popover", "manual");
407
- attr(TIP, ATTR_SCHEME, scheme?.getAttribute(ATTR_SCHEME) || null);
408
- attr(TIP, ATTR_COLOR, isReset && color?.getAttribute(ATTR_COLOR) || null);
409
- setTextWithoutMutation(TIP, attr(source, ATTR_TOOLTIP));
410
- TIP.showPopover();
411
- TIP.dispatchEvent(new CustomEvent("ds-toggle-source", { detail: source }));
412
- SOURCE = source;
413
- };
414
- var hideTooltip = () => TIP?.isConnected && TIP.popover && TIP.hidePopover();
415
- var handleClose = (event) => {
416
- if (event?.type === "keydown")
417
- return event?.key === "Escape" && hideTooltip();
418
- if (!event) SOURCE = void 0;
419
- else if (event.target === TIP && event.newState === "closed")
420
- SKIP_TIMER = setTimeout(handleClose, DELAY_SKIP);
550
+ //#endregion
551
+ //#region src/toggle-group/toggle-group.ts
552
+ const ARIA_LABELLEDBY = "aria-labelledby";
553
+ const ARIA_LABEL$1 = "aria-label";
554
+ const ATTR_TOGGLEGROUP = "data-toggle-group";
555
+ const SELECTOR_TOGGLEGROUP = `[${ATTR_TOGGLEGROUP}]`;
556
+ const handleAriaAttributes$1 = () => {
557
+ for (const group of document.querySelectorAll(SELECTOR_TOGGLEGROUP)) attr(group, "aria-label", attrOrCSS(group, ATTR_TOGGLEGROUP));
558
+ };
559
+ const handleKeydown = (event) => {
560
+ const { key, target: el } = event;
561
+ const group = el instanceof HTMLInputElement && el.closest(SELECTOR_TOGGLEGROUP);
562
+ if (!group) return;
563
+ if (!attr(group, ARIA_LABEL$1) && !attr(group, ARIA_LABELLEDBY)) attr(group, ARIA_LABEL$1, attrOrCSS(group, ATTR_TOGGLEGROUP));
564
+ if (key === "Enter") el.click();
565
+ if (key?.startsWith("Arrow")) {
566
+ event.preventDefault?.();
567
+ const inputs = [...group.getElementsByTagName("input")];
568
+ const index = inputs.indexOf(el);
569
+ const move = key.match(/Arrow(Right|Down)/) ? 1 : -1;
570
+ let nextIndex = index;
571
+ for (let i = 0; i < inputs.length; i++) {
572
+ nextIndex = (inputs.length + nextIndex + move) % inputs.length;
573
+ if (!inputs[nextIndex]?.disabled) {
574
+ inputs[nextIndex]?.focus();
575
+ break;
576
+ }
577
+ }
578
+ }
579
+ };
580
+ onHotReload("toggle-group", () => [on(document, "keydown", handleKeydown), onMutation(document, handleAriaAttributes$1, {
581
+ attributeFilter: [ATTR_TOGGLEGROUP],
582
+ attributes: true,
583
+ childList: true,
584
+ subtree: true
585
+ })]);
586
+ //#endregion
587
+ //#region src/tooltip/tooltip.ts
588
+ let TIP;
589
+ let SOURCE;
590
+ let IS_HOVERING = false;
591
+ let HOVER_TIMER = 0;
592
+ let SKIP_TIMER = 0;
593
+ const IS_IOS = isBrowser() && /iPad|iPhone|iPod/.test(navigator.userAgent);
594
+ const ATTR_TOOLTIP = "data-tooltip";
595
+ const ATTR_COLOR = "data-color";
596
+ const ARIA_LABEL = "aria-label";
597
+ const ARIA_DESC = "aria-description";
598
+ const SELECTOR_COLOR = `[${ATTR_COLOR}]`;
599
+ const SELECTOR_TOOLTIP = `[${ATTR_TOOLTIP}]`;
600
+ const ATTR_SCHEME = "data-color-scheme";
601
+ const SELECTOR_SCHEME = `[${ATTR_SCHEME}]`;
602
+ const SELECTOR_INTERACTIVE = "a,button,input,label,select,textarea,[tabindex]";
603
+ const DELAY_HOVER = 300;
604
+ const DELAY_SKIP = 300;
605
+ /**
606
+ * setTooltipElement
607
+ * @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.
608
+ * @param el The HTMLElement to use as tooltip
609
+ */
610
+ const setTooltipElement = (el) => {
611
+ if (el && !(el instanceof HTMLElement)) warn("setTooltipElement expects an HTMLElement, got: ", el);
612
+ clearTimeout(SKIP_TIMER);
613
+ clearTimeout(HOVER_TIMER);
614
+ SOURCE = void 0;
615
+ IS_HOVERING = false;
616
+ TIP = el || void 0;
617
+ };
618
+ const handleAriaAttributes = () => {
619
+ for (const el of document.querySelectorAll(SELECTOR_TOOLTIP)) {
620
+ const text = attrOrCSS(el, ATTR_TOOLTIP);
621
+ if (!text) return;
622
+ if (text !== (el.getAttribute(ARIA_LABEL) || el.getAttribute(ARIA_DESC))) {
623
+ const hasText = attr(el, "role") !== "img" && el.textContent?.trim();
624
+ attr(el, ATTR_TOOLTIP, text);
625
+ attr(el, ARIA_LABEL, hasText ? null : text);
626
+ attr(el, ARIA_DESC, hasText ? text : null);
627
+ if (!el.matches(SELECTOR_INTERACTIVE)) warn("Missing tabindex=\"0\" attribute on: ", el);
628
+ }
629
+ const isCurrent = el === SOURCE && TIP?.offsetHeight && TIP?.offsetWidth;
630
+ const isChanged = isCurrent && text && TIP?.textContent !== text;
631
+ if (isCurrent && isChanged) {
632
+ if (TIP) TIP.textContent = text;
633
+ if (document.activeElement === el) announce(text);
634
+ }
635
+ }
636
+ };
637
+ const handleInterest = ({ type, target }) => {
638
+ clearTimeout(HOVER_TIMER);
639
+ if (target === TIP) return;
640
+ if (type === "mouseover" && !IS_HOVERING && !IS_IOS) {
641
+ HOVER_TIMER = setTimeout(handleInterest, DELAY_HOVER, { target });
642
+ return;
643
+ }
644
+ const source = target?.closest?.(`[${ATTR_TOOLTIP}]`);
645
+ if (source === SOURCE) return;
646
+ if (!source) return hideTooltip();
647
+ if (!TIP) TIP = tag("div", { class: "ds-tooltip" });
648
+ if (!TIP.isConnected) document.body.appendChild(TIP);
649
+ const color = source.closest(SELECTOR_COLOR);
650
+ const scheme = source.closest(SELECTOR_SCHEME);
651
+ const isReset = color !== scheme && color?.contains(scheme);
652
+ clearTimeout(SKIP_TIMER);
653
+ attr(TIP, "popover", "manual");
654
+ attr(TIP, ATTR_SCHEME, scheme?.getAttribute(ATTR_SCHEME) || null);
655
+ attr(TIP, ATTR_COLOR, isReset && color?.getAttribute(ATTR_COLOR) || null);
656
+ TIP.textContent = attr(source, ATTR_TOOLTIP);
657
+ TIP.showPopover();
658
+ TIP.dispatchEvent(new CustomEvent("ds-toggle-source", { detail: source }));
659
+ IS_HOVERING = true;
660
+ SOURCE = source;
661
+ };
662
+ const hideTooltip = () => TIP?.isConnected && TIP.popover && TIP.hidePopover();
663
+ const handleClose = (event) => {
664
+ if (event?.type === "keydown") return event?.key === "Escape" && hideTooltip();
665
+ if (!event) IS_HOVERING = false;
666
+ else if (event.target === TIP && event.newState === "closed") {
667
+ SOURCE = void 0;
668
+ SKIP_TIMER = setTimeout(handleClose, DELAY_SKIP);
669
+ }
421
670
  };
422
671
  onHotReload("tooltip", () => [
423
- on(document, "blur focus mouseover", handleInterest, QUICK_EVENT),
424
- on(document, "toggle keydown", handleClose, QUICK_EVENT),
425
- onMutation(document, handleAriaAttributes3, {
426
- attributeFilter: [ATTR_TOOLTIP],
427
- attributes: true,
428
- childList: true,
429
- subtree: true
430
- })
672
+ on(document, "blur focus mouseover", handleInterest, QUICK_EVENT),
673
+ on(document, "toggle keydown", handleClose, QUICK_EVENT),
674
+ onMutation(document, handleAriaAttributes, {
675
+ attributeFilter: [ATTR_TOOLTIP],
676
+ attributes: true,
677
+ childList: true,
678
+ subtree: true
679
+ })
431
680
  ]);
432
-
433
- // src/index.ts
434
- export * from "@u-elements/u-datalist";
435
-
436
- // src/breadcrumbs/breadcrumbs.ts
437
- var ATTR_LABEL = "aria-label";
681
+ //#endregion
682
+ //#region src/breadcrumbs/breadcrumbs.ts
683
+ const ATTR_LABEL$1 = "aria-label";
438
684
  var DSBreadcrumbsElement = class extends DSElement {
439
- _items;
440
- // Using underscore instead of private fields for backwards compatibility
441
- _label = null;
442
- _render;
443
- _unresize;
444
- _unmutate;
445
- static get observedAttributes() {
446
- return [ATTR_LABEL];
447
- }
448
- connectedCallback() {
449
- this._label = attrOrCSS(this, ATTR_LABEL);
450
- this._items = this.getElementsByTagName("a");
451
- this._render = debounce(() => render(this), 100);
452
- this._unresize = on(window, "resize", this._render);
453
- this._unmutate = onMutation(this, this._render, {
454
- childList: true,
455
- subtree: true
456
- });
457
- }
458
- attributeChangedCallback(_name, _prev, next) {
459
- if (next) this._label = next;
460
- this._render?.();
461
- }
462
- disconnectedCallback() {
463
- this._unresize?.();
464
- this._unmutate?.();
465
- this._unresize = this._unmutate = this._render = this._items = void 0;
466
- }
467
- };
468
- var render = (self) => {
469
- const lastItem = self._items?.[self._items.length - 1];
470
- const lastItemInList = lastItem?.parentElement === self ? null : lastItem;
471
- const isListHidden = !lastItemInList?.offsetHeight;
472
- attr(self, "role", isListHidden ? null : "navigation");
473
- attr(self, ATTR_LABEL, isListHidden ? null : self._label);
474
- for (const item of self._items || [])
475
- attr(item, "aria-current", item === lastItemInList ? "page" : null);
685
+ _items;
686
+ _label = null;
687
+ _unresize;
688
+ _unmutate;
689
+ static get observedAttributes() {
690
+ return [ATTR_LABEL$1];
691
+ }
692
+ connectedCallback() {
693
+ const resize = debounce(() => render$3(this), 100);
694
+ this._label = attrOrCSS(this, ATTR_LABEL$1);
695
+ this._items = this.getElementsByTagName("a");
696
+ this._unresize = on(window, "resize", resize);
697
+ this._unmutate = onMutation(this, render$3, {
698
+ childList: true,
699
+ subtree: true
700
+ });
701
+ }
702
+ attributeChangedCallback(_name, _prev, next) {
703
+ if (!this._unmutate || !next) return;
704
+ this._label = next;
705
+ render$3(this);
706
+ }
707
+ disconnectedCallback() {
708
+ this._unresize?.();
709
+ this._unmutate?.();
710
+ this._unresize = this._unmutate = this._items = void 0;
711
+ }
712
+ };
713
+ const render$3 = (self) => {
714
+ const lastItem = self._items?.[self._items.length - 1];
715
+ const lastItemInList = lastItem?.parentElement === self ? null : lastItem;
716
+ const isListHidden = !lastItemInList?.offsetHeight;
717
+ attr(self, "role", isListHidden ? null : "navigation");
718
+ attr(self, ATTR_LABEL$1, isListHidden ? null : self._label);
719
+ for (const item of self._items || []) attr(item, "aria-current", item === lastItemInList ? "page" : null);
476
720
  };
477
721
  customElements.define("ds-breadcrumbs", DSBreadcrumbsElement);
478
-
479
- // src/error-summary/error-summary.ts
722
+ //#endregion
723
+ //#region src/error-summary/error-summary.ts
480
724
  var DSErrorSummaryElement = class extends DSElement {
481
- connectedCallback() {
482
- on(this, "animationend", this, QUICK_EVENT);
483
- requestAnimationFrame(() => this.handleEvent({ target: this }));
484
- }
485
- handleEvent({ target }) {
486
- if (target !== this) return;
487
- const heading = this.querySelector("h2,h3,h4,h5,h6");
488
- if (heading) attr(this, "aria-labelledby", useId(heading));
489
- attr(this, "tabindex", "-1");
490
- this.focus();
491
- }
492
- disconnectedCallback() {
493
- off(this, "animationend", this, QUICK_EVENT);
494
- }
725
+ _unmutate;
726
+ connectedCallback() {
727
+ on(this, "animationend", this, QUICK_EVENT);
728
+ attr(this, "tabindex", "-1");
729
+ this._unmutate = onMutation(this, render$2, {
730
+ childList: true,
731
+ subtree: true
732
+ });
733
+ this.focus();
734
+ }
735
+ handleEvent({ target }) {
736
+ if (target === this) this.focus();
737
+ }
738
+ disconnectedCallback() {
739
+ off(this, "animationend", this, QUICK_EVENT);
740
+ this._unmutate?.();
741
+ this._unmutate = void 0;
742
+ }
743
+ };
744
+ const render$2 = (self) => {
745
+ const heading = self.querySelector("h2,h3,h4,h5,h6");
746
+ if (heading) attr(self, "aria-labelledby", useId(heading));
495
747
  };
496
748
  customElements.define("ds-error-summary", DSErrorSummaryElement);
497
-
498
- // src/field/field.ts
499
- var INDETERMINATE = "data-indeterminate";
500
- var FIELDS = /* @__PURE__ */ new Set();
501
- var COUNTS = /* @__PURE__ */ new WeakMap();
502
- var FIELDSETS = isBrowser() ? document.getElementsByTagName("fieldset") : [];
503
- var HAS_FIELD_SIZING = isBrowser() && CSS.supports("field-sizing", "content");
504
- var COUNTER_DEBOUNCE = isWindows() ? 800 : 200;
505
- var HAS_VALIDATION = /* @__PURE__ */ new WeakSet();
506
- var handleMutations = debounce(() => {
507
- for (const el of FIELDSETS) {
508
- const labelledby = `${useId(el.querySelector("legend"))} ${useId(el.querySelector(':scope > :is([data-field="description"],legend + p)'))}`;
509
- attr(el, "aria-labelledby", labelledby.trim() || null);
510
- }
511
- for (const field of FIELDS) {
512
- const descs = [];
513
- const labels = [];
514
- let input;
515
- let counter;
516
- let hasValidation = false;
517
- let invalid = false;
518
- for (const el of field.getElementsByTagName("*")) {
519
- if (el instanceof HTMLLabelElement) labels.push(el);
520
- if (el.hidden) continue;
521
- if (isInputLike(el)) {
522
- if (input)
523
- warn(
524
- `Fields should only have one input element. Use <fieldset> to group multiple fields:`,
525
- field
526
- );
527
- else input = el;
528
- } else {
529
- const type = el.getAttribute("data-field");
530
- if (type === "counter") counter = el;
531
- if (type === "validation") {
532
- descs.unshift(el);
533
- hasValidation = true;
534
- invalid = invalid || isInvalid(el);
535
- } else if (type) descs.push(el);
536
- }
537
- }
538
- if (!input) warn(`Field is missing input element:`, field);
539
- else {
540
- if (counter) COUNTS.set(input, counter);
541
- for (const label of labels) attr(label, "for", useId(input));
542
- const isBoolish = input.type === "radio" || input.type === "checkbox";
543
- const fieldsetValidation = field.closest("fieldset")?.querySelector(':scope > [data-field="validation"]');
544
- if (fieldsetValidation && !fieldsetValidation?.hidden) {
545
- hasValidation = true;
546
- invalid = invalid || isInvalid(fieldsetValidation);
547
- descs.unshift(fieldsetValidation);
548
- }
549
- const indeterminate = attr(input, INDETERMINATE);
550
- if (indeterminate) input.indeterminate = indeterminate === "true";
551
- attr(field, "data-clickdelegatefor", isBoolish ? useId(input) : null);
552
- attr(input, "aria-describedby", descs.map(useId).join(" ") || null);
553
- if (hasValidation || HAS_VALIDATION.has(input)) {
554
- HAS_VALIDATION[hasValidation ? "add" : "delete"](input);
555
- attr(input, "aria-invalid", `${invalid}`);
556
- }
557
- updateField(input);
558
- }
559
- }
560
- }, 0);
561
- var updateField = (e) => {
562
- const input = e.target || e;
563
- const counter = COUNTS.get(input);
564
- if (counter?.isConnected) {
565
- const limit = Number(attr(counter, "data-limit")) || 0;
566
- const count = limit - input.value.length;
567
- const state = count < 0 ? "over" : "under";
568
- const label = attrOrCSS(counter, `data-${state}`)?.replace(
569
- "%d",
570
- `${Math.abs(count)}`
571
- );
572
- attr(counter, "data-label", label);
573
- attr(counter, "data-state", state);
574
- attr(counter, "data-color", count < 0 ? "danger" : null);
575
- if (e.type === "input" && label)
576
- debouncedCounterLiveRegion(input, label);
577
- }
578
- if (!HAS_FIELD_SIZING && input instanceof HTMLTextAreaElement) {
579
- input.style.setProperty("--_ds-field-sizing", "auto");
580
- input.style.setProperty("--_ds-field-sizing", `${input.scrollHeight}px`);
581
- }
582
- };
583
- var debouncedCounterLiveRegion = debounce((input, text) => {
584
- if (document.activeElement === input) announce(text);
749
+ //#endregion
750
+ //#region src/field/field.ts
751
+ const ATTR_INVALID = "aria-invalid";
752
+ const ATTR_DESCRIBEDBY = "aria-describedby";
753
+ const ATTR_INDETERMINATE = "data-indeterminate";
754
+ const COUNTER_DEBOUNCE = isWindows() ? 800 : 200;
755
+ const COUNTS = /* @__PURE__ */ new WeakMap();
756
+ const FIELDS = /* @__PURE__ */ new Map();
757
+ const VALIDATIONS = /* @__PURE__ */ new WeakSet();
758
+ const WARNING_MULTIPLE_INPUTS = `Fields should only have one input element. Use <fieldset> to group multiple fields:`;
759
+ const handleFieldMutations = (_doc, records = []) => {
760
+ for (const { target } of records) {
761
+ const isFieldset = target instanceof HTMLFieldSetElement;
762
+ for (const [field] of FIELDS) if (isFieldset ? target.contains(field) : field.contains(target)) handleFieldMutation(field);
763
+ }
764
+ };
765
+ const handleFieldMutation = (field) => {
766
+ const labels = [];
767
+ const nextDescs = [];
768
+ const prevDescs = FIELDS.get(field) || [];
769
+ let input;
770
+ let counter;
771
+ let hasValidation = false;
772
+ let invalid = false;
773
+ for (const el of field.getElementsByTagName("*")) {
774
+ if (el instanceof HTMLLabelElement) labels.push(el);
775
+ if (el.hidden) continue;
776
+ if (isInputLike(el)) if (input) warn(WARNING_MULTIPLE_INPUTS, field);
777
+ else input = el;
778
+ else {
779
+ const type = el.getAttribute("data-field");
780
+ if (type === "counter") counter = el;
781
+ if (type === "validation") {
782
+ nextDescs.unshift(useId(el));
783
+ hasValidation = true;
784
+ invalid = invalid || isInvalid(el);
785
+ } else if (type) nextDescs.push(useId(el));
786
+ }
787
+ }
788
+ if (!input) return;
789
+ if (counter) COUNTS.set(input, counter);
790
+ for (const label of labels) attr(label, "for", useId(input));
791
+ const fieldsetValidation = field.closest("fieldset")?.querySelector(":scope > [data-field=\"validation\"]");
792
+ if (fieldsetValidation && !fieldsetValidation?.hidden) {
793
+ hasValidation = true;
794
+ invalid = invalid || isInvalid(fieldsetValidation);
795
+ nextDescs.unshift(useId(fieldsetValidation));
796
+ }
797
+ const indeterminate = attr(input, ATTR_INDETERMINATE);
798
+ if (indeterminate) input.indeterminate = indeterminate === "true";
799
+ if (input.type === "radio" || input.type === "checkbox") attr(field, "data-clickdelegatefor", useId(input));
800
+ const keep = (attr(input, ATTR_DESCRIBEDBY)?.trim().split(/\s+/))?.filter((id) => !prevDescs.includes(id)) || [];
801
+ attr(input, ATTR_DESCRIBEDBY, [...nextDescs, ...keep].join(" ") || null);
802
+ FIELDS.set(field, nextDescs);
803
+ const hadValidation = VALIDATIONS.has(input);
804
+ if (hasValidation && !hadValidation && !input.hasAttribute(ATTR_INVALID)) {
805
+ attr(input, ATTR_INVALID, "true");
806
+ VALIDATIONS.add(input);
807
+ } else if (!hasValidation && hadValidation) {
808
+ attr(input, ATTR_INVALID, null);
809
+ VALIDATIONS.delete(input);
810
+ }
811
+ handleFieldInput(input);
812
+ };
813
+ const TEXTS = {
814
+ over: "%d tegn for mye",
815
+ under: "%d tegn igjen"
816
+ };
817
+ const handleFieldInput = (e) => {
818
+ const input = e.target || e;
819
+ const counter = COUNTS.get(input);
820
+ if (counter?.isConnected) {
821
+ const count = (Number(attr(counter, "data-limit")) || 0) - input.value.length;
822
+ const state = count < 0 ? "over" : "under";
823
+ const label = (attrOrCSS(counter, `data-${state}`) || TEXTS[state])?.replace("%d", `${Math.abs(count)}`);
824
+ attr(counter, "data-label", label);
825
+ attr(counter, "data-state", state);
826
+ attr(counter, "data-color", count < 0 ? "danger" : null);
827
+ if (e.type === "input" && label) debouncedCounterLiveRegion(input, label);
828
+ }
829
+ if (input instanceof HTMLTextAreaElement) {
830
+ input.style.setProperty("--_ds-field-sizing", "auto");
831
+ input.style.setProperty("--_ds-field-sizing", `${input.scrollHeight}px`);
832
+ }
833
+ };
834
+ const debouncedCounterLiveRegion = debounce((input, text) => {
835
+ if (document.activeElement === input) announce(text);
585
836
  }, COUNTER_DEBOUNCE);
586
- var isInvalid = (el) => el.getAttribute("data-color") !== "success";
587
- var isInputLike = (el) => el instanceof HTMLElement && "validity" in el && // Adds support for custom elements implemeted with attachInternals()
588
- !(el instanceof HTMLButtonElement) && // But skip <button> elements
589
- el.type !== "hidden";
837
+ const isInvalid = (el) => el.getAttribute("data-color") !== "success";
838
+ const isInputLike = (el) => el instanceof HTMLElement && "validity" in el && !(el instanceof HTMLButtonElement) && el.type !== "hidden";
590
839
  var DSFieldElement = class extends DSElement {
591
- connectedCallback() {
592
- FIELDS.add(this);
593
- handleMutations();
594
- }
595
- disconnectedCallback() {
596
- FIELDS.delete(this);
597
- }
840
+ connectedCallback() {
841
+ FIELDS.set(this, []);
842
+ handleFieldMutation(this);
843
+ }
844
+ disconnectedCallback() {
845
+ FIELDS.delete(this);
846
+ }
598
847
  };
599
848
  customElements.define("ds-field", DSFieldElement);
600
- onHotReload("field", () => [
601
- on(document, "input", updateField, QUICK_EVENT),
602
- onMutation(document, handleMutations, {
603
- attributeFilter: [
604
- "data-field",
605
- "data-limit",
606
- "hidden",
607
- "value",
608
- INDETERMINATE
609
- ],
610
- attributes: true,
611
- childList: true,
612
- subtree: true
613
- })
614
- ]);
615
-
616
- // src/pagination/pagination.ts
617
- var ATTR_LABEL2 = "aria-label";
618
- var ATTR_CURRENT = "data-current";
619
- var ATTR_TOTAL = "data-total";
620
- var ATTR_HREF = "data-href";
621
- var pagination = ({ current = 1, total = 10, show = 7 }) => ({
622
- prev: current > 1 ? current - 1 : 0,
623
- next: current < total ? current + 1 : 0,
624
- pages: getSteps(current, total, show).map((page, index) => ({
625
- current: page === current && "page",
626
- key: `key-${page}-${index}`,
627
- page
628
- }))
849
+ onHotReload("field", () => [on(document, "input", handleFieldInput, QUICK_EVENT), onMutation(document, handleFieldMutations, {
850
+ attributeFilter: [
851
+ "data-field",
852
+ "data-limit",
853
+ "hidden",
854
+ "id",
855
+ "value",
856
+ ATTR_INDETERMINATE
857
+ ],
858
+ attributes: true,
859
+ childList: true,
860
+ subtree: true
861
+ })]);
862
+ //#endregion
863
+ //#region src/pagination/pagination.ts
864
+ const ATTR_LABEL = "aria-label";
865
+ const ATTR_CURRENT = "data-current";
866
+ const ATTR_TOTAL = "data-total";
867
+ const ATTR_HREF = "data-href";
868
+ const pagination = ({ current = 1, total = 10, show = 7 }) => ({
869
+ prev: current > 1 ? current - 1 : 0,
870
+ next: current < total ? current + 1 : 0,
871
+ pages: getSteps(current, total, show).map((page, index) => ({
872
+ current: page === current && "page",
873
+ key: `key-${page}-${index}`,
874
+ page
875
+ }))
629
876
  });
630
877
  var DSPaginationElement = class extends DSElement {
631
- _unmutate;
632
- // Using underscore instead of private fields for backwards compatibility
633
- _render;
634
- static get observedAttributes() {
635
- return [ATTR_LABEL2, ATTR_CURRENT, ATTR_TOTAL, ATTR_HREF];
636
- }
637
- connectedCallback() {
638
- const total = attr(this, ATTR_TOTAL);
639
- const current = attr(this, ATTR_CURRENT);
640
- if (current && !total) warn(`Missing ${ATTR_TOTAL} attribute on:`, this);
641
- if (total && !current) warn(`Missing ${ATTR_CURRENT} attribute on:`, this);
642
- attr(this, ATTR_LABEL2, attrOrCSS(this, ATTR_LABEL2));
643
- attr(this, "role", "navigation");
644
- this._render = debounce(() => render2(this), 0);
645
- this._unmutate = onMutation(this, this._render, {
646
- childList: true,
647
- subtree: true
648
- });
649
- }
650
- attributeChangedCallback() {
651
- this._render?.();
652
- }
653
- disconnectedCallback() {
654
- this._unmutate?.();
655
- this._unmutate = this._render = void 0;
656
- }
657
- };
658
- var render2 = (self) => {
659
- const current = Number(attr(self, ATTR_CURRENT));
660
- const total = Number(attr(self, ATTR_TOTAL));
661
- if (current && total) {
662
- const items = self.querySelectorAll("button,a");
663
- const show = items.length - 2;
664
- const href = attr(self, ATTR_HREF);
665
- const { next, prev, pages } = pagination({ current, total, show });
666
- items.forEach((item, i) => {
667
- const page = i ? items[i + 1] ? pages[i - 1]?.page : next : prev;
668
- attr(item, "aria-current", pages[i - 1]?.current ? "true" : null);
669
- attr(item, "aria-label", `${page ?? "hidden"}`);
670
- attr(item, "role", page ? null : "none");
671
- attr(item, "tabindex", page ? null : "-1");
672
- if (item instanceof HTMLButtonElement) attr(item, "value", `${page}`);
673
- if (href && item instanceof HTMLAnchorElement)
674
- attr(item, "href", href.replace("%d", `${page}`));
675
- });
676
- }
677
- };
678
- var getSteps = (now, max, show = Number.POSITIVE_INFINITY) => {
679
- const offset2 = (show - 1) / 2;
680
- const start = Math.max(Math.min(now - Math.floor(offset2), max - show + 1), 1);
681
- const end = Math.min(Math.max(now + Math.ceil(offset2), show), max);
682
- const pages = Array.from({ length: end + 1 - start }, (_, i) => i + start);
683
- if (show > 4 && start > 1) pages.splice(0, 2, 1, 0);
684
- if (show > 3 && end < max) pages.splice(-2, 2, 0, max);
685
- return pages;
878
+ _unmutate;
879
+ _render;
880
+ static get observedAttributes() {
881
+ return [
882
+ ATTR_LABEL,
883
+ ATTR_CURRENT,
884
+ ATTR_TOTAL,
885
+ ATTR_HREF
886
+ ];
887
+ }
888
+ connectedCallback() {
889
+ const total = attr(this, ATTR_TOTAL);
890
+ const current = attr(this, ATTR_CURRENT);
891
+ if (current && !total) warn(`Missing ${ATTR_TOTAL} attribute on:`, this);
892
+ if (total && !current) warn(`Missing ${ATTR_CURRENT} attribute on:`, this);
893
+ attr(this, ATTR_LABEL, attrOrCSS(this, ATTR_LABEL));
894
+ attr(this, "role", "navigation");
895
+ this._unmutate = onMutation(this, render$1, {
896
+ childList: true,
897
+ subtree: true
898
+ });
899
+ }
900
+ attributeChangedCallback() {
901
+ if (this._unmutate) render$1(this);
902
+ }
903
+ disconnectedCallback() {
904
+ this._unmutate?.();
905
+ this._unmutate = this._render = void 0;
906
+ }
907
+ };
908
+ const render$1 = (self) => {
909
+ const current = Number(attr(self, ATTR_CURRENT));
910
+ const total = Number(attr(self, ATTR_TOTAL));
911
+ if (current && total) {
912
+ const items = self.querySelectorAll("button,a");
913
+ const show = items.length - 2;
914
+ const href = attr(self, ATTR_HREF);
915
+ const { next, prev, pages } = pagination({
916
+ current,
917
+ total,
918
+ show
919
+ });
920
+ items.forEach((item, i) => {
921
+ const page = i ? items[i + 1] ? pages[i - 1]?.page : next : prev;
922
+ attr(item, "aria-current", pages[i - 1]?.current ? "true" : null);
923
+ attr(item, "aria-label", `${page ?? "hidden"}`);
924
+ attr(item, "role", page ? null : "none");
925
+ attr(item, "tabindex", page ? null : "-1");
926
+ if (item instanceof HTMLButtonElement) attr(item, "value", `${page}`);
927
+ if (href && item instanceof HTMLAnchorElement) attr(item, "href", href.replace("%d", `${page}`));
928
+ });
929
+ }
930
+ };
931
+ const getSteps = (now, max, show = Number.POSITIVE_INFINITY) => {
932
+ const offset = (show - 1) / 2;
933
+ const start = Math.max(Math.min(now - Math.floor(offset), max - show + 1), 1);
934
+ const end = Math.min(Math.max(now + Math.ceil(offset), show), max);
935
+ const pages = Array.from({ length: end + 1 - start }, (_, i) => i + start);
936
+ if (show > 4 && start > 1) pages.splice(0, 2, 1, 0);
937
+ if (show > 3 && end < max) pages.splice(-2, 2, 0, max);
938
+ return pages;
686
939
  };
687
940
  customElements.define("ds-pagination", DSPaginationElement);
688
-
689
- // src/suggestion/suggestion.ts
690
- import { UHTMLComboboxElement } from "@u-elements/u-combobox";
941
+ //#endregion
942
+ //#region src/suggestion/suggestion.ts
691
943
  var DSSuggestionElement = class extends UHTMLComboboxElement {
692
- _unmutate;
693
- // Using underscore instead of private fields for backwards compatibility
694
- _render;
695
- connectedCallback() {
696
- super.connectedCallback();
697
- this._render = () => render3(this);
698
- this._unmutate = onMutation(this, this._render, { childList: true });
699
- on(this, "toggle", polyfillToggleSource, QUICK_EVENT);
700
- }
701
- disconnectedCallback() {
702
- super.disconnectedCallback();
703
- this._unmutate?.();
704
- this._unmutate = this._render = void 0;
705
- off(this, "toggle", polyfillToggleSource, QUICK_EVENT);
706
- }
707
- };
708
- var render3 = ({ control, list }) => {
709
- if (control && !control.placeholder) attr(control, "placeholder", " ");
710
- if (control) attr(control, "popovertarget", useId(list) || null);
711
- if (list) attr(list, "popover", "manual");
712
- };
713
- var polyfillToggleSource = (event) => {
714
- const self = event.currentTarget;
715
- const detail = event.newState === "open" && self.control;
716
- if (detail)
717
- self.list?.dispatchEvent(new CustomEvent("ds-toggle-source", { detail }));
944
+ _render;
945
+ _unmutate;
946
+ connectedCallback() {
947
+ super.connectedCallback();
948
+ this._unmutate = onMutation(this, render, { childList: true });
949
+ on(this, "toggle", polyfillToggleSource, QUICK_EVENT);
950
+ }
951
+ disconnectedCallback() {
952
+ super.disconnectedCallback();
953
+ this._unmutate?.();
954
+ this._unmutate = this._render = void 0;
955
+ off(this, "toggle", polyfillToggleSource, QUICK_EVENT);
956
+ }
957
+ };
958
+ const render = ({ control, list }) => {
959
+ if (control && !control.placeholder) attr(control, "placeholder", " ");
960
+ if (control) attr(control, "popovertarget", useId(list) || null);
961
+ if (list) attr(list, "popover", "manual");
962
+ };
963
+ const polyfillToggleSource = (event) => {
964
+ const self = event.currentTarget;
965
+ const detail = event.newState === "open" && self.control;
966
+ if (detail) self.list?.dispatchEvent(new CustomEvent("ds-toggle-source", { detail }));
718
967
  };
719
968
  customElements.define("ds-suggestion", DSSuggestionElement);
720
-
721
- // src/tabs/tabs.ts
722
- import * as UTabs from "@u-elements/u-tabs";
723
- var DSTabsElement = class extends UTabs.UHTMLTabsElement {
724
- };
725
- var DSTabListElement = class extends UTabs.UHTMLTabListElement {
726
- };
727
- var DSTabElement = class extends UTabs.UHTMLTabElement {
728
- };
729
- var DSTabPanelElement = class extends UTabs.UHTMLTabPanelElement {
730
- };
969
+ //#endregion
970
+ //#region src/tabs/tabs.ts
971
+ var DSTabsElement = class extends UTabs.UHTMLTabsElement {};
972
+ var DSTabListElement = class extends UTabs.UHTMLTabListElement {};
973
+ var DSTabElement = class extends UTabs.UHTMLTabElement {};
974
+ var DSTabPanelElement = class extends UTabs.UHTMLTabPanelElement {};
731
975
  customElements.define("ds-tabs", DSTabsElement);
732
976
  customElements.define("ds-tablist", DSTabListElement);
733
977
  customElements.define("ds-tab", DSTabElement);
734
978
  customElements.define("ds-tabpanel", DSTabPanelElement);
979
+ //#endregion
980
+ //#region src/index.ts
981
+ if (isBrowser() && !isSupported()) apply();
982
+ //#endregion
983
+ export { DSBreadcrumbsElement, DSErrorSummaryElement, DSFieldElement, DSPaginationElement, DSSuggestionElement, DSTabElement, DSTabListElement, DSTabPanelElement, DSTabsElement, pagination, setTooltipElement };
735
984
 
736
- // src/index.ts
737
- if (isBrowser() && !isSupported()) polyfillInvokers();
738
- export {
739
- DSBreadcrumbsElement,
740
- DSErrorSummaryElement,
741
- DSFieldElement,
742
- DSPaginationElement,
743
- DSSuggestionElement,
744
- DSTabElement,
745
- DSTabListElement,
746
- DSTabPanelElement,
747
- DSTabsElement,
748
- pagination,
749
- setTooltipElement
750
- };
985
+ //# sourceMappingURL=index.js.map