@digdir/designsystemet-web 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/cjs/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/cjs/breadcrumbs.cjs +50 -0
- package/dist/cjs/breadcrumbs.cjs.map +1 -0
- package/dist/cjs/clickdelegatefor.cjs +35 -0
- package/dist/cjs/clickdelegatefor.cjs.map +1 -0
- package/dist/cjs/details.cjs +21 -0
- package/dist/cjs/details.cjs.map +1 -0
- package/dist/cjs/dialog.cjs +18 -0
- package/dist/cjs/dialog.cjs.map +1 -0
- package/dist/cjs/error-summary.cjs +24 -0
- package/dist/cjs/error-summary.cjs.map +1 -0
- package/dist/cjs/field.cjs +115 -0
- package/dist/cjs/field.cjs.map +1 -0
- package/dist/cjs/index.cjs +37 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/pagination.cjs +72 -0
- package/dist/cjs/pagination.cjs.map +1 -0
- package/dist/cjs/popover.cjs +89 -0
- package/dist/cjs/popover.cjs.map +1 -0
- package/dist/cjs/suggestion.cjs +24 -0
- package/dist/cjs/suggestion.cjs.map +1 -0
- package/dist/cjs/tabs.cjs +21 -0
- package/dist/cjs/tabs.cjs.map +1 -0
- package/dist/cjs/toggle-group.cjs +29 -0
- package/dist/cjs/toggle-group.cjs.map +1 -0
- package/dist/cjs/tooltip.cjs +55 -0
- package/dist/cjs/tooltip.cjs.map +1 -0
- package/dist/cjs/utils.cjs +159 -0
- package/dist/cjs/utils.cjs.map +1 -0
- package/dist/esm/breadcrumbs.js +50 -0
- package/dist/esm/breadcrumbs.js.map +1 -0
- package/dist/esm/clickdelegatefor.js +35 -0
- package/dist/esm/clickdelegatefor.js.map +1 -0
- package/dist/esm/details.js +21 -0
- package/dist/esm/details.js.map +1 -0
- package/dist/esm/dialog.js +18 -0
- package/dist/esm/dialog.js.map +1 -0
- package/dist/esm/error-summary.js +24 -0
- package/dist/esm/error-summary.js.map +1 -0
- package/dist/esm/field.js +115 -0
- package/dist/esm/field.js.map +1 -0
- package/dist/esm/index.js +22 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/pagination.js +71 -0
- package/dist/esm/pagination.js.map +1 -0
- package/dist/esm/popover.js +88 -0
- package/dist/esm/popover.js.map +1 -0
- package/dist/esm/src/breadcrumbs.d.ts +16 -0
- package/dist/esm/src/breadcrumbs.d.ts.map +1 -0
- package/dist/esm/src/clickdelegatefor.d.ts +2 -0
- package/dist/esm/src/clickdelegatefor.d.ts.map +1 -0
- package/dist/esm/src/details.d.ts +2 -0
- package/dist/esm/src/details.d.ts.map +1 -0
- package/dist/esm/src/dialog.d.ts +2 -0
- package/dist/esm/src/dialog.d.ts.map +1 -0
- package/dist/esm/src/error-summary.d.ts +12 -0
- package/dist/esm/src/error-summary.d.ts.map +1 -0
- package/dist/esm/src/field.d.ts +15 -0
- package/dist/esm/src/field.d.ts.map +1 -0
- package/dist/esm/src/index.d.ts +14 -0
- package/dist/esm/src/index.d.ts.map +1 -0
- package/dist/esm/src/pagination.d.ts +27 -0
- package/dist/esm/src/pagination.d.ts.map +1 -0
- package/dist/esm/src/popover.d.ts +2 -0
- package/dist/esm/src/popover.d.ts.map +1 -0
- package/dist/esm/src/suggestion.d.ts +11 -0
- package/dist/esm/src/suggestion.d.ts.map +1 -0
- package/dist/esm/src/tabs.d.ts +18 -0
- package/dist/esm/src/tabs.d.ts.map +1 -0
- package/dist/esm/src/toggle-group.d.ts +2 -0
- package/dist/esm/src/toggle-group.d.ts.map +1 -0
- package/dist/esm/src/tooltip.d.ts +2 -0
- package/dist/esm/src/tooltip.d.ts.map +1 -0
- package/dist/esm/src/utils.d.ts +78 -0
- package/dist/esm/src/utils.d.ts.map +1 -0
- package/dist/esm/suggestion.js +23 -0
- package/dist/esm/suggestion.js.map +1 -0
- package/dist/esm/tabs.js +16 -0
- package/dist/esm/tabs.js.map +1 -0
- package/dist/esm/toggle-group.js +29 -0
- package/dist/esm/toggle-group.js.map +1 -0
- package/dist/esm/tooltip.js +55 -0
- package/dist/esm/tooltip.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -0
- package/dist/esm/utils.js +145 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
> ⚠️ **WARNING** ⚠️
|
|
2
|
+
> The web components created here are **not** meant to be used in production.
|
|
3
|
+
> This project only serves as a playground for us to test using web components for some of our react components.
|
|
4
|
+
>
|
|
5
|
+
> If you are here because you don't want to use React, you should take a look at only using our CSS package: [@digdir/designsystemet-css](../css/README.md).
|
|
6
|
+
|
|
7
|
+
## Web Components
|
|
8
|
+
|
|
9
|
+
Most of our components are plain HTML elements, and these don't need any JavaScript to work.
|
|
10
|
+
However, some components need a bit of JavaScript to work properly.
|
|
11
|
+
These components include:
|
|
12
|
+
- Floating components (e.g. Popover, Tooltip, Dropdown)
|
|
13
|
+
- Field
|
|
14
|
+
- Components that need to manage focus or keyboard interaction (e.g. Tabs, ToggleGroup)
|
|
15
|
+
- Components that need JavaScript to be managed
|
|
16
|
+
|
|
17
|
+
## Current implementation
|
|
18
|
+
We have implemented this as simple as possible. No framework, but pure JS.
|
|
19
|
+
Rollup bundles it, and adds all dependencies (floating-ui) to the bundle, making for a single JS file.
|
|
20
|
+
|
|
21
|
+
Run `pnpm dev` to build and start a local server. It does not have hot reloading.
|
|
@@ -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 {\r\n attr,\r\n attrRequiredWarning,\r\n customElements,\r\n DSElement,\r\n debounce,\r\n on,\r\n onMutation,\r\n} from './utils';\r\n\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n 'ds-breadcrumbs': DSBreadcrumbsElement;\r\n }\r\n}\r\n\r\nconst ATTR_LABEL = 'aria-label';\r\nconst ATTR_LABEL_HIDDEN = 'data-label'; // Used to hide label on mobile when only showing back link\r\n\r\nexport class DSBreadcrumbsElement extends DSElement {\r\n _items?: HTMLCollectionOf<HTMLAnchorElement>; // Using underscore instead of private fields for backwards compatibility\r\n _unresize?: () => void;\r\n _unmutate?: () => void;\r\n\r\n static get observedAttributes() {\r\n return [ATTR_LABEL]; // Using ES2015 syntax for backwards compatibility\r\n }\r\n connectedCallback() {\r\n if (!attr(this, ATTR_LABEL_HIDDEN)) attrRequiredWarning(this, ATTR_LABEL); // aria-label can allready have been hidden by attributeChangedCallback\r\n const render = debounce(this.attributeChangedCallback.bind(this), 100);\r\n this._items = this.getElementsByTagName('a'); // Speed up by caching HTMLCollection\r\n this._unresize = on(window, 'resize', render);\r\n this._unmutate = onMutation(this, render, {\r\n debounce: 0, // No debounce, as we already debounce render\r\n childList: true,\r\n subtree: true,\r\n });\r\n }\r\n attributeChangedCallback() {\r\n const last = this._items?.[this._items.length - 1];\r\n const lastInList = last?.parentElement === this ? null : last;\r\n const isListHidden = !lastInList?.offsetHeight;\r\n const labelHidden = attr(this, ATTR_LABEL_HIDDEN);\r\n const label = attr(this, ATTR_LABEL);\r\n\r\n // Only labels if needed to prevent infinite attribute update loop\r\n attr(this, 'role', isListHidden ? null : 'navigation');\r\n if (isListHidden && !labelHidden && label) {\r\n attr(this, ATTR_LABEL_HIDDEN, label);\r\n attr(this, ATTR_LABEL, null);\r\n } else if (!isListHidden && labelHidden && !label) {\r\n attr(this, ATTR_LABEL, labelHidden);\r\n attr(this, ATTR_LABEL_HIDDEN, null);\r\n }\r\n\r\n for (const item of this._items || [])\r\n attr(item, 'aria-current', item === lastInList ? 'page' : null);\r\n }\r\n disconnectedCallback() {\r\n this._unresize?.();\r\n this._unmutate?.();\r\n this._unresize = this._unmutate = this._items = undefined;\r\n }\r\n}\r\n\r\ncustomElements.define('ds-breadcrumbs', DSBreadcrumbsElement);\r\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\r\n// https://open-ui.org/components/link-area-delegation-explainer/\r\n// and https://github.com/openui/open-ui/issues/1104#issuecomment-3151387080\r\nimport { on, onHotReload, QUICK_EVENT } from './utils';\r\n\r\nconst CLASS_HOVER = ':click-delegate-hover';\r\nconst ATTR_CLICKDELEGATEFOR = 'data-clickdelegatefor';\r\nconst SELECTOR_CLICKDELEGATEFOR = `[${ATTR_CLICKDELEGATEFOR}]`;\r\nconst SELECTOR_SKIP =\r\n 'a,button,label,input,select,textarea,dialog,[role=\"button\"],[popover],[contenteditable]';\r\n\r\nexport const handleClickDelegateFor = (event: MouseEvent) => {\r\n const isNewTab = event.button === 1 || event.metaKey || event.ctrlKey;\r\n const isUserLeftOrMiddleClick = event.isTrusted && event.button < 2;\r\n const delegateTarget = isUserLeftOrMiddleClick && getDelegateTarget(event);\r\n\r\n if (delegateTarget instanceof HTMLAnchorElement && isNewTab)\r\n window.open(delegateTarget.href, undefined, delegateTarget.rel); // If middle click or cmd/ctrl click on link, open in new tab\r\n else if (\r\n delegateTarget instanceof HTMLElement &&\r\n !delegateTarget.contains(event.target as Node) // Only proxy event if delegated target isn't the original target\r\n ) {\r\n event.stopImmediatePropagation(); // We'll trigger a new click event anyway, so prevent actions on this one\r\n delegateTarget.click(); // Forward click to the clickable element\r\n }\r\n};\r\n\r\nlet HOVER: Element | undefined;\r\nconst handleMouseOver = (event: Event) => {\r\n const delegateTarget = getDelegateTarget(event);\r\n if (HOVER === delegateTarget) return; // No change\r\n if (HOVER) HOVER.classList.remove(CLASS_HOVER);\r\n if (delegateTarget) delegateTarget.classList.add(CLASS_HOVER);\r\n HOVER = delegateTarget;\r\n};\r\n\r\nconst getDelegateTarget = ({ target: el }: Event) => {\r\n const scope =\r\n el instanceof Element ? el.closest(SELECTOR_CLICKDELEGATEFOR) : null;\r\n const id = scope?.getAttribute(ATTR_CLICKDELEGATEFOR);\r\n const target = document.getElementById(id || '');\r\n const skip = target && (el as Element).closest(SELECTOR_SKIP); // Ignore if interactive\r\n\r\n return ((!skip || skip === target) && target) || undefined;\r\n};\r\n\r\nonHotReload('clickdelegatefor', () => [\r\n on(window, 'click auxclick', handleClickDelegateFor as EventListener, true), // Use capture to ensure we run before other click listeners\r\n on(document, 'mouseover', handleMouseOver, QUICK_EVENT), // Use passive for better performance\r\n]);\r\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';\r\n\r\n// Polyfill for Android Firefox not supporting details/summary accessibility properly\r\nconst IS_ANDROID_FIREFOX =\r\n isBrowser() &&\r\n /android/i.test(navigator.userAgent) &&\r\n /firefox/i.test(navigator.userAgent);\r\nconst SUMMARYS = IS_ANDROID_FIREFOX\r\n ? document.getElementsByTagName('summary')\r\n : [];\r\n\r\nfunction handleAriaPolyfill() {\r\n for (const summary of SUMMARYS) {\r\n const open = !!(summary.parentElement as HTMLDetailsElement)?.open;\r\n attr(summary, 'role', 'button');\r\n attr(summary, 'aria-expanded', `${open}`);\r\n }\r\n}\r\n\r\nif (IS_ANDROID_FIREFOX)\r\n onHotReload('details-android-firefox', () => [\r\n onMutation(document, handleAriaPolyfill, {\r\n attributeFilter: ['open'],\r\n attributes: true,\r\n childList: true,\r\n subtree: true,\r\n }),\r\n ]);\r\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';\r\n\r\n// Polyfill closedby functionaliy in Safari\r\n// Also in Safari 26.2 where `closedBy` property is supported natively,\r\n// but no corresponding functionality/behavior is implemented.\r\nlet DOWN_INSIDE = false; // Prevent close if selecting text inside dialog\r\nfunction handleClosedbyAny({\r\n type,\r\n target: el,\r\n clientX: x = 0,\r\n clientY: y = 0,\r\n}: Partial<MouseEvent>) {\r\n if (type === 'pointerdown') {\r\n const r = (el as Element)?.closest?.('dialog')?.getBoundingClientRect();\r\n const isInside =\r\n r && r.top <= y && y <= r.bottom && r.left <= x && x <= r.right;\r\n\r\n DOWN_INSIDE = !!isInside;\r\n } else {\r\n const isDialog = el instanceof HTMLDialogElement;\r\n const isClose = isDialog && !DOWN_INSIDE && attr(el, 'closedby') === 'any';\r\n\r\n DOWN_INSIDE = false; // Reset on every pointerup\r\n if (isClose) requestAnimationFrame(() => el.open && el.close()); // Close if browser did not do it\r\n }\r\n}\r\n\r\nonHotReload('dialog-closedby', () => [\r\n on(document, 'pointerdown pointerup', handleClosedbyAny, QUICK_EVENT),\r\n]);\r\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';\r\n\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n 'ds-error-summary': DSErrorSummaryElement;\r\n }\r\n}\r\n\r\nexport class DSErrorSummaryElement extends DSElement {\r\n connectedCallback() {\r\n on(this, 'animationend', this, QUICK_EVENT); // Using animationend to detect when element is visible\r\n requestAnimationFrame(() => this.handleEvent({ target: this })); // Initial setup when children has rendered\r\n }\r\n handleEvent({ target }: Partial<Event>) {\r\n if (target !== this) return; // Ignore if animation event was triggered by child\r\n const heading = this.querySelector('h2,h3,h4,h5,h6');\r\n if (heading) attr(heading, 'aria-labelledby', useId(heading));\r\n attr(this, 'tabindex', '-1');\r\n this.focus();\r\n }\r\n disconnectedCallback() {\r\n off(this, 'animationend', this, QUICK_EVENT);\r\n }\r\n}\r\n\r\ncustomElements.define('ds-error-summary', DSErrorSummaryElement);\r\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 for mye",
|
|
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 {\r\n attr,\r\n customElements,\r\n DSElement,\r\n debounce,\r\n isBrowser,\r\n off,\r\n on,\r\n onHotReload,\r\n onMutation,\r\n QUICK_EVENT,\r\n tag,\r\n useId,\r\n isWindows,\r\n} from './utils';\r\n\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n 'ds-field': DSFieldElement;\r\n }\r\n}\r\n\r\nconst CSS_FIELD_SIZE = '--_ds-field-sizing';\r\nconst ATTR_COUNTER_TEXT = 'data-counter-text';\r\nconst ATTR_COUNTER_ARIA = 'data-counter-aria';\r\nconst ATTR_FIELD = 'data-field';\r\nconst TYPE_DESCRIPTION = 'description';\r\nconst TYPE_VALIDATION = 'validation';\r\nconst COUNTER_DEBOUNCE = isWindows() ? 800 : 200; // Longer debounce on Windows due to NVDA performance\r\nconst COUNTER_TEXT = {\r\n over: '%d tegn for mye',\r\n under: '%d tegn for mye',\r\n hint: 'Maks %d tegn tillatt.',\r\n};\r\n\r\nconst SELECTOR_FIELDSET_DESCRIPTION = `:scope > [${ATTR_FIELD}=\"${TYPE_DESCRIPTION}\"],:scope > legend + p`; // legend + p is kept for backwards compatibility\r\nconst SELECTOR_FIELDSET_VALIDATION = `:scope > [${ATTR_FIELD}=\"${TYPE_VALIDATION}\"]`;\r\nconst SELECTOR_FIELD_COUNTER = '[data-field=\"counter\"]';\r\n\r\nconst FIELDS = new Set<DSFieldElement>();\r\nconst FILEDSETS = isBrowser() ? document.getElementsByTagName('fieldset') : [];\r\nconst STYLE_SR_ONLY = `position:absolute;clip:rect(0 0 0 0);overflow:hidden;width:1px;height:1px;white-space:nowrap;pointer-events:none`;\r\n\r\n// TODO: Document that Validation must be hidden with \"hidden\" attribute (or completely removed from DOM), not display: none\r\nconst handleMutations = debounce(() => {\r\n setupFieldsets();\r\n setupFields();\r\n}, 100); // Debounce to avoid excessive calculations on multiple mutations\r\n\r\n// Connect fieldset legend and descriptions\r\nconst setupFieldsets = () => {\r\n for (const fieldset of FILEDSETS) {\r\n const labelledby = [\r\n fieldset.querySelector('legend'),\r\n fieldset.querySelector(SELECTOR_FIELDSET_DESCRIPTION),\r\n ].filter(isNotHidden);\r\n\r\n attr(fieldset, 'aria-labelledby', labelledby.map(useId).join(' '));\r\n }\r\n};\r\n\r\nconst setupFields = () => {\r\n for (const field of FIELDS) {\r\n const descs: Element[] = [];\r\n const labels: HTMLLabelElement[] = [];\r\n let input: HTMLInputElement | undefined;\r\n\r\n for (const el of field.getElementsByTagName('*')) {\r\n if (el instanceof HTMLLabelElement) labels.push(el);\r\n else if (isInputLike(el)) {\r\n if (input)\r\n console.warn(\r\n `Designsystemet: Fields should only have one input element. Use <fieldset> to group multiple fields:`,\r\n field,\r\n );\r\n input = el;\r\n } else if (isNotHidden(el)) {\r\n const type = el.getAttribute(ATTR_FIELD); // Using getAttribute not attr for best performance\r\n if (type === TYPE_VALIDATION) descs.unshift(el);\r\n else if (type) descs.push(el);\r\n }\r\n }\r\n\r\n if (!input)\r\n console.warn(`Designsystemet: Field is missing input element:`, field);\r\n else {\r\n for (const label of labels) attr(label, 'for', useId(input));\r\n\r\n const isBoolish = input.type === 'radio' || input.type === 'checkbox';\r\n const fieldsetValidation = field\r\n .closest('fieldset')\r\n ?.querySelector(SELECTOR_FIELDSET_VALIDATION);\r\n if (isNotHidden(fieldsetValidation)) descs.unshift(fieldsetValidation);\r\n\r\n field.handleEvent({ target: input }); // Run counter and textrarea resize\r\n attr(field, 'data-clickdelegatefor', isBoolish ? useId(input) : null); // Expand click area to ds-field if radio/checkbox\r\n attr(input, 'aria-describedby', descs.map(useId).join(' '));\r\n attr(input, 'aria-invalid', `${descs.some(isInvalid)}`);\r\n }\r\n }\r\n};\r\n\r\nconst getCounterText = (el: Element, key: keyof typeof COUNTER_TEXT, num: number) =>\r\n (attr(el, `data-${key}`) || COUNTER_TEXT[key]).replace('%d', `${Math.abs(num)}`);\r\n\r\nconst setupCounter = (field: DSFieldElement, target: EventTarget | null) => {\r\n const el =\r\n isInputLike(target) &&\r\n field.querySelector<HTMLElement>(SELECTOR_FIELD_COUNTER);\r\n\r\n if (el) {\r\n const live = field.shadowRoot?.lastElementChild as HTMLElement;\r\n const limit = Number(attr(el, 'data-limit')) || 0;\r\n const count = limit - target.value.length;\r\n const text = getCounterText(el, count < 0 ? 'over' : 'under', count);\r\n\r\n attr(el, ATTR_COUNTER_TEXT, text);\r\n attr(el, ATTR_COUNTER_ARIA, getCounterText(el, 'hint', limit));\r\n attr(el, 'data-color', count < 0 ? 'danger' : null);\r\n setupCounterLiveRegion(live, text); // Debounce live region to avoid NVDA interupting announcing typed text\r\n }\r\n};\r\n\r\nconst setupCounterLiveRegion = debounce((live: Element, text: string) => {\r\n live.textContent = text;\r\n}, COUNTER_DEBOUNCE);\r\n\r\n// iOS does not support field-sizing: content, so we need to manually resize\r\nconst setupTextareaFieldSizingiOS = (target: EventTarget | null) => {\r\n if (target instanceof HTMLTextAreaElement) {\r\n target.style.setProperty(CSS_FIELD_SIZE, 'auto');\r\n target.style.setProperty(CSS_FIELD_SIZE, `${target.scrollHeight}px`);\r\n }\r\n};\r\n\r\nconst isInputLike = (el: unknown): el is HTMLInputElement =>\r\n el instanceof HTMLElement &&\r\n 'validity' in el && // Adds support for custom elements implemeted with attachInternals()\r\n !(el instanceof HTMLButtonElement); // But skip <button> elements\r\n\r\nconst isNotHidden = (el?: Element | null): el is Element =>\r\n !!el && !(el as HTMLElement).hidden;\r\n\r\nconst isInvalid = (el: Element): boolean =>\r\n el.getAttribute(ATTR_FIELD) === TYPE_VALIDATION &&\r\n attr(el, 'data-color') !== 'success';\r\n\r\n// Custom element is used to performantly keep track of fields on the page\r\nexport class DSFieldElement extends DSElement {\r\n constructor() {\r\n super();\r\n this.attachShadow({ mode: 'open' }).append(\r\n tag('slot'),\r\n tag('div', { 'aria-live': 'polite', style: STYLE_SR_ONLY }), // Used to announce counter updates\r\n );\r\n }\r\n connectedCallback() {\r\n FIELDS.add(this);\r\n on(this, 'input', this, QUICK_EVENT);\r\n handleMutations(); // Initial setup\r\n }\r\n handleEvent({ target }: { target: EventTarget | null }) {\r\n setupCounter(this, target);\r\n setupTextareaFieldSizingiOS(target);\r\n }\r\n disconnectedCallback() {\r\n off(this, 'input', this, QUICK_EVENT);\r\n FIELDS.delete(this);\r\n }\r\n}\r\n\r\ncustomElements.define('ds-field', DSFieldElement);\r\n\r\nonHotReload('field', () => [\r\n onMutation(document, handleMutations, {\r\n debounce: false, // No need to timeout debounce here as we handle it ourselves\r\n attributeFilter: ['hidden', ATTR_FIELD], // Listen for hidden to detect hidden validations\r\n attributes: true,\r\n childList: true,\r\n subtree: true,\r\n }),\r\n]);\r\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';\r\n\r\n// Ensure polyfill is loaded in browser environment only\r\nif (isBrowser()) import('invokers-polyfill');\r\n\r\nexport * from '@u-elements/u-datalist'; // Re-export u-datalist since this is a pure polyfill and not custom Designsystemet elements\r\nexport * from './breadcrumbs';\r\nexport * from './error-summary';\r\nexport * from './field';\r\nexport * from './pagination';\r\nexport * from './suggestion';\r\nexport * from './tabs';\r\nimport './clickdelegatefor';\r\nimport './details';\r\nimport './dialog';\r\nimport './popover';\r\nimport './toggle-group';\r\nimport './tooltip';\r\n"],"mappings":";;;;;;;;;;;;;;;AAGA,IAAIA,yBAAW,CAAE,QAAO"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const require_utils = require('./utils.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/pagination.ts
|
|
4
|
+
const ATTR_LABEL = "aria-label";
|
|
5
|
+
const ATTR_CURRENT = "data-current";
|
|
6
|
+
const ATTR_TOTAL = "data-total";
|
|
7
|
+
const ATTR_HREF = "data-href";
|
|
8
|
+
const pagination = ({ current = 1, total = 10, show = 7 }) => ({
|
|
9
|
+
prev: current > 1 ? current - 1 : 0,
|
|
10
|
+
next: current < total ? current + 1 : 0,
|
|
11
|
+
pages: getSteps(current, total, show).map((page, index) => ({
|
|
12
|
+
current: page === current && "page",
|
|
13
|
+
key: `key-${page}-${index}`,
|
|
14
|
+
page
|
|
15
|
+
}))
|
|
16
|
+
});
|
|
17
|
+
var DSPaginationElement = class extends require_utils.DSElement {
|
|
18
|
+
_unmutate;
|
|
19
|
+
static get observedAttributes() {
|
|
20
|
+
return [ATTR_CURRENT, ATTR_TOTAL];
|
|
21
|
+
}
|
|
22
|
+
connectedCallback() {
|
|
23
|
+
require_utils.attrRequiredWarning(this, ATTR_LABEL);
|
|
24
|
+
if (require_utils.attr(this, ATTR_TOTAL)) require_utils.attrRequiredWarning(this, ATTR_CURRENT);
|
|
25
|
+
if (require_utils.attr(this, ATTR_CURRENT)) require_utils.attrRequiredWarning(this, ATTR_TOTAL);
|
|
26
|
+
this._unmutate = require_utils.onMutation(this, this.render.bind(this), {
|
|
27
|
+
childList: true,
|
|
28
|
+
subtree: true,
|
|
29
|
+
debounce: 0
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
disconnectedCallback() {
|
|
33
|
+
this._unmutate?.();
|
|
34
|
+
this._unmutate = void 0;
|
|
35
|
+
}
|
|
36
|
+
render() {
|
|
37
|
+
const items = this.querySelectorAll("button,a");
|
|
38
|
+
const href = require_utils.attr(this, ATTR_HREF);
|
|
39
|
+
const CURRENT = require_utils.attr(this, ATTR_CURRENT) || void 0;
|
|
40
|
+
const TOTAL = require_utils.attr(this, ATTR_TOTAL) || void 0;
|
|
41
|
+
if (!CURRENT || !TOTAL) return;
|
|
42
|
+
const { next, prev, pages } = pagination({
|
|
43
|
+
current: parseInt(CURRENT, 10),
|
|
44
|
+
total: parseInt(TOTAL, 10),
|
|
45
|
+
show: items.length - 2
|
|
46
|
+
});
|
|
47
|
+
items.forEach((item, i) => {
|
|
48
|
+
const page = i ? items[i + 1] ? pages[i - 1]?.page : next : prev;
|
|
49
|
+
require_utils.attr(item, "aria-current", pages[i - 1]?.current ? "true" : null);
|
|
50
|
+
require_utils.attr(item, "aria-hidden", page ? null : "true");
|
|
51
|
+
require_utils.attr(item, "data-page", `${page}`);
|
|
52
|
+
require_utils.attr(item, "tabindex", page ? null : "-1");
|
|
53
|
+
if (item instanceof HTMLButtonElement) require_utils.attr(item, "value", `${page}`);
|
|
54
|
+
if (href) require_utils.attr(item, "href", href.replace("$page", `${page}`));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
require_utils.customElements.define("ds-pagination", DSPaginationElement);
|
|
59
|
+
function getSteps(now, max, show = Number.POSITIVE_INFINITY) {
|
|
60
|
+
const offset = (show - 1) / 2;
|
|
61
|
+
const start = Math.max(Math.min(now - Math.floor(offset), max - show + 1), 1);
|
|
62
|
+
const end = Math.min(Math.max(now + Math.ceil(offset), show), max);
|
|
63
|
+
const pages = Array.from({ length: end + 1 - start }, (_, i) => i + start);
|
|
64
|
+
if (show > 4 && start > 1) pages.splice(0, 2, 1, 0);
|
|
65
|
+
if (show > 3 && end < max) pages.splice(-2, 2, 0, max);
|
|
66
|
+
return pages;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
exports.DSPaginationElement = DSPaginationElement;
|
|
71
|
+
exports.pagination = pagination;
|
|
72
|
+
//# sourceMappingURL=pagination.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.cjs","names":["DSElement","attr","onMutation","customElements"],"sources":["../../src/pagination.ts"],"sourcesContent":["import {\r\n attr,\r\n attrRequiredWarning,\r\n customElements,\r\n DSElement,\r\n onMutation,\r\n} from './utils';\r\n\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n 'ds-pagination': DSPaginationElement;\r\n }\r\n}\r\n\r\nconst ATTR_LABEL = 'aria-label';\r\nconst ATTR_CURRENT = 'data-current';\r\nconst ATTR_TOTAL = 'data-total';\r\nconst ATTR_HREF = 'data-href';\r\n\r\n// Expose pagination logic if wanting to do custom rendering (i.e. in React/Vue/etc)\r\nexport const pagination = ({ current = 1, total = 10, show = 7 }) => ({\r\n prev: current > 1 ? current - 1 : 0,\r\n next: current < total ? current + 1 : 0,\r\n pages: getSteps(current, total, show).map((page, index) => ({\r\n current: page === current && ('page' as const),\r\n key: `key-${page}-${index}`,\r\n page,\r\n })),\r\n});\r\n\r\nexport class DSPaginationElement extends DSElement {\r\n _unmutate?: () => void;\r\n\r\n static get observedAttributes() {\r\n return [ATTR_CURRENT, ATTR_TOTAL]; // Using ES2015 syntax for backwards compatibility\r\n }\r\n connectedCallback() {\r\n attrRequiredWarning(this, ATTR_LABEL);\r\n if (attr(this, ATTR_TOTAL)) attrRequiredWarning(this, ATTR_CURRENT);\r\n if (attr(this, ATTR_CURRENT)) attrRequiredWarning(this, ATTR_TOTAL);\r\n\r\n this._unmutate = onMutation(this, this.render.bind(this), {\r\n childList: true,\r\n subtree: true,\r\n debounce: 0,\r\n });\r\n }\r\n disconnectedCallback() {\r\n this._unmutate?.();\r\n this._unmutate = undefined;\r\n }\r\n render() {\r\n const items = this.querySelectorAll('button,a');\r\n const href = attr(this, ATTR_HREF);\r\n const CURRENT = attr(this, ATTR_CURRENT) || undefined;\r\n const TOTAL = attr(this, ATTR_TOTAL) || undefined;\r\n\r\n if (!CURRENT || !TOTAL) return;\r\n\r\n const { next, prev, pages } = pagination({\r\n current: parseInt(CURRENT, 10),\r\n total: parseInt(TOTAL, 10),\r\n show: items.length - 2,\r\n });\r\n\r\n items.forEach((item, i) => {\r\n const page = i ? (items[i + 1] ? pages[i - 1]?.page : next) : prev; // First is prev, last is next\r\n attr(item, 'aria-current', pages[i - 1]?.current ? 'true' : null);\r\n attr(item, 'aria-hidden', page ? null : 'true');\r\n attr(item, 'data-page', `${page}`);\r\n attr(item, 'tabindex', page ? null : '-1');\r\n if (item instanceof HTMLButtonElement) attr(item, 'value', `${page}`);\r\n if (href) attr(item, 'href', href.replace('$page', `${page}`));\r\n });\r\n }\r\n}\r\n\r\ncustomElements.define('ds-pagination', DSPaginationElement);\r\n\r\nfunction getSteps(now: number, max: number, show = Number.POSITIVE_INFINITY) {\r\n const offset = (show - 1) / 2;\r\n const start = Math.max(Math.min(now - Math.floor(offset), max - show + 1), 1);\r\n const end = Math.min(Math.max(now + Math.ceil(offset), show), max);\r\n const pages = Array.from({ length: end + 1 - start }, (_, i) => i + start);\r\n\r\n if (show > 4 && start > 1) pages.splice(0, 2, 1, 0);\r\n if (show > 3 && end < max) pages.splice(-2, 2, 0, max);\r\n return pages;\r\n}\r\n"],"mappings":";;;AAcA,MAAM,aAAa;AACnB,MAAM,eAAe;AACrB,MAAM,aAAa;AACnB,MAAM,YAAY;AAGlB,MAAa,cAAc,EAAE,UAAU,GAAG,QAAQ,IAAI,OAAO,SAAS;CACpE,MAAM,UAAU,IAAI,UAAU,IAAI;CAClC,MAAM,UAAU,QAAQ,UAAU,IAAI;CACtC,OAAO,SAAS,SAAS,OAAO,KAAK,CAAC,KAAK,MAAM,WAAW;EAC1D,SAAS,SAAS,WAAY;EAC9B,KAAK,OAAO,KAAK,GAAG;EACpB;EACD,EAAE;CACJ;AAED,IAAa,sBAAb,cAAyCA,wBAAU;CACjD;CAEA,WAAW,qBAAqB;AAC9B,SAAO,CAAC,cAAc,WAAW;;CAEnC,oBAAoB;AAClB,oCAAoB,MAAM,WAAW;AACrC,MAAIC,mBAAK,MAAM,WAAW,CAAE,mCAAoB,MAAM,aAAa;AACnE,MAAIA,mBAAK,MAAM,aAAa,CAAE,mCAAoB,MAAM,WAAW;AAEnE,OAAK,YAAYC,yBAAW,MAAM,KAAK,OAAO,KAAK,KAAK,EAAE;GACxD,WAAW;GACX,SAAS;GACT,UAAU;GACX,CAAC;;CAEJ,uBAAuB;AACrB,OAAK,aAAa;AAClB,OAAK,YAAY;;CAEnB,SAAS;EACP,MAAM,QAAQ,KAAK,iBAAiB,WAAW;EAC/C,MAAM,OAAOD,mBAAK,MAAM,UAAU;EAClC,MAAM,UAAUA,mBAAK,MAAM,aAAa,IAAI;EAC5C,MAAM,QAAQA,mBAAK,MAAM,WAAW,IAAI;AAExC,MAAI,CAAC,WAAW,CAAC,MAAO;EAExB,MAAM,EAAE,MAAM,MAAM,UAAU,WAAW;GACvC,SAAS,SAAS,SAAS,GAAG;GAC9B,OAAO,SAAS,OAAO,GAAG;GAC1B,MAAM,MAAM,SAAS;GACtB,CAAC;AAEF,QAAM,SAAS,MAAM,MAAM;GACzB,MAAM,OAAO,IAAK,MAAM,IAAI,KAAK,MAAM,IAAI,IAAI,OAAO,OAAQ;AAC9D,sBAAK,MAAM,gBAAgB,MAAM,IAAI,IAAI,UAAU,SAAS,KAAK;AACjE,sBAAK,MAAM,eAAe,OAAO,OAAO,OAAO;AAC/C,sBAAK,MAAM,aAAa,GAAG,OAAO;AAClC,sBAAK,MAAM,YAAY,OAAO,OAAO,KAAK;AAC1C,OAAI,gBAAgB,kBAAmB,oBAAK,MAAM,SAAS,GAAG,OAAO;AACrE,OAAI,KAAM,oBAAK,MAAM,QAAQ,KAAK,QAAQ,SAAS,GAAG,OAAO,CAAC;IAC9D;;;AAINE,6BAAe,OAAO,iBAAiB,oBAAoB;AAE3D,SAAS,SAAS,KAAa,KAAa,OAAO,OAAO,mBAAmB;CAC3E,MAAM,UAAU,OAAO,KAAK;CAC5B,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,MAAM,KAAK,MAAM,OAAO,EAAE,MAAM,OAAO,EAAE,EAAE,EAAE;CAC7E,MAAM,MAAM,KAAK,IAAI,KAAK,IAAI,MAAM,KAAK,KAAK,OAAO,EAAE,KAAK,EAAE,IAAI;CAClE,MAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,OAAO,GAAG,GAAG,MAAM,IAAI,MAAM;AAE1E,KAAI,OAAO,KAAK,QAAQ,EAAG,OAAM,OAAO,GAAG,GAAG,GAAG,EAAE;AACnD,KAAI,OAAO,KAAK,MAAM,IAAK,OAAM,OAAO,IAAI,GAAG,GAAG,IAAI;AACtD,QAAO"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_utils = require('./utils.cjs');
|
|
3
|
+
let _floating_ui_dom = require("@floating-ui/dom");
|
|
4
|
+
|
|
5
|
+
//#region src/popover.ts
|
|
6
|
+
const ATTR_PLACEMENT = "data-placement";
|
|
7
|
+
const ATTR_FLOATING = "data-floating";
|
|
8
|
+
const ATTR_AUTOPLACEMENT = "data-autoplacement";
|
|
9
|
+
const CSS_FLOATING = "--_ds-floating";
|
|
10
|
+
const CSS_FLOATING_ARROW_X = "--_ds-floating-arrow-x";
|
|
11
|
+
const CSS_FLOATING_ARROW_Y = "--_ds-floating-arrow-y";
|
|
12
|
+
const CSS_FLOATING_OVERSCROLL = "--_ds-floating-overscroll";
|
|
13
|
+
const POPOVERS = /* @__PURE__ */ new Map();
|
|
14
|
+
function handleToggle(event) {
|
|
15
|
+
const { newState, target, source = event.detail } = event;
|
|
16
|
+
if (!isDSFloating(target)) return;
|
|
17
|
+
if (newState === "closed") return POPOVERS.get(target)?.();
|
|
18
|
+
if (!source || source === target) return;
|
|
19
|
+
const padding = 10;
|
|
20
|
+
const overscroll = getCSSProp(target, CSS_FLOATING_OVERSCROLL);
|
|
21
|
+
const placement = require_utils.attr(target, ATTR_PLACEMENT) || require_utils.attr(source, ATTR_PLACEMENT) || getCSSProp(target, CSS_FLOATING);
|
|
22
|
+
const shiftOffset = placement.match(/left|right/gi) ? source.offsetHeight : source.offsetWidth;
|
|
23
|
+
const autoPlacement = require_utils.attr(target, ATTR_AUTOPLACEMENT) || require_utils.attr(source, ATTR_AUTOPLACEMENT);
|
|
24
|
+
const options = {
|
|
25
|
+
strategy: "absolute",
|
|
26
|
+
placement,
|
|
27
|
+
middleware: [
|
|
28
|
+
(0, _floating_ui_dom.offset)(parseFloat(getComputedStyle(target, "::before").height) || 0),
|
|
29
|
+
(0, _floating_ui_dom.shift)({
|
|
30
|
+
padding,
|
|
31
|
+
limiter: (0, _floating_ui_dom.limitShift)({ offset: { mainAxis: shiftOffset } })
|
|
32
|
+
}),
|
|
33
|
+
arrowPseudo(),
|
|
34
|
+
...autoPlacement !== "false" ? [(0, _floating_ui_dom.flip)({
|
|
35
|
+
padding,
|
|
36
|
+
crossAxis: false
|
|
37
|
+
})] : [],
|
|
38
|
+
...overscroll ? [(0, _floating_ui_dom.size)({ apply({ availableHeight }) {
|
|
39
|
+
if (overscroll === "fit") target.style.width = `${source.clientWidth}px`;
|
|
40
|
+
target.style.maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;
|
|
41
|
+
} })] : []
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
const unfloat = (0, _floating_ui_dom.autoUpdate)(source, target, async () => {
|
|
45
|
+
if (!source?.isConnected) return POPOVERS.get(target)?.();
|
|
46
|
+
const { x, y } = await (0, _floating_ui_dom.computePosition)(source, target, options);
|
|
47
|
+
target.style.translate = `${x}px ${y}px`;
|
|
48
|
+
});
|
|
49
|
+
POPOVERS.set(target, () => POPOVERS.delete(target) && unfloat());
|
|
50
|
+
}
|
|
51
|
+
function handleBeforeToggle({ target: el, newState }) {
|
|
52
|
+
if (newState === "open" && isDSFloating(el)) require_utils.attr(el, "popover", "manual");
|
|
53
|
+
}
|
|
54
|
+
function handleClickOutside({ target: el }) {
|
|
55
|
+
for (const [popover] of POPOVERS) if (!popover.contains(el)) {
|
|
56
|
+
const id = popover.id;
|
|
57
|
+
const trigger = `[popovertarget="${id}"],[commandfor="${id}"]`;
|
|
58
|
+
if (!el?.closest?.(trigger)) popover.hidePopover();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function handleKeydown(event) {
|
|
62
|
+
const last = event.key === "Escape" && Array.from(POPOVERS.keys()).pop();
|
|
63
|
+
if (last) last.hidePopover();
|
|
64
|
+
if (last) event.preventDefault?.();
|
|
65
|
+
}
|
|
66
|
+
require_utils.onHotReload("popover", () => [
|
|
67
|
+
require_utils.on(document, "beforetoggle", handleBeforeToggle, require_utils.QUICK_EVENT),
|
|
68
|
+
require_utils.on(document, "click", handleClickOutside, require_utils.QUICK_EVENT),
|
|
69
|
+
require_utils.on(document, "keydown", handleKeydown),
|
|
70
|
+
require_utils.on(document, "toggle ds-toggle-source", handleToggle, require_utils.QUICK_EVENT)
|
|
71
|
+
]);
|
|
72
|
+
const getCSSProp = (el, prop) => getComputedStyle(el).getPropertyValue(prop).trim();
|
|
73
|
+
const isDSFloating = (el) => el instanceof HTMLElement && !!getCSSProp(el, CSS_FLOATING);
|
|
74
|
+
const arrowPseudo = () => ({
|
|
75
|
+
name: "arrowPseudo",
|
|
76
|
+
fn(data) {
|
|
77
|
+
const target = data.elements.floating;
|
|
78
|
+
const source = data.rects.reference;
|
|
79
|
+
const x = `${Math.round(source.width / 2 + source.x - data.x)}px`;
|
|
80
|
+
const y = `${Math.round(source.height / 2 + source.y - data.y)}px`;
|
|
81
|
+
target.style.setProperty(CSS_FLOATING_ARROW_X, x);
|
|
82
|
+
target.style.setProperty(CSS_FLOATING_ARROW_Y, y);
|
|
83
|
+
require_utils.attr(target, ATTR_FLOATING, data.placement);
|
|
84
|
+
return data;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
//# sourceMappingURL=popover.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"popover.cjs","names":["attr","onHotReload","on","QUICK_EVENT"],"sources":["../../src/popover.ts"],"sourcesContent":["import type { ComputePositionConfig, MiddlewareState } from '@floating-ui/dom';\r\nimport {\r\n autoUpdate,\r\n computePosition,\r\n limitShift,\r\n flip,\r\n offset,\r\n shift,\r\n size,\r\n} from '@floating-ui/dom';\r\nimport { attr, on, onHotReload, QUICK_EVENT } from './utils';\r\n\r\nconst ATTR_PLACEMENT = 'data-placement';\r\nconst ATTR_FLOATING = 'data-floating';\r\nconst ATTR_AUTOPLACEMENT = 'data-autoplacement';\r\nconst CSS_FLOATING = '--_ds-floating';\r\nconst CSS_FLOATING_ARROW_X = '--_ds-floating-arrow-x';\r\nconst CSS_FLOATING_ARROW_Y = '--_ds-floating-arrow-y';\r\nconst CSS_FLOATING_OVERSCROLL = '--_ds-floating-overscroll';\r\nconst POPOVERS = new Map<HTMLElement, () => void>();\r\n\r\n// Sometimes use \"ds-toggle\" event while waiting for better support of\r\n// event.source (https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent/source)\r\ntype DSToggleEvent = Partial<ToggleEvent> & {\r\n detail?: HTMLElement;\r\n source?: HTMLElement;\r\n};\r\n\r\nfunction handleToggle(event: DSToggleEvent) {\r\n const { newState, target, source = event.detail } = event;\r\n\r\n if (!isDSFloating(target)) return;\r\n if (newState === 'closed') return POPOVERS.get(target)?.(); // Cleanup on close\r\n if (!source || source === target) return; // No need to update\r\n const padding = 10;\r\n const overscroll = getCSSProp(target, CSS_FLOATING_OVERSCROLL);\r\n const placement = attr(target, ATTR_PLACEMENT) || attr(source, ATTR_PLACEMENT) || getCSSProp(target, CSS_FLOATING);\r\n const shiftOffset = placement.match(/left|right/gi) ? source.offsetHeight : source.offsetWidth;\r\n const autoPlacement = attr(target, ATTR_AUTOPLACEMENT) || attr(source, ATTR_AUTOPLACEMENT);\r\n const options = {\r\n strategy: 'absolute',\r\n placement,\r\n middleware: [\r\n offset(parseFloat(getComputedStyle(target, '::before').height) || 0),\r\n shift({\r\n padding,\r\n limiter: limitShift({ offset: { mainAxis: shiftOffset } }) // Prevent from shifing away from source\r\n }),\r\n arrowPseudo(),\r\n ...(autoPlacement !== 'false' ? [flip({ padding, crossAxis: false })] : []),\r\n ...(overscroll\r\n ? [\r\n size({\r\n apply({ availableHeight }) {\r\n if (overscroll === 'fit')\r\n target.style.width = `${source.clientWidth}px`;\r\n target.style.maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;\r\n },\r\n }),\r\n ]\r\n : []),\r\n ],\r\n } as ComputePositionConfig;\r\n const unfloat = autoUpdate(source, target, async () => {\r\n if (!source?.isConnected) return POPOVERS.get(target)?.(); // Cleanup if source element is removed\r\n const { x, y } = await computePosition(source, target, options);\r\n target.style.translate = `${x}px ${y}px`;\r\n });\r\n POPOVERS.set(target, () => POPOVERS.delete(target) && unfloat());\r\n}\r\n\r\nfunction handleBeforeToggle({ target: el, newState }: Partial<ToggleEvent>) {\r\n if (newState === 'open' && isDSFloating(el)) attr(el, 'popover', 'manual'); // Make manual to prevent closing when clicking scrollbar\r\n}\r\n\r\n// Since we use manual popover, we also manually need to close on outside click\r\nfunction handleClickOutside({ target: el }: Event) {\r\n for (const [popover] of POPOVERS)\r\n if (!popover.contains(el as Node)) {\r\n const id = popover.id;\r\n const trigger = `[popovertarget=\"${id}\"],[commandfor=\"${id}\"]`;\r\n if (!(el as Element)?.closest?.(trigger)) popover.hidePopover();\r\n }\r\n}\r\n\r\nfunction handleKeydown(event: Partial<KeyboardEvent>) {\r\n const last = event.key === 'Escape' && Array.from(POPOVERS.keys()).pop();\r\n if (last) last.hidePopover();\r\n if (last) event.preventDefault?.(); // Prevent minimize fullscreen Safari\r\n}\r\n\r\nonHotReload('popover', () => [\r\n on(document, 'beforetoggle', handleBeforeToggle, QUICK_EVENT), // Use capture since toggle does not bubble\r\n on(document, 'click', handleClickOutside, QUICK_EVENT), // Close open popovers on outside click\r\n on(document, 'keydown', handleKeydown),\r\n on(document, 'toggle ds-toggle-source', handleToggle, QUICK_EVENT), // Use capture since the toggle event does not bubble\r\n]);\r\n\r\nconst getCSSProp = (el: Element, prop: string) =>\r\n getComputedStyle(el).getPropertyValue(prop).trim();\r\n\r\nconst isDSFloating = (el?: EventTarget | null): el is HTMLElement =>\r\n el instanceof HTMLElement && !!getCSSProp(el, CSS_FLOATING);\r\n\r\nconst arrowPseudo = () => ({\r\n name: 'arrowPseudo',\r\n fn(data: MiddlewareState) {\r\n const target = data.elements.floating;\r\n const source = data.rects.reference;\r\n const x = `${Math.round(source.width / 2 + source.x - data.x)}px`;\r\n const y = `${Math.round(source.height / 2 + source.y - data.y)}px`;\r\n\r\n target.style.setProperty(CSS_FLOATING_ARROW_X, x);\r\n target.style.setProperty(CSS_FLOATING_ARROW_Y, y);\r\n attr(target, ATTR_FLOATING, data.placement);\r\n return data;\r\n },\r\n});\r\n"],"mappings":"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"}
|