@digdir/designsystemet-web 0.0.0-fix-designsystemet-web-20260121152531

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 (89) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +179 -0
  3. package/dist/cjs/_virtual/rolldown_runtime.cjs +29 -0
  4. package/dist/cjs/breadcrumbs.cjs +50 -0
  5. package/dist/cjs/breadcrumbs.cjs.map +1 -0
  6. package/dist/cjs/clickdelegatefor.cjs +35 -0
  7. package/dist/cjs/clickdelegatefor.cjs.map +1 -0
  8. package/dist/cjs/details.cjs +21 -0
  9. package/dist/cjs/details.cjs.map +1 -0
  10. package/dist/cjs/dialog.cjs +18 -0
  11. package/dist/cjs/dialog.cjs.map +1 -0
  12. package/dist/cjs/error-summary.cjs +24 -0
  13. package/dist/cjs/error-summary.cjs.map +1 -0
  14. package/dist/cjs/field.cjs +115 -0
  15. package/dist/cjs/field.cjs.map +1 -0
  16. package/dist/cjs/index.cjs +37 -0
  17. package/dist/cjs/index.cjs.map +1 -0
  18. package/dist/cjs/pagination.cjs +72 -0
  19. package/dist/cjs/pagination.cjs.map +1 -0
  20. package/dist/cjs/popover.cjs +89 -0
  21. package/dist/cjs/popover.cjs.map +1 -0
  22. package/dist/cjs/suggestion.cjs +24 -0
  23. package/dist/cjs/suggestion.cjs.map +1 -0
  24. package/dist/cjs/tabs.cjs +21 -0
  25. package/dist/cjs/tabs.cjs.map +1 -0
  26. package/dist/cjs/toggle-group.cjs +29 -0
  27. package/dist/cjs/toggle-group.cjs.map +1 -0
  28. package/dist/cjs/tooltip.cjs +55 -0
  29. package/dist/cjs/tooltip.cjs.map +1 -0
  30. package/dist/cjs/utils.cjs +159 -0
  31. package/dist/cjs/utils.cjs.map +1 -0
  32. package/dist/esm/breadcrumbs.js +50 -0
  33. package/dist/esm/breadcrumbs.js.map +1 -0
  34. package/dist/esm/clickdelegatefor.js +35 -0
  35. package/dist/esm/clickdelegatefor.js.map +1 -0
  36. package/dist/esm/details.js +21 -0
  37. package/dist/esm/details.js.map +1 -0
  38. package/dist/esm/dialog.js +18 -0
  39. package/dist/esm/dialog.js.map +1 -0
  40. package/dist/esm/error-summary.js +24 -0
  41. package/dist/esm/error-summary.js.map +1 -0
  42. package/dist/esm/field.js +115 -0
  43. package/dist/esm/field.js.map +1 -0
  44. package/dist/esm/index.js +22 -0
  45. package/dist/esm/index.js.map +1 -0
  46. package/dist/esm/pagination.js +71 -0
  47. package/dist/esm/pagination.js.map +1 -0
  48. package/dist/esm/popover.js +88 -0
  49. package/dist/esm/popover.js.map +1 -0
  50. package/dist/esm/src/breadcrumbs.d.ts +16 -0
  51. package/dist/esm/src/breadcrumbs.d.ts.map +1 -0
  52. package/dist/esm/src/clickdelegatefor.d.ts +2 -0
  53. package/dist/esm/src/clickdelegatefor.d.ts.map +1 -0
  54. package/dist/esm/src/details.d.ts +2 -0
  55. package/dist/esm/src/details.d.ts.map +1 -0
  56. package/dist/esm/src/dialog.d.ts +2 -0
  57. package/dist/esm/src/dialog.d.ts.map +1 -0
  58. package/dist/esm/src/error-summary.d.ts +12 -0
  59. package/dist/esm/src/error-summary.d.ts.map +1 -0
  60. package/dist/esm/src/field.d.ts +15 -0
  61. package/dist/esm/src/field.d.ts.map +1 -0
  62. package/dist/esm/src/index.d.ts +14 -0
  63. package/dist/esm/src/index.d.ts.map +1 -0
  64. package/dist/esm/src/pagination.d.ts +27 -0
  65. package/dist/esm/src/pagination.d.ts.map +1 -0
  66. package/dist/esm/src/popover.d.ts +2 -0
  67. package/dist/esm/src/popover.d.ts.map +1 -0
  68. package/dist/esm/src/suggestion.d.ts +11 -0
  69. package/dist/esm/src/suggestion.d.ts.map +1 -0
  70. package/dist/esm/src/tabs.d.ts +18 -0
  71. package/dist/esm/src/tabs.d.ts.map +1 -0
  72. package/dist/esm/src/toggle-group.d.ts +2 -0
  73. package/dist/esm/src/toggle-group.d.ts.map +1 -0
  74. package/dist/esm/src/tooltip.d.ts +2 -0
  75. package/dist/esm/src/tooltip.d.ts.map +1 -0
  76. package/dist/esm/src/utils.d.ts +78 -0
  77. package/dist/esm/src/utils.d.ts.map +1 -0
  78. package/dist/esm/suggestion.js +23 -0
  79. package/dist/esm/suggestion.js.map +1 -0
  80. package/dist/esm/tabs.js +16 -0
  81. package/dist/esm/tabs.js.map +1 -0
  82. package/dist/esm/toggle-group.js +29 -0
  83. package/dist/esm/toggle-group.js.map +1 -0
  84. package/dist/esm/tooltip.js +55 -0
  85. package/dist/esm/tooltip.js.map +1 -0
  86. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  87. package/dist/esm/utils.js +145 -0
  88. package/dist/esm/utils.js.map +1 -0
  89. package/package.json +47 -0
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2024 Digitaliseringsdirektoratet (Digdir)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,179 @@
1
+ > ⚠️ **WARNING** ⚠️
2
+ > This package is not fully released yet.
3
+ > It is still under development, and feedback is welcome.
4
+
5
+ ## `@digdir/designsystemet-web`
6
+
7
+ ### Types
8
+ We have simple DOM types for our web components that can be used in TypeScript projects.
9
+ Types should be automatically included when you install the package.
10
+
11
+ We **do not** have types from frameworks _yet_.
12
+
13
+ ### Get started
14
+
15
+ To get everything at once, you simply import the package:
16
+ ```ts
17
+ import '@digdir/designsystemet-web';
18
+ ```
19
+
20
+ This will register all web components and observers globally, so you only need to do this once.
21
+
22
+ ### `ds-breadcrumbs`
23
+ Automatically hides/shows `aria-label` on desktop/mobile and `aria-current="page"` on last link in list. No API.
24
+
25
+ ```html
26
+ <ds-breadcrumbs class="ds-breadcrumbs" aria-label="You are here:">
27
+ <a href="#none" aria-label="Tilbake til Nivå 3">
28
+ Nivå 3
29
+ </a>
30
+ <ol>
31
+ <li><a href="#none">Nivå 1</a></li>
32
+ <li><a href="#none">Nivå 2</a></li>
33
+ <li><a href="#none">Nivå 3</a></li>
34
+ </ol>
35
+ </ds-breadcrumbs>
36
+ ```
37
+
38
+ ### `details` and `summary`
39
+ Use native elements. We polyfill a bug in Firefox when combined with Android Talkback screen reader to announce state and role properly.
40
+
41
+ ```html
42
+ <details class="ds-details">
43
+ <summary>More info</summary>
44
+ <div>Lorem ipsum dolor sit amet.</div>
45
+ </details>
46
+ ```
47
+
48
+ ### `dialog`
49
+ Use the native `<dialog>` element. We polyfill support for `closedby="any"`.
50
+
51
+ ```html
52
+ <dialog class="ds-dialog" closedby="any" id="my-dialog">
53
+ my dialog
54
+ </dialog>
55
+ ```
56
+
57
+ See [Polyfills](#polyfills) for how to open and close the dialog with commands.
58
+
59
+ ### `ds-error-summary`
60
+ Automatically takes focus when visible and sets `aria-labelledby` to the first child heading. No API.
61
+
62
+ ```html
63
+ <ds-errorsummary class="ds-error-summary">
64
+ <h2>Oppsummering</h2>
65
+ <ul>
66
+ <li><a href="#none">Feil 1</a></li>
67
+ <li><a href="#none">Feil 2</a></li>
68
+ <li><a href="#none">Feil 3</a></li>
69
+ </ul>
70
+ </ds-errorsummary>
71
+ ```
72
+
73
+ ### `ds-field`
74
+ Connects inputs, labels and error messages.
75
+
76
+ ```html
77
+ <ds-field class="ds-field">
78
+ <label>Label</label>
79
+ <input type="text" placeholder="Placeholder" class="ds-input" />
80
+ <div class="ds-validation-message" data-field="validation">
81
+ Dette er ein feilmelding
82
+ </div>
83
+ </ds-field>
84
+ ```
85
+
86
+ #### Counter
87
+ You can add a counter to inputs and textareas by adding the `data-field="counter"` attribute to a `<p>` element inside a `ds-field`.
88
+
89
+ ```html
90
+ <ds-field class="ds-field">
91
+ <label>Label</label>
92
+ <textarea class="ds-input"></textarea>
93
+ <p data-field="counter" data-limit="20" data-over="%d tegn for mye" data-under="%d tegn igjen" data-hint="Maks %d tegn tillatt."></p>
94
+ </ds-field>
95
+ ```
96
+
97
+ | attribute | type | default | required |
98
+ |------------|--------|-----------------------|----------|
99
+ | data-limit | number | undefined | false |
100
+ | data-over | string | %d tegn for mye | false |
101
+ | data-under | string | %d tegn igjen | false |
102
+ | data-hint | string | Maks %d tegn tillatt. | false |
103
+
104
+ ### `ds-pagination`
105
+ Implements pagination, fills buttons with text.
106
+ You can use both `<a>` and `<button>` elements inside the pagination.
107
+
108
+ If you don't pass any attributes you can implement your own logic for current page and total pages.
109
+
110
+ ```html
111
+ <ds-pagination class="ds-pagination" aria-label="Bla i sider:" data-href="?page=$page" data-current="2" data-total="100">
112
+ <ol>
113
+ <li><a></a></li>
114
+ <li><a></a></li>
115
+ <li><a></a></li>
116
+ <li><a></a></li>
117
+ </ol>
118
+ </ds-pagination>
119
+ ```
120
+
121
+ ### `popover`
122
+ We use native popover functionality, but we attach an event listener that fixes placement of designsystem components.
123
+
124
+ ```html
125
+ <button class="ds-button" popovertarget="popover">Open popover</button>
126
+ <div class="ds-popover" popover id="popover" data-placement="left">
127
+ This is some popover content. It can be very long, but it will wrap and
128
+ stay within the viewport.
129
+ </div>
130
+ ```
131
+
132
+ | attribute | type | default | required |
133
+ |---------------|--------|------------|----------|
134
+ | data-placement | string | top | false |
135
+ | data-overscroll | 'contain' | undefined | undefined | false |
136
+ | data-autoplacement | boolean | true | false |
137
+
138
+ **If you don't use the class `ds-popover` you need to add the CSS property `--_ds-floating` to the popover element.** This can be `top`, `bottom`, `left` or `right`.
139
+
140
+ ### `ds-suggestion`
141
+ Extends `u-combobox` from u-elements. See u-elements for documentation:
142
+ [https://u-elements.github.io/u-elements/elements/u-combobox](https://u-elements.github.io/u-elements/elements/u-combobox)
143
+
144
+ ### `ds-tabs`
145
+ Extends `u-tabs` from u-elements. See u-elements for documentation:
146
+ [https://u-elements.github.io/u-elements/elements/u-tabs](https://u-elements.github.io/u-elements/elements/u-tabs)
147
+
148
+ ### `ds-toggle-group`
149
+ This is implemented differently from `ToggleGroup` in the react package.
150
+
151
+ An observer will look for `[data-toggle-group]` and add proper arrow navigation plus Enter-key support.
152
+
153
+ ```html
154
+ <fieldset class="ds-toggle-group" data-toggle-group="Tekstjustering" data-variant="secondary">
155
+ <label>
156
+ <input type="radio" name="alignment-two" value="left" checked />
157
+ Venstestilt
158
+ </label>
159
+ <label>
160
+ <input type="radio" name="alignment-two" value="center" />
161
+ Midtstilt
162
+ </label>
163
+ <label>
164
+ <input type="radio" name="alignment-two" value="right" />
165
+ Høyrestilt
166
+ </label>
167
+ </fieldset>
168
+ ```
169
+
170
+ ### `data-tooltip`
171
+ Using a single element for rendering next to elements with `data-tooltip` attribute.
172
+ Also automatically sets `aria-label` or `aria-description` as needed.
173
+
174
+ ```html
175
+ <button data-placement="left" data-tooltip="venstre" class="ds-button">venstre</button>
176
+ ```
177
+
178
+ ### Polyfills
179
+ We automatically attach [invokers-polyfill](https://www.npmjs.com/package/invokers-polyfill/v/0.5.2), which means that you get support for `command` and `commandfor`.
@@ -0,0 +1,29 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+
29
+ exports.__toESM = __toESM;
@@ -0,0 +1,50 @@
1
+ const require_utils = require('./utils.cjs');
2
+
3
+ //#region src/breadcrumbs.ts
4
+ const ATTR_LABEL = "aria-label";
5
+ const ATTR_LABEL_HIDDEN = "data-label";
6
+ var DSBreadcrumbsElement = class extends require_utils.DSElement {
7
+ _items;
8
+ _unresize;
9
+ _unmutate;
10
+ static get observedAttributes() {
11
+ return [ATTR_LABEL];
12
+ }
13
+ connectedCallback() {
14
+ if (!require_utils.attr(this, ATTR_LABEL_HIDDEN)) require_utils.attrRequiredWarning(this, ATTR_LABEL);
15
+ const render = require_utils.debounce(this.attributeChangedCallback.bind(this), 100);
16
+ this._items = this.getElementsByTagName("a");
17
+ this._unresize = require_utils.on(window, "resize", render);
18
+ this._unmutate = require_utils.onMutation(this, render, {
19
+ debounce: 0,
20
+ childList: true,
21
+ subtree: true
22
+ });
23
+ }
24
+ attributeChangedCallback() {
25
+ const last = this._items?.[this._items.length - 1];
26
+ const lastInList = last?.parentElement === this ? null : last;
27
+ const isListHidden = !lastInList?.offsetHeight;
28
+ const labelHidden = require_utils.attr(this, ATTR_LABEL_HIDDEN);
29
+ const label = require_utils.attr(this, ATTR_LABEL);
30
+ require_utils.attr(this, "role", isListHidden ? null : "navigation");
31
+ if (isListHidden && !labelHidden && label) {
32
+ require_utils.attr(this, ATTR_LABEL_HIDDEN, label);
33
+ require_utils.attr(this, ATTR_LABEL, null);
34
+ } else if (!isListHidden && labelHidden && !label) {
35
+ require_utils.attr(this, ATTR_LABEL, labelHidden);
36
+ require_utils.attr(this, ATTR_LABEL_HIDDEN, null);
37
+ }
38
+ for (const item of this._items || []) require_utils.attr(item, "aria-current", item === lastInList ? "page" : null);
39
+ }
40
+ disconnectedCallback() {
41
+ this._unresize?.();
42
+ this._unmutate?.();
43
+ this._unresize = this._unmutate = this._items = void 0;
44
+ }
45
+ };
46
+ require_utils.customElements.define("ds-breadcrumbs", DSBreadcrumbsElement);
47
+
48
+ //#endregion
49
+ exports.DSBreadcrumbsElement = DSBreadcrumbsElement;
50
+ //# sourceMappingURL=breadcrumbs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumbs.cjs","names":["DSElement","attr","debounce","on","onMutation","customElements"],"sources":["../../src/breadcrumbs.ts"],"sourcesContent":["import {\n attr,\n attrRequiredWarning,\n customElements,\n DSElement,\n debounce,\n on,\n onMutation,\n} from './utils';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-breadcrumbs': DSBreadcrumbsElement;\n }\n}\n\nconst ATTR_LABEL = 'aria-label';\nconst ATTR_LABEL_HIDDEN = 'data-label'; // Used to hide label on mobile when only showing back link\n\nexport class DSBreadcrumbsElement extends DSElement {\n _items?: HTMLCollectionOf<HTMLAnchorElement>; // Using underscore instead of private fields for backwards compatibility\n _unresize?: () => void;\n _unmutate?: () => void;\n\n static get observedAttributes() {\n return [ATTR_LABEL]; // Using ES2015 syntax for backwards compatibility\n }\n connectedCallback() {\n if (!attr(this, ATTR_LABEL_HIDDEN)) attrRequiredWarning(this, ATTR_LABEL); // aria-label can allready have been hidden by attributeChangedCallback\n const render = debounce(this.attributeChangedCallback.bind(this), 100);\n this._items = this.getElementsByTagName('a'); // Speed up by caching HTMLCollection\n this._unresize = on(window, 'resize', render);\n this._unmutate = onMutation(this, render, {\n debounce: 0, // No debounce, as we already debounce render\n childList: true,\n subtree: true,\n });\n }\n attributeChangedCallback() {\n const last = this._items?.[this._items.length - 1];\n const lastInList = last?.parentElement === this ? null : last;\n const isListHidden = !lastInList?.offsetHeight;\n const labelHidden = attr(this, ATTR_LABEL_HIDDEN);\n const label = attr(this, ATTR_LABEL);\n\n // Only labels if needed to prevent infinite attribute update loop\n attr(this, 'role', isListHidden ? null : 'navigation');\n if (isListHidden && !labelHidden && label) {\n attr(this, ATTR_LABEL_HIDDEN, label);\n attr(this, ATTR_LABEL, null);\n } else if (!isListHidden && labelHidden && !label) {\n attr(this, ATTR_LABEL, labelHidden);\n attr(this, ATTR_LABEL_HIDDEN, null);\n }\n\n for (const item of this._items || [])\n attr(item, 'aria-current', item === lastInList ? 'page' : null);\n }\n disconnectedCallback() {\n this._unresize?.();\n this._unmutate?.();\n this._unresize = this._unmutate = this._items = undefined;\n }\n}\n\ncustomElements.define('ds-breadcrumbs', DSBreadcrumbsElement);\n"],"mappings":";;;AAgBA,MAAM,aAAa;AACnB,MAAM,oBAAoB;AAE1B,IAAa,uBAAb,cAA0CA,wBAAU;CAClD;CACA;CACA;CAEA,WAAW,qBAAqB;AAC9B,SAAO,CAAC,WAAW;;CAErB,oBAAoB;AAClB,MAAI,CAACC,mBAAK,MAAM,kBAAkB,CAAE,mCAAoB,MAAM,WAAW;EACzE,MAAM,SAASC,uBAAS,KAAK,yBAAyB,KAAK,KAAK,EAAE,IAAI;AACtE,OAAK,SAAS,KAAK,qBAAqB,IAAI;AAC5C,OAAK,YAAYC,iBAAG,QAAQ,UAAU,OAAO;AAC7C,OAAK,YAAYC,yBAAW,MAAM,QAAQ;GACxC,UAAU;GACV,WAAW;GACX,SAAS;GACV,CAAC;;CAEJ,2BAA2B;EACzB,MAAM,OAAO,KAAK,SAAS,KAAK,OAAO,SAAS;EAChD,MAAM,aAAa,MAAM,kBAAkB,OAAO,OAAO;EACzD,MAAM,eAAe,CAAC,YAAY;EAClC,MAAM,cAAcH,mBAAK,MAAM,kBAAkB;EACjD,MAAM,QAAQA,mBAAK,MAAM,WAAW;AAGpC,qBAAK,MAAM,QAAQ,eAAe,OAAO,aAAa;AACtD,MAAI,gBAAgB,CAAC,eAAe,OAAO;AACzC,sBAAK,MAAM,mBAAmB,MAAM;AACpC,sBAAK,MAAM,YAAY,KAAK;aACnB,CAAC,gBAAgB,eAAe,CAAC,OAAO;AACjD,sBAAK,MAAM,YAAY,YAAY;AACnC,sBAAK,MAAM,mBAAmB,KAAK;;AAGrC,OAAK,MAAM,QAAQ,KAAK,UAAU,EAAE,CAClC,oBAAK,MAAM,gBAAgB,SAAS,aAAa,SAAS,KAAK;;CAEnE,uBAAuB;AACrB,OAAK,aAAa;AAClB,OAAK,aAAa;AAClB,OAAK,YAAY,KAAK,YAAY,KAAK,SAAS;;;AAIpDI,6BAAe,OAAO,kBAAkB,qBAAqB"}
@@ -0,0 +1,35 @@
1
+ const require_utils = require('./utils.cjs');
2
+
3
+ //#region src/clickdelegatefor.ts
4
+ const CLASS_HOVER = ":click-delegate-hover";
5
+ const ATTR_CLICKDELEGATEFOR = "data-clickdelegatefor";
6
+ const SELECTOR_CLICKDELEGATEFOR = `[${ATTR_CLICKDELEGATEFOR}]`;
7
+ const SELECTOR_SKIP = "a,button,label,input,select,textarea,dialog,[role=\"button\"],[popover],[contenteditable]";
8
+ const handleClickDelegateFor = (event) => {
9
+ const isNewTab = event.button === 1 || event.metaKey || event.ctrlKey;
10
+ const delegateTarget = event.isTrusted && event.button < 2 && getDelegateTarget(event);
11
+ if (delegateTarget instanceof HTMLAnchorElement && isNewTab) window.open(delegateTarget.href, void 0, delegateTarget.rel);
12
+ else if (delegateTarget instanceof HTMLElement && !delegateTarget.contains(event.target)) {
13
+ event.stopImmediatePropagation();
14
+ delegateTarget.click();
15
+ }
16
+ };
17
+ let HOVER;
18
+ const handleMouseOver = (event) => {
19
+ const delegateTarget = getDelegateTarget(event);
20
+ if (HOVER === delegateTarget) return;
21
+ if (HOVER) HOVER.classList.remove(CLASS_HOVER);
22
+ if (delegateTarget) delegateTarget.classList.add(CLASS_HOVER);
23
+ HOVER = delegateTarget;
24
+ };
25
+ const getDelegateTarget = ({ target: el }) => {
26
+ const id = (el instanceof Element ? el.closest(SELECTOR_CLICKDELEGATEFOR) : null)?.getAttribute(ATTR_CLICKDELEGATEFOR);
27
+ const target = document.getElementById(id || "");
28
+ const skip = target && el.closest(SELECTOR_SKIP);
29
+ return (!skip || skip === target) && target || void 0;
30
+ };
31
+ require_utils.onHotReload("clickdelegatefor", () => [require_utils.on(window, "click auxclick", handleClickDelegateFor, true), require_utils.on(document, "mouseover", handleMouseOver, require_utils.QUICK_EVENT)]);
32
+
33
+ //#endregion
34
+ exports.handleClickDelegateFor = handleClickDelegateFor;
35
+ //# sourceMappingURL=clickdelegatefor.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickdelegatefor.cjs","names":["onHotReload","on","QUICK_EVENT"],"sources":["../../src/clickdelegatefor.ts"],"sourcesContent":["// Adding support for click deletagtion, following\n// https://open-ui.org/components/link-area-delegation-explainer/\n// and https://github.com/openui/open-ui/issues/1104#issuecomment-3151387080\nimport { on, onHotReload, QUICK_EVENT } from './utils';\n\nconst CLASS_HOVER = ':click-delegate-hover';\nconst ATTR_CLICKDELEGATEFOR = 'data-clickdelegatefor';\nconst SELECTOR_CLICKDELEGATEFOR = `[${ATTR_CLICKDELEGATEFOR}]`;\nconst SELECTOR_SKIP =\n 'a,button,label,input,select,textarea,dialog,[role=\"button\"],[popover],[contenteditable]';\n\nexport const handleClickDelegateFor = (event: MouseEvent) => {\n const isNewTab = event.button === 1 || event.metaKey || event.ctrlKey;\n const isUserLeftOrMiddleClick = event.isTrusted && event.button < 2;\n const delegateTarget = isUserLeftOrMiddleClick && getDelegateTarget(event);\n\n if (delegateTarget instanceof HTMLAnchorElement && isNewTab)\n window.open(delegateTarget.href, undefined, delegateTarget.rel); // If middle click or cmd/ctrl click on link, open in new tab\n else if (\n delegateTarget instanceof HTMLElement &&\n !delegateTarget.contains(event.target as Node) // Only proxy event if delegated target isn't the original target\n ) {\n event.stopImmediatePropagation(); // We'll trigger a new click event anyway, so prevent actions on this one\n delegateTarget.click(); // Forward click to the clickable element\n }\n};\n\nlet HOVER: Element | undefined;\nconst handleMouseOver = (event: Event) => {\n const delegateTarget = getDelegateTarget(event);\n if (HOVER === delegateTarget) return; // No change\n if (HOVER) HOVER.classList.remove(CLASS_HOVER);\n if (delegateTarget) delegateTarget.classList.add(CLASS_HOVER);\n HOVER = delegateTarget;\n};\n\nconst getDelegateTarget = ({ target: el }: Event) => {\n const scope =\n el instanceof Element ? el.closest(SELECTOR_CLICKDELEGATEFOR) : null;\n const id = scope?.getAttribute(ATTR_CLICKDELEGATEFOR);\n const target = document.getElementById(id || '');\n const skip = target && (el as Element).closest(SELECTOR_SKIP); // Ignore if interactive\n\n return ((!skip || skip === target) && target) || undefined;\n};\n\nonHotReload('clickdelegatefor', () => [\n on(window, 'click auxclick', handleClickDelegateFor as EventListener, true), // Use capture to ensure we run before other click listeners\n on(document, 'mouseover', handleMouseOver, QUICK_EVENT), // Use passive for better performance\n]);\n"],"mappings":";;;AAKA,MAAM,cAAc;AACpB,MAAM,wBAAwB;AAC9B,MAAM,4BAA4B,IAAI,sBAAsB;AAC5D,MAAM,gBACJ;AAEF,MAAa,0BAA0B,UAAsB;CAC3D,MAAM,WAAW,MAAM,WAAW,KAAK,MAAM,WAAW,MAAM;CAE9D,MAAM,iBAD0B,MAAM,aAAa,MAAM,SAAS,KAChB,kBAAkB,MAAM;AAE1E,KAAI,0BAA0B,qBAAqB,SACjD,QAAO,KAAK,eAAe,MAAM,QAAW,eAAe,IAAI;UAE/D,0BAA0B,eAC1B,CAAC,eAAe,SAAS,MAAM,OAAe,EAC9C;AACA,QAAM,0BAA0B;AAChC,iBAAe,OAAO;;;AAI1B,IAAI;AACJ,MAAM,mBAAmB,UAAiB;CACxC,MAAM,iBAAiB,kBAAkB,MAAM;AAC/C,KAAI,UAAU,eAAgB;AAC9B,KAAI,MAAO,OAAM,UAAU,OAAO,YAAY;AAC9C,KAAI,eAAgB,gBAAe,UAAU,IAAI,YAAY;AAC7D,SAAQ;;AAGV,MAAM,qBAAqB,EAAE,QAAQ,SAAgB;CAGnD,MAAM,MADJ,cAAc,UAAU,GAAG,QAAQ,0BAA0B,GAAG,OAChD,aAAa,sBAAsB;CACrD,MAAM,SAAS,SAAS,eAAe,MAAM,GAAG;CAChD,MAAM,OAAO,UAAW,GAAe,QAAQ,cAAc;AAE7D,SAAS,CAAC,QAAQ,SAAS,WAAW,UAAW;;AAGnDA,0BAAY,0BAA0B,CACpCC,iBAAG,QAAQ,kBAAkB,wBAAyC,KAAK,EAC3EA,iBAAG,UAAU,aAAa,iBAAiBC,0BAAY,CACxD,CAAC"}
@@ -0,0 +1,21 @@
1
+ const require_utils = require('./utils.cjs');
2
+
3
+ //#region src/details.ts
4
+ const IS_ANDROID_FIREFOX = require_utils.isBrowser() && /android/i.test(navigator.userAgent) && /firefox/i.test(navigator.userAgent);
5
+ const SUMMARYS = IS_ANDROID_FIREFOX ? document.getElementsByTagName("summary") : [];
6
+ function handleAriaPolyfill() {
7
+ for (const summary of SUMMARYS) {
8
+ const open = !!summary.parentElement?.open;
9
+ require_utils.attr(summary, "role", "button");
10
+ require_utils.attr(summary, "aria-expanded", `${open}`);
11
+ }
12
+ }
13
+ if (IS_ANDROID_FIREFOX) require_utils.onHotReload("details-android-firefox", () => [require_utils.onMutation(document, handleAriaPolyfill, {
14
+ attributeFilter: ["open"],
15
+ attributes: true,
16
+ childList: true,
17
+ subtree: true
18
+ })]);
19
+
20
+ //#endregion
21
+ //# sourceMappingURL=details.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"details.cjs","names":["isBrowser","onMutation"],"sources":["../../src/details.ts"],"sourcesContent":["import { attr, isBrowser, onHotReload, onMutation } from './utils';\n\n// Polyfill for Android Firefox not supporting details/summary accessibility properly\nconst IS_ANDROID_FIREFOX =\n isBrowser() &&\n /android/i.test(navigator.userAgent) &&\n /firefox/i.test(navigator.userAgent);\nconst SUMMARYS = IS_ANDROID_FIREFOX\n ? document.getElementsByTagName('summary')\n : [];\n\nfunction handleAriaPolyfill() {\n for (const summary of SUMMARYS) {\n const open = !!(summary.parentElement as HTMLDetailsElement)?.open;\n attr(summary, 'role', 'button');\n attr(summary, 'aria-expanded', `${open}`);\n }\n}\n\nif (IS_ANDROID_FIREFOX)\n onHotReload('details-android-firefox', () => [\n onMutation(document, handleAriaPolyfill, {\n attributeFilter: ['open'],\n attributes: true,\n childList: true,\n subtree: true,\n }),\n ]);\n"],"mappings":";;;AAGA,MAAM,qBACJA,yBAAW,IACX,WAAW,KAAK,UAAU,UAAU,IACpC,WAAW,KAAK,UAAU,UAAU;AACtC,MAAM,WAAW,qBACb,SAAS,qBAAqB,UAAU,GACxC,EAAE;AAEN,SAAS,qBAAqB;AAC5B,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,OAAO,CAAC,CAAE,QAAQ,eAAsC;AAC9D,qBAAK,SAAS,QAAQ,SAAS;AAC/B,qBAAK,SAAS,iBAAiB,GAAG,OAAO;;;AAI7C,IAAI,mBACF,2BAAY,iCAAiC,CAC3CC,yBAAW,UAAU,oBAAoB;CACvC,iBAAiB,CAAC,OAAO;CACzB,YAAY;CACZ,WAAW;CACX,SAAS;CACV,CAAC,CACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ const require_utils = require('./utils.cjs');
2
+
3
+ //#region src/dialog.ts
4
+ let DOWN_INSIDE = false;
5
+ function handleClosedbyAny({ type, target: el, clientX: x = 0, clientY: y = 0 }) {
6
+ if (type === "pointerdown") {
7
+ const r = el?.closest?.("dialog")?.getBoundingClientRect();
8
+ DOWN_INSIDE = !!(r && r.top <= y && y <= r.bottom && r.left <= x && x <= r.right);
9
+ } else {
10
+ const isClose = el instanceof HTMLDialogElement && !DOWN_INSIDE && require_utils.attr(el, "closedby") === "any";
11
+ DOWN_INSIDE = false;
12
+ if (isClose) requestAnimationFrame(() => el.open && el.close());
13
+ }
14
+ }
15
+ require_utils.onHotReload("dialog-closedby", () => [require_utils.on(document, "pointerdown pointerup", handleClosedbyAny, require_utils.QUICK_EVENT)]);
16
+
17
+ //#endregion
18
+ //# sourceMappingURL=dialog.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog.cjs","names":["attr","onHotReload","on","QUICK_EVENT"],"sources":["../../src/dialog.ts"],"sourcesContent":["import { attr, on, onHotReload, QUICK_EVENT } from './utils';\n\n// Polyfill closedby functionaliy in Safari\n// Also in Safari 26.2 where `closedBy` property is supported natively,\n// but no corresponding functionality/behavior is implemented.\nlet DOWN_INSIDE = false; // Prevent close if selecting text inside dialog\nfunction handleClosedbyAny({\n type,\n target: el,\n clientX: x = 0,\n clientY: y = 0,\n}: Partial<MouseEvent>) {\n if (type === 'pointerdown') {\n const r = (el as Element)?.closest?.('dialog')?.getBoundingClientRect();\n const isInside =\n r && r.top <= y && y <= r.bottom && r.left <= x && x <= r.right;\n\n DOWN_INSIDE = !!isInside;\n } else {\n const isDialog = el instanceof HTMLDialogElement;\n const isClose = isDialog && !DOWN_INSIDE && attr(el, 'closedby') === 'any';\n\n DOWN_INSIDE = false; // Reset on every pointerup\n if (isClose) requestAnimationFrame(() => el.open && el.close()); // Close if browser did not do it\n }\n}\n\nonHotReload('dialog-closedby', () => [\n on(document, 'pointerdown pointerup', handleClosedbyAny, QUICK_EVENT),\n]);\n"],"mappings":";;;AAKA,IAAI,cAAc;AAClB,SAAS,kBAAkB,EACzB,MACA,QAAQ,IACR,SAAS,IAAI,GACb,SAAS,IAAI,KACS;AACtB,KAAI,SAAS,eAAe;EAC1B,MAAM,IAAK,IAAgB,UAAU,SAAS,EAAE,uBAAuB;AAIvE,gBAAc,CAAC,EAFb,KAAK,EAAE,OAAO,KAAK,KAAK,EAAE,UAAU,EAAE,QAAQ,KAAK,KAAK,EAAE;QAGvD;EAEL,MAAM,UADW,cAAc,qBACH,CAAC,eAAeA,mBAAK,IAAI,WAAW,KAAK;AAErE,gBAAc;AACd,MAAI,QAAS,6BAA4B,GAAG,QAAQ,GAAG,OAAO,CAAC;;;AAInEC,0BAAY,yBAAyB,CACnCC,iBAAG,UAAU,yBAAyB,mBAAmBC,0BAAY,CACtE,CAAC"}
@@ -0,0 +1,24 @@
1
+ const require_utils = require('./utils.cjs');
2
+
3
+ //#region src/error-summary.ts
4
+ var DSErrorSummaryElement = class extends require_utils.DSElement {
5
+ connectedCallback() {
6
+ require_utils.on(this, "animationend", this, require_utils.QUICK_EVENT);
7
+ requestAnimationFrame(() => this.handleEvent({ target: this }));
8
+ }
9
+ handleEvent({ target }) {
10
+ if (target !== this) return;
11
+ const heading = this.querySelector("h2,h3,h4,h5,h6");
12
+ if (heading) require_utils.attr(heading, "aria-labelledby", require_utils.useId(heading));
13
+ require_utils.attr(this, "tabindex", "-1");
14
+ this.focus();
15
+ }
16
+ disconnectedCallback() {
17
+ require_utils.off(this, "animationend", this, require_utils.QUICK_EVENT);
18
+ }
19
+ };
20
+ customElements.define("ds-error-summary", DSErrorSummaryElement);
21
+
22
+ //#endregion
23
+ exports.DSErrorSummaryElement = DSErrorSummaryElement;
24
+ //# sourceMappingURL=error-summary.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-summary.cjs","names":["DSElement","QUICK_EVENT","useId"],"sources":["../../src/error-summary.ts"],"sourcesContent":["import { attr, DSElement, off, on, QUICK_EVENT, useId } from './utils';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-error-summary': DSErrorSummaryElement;\n }\n}\n\nexport class DSErrorSummaryElement extends DSElement {\n connectedCallback() {\n on(this, 'animationend', this, QUICK_EVENT); // Using animationend to detect when element is visible\n requestAnimationFrame(() => this.handleEvent({ target: this })); // Initial setup when children has rendered\n }\n handleEvent({ target }: Partial<Event>) {\n if (target !== this) return; // Ignore if animation event was triggered by child\n const heading = this.querySelector('h2,h3,h4,h5,h6');\n if (heading) attr(heading, 'aria-labelledby', useId(heading));\n attr(this, 'tabindex', '-1');\n this.focus();\n }\n disconnectedCallback() {\n off(this, 'animationend', this, QUICK_EVENT);\n }\n}\n\ncustomElements.define('ds-error-summary', DSErrorSummaryElement);\n"],"mappings":";;;AAQA,IAAa,wBAAb,cAA2CA,wBAAU;CACnD,oBAAoB;AAClB,mBAAG,MAAM,gBAAgB,MAAMC,0BAAY;AAC3C,8BAA4B,KAAK,YAAY,EAAE,QAAQ,MAAM,CAAC,CAAC;;CAEjE,YAAY,EAAE,UAA0B;AACtC,MAAI,WAAW,KAAM;EACrB,MAAM,UAAU,KAAK,cAAc,iBAAiB;AACpD,MAAI,QAAS,oBAAK,SAAS,mBAAmBC,oBAAM,QAAQ,CAAC;AAC7D,qBAAK,MAAM,YAAY,KAAK;AAC5B,OAAK,OAAO;;CAEd,uBAAuB;AACrB,oBAAI,MAAM,gBAAgB,MAAMD,0BAAY;;;AAIhD,eAAe,OAAO,oBAAoB,sBAAsB"}
@@ -0,0 +1,115 @@
1
+ const require_utils = require('./utils.cjs');
2
+
3
+ //#region src/field.ts
4
+ const CSS_FIELD_SIZE = "--_ds-field-sizing";
5
+ const ATTR_COUNTER_TEXT = "data-counter-text";
6
+ const ATTR_COUNTER_ARIA = "data-counter-aria";
7
+ const ATTR_FIELD = "data-field";
8
+ const TYPE_DESCRIPTION = "description";
9
+ const TYPE_VALIDATION = "validation";
10
+ const COUNTER_DEBOUNCE = require_utils.isWindows() ? 800 : 200;
11
+ const COUNTER_TEXT = {
12
+ over: "%d tegn for mye",
13
+ under: "%d tegn igjen",
14
+ hint: "Maks %d tegn tillatt."
15
+ };
16
+ const SELECTOR_FIELDSET_DESCRIPTION = `:scope > [${ATTR_FIELD}="${TYPE_DESCRIPTION}"],:scope > legend + p`;
17
+ const SELECTOR_FIELDSET_VALIDATION = `:scope > [${ATTR_FIELD}="${TYPE_VALIDATION}"]`;
18
+ const SELECTOR_FIELD_COUNTER = "[data-field=\"counter\"]";
19
+ const FIELDS = /* @__PURE__ */ new Set();
20
+ const FILEDSETS = require_utils.isBrowser() ? document.getElementsByTagName("fieldset") : [];
21
+ const STYLE_SR_ONLY = `position:absolute;clip:rect(0 0 0 0);overflow:hidden;width:1px;height:1px;white-space:nowrap;pointer-events:none`;
22
+ const handleMutations = require_utils.debounce(() => {
23
+ setupFieldsets();
24
+ setupFields();
25
+ }, 100);
26
+ const setupFieldsets = () => {
27
+ for (const fieldset of FILEDSETS) require_utils.attr(fieldset, "aria-labelledby", [fieldset.querySelector("legend"), fieldset.querySelector(SELECTOR_FIELDSET_DESCRIPTION)].filter(isNotHidden).map(require_utils.useId).join(" "));
28
+ };
29
+ const setupFields = () => {
30
+ for (const field of FIELDS) {
31
+ const descs = [];
32
+ const labels = [];
33
+ let input;
34
+ for (const el of field.getElementsByTagName("*")) if (el instanceof HTMLLabelElement) labels.push(el);
35
+ else if (isInputLike(el)) {
36
+ if (input) console.warn(`Designsystemet: Fields should only have one input element. Use <fieldset> to group multiple fields:`, field);
37
+ input = el;
38
+ } else if (isNotHidden(el)) {
39
+ const type = el.getAttribute(ATTR_FIELD);
40
+ if (type === TYPE_VALIDATION) descs.unshift(el);
41
+ else if (type) descs.push(el);
42
+ }
43
+ if (!input) console.warn(`Designsystemet: Field is missing input element:`, field);
44
+ else {
45
+ for (const label of labels) require_utils.attr(label, "for", require_utils.useId(input));
46
+ const isBoolish = input.type === "radio" || input.type === "checkbox";
47
+ const fieldsetValidation = field.closest("fieldset")?.querySelector(SELECTOR_FIELDSET_VALIDATION);
48
+ if (isNotHidden(fieldsetValidation)) descs.unshift(fieldsetValidation);
49
+ field.handleEvent({ target: input });
50
+ require_utils.attr(field, "data-clickdelegatefor", isBoolish ? require_utils.useId(input) : null);
51
+ require_utils.attr(input, "aria-describedby", descs.map(require_utils.useId).join(" "));
52
+ require_utils.attr(input, "aria-invalid", `${descs.some(isInvalid)}`);
53
+ }
54
+ }
55
+ };
56
+ const getCounterText = (el, key, num) => (require_utils.attr(el, `data-${key}`) || COUNTER_TEXT[key]).replace("%d", `${Math.abs(num)}`);
57
+ const setupCounter = (field, target) => {
58
+ const el = isInputLike(target) && field.querySelector(SELECTOR_FIELD_COUNTER);
59
+ if (el) {
60
+ const live = field.shadowRoot?.lastElementChild;
61
+ const limit = Number(require_utils.attr(el, "data-limit")) || 0;
62
+ const count = limit - target.value.length;
63
+ const text = getCounterText(el, count < 0 ? "over" : "under", count);
64
+ require_utils.attr(el, ATTR_COUNTER_TEXT, text);
65
+ require_utils.attr(el, ATTR_COUNTER_ARIA, getCounterText(el, "hint", limit));
66
+ require_utils.attr(el, "data-color", count < 0 ? "danger" : null);
67
+ setupCounterLiveRegion(live, text);
68
+ }
69
+ };
70
+ const setupCounterLiveRegion = require_utils.debounce((live, text) => {
71
+ live.textContent = text;
72
+ }, COUNTER_DEBOUNCE);
73
+ const setupTextareaFieldSizingiOS = (target) => {
74
+ if (target instanceof HTMLTextAreaElement) {
75
+ target.style.setProperty(CSS_FIELD_SIZE, "auto");
76
+ target.style.setProperty(CSS_FIELD_SIZE, `${target.scrollHeight}px`);
77
+ }
78
+ };
79
+ const isInputLike = (el) => el instanceof HTMLElement && "validity" in el && !(el instanceof HTMLButtonElement);
80
+ const isNotHidden = (el) => !!el && !el.hidden;
81
+ const isInvalid = (el) => el.getAttribute(ATTR_FIELD) === TYPE_VALIDATION && require_utils.attr(el, "data-color") !== "success";
82
+ var DSFieldElement = class extends require_utils.DSElement {
83
+ constructor() {
84
+ super();
85
+ this.attachShadow({ mode: "open" }).append(require_utils.tag("slot"), require_utils.tag("div", {
86
+ "aria-live": "polite",
87
+ style: STYLE_SR_ONLY
88
+ }));
89
+ }
90
+ connectedCallback() {
91
+ FIELDS.add(this);
92
+ require_utils.on(this, "input", this, require_utils.QUICK_EVENT);
93
+ handleMutations();
94
+ }
95
+ handleEvent({ target }) {
96
+ setupCounter(this, target);
97
+ setupTextareaFieldSizingiOS(target);
98
+ }
99
+ disconnectedCallback() {
100
+ require_utils.off(this, "input", this, require_utils.QUICK_EVENT);
101
+ FIELDS.delete(this);
102
+ }
103
+ };
104
+ require_utils.customElements.define("ds-field", DSFieldElement);
105
+ require_utils.onHotReload("field", () => [require_utils.onMutation(document, handleMutations, {
106
+ debounce: false,
107
+ attributeFilter: ["hidden", ATTR_FIELD],
108
+ attributes: true,
109
+ childList: true,
110
+ subtree: true
111
+ })]);
112
+
113
+ //#endregion
114
+ exports.DSFieldElement = DSFieldElement;
115
+ //# sourceMappingURL=field.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.cjs","names":["isWindows","isBrowser","debounce","useId","attr","DSElement","tag","QUICK_EVENT","customElements","onHotReload","onMutation"],"sources":["../../src/field.ts"],"sourcesContent":["import {\n attr,\n customElements,\n DSElement,\n debounce,\n isBrowser,\n isWindows,\n off,\n on,\n onHotReload,\n onMutation,\n QUICK_EVENT,\n tag,\n useId,\n} from './utils';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-field': DSFieldElement;\n }\n}\n\nconst CSS_FIELD_SIZE = '--_ds-field-sizing';\nconst ATTR_COUNTER_TEXT = 'data-counter-text';\nconst ATTR_COUNTER_ARIA = 'data-counter-aria';\nconst ATTR_FIELD = 'data-field';\nconst TYPE_DESCRIPTION = 'description';\nconst TYPE_VALIDATION = 'validation';\nconst COUNTER_DEBOUNCE = isWindows() ? 800 : 200; // Longer debounce on Windows due to NVDA performance\nconst COUNTER_TEXT = {\n over: '%d tegn for mye',\n under: '%d tegn igjen',\n hint: 'Maks %d tegn tillatt.',\n};\n\nconst SELECTOR_FIELDSET_DESCRIPTION = `:scope > [${ATTR_FIELD}=\"${TYPE_DESCRIPTION}\"],:scope > legend + p`; // legend + p is kept for backwards compatibility\nconst SELECTOR_FIELDSET_VALIDATION = `:scope > [${ATTR_FIELD}=\"${TYPE_VALIDATION}\"]`;\nconst SELECTOR_FIELD_COUNTER = '[data-field=\"counter\"]';\n\nconst FIELDS = new Set<DSFieldElement>();\nconst FILEDSETS = isBrowser() ? document.getElementsByTagName('fieldset') : [];\nconst STYLE_SR_ONLY = `position:absolute;clip:rect(0 0 0 0);overflow:hidden;width:1px;height:1px;white-space:nowrap;pointer-events:none`;\n\n// TODO: Document that Validation must be hidden with \"hidden\" attribute (or completely removed from DOM), not display: none\nconst handleMutations = debounce(() => {\n setupFieldsets();\n setupFields();\n}, 100); // Debounce to avoid excessive calculations on multiple mutations\n\n// Connect fieldset legend and descriptions\nconst setupFieldsets = () => {\n for (const fieldset of FILEDSETS) {\n const labelledby = [\n fieldset.querySelector('legend'),\n fieldset.querySelector(SELECTOR_FIELDSET_DESCRIPTION),\n ].filter(isNotHidden);\n\n attr(fieldset, 'aria-labelledby', labelledby.map(useId).join(' '));\n }\n};\n\nconst setupFields = () => {\n for (const field of FIELDS) {\n const descs: Element[] = [];\n const labels: HTMLLabelElement[] = [];\n let input: HTMLInputElement | undefined;\n\n for (const el of field.getElementsByTagName('*')) {\n if (el instanceof HTMLLabelElement) labels.push(el);\n else if (isInputLike(el)) {\n if (input)\n console.warn(\n `Designsystemet: Fields should only have one input element. Use <fieldset> to group multiple fields:`,\n field,\n );\n input = el;\n } else if (isNotHidden(el)) {\n const type = el.getAttribute(ATTR_FIELD); // Using getAttribute not attr for best performance\n if (type === TYPE_VALIDATION) descs.unshift(el);\n else if (type) descs.push(el);\n }\n }\n\n if (!input)\n console.warn(`Designsystemet: Field is missing input element:`, field);\n else {\n for (const label of labels) attr(label, 'for', useId(input));\n\n const isBoolish = input.type === 'radio' || input.type === 'checkbox';\n const fieldsetValidation = field\n .closest('fieldset')\n ?.querySelector(SELECTOR_FIELDSET_VALIDATION);\n if (isNotHidden(fieldsetValidation)) descs.unshift(fieldsetValidation);\n\n field.handleEvent({ target: input }); // Run counter and textrarea resize\n attr(field, 'data-clickdelegatefor', isBoolish ? useId(input) : null); // Expand click area to ds-field if radio/checkbox\n attr(input, 'aria-describedby', descs.map(useId).join(' '));\n attr(input, 'aria-invalid', `${descs.some(isInvalid)}`);\n }\n }\n};\n\nconst getCounterText = (\n el: Element,\n key: keyof typeof COUNTER_TEXT,\n num: number,\n) =>\n (attr(el, `data-${key}`) || COUNTER_TEXT[key]).replace(\n '%d',\n `${Math.abs(num)}`,\n );\n\nconst setupCounter = (field: DSFieldElement, target: EventTarget | null) => {\n const el =\n isInputLike(target) &&\n field.querySelector<HTMLElement>(SELECTOR_FIELD_COUNTER);\n\n if (el) {\n const live = field.shadowRoot?.lastElementChild as HTMLElement;\n const limit = Number(attr(el, 'data-limit')) || 0;\n const count = limit - target.value.length;\n const text = getCounterText(el, count < 0 ? 'over' : 'under', count);\n\n attr(el, ATTR_COUNTER_TEXT, text);\n attr(el, ATTR_COUNTER_ARIA, getCounterText(el, 'hint', limit));\n attr(el, 'data-color', count < 0 ? 'danger' : null);\n setupCounterLiveRegion(live, text); // Debounce live region to avoid NVDA interupting announcing typed text\n }\n};\n\nconst setupCounterLiveRegion = debounce((live: Element, text: string) => {\n live.textContent = text;\n}, COUNTER_DEBOUNCE);\n\n// iOS does not support field-sizing: content, so we need to manually resize\nconst setupTextareaFieldSizingiOS = (target: EventTarget | null) => {\n if (target instanceof HTMLTextAreaElement) {\n target.style.setProperty(CSS_FIELD_SIZE, 'auto');\n target.style.setProperty(CSS_FIELD_SIZE, `${target.scrollHeight}px`);\n }\n};\n\nconst isInputLike = (el: unknown): el is HTMLInputElement =>\n el instanceof HTMLElement &&\n 'validity' in el && // Adds support for custom elements implemeted with attachInternals()\n !(el instanceof HTMLButtonElement); // But skip <button> elements\n\nconst isNotHidden = (el?: Element | null): el is Element =>\n !!el && !(el as HTMLElement).hidden;\n\nconst isInvalid = (el: Element): boolean =>\n el.getAttribute(ATTR_FIELD) === TYPE_VALIDATION &&\n attr(el, 'data-color') !== 'success';\n\n// Custom element is used to performantly keep track of fields on the page\nexport class DSFieldElement extends DSElement {\n constructor() {\n super();\n this.attachShadow({ mode: 'open' }).append(\n tag('slot'),\n tag('div', { 'aria-live': 'polite', style: STYLE_SR_ONLY }), // Used to announce counter updates\n );\n }\n connectedCallback() {\n FIELDS.add(this);\n on(this, 'input', this, QUICK_EVENT);\n handleMutations(); // Initial setup\n }\n handleEvent({ target }: { target: EventTarget | null }) {\n setupCounter(this, target);\n setupTextareaFieldSizingiOS(target);\n }\n disconnectedCallback() {\n off(this, 'input', this, QUICK_EVENT);\n FIELDS.delete(this);\n }\n}\n\ncustomElements.define('ds-field', DSFieldElement);\n\nonHotReload('field', () => [\n onMutation(document, handleMutations, {\n debounce: false, // No need to timeout debounce here as we handle it ourselves\n attributeFilter: ['hidden', ATTR_FIELD], // Listen for hidden to detect hidden validations\n attributes: true,\n childList: true,\n subtree: true,\n }),\n]);\n"],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ const require_utils = require('./utils.cjs');
2
+ const require_breadcrumbs = require('./breadcrumbs.cjs');
3
+ require('./clickdelegatefor.cjs');
4
+ require('./details.cjs');
5
+ require('./dialog.cjs');
6
+ const require_error_summary = require('./error-summary.cjs');
7
+ const require_field = require('./field.cjs');
8
+ const require_pagination = require('./pagination.cjs');
9
+ const require_suggestion = require('./suggestion.cjs');
10
+ const require_tabs = require('./tabs.cjs');
11
+ require('./popover.cjs');
12
+ require('./toggle-group.cjs');
13
+ require('./tooltip.cjs');
14
+
15
+ //#region src/index.ts
16
+ if (require_utils.isBrowser()) import("invokers-polyfill");
17
+
18
+ //#endregion
19
+ exports.DSBreadcrumbsElement = require_breadcrumbs.DSBreadcrumbsElement;
20
+ exports.DSErrorSummaryElement = require_error_summary.DSErrorSummaryElement;
21
+ exports.DSFieldElement = require_field.DSFieldElement;
22
+ exports.DSPaginationElement = require_pagination.DSPaginationElement;
23
+ exports.DSSuggestionElement = require_suggestion.DSSuggestionElement;
24
+ exports.DSTabElement = require_tabs.DSTabElement;
25
+ exports.DSTabListElement = require_tabs.DSTabListElement;
26
+ exports.DSTabPanelElement = require_tabs.DSTabPanelElement;
27
+ exports.DSTabsElement = require_tabs.DSTabsElement;
28
+ exports.pagination = require_pagination.pagination;
29
+ var _u_elements_u_datalist = require("@u-elements/u-datalist");
30
+ Object.keys(_u_elements_u_datalist).forEach(function (k) {
31
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
32
+ enumerable: true,
33
+ get: function () { return _u_elements_u_datalist[k]; }
34
+ });
35
+ });
36
+
37
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["isBrowser"],"sources":["../../src/index.ts"],"sourcesContent":["import { isBrowser } from './utils';\n\n// Ensure polyfill is loaded in browser environment only\nif (isBrowser()) import('invokers-polyfill');\n\nexport * from '@u-elements/u-datalist'; // Re-export u-datalist since this is a pure polyfill and not custom Designsystemet elements\nexport * from './breadcrumbs';\nexport * from './error-summary';\nexport * from './field';\nexport * from './pagination';\nexport * from './suggestion';\nexport * from './tabs';\nimport './clickdelegatefor';\nimport './details';\nimport './dialog';\nimport './popover';\nimport './toggle-group';\nimport './tooltip';\n"],"mappings":";;;;;;;;;;;;;;;AAGA,IAAIA,yBAAW,CAAE,QAAO"}