@digdir/designsystemet-web 0.0.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +277 -21
  3. package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/cjs/breadcrumbs/breadcrumbs.cjs +2 -0
  5. package/dist/cjs/breadcrumbs/breadcrumbs.cjs.map +1 -0
  6. package/dist/cjs/clickdelegatefor/clickdelegatefor.cjs +2 -0
  7. package/dist/cjs/clickdelegatefor/clickdelegatefor.cjs.map +1 -0
  8. package/dist/cjs/dialog/dialog.cjs +2 -0
  9. package/dist/cjs/dialog/dialog.cjs.map +1 -0
  10. package/dist/cjs/error-summary/error-summary.cjs +2 -0
  11. package/dist/cjs/error-summary/error-summary.cjs.map +1 -0
  12. package/dist/cjs/field/field.cjs +2 -0
  13. package/dist/cjs/field/field.cjs.map +1 -0
  14. package/dist/cjs/index.cjs +1 -36
  15. package/dist/cjs/index.cjs.map +1 -1
  16. package/dist/cjs/pagination/pagination.cjs +2 -0
  17. package/dist/cjs/pagination/pagination.cjs.map +1 -0
  18. package/dist/cjs/popover/popover.cjs +2 -0
  19. package/dist/cjs/popover/popover.cjs.map +1 -0
  20. package/dist/cjs/readonly/readonly.cjs +2 -0
  21. package/dist/cjs/readonly/readonly.cjs.map +1 -0
  22. package/dist/cjs/suggestion/suggestion.cjs +2 -0
  23. package/dist/cjs/suggestion/suggestion.cjs.map +1 -0
  24. package/dist/cjs/tabs/tabs.cjs +2 -0
  25. package/dist/cjs/tabs/tabs.cjs.map +1 -0
  26. package/dist/cjs/toggle-group/toggle-group.cjs +2 -0
  27. package/dist/cjs/toggle-group/toggle-group.cjs.map +1 -0
  28. package/dist/cjs/tooltip/tooltip.cjs +2 -0
  29. package/dist/cjs/tooltip/tooltip.cjs.map +1 -0
  30. package/dist/cjs/utils/utils.cjs +2 -0
  31. package/dist/cjs/utils/utils.cjs.map +1 -0
  32. package/dist/custom-elements.json +531 -0
  33. package/dist/esm/breadcrumbs/breadcrumbs.js +2 -0
  34. package/dist/esm/breadcrumbs/breadcrumbs.js.map +1 -0
  35. package/dist/esm/clickdelegatefor/clickdelegatefor.js +2 -0
  36. package/dist/esm/clickdelegatefor/clickdelegatefor.js.map +1 -0
  37. package/dist/esm/dialog/dialog.js +2 -0
  38. package/dist/esm/dialog/dialog.js.map +1 -0
  39. package/dist/esm/error-summary/error-summary.js +2 -0
  40. package/dist/esm/error-summary/error-summary.js.map +1 -0
  41. package/dist/esm/field/field.js +2 -0
  42. package/dist/esm/field/field.js.map +1 -0
  43. package/dist/esm/index.js +1 -21
  44. package/dist/esm/index.js.map +1 -1
  45. package/dist/esm/pagination/pagination.js +2 -0
  46. package/dist/esm/pagination/pagination.js.map +1 -0
  47. package/dist/esm/popover/popover.js +2 -0
  48. package/dist/esm/popover/popover.js.map +1 -0
  49. package/dist/esm/readonly/readonly.js +2 -0
  50. package/dist/esm/readonly/readonly.js.map +1 -0
  51. package/dist/esm/suggestion/suggestion.js +2 -0
  52. package/dist/esm/suggestion/suggestion.js.map +1 -0
  53. package/dist/esm/tabs/tabs.js +2 -0
  54. package/dist/esm/tabs/tabs.js.map +1 -0
  55. package/dist/esm/toggle-group/toggle-group.js +2 -0
  56. package/dist/esm/toggle-group/toggle-group.js.map +1 -0
  57. package/dist/esm/tooltip/tooltip.js +2 -0
  58. package/dist/esm/tooltip/tooltip.js.map +1 -0
  59. package/dist/esm/utils/utils.js +2 -0
  60. package/dist/esm/utils/utils.js.map +1 -0
  61. package/dist/index.d.ts +321 -0
  62. package/dist/index.js +710 -0
  63. package/dist/umd/index.js +15 -0
  64. package/dist/umd/index.js.map +1 -0
  65. package/dist/vscode.html-custom-data.json +60 -0
  66. package/dist/web.manifest.json +969 -0
  67. package/dist/web.vscode.json +60 -0
  68. package/package.json +56 -46
  69. package/dist/cjs/_virtual/rolldown_runtime.cjs +0 -29
  70. package/dist/cjs/breadcrumbs.cjs +0 -50
  71. package/dist/cjs/breadcrumbs.cjs.map +0 -1
  72. package/dist/cjs/clickdelegatefor.cjs +0 -35
  73. package/dist/cjs/clickdelegatefor.cjs.map +0 -1
  74. package/dist/cjs/details.cjs +0 -21
  75. package/dist/cjs/details.cjs.map +0 -1
  76. package/dist/cjs/dialog.cjs +0 -18
  77. package/dist/cjs/dialog.cjs.map +0 -1
  78. package/dist/cjs/error-summary.cjs +0 -24
  79. package/dist/cjs/error-summary.cjs.map +0 -1
  80. package/dist/cjs/field.cjs +0 -115
  81. package/dist/cjs/field.cjs.map +0 -1
  82. package/dist/cjs/pagination.cjs +0 -72
  83. package/dist/cjs/pagination.cjs.map +0 -1
  84. package/dist/cjs/popover.cjs +0 -89
  85. package/dist/cjs/popover.cjs.map +0 -1
  86. package/dist/cjs/suggestion.cjs +0 -24
  87. package/dist/cjs/suggestion.cjs.map +0 -1
  88. package/dist/cjs/tabs.cjs +0 -21
  89. package/dist/cjs/tabs.cjs.map +0 -1
  90. package/dist/cjs/toggle-group.cjs +0 -29
  91. package/dist/cjs/toggle-group.cjs.map +0 -1
  92. package/dist/cjs/tooltip.cjs +0 -55
  93. package/dist/cjs/tooltip.cjs.map +0 -1
  94. package/dist/cjs/utils.cjs +0 -159
  95. package/dist/cjs/utils.cjs.map +0 -1
  96. package/dist/esm/breadcrumbs.js +0 -50
  97. package/dist/esm/breadcrumbs.js.map +0 -1
  98. package/dist/esm/clickdelegatefor.js +0 -35
  99. package/dist/esm/clickdelegatefor.js.map +0 -1
  100. package/dist/esm/details.js +0 -21
  101. package/dist/esm/details.js.map +0 -1
  102. package/dist/esm/dialog.js +0 -18
  103. package/dist/esm/dialog.js.map +0 -1
  104. package/dist/esm/error-summary.js +0 -24
  105. package/dist/esm/error-summary.js.map +0 -1
  106. package/dist/esm/field.js +0 -115
  107. package/dist/esm/field.js.map +0 -1
  108. package/dist/esm/pagination.js +0 -71
  109. package/dist/esm/pagination.js.map +0 -1
  110. package/dist/esm/popover.js +0 -88
  111. package/dist/esm/popover.js.map +0 -1
  112. package/dist/esm/src/breadcrumbs.d.ts +0 -16
  113. package/dist/esm/src/breadcrumbs.d.ts.map +0 -1
  114. package/dist/esm/src/clickdelegatefor.d.ts +0 -2
  115. package/dist/esm/src/clickdelegatefor.d.ts.map +0 -1
  116. package/dist/esm/src/details.d.ts +0 -2
  117. package/dist/esm/src/details.d.ts.map +0 -1
  118. package/dist/esm/src/dialog.d.ts +0 -2
  119. package/dist/esm/src/dialog.d.ts.map +0 -1
  120. package/dist/esm/src/error-summary.d.ts +0 -12
  121. package/dist/esm/src/error-summary.d.ts.map +0 -1
  122. package/dist/esm/src/field.d.ts +0 -15
  123. package/dist/esm/src/field.d.ts.map +0 -1
  124. package/dist/esm/src/index.d.ts +0 -14
  125. package/dist/esm/src/index.d.ts.map +0 -1
  126. package/dist/esm/src/pagination.d.ts +0 -27
  127. package/dist/esm/src/pagination.d.ts.map +0 -1
  128. package/dist/esm/src/popover.d.ts +0 -2
  129. package/dist/esm/src/popover.d.ts.map +0 -1
  130. package/dist/esm/src/suggestion.d.ts +0 -11
  131. package/dist/esm/src/suggestion.d.ts.map +0 -1
  132. package/dist/esm/src/tabs.d.ts +0 -18
  133. package/dist/esm/src/tabs.d.ts.map +0 -1
  134. package/dist/esm/src/toggle-group.d.ts +0 -2
  135. package/dist/esm/src/toggle-group.d.ts.map +0 -1
  136. package/dist/esm/src/tooltip.d.ts +0 -2
  137. package/dist/esm/src/tooltip.d.ts.map +0 -1
  138. package/dist/esm/src/utils.d.ts +0 -78
  139. package/dist/esm/src/utils.d.ts.map +0 -1
  140. package/dist/esm/suggestion.js +0 -23
  141. package/dist/esm/suggestion.js.map +0 -1
  142. package/dist/esm/tabs.js +0 -16
  143. package/dist/esm/tabs.js.map +0 -1
  144. package/dist/esm/toggle-group.js +0 -29
  145. package/dist/esm/toggle-group.js.map +0 -1
  146. package/dist/esm/tooltip.js +0 -55
  147. package/dist/esm/tooltip.js.map +0 -1
  148. package/dist/esm/tsconfig.tsbuildinfo +0 -1
  149. package/dist/esm/utils.js +0 -145
  150. package/dist/esm/utils.js.map +0 -1
package/dist/index.js ADDED
@@ -0,0 +1,710 @@
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;
11
+ function debounce(callback, delay) {
12
+ let timer;
13
+ return function(...args) {
14
+ clearTimeout(timer);
15
+ timer = setTimeout(() => callback.apply(this, args), delay);
16
+ };
17
+ }
18
+ var warn = (message, ...args) => typeof window === "undefined" || 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;
84
+ var hash = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 5)}`;
85
+ function useId(el) {
86
+ if (el && !el.id) el.id = `${hash}${++id}`;
87
+ return el?.id || "";
88
+ }
89
+
90
+ // src/index.ts
91
+ import "@u-elements/u-details/polyfill";
92
+
93
+ // src/clickdelegatefor/clickdelegatefor.ts
94
+ var CLASS_HOVER = ":click-delegate-hover";
95
+ var ATTR_CLICKDELEGATEFOR = "data-clickdelegatefor";
96
+ var SELECTOR_CLICKDELEGATEFOR = `[${ATTR_CLICKDELEGATEFOR}]`;
97
+ var SELECTOR_SKIP = 'a,button,label,input,select,textarea,details,dialog,[role="button"],[popover],[contenteditable]';
98
+ var handleClickDelegateFor = (event) => {
99
+ const isNewTab = event.button === 1 || event.metaKey || event.ctrlKey;
100
+ const isUserLeftOrMiddleClick = event.isTrusted && event.button < 2;
101
+ const delegateTarget = isUserLeftOrMiddleClick && getDelegateTarget(event);
102
+ if (!delegateTarget || delegateTarget.contains(event.target)) return;
103
+ if (isNewTab && delegateTarget instanceof HTMLAnchorElement)
104
+ return window.open(delegateTarget.href, void 0, delegateTarget.rel);
105
+ event.stopImmediatePropagation();
106
+ delegateTarget.click();
107
+ };
108
+ var HOVER;
109
+ var handleMouseOver = (event) => {
110
+ const delegateTarget = getDelegateTarget(event);
111
+ if (HOVER === delegateTarget) return;
112
+ if (HOVER) HOVER.classList.remove(CLASS_HOVER);
113
+ if (delegateTarget) delegateTarget.classList.add(CLASS_HOVER);
114
+ HOVER = delegateTarget;
115
+ };
116
+ var getDelegateTarget = ({ target: el }) => {
117
+ const scope = el instanceof Element ? el.closest(SELECTOR_CLICKDELEGATEFOR) : null;
118
+ const id2 = scope?.getAttribute(ATTR_CLICKDELEGATEFOR);
119
+ const target = id2 && document.getElementById(id2) || void 0;
120
+ const skip = target && el.closest(SELECTOR_SKIP);
121
+ return (!skip || skip === target) && !target?.disabled ? target : void 0;
122
+ };
123
+ onHotReload("clickdelegatefor", () => [
124
+ on(window, "click auxclick", handleClickDelegateFor, true),
125
+ // Use capture to ensure we run before other click listeners
126
+ on(document, "mouseover", handleMouseOver, QUICK_EVENT)
127
+ // Use passive for better performance
128
+ ]);
129
+
130
+ // src/dialog/dialog.ts
131
+ var DOWN_INSIDE = false;
132
+ var handleClosedbyAny = ({
133
+ type,
134
+ target: el,
135
+ clientX: x = 0,
136
+ clientY: y = 0
137
+ }) => {
138
+ if (type === "pointerdown") {
139
+ const r = el?.closest?.("dialog")?.getBoundingClientRect();
140
+ const isInside = r && r.top <= y && y <= r.bottom && r.left <= x && x <= r.right;
141
+ DOWN_INSIDE = !!isInside;
142
+ } else {
143
+ const isDialog = el instanceof HTMLDialogElement;
144
+ const isClose = isDialog && !DOWN_INSIDE && attr(el, "closedby") === "any";
145
+ DOWN_INSIDE = false;
146
+ if (isClose) requestAnimationFrame(() => el.open && el.close());
147
+ }
148
+ };
149
+ var handleAriaAttributes = () => {
150
+ for (const btn of document.querySelectorAll('button[command="show-modal"]'))
151
+ attr(btn, "aria-haspopup", "dialog");
152
+ };
153
+ var handleCommand = ({ command, target }) => command === "--show-non-modal" && target instanceof HTMLDialogElement && target.show();
154
+ onHotReload("dialog", () => [
155
+ on(document, "command", handleCommand, QUICK_EVENT),
156
+ on(document, "pointerdown pointerup", handleClosedbyAny, QUICK_EVENT),
157
+ onMutation(document, handleAriaAttributes, {
158
+ attributeFilter: ["command"],
159
+ attributes: true,
160
+ childList: true,
161
+ subtree: true
162
+ })
163
+ ]);
164
+
165
+ // src/popover/popover.ts
166
+ import {
167
+ autoUpdate,
168
+ computePosition,
169
+ flip,
170
+ limitShift,
171
+ offset,
172
+ shift,
173
+ size
174
+ } from "@floating-ui/dom";
175
+ var ATTR_PLACE = "data-placement";
176
+ var ATTR_AUTO = "data-autoplacement";
177
+ var POPOVERS = /* @__PURE__ */ new Map();
178
+ function handleToggle(event) {
179
+ let { newState, oldState, target: el, source = event.detail } = event;
180
+ const float = el instanceof HTMLElement && getCSSProp(el, "--_ds-floating");
181
+ if (!float) return;
182
+ if (newState === "closed") return POPOVERS.get(el)?.();
183
+ if (!source) {
184
+ const root = el.getRootNode();
185
+ const css = `[popovertarget="${el.id}"],[commandfor="${el.id}"]`;
186
+ source = el.id && root?.querySelector?.(css) || void 0;
187
+ }
188
+ if (!source || source === el || oldState && oldState === newState) return;
189
+ const padding = 10;
190
+ const overscroll = getCSSProp(el, "--_ds-floating-overscroll");
191
+ const placement = attr(el, ATTR_PLACE) || attr(source, ATTR_PLACE) || float;
192
+ const auto = attr(el, ATTR_AUTO) || attr(source, ATTR_AUTO);
193
+ const arrowSize = parseFloat(getComputedStyle(el, "::before").height) || 0;
194
+ const shiftProp = placement.match(/left|right/gi) ? "Height" : "Width";
195
+ const shiftLimit = source[`offset${shiftProp}`] / 2 + arrowSize;
196
+ if (placement === "none") return;
197
+ const options = {
198
+ strategy: "absolute",
199
+ placement,
200
+ middleware: [
201
+ offset(arrowSize || 0),
202
+ // Add space for arrow or default to 8px
203
+ shift({
204
+ padding,
205
+ limiter: limitShift({ offset: { mainAxis: shiftLimit } })
206
+ // Prevent from shifing away from source
207
+ }),
208
+ arrowPseudo(),
209
+ ...auto !== "false" ? [flip({ padding, crossAxis: false })] : [],
210
+ ...overscroll ? [
211
+ size({
212
+ apply({ availableHeight }) {
213
+ if (overscroll === "fit")
214
+ el.style.width = `${source.clientWidth}px`;
215
+ el.style.maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;
216
+ }
217
+ })
218
+ ] : []
219
+ ]
220
+ };
221
+ const unfloat = autoUpdate(source, el, async () => {
222
+ if (!source?.isConnected) return POPOVERS.get(el)?.();
223
+ const { x, y } = await computePosition(source, el, options);
224
+ el.style.translate = `${x}px ${y}px`;
225
+ });
226
+ POPOVERS.set(el, () => POPOVERS.delete(el) && unfloat());
227
+ }
228
+ var IS_SCROLL;
229
+ var handleScrollbar = ({ type }) => {
230
+ if (type === "mousedown") IS_SCROLL = false;
231
+ if (type === "scroll" && IS_SCROLL === false) IS_SCROLL = true;
232
+ if (type === "mouseup" && IS_SCROLL)
233
+ for (const [popover] of POPOVERS) popover.showPopover();
234
+ };
235
+ onHotReload("popover", () => [
236
+ on(document, "mousedown scroll mouseup", handleScrollbar, true),
237
+ on(document, "toggle ds-toggle-source", handleToggle, QUICK_EVENT)
238
+ // Use capture since the toggle event does not bubble
239
+ ]);
240
+ var getCSSProp = (el, prop) => getComputedStyle(el).getPropertyValue(prop).trim();
241
+ var arrowPseudo = () => ({
242
+ name: "arrowPseudo",
243
+ fn(data) {
244
+ const target = data.elements.floating;
245
+ const source = data.rects.reference;
246
+ const x = `${Math.round(source.width / 2 + source.x - data.x)}px`;
247
+ const y = `${Math.round(source.height / 2 + source.y - data.y)}px`;
248
+ target.style.setProperty("--_ds-floating-arrow-x", x);
249
+ target.style.setProperty("--_ds-floating-arrow-y", y);
250
+ attr(target, "data-floating", data.placement);
251
+ return data;
252
+ }
253
+ });
254
+
255
+ // src/readonly/readonly.ts
256
+ var isReadOnly = (el) => (el instanceof HTMLSelectElement || el instanceof HTMLInputElement) && (el.hasAttribute("readonly") || attr(el, "aria-readonly") === "true");
257
+ var handleKeyDown = (e) => {
258
+ if (e.key !== "Tab" && isReadOnly(e.target)) {
259
+ e.preventDefault();
260
+ if (e.key?.startsWith("Arrow") && attr(e.target, "type") === "radio") {
261
+ const all = document.querySelectorAll(`input[name="${e.target.name}"]`);
262
+ const move = e.key?.match(/Arrow(Right|Down)/) ? 1 : -1;
263
+ const next = all.length + [...all].indexOf(e.target) + move;
264
+ all[next % all.length]?.focus();
265
+ }
266
+ }
267
+ };
268
+ var handleClick = (e) => {
269
+ const input = e.target?.closest?.("label")?.control || e.target;
270
+ if (isReadOnly(input)) {
271
+ e.preventDefault();
272
+ input.focus();
273
+ }
274
+ };
275
+ var handleMouseDown = (e) => {
276
+ if (e.target instanceof HTMLSelectElement && isReadOnly(e.target))
277
+ e.preventDefault();
278
+ };
279
+ onHotReload("readonly", () => [
280
+ on(document, "keydown", handleKeyDown),
281
+ on(document, "click", handleClick),
282
+ // click needed for <label> and <input>
283
+ on(document, "mousedown", handleMouseDown)
284
+ // mousedown needed for <select>
285
+ ]);
286
+
287
+ // src/toggle-group/toggle-group.ts
288
+ var ATTR_TOGGLEGROUP = "data-toggle-group";
289
+ var SELECTOR_TOGGLEGROUP = `[${ATTR_TOGGLEGROUP}]`;
290
+ var handleAriaAttributes2 = debounce(() => {
291
+ for (const group of document.querySelectorAll(SELECTOR_TOGGLEGROUP))
292
+ attr(group, "aria-label", attrOrCSS(group, ATTR_TOGGLEGROUP));
293
+ }, 0);
294
+ var handleKeydown = (event) => {
295
+ const group = event.target instanceof HTMLInputElement && event.target.closest(SELECTOR_TOGGLEGROUP);
296
+ if (!group) return;
297
+ if (event.key === "Enter") event.target.click();
298
+ if (event.key?.startsWith("Arrow")) {
299
+ event.preventDefault?.();
300
+ const inputs = group.getElementsByTagName("input");
301
+ const index = [...inputs].indexOf(event.target);
302
+ const move = event.key.match(/Arrow(Right|Down)/) ? 1 : -1;
303
+ inputs[(inputs.length + index + move) % inputs.length]?.focus();
304
+ }
305
+ };
306
+ onHotReload("toggle-group", () => [
307
+ on(document, "keydown", handleKeydown),
308
+ onMutation(document, handleAriaAttributes2, {
309
+ attributeFilter: [ATTR_TOGGLEGROUP],
310
+ attributes: true,
311
+ childList: true,
312
+ subtree: true
313
+ })
314
+ ]);
315
+
316
+ // src/tooltip/tooltip.ts
317
+ var TIP;
318
+ var SOURCE;
319
+ var HOVER_TIMER = 0;
320
+ var SKIP_TIMER = 0;
321
+ var ATTR_TOOLTIP = "data-tooltip";
322
+ var ATTR_COLOR = "data-color";
323
+ var ARIA_LABEL = "aria-label";
324
+ var ARIA_DESC = "aria-description";
325
+ var SELECTOR_COLOR = `[${ATTR_COLOR}]`;
326
+ var SELECTOR_TOOLTIP = `[${ATTR_TOOLTIP}]`;
327
+ var ATTR_SCHEME = "data-color-scheme";
328
+ var SELECTOR_SCHEME = `[${ATTR_SCHEME}]`;
329
+ var SELECTOR_INTERACTIVE = "a,button,input,label,select,textarea,[tabindex]";
330
+ var DELAY_HOVER = 300;
331
+ var DELAY_SKIP = 300;
332
+ var setTooltipElement = (el) => {
333
+ if (el && !(el instanceof HTMLElement))
334
+ warn("setTooltipElement expects an HTMLElement, got: ", el);
335
+ TIP = el || void 0;
336
+ };
337
+ var handleAriaAttributes3 = debounce(() => {
338
+ for (const el of document.querySelectorAll(SELECTOR_TOOLTIP)) {
339
+ const aria = el.getAttribute(ARIA_LABEL) || el.getAttribute(ARIA_DESC);
340
+ const text = el.getAttribute(ATTR_TOOLTIP) || attrOrCSS(el, ATTR_TOOLTIP);
341
+ if (aria !== text) {
342
+ const hasText = attr(el, "role") !== "img" && el.textContent?.trim();
343
+ attr(el, ATTR_TOOLTIP, text);
344
+ attr(el, ARIA_LABEL, hasText ? null : text);
345
+ attr(el, ARIA_DESC, hasText ? text : null);
346
+ if (!el.matches(SELECTOR_INTERACTIVE))
347
+ warn('Missing tabindex="0" attribute on: ', el);
348
+ }
349
+ }
350
+ }, 0);
351
+ var handleInterest = ({ type, target }) => {
352
+ clearTimeout(HOVER_TIMER);
353
+ if (target === TIP) return;
354
+ if (type === "mouseover" && !SOURCE) {
355
+ HOVER_TIMER = setTimeout(handleInterest, DELAY_HOVER, { target });
356
+ return;
357
+ }
358
+ const source = target?.closest?.(`[${ATTR_TOOLTIP}]`);
359
+ if (source === SOURCE) return;
360
+ if (!source) return hideTooltip();
361
+ if (!TIP) TIP = tag("div", { class: "ds-tooltip" });
362
+ if (!TIP.isConnected) document.body.appendChild(TIP);
363
+ const color = source.closest(SELECTOR_COLOR);
364
+ const scheme = source.closest(SELECTOR_SCHEME);
365
+ const isReset = color !== scheme && color?.contains(scheme);
366
+ clearTimeout(SKIP_TIMER);
367
+ attr(TIP, "popover", "manual");
368
+ attr(TIP, ATTR_SCHEME, scheme?.getAttribute(ATTR_SCHEME) || null);
369
+ attr(TIP, ATTR_COLOR, isReset && color?.getAttribute(ATTR_COLOR) || null);
370
+ setTextWithoutMutation(TIP, attr(source, ATTR_TOOLTIP));
371
+ TIP.showPopover();
372
+ TIP.dispatchEvent(new CustomEvent("ds-toggle-source", { detail: source }));
373
+ SOURCE = source;
374
+ };
375
+ var hideTooltip = () => TIP?.isConnected && TIP.popover && TIP.hidePopover();
376
+ var handleClose = (event) => {
377
+ if (event?.type === "keydown")
378
+ return event?.key === "Escape" && TIP?.hidePopover();
379
+ if (!event) SOURCE = void 0;
380
+ else if (event.target === TIP && event.newState === "closed")
381
+ SKIP_TIMER = setTimeout(handleClose, DELAY_SKIP);
382
+ };
383
+ onHotReload("tooltip", () => [
384
+ on(document, "blur focus mouseover", handleInterest, QUICK_EVENT),
385
+ on(document, "toggle keydown", handleClose, QUICK_EVENT),
386
+ onMutation(document, handleAriaAttributes3, {
387
+ attributeFilter: [ATTR_TOOLTIP],
388
+ attributes: true,
389
+ childList: true,
390
+ subtree: true
391
+ })
392
+ ]);
393
+
394
+ // src/index.ts
395
+ export * from "@u-elements/u-datalist";
396
+
397
+ // src/breadcrumbs/breadcrumbs.ts
398
+ var ATTR_LABEL = "aria-label";
399
+ var DSBreadcrumbsElement = class extends DSElement {
400
+ _items;
401
+ // Using underscore instead of private fields for backwards compatibility
402
+ _label = null;
403
+ _render;
404
+ _unresize;
405
+ _unmutate;
406
+ static get observedAttributes() {
407
+ return [ATTR_LABEL];
408
+ }
409
+ connectedCallback() {
410
+ this._label = attrOrCSS(this, ATTR_LABEL);
411
+ this._items = this.getElementsByTagName("a");
412
+ this._render = debounce(() => render(this), 100);
413
+ this._unresize = on(window, "resize", this._render);
414
+ this._unmutate = onMutation(this, this._render, {
415
+ childList: true,
416
+ subtree: true
417
+ });
418
+ }
419
+ attributeChangedCallback(_name, _prev, next) {
420
+ if (next) this._label = next;
421
+ this._render?.();
422
+ }
423
+ disconnectedCallback() {
424
+ this._unresize?.();
425
+ this._unmutate?.();
426
+ this._unresize = this._unmutate = this._render = this._items = void 0;
427
+ }
428
+ };
429
+ var render = (self) => {
430
+ const lastItem = self._items?.[self._items.length - 1];
431
+ const lastItemInList = lastItem?.parentElement === self ? null : lastItem;
432
+ const isListHidden = !lastItemInList?.offsetHeight;
433
+ attr(self, "role", isListHidden ? null : "navigation");
434
+ attr(self, ATTR_LABEL, isListHidden ? null : self._label);
435
+ for (const item of self._items || [])
436
+ attr(item, "aria-current", item === lastItemInList ? "page" : null);
437
+ };
438
+ customElements.define("ds-breadcrumbs", DSBreadcrumbsElement);
439
+
440
+ // src/error-summary/error-summary.ts
441
+ var DSErrorSummaryElement = class extends DSElement {
442
+ connectedCallback() {
443
+ on(this, "animationend", this, QUICK_EVENT);
444
+ requestAnimationFrame(() => this.handleEvent({ target: this }));
445
+ }
446
+ handleEvent({ target }) {
447
+ if (target !== this) return;
448
+ const heading = this.querySelector("h2,h3,h4,h5,h6");
449
+ if (heading) attr(this, "aria-labelledby", useId(heading));
450
+ attr(this, "tabindex", "-1");
451
+ this.focus();
452
+ }
453
+ disconnectedCallback() {
454
+ off(this, "animationend", this, QUICK_EVENT);
455
+ }
456
+ };
457
+ customElements.define("ds-error-summary", DSErrorSummaryElement);
458
+
459
+ // src/field/field.ts
460
+ var INDETERMINATE = "data-indeterminate";
461
+ var FIELDS = /* @__PURE__ */ new Set();
462
+ var COUNTS = /* @__PURE__ */ new WeakMap();
463
+ var FIELDSETS = isBrowser() ? document.getElementsByTagName("fieldset") : [];
464
+ var HAS_FIELD_SIZING = isBrowser() && CSS.supports("field-sizing", "content");
465
+ var COUNTER_DEBOUNCE = isWindows() ? 800 : 200;
466
+ var HAS_VALIDATION = /* @__PURE__ */ new WeakSet();
467
+ var handleMutations = debounce(() => {
468
+ for (const el of FIELDSETS) {
469
+ const labelledby = `${useId(el.querySelector("legend"))} ${useId(el.querySelector(':scope > :is([data-field="description"],legend + p)'))}`;
470
+ attr(el, "aria-labelledby", labelledby.trim() || null);
471
+ }
472
+ for (const field of FIELDS) {
473
+ const descs = [];
474
+ const labels = [];
475
+ let input;
476
+ let counter;
477
+ let hasValidation = false;
478
+ let invalid = false;
479
+ for (const el of field.getElementsByTagName("*")) {
480
+ if (el instanceof HTMLLabelElement) labels.push(el);
481
+ if (el.hidden) continue;
482
+ if (isInputLike(el)) {
483
+ if (input)
484
+ warn(
485
+ `Fields should only have one input element. Use <fieldset> to group multiple fields:`,
486
+ field
487
+ );
488
+ else input = el;
489
+ } else {
490
+ const type = el.getAttribute("data-field");
491
+ if (type === "counter") counter = el;
492
+ if (type === "validation") {
493
+ descs.unshift(el);
494
+ hasValidation = true;
495
+ invalid = invalid || isInvalid(el);
496
+ } else if (type) descs.push(el);
497
+ }
498
+ }
499
+ if (!input) warn(`Field is missing input element:`, field);
500
+ else {
501
+ if (counter) COUNTS.set(input, counter);
502
+ for (const label of labels) attr(label, "for", useId(input));
503
+ const isBoolish = input.type === "radio" || input.type === "checkbox";
504
+ const fieldsetValidation = field.closest("fieldset")?.querySelector(':scope > [data-field="validation"]');
505
+ if (fieldsetValidation && !fieldsetValidation?.hidden) {
506
+ hasValidation = true;
507
+ invalid = invalid || isInvalid(fieldsetValidation);
508
+ descs.unshift(fieldsetValidation);
509
+ }
510
+ const indeterminate = attr(input, INDETERMINATE);
511
+ if (indeterminate) input.indeterminate = indeterminate === "true";
512
+ attr(field, "data-clickdelegatefor", isBoolish ? useId(input) : null);
513
+ attr(input, "aria-describedby", descs.map(useId).join(" ") || null);
514
+ if (hasValidation || HAS_VALIDATION.has(input)) {
515
+ HAS_VALIDATION[hasValidation ? "add" : "delete"](input);
516
+ attr(input, "aria-invalid", `${invalid}`);
517
+ }
518
+ updateField(input);
519
+ }
520
+ }
521
+ }, 0);
522
+ var SR_ONLY = "position:fixed;white-space:nowrap;clip:rect(0 0 0 0)";
523
+ var SR_LIVE = isBrowser() ? tag("div", { "aria-live": "polite", style: SR_ONLY }) : null;
524
+ var updateField = (e) => {
525
+ const input = e.target || e;
526
+ const counter = COUNTS.get(input);
527
+ if (counter?.isConnected) {
528
+ const limit = Number(attr(counter, "data-limit")) || 0;
529
+ const count = limit - input.value.length;
530
+ const state = count < 0 ? "over" : "under";
531
+ const label = attrOrCSS(counter, `data-${state}`)?.replace(
532
+ "%d",
533
+ `${Math.abs(count)}`
534
+ );
535
+ attr(counter, "data-label", label);
536
+ attr(counter, "data-state", state);
537
+ attr(counter, "data-color", count < 0 ? "danger" : null);
538
+ if (e.type === "input" && SR_LIVE && label) {
539
+ if (!SR_LIVE?.isConnected) document.body.appendChild(SR_LIVE);
540
+ debouncedCounterLiveRegion(input, label);
541
+ }
542
+ }
543
+ if (!HAS_FIELD_SIZING && input instanceof HTMLTextAreaElement) {
544
+ input.style.setProperty("--_ds-field-sizing", "auto");
545
+ input.style.setProperty("--_ds-field-sizing", `${input.scrollHeight}px`);
546
+ }
547
+ };
548
+ var debouncedCounterLiveRegion = debounce((input, text) => {
549
+ const hasFocus = document.activeElement === input;
550
+ if (SR_LIVE?.isConnected && hasFocus) setTextWithoutMutation(SR_LIVE, text);
551
+ }, COUNTER_DEBOUNCE);
552
+ var isInvalid = (el) => el.getAttribute("data-color") !== "success";
553
+ var isInputLike = (el) => el instanceof HTMLElement && "validity" in el && // Adds support for custom elements implemeted with attachInternals()
554
+ !(el instanceof HTMLButtonElement) && // But skip <button> elements
555
+ el.type !== "hidden";
556
+ var DSFieldElement = class extends DSElement {
557
+ connectedCallback() {
558
+ FIELDS.add(this);
559
+ handleMutations();
560
+ }
561
+ disconnectedCallback() {
562
+ FIELDS.delete(this);
563
+ }
564
+ };
565
+ customElements.define("ds-field", DSFieldElement);
566
+ onHotReload("field", () => [
567
+ on(document, "input", updateField, QUICK_EVENT),
568
+ onMutation(document, handleMutations, {
569
+ attributeFilter: ["value", "hidden", "data-field", INDETERMINATE],
570
+ attributes: true,
571
+ childList: true,
572
+ subtree: true
573
+ })
574
+ ]);
575
+
576
+ // src/pagination/pagination.ts
577
+ var ATTR_LABEL2 = "aria-label";
578
+ var ATTR_CURRENT = "data-current";
579
+ var ATTR_TOTAL = "data-total";
580
+ var ATTR_HREF = "data-href";
581
+ var pagination = ({ current = 1, total = 10, show = 7 }) => ({
582
+ prev: current > 1 ? current - 1 : 0,
583
+ next: current < total ? current + 1 : 0,
584
+ pages: getSteps(current, total, show).map((page, index) => ({
585
+ current: page === current && "page",
586
+ key: `key-${page}-${index}`,
587
+ page
588
+ }))
589
+ });
590
+ var DSPaginationElement = class extends DSElement {
591
+ _unmutate;
592
+ // Using underscore instead of private fields for backwards compatibility
593
+ _render;
594
+ static get observedAttributes() {
595
+ return [ATTR_LABEL2, ATTR_CURRENT, ATTR_TOTAL, ATTR_HREF];
596
+ }
597
+ connectedCallback() {
598
+ const total = attr(this, ATTR_TOTAL);
599
+ const current = attr(this, ATTR_CURRENT);
600
+ if (current && !total) warn(`Missing ${ATTR_TOTAL} attribute on:`, this);
601
+ if (total && !current) warn(`Missing ${ATTR_CURRENT} attribute on:`, this);
602
+ attr(this, ATTR_LABEL2, attrOrCSS(this, ATTR_LABEL2));
603
+ attr(this, "role", "navigation");
604
+ this._render = debounce(() => render2(this), 0);
605
+ this._unmutate = onMutation(this, this._render, {
606
+ childList: true,
607
+ subtree: true
608
+ });
609
+ }
610
+ attributeChangedCallback() {
611
+ this._render?.();
612
+ }
613
+ disconnectedCallback() {
614
+ this._unmutate?.();
615
+ this._unmutate = this._render = void 0;
616
+ }
617
+ };
618
+ var render2 = (self) => {
619
+ const current = Number(attr(self, ATTR_CURRENT));
620
+ const total = Number(attr(self, ATTR_TOTAL));
621
+ if (current && total) {
622
+ const items = self.querySelectorAll("button,a");
623
+ const show = items.length - 2;
624
+ const href = attr(self, ATTR_HREF);
625
+ const { next, prev, pages } = pagination({ current, total, show });
626
+ items.forEach((item, i) => {
627
+ const page = i ? items[i + 1] ? pages[i - 1]?.page : next : prev;
628
+ attr(item, "aria-current", pages[i - 1]?.current ? "true" : null);
629
+ attr(item, "aria-label", `${page ?? "hidden"}`);
630
+ attr(item, "role", page ? null : "none");
631
+ attr(item, "tabindex", page ? null : "-1");
632
+ if (item instanceof HTMLButtonElement) attr(item, "value", `${page}`);
633
+ if (href && item instanceof HTMLAnchorElement)
634
+ attr(item, "href", href.replace("%d", `${page}`));
635
+ });
636
+ }
637
+ };
638
+ var getSteps = (now, max, show = Number.POSITIVE_INFINITY) => {
639
+ const offset2 = (show - 1) / 2;
640
+ const start = Math.max(Math.min(now - Math.floor(offset2), max - show + 1), 1);
641
+ const end = Math.min(Math.max(now + Math.ceil(offset2), show), max);
642
+ const pages = Array.from({ length: end + 1 - start }, (_, i) => i + start);
643
+ if (show > 4 && start > 1) pages.splice(0, 2, 1, 0);
644
+ if (show > 3 && end < max) pages.splice(-2, 2, 0, max);
645
+ return pages;
646
+ };
647
+ customElements.define("ds-pagination", DSPaginationElement);
648
+
649
+ // src/suggestion/suggestion.ts
650
+ import { UHTMLComboboxElement } from "@u-elements/u-combobox";
651
+ var DSSuggestionElement = class extends UHTMLComboboxElement {
652
+ _unmutate;
653
+ // Using underscore instead of private fields for backwards compatibility
654
+ _render;
655
+ connectedCallback() {
656
+ super.connectedCallback();
657
+ this._render = () => render3(this);
658
+ this._unmutate = onMutation(this, this._render, { childList: true });
659
+ on(this, "toggle", polyfillToggleSource, QUICK_EVENT);
660
+ }
661
+ disconnectedCallback() {
662
+ super.disconnectedCallback();
663
+ this._unmutate?.();
664
+ this._unmutate = this._render = void 0;
665
+ off(this, "toggle", polyfillToggleSource, QUICK_EVENT);
666
+ }
667
+ };
668
+ var render3 = ({ control, list }) => {
669
+ if (control && !control.placeholder) attr(control, "placeholder", " ");
670
+ if (control) attr(control, "popovertarget", useId(list) || null);
671
+ if (list) attr(list, "popover", "manual");
672
+ };
673
+ var polyfillToggleSource = (event) => {
674
+ const self = event.currentTarget;
675
+ const detail = event.newState === "open" && self.control;
676
+ if (detail)
677
+ self.list?.dispatchEvent(new CustomEvent("ds-toggle-source", { detail }));
678
+ };
679
+ customElements.define("ds-suggestion", DSSuggestionElement);
680
+
681
+ // src/tabs/tabs.ts
682
+ import * as UTabs from "@u-elements/u-tabs";
683
+ var DSTabsElement = class extends UTabs.UHTMLTabsElement {
684
+ };
685
+ var DSTabListElement = class extends UTabs.UHTMLTabListElement {
686
+ };
687
+ var DSTabElement = class extends UTabs.UHTMLTabElement {
688
+ };
689
+ var DSTabPanelElement = class extends UTabs.UHTMLTabPanelElement {
690
+ };
691
+ customElements.define("ds-tabs", DSTabsElement);
692
+ customElements.define("ds-tablist", DSTabListElement);
693
+ customElements.define("ds-tab", DSTabElement);
694
+ customElements.define("ds-tabpanel", DSTabPanelElement);
695
+
696
+ // src/index.ts
697
+ if (isBrowser() && !isSupported()) polyfillInvokers();
698
+ export {
699
+ DSBreadcrumbsElement,
700
+ DSErrorSummaryElement,
701
+ DSFieldElement,
702
+ DSPaginationElement,
703
+ DSSuggestionElement,
704
+ DSTabElement,
705
+ DSTabListElement,
706
+ DSTabPanelElement,
707
+ DSTabsElement,
708
+ pagination,
709
+ setTooltipElement
710
+ };