@digdir/designsystemet-web 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +13 -15
  2. package/dist/cjs/_vendors/@oddbird/popover-polyfill/dist/popover-fn.cjs +4 -4
  3. package/dist/cjs/_vendors/@oddbird/popover-polyfill/dist/popover-fn.cjs.map +1 -1
  4. package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -1
  5. package/dist/cjs/breadcrumbs/breadcrumbs.cjs +1 -1
  6. package/dist/cjs/breadcrumbs/breadcrumbs.cjs.map +1 -1
  7. package/dist/cjs/clickdelegatefor/clickdelegatefor.cjs +1 -1
  8. package/dist/cjs/clickdelegatefor/clickdelegatefor.cjs.map +1 -1
  9. package/dist/cjs/dialog/dialog.cjs +1 -1
  10. package/dist/cjs/dialog/dialog.cjs.map +1 -1
  11. package/dist/cjs/error-summary/error-summary.cjs +1 -2
  12. package/dist/cjs/error-summary/error-summary.cjs.map +1 -1
  13. package/dist/cjs/field/field.cjs +1 -1
  14. package/dist/cjs/field/field.cjs.map +1 -1
  15. package/dist/cjs/fieldset/fieldset.cjs +1 -1
  16. package/dist/cjs/fieldset/fieldset.cjs.map +1 -1
  17. package/dist/cjs/focusgroup/focusgroup.cjs +2 -0
  18. package/dist/cjs/focusgroup/focusgroup.cjs.map +1 -0
  19. package/dist/cjs/index.cjs +1 -1
  20. package/dist/cjs/invokers/invokers.cjs.map +1 -1
  21. package/dist/cjs/packages/web/package.cjs +2 -0
  22. package/dist/cjs/packages/web/package.cjs.map +1 -0
  23. package/dist/cjs/pagination/pagination.cjs +1 -1
  24. package/dist/cjs/pagination/pagination.cjs.map +1 -1
  25. package/dist/cjs/popover/popover.cjs +1 -1
  26. package/dist/cjs/popover/popover.cjs.map +1 -1
  27. package/dist/cjs/readonly/readonly.cjs +1 -1
  28. package/dist/cjs/readonly/readonly.cjs.map +1 -1
  29. package/dist/cjs/suggestion/suggestion.cjs +1 -1
  30. package/dist/cjs/suggestion/suggestion.cjs.map +1 -1
  31. package/dist/cjs/toggle-group/toggle-group.cjs +1 -1
  32. package/dist/cjs/toggle-group/toggle-group.cjs.map +1 -1
  33. package/dist/cjs/tooltip/tooltip.cjs +1 -1
  34. package/dist/cjs/tooltip/tooltip.cjs.map +1 -1
  35. package/dist/cjs/utils/utils.cjs +1 -1
  36. package/dist/cjs/utils/utils.cjs.map +1 -1
  37. package/dist/custom-elements.json +189 -4
  38. package/dist/esm/_vendors/@oddbird/popover-polyfill/dist/popover-fn.js +4 -4
  39. package/dist/esm/_vendors/@oddbird/popover-polyfill/dist/popover-fn.js.map +1 -1
  40. package/dist/esm/breadcrumbs/breadcrumbs.js +1 -1
  41. package/dist/esm/breadcrumbs/breadcrumbs.js.map +1 -1
  42. package/dist/esm/clickdelegatefor/clickdelegatefor.js +1 -1
  43. package/dist/esm/clickdelegatefor/clickdelegatefor.js.map +1 -1
  44. package/dist/esm/dialog/dialog.js +1 -1
  45. package/dist/esm/dialog/dialog.js.map +1 -1
  46. package/dist/esm/error-summary/error-summary.js +1 -2
  47. package/dist/esm/error-summary/error-summary.js.map +1 -1
  48. package/dist/esm/field/field.js +1 -1
  49. package/dist/esm/field/field.js.map +1 -1
  50. package/dist/esm/fieldset/fieldset.js +1 -1
  51. package/dist/esm/fieldset/fieldset.js.map +1 -1
  52. package/dist/esm/focusgroup/focusgroup.js +2 -0
  53. package/dist/esm/focusgroup/focusgroup.js.map +1 -0
  54. package/dist/esm/index.js +1 -1
  55. package/dist/esm/invokers/invokers.js.map +1 -1
  56. package/dist/esm/packages/web/package.js +2 -0
  57. package/dist/esm/packages/web/package.js.map +1 -0
  58. package/dist/esm/pagination/pagination.js +1 -1
  59. package/dist/esm/pagination/pagination.js.map +1 -1
  60. package/dist/esm/popover/popover.js +1 -1
  61. package/dist/esm/popover/popover.js.map +1 -1
  62. package/dist/esm/readonly/readonly.js +1 -1
  63. package/dist/esm/readonly/readonly.js.map +1 -1
  64. package/dist/esm/suggestion/suggestion.js +1 -1
  65. package/dist/esm/suggestion/suggestion.js.map +1 -1
  66. package/dist/esm/toggle-group/toggle-group.js +1 -1
  67. package/dist/esm/toggle-group/toggle-group.js.map +1 -1
  68. package/dist/esm/tooltip/tooltip.js +1 -1
  69. package/dist/esm/tooltip/tooltip.js.map +1 -1
  70. package/dist/esm/utils/utils.js +1 -1
  71. package/dist/esm/utils/utils.js.map +1 -1
  72. package/dist/index.d.ts +18 -22
  73. package/dist/index.js +636 -263
  74. package/dist/umd/index.js +16 -11
  75. package/dist/umd/index.js.map +1 -1
  76. package/package.json +15 -10
  77. package/dist/index.d.ts.map +0 -1
  78. package/dist/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"error-summary.js","names":[],"sources":["../../../src/error-summary/error-summary.ts"],"sourcesContent":["import {\n attr,\n customElements,\n DSElement,\n off,\n on,\n onMutation,\n QUICK_EVENT,\n useId,\n warn,\n} from '../utils/utils';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-error-summary': DSErrorSummaryElement;\n }\n}\n\nexport class DSErrorSummaryElement extends DSElement {\n _unmutate?: () => void; // Using underscore instead of private fields for backwards compatibility\n\n connectedCallback() {\n on(this, 'animationend', this, QUICK_EVENT); // Using animationend to detect when element is visible\n attr(this, 'role', 'group');\n attr(this, 'tabindex', '-1');\n this._unmutate = onMutation(this, render, {\n childList: true,\n subtree: true,\n });\n this.focus();\n }\n handleEvent({ target }: Event) {\n if (target === this) this.focus(); // Ignore if animation event was triggered by child\n }\n disconnectedCallback() {\n off(this, 'animationend', this, QUICK_EVENT);\n this._unmutate?.();\n this._unmutate = undefined;\n }\n}\n\nconst render = (self: DSErrorSummaryElement) => {\n const label = attr(self, 'aria-label')?.trim();\n const labelledBy = attr(self, 'aria-labelledby')?.trim();\n const heading = self.querySelector('h2,h3,h4,h5,h6');\n if (heading && !label && !labelledBy) {\n attr(self, 'aria-labelledby', useId(heading));\n }\n if (!heading && !label && !labelledBy) {\n warn(\n 'Missing accessible name on:',\n self,\n '\\nAdd a heading (h2–h6), or set aria-label or aria-labelledby to provide an accessible name for screen readers.',\n );\n }\n};\n\ncustomElements.define('ds-error-summary', DSErrorSummaryElement);\n"],"mappings":"mJAkBA,IAAa,EAAb,cAA2C,CAAU,CACnD,UAEA,mBAAoB,CAClB,EAAG,KAAM,eAAgB,KAAM,CAAW,EAC1C,EAAK,KAAM,OAAQ,OAAO,EAC1B,EAAK,KAAM,WAAY,IAAI,EAC3B,KAAK,UAAY,EAAW,KAAM,EAAQ,CACxC,UAAW,GACX,QAAS,EACX,CAAC,EACD,KAAK,MAAM,CACb,CACA,YAAY,CAAE,UAAiB,CACzB,IAAW,MAAM,KAAK,MAAM,CAClC,CACA,sBAAuB,CACrB,EAAI,KAAM,eAAgB,KAAM,CAAW,EAC3C,KAAK,YAAY,EACjB,KAAK,UAAY,IAAA,EACnB,CACF,EAEA,MAAM,EAAU,GAAgC,CAC9C,IAAM,EAAQ,EAAK,EAAM,YAAY,CAAC,EAAE,KAAK,EACvC,EAAa,EAAK,EAAM,iBAAiB,CAAC,EAAE,KAAK,EACjD,EAAU,EAAK,cAAc,gBAAgB,EAC/C,GAAW,CAAC,GAAS,CAAC,GACxB,EAAK,EAAM,kBAAmB,EAAM,CAAO,CAAC,EAE1C,CAAC,GAAW,CAAC,GAAS,CAAC,GACzB,EACE,8BACA,EACA;8GACF,CAEJ,EAEA,EAAe,OAAO,mBAAoB,CAAqB"}
1
+ {"version":3,"file":"error-summary.js","names":[],"sources":["../../../src/error-summary/error-summary.ts"],"sourcesContent":["import {\n ARIA_LABEL,\n ARIA_LABELLEDBY,\n attr,\n customElements,\n DSElement,\n off,\n on,\n onMutation,\n QUICK_EVENT,\n useId,\n warn,\n} from '../utils/utils';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-error-summary': DSErrorSummaryElement;\n }\n}\n\nexport class DSErrorSummaryElement extends DSElement {\n _unmutate?: () => void; // Using underscore instead of private fields for backwards compatibility\n\n connectedCallback() {\n on(this, 'animationend', this, QUICK_EVENT); // Using animationend to detect when element is visible\n attr(this, 'role', 'group');\n attr(this, 'tabindex', '-1');\n this._unmutate = onMutation(this, render, {\n childList: true,\n subtree: true,\n });\n this.focus();\n }\n handleEvent({ target }: Event) {\n if (target === this) this.focus(); // Ignore if animation event was triggered by child\n }\n disconnectedCallback() {\n off(this, 'animationend', this, QUICK_EVENT);\n this._unmutate?.();\n this._unmutate = undefined;\n }\n}\n\nconst render = (self: DSErrorSummaryElement) => {\n const label = attr(self, ARIA_LABEL)?.trim();\n const labelledBy = attr(self, ARIA_LABELLEDBY)?.trim();\n const heading = self.querySelector('h2,h3,h4,h5,h6');\n if (heading && !label && !labelledBy) {\n attr(self, ARIA_LABELLEDBY, useId(heading));\n }\n if (!heading && !label && !labelledBy) {\n warn(\n 'Missing accessible name on:',\n self,\n `\\nAdd a heading (h2–h6), or set ${ARIA_LABEL} or ${ARIA_LABELLEDBY} to provide an accessible name for screen readers.`,\n );\n }\n};\n\ncustomElements.define('ds-error-summary', DSErrorSummaryElement);\n"],"mappings":"wLAoBA,IAAa,EAAb,cAA2C,CAAU,CACnD,UAEA,mBAAoB,CAClB,EAAG,KAAM,eAAgB,KAAM,CAAW,EAC1C,EAAK,KAAM,OAAQ,OAAO,EAC1B,EAAK,KAAM,WAAY,IAAI,EAC3B,KAAK,UAAY,EAAW,KAAM,EAAQ,CACxC,UAAW,GACX,QAAS,EACX,CAAC,EACD,KAAK,MAAM,CACb,CACA,YAAY,CAAE,UAAiB,CACzB,IAAW,MAAM,KAAK,MAAM,CAClC,CACA,sBAAuB,CACrB,EAAI,KAAM,eAAgB,KAAM,CAAW,EAC3C,KAAK,YAAY,EACjB,KAAK,UAAY,IAAA,EACnB,CACF,EAEA,MAAM,EAAU,GAAgC,CAC9C,IAAM,EAAQ,EAAK,EAAM,CAAU,CAAC,EAAE,KAAK,EACrC,EAAa,EAAK,EAAM,CAAe,CAAC,EAAE,KAAK,EAC/C,EAAU,EAAK,cAAc,gBAAgB,EAC/C,GAAW,CAAC,GAAS,CAAC,GACxB,EAAK,EAAM,EAAiB,EAAM,CAAO,CAAC,EAExC,CAAC,GAAW,CAAC,GAAS,CAAC,GACzB,EACE,8BACA,EACA,mCAAmC,EAAW,MAAM,EAAgB,mDACtE,CAEJ,EAEA,EAAe,OAAO,mBAAoB,CAAqB"}
@@ -1,2 +1,2 @@
1
- import{DSElement as e,QUICK_EVENT as t,announce as n,attr as r,attrOrCSS as i,customElements as a,debounce as o,isWindows as s,on as c,onHotReload as l,onMutation as u,useId as d,warn as f}from"../utils/utils.js";const p=`aria-invalid`,m=`aria-describedby`,h=`data-indeterminate`,g=s()?800:200,_=new WeakMap,v=new Map,y=new WeakSet,b=(e,t=[])=>{for(let{target:e}of t){let t=e instanceof HTMLFieldSetElement;for(let[n]of v)(t?e.contains(n):n.contains(e))&&x(n)}},x=e=>{let t=[],n=[],i=v.get(e)||[],a,o,s=!1,c=!1;for(let r of e.getElementsByTagName(`*`))if(r instanceof HTMLLabelElement&&t.push(r),!r.hidden)if(E(r))a?f(`Fields should only have one input element. Use <fieldset> to group multiple fields:`,e):a=r;else{let e=r.getAttribute(`data-field`);e===`counter`&&(o=r),e===`validation`?(n.unshift(d(r)),s=!0,c||=T(r)):e&&n.push(d(r))}if(!a)return;o&&_.set(a,o);for(let e of t)r(e,`for`,d(a));let l=e.closest(`fieldset`)?.querySelector(`:scope > [data-field="validation"]`);l&&!l?.hidden&&(s=!0,c||=T(l),n.unshift(d(l)));let u=r(a,h);u&&(a.indeterminate=u===`true`),(a.type===`radio`||a.type===`checkbox`)&&r(e,`data-clickdelegatefor`,d(a));let g=(r(a,m)?.trim().split(/\s+/))?.filter(e=>!i.includes(e))||[];r(a,m,[...n,...g].join(` `)||null),v.set(e,n);let b=y.has(a);s&&!b&&!a.hasAttribute(p)?(r(a,p,`true`),y.add(a)):!s&&b&&(r(a,p,null),y.delete(a)),C(a)},S={over:`%d tegn for mye`,under:`%d tegn igjen`},C=e=>{let t=e.target||e,n=_.get(t);if(n?.isConnected){let a=(Number(r(n,`data-limit`))||0)-t.value.length,o=a<0?`over`:`under`,s=(i(n,`data-${o}`)||S[o])?.replace(`%d`,`${Math.abs(a)}`);r(n,`data-label`,s),r(n,`data-state`,o),r(n,`data-color`,a<0?`danger`:null),e.type===`input`&&s&&w(t,s)}t instanceof HTMLTextAreaElement&&(t.style.setProperty(`--_ds-field-sizing`,`auto`),t.style.setProperty(`--_ds-field-sizing`,`${t.scrollHeight}px`))},w=o((e,t)=>{document.activeElement===e&&n(t)},g),T=e=>e.getAttribute(`data-color`)!==`success`,E=e=>e instanceof HTMLElement&&`validity`in e&&!(e instanceof HTMLButtonElement)&&e.type!==`hidden`;var D=class extends e{connectedCallback(){v.set(this,[]),x(this)}disconnectedCallback(){v.delete(this)}};a.define(`ds-field`,D),l(`field`,()=>[c(document,`input`,C,t),u(document,b,{attributeFilter:[`data-field`,`data-limit`,`hidden`,`id`,`value`,h],attributes:!0,childList:!0,subtree:!0})]);export{D as DSFieldElement};
1
+ import{DSElement as e,QUICK_EVENT as t,announce as n,attr as r,attrOrCSS as i,customElements as a,debounce as o,isWindows as s,on as c,onMutation as l,useId as u,warn as d}from"../utils/utils.js";const f=`aria-invalid`,p=`aria-describedby`,m=`data-indeterminate`,h=s()?800:200,g=e=>{let t=[],n=[],i=e._descs||[],a=!1,o=!1;e._input?.hidden&&(e._input=void 0);for(let r of e.getElementsByTagName(`*`))if(r instanceof HTMLLabelElement&&t.push(r),!r.hidden)if(b(r))e._input?.isConnected&&e._input!==r?d(`Fields should only have one input element. Use <fieldset> to group multiple fields:`,e):e._input=r;else{let t=r.getAttribute(`data-field`);t===`counter`&&(e._counter=r),t===`validation`?(n.unshift(u(r)),a=!0,o||=y(r)):t&&n.push(u(r))}if(!e._input?.isConnected)return;let s=e._input;for(let e of t)r(e,`for`,u(s)||null);let c=e.closest(`fieldset`)?.querySelector(`:scope > [data-field="validation"]`);c&&!c?.hidden&&(a=!0,o||=y(c),n.unshift(u(c)));let l=r(s,m);l&&(s.indeterminate=l===`true`),(s.type===`radio`||s.type===`checkbox`)&&r(e,`data-clickdelegatefor`,u(s));let h=(r(s,p)?.trim().split(/\s+/))?.filter(e=>!i.includes(e))||[];r(s,p,[...n,...h].join(` `)||null),e._descs=n;let g=e._validation;a&&!g&&!s.hasAttribute(f)?(r(s,f,`true`),e._validation=!0):!a&&g&&(r(s,f,null),e._validation=void 0),e.handleEvent()},_={over:`%d tegn for mye`,under:`%d tegn igjen`},v=o((e,t)=>{document.activeElement===e&&n(t)},h),y=e=>e.getAttribute(`data-color`)!==`success`,b=e=>e instanceof HTMLElement&&`validity`in e&&!(e instanceof HTMLButtonElement)&&e.type!==`hidden`;var x=class extends e{_counter;_descs;_input;_validation;_unevents;_unmutate;connectedCallback(){this._unevents=c(this,`input`,this,t),this._unmutate=l(this,()=>g(this),{attributeFilter:[`data-field`,`data-limit`,`hidden`,`id`,`value`,m],attributes:!0,childList:!0,subtree:!0})}handleEvent(e){let{_counter:t,_input:n}=this;if(t?.isConnected&&n){let a=(Number(r(t,`data-limit`))||0)-n.value.length,o=a<0?`over`:`under`,s=(i(t,`data-${o}`)||_[o])?.replace(`%d`,`${Math.abs(a)}`);s||d(`Missing data-${o} on:`,t),r(t,`data-label`,s),r(t,`data-state`,o),r(t,`data-color`,a<0?`danger`:null),e?.type===`input`&&s&&v(n,s)}n instanceof HTMLTextAreaElement&&(n.style.setProperty(`--_ds-field-sizing`,`auto`),n.style.setProperty(`--_ds-field-sizing`,`${n.scrollHeight}px`))}disconnectedCallback(){this._unevents?.(),this._unmutate?.(),this._unevents=this._unmutate=void 0,this._input=this._counter=this._descs=this._validation=void 0}};a.define(`ds-field`,x);export{x as DSFieldElement};
2
2
  //# sourceMappingURL=field.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"field.js","names":[],"sources":["../../../src/field/field.ts"],"sourcesContent":["import {\n announce,\n attr,\n attrOrCSS,\n customElements,\n DSElement,\n debounce,\n isWindows,\n on,\n onHotReload,\n onMutation,\n QUICK_EVENT,\n useId,\n warn,\n} from '../utils/utils';\n\n// TODO: Document that Validation must be hidden with \"hidden\" attribute (or completely removed from DOM), not display: none\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-field': DSFieldElement;\n }\n}\n\nconst ATTR_INVALID = 'aria-invalid';\nconst ATTR_DESCRIBEDBY = 'aria-describedby';\nconst ATTR_INDETERMINATE = 'data-indeterminate';\nconst COUNTER_DEBOUNCE = isWindows() ? 800 : 200; // Longer debounce on Windows due to NVDA performance\nconst COUNTS = new WeakMap<HTMLInputElement, Element>(); // Using WeakMap so removed inputs/counts does not cause memory leaks\nconst FIELDS = new Map<DSFieldElement, string[]>(); // Map of Field and its describedby IDs so we can identify the ones we add/remove\nconst VALIDATIONS = new WeakSet<HTMLInputElement>(); // Used to ensure we only take control of aria-invalid if the current is or has been a validation element\nconst WARNING_MULTIPLE_INPUTS = `Fields should only have one input element. Use <fieldset> to group multiple fields:`;\n\nconst handleFieldMutations = (_doc: Node, records: MutationRecord[] = []) => {\n for (const { target } of records) {\n const isFieldset = target instanceof HTMLFieldSetElement;\n for (const [field] of FIELDS)\n if (isFieldset ? target.contains(field) : field.contains(target))\n handleFieldMutation(field);\n }\n};\n\nconst handleFieldMutation = (field: DSFieldElement) => {\n const labels: HTMLLabelElement[] = [];\n const nextDescs: string[] = []; // Keep track of descriptions we are adding in this mutation\n const prevDescs = FIELDS.get(field) || []; // Retrieve previously managed IDs for this field\n let input: HTMLInputElement | undefined;\n let counter: Element | undefined;\n let hasValidation = false;\n let invalid = false;\n\n for (const el of field.getElementsByTagName('*')) {\n if (el instanceof HTMLLabelElement) labels.push(el);\n if ((el as HTMLElement).hidden) continue; // Skip hidden elements except labels\n if (isInputLike(el)) {\n if (input) warn(WARNING_MULTIPLE_INPUTS, field);\n else input = el; // Only register if visible input\n } else {\n const type = el.getAttribute('data-field'); // Using getAttribute instead of attr for best performance\n if (type === 'counter') counter = el;\n if (type === 'validation') {\n nextDescs.unshift(useId(el));\n hasValidation = true;\n invalid = invalid || isInvalid(el);\n } else if (type) nextDescs.push(useId(el)); // Adds both counter and descriptions\n }\n }\n\n if (!input) return; // Do not warn about missing input as virtual DOM libraries might give false positives\n if (counter) COUNTS.set(input, counter);\n for (const label of labels) attr(label, 'for', useId(input));\n\n const fieldsetValidation = field\n .closest('fieldset')\n ?.querySelector<HTMLElement>(':scope > [data-field=\"validation\"]');\n\n // Connect fieldset validation to inputs\n if (fieldsetValidation && !fieldsetValidation?.hidden) {\n hasValidation = true;\n invalid = invalid || isInvalid(fieldsetValidation);\n nextDescs.unshift(useId(fieldsetValidation));\n }\n\n // Add support for data-indeterminate attribute as this normally can only be set by javascript\n const indeterminate = attr(input, ATTR_INDETERMINATE);\n if (indeterminate) input.indeterminate = indeterminate === 'true';\n\n // Expand click area to ds-field if radio/checkbox\n const isBoolish = input.type === 'radio' || input.type === 'checkbox';\n if (isBoolish) attr(field, 'data-clickdelegatefor', useId(input));\n\n // Setup aria-describedby, but repsect existing ids in aria-describedby\n const describedby = attr(input, ATTR_DESCRIBEDBY)?.trim().split(/\\s+/);\n const keep = describedby?.filter((id) => !prevDescs.includes(id)) || []; // Find non-ds-field-managed aria-describedby IDs\n attr(input, ATTR_DESCRIBEDBY, [...nextDescs, ...keep].join(' ') || null);\n FIELDS.set(field, nextDescs);\n\n // Only manage aria-invalid when field has validation elements, and aria-invalid does not already exist\n const hadValidation = VALIDATIONS.has(input);\n if (hasValidation && !hadValidation && !input.hasAttribute(ATTR_INVALID)) {\n attr(input, ATTR_INVALID, 'true');\n VALIDATIONS.add(input);\n } else if (!hasValidation && hadValidation) {\n attr(input, ATTR_INVALID, null); // Remove aria-invalid\n VALIDATIONS.delete(input);\n }\n\n handleFieldInput(input); // Update counter and textarea sizing\n};\n\n// Used as fallback in tests when CSS is not loaded\nconst TEXTS = {\n over: '%d tegn for mye',\n under: '%d tegn igjen',\n};\n\nconst handleFieldInput = (e: Event | Element) => {\n const input = ((e as Event).target || e) as HTMLInputElement;\n const counter = COUNTS.get(input);\n\n if (counter?.isConnected) {\n const limit = Number(attr(counter, 'data-limit')) || 0;\n const count = limit - input.value.length;\n const state = count < 0 ? 'over' : 'under';\n const label = (\n attrOrCSS(counter, `data-${state}`) || TEXTS[state]\n )?.replace('%d', `${Math.abs(count)}`);\n\n attr(counter, 'data-label', label); // Using attribute to prevent hydation errors, not using aria-label to make axe tests happy\n attr(counter, 'data-state', state);\n attr(counter, 'data-color', count < 0 ? 'danger' : null);\n\n // Only update live region when user is actually typing\n if ((e as Event).type === 'input' && label)\n debouncedCounterLiveRegion(input, label); // Debounce live region to avoid NVDA interupting announcing typed text\n }\n if (input instanceof HTMLTextAreaElement) {\n input.style.setProperty('--_ds-field-sizing', 'auto');\n input.style.setProperty('--_ds-field-sizing', `${input.scrollHeight}px`);\n }\n};\n\nconst debouncedCounterLiveRegion = debounce((input: Element, text: string) => {\n if (document.activeElement === input) announce(text); // Only announce if input is still focused\n}, COUNTER_DEBOUNCE);\n\nconst isInvalid = (el: Element) => el.getAttribute('data-color') !== 'success';\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 (el as HTMLInputElement).type !== 'hidden'; // And skip input type=\"hidden\"\n\n// Custom element is used to performantly keep track of fields on the page\nexport class DSFieldElement extends DSElement {\n connectedCallback() {\n FIELDS.set(this, []); // Register field\n handleFieldMutation(this); // Initial setup\n }\n disconnectedCallback() {\n FIELDS.delete(this);\n }\n}\n\ncustomElements.define('ds-field', DSFieldElement);\n\nonHotReload('field', () => [\n on(document, 'input', handleFieldInput, QUICK_EVENT),\n onMutation(document, handleFieldMutations, {\n attributeFilter: [\n 'data-field',\n 'data-limit',\n 'hidden', // Needed to check validation visibility\n 'id', // Needed to sync label \"for\" when ID of input/selec/textarea changes\n 'value', // Needed to detect changes in controlled React inputs as they do not trigger input events\n ATTR_INDETERMINATE,\n ],\n attributes: true,\n childList: true,\n subtree: true,\n }),\n]);\n"],"mappings":"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"}
1
+ {"version":3,"file":"field.js","names":[],"sources":["../../../src/field/field.ts"],"sourcesContent":["import {\n announce,\n attr,\n attrOrCSS,\n customElements,\n DSElement,\n debounce,\n isWindows,\n on,\n onMutation,\n QUICK_EVENT,\n useId,\n warn,\n} from '../utils/utils';\n\n// TODO: Document that Validation must be hidden with \"hidden\" attribute (or completely removed from DOM), not display: none\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-field': DSFieldElement;\n }\n}\n\nconst ATTR_INVALID = 'aria-invalid';\nconst ATTR_DESCRIBEDBY = 'aria-describedby';\nconst ATTR_INDETERMINATE = 'data-indeterminate';\nconst COUNTER_DEBOUNCE = isWindows() ? 800 : 200; // Longer debounce on Windows due to NVDA performance\nconst WARNING_MULTIPLE_INPUTS = `Fields should only have one input element. Use <fieldset> to group multiple fields:`;\n\nconst handleFieldMutation = (field: DSFieldElement) => {\n const labels: HTMLLabelElement[] = [];\n const nextDescs: string[] = []; // Keep track of descriptions we are adding in this mutation\n const prevDescs = field._descs || []; // Retrieve previously managed IDs for this field\n let hasValidation = false;\n let invalid = false;\n\n if (field._input?.hidden) field._input = undefined; // Reset input if it has been hidden\n\n for (const el of field.getElementsByTagName('*')) {\n if (el instanceof HTMLLabelElement) labels.push(el);\n if ((el as HTMLElement).hidden) continue; // Skip hidden elements except labels\n if (isInputLike(el)) {\n if (field._input?.isConnected && field._input !== el)\n warn(WARNING_MULTIPLE_INPUTS, field);\n else field._input = el; // Only register if visible input\n } else {\n const type = el.getAttribute('data-field'); // Using getAttribute instead of attr for best performance\n if (type === 'counter') field._counter = el;\n if (type === 'validation') {\n nextDescs.unshift(useId(el));\n hasValidation = true;\n invalid = invalid || isInvalid(el);\n } else if (type) nextDescs.push(useId(el)); // Adds both counter and descriptions\n }\n }\n\n if (!field._input?.isConnected) return; // Do not warn about missing input as virtual DOM libraries might give false positives\n\n const input = field._input;\n for (const label of labels) attr(label, 'for', useId(input) || null);\n\n const fieldsetValidation = field\n .closest('fieldset')\n ?.querySelector<HTMLElement>(':scope > [data-field=\"validation\"]');\n\n // Connect fieldset validation to inputs\n if (fieldsetValidation && !fieldsetValidation?.hidden) {\n hasValidation = true;\n invalid = invalid || isInvalid(fieldsetValidation);\n nextDescs.unshift(useId(fieldsetValidation));\n }\n\n // Add support for data-indeterminate attribute as this normally can only be set by javascript\n\n const indeterminate = attr(input, ATTR_INDETERMINATE);\n if (indeterminate) input.indeterminate = indeterminate === 'true';\n\n // Expand click area to ds-field if radio/checkbox\n const isBoolish = input.type === 'radio' || input.type === 'checkbox';\n if (isBoolish) attr(field, 'data-clickdelegatefor', useId(input));\n\n // Setup aria-describedby, but repsect existing ids in aria-describedby\n const describedby = attr(input, ATTR_DESCRIBEDBY)?.trim().split(/\\s+/);\n const keep = describedby?.filter((id) => !prevDescs.includes(id)) || []; // Find non-ds-field-managed aria-describedby IDs\n attr(input, ATTR_DESCRIBEDBY, [...nextDescs, ...keep].join(' ') || null);\n field._descs = nextDescs;\n\n // Only manage aria-invalid when field has validation elements, and aria-invalid does not already exist\n const hadValidation = field._validation;\n if (hasValidation && !hadValidation && !input.hasAttribute(ATTR_INVALID)) {\n attr(input, ATTR_INVALID, 'true');\n field._validation = true;\n } else if (!hasValidation && hadValidation) {\n attr(input, ATTR_INVALID, null); // Remove aria-invalid\n field._validation = undefined;\n }\n\n field.handleEvent(); // Update counter and textarea sizing\n};\n\n// Used as fallback in tests when CSS is not loaded\nconst TEXTS = {\n over: '%d tegn for mye',\n under: '%d tegn igjen',\n};\n\nconst debouncedCounterLiveRegion = debounce((input: Element, text: string) => {\n if (document.activeElement === input) announce(text); // Only announce if input is still focused\n}, COUNTER_DEBOUNCE);\n\nconst isInvalid = (el: Element) => el.getAttribute('data-color') !== 'success';\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 (el as HTMLInputElement).type !== 'hidden'; // And skip input type=\"hidden\"\n\n// Custom element is used to performantly keep track of fields on the page\nexport class DSFieldElement extends DSElement {\n _counter?: Element; // Using underscore instead of private fields for backwards compatibility\n _descs?: string[];\n _input?: HTMLInputElement;\n _validation?: boolean;\n _unevents?: () => void;\n _unmutate?: () => void;\n\n connectedCallback() {\n this._unevents = on(this, 'input', this, QUICK_EVENT);\n this._unmutate = onMutation(this, () => handleFieldMutation(this), {\n attributeFilter: [\n 'data-field',\n 'data-limit',\n 'hidden', // Needed to check validation visibility\n 'id', // Needed to sync label \"for\" when ID of input/selec/textarea changes\n 'value', // Needed to detect changes in controlled React inputs as they do not trigger input events\n ATTR_INDETERMINATE,\n ],\n attributes: true,\n childList: true,\n subtree: true,\n });\n }\n handleEvent(event?: Event) {\n const { _counter, _input } = this;\n\n if (_counter?.isConnected && _input) {\n const limit = Number(attr(_counter, 'data-limit')) || 0;\n const count = limit - _input.value.length;\n const state = count < 0 ? 'over' : 'under';\n const label = (attrOrCSS(_counter, `data-${state}`) || TEXTS[state]) // Browser translation tools will not pick up dynamic text anyway, so no need to use aria-labelledby here\n ?.replace('%d', `${Math.abs(count)}`);\n\n if (!label) warn(`Missing data-${state} on:`, _counter);\n attr(_counter, 'data-label', label); // Using attribute to prevent hydration errors, not using aria-label to make axe tests happy\n attr(_counter, 'data-state', state);\n attr(_counter, 'data-color', count < 0 ? 'danger' : null);\n\n // Only update live region when user is actually typing\n if (event?.type === 'input' && label)\n debouncedCounterLiveRegion(_input, label); // Debounce live region to avoid NVDA interupting announcing typed text\n }\n if (_input instanceof HTMLTextAreaElement) {\n _input.style.setProperty('--_ds-field-sizing', 'auto');\n _input.style.setProperty(\n '--_ds-field-sizing',\n `${_input.scrollHeight}px`,\n );\n }\n }\n disconnectedCallback() {\n this._unevents?.();\n this._unmutate?.();\n this._unevents = this._unmutate = undefined;\n this._input = this._counter = this._descs = this._validation = undefined;\n }\n}\n\ncustomElements.define('ds-field', DSFieldElement);\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{attr as e,isBrowser as t,onHotReload as n,onMutation as r,useId as i}from"../utils/utils.js";const a=t()?document.getElementsByTagName(`fieldset`):[],o=()=>{for(let t of a)t.hasAttribute(`aria-labelledby`)||e(t,`aria-labelledby`,`${i(t.querySelector(`legend`))} ${i(t.querySelector(`:scope > :is([data-field="description"],legend + p)`))}`.trim()||null)};n(`fieldset`,()=>[r(document,o,{childList:!0,subtree:!0})]);
1
+ import{ARIA_LABELLEDBY as e,attr as t,isBrowser as n,onHotReload as r,onMutation as i,useId as a}from"../utils/utils.js";const o=n()?document.getElementsByTagName(`fieldset`):[],s=()=>{for(let n of o){if(n.hasAttribute(`aria-labelledby`))continue;let r=`${a(n.querySelector(`legend`))} ${a(n.querySelector(`:scope > :is([data-field="description"],legend + p)`))}`;t(n,e,r.trim()||null)}};r(`fieldset`,()=>[i(document,s,{childList:!0,subtree:!0})]);
2
2
  //# sourceMappingURL=fieldset.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fieldset.js","names":[],"sources":["../../../src/fieldset/fieldset.ts"],"sourcesContent":["import {\n attr,\n isBrowser,\n onHotReload,\n onMutation,\n useId,\n} from '../utils/utils';\n\nconst FIELDSETS = isBrowser() ? document.getElementsByTagName('fieldset') : [];\n\n// NOTE:\n// <fieldset> descriptions should be accessible to screen reader users. However, using aria-describedby\n// on <fieldset> causes all child <input> elements to inherit the same description, resulting in redundant and confusing announcements.\n// To avoid this, we use aria-labelledby to reference both the legend and the description.\n// aria-labelledby is only announced when screen readers enter the fieldset, not when navigating its child elements.\n// This means the accessible name of <fieldset> includes both the legend and description, which may differ from some test expectations,\n// but as of March 2026, this approach provides the best user experience across assistive technologies.\n// This approach is also verified by the chief of accessibility at NRK and the accessibility expert at NAV\nconst handleFieldsetMutations = () => {\n for (const el of FIELDSETS) {\n if (el.hasAttribute('aria-labelledby')) continue; // Speed up by skipping labelled fieldsets\n const labelledby = `${useId(el.querySelector('legend'))} ${useId(el.querySelector(':scope > :is([data-field=\"description\"],legend + p)'))}`;\n attr(el, 'aria-labelledby', labelledby.trim() || null);\n }\n};\n\nonHotReload('fieldset', () => [\n onMutation(document, handleFieldsetMutations, {\n childList: true,\n subtree: true,\n }),\n]);\n"],"mappings":"oGAQA,MAAM,EAAY,EAAU,EAAI,SAAS,qBAAqB,UAAU,EAAI,CAAC,EAUvE,MAAgC,CACpC,IAAK,IAAM,KAAM,EACX,EAAG,aAAa,iBAAiB,GAErC,EAAK,EAAI,kBAAmB,GADN,EAAM,EAAG,cAAc,QAAQ,CAAC,EAAE,GAAG,EAAM,EAAG,cAAc,qDAAqD,CAAC,IACjG,KAAK,GAAK,IAAI,CAEzD,EAEA,EAAY,eAAkB,CAC5B,EAAW,SAAU,EAAyB,CAC5C,UAAW,GACX,QAAS,EACX,CAAC,CACH,CAAC"}
1
+ {"version":3,"file":"fieldset.js","names":[],"sources":["../../../src/fieldset/fieldset.ts"],"sourcesContent":["import {\n ARIA_LABELLEDBY,\n attr,\n isBrowser,\n onHotReload,\n onMutation,\n useId,\n} from '../utils/utils';\n\nconst FIELDSETS = isBrowser() ? document.getElementsByTagName('fieldset') : [];\n\n// NOTE:\n// <fieldset> descriptions should be accessible to screen reader users. However, using aria-describedby\n// on <fieldset> causes all child <input> elements to inherit the same description, resulting in redundant and confusing announcements.\n// To avoid this, we use aria-labelledby to reference both the legend and the description.\n// aria-labelledby is only announced when screen readers enter the fieldset, not when navigating its child elements.\n// This means the accessible name of <fieldset> includes both the legend and description, which may differ from some test expectations,\n// but as of March 2026, this approach provides the best user experience across assistive technologies.\n// This approach is also verified by the chief of accessibility at NRK and the accessibility expert at NAV\nconst handleFieldsetMutations = () => {\n for (const el of FIELDSETS) {\n if (el.hasAttribute(ARIA_LABELLEDBY)) continue; // Speed up by skipping labelled fieldsets\n const labelledby = `${useId(el.querySelector('legend'))} ${useId(el.querySelector(':scope > :is([data-field=\"description\"],legend + p)'))}`;\n attr(el, ARIA_LABELLEDBY, labelledby.trim() || null);\n }\n};\n\nonHotReload('fieldset', () => [\n onMutation(document, handleFieldsetMutations, {\n childList: true,\n subtree: true,\n }),\n]);\n"],"mappings":"yHASA,MAAM,EAAY,EAAU,EAAI,SAAS,qBAAqB,UAAU,EAAI,CAAC,EAUvE,MAAgC,CACpC,IAAK,IAAM,KAAM,EAAW,CAC1B,GAAI,EAAG,aAAA,iBAA4B,EAAG,SACtC,IAAM,EAAa,GAAG,EAAM,EAAG,cAAc,QAAQ,CAAC,EAAE,GAAG,EAAM,EAAG,cAAc,qDAAqD,CAAC,IACxI,EAAK,EAAI,EAAiB,EAAW,KAAK,GAAK,IAAI,CACrD,CACF,EAEA,EAAY,eAAkB,CAC5B,EAAW,SAAU,EAAyB,CAC5C,UAAW,GACX,QAAS,EACX,CAAC,CACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import{QUICK_EVENT as e,attr as t,getComposedPath as n,getComposedTarget as r,isBrowser as i,on as a,onHotReload as o}from"../utils/utils.js";const s=`focusgroup`,c=`${s}start`,l=new WeakMap,u=new Map,d={listbox:{block:!0,wrap:!1,items:`option`},menu:{block:!0,wrap:!0,items:`menuitem`},menubar:{block:!1,wrap:!0,items:`menuitem`},radiogroup:{block:null,wrap:!0,items:`radio`},tablist:{block:!1,wrap:!0,items:`tab`},toolbar:{block:!1,wrap:!1,items:null}};let f=!1;const p=new Set([`AUDIO`,`VIDEO`,`TEXTAREA`,`SELECT`,`date`,`datetime-local`,`email`,`month`,`number`,`password`,`range`,`search`,`tel`,`text`,`time`,`url`,`week`]),m=i()&&(s in HTMLElement.prototype||`focusGroup`in HTMLElement.prototype),h=e=>f=e,g=e=>{if(e.defaultPrevented||e.altKey||e.metaKey||e.ctrlKey)return;let t=e.key===`Tab`,i=e.key===`ArrowUp`||e.key===`ArrowDown`,a=i||e.key===`ArrowLeft`||e.key===`ArrowRight`;if(!t&&!a&&e.key!==`Home`&&e.key!==`End`)return;t&&(h(!0),setTimeout(h,100,!1));let o=r(e);if(!o||E(o))return;let s=v(n(o));if(s?.role&&s?.el===o&&(s=v(n(s.el.parentNode))),!s?.role)return;let c=b(s.el,o),l=0;if(t)return setTimeout(D,0,c,D(c));if(e.key===`Home`)l=0;else if(e.key===`End`)l=c.length-1;else{let{direction:t,writingMode:n}=getComputedStyle(o),r=n.startsWith(`vertical`),a=r?n===`vertical-rl`:t===`rtl`,u=e.key===`ArrowDown`||e.key===`ArrowRight`,d=i!==r,f=u!==(!i&&a);if((s.block??d)!==d)return;l=c.indexOf(o)+(f?1:-1),l=s.wrap?(c.length+l)%c.length:Math.max(0,Math.min(l,c.length-1))}c[l]!==o&&e.preventDefault(),c[l]?.focus?.()},_=t=>{let i=r(t),o=n(i);for(let[e,t]of u)!o.has(e)&&u.delete(e)&&t();for(let t of o)t.nodeType===11&&!u.has(t)&&u.set(t,a(t,`focus`,_,e));if(t.target?.shadowRoot)return;let s=v(o);if(s?.role){if(f&&!E(s.focus)){let e=x(b(s.el,null),i),t=s.memory&&e.includes(s.focus)&&s.focus||e.find(e=>e?.hasAttribute(c))||e[0];if(t!==i)return t?.focus?.()}s.focus=i}},v=e=>{for(let n of e){if(n.nodeType!==Node.ELEMENT_NODE)continue;let e=n.getAttribute(s);if(e!==null){let r=l.get(n);if(r?.key===e)return r;r=y(n,e),l.set(n,r);for(let e of b(n))e&&!e.hasAttribute(`role`)&&t(e,`role`,r.items);return n.hasAttribute(`role`)||t(n,`role`,r.role),r}if(C(n))return}},y=(e,t)=>{let n=new Set(t.toLowerCase().split(` `)),r=[...n].find(e=>e in d),i=d[r],a=n.has(`inline`),o=n.has(`block`);return{key:t,el:e,role:r,block:a&&o?null:o||!a&&i?.block,focus:null,items:i?.items,memory:!n.has(`nomemory`),wrap:n.has(`wrap`)||!n.has(`nowrap`)&&i?.wrap}},b=(e,t,n=[],r=!1)=>{let i=e?.nodeName===`SLOT`?e.assignedElements({flatten:!0}):(e?.shadowRoot||e)?.children;for(let e=0,a=i?.length||e;i&&e<a;e++){let a=i[e];if(a.nodeName===`SLOT`)b(a,t,n,r);else if(S(a)&&!C(a)&&w(a)){let e=a.getAttribute(s);a===t||!r&&e!==`none`&&T(a)?n.push(a):e!==null&&t===null?n.push(null):b(a,t,n,r||e!==null)}}return n},x=(e,t)=>{let n=e.indexOf(t),r=e.indexOf(null,n);return e.slice(e.lastIndexOf(null,n)+1,r===-1?void 0:r)},S=e=>!e.inert&&!e.hidden&&!e.disabled,C=e=>e?.nodeName===`DIALOG`||e?.hasAttribute(`popover`),w=i()&&typeof Element.prototype.checkVisibility==`function`?e=>e.checkVisibility():e=>e.offsetParent!==null,T=e=>e.isContentEditable||e.tabIndex>=0&&!e.disabled,E=e=>e?.isContentEditable||p.has(e?.nodeName)||e?.nodeName===`INPUT`&&p.has(e.type),D=(e,n)=>Array.from(e,(e,r)=>{let i=e?.getAttribute(`tabindex`);return e&&t(e,`tabindex`,n?n[r]:`-1`),i});m||o(s,()=>[a(document,`keydown`,g),a(document,`focus`,_,e),()=>{for(let[,e]of u)e();u.clear()}]);export{v as getGroup,b as getItems,E as isConflict,T as isFocusable};
2
+ //# sourceMappingURL=focusgroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focusgroup.js","names":[],"sources":["../../../src/focusgroup/focusgroup.ts"],"sourcesContent":["import {\n attr,\n getComposedPath,\n getComposedTarget,\n isBrowser,\n on,\n onHotReload,\n QUICK_EVENT,\n} from '../utils/utils';\n\n// Intentionally not implemented:\n// - Clearing memory based on attribute changes: https://open-ui.org/components/scoped-focusgroup.explainer/#disabling-focusgroup-memory\n// - Setting ARIA roles before focus or keydown occurs (this is too performance consuming, and does not affect a11y much)\n\nconst ATTR_GROUP = 'focusgroup';\nconst PROP_GROUP = 'focusGroup';\nconst ATTR_START = `${ATTR_GROUP}start`;\nconst GROUPS = new WeakMap<Element, ReturnType<typeof parseGroup>>();\nconst ROOTS = new Map<Node, () => void>(); // ShadowRoots we listen for focus events on, since focus only triggers once on ShadowDOM due to event retargeting\nconst ROLES = {\n listbox: { block: true, wrap: false, items: 'option' },\n menu: { block: true, wrap: true, items: 'menuitem' },\n menubar: { block: false, wrap: true, items: 'menuitem' },\n radiogroup: { block: null, wrap: true, items: 'radio' },\n tablist: { block: false, wrap: true, items: 'tab' },\n toolbar: { block: false, wrap: false, items: null },\n};\nlet IS_TAB = false; // Used to check if focus event is a result of tabbing\nconst IS_CONFLICT = new Set([\n 'AUDIO',\n 'VIDEO',\n 'TEXTAREA',\n 'SELECT',\n 'date',\n 'datetime-local',\n 'email',\n 'month',\n 'number',\n 'password',\n 'range',\n 'search',\n 'tel',\n 'text',\n 'time',\n 'url',\n 'week',\n]);\n\n// Chrome has implemented camel case \"focusGroup\", but several polyfills checks both camel case and kebab case, so we support both for now\nconst IS_SUPPORTED =\n isBrowser() &&\n (ATTR_GROUP in HTMLElement.prototype || PROP_GROUP in HTMLElement.prototype);\n\nconst setIsTab = (state: boolean) => (IS_TAB = state);\nconst handleKeydown = (e: Event & Partial<KeyboardEvent>) => {\n if (e.defaultPrevented || e.altKey || e.metaKey || e.ctrlKey) return;\n\n const isTab = e.key === 'Tab';\n const isBlock = e.key === 'ArrowUp' || e.key === 'ArrowDown';\n const isArrow = isBlock || e.key === 'ArrowLeft' || e.key === 'ArrowRight';\n if (!isTab && !isArrow && e.key !== 'Home' && e.key !== 'End') return;\n if (isTab) {\n setIsTab(true);\n setTimeout(setIsTab, 100, false); // Reset after event loop so we can check if next focus event is a result of tabbing\n }\n\n const target = getComposedTarget(e) as Element | null;\n if (!target || isConflict(target)) return; // See https://open-ui.org/components/scoped-focusgroup.explainer/#key-conflict-elements\n\n let group = getGroup(getComposedPath(target));\n\n if (group?.role && group?.el === target)\n group = getGroup(getComposedPath(group.el.parentNode)); // If focus is on the group itself, check if it is nested inside another group\n if (!group?.role) return; // Ignore invalid parent groups\n\n const items = getItems(group.el, target); // Include target so we can move from tabindex=\"-1\" as according to spec\n let next = 0;\n\n if (isTab) return setTimeout(setTab, 0, items, setTab(items)); // Make sure next tab stop is outside focusgroup\n if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = items.length - 1;\n else {\n const { direction: dir, writingMode: mode } = getComputedStyle(target);\n const isFlipped = mode.startsWith('vertical');\n const isReverse = isFlipped ? mode === 'vertical-rl' : dir === 'rtl';\n const isForward = e.key === 'ArrowDown' || e.key === 'ArrowRight';\n\n const moveBlock = isBlock !== isFlipped;\n const moveForward = isForward !== (!isBlock && isReverse);\n\n if ((group.block ?? moveBlock) !== moveBlock) return; // Ignore if group direction does not match move direction\n next = items.indexOf(target) + (moveForward ? 1 : -1);\n\n if (group.wrap) next = (items.length + next) % items.length;\n else next = Math.max(0, Math.min(next, items.length - 1));\n }\n\n // Note: preventDefault() prevents native <input type=\"radio\"> elements from changing checked state with arrow keys.\n // Although this differs from native behavior, it matches focusgroup=\"radiogroup\" for <button role=\"radio\">.\n // toggle-group.ts already normalizes this for both <input type=\"radio\"> and <button role=\"radio\">,\n // meaning the following preventDefault() introduces no inconsistencies:\n if (items[next] !== target) e.preventDefault(); // Prevent scrolling if changing item\n (items[next] as HTMLElement)?.focus?.();\n};\n\nconst handleFocus = (e: Event & Partial<FocusEvent>) => {\n const target = getComposedTarget(e);\n const path = getComposedPath(target);\n\n for (const [el, off] of ROOTS) if (!path.has(el) && ROOTS.delete(el)) off(); // Remove focus listener on previous roots\n for (const el of path)\n if (el.nodeType === 11 && !ROOTS.has(el))\n ROOTS.set(el, on(el, 'focus', handleFocus, QUICK_EVENT)); // Listener will instantly dispatch since we are binding during triggering\n\n if ((e.target as Element)?.shadowRoot) return; // Avoid double handling by only processing focus events that are not already handled in a shadowRoot\n\n const group = getGroup(path);\n if (!group?.role) return;\n if (IS_TAB && !isConflict(group.focus)) {\n const segment = getSegment(getItems(group.el, null), target); // See https://open-ui.org/components/scoped-focusgroup.explainer/#focusgroup-segments\n const start =\n (group.memory && segment.includes(group.focus) && group.focus) ||\n segment.find((el) => el?.hasAttribute(ATTR_START)) ||\n segment[0];\n if (start !== target) return (start as HTMLElement)?.focus?.(); // Fix focus position if tabbing into segment\n }\n group.focus = target; // Always store focus\n};\n\nexport const getGroup = (path: Set<Node>) => {\n for (const el of path as Set<Element>) {\n if (el.nodeType !== Node.ELEMENT_NODE) continue;\n\n const key = el.getAttribute(ATTR_GROUP);\n if (key !== null) {\n let group = GROUPS.get(el);\n if (group?.key === key) return group; // Return cache if attribute is unchanged\n\n group = parseGroup(el, key);\n GROUPS.set(el, group);\n for (const item of getItems(el))\n if (item && !item.hasAttribute('role')) attr(item, 'role', group.items);\n if (!el.hasAttribute('role')) attr(el, 'role', group.role);\n return group;\n }\n if (isTopLayer(el)) return;\n }\n};\n\nconst parseGroup = (el: Element, key: string) => {\n const opts = new Set(key.toLowerCase().split(' '));\n const role = [...opts].find((t) => t in ROLES) as keyof typeof ROLES;\n const base = ROLES[role];\n const x = opts.has('inline');\n const y = opts.has('block');\n\n return {\n key,\n el,\n role,\n block: x && y ? null : y || (x ? false : base?.block),\n focus: null as Element | null,\n items: base?.items,\n memory: !opts.has('nomemory'),\n wrap: opts.has('wrap') || (opts.has('nowrap') ? false : base?.wrap),\n };\n};\n\nexport const getItems = (\n root?: Element | null,\n keep?: Element | null, // Provide Element to include, or null to keep segments (https://open-ui.org/components/scoped-focusgroup.explainer/#focusgroup-segments)\n items: (Element | null)[] = [], // Used only for recursion\n isNested = false, // Used only for recursion, to check if already inside a nested group\n) => {\n const children =\n root?.nodeName === 'SLOT'\n ? (root as HTMLSlotElement).assignedElements({ flatten: true })\n : (root?.shadowRoot || root)?.children;\n\n for (let i = 0, l = children?.length || i; children && i < l; i++) {\n const el = children[i] as HTMLElement;\n if (el.nodeName === 'SLOT') getItems(el, keep, items, isNested);\n else if (isKeyboardReachable(el) && !isTopLayer(el) && isVisible(el)) {\n const group = el.getAttribute(ATTR_GROUP);\n if (el === keep || (!isNested && group !== 'none' && isFocusable(el)))\n items.push(el);\n else if (group !== null && keep === null) items.push(null);\n else getItems(el, keep, items, isNested || group !== null);\n }\n }\n return items;\n};\n\n// Treats the null returned from getItems as a separator and returns segment containing \"item\"\nconst getSegment = <T>(acc: (T | null)[], item: T) => {\n const at = acc.indexOf(item);\n const to = acc.indexOf(null, at);\n return acc.slice(acc.lastIndexOf(null, at) + 1, to === -1 ? undefined : to);\n};\n\nconst isKeyboardReachable = (el: HTMLElement) =>\n !el.inert && !el.hidden && !(el as HTMLInputElement).disabled;\n\nconst isTopLayer = (el: Element | null) =>\n el?.nodeName === 'DIALOG' || el?.hasAttribute('popover'); // See https://open-ui.org/components/scoped-focusgroup.explainer/#top-layer-elements)\n\nconst isVisible =\n isBrowser() && typeof Element.prototype.checkVisibility === 'function'\n ? (el: HTMLElement) => el.checkVisibility()\n : (el: HTMLElement) => el.offsetParent !== null;\n\nexport const isFocusable = (el: HTMLElement) =>\n el.isContentEditable ||\n (el.tabIndex >= 0 && !(el as HTMLInputElement).disabled);\n\nexport const isConflict = (el: Node | null) =>\n (el as HTMLElement)?.isContentEditable ||\n IS_CONFLICT.has(el?.nodeName as string) ||\n (el?.nodeName === 'INPUT' && IS_CONFLICT.has((el as HTMLInputElement).type));\n\nconst setTab = (items: ReturnType<typeof getItems>, prevs?: string[]) =>\n Array.from(items, (item, index) => {\n const prev = item?.getAttribute('tabindex');\n if (item) attr(item, 'tabindex', prevs ? prevs[index] : '-1'); // Restore previous tabindex if provided\n return prev;\n });\n\nif (!IS_SUPPORTED)\n onHotReload(ATTR_GROUP, () => [\n on(document, 'keydown', handleKeydown),\n on(document, 'focus', handleFocus, QUICK_EVENT),\n () => {\n for (const [, off] of ROOTS) off(); // Cleanup listeners on ShadowRoots on hot reload\n ROOTS.clear();\n },\n ]);\n"],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- import"./clickdelegatefor/clickdelegatefor.js";import"./details/details.js";import"./dialog/dialog.js";import"./fieldset/fieldset.js";import"./invokers/invokers.js";import"./popover/popover.js";import"./readonly/readonly.js";import"./toggle-group/toggle-group.js";import{setTooltipElement as e}from"./tooltip/tooltip.js";import{DSBreadcrumbsElement as t}from"./breadcrumbs/breadcrumbs.js";import{DSErrorSummaryElement as n}from"./error-summary/error-summary.js";import{DSFieldElement as r}from"./field/field.js";import{DSPaginationElement as i,pagination as a}from"./pagination/pagination.js";import{DSSuggestionElement as o}from"./suggestion/suggestion.js";import{DSTabElement as s,DSTabListElement as c,DSTabPanelElement as l,DSTabsElement as u}from"./tabs/tabs.js";export*from"@u-elements/u-datalist";export{t as DSBreadcrumbsElement,n as DSErrorSummaryElement,r as DSFieldElement,i as DSPaginationElement,o as DSSuggestionElement,s as DSTabElement,c as DSTabListElement,l as DSTabPanelElement,u as DSTabsElement,a as pagination,e as setTooltipElement};
1
+ import"./clickdelegatefor/clickdelegatefor.js";import"./details/details.js";import"./dialog/dialog.js";import"./fieldset/fieldset.js";import"./focusgroup/focusgroup.js";import"./invokers/invokers.js";import"./popover/popover.js";import"./readonly/readonly.js";import"./toggle-group/toggle-group.js";import{initTooltips as e,setTooltipElement as t}from"./tooltip/tooltip.js";import{DSBreadcrumbsElement as n}from"./breadcrumbs/breadcrumbs.js";import{DSErrorSummaryElement as r}from"./error-summary/error-summary.js";import{DSFieldElement as i}from"./field/field.js";import{DSPaginationElement as a,pagination as o}from"./pagination/pagination.js";import{DSSuggestionElement as s}from"./suggestion/suggestion.js";import{DSTabElement as c,DSTabListElement as l,DSTabPanelElement as u,DSTabsElement as d}from"./tabs/tabs.js";export*from"@u-elements/u-datalist";export{n as DSBreadcrumbsElement,r as DSErrorSummaryElement,i as DSFieldElement,a as DSPaginationElement,s as DSSuggestionElement,c as DSTabElement,l as DSTabListElement,u as DSTabPanelElement,d as DSTabsElement,e as initTooltips,o as pagination,t as setTooltipElement};
@@ -1 +1 @@
1
- {"version":3,"file":"invokers.js","names":[],"sources":["../../../src/invokers/invokers.ts"],"sourcesContent":["import { isSupported, apply as polyfillInvokers } from 'invokers-polyfill/fn';\nimport { isBrowser } from '../utils/utils';\n\nif (isBrowser() && !isSupported()) polyfillInvokers(); // Ensure invoker commands polyfill is loaded in browser environment only\n"],"mappings":"gIAGI,EAAU,GAAK,CAAC,EAAY,GAAG,EAAiB"}
1
+ {"version":3,"file":"invokers.js","names":[],"sources":["../../../src/invokers/invokers.ts"],"sourcesContent":["import { isSupported, apply as polyfillInvokers } from 'invokers-polyfill/fn';\nimport { isBrowser } from '../utils/utils';\n\n// isPolyfilled is not used here as isSupported is enough to determine if the polyfill needs to be applied as it checks for the presence of the invoker commands in the environment. If they are not present, it means the polyfill is not applied and needs to be applied.\nif (isBrowser() && !isSupported()) polyfillInvokers(); // Ensure invoker commands polyfill is loaded in browser environment only\n"],"mappings":"gIAII,EAAU,GAAK,CAAC,EAAY,GAAG,EAAiB"}
@@ -0,0 +1,2 @@
1
+ var e=`1.19.0`;export{e as version};
2
+ //# sourceMappingURL=package.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.js","names":[],"sources":["../../../../package.json"],"sourcesContent":[""],"mappings":""}
@@ -1,2 +1,2 @@
1
- import{DSElement as e,attr as t,attrOrCSS as n,customElements as r,onMutation as i,warn as a}from"../utils/utils.js";const o=`aria-label`,s=`data-current`,c=`data-total`,l=`data-href`,u=({current:e=1,total:t=10,show:n=7})=>({prev:e>1?e-1:0,next:e<t?e+1:0,pages:p(e,t,n).map((t,n)=>({current:t===e&&`page`,key:`key-${t}-${n}`,page:t}))});var d=class extends e{_unmutate;_render;static get observedAttributes(){return[o,s,c,l]}connectedCallback(){let e=t(this,c),r=t(this,s);r&&!e&&a(`Missing ${c} attribute on:`,this),e&&!r&&a(`Missing ${s} attribute on:`,this),t(this,o,n(this,o)),t(this,`role`,`navigation`),this._unmutate=i(this,f,{childList:!0,subtree:!0})}attributeChangedCallback(){this._unmutate&&f(this)}disconnectedCallback(){this._unmutate?.(),this._unmutate=this._render=void 0}};const f=e=>{let n=Number(t(e,s)),r=Number(t(e,c));if(n&&r){let i=e.querySelectorAll(`button,a`),a=i.length-2,o=t(e,l),{next:s,prev:c,pages:d}=u({current:n,total:r,show:a});i.forEach((e,n)=>{let r=n?i[n+1]?d[n-1]?.page:s:c;t(e,`aria-current`,d[n-1]?.current?`true`:null),t(e,`aria-label`,`${r??`hidden`}`),t(e,`role`,r?null:`none`),t(e,`tabindex`,r?null:`-1`),e instanceof HTMLButtonElement&&t(e,`value`,`${r}`),o&&e instanceof HTMLAnchorElement&&t(e,`href`,o.replace(`%d`,`${r}`))})}},p=(e,t,n=1/0)=>{let r=(n-1)/2,i=Math.max(Math.min(e-Math.floor(r),t-n+1),1),a=Math.min(Math.max(e+Math.ceil(r),n),t),o=Array.from({length:a+1-i},(e,t)=>t+i);return n>4&&i>1&&o.splice(0,2,1,0),n>3&&a<t&&o.splice(-2,2,0,t),o};r.define(`ds-pagination`,d);export{d as DSPaginationElement,u as pagination};
1
+ import{ARIA_LABEL as e,ARIA_LABELLEDBY as t,DSElement as n,attr as r,attrOrCSS as i,customElements as a,onMutation as o,warn as s}from"../utils/utils.js";const c=`data-current`,l=`data-total`,u=`data-href`,d=({current:e=1,total:t=10,show:n=7})=>({prev:e>1?e-1:0,next:e<t?e+1:0,pages:m(e,t,n).map((t,n)=>({current:t===e&&`page`,key:`key-${t}-${n}`,page:t}))});var f=class extends n{_unmutate;_render;static get observedAttributes(){return[e,t,c,l,u]}connectedCallback(){let n=r(this,l),a=r(this,c);a&&!n&&s(`Missing ${l} attribute on:`,this),n&&!a&&s(`Missing ${c} attribute on:`,this);let u=i(this,e),d=r(this,t)?.trim();u||d?r(this,e,d?null:u):s(`Missing ${e} on:`,this),r(this,`role`,`navigation`),this._unmutate=o(this,p,{childList:!0,subtree:!0})}attributeChangedCallback(){this._unmutate&&p(this)}disconnectedCallback(){this._unmutate?.(),this._unmutate=this._render=void 0}};const p=t=>{let n=Number(r(t,c)),i=Number(r(t,l));if(n&&i){let a=t.querySelectorAll(`button,a`),o=a.length-2,s=r(t,u),{next:c,prev:l,pages:f}=d({current:n,total:i,show:o});a.forEach((t,n)=>{let i=n?a[n+1]?f[n-1]?.page:c:l;r(t,`aria-current`,f[n-1]?.current?`true`:null),r(t,e,`${i??`hidden`}`),r(t,`role`,i?null:`none`),r(t,`tabindex`,i?null:`-1`),t instanceof HTMLButtonElement&&r(t,`value`,`${i}`),s&&t instanceof HTMLAnchorElement&&r(t,`href`,s.replace(`%d`,`${i}`))})}},m=(e,t,n=1/0)=>{let r=(n-1)/2,i=Math.max(Math.min(e-Math.floor(r),t-n+1),1),a=Math.min(Math.max(e+Math.ceil(r),n),t),o=Array.from({length:a+1-i},(e,t)=>t+i);return n>4&&i>1&&o.splice(0,2,1,0),n>3&&a<t&&o.splice(-2,2,0,t),o};a.define(`ds-pagination`,f);export{f as DSPaginationElement,d as pagination};
2
2
  //# sourceMappingURL=pagination.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.js","names":[],"sources":["../../../src/pagination/pagination.ts"],"sourcesContent":["import {\n attr,\n attrOrCSS,\n customElements,\n DSElement,\n onMutation,\n warn,\n} from '../utils/utils';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-pagination': DSPaginationElement;\n }\n}\n\nconst ATTR_LABEL = 'aria-label';\nconst ATTR_CURRENT = 'data-current';\nconst ATTR_TOTAL = 'data-total';\nconst ATTR_HREF = 'data-href';\n\n// Expose pagination logic if wanting to do custom rendering (i.e. in React/Vue/etc)\nexport const pagination = ({ current = 1, total = 10, show = 7 }) => ({\n prev: current > 1 ? current - 1 : 0,\n next: current < total ? current + 1 : 0,\n pages: getSteps(current, total, show).map((page, index) => ({\n current: page === current && ('page' as const),\n key: `key-${page}-${index}`,\n page,\n })),\n});\n\nexport class DSPaginationElement extends DSElement {\n _unmutate?: () => void; // Using underscore instead of private fields for backwards compatibility\n _render?: () => void;\n\n static get observedAttributes() {\n return [ATTR_LABEL, ATTR_CURRENT, ATTR_TOTAL, ATTR_HREF]; // Using ES2015 syntax for backwards compatibility\n }\n connectedCallback() {\n // Check for required attributes\n const total = attr(this, ATTR_TOTAL);\n const current = attr(this, ATTR_CURRENT);\n if (current && !total) warn(`Missing ${ATTR_TOTAL} attribute on:`, this);\n if (total && !current) warn(`Missing ${ATTR_CURRENT} attribute on:`, this);\n\n attr(this, ATTR_LABEL, attrOrCSS(this, ATTR_LABEL));\n attr(this, 'role', 'navigation');\n this._unmutate = onMutation(this, render, {\n childList: true,\n subtree: true,\n });\n }\n attributeChangedCallback() {\n if (this._unmutate) render(this); // Ensure we do not run any renders before connectedCallback\n }\n disconnectedCallback() {\n this._unmutate?.();\n this._unmutate = this._render = undefined;\n }\n}\n\nconst render = (self: DSPaginationElement) => {\n const current = Number(attr(self, ATTR_CURRENT));\n const total = Number(attr(self, ATTR_TOTAL));\n\n // Allowing server side generated pagination, buy only doing client side updates if total/current attributes are provided\n if (current && total) {\n const items = self.querySelectorAll('button,a');\n const show = items.length - 2;\n const href = attr(self, ATTR_HREF);\n const { next, prev, pages } = pagination({ current, total, show });\n items.forEach((item, i) => {\n const page = i ? (items[i + 1] ? pages[i - 1]?.page : next) : prev; // First is prev, last is next\n attr(item, 'aria-current', pages[i - 1]?.current ? 'true' : null);\n attr(item, 'aria-label', `${page ?? 'hidden'}`); // Used for CSS content and to hide if more items than pages, using aria-label to make Axe tests and VoiceOver rotor happy\n attr(item, 'role', page ? null : 'none'); // Prevent validation errors for aria-hidden buttons\n attr(item, 'tabindex', page ? null : '-1');\n if (item instanceof HTMLButtonElement) attr(item, 'value', `${page}`);\n if (href && item instanceof HTMLAnchorElement)\n attr(item, 'href', href.replace('%d', `${page}`));\n });\n }\n};\n\nconst getSteps = (\n now: number,\n max: number,\n show = Number.POSITIVE_INFINITY,\n) => {\n const offset = (show - 1) / 2;\n const start = Math.max(Math.min(now - Math.floor(offset), max - show + 1), 1);\n const end = Math.min(Math.max(now + Math.ceil(offset), show), max);\n const pages = Array.from({ length: end + 1 - start }, (_, i) => i + start);\n\n if (show > 4 && start > 1) pages.splice(0, 2, 1, 0);\n if (show > 3 && end < max) pages.splice(-2, 2, 0, max);\n return pages;\n};\n\ncustomElements.define('ds-pagination', DSPaginationElement);\n"],"mappings":"qHAeA,MAAM,EAAa,aACb,EAAe,eACf,EAAa,aACb,EAAY,YAGL,GAAc,CAAE,UAAU,EAAG,QAAQ,GAAI,OAAO,MAAS,CACpE,KAAM,EAAU,EAAI,EAAU,EAAI,EAClC,KAAM,EAAU,EAAQ,EAAU,EAAI,EACtC,MAAO,EAAS,EAAS,EAAO,CAAI,CAAC,CAAC,KAAK,EAAM,KAAW,CAC1D,QAAS,IAAS,GAAY,OAC9B,IAAK,OAAO,EAAK,GAAG,IACpB,MACF,EAAE,CACJ,GAEA,IAAa,EAAb,cAAyC,CAAU,CACjD,UACA,QAEA,WAAW,oBAAqB,CAC9B,MAAO,CAAC,EAAY,EAAc,EAAY,CAAS,CACzD,CACA,mBAAoB,CAElB,IAAM,EAAQ,EAAK,KAAM,CAAU,EAC7B,EAAU,EAAK,KAAM,CAAY,EACnC,GAAW,CAAC,GAAO,EAAK,WAAW,EAAW,gBAAiB,IAAI,EACnE,GAAS,CAAC,GAAS,EAAK,WAAW,EAAa,gBAAiB,IAAI,EAEzE,EAAK,KAAM,EAAY,EAAU,KAAM,CAAU,CAAC,EAClD,EAAK,KAAM,OAAQ,YAAY,EAC/B,KAAK,UAAY,EAAW,KAAM,EAAQ,CACxC,UAAW,GACX,QAAS,EACX,CAAC,CACH,CACA,0BAA2B,CACrB,KAAK,WAAW,EAAO,IAAI,CACjC,CACA,sBAAuB,CACrB,KAAK,YAAY,EACjB,KAAK,UAAY,KAAK,QAAU,IAAA,EAClC,CACF,EAEA,MAAM,EAAU,GAA8B,CAC5C,IAAM,EAAU,OAAO,EAAK,EAAM,CAAY,CAAC,EACzC,EAAQ,OAAO,EAAK,EAAM,CAAU,CAAC,EAG3C,GAAI,GAAW,EAAO,CACpB,IAAM,EAAQ,EAAK,iBAAiB,UAAU,EACxC,EAAO,EAAM,OAAS,EACtB,EAAO,EAAK,EAAM,CAAS,EAC3B,CAAE,OAAM,OAAM,SAAU,EAAW,CAAE,UAAS,QAAO,MAAK,CAAC,EACjE,EAAM,SAAS,EAAM,IAAM,CACzB,IAAM,EAAO,EAAK,EAAM,EAAI,GAAK,EAAM,EAAI,EAAE,EAAE,KAAO,EAAQ,EAC9D,EAAK,EAAM,eAAgB,EAAM,EAAI,EAAE,EAAE,QAAU,OAAS,IAAI,EAChE,EAAK,EAAM,aAAc,GAAG,GAAQ,UAAU,EAC9C,EAAK,EAAM,OAAQ,EAAO,KAAO,MAAM,EACvC,EAAK,EAAM,WAAY,EAAO,KAAO,IAAI,EACrC,aAAgB,mBAAmB,EAAK,EAAM,QAAS,GAAG,GAAM,EAChE,GAAQ,aAAgB,mBAC1B,EAAK,EAAM,OAAQ,EAAK,QAAQ,KAAM,GAAG,GAAM,CAAC,CACpD,CAAC,CACH,CACF,EAEM,GACJ,EACA,EACA,EAAO,MACJ,CACH,IAAM,GAAU,EAAO,GAAK,EACtB,EAAQ,KAAK,IAAI,KAAK,IAAI,EAAM,KAAK,MAAM,CAAM,EAAG,EAAM,EAAO,CAAC,EAAG,CAAC,EACtE,EAAM,KAAK,IAAI,KAAK,IAAI,EAAM,KAAK,KAAK,CAAM,EAAG,CAAI,EAAG,CAAG,EAC3D,EAAQ,MAAM,KAAK,CAAE,OAAQ,EAAM,EAAI,CAAM,GAAI,EAAG,IAAM,EAAI,CAAK,EAIzE,OAFI,EAAO,GAAK,EAAQ,GAAG,EAAM,OAAO,EAAG,EAAG,EAAG,CAAC,EAC9C,EAAO,GAAK,EAAM,GAAK,EAAM,OAAO,GAAI,EAAG,EAAG,CAAG,EAC9C,CACT,EAEA,EAAe,OAAO,gBAAiB,CAAmB"}
1
+ {"version":3,"file":"pagination.js","names":[],"sources":["../../../src/pagination/pagination.ts"],"sourcesContent":["import {\n ARIA_LABEL,\n ARIA_LABELLEDBY,\n attr,\n attrOrCSS,\n customElements,\n DSElement,\n onMutation,\n warn,\n} from '../utils/utils';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-pagination': DSPaginationElement;\n }\n}\n\nconst ATTR_CURRENT = 'data-current';\nconst ATTR_TOTAL = 'data-total';\nconst ATTR_HREF = 'data-href';\n\n// Expose pagination logic if wanting to do custom rendering (i.e. in React/Vue/etc)\nexport const pagination = ({ current = 1, total = 10, show = 7 }) => ({\n prev: current > 1 ? current - 1 : 0,\n next: current < total ? current + 1 : 0,\n pages: getSteps(current, total, show).map((page, index) => ({\n current: page === current && ('page' as const),\n key: `key-${page}-${index}`,\n page,\n })),\n});\n\nexport class DSPaginationElement extends DSElement {\n _unmutate?: () => void; // Using underscore instead of private fields for backwards compatibility\n _render?: () => void;\n\n static get observedAttributes() {\n return [ARIA_LABEL, ARIA_LABELLEDBY, ATTR_CURRENT, ATTR_TOTAL, ATTR_HREF]; // Using ES2015 syntax for backwards compatibility\n }\n connectedCallback() {\n // Check for required attributes\n const total = attr(this, ATTR_TOTAL);\n const current = attr(this, ATTR_CURRENT);\n if (current && !total) warn(`Missing ${ATTR_TOTAL} attribute on:`, this);\n if (total && !current) warn(`Missing ${ATTR_CURRENT} attribute on:`, this);\n\n const label = attrOrCSS(this, ARIA_LABEL);\n const labelledby = attr(this, ARIA_LABELLEDBY)?.trim();\n if (label || labelledby) attr(this, ARIA_LABEL, labelledby ? null : label);\n else warn(`Missing ${ARIA_LABEL} on:`, this);\n\n attr(this, 'role', 'navigation');\n this._unmutate = onMutation(this, render, {\n childList: true,\n subtree: true,\n });\n }\n attributeChangedCallback() {\n if (this._unmutate) render(this); // Ensure we do not run any renders before connectedCallback\n }\n disconnectedCallback() {\n this._unmutate?.();\n this._unmutate = this._render = undefined;\n }\n}\n\nconst render = (self: DSPaginationElement) => {\n const current = Number(attr(self, ATTR_CURRENT));\n const total = Number(attr(self, ATTR_TOTAL));\n\n // Allowing server side generated pagination, buy only doing client side updates if total/current attributes are provided\n if (current && total) {\n const items = self.querySelectorAll('button,a');\n const show = items.length - 2;\n const href = attr(self, ATTR_HREF);\n const { next, prev, pages } = pagination({ current, total, show });\n items.forEach((item, i) => {\n const page = i ? (items[i + 1] ? pages[i - 1]?.page : next) : prev; // First is prev, last is next\n attr(item, 'aria-current', pages[i - 1]?.current ? 'true' : null);\n attr(item, ARIA_LABEL, `${page ?? 'hidden'}`); // Used for CSS content and to hide if more items than pages, using aria-label to make Axe tests and VoiceOver rotor happy\n attr(item, 'role', page ? null : 'none'); // Prevent validation errors for aria-hidden buttons\n attr(item, 'tabindex', page ? null : '-1');\n if (item instanceof HTMLButtonElement) attr(item, 'value', `${page}`);\n if (href && item instanceof HTMLAnchorElement)\n attr(item, 'href', href.replace('%d', `${page}`));\n });\n }\n};\n\nconst getSteps = (\n now: number,\n max: number,\n show = Number.POSITIVE_INFINITY,\n) => {\n const offset = (show - 1) / 2;\n const start = Math.max(Math.min(now - Math.floor(offset), max - show + 1), 1);\n const end = Math.min(Math.max(now + Math.ceil(offset), show), max);\n const pages = Array.from({ length: end + 1 - start }, (_, i) => i + start);\n\n if (show > 4 && start > 1) pages.splice(0, 2, 1, 0);\n if (show > 3 && end < max) pages.splice(-2, 2, 0, max);\n return pages;\n};\n\ncustomElements.define('ds-pagination', DSPaginationElement);\n"],"mappings":"0JAiBA,MAAM,EAAe,eACf,EAAa,aACb,EAAY,YAGL,GAAc,CAAE,UAAU,EAAG,QAAQ,GAAI,OAAO,MAAS,CACpE,KAAM,EAAU,EAAI,EAAU,EAAI,EAClC,KAAM,EAAU,EAAQ,EAAU,EAAI,EACtC,MAAO,EAAS,EAAS,EAAO,CAAI,CAAC,CAAC,KAAK,EAAM,KAAW,CAC1D,QAAS,IAAS,GAAY,OAC9B,IAAK,OAAO,EAAK,GAAG,IACpB,MACF,EAAE,CACJ,GAEA,IAAa,EAAb,cAAyC,CAAU,CACjD,UACA,QAEA,WAAW,oBAAqB,CAC9B,MAAO,CAAC,EAAY,EAAiB,EAAc,EAAY,CAAS,CAC1E,CACA,mBAAoB,CAElB,IAAM,EAAQ,EAAK,KAAM,CAAU,EAC7B,EAAU,EAAK,KAAM,CAAY,EACnC,GAAW,CAAC,GAAO,EAAK,WAAW,EAAW,gBAAiB,IAAI,EACnE,GAAS,CAAC,GAAS,EAAK,WAAW,EAAa,gBAAiB,IAAI,EAEzE,IAAM,EAAQ,EAAU,KAAM,CAAU,EAClC,EAAa,EAAK,KAAM,CAAe,CAAC,EAAE,KAAK,EACjD,GAAS,EAAY,EAAK,KAAM,EAAY,EAAa,KAAO,CAAK,EACpE,EAAK,WAAW,EAAW,MAAO,IAAI,EAE3C,EAAK,KAAM,OAAQ,YAAY,EAC/B,KAAK,UAAY,EAAW,KAAM,EAAQ,CACxC,UAAW,GACX,QAAS,EACX,CAAC,CACH,CACA,0BAA2B,CACrB,KAAK,WAAW,EAAO,IAAI,CACjC,CACA,sBAAuB,CACrB,KAAK,YAAY,EACjB,KAAK,UAAY,KAAK,QAAU,IAAA,EAClC,CACF,EAEA,MAAM,EAAU,GAA8B,CAC5C,IAAM,EAAU,OAAO,EAAK,EAAM,CAAY,CAAC,EACzC,EAAQ,OAAO,EAAK,EAAM,CAAU,CAAC,EAG3C,GAAI,GAAW,EAAO,CACpB,IAAM,EAAQ,EAAK,iBAAiB,UAAU,EACxC,EAAO,EAAM,OAAS,EACtB,EAAO,EAAK,EAAM,CAAS,EAC3B,CAAE,OAAM,OAAM,SAAU,EAAW,CAAE,UAAS,QAAO,MAAK,CAAC,EACjE,EAAM,SAAS,EAAM,IAAM,CACzB,IAAM,EAAO,EAAK,EAAM,EAAI,GAAK,EAAM,EAAI,EAAE,EAAE,KAAO,EAAQ,EAC9D,EAAK,EAAM,eAAgB,EAAM,EAAI,EAAE,EAAE,QAAU,OAAS,IAAI,EAChE,EAAK,EAAM,EAAY,GAAG,GAAQ,UAAU,EAC5C,EAAK,EAAM,OAAQ,EAAO,KAAO,MAAM,EACvC,EAAK,EAAM,WAAY,EAAO,KAAO,IAAI,EACrC,aAAgB,mBAAmB,EAAK,EAAM,QAAS,GAAG,GAAM,EAChE,GAAQ,aAAgB,mBAC1B,EAAK,EAAM,OAAQ,EAAK,QAAQ,KAAM,GAAG,GAAM,CAAC,CACpD,CAAC,CACH,CACF,EAEM,GACJ,EACA,EACA,EAAO,MACJ,CACH,IAAM,GAAU,EAAO,GAAK,EACtB,EAAQ,KAAK,IAAI,KAAK,IAAI,EAAM,KAAK,MAAM,CAAM,EAAG,EAAM,EAAO,CAAC,EAAG,CAAC,EACtE,EAAM,KAAK,IAAI,KAAK,IAAI,EAAM,KAAK,KAAK,CAAM,EAAG,CAAI,EAAG,CAAG,EAC3D,EAAQ,MAAM,KAAK,CAAE,OAAQ,EAAM,EAAI,CAAM,GAAI,EAAG,IAAM,EAAI,CAAK,EAIzE,OAFI,EAAO,GAAK,EAAQ,GAAG,EAAM,OAAO,EAAG,EAAG,EAAG,CAAC,EAC9C,EAAO,GAAK,EAAM,GAAK,EAAM,OAAO,GAAI,EAAG,EAAG,CAAG,EAC9C,CACT,EAEA,EAAe,OAAO,gBAAiB,CAAmB"}
@@ -1,2 +1,2 @@
1
- import{QUICK_EVENT as e,attr as t,getCSSProp as n,isBrowser as r,on as i,onHotReload as a}from"../utils/utils.js";import{apply as o,isSupported as s}from"../_vendors/@oddbird/popover-polyfill/dist/popover-fn.js";import{autoUpdate as c,computePosition as l,flip as u,limitShift as d,offset as f,shift as p,size as m}from"@floating-ui/dom";r()&&!s()&&o();const h=`data-placement`,g=`data-autoplacement`,_=new Map;function v(e){let{newState:r,oldState:i,target:a,source:o=e.detail}=e,s=a instanceof HTMLElement&&t(a,`popover`)!==null&&n(a,`--_ds-floating`);if(!s)return;if(r===`closed`)return _.get(a)?.();if(!o){let e=a.getRootNode(),t=`[popovertarget="${a.id}"],[commandfor="${a.id}"]`;o=a.id&&e?.querySelector?.(t)||void 0}if(!o||o===a||i&&i===r)return;a.style.scrollMarginBottom=`var(--_ds-floating-arrow-size)`;let v=n(a,`--_ds-floating-overscroll`),y=t(a,h)||t(o,h)||s,b=t(a,g)||t(o,g),S=parseFloat(n(a,`scroll-margin-bottom`))||0,C=y.match(/left|right/gi)?`Height`:`Width`,w=o[`offset${C}`]/2+S;if(y===`none`)return;let T={strategy:`absolute`,placement:y,middleware:[f(S),p({padding:10,limiter:d({offset:{mainAxis:w}})}),x(),...b===`false`?[]:[u({padding:10,crossAxis:!1})],...v?[m({apply({availableHeight:e}){v===`fit`&&(a.style.width=`${o.offsetWidth}px`),a.style.maxHeight=`${Math.max(50,e-20)}px`}})]:[]]},E=c(o,a,async()=>{if(!o?.isConnected)return _.get(a)?.();let{x:e,y:t}=await l(o,a,T);a.style.translate=`${e}px ${t}px`});_.set(a,()=>_.delete(a)&&E())}let y;const b=({type:e})=>{if(e===`mousedown`&&(y=!1),e===`scroll`&&y===!1&&(y=!0),e===`mouseup`&&y)for(let[e]of _)e.showPopover()};a(`popover`,()=>[i(document,`mousedown scroll mouseup`,b,!0),i(document,`toggle ds-toggle-source`,v,e)]);const x=()=>({name:`arrowPseudo`,fn(e){let n=e.elements.floating,r=e.rects.reference,i=`${Math.round(r.width/2+r.x-e.x)}px`,a=`${Math.round(r.height/2+r.y-e.y)}px`;return n.style.setProperty(`--_ds-floating-arrow-x`,i),n.style.setProperty(`--_ds-floating-arrow-y`,a),t(n,`data-floating`,e.placement),e}});
1
+ import{QUICK_EVENT as e,attr as t,getCSSProp as n,getComposedTarget as r,getRoot as i,isBrowser as a,on as o,onHotReload as s}from"../utils/utils.js";import{apply as c,isPolyfilled as l,isSupported as u}from"../_vendors/@oddbird/popover-polyfill/dist/popover-fn.js";import{autoUpdate as d,computePosition as f,flip as p,limitShift as m,offset as h,shift as g,size as _}from"@floating-ui/dom";a()&&!u()&&!l()&&c({layerName:`ds.base`});const v=new Map,y=`data-placement`,b=`data-autoplacement`,x=new Map,S=e=>C(r(e),e.newState,e.oldState,e.source||e.detail);function C(e,r,a,o){let s=e instanceof HTMLElement&&t(e,`popover`)!==null&&n(e,`--_ds-floating`),c=x.get(e);if(r===`open`&&c&&c.source===o||(c?.cleanup(),r===`closed`||!s))return;if(!o){let t=e.id&&`[popovertarget="${e.id}"],[commandfor="${e.id}"]`;o=t&&i(e).querySelector(t)||void 0}if(!o||o===e||a&&a===r)return;e.style.scrollMarginBottom=`var(--_ds-floating-arrow-size)`;let l=n(e,`--_ds-floating-overscroll`),u=t(e,y)||t(o,y)||s,v=t(e,b)||t(o,b),S=parseFloat(n(e,`scroll-margin-bottom`))||0,C=u.match(/left|right/gi)?`Height`:`Width`,w=o[`offset${C}`]/2+S;if(u===`none`)return;let T={strategy:`absolute`,placement:u,middleware:[h(S),g({padding:10,limiter:m({offset:{mainAxis:w}})}),D(),...v===`false`?[]:[p({padding:10,crossAxis:!1})],...l?[_({apply({availableHeight:t}){l===`fit`&&(e.style.width=`${o.offsetWidth}px`),e.style.maxHeight=`${Math.max(50,t-20)}px`}})]:[]]},E=d(o,e,async()=>{if(!o?.isConnected)return x.get(e)?.cleanup();let{x:t,y:n}=await f(o,e,T);e.style.translate=`${t}px ${n}px`});x.set(e,{source:o,cleanup:()=>x.delete(e)&&E()})}let w;const T=e=>{if(e.type===`pointerdown`&&(w=!1),e.type===`scroll`&&w===!1&&(w=!0),e.type===`pointerup`&&w)for(let[e]of x)e.showPopover()},E=t=>{for(let[e,t]of v)!e.host?.isConnected&&v.delete(e)&&t();let n=i(r(t));n instanceof ShadowRoot&&!v.has(n)&&v.set(n,o(n,`toggle`,S,e))};s(`popover`,()=>{let t=e=>typeof e==`function`,n=HTMLElement.prototype.togglePopover,r=HTMLElement.prototype.showPopover,i=HTMLElement.prototype.hidePopover;return t(n)&&(HTMLElement.prototype.togglePopover=function(e){let t=this.matches(`:popover-open`),r=typeof e==`boolean`,i=t?`open`:`closed`,a=(r?e:e?.force??!t)?`open`:`closed`,o=r?void 0:e?.source,s=n.call(this,e);return C(this,a,i,o),s}),t(r)&&(HTMLElement.prototype.showPopover=function(e){let t=this.matches(`:popover-open`)?`open`:`closed`,n=r.call(this,e);return C(this,`open`,t,e?.source),n}),t(i)&&(HTMLElement.prototype.hidePopover=function(){let e=this.matches(`:popover-open`)?`open`:`closed`,t=i.call(this);return C(this,`closed`,e),t}),[o(document,`click`,E,e),o(document,`pointerdown pointerup scroll`,T,e),o(document,`toggle ds-toggle-source`,S,e),()=>{HTMLElement.prototype.togglePopover=n,HTMLElement.prototype.showPopover=r,HTMLElement.prototype.hidePopover=i;for(let[,e]of v)e();v.clear()}]});const D=()=>({name:`arrowPseudo`,fn(e){let n=e.elements.floating,r=e.rects.reference,i=`${Math.round(r.width/2+r.x-e.x)}px`,a=`${Math.round(r.height/2+r.y-e.y)}px`;return n.style.setProperty(`--_ds-floating-arrow-x`,i),n.style.setProperty(`--_ds-floating-arrow-y`,a),t(n,`data-floating`,e.placement),e}});
2
2
  //# sourceMappingURL=popover.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"popover.js","names":[],"sources":["../../../src/popover/popover.ts"],"sourcesContent":["import type { ComputePositionConfig, MiddlewareState } from '@floating-ui/dom';\nimport {\n autoUpdate,\n computePosition,\n flip,\n limitShift,\n offset,\n shift,\n size,\n} from '@floating-ui/dom';\nimport {\n isSupported,\n apply as polyfillPopover,\n} from '@oddbird/popover-polyfill/fn';\nimport {\n attr,\n getCSSProp,\n isBrowser,\n on,\n onHotReload,\n QUICK_EVENT,\n} from '../utils/utils';\n\nif (isBrowser() && !isSupported()) polyfillPopover(); // Ensure popover polyfill is loaded in browser environment only\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n 'ds-toggle-source': CustomEvent<Element>;\n }\n}\n\nconst ATTR_PLACE = 'data-placement';\nconst ATTR_AUTO = 'data-autoplacement';\nconst POPOVERS = new Map<HTMLElement, () => void>();\n\n// Sometimes use \"ds-toggle\" event while waiting for better support of\n// event.source (https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent/source)\nfunction handleToggle(\n event: Partial<ToggleEvent> & {\n detail?: HTMLElement;\n source?: HTMLElement;\n },\n) {\n let { newState, oldState, target: el, source = event.detail } = event;\n const isPopover = el instanceof HTMLElement && attr(el, 'popover') !== null;\n const float = isPopover && getCSSProp(el, '--_ds-floating');\n\n if (!float) return;\n if (newState === 'closed') return POPOVERS.get(el)?.(); // Cleanup on close\n if (!source) {\n const root = el.getRootNode() as Document; // Support shadow DOM\n const css = `[popovertarget=\"${el.id}\"],[commandfor=\"${el.id}\"]`;\n source = (el.id && root?.querySelector?.<HTMLElement>(css)) || undefined; // Polyfill ToggleEvent .source for older browsers\n }\n if (!source || source === el || (oldState && oldState === newState)) return; // No need to update\n\n // Use scroll-margin-bottom to measure computed arrow-size property as this does\n // not affect layout or position, makes the browser calculate the pixel value instead\n // of returning the calc() (as it would if reading the --_ds-floating-arrow-size directly)\n // and makes it possible to read the value even if ::before is not used to draw the arrow.\n el.style.scrollMarginBottom = `var(--_ds-floating-arrow-size)`;\n\n const padding = 10;\n const overscroll = getCSSProp(el, '--_ds-floating-overscroll');\n const placement = attr(el, ATTR_PLACE) || attr(source, ATTR_PLACE) || float;\n const auto = attr(el, ATTR_AUTO) || attr(source, ATTR_AUTO);\n const arrowSize = parseFloat(getCSSProp(el, 'scroll-margin-bottom')) || 0;\n const shiftProp = placement.match(/left|right/gi) ? 'Height' : 'Width';\n const shiftLimit = source[`offset${shiftProp}`] / 2 + arrowSize;\n\n if (placement === 'none') return; // No need to position\n\n const options = {\n strategy: 'absolute',\n placement,\n middleware: [\n offset(arrowSize),\n shift({\n padding,\n limiter: limitShift({ offset: { mainAxis: shiftLimit } }), // Prevent from shifing away from source\n }),\n arrowPseudo(),\n ...(auto !== 'false' ? [flip({ padding, crossAxis: false })] : []),\n ...(overscroll\n ? [\n size({\n apply({ availableHeight }) {\n if (overscroll === 'fit')\n el.style.width = `${source.offsetWidth}px`; // Use offsetWidth to include padding, matching the width of the source element\n el.style.maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;\n },\n }),\n ]\n : []),\n ],\n } as ComputePositionConfig;\n const unfloat = autoUpdate(source, el, async () => {\n if (!source?.isConnected) return POPOVERS.get(el)?.(); // Cleanup if source element is removed\n const { x, y } = await computePosition(source, el, options);\n el.style.translate = `${x}px ${y}px`;\n });\n POPOVERS.set(el, () => POPOVERS.delete(el) && unfloat());\n}\n\n// Prevent closing when mouse interacts with scrollbar\nlet IS_SCROLL: boolean | undefined;\nconst handleScrollbar = ({ type }: Event) => {\n if (type === 'mousedown') IS_SCROLL = false;\n if (type === 'scroll' && IS_SCROLL === false) IS_SCROLL = true;\n if (type === 'mouseup' && IS_SCROLL)\n for (const [popover] of POPOVERS) popover.showPopover(); // Immediately show again to prevent flicker\n};\n\nonHotReload('popover', () => [\n on(document, 'mousedown scroll mouseup', handleScrollbar, true),\n on(document, 'toggle ds-toggle-source', handleToggle, QUICK_EVENT), // Use capture since the toggle event does not bubble\n]);\n\nconst arrowPseudo = () => ({\n name: 'arrowPseudo',\n fn(data: MiddlewareState) {\n const target = data.elements.floating;\n const source = data.rects.reference;\n const x = `${Math.round(source.width / 2 + source.x - data.x)}px`;\n const y = `${Math.round(source.height / 2 + source.y - data.y)}px`;\n\n target.style.setProperty('--_ds-floating-arrow-x', x);\n target.style.setProperty('--_ds-floating-arrow-y', y);\n attr(target, 'data-floating', data.placement);\n return data;\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"popover.js","names":[],"sources":["../../../src/popover/popover.ts"],"sourcesContent":["import type { ComputePositionConfig, MiddlewareState } from '@floating-ui/dom';\nimport {\n autoUpdate,\n computePosition,\n flip,\n limitShift,\n offset,\n shift,\n size,\n} from '@floating-ui/dom';\nimport {\n isPolyfilled,\n isSupported,\n apply as polyfillPopover,\n} from '@oddbird/popover-polyfill/fn';\nimport {\n attr,\n getComposedTarget,\n getCSSProp,\n getRoot,\n isBrowser,\n on,\n onHotReload,\n QUICK_EVENT,\n} from '../utils/utils';\n\nif (isBrowser() && !isSupported() && !isPolyfilled())\n polyfillPopover({ layerName: 'ds.base' }); // Load popover polyfill in the ds.base CSS layer to keep cascade order consistent with Designsystemet layers.\n\n// NOTE:\n// The native popover event \"toggle\" is not composed, meaning it does not bubble out of shadow DOM.\n// This means that if a popover is inside a shadow root, the toggle event will not be visible outside of that shadow root.\n// To handle this, we monkey-patch the showPopover and hidePopover methods to manually trigger the toggle-function.\n// We also bind toggle events to shadow roots found during click events, so that we can listen for toggle events on shadow roots as well.\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n 'ds-toggle-source': CustomEvent<Element>;\n }\n}\n\nconst ROOTS = new Map<ShadowRoot, () => void>();\nconst ATTR_PLACE = 'data-placement';\nconst ATTR_AUTO = 'data-autoplacement';\nconst POPOVERS = new Map<\n HTMLElement,\n { source: Element; cleanup: () => void }\n>();\n\n// Sometimes use \"ds-toggle\" event while waiting for better support of\n// event.source (https://developer.mozilla.org/en-US/docs/Web/API/ToggleEvent/source)\n\nconst handleToggle = (\n e: Event &\n Partial<ToggleEvent> & { detail?: HTMLElement; source?: HTMLElement },\n) => toggle(getComposedTarget(e), e.newState, e.oldState, e.source || e.detail);\n\nfunction toggle(\n el: Element | null,\n newState?: string,\n oldState?: string,\n source?: HTMLElement,\n) {\n const isPopover = el instanceof HTMLElement && attr(el, 'popover') !== null;\n const float = isPopover && getCSSProp(el, '--_ds-floating');\n const prev = POPOVERS.get(el as HTMLElement);\n\n if (newState === 'open' && prev && prev.source === source) return; // Prevent double binding\n prev?.cleanup(); // Cleanup if previously bound popover exists to avoid multiple autoUpdate memory leaks\n if (newState === 'closed' || !float) return;\n if (!source) {\n const css = el.id && `[popovertarget=\"${el.id}\"],[commandfor=\"${el.id}\"]`;\n source = (css && getRoot(el).querySelector<HTMLElement>(css)) || undefined; // Polyfill ToggleEvent .source for older browsers\n }\n if (!source || source === el || (oldState && oldState === newState)) return; // No need to update\n\n // Use scroll-margin-bottom to measure computed arrow-size property as this does\n // not affect layout or position, makes the browser calculate the pixel value instead\n // of returning the calc() (as it would if reading the --_ds-floating-arrow-size directly)\n // and makes it possible to read the value even if ::before is not used to draw the arrow.\n el.style.scrollMarginBottom = `var(--_ds-floating-arrow-size)`;\n\n const padding = 10;\n const overscroll = getCSSProp(el, '--_ds-floating-overscroll');\n const placement = attr(el, ATTR_PLACE) || attr(source, ATTR_PLACE) || float;\n const auto = attr(el, ATTR_AUTO) || attr(source, ATTR_AUTO);\n const arrowSize = parseFloat(getCSSProp(el, 'scroll-margin-bottom')) || 0;\n const shiftProp = placement.match(/left|right/gi) ? 'Height' : 'Width';\n const shiftLimit = source[`offset${shiftProp}`] / 2 + arrowSize;\n\n if (placement === 'none') return; // No need to position\n\n const options = {\n strategy: 'absolute',\n placement,\n middleware: [\n offset(arrowSize),\n shift({\n padding,\n limiter: limitShift({ offset: { mainAxis: shiftLimit } }), // Prevent from shifing away from source\n }),\n arrowPseudo(),\n ...(auto !== 'false' ? [flip({ padding, crossAxis: false })] : []),\n ...(overscroll\n ? [\n size({\n apply({ availableHeight }) {\n if (overscroll === 'fit')\n el.style.width = `${source.offsetWidth}px`; // Use offsetWidth to include padding, matching the width of the source element\n el.style.maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;\n },\n }),\n ]\n : []),\n ],\n } as ComputePositionConfig;\n const unfloat = autoUpdate(source, el, async () => {\n if (!source?.isConnected) return POPOVERS.get(el)?.cleanup(); // Cleanup if source element is removed\n const { x, y } = await computePosition(source, el, options);\n el.style.translate = `${x}px ${y}px`;\n });\n POPOVERS.set(el, {\n source,\n cleanup: () => POPOVERS.delete(el) && unfloat(),\n });\n}\n\n// Prevent closing when pointer interacts with scrollbar\nlet IS_SCROLL: boolean | undefined;\nconst handleScrollbar = (e: Event) => {\n if (e.type === 'pointerdown') {\n IS_SCROLL = false;\n }\n if (e.type === 'scroll' && IS_SCROLL === false) IS_SCROLL = true;\n if (e.type === 'pointerup' && IS_SCROLL)\n for (const [popover] of POPOVERS) popover.showPopover(); // Immediately show again to prevent flicker\n};\n\n// And add listeners for toggle event on shadowRoots as \"toggle\" is not a composed event\nconst handleClick = (e: Event) => {\n for (const [root, off] of ROOTS)\n if (!root.host?.isConnected && ROOTS.delete(root)) off(); // Prune roots whose host is no longer connected (e.g. removed custom elements) to avoid unbounded growth\n\n const root = getRoot(getComposedTarget(e));\n if (root instanceof ShadowRoot && !ROOTS.has(root))\n ROOTS.set(root, on(root, 'toggle', handleToggle, QUICK_EVENT));\n};\n\nonHotReload('popover', () => {\n const isFunction = (fn: unknown) => typeof fn === 'function';\n const togglePopover = HTMLElement.prototype.togglePopover;\n const showPopover = HTMLElement.prototype.showPopover;\n const hidePopover = HTMLElement.prototype.hidePopover;\n\n // Since toggle event is not composed, we need to trigger it when programatically called inside shadow DOM\n if (isFunction(togglePopover))\n HTMLElement.prototype.togglePopover = function (opt) {\n const isOpen = this.matches(':popover-open');\n const isBool = typeof opt === 'boolean';\n const prev = isOpen ? 'open' : 'closed';\n const next = (isBool ? opt : (opt?.force ?? !isOpen)) ? 'open' : 'closed';\n const source = isBool ? undefined : opt?.source;\n const result = togglePopover.call(this, opt as TogglePopoverOptions);\n toggle(this, next, prev, source);\n return result;\n };\n if (isFunction(showPopover))\n HTMLElement.prototype.showPopover = function (opt) {\n const prev = this.matches(':popover-open') ? 'open' : 'closed';\n const result = showPopover.call(this, opt);\n toggle(this, 'open', prev, opt?.source);\n return result;\n };\n if (isFunction(hidePopover))\n HTMLElement.prototype.hidePopover = function () {\n const prev = this.matches(':popover-open') ? 'open' : 'closed';\n const result = hidePopover.call(this);\n toggle(this, 'closed', prev);\n return result;\n };\n\n return [\n on(document, 'click', handleClick, QUICK_EVENT),\n on(document, 'pointerdown pointerup scroll', handleScrollbar, QUICK_EVENT),\n on(document, 'toggle ds-toggle-source', handleToggle, QUICK_EVENT), // Use capture since the toggle event does not bubble\n () => {\n HTMLElement.prototype.togglePopover = togglePopover; // Restore original methods on hot reload\n HTMLElement.prototype.showPopover = showPopover;\n HTMLElement.prototype.hidePopover = hidePopover;\n for (const [, off] of ROOTS) off(); // Cleanup listeners on ShadowRoots on hot reload\n ROOTS.clear();\n },\n ];\n});\n\nconst arrowPseudo = () => ({\n name: 'arrowPseudo',\n fn(data: MiddlewareState) {\n const target = data.elements.floating;\n const source = data.rects.reference;\n const x = `${Math.round(source.width / 2 + source.x - data.x)}px`;\n const y = `${Math.round(source.height / 2 + source.y - data.y)}px`;\n\n target.style.setProperty('--_ds-floating-arrow-x', x);\n target.style.setProperty('--_ds-floating-arrow-y', y);\n attr(target, 'data-floating', data.placement);\n return data;\n },\n});\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{attr as e,on as t,onHotReload as n}from"../utils/utils.js";const r=t=>(t instanceof HTMLSelectElement||t instanceof HTMLInputElement)&&(t.hasAttribute(`readonly`)||e(t,`aria-readonly`)===`true`),i=t=>{if(t.key!==`Tab`&&r(t.target)){let n=t.key?.startsWith(`Arrow`),r=t.altKey||t.ctrlKey||t.metaKey;if((n||!r)&&t.preventDefault(),n&&e(t.target,`type`)===`radio`){let e=document.querySelectorAll(`input[name="${t.target.name}"]`),n=t.key?.match(/Arrow(Right|Down)/)?1:-1;e[(e.length+[...e].indexOf(t.target)+n)%e.length]?.focus()}}},a=e=>{let t=e.target?.closest?.(`label`)?.control||e.target;r(t)&&(e.preventDefault(),t.focus())},o=e=>{e.target instanceof HTMLSelectElement&&r(e.target)&&e.preventDefault()};n(`readonly`,()=>[t(document,`keydown`,i),t(document,`click`,a),t(document,`mousedown`,o)]);
1
+ import{attr as e,getComposedTarget as t,on as n,onHotReload as r}from"../utils/utils.js";const i=t=>e(t,`readonly`)!==null||e(t,`aria-readonly`)===`true`,a=e=>{(!(e.key===`Tab`||e.altKey||e.ctrlKey||e.metaKey)||e.key?.startsWith(`Arrow`))&&o(e)},o=e=>{let n=t(e);n?.nodeName===`SELECT`&&i(n)&&e.preventDefault()},s=e=>{for(let t of e.composedPath())if(t?.nodeName===`LABEL`&&(t=t.control),t?.nodeName===`INPUT`||t?.nodeName===`SELECT`){i(t)&&(e.stopImmediatePropagation(),e.preventDefault(),t[t.nodeName===`SELECT`?`blur`:`focus`](),requestAnimationFrame(()=>t.isConnected&&t.focus()));return}};r(`readonly`,()=>[n(document,`keydown`,a),n(document,`click`,s,!0),n(document,`mousedown`,o,!0)]);
2
2
  //# sourceMappingURL=readonly.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"readonly.js","names":[],"sources":["../../../src/readonly/readonly.ts"],"sourcesContent":["import { attr, on, onHotReload } from '../utils/utils';\n\nconst isReadOnly = (el: unknown): el is HTMLInputElement | HTMLSelectElement =>\n (el instanceof HTMLSelectElement || el instanceof HTMLInputElement) &&\n (el.hasAttribute('readonly') || attr(el, 'aria-readonly') === 'true');\n\n// Allow tabbing when readonly, and only fix readonly input/select elements (since type select and non-text-inputs do not support readonly)\n// If radio buttons, move focus without changing checked state\nconst handleKeyDown = (e: Event & Partial<KeyboardEvent>) => {\n if (e.key !== 'Tab' && isReadOnly(e.target)) {\n const isArrow = e.key?.startsWith('Arrow'); // Always control arrow keys\n const isModifier = e.altKey || e.ctrlKey || e.metaKey; // Allow modifier keys so native functions like CMD + D to bookmark etc. still works\n\n if (isArrow || !isModifier) e.preventDefault(); // Prevent changing <select> value with keyboard, but allow non-arrow modifier keys\n if (isArrow && attr(e.target, 'type') === 'radio') {\n const all = document.querySelectorAll(`input[name=\"${e.target.name}\"]`);\n const move = e.key?.match(/Arrow(Right|Down)/) ? 1 : -1;\n const next = all.length + [...all].indexOf(e.target) + move;\n (all[next % all.length] as HTMLElement)?.focus();\n }\n }\n};\n\nconst handleClick = (e: Event) => {\n const input = (e.target as Element)?.closest?.('label')?.control || e.target;\n if (isReadOnly(input)) {\n e.preventDefault();\n input.focus();\n }\n};\n\nconst handleMouseDown = (e: Event) => {\n if (e.target instanceof HTMLSelectElement && isReadOnly(e.target))\n e.preventDefault();\n};\n\nonHotReload('readonly', () => [\n on(document, 'keydown', handleKeyDown),\n on(document, 'click', handleClick), // click needed for <label> and <input>\n on(document, 'mousedown', handleMouseDown), // mousedown needed for <select>\n]);\n"],"mappings":"kEAEA,MAAM,EAAc,IACjB,aAAc,mBAAqB,aAAc,oBACjD,EAAG,aAAa,UAAU,GAAK,EAAK,EAAI,eAAe,IAAM,QAI1D,EAAiB,GAAsC,CAC3D,GAAI,EAAE,MAAQ,OAAS,EAAW,EAAE,MAAM,EAAG,CAC3C,IAAM,EAAU,EAAE,KAAK,WAAW,OAAO,EACnC,EAAa,EAAE,QAAU,EAAE,SAAW,EAAE,QAG9C,IADI,GAAW,CAAC,IAAY,EAAE,eAAe,EACzC,GAAW,EAAK,EAAE,OAAQ,MAAM,IAAM,QAAS,CACjD,IAAM,EAAM,SAAS,iBAAiB,eAAe,EAAE,OAAO,KAAK,GAAG,EAChE,EAAO,EAAE,KAAK,MAAM,mBAAmB,EAAI,EAAI,GAErD,GADa,EAAI,OAAS,CAAC,GAAG,CAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAI,GAC3C,EAAI,OAAO,EAAkB,MAAM,CACjD,CACF,CACF,EAEM,EAAe,GAAa,CAChC,IAAM,EAAS,EAAE,QAAoB,UAAU,OAAO,CAAC,EAAE,SAAW,EAAE,OAClE,EAAW,CAAK,IAClB,EAAE,eAAe,EACjB,EAAM,MAAM,EAEhB,EAEM,EAAmB,GAAa,CAChC,EAAE,kBAAkB,mBAAqB,EAAW,EAAE,MAAM,GAC9D,EAAE,eAAe,CACrB,EAEA,EAAY,eAAkB,CAC5B,EAAG,SAAU,UAAW,CAAa,EACrC,EAAG,SAAU,QAAS,CAAW,EACjC,EAAG,SAAU,YAAa,CAAe,CAC3C,CAAC"}
1
+ {"version":3,"file":"readonly.js","names":[],"sources":["../../../src/readonly/readonly.ts"],"sourcesContent":["import { attr, getComposedTarget, on, onHotReload } from '../utils/utils';\n\nconst isReadOnly = (el: Element) =>\n attr(el, 'readonly') !== null || attr(el, 'aria-readonly') === 'true';\n\nconst handleKeydown = (e: Event & Partial<KeyboardEvent>) => {\n const allow = e.key === 'Tab' || e.altKey || e.ctrlKey || e.metaKey; // Allow modifier keys so native functions like CMD + D to bookmark etc. still works\n if (!allow || e.key?.startsWith('Arrow')) handleSelect(e);\n};\n\nconst handleSelect = (e: Event) => {\n const el = getComposedTarget(e) as HTMLSelectElement | null;\n if (el?.nodeName === 'SELECT' && isReadOnly(el)) e.preventDefault();\n};\n\nconst handleClick = (e: Event) => {\n for (let el of e.composedPath() as HTMLLabelElement[]) {\n if (el?.nodeName === 'LABEL') el = el.control as HTMLLabelElement;\n if (el?.nodeName === 'INPUT' || el?.nodeName === 'SELECT') {\n if (isReadOnly(el)) {\n e.stopImmediatePropagation(); // Prevent click from reaching React and other listeners\n e.preventDefault();\n el[el.nodeName === 'SELECT' ? 'blur' : 'focus'](); // Blur select to prevent opening on mobile, focus input to recreate <label>-click behavior\n requestAnimationFrame(() => el.isConnected && el.focus()); // Move focus back to select after event has finished bubbling\n }\n return;\n }\n }\n};\n\nonHotReload('readonly', () => [\n on(document, 'keydown', handleKeydown),\n on(document, 'click', handleClick, true), // click needed for <label> and <input type=\"checkbox|radio\">, using capture to ensure we run before React\n on(document, 'mousedown', handleSelect, true), // needed for <select> on desktop\n]);\n"],"mappings":"yFAEA,MAAM,EAAc,GAClB,EAAK,EAAI,UAAU,IAAM,MAAQ,EAAK,EAAI,eAAe,IAAM,OAE3D,EAAiB,GAAsC,EAEvD,EADU,EAAE,MAAQ,OAAS,EAAE,QAAU,EAAE,SAAW,EAAE,UAC9C,EAAE,KAAK,WAAW,OAAO,IAAG,EAAa,CAAC,CAC1D,EAEM,EAAgB,GAAa,CACjC,IAAM,EAAK,EAAkB,CAAC,EAC1B,GAAI,WAAa,UAAY,EAAW,CAAE,GAAG,EAAE,eAAe,CACpE,EAEM,EAAe,GAAa,CAChC,IAAK,IAAI,KAAM,EAAE,aAAa,EAE5B,GADI,GAAI,WAAa,UAAS,EAAK,EAAG,SAClC,GAAI,WAAa,SAAW,GAAI,WAAa,SAAU,CACrD,EAAW,CAAE,IACf,EAAE,yBAAyB,EAC3B,EAAE,eAAe,EACjB,EAAG,EAAG,WAAa,SAAW,OAAS,QAAQ,CAAC,EAChD,0BAA4B,EAAG,aAAe,EAAG,MAAM,CAAC,GAE1D,MACF,CAEJ,EAEA,EAAY,eAAkB,CAC5B,EAAG,SAAU,UAAW,CAAa,EACrC,EAAG,SAAU,QAAS,EAAa,EAAI,EACvC,EAAG,SAAU,YAAa,EAAc,EAAI,CAC9C,CAAC"}
@@ -1,2 +1,2 @@
1
- import{QUICK_EVENT as e,attr as t,customElements as n,off as r,on as i,onMutation as a,useId as o}from"../utils/utils.js";import{UHTMLComboboxElement as s}from"@u-elements/u-combobox";var c=class extends s{_unmutate;connectedCallback(){super.connectedCallback(),this._unmutate=a(this,l,{childList:!0}),i(this,`toggle`,u,e)}disconnectedCallback(){super.disconnectedCallback(),this._unmutate?.(),this._unmutate=void 0,r(this,`toggle`,u,e)}};const l=e=>{let{control:n,list:r}=e,i=r||e.querySelector(`u-datalist`);n&&!n.placeholder&&t(n,`placeholder`,` `),n&&t(n,`popovertarget`,o(r)||null),i&&t(i,`popover`,`manual`),i&&t(i,`data-is-floating`,`true`)},u=e=>{let t=e.currentTarget,n=e.newState===`open`&&t.control;n&&t.list?.dispatchEvent(new CustomEvent(`ds-toggle-source`,{detail:n}))};n.define(`ds-suggestion`,c);export{c as DSSuggestionElement};
1
+ import{QUICK_EVENT as e,attr as t,attrOrCSS as n,customElements as r,off as i,on as a,onMutation as o,useId as s,warn as c}from"../utils/utils.js";import{UHTMLComboboxElement as l}from"@u-elements/u-combobox";const u=`data-empty`;var d=class extends l{_unmutate;connectedCallback(){super.connectedCallback(),this._unmutate=o(this,f,{childList:!0}),a(this,`comboboxafterselect input`,p,e),a(this,`toggle`,m,e)}disconnectedCallback(){super.disconnectedCallback(),this._unmutate?.(),this._unmutate=void 0,i(this,`comboboxafterselect input`,p,e),i(this,`toggle`,m,e)}};const f=e=>{let{control:n,list:r}=e,i=r||e.querySelector(`u-datalist`);n&&(t(n,`popovertarget`,r?s(r):null),n.placeholder||t(n,`placeholder`,` `)),i&&(t(i,`popover`,`manual`),t(i,`data-is-floating`,`true`))},p=({currentTarget:e})=>{let{creatable:r,control:i,options:a}=e;if(!a)return;let o=i?.value.trim()||``,s=o.toLowerCase(),l,d=!o;for(let e of a)if(!l&&e.hasAttribute(u)?l=e:!d&&e.label?.toLowerCase()===s&&(d=!0),d&&l)break;if(!l||(l.hidden=d,l.label=o,l.value=r?o:``,!r||l.textContent))return;let f=n(l,u);f?t(l,u,f):c(`Missing ${u} value on:`,l),t(l,`data-create`,f?.replace(`{value}`,o))},m=e=>{let t=e.currentTarget,n=e.newState===`open`&&t.control;n&&t.list?.dispatchEvent(new CustomEvent(`ds-toggle-source`,{bubbles:!0,composed:!0,detail:n}))};r.define(`ds-suggestion`,d);export{d as DSSuggestionElement};
2
2
  //# sourceMappingURL=suggestion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"suggestion.js","names":[],"sources":["../../../src/suggestion/suggestion.ts"],"sourcesContent":["import { UHTMLComboboxElement } from '@u-elements/u-combobox';\nimport {\n attr,\n customElements,\n off,\n on,\n onMutation,\n QUICK_EVENT,\n useId,\n} from '../utils/utils';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-suggestion': DSSuggestionElement;\n }\n}\n\nexport class DSSuggestionElement extends UHTMLComboboxElement {\n _unmutate?: ReturnType<typeof onMutation>; // Using underscore instead of private fields for backwards compatibility\n\n connectedCallback() {\n super.connectedCallback();\n this._unmutate = onMutation(this, render, { childList: true }); // .control and .list are direct children of the custom element\n on(this, 'toggle', polyfillToggleSource, QUICK_EVENT);\n }\n disconnectedCallback() {\n super.disconnectedCallback();\n this._unmutate?.();\n this._unmutate = undefined;\n off(this, 'toggle', polyfillToggleSource, QUICK_EVENT);\n }\n}\n\n// A non-empty placeholder attribute is required to activate the :placeholder-shown pseudo selector used in our chevron styling\nconst render = (self: DSSuggestionElement) => {\n const { control, list } = self;\n const datalist = list || self.querySelector('u-datalist'); // Fallback to u-datalist since React can render the ds-suggestion before u-datalist is connected\n\n if (control && !control.placeholder) attr(control, 'placeholder', ' '); // .control comes from UHTMLComboboxElement\n if (control) attr(control, 'popovertarget', useId(list) || null);\n if (datalist) attr(datalist, 'popover', 'manual'); // Ensure popover attribute is set on the list\n if (datalist) attr(datalist, 'data-is-floating', 'true'); // identifier for css to toggle opacity when it is placed by floating-ui.\n};\n\n// Since showPopover({ source }) is not supported in all browsers yet:\nconst polyfillToggleSource = (event: Partial<ToggleEvent>) => {\n const self = event.currentTarget as DSSuggestionElement;\n const detail = event.newState === 'open' && self.control; // .control comes from UHTMLComboboxElement\n\n if (detail)\n self.list?.dispatchEvent(new CustomEvent('ds-toggle-source', { detail }));\n};\n\n// Ensure u-datalist is defined before ds-suggestion\ncustomElements.define('ds-suggestion', DSSuggestionElement);\n"],"mappings":"wLAiBA,IAAa,EAAb,cAAyC,CAAqB,CAC5D,UAEA,mBAAoB,CAClB,MAAM,kBAAkB,EACxB,KAAK,UAAY,EAAW,KAAM,EAAQ,CAAE,UAAW,EAAK,CAAC,EAC7D,EAAG,KAAM,SAAU,EAAsB,CAAW,CACtD,CACA,sBAAuB,CACrB,MAAM,qBAAqB,EAC3B,KAAK,YAAY,EACjB,KAAK,UAAY,IAAA,GACjB,EAAI,KAAM,SAAU,EAAsB,CAAW,CACvD,CACF,EAGA,MAAM,EAAU,GAA8B,CAC5C,GAAM,CAAE,UAAS,QAAS,EACpB,EAAW,GAAQ,EAAK,cAAc,YAAY,EAEpD,GAAW,CAAC,EAAQ,aAAa,EAAK,EAAS,cAAe,GAAG,EACjE,GAAS,EAAK,EAAS,gBAAiB,EAAM,CAAI,GAAK,IAAI,EAC3D,GAAU,EAAK,EAAU,UAAW,QAAQ,EAC5C,GAAU,EAAK,EAAU,mBAAoB,MAAM,CACzD,EAGM,EAAwB,GAAgC,CAC5D,IAAM,EAAO,EAAM,cACb,EAAS,EAAM,WAAa,QAAU,EAAK,QAE7C,GACF,EAAK,MAAM,cAAc,IAAI,YAAY,mBAAoB,CAAE,QAAO,CAAC,CAAC,CAC5E,EAGA,EAAe,OAAO,gBAAiB,CAAmB"}
1
+ {"version":3,"file":"suggestion.js","names":[],"sources":["../../../src/suggestion/suggestion.ts"],"sourcesContent":["import { UHTMLComboboxElement } from '@u-elements/u-combobox';\nimport {\n attr,\n attrOrCSS,\n customElements,\n off,\n on,\n onMutation,\n QUICK_EVENT,\n useId,\n warn,\n} from '../utils/utils';\n\nconst ATTR_EMPTY = 'data-empty';\nconst ATTR_CREATE = 'data-create';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ds-suggestion': DSSuggestionElement;\n }\n}\n\nexport class DSSuggestionElement extends UHTMLComboboxElement {\n _unmutate?: ReturnType<typeof onMutation>; // Using underscore instead of private fields for backwards compatibility\n\n connectedCallback() {\n super.connectedCallback();\n this._unmutate = onMutation(this, render, { childList: true }); // .control and .list are direct children of the custom element\n on(this, 'comboboxafterselect input', handleEmpty, QUICK_EVENT);\n on(this, 'toggle', polyfillToggleSource, QUICK_EVENT);\n }\n disconnectedCallback() {\n super.disconnectedCallback();\n this._unmutate?.();\n this._unmutate = undefined;\n off(this, 'comboboxafterselect input', handleEmpty, QUICK_EVENT);\n off(this, 'toggle', polyfillToggleSource, QUICK_EVENT);\n }\n}\n\n// A non-empty placeholder attribute is required to activate the :placeholder-shown pseudo selector used in our chevron styling\nconst render = (self: DSSuggestionElement) => {\n const { control, list } = self;\n const datalist = list || self.querySelector('u-datalist'); // Fallback to u-datalist since React can render the ds-suggestion before u-datalist is connected\n\n if (control) {\n attr(control, 'popovertarget', list ? useId(list) : null);\n if (!control.placeholder) attr(control, 'placeholder', ' '); // .control comes from UHTMLComboboxElement\n }\n if (datalist) {\n attr(datalist, 'popover', 'manual'); // Ensure popover attribute is set on the list\n attr(datalist, 'data-is-floating', 'true'); // identifier for css to toggle opacity when it is placed by floating-ui.\n }\n};\n\nconst handleEmpty = ({ currentTarget: self }: Event) => {\n const { creatable, control, options } = self as DSSuggestionElement;\n if (!options) return;\n\n const value = control?.value.trim() || '';\n const query = value.toLowerCase();\n let empty: HTMLOptionElement | undefined;\n let exists = !value;\n\n for (const opt of options) {\n if (!empty && opt.hasAttribute(ATTR_EMPTY)) empty = opt;\n else if (!exists && opt.label?.toLowerCase() === query) exists = true; // Prevent creating an option that already exists\n if (exists && empty) break; // Speed up if both conditions are met\n }\n if (!empty) return;\n\n empty.hidden = exists;\n empty.label = value; // Ensures option is not filtered out by <u-combobox>\n empty.value = creatable ? value : ''; // Ensures clicking option does nothing\n\n if (!creatable || empty.textContent) return;\n const text = attrOrCSS(empty, ATTR_EMPTY);\n if (!text) warn(`Missing ${ATTR_EMPTY} value on:`, empty);\n else attr(empty, ATTR_EMPTY, text); // Speed up by caching attribute value\n attr(empty, ATTR_CREATE, text?.replace('{value}', value));\n};\n\n// Since showPopover({ source }) is not supported in all browsers yet:\nconst polyfillToggleSource = (event: Partial<ToggleEvent>) => {\n const self = event.currentTarget as DSSuggestionElement;\n const detail = event.newState === 'open' && self.control; // .control comes from UHTMLComboboxElement\n\n if (detail)\n self.list?.dispatchEvent(\n new CustomEvent('ds-toggle-source', {\n bubbles: true,\n composed: true, // Enable bubbling out of shadow DOM boundaries\n detail, // Since showPopover({ source }) is not supported in all browsers yet\n }),\n );\n};\n\n// Ensure u-datalist is defined before ds-suggestion\ncustomElements.define('ds-suggestion', DSSuggestionElement);\n"],"mappings":"iNAaA,MAAM,EAAa,aASnB,IAAa,EAAb,cAAyC,CAAqB,CAC5D,UAEA,mBAAoB,CAClB,MAAM,kBAAkB,EACxB,KAAK,UAAY,EAAW,KAAM,EAAQ,CAAE,UAAW,EAAK,CAAC,EAC7D,EAAG,KAAM,4BAA6B,EAAa,CAAW,EAC9D,EAAG,KAAM,SAAU,EAAsB,CAAW,CACtD,CACA,sBAAuB,CACrB,MAAM,qBAAqB,EAC3B,KAAK,YAAY,EACjB,KAAK,UAAY,IAAA,GACjB,EAAI,KAAM,4BAA6B,EAAa,CAAW,EAC/D,EAAI,KAAM,SAAU,EAAsB,CAAW,CACvD,CACF,EAGA,MAAM,EAAU,GAA8B,CAC5C,GAAM,CAAE,UAAS,QAAS,EACpB,EAAW,GAAQ,EAAK,cAAc,YAAY,EAEpD,IACF,EAAK,EAAS,gBAAiB,EAAO,EAAM,CAAI,EAAI,IAAI,EACnD,EAAQ,aAAa,EAAK,EAAS,cAAe,GAAG,GAExD,IACF,EAAK,EAAU,UAAW,QAAQ,EAClC,EAAK,EAAU,mBAAoB,MAAM,EAE7C,EAEM,GAAe,CAAE,cAAe,KAAkB,CACtD,GAAM,CAAE,YAAW,UAAS,WAAY,EACxC,GAAI,CAAC,EAAS,OAEd,IAAM,EAAQ,GAAS,MAAM,KAAK,GAAK,GACjC,EAAQ,EAAM,YAAY,EAC5B,EACA,EAAS,CAAC,EAEd,IAAK,IAAM,KAAO,EAGhB,GAFI,CAAC,GAAS,EAAI,aAAa,CAAU,EAAG,EAAQ,EAC3C,CAAC,GAAU,EAAI,OAAO,YAAY,IAAM,IAAO,EAAS,IAC7D,GAAU,EAAO,MAQvB,GANI,CAAC,IAEL,EAAM,OAAS,EACf,EAAM,MAAQ,EACd,EAAM,MAAQ,EAAY,EAAQ,GAE9B,CAAC,GAAa,EAAM,aAAa,OACrC,IAAM,EAAO,EAAU,EAAO,CAAU,EACnC,EACA,EAAK,EAAO,EAAY,CAAI,EADtB,EAAK,WAAW,EAAW,YAAa,CAAK,EAExD,EAAK,EAAO,cAAa,GAAM,QAAQ,UAAW,CAAK,CAAC,CAC1D,EAGM,EAAwB,GAAgC,CAC5D,IAAM,EAAO,EAAM,cACb,EAAS,EAAM,WAAa,QAAU,EAAK,QAE7C,GACF,EAAK,MAAM,cACT,IAAI,YAAY,mBAAoB,CAClC,QAAS,GACT,SAAU,GACV,QACF,CAAC,CACH,CACJ,EAGA,EAAe,OAAO,gBAAiB,CAAmB"}
@@ -1,2 +1,2 @@
1
- import{attr as e,attrOrCSS as t,on as n,onHotReload as r,onMutation as i}from"../utils/utils.js";const a=`aria-label`,o=`data-toggle-group`,s=`[${o}]`,c=()=>{for(let n of document.querySelectorAll(s))e(n,`aria-label`,t(n,o))},l=n=>{let{key:r,target:i}=n,c=i instanceof HTMLInputElement&&i.closest(s);if(c&&(!e(c,a)&&!e(c,`aria-labelledby`)&&e(c,a,t(c,o)),r===`Enter`&&i.click(),r?.startsWith(`Arrow`))){n.preventDefault?.();let e=[...c.getElementsByTagName(`input`)],t=e.indexOf(i),a=r.match(/Arrow(Right|Down)/)?1:-1,o=t;for(let t=0;t<e.length;t++)if(o=(e.length+o+a)%e.length,!e[o]?.disabled){e[o]?.focus();break}}};r(`toggle-group`,()=>[n(document,`keydown`,l),i(document,c,{attributeFilter:[o],attributes:!0,childList:!0,subtree:!0})]);
1
+ import{ARIA_LABEL as e,ARIA_LABELLEDBY as t,attr as n,attrOrCSS as r,getComposedPath as i,getComposedTarget as a,on as o,onHotReload as s,onMutation as c,warn as l}from"../utils/utils.js";const u=`focusgroup`,d=`data-toggle-group`,f=`[${d}]`;let p;const m=(e,t)=>{for(let n of t||[null])if(n?.attributeName===d)y(n.target);else if(!n||n.addedNodes.length){let t=n?.target||e;for(let e of t.querySelectorAll(f))y(e)}},h=e=>{p&&v(e)&&(e.preventDefault(),e.stopImmediatePropagation())},g=()=>p=!1,_=e=>{p=e.key?.startsWith(`Arrow`),e.key===`Enter`&&v(e)?.click(),p&&setTimeout(g,0)},v=e=>{let t=a(e);if(!(t?.nodeName!==`INPUT`||t.type!==`radio`||!t.name)){for(let e of i(t))if(!(e.nodeType!==1||!e.hasAttribute(u)))return n(e,u)?.includes(`radiogroup`)?t:void 0}},y=i=>{if(i.hasAttribute(u))return;let a=r(i,d),o=n(i,t)?.trim(),s=`Please use ${u}="radiogroup" and ${o?t:e}="${o||a}" instead of deprecated ${d} on:`;n(i,e,o?null:a),n(i,u,`radiogroup`),l(s,i)};s(`toggle-group`,()=>[o(document,`click`,h,!0),o(document,`keydown`,_),c(document,m,{attributeFilter:[d],attributes:!0,childList:!0,subtree:!0})]);
2
2
  //# sourceMappingURL=toggle-group.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"toggle-group.js","names":[],"sources":["../../../src/toggle-group/toggle-group.ts"],"sourcesContent":["import { attr, attrOrCSS, on, onHotReload, onMutation } from '../utils/utils';\n\nconst ARIA_LABELLEDBY = 'aria-labelledby';\nconst ARIA_LABEL = 'aria-label';\nconst ATTR_TOGGLEGROUP = 'data-toggle-group';\nconst SELECTOR_TOGGLEGROUP = `[${ATTR_TOGGLEGROUP}]`;\n\nconst handleAriaAttributes = () => {\n for (const group of document.querySelectorAll(SELECTOR_TOGGLEGROUP))\n attr(group, 'aria-label', attrOrCSS(group, ATTR_TOGGLEGROUP));\n};\n\nconst handleKeydown = (event: Partial<KeyboardEvent>) => {\n const { key, target: el } = event;\n const group =\n el instanceof HTMLInputElement && el.closest(SELECTOR_TOGGLEGROUP);\n\n if (!group) return;\n if (!attr(group, ARIA_LABEL) && !attr(group, ARIA_LABELLEDBY))\n attr(group, ARIA_LABEL, attrOrCSS(group, ATTR_TOGGLEGROUP));\n if (key === 'Enter') el.click(); // Forward Enter, but no need to listen for space key, as this is handled by the browser\n if (key?.startsWith('Arrow')) {\n event.preventDefault?.();\n const inputs = [...group.getElementsByTagName('input')];\n const index = inputs.indexOf(el);\n const move = key.match(/Arrow(Right|Down)/) ? 1 : -1;\n let nextIndex = index;\n\n for (let i = 0; i < inputs.length; i++) {\n nextIndex = (inputs.length + nextIndex + move) % inputs.length;\n if (!inputs[nextIndex]?.disabled) {\n inputs[nextIndex]?.focus();\n break;\n }\n }\n }\n};\n\nonHotReload('toggle-group', () => [\n on(document, 'keydown', handleKeydown),\n onMutation(document, handleAriaAttributes, {\n attributeFilter: [ATTR_TOGGLEGROUP],\n attributes: true,\n childList: true,\n subtree: true,\n }),\n]);\n"],"mappings":"iGAEA,MACM,EAAa,aACb,EAAmB,oBACnB,EAAuB,IAAI,EAAiB,GAE5C,MAA6B,CACjC,IAAK,IAAM,KAAS,SAAS,iBAAiB,CAAoB,EAChE,EAAK,EAAO,aAAc,EAAU,EAAO,CAAgB,CAAC,CAChE,EAEM,EAAiB,GAAkC,CACvD,GAAM,CAAE,MAAK,OAAQ,GAAO,EACtB,EACJ,aAAc,kBAAoB,EAAG,QAAQ,CAAoB,EAE9D,OACD,CAAC,EAAK,EAAO,CAAU,GAAK,CAAC,EAAK,EAAO,iBAAe,GAC1D,EAAK,EAAO,EAAY,EAAU,EAAO,CAAgB,CAAC,EACxD,IAAQ,SAAS,EAAG,MAAM,EAC1B,GAAK,WAAW,OAAO,GAAG,CAC5B,EAAM,iBAAiB,EACvB,IAAM,EAAS,CAAC,GAAG,EAAM,qBAAqB,OAAO,CAAC,EAChD,EAAQ,EAAO,QAAQ,CAAE,EACzB,EAAO,EAAI,MAAM,mBAAmB,EAAI,EAAI,GAC9C,EAAY,EAEhB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IAEjC,GADA,GAAa,EAAO,OAAS,EAAY,GAAQ,EAAO,OACpD,CAAC,EAAO,EAAU,EAAE,SAAU,CAChC,EAAO,EAAU,EAAE,MAAM,EACzB,KACF,CAEJ,CACF,EAEA,EAAY,mBAAsB,CAChC,EAAG,SAAU,UAAW,CAAa,EACrC,EAAW,SAAU,EAAsB,CACzC,gBAAiB,CAAC,CAAgB,EAClC,WAAY,GACZ,UAAW,GACX,QAAS,EACX,CAAC,CACH,CAAC"}
1
+ {"version":3,"file":"toggle-group.js","names":[],"sources":["../../../src/toggle-group/toggle-group.ts"],"sourcesContent":["import {\n ARIA_LABEL,\n ARIA_LABELLEDBY,\n attr,\n attrOrCSS,\n getComposedPath,\n getComposedTarget,\n on,\n onHotReload,\n onMutation,\n warn,\n} from '../utils/utils';\n\nconst ATTR_FOCUSGROUP = 'focusgroup';\nconst ATTR_TOGGLEGROUP = 'data-toggle-group';\nconst SELECTOR_TOGGLEGROUP = `[${ATTR_TOGGLEGROUP}]`;\nlet IS_ARROW: boolean | undefined; // Used to prevent clicks that are a result of keyboard navigation\n\nconst handleMutations = (root: Document, records?: MutationRecord[]) => {\n for (const r of records || [null]) {\n if (r?.attributeName === ATTR_TOGGLEGROUP) deprecate(r.target as Element);\n else if (!r || r.addedNodes.length) {\n const scope = (r?.target as Element) || root;\n for (const el of scope.querySelectorAll(SELECTOR_TOGGLEGROUP))\n deprecate(el);\n }\n }\n};\n\n// Prevent clicks that are a result of keyboard navigation\nconst handleRadioClick = (e: Event & Partial<PointerEvent>) => {\n if (IS_ARROW && getRadioInFocusGroup(e)) {\n e.preventDefault(); // Only handle clicks that are a result of keyboard navigation\n e.stopImmediatePropagation(); // Prevent stopped \"click\" from reaching React and other listeners\n }\n};\n\nconst resetIsArrow = () => (IS_ARROW = false);\nconst handleKeydown = (e: Event & Partial<KeyboardEvent>) => {\n IS_ARROW = e.key?.startsWith('Arrow');\n if (e.key === 'Enter') getRadioInFocusGroup(e)?.click(); // Allow Enter to activate the radio input\n if (IS_ARROW) setTimeout(resetIsArrow, 0); // Reset after event loop so we can check if next focus event is a result of arrow key navigation\n};\n\n// This function does not handle nested focusgroups, but toggle-groups should not be nested so this is not a problem\nconst getRadioInFocusGroup = (e: Event) => {\n const el = getComposedTarget(e) as HTMLInputElement | null;\n if (el?.nodeName !== 'INPUT' || el.type !== 'radio' || !el.name) return;\n for (const group of getComposedPath(el) as Set<Element>) {\n if (group.nodeType !== 1 || !group.hasAttribute(ATTR_FOCUSGROUP)) continue; // Ignore non-group nodes\n if (!attr(group, ATTR_FOCUSGROUP)?.includes('radiogroup')) return; // Ignore invalid groups\n return el;\n }\n};\n\nconst deprecate = (el: Element) => {\n if (el.hasAttribute(ATTR_FOCUSGROUP)) return; // Ignore if already migrated\n const label = attrOrCSS(el, ATTR_TOGGLEGROUP);\n const labelledby = attr(el, ARIA_LABELLEDBY)?.trim();\n const message = `Please use ${ATTR_FOCUSGROUP}=\"radiogroup\" and ${labelledby ? ARIA_LABELLEDBY : ARIA_LABEL}=\"${labelledby || label}\" instead of deprecated ${ATTR_TOGGLEGROUP} on:`;\n\n attr(el, ARIA_LABEL, labelledby ? null : label);\n attr(el, ATTR_FOCUSGROUP, 'radiogroup');\n warn(message, el);\n};\n\nonHotReload('toggle-group', () => [\n on(document, 'click', handleRadioClick, true), // Use capture to ensure we run before React and other listeners\n on(document, 'keydown', handleKeydown),\n onMutation(document, handleMutations, {\n attributeFilter: [ATTR_TOGGLEGROUP],\n attributes: true,\n childList: true,\n subtree: true,\n }),\n]);\n"],"mappings":"4LAaA,MAAM,EAAkB,aAClB,EAAmB,oBACnB,EAAuB,IAAI,EAAiB,GAClD,IAAI,EAEJ,MAAM,GAAmB,EAAgB,IAA+B,CACtE,IAAK,IAAM,KAAK,GAAW,CAAC,IAAI,EAC9B,GAAI,GAAG,gBAAkB,EAAkB,EAAU,EAAE,MAAiB,OACnE,GAAI,CAAC,GAAK,EAAE,WAAW,OAAQ,CAClC,IAAM,EAAS,GAAG,QAAsB,EACxC,IAAK,IAAM,KAAM,EAAM,iBAAiB,CAAoB,EAC1D,EAAU,CAAE,CAChB,CAEJ,EAGM,EAAoB,GAAqC,CACzD,GAAY,EAAqB,CAAC,IACpC,EAAE,eAAe,EACjB,EAAE,yBAAyB,EAE/B,EAEM,MAAsB,EAAW,GACjC,EAAiB,GAAsC,CAC3D,EAAW,EAAE,KAAK,WAAW,OAAO,EAChC,EAAE,MAAQ,SAAS,EAAqB,CAAC,CAAC,EAAE,MAAM,EAClD,GAAU,WAAW,EAAc,CAAC,CAC1C,EAGM,EAAwB,GAAa,CACzC,IAAM,EAAK,EAAkB,CAAC,EAC1B,QAAI,WAAa,SAAW,EAAG,OAAS,SAAW,CAAC,EAAG,MAC3D,KAAK,IAAM,KAAS,EAAgB,CAAE,EAChC,OAAM,WAAa,GAAK,CAAC,EAAM,aAAa,CAAe,GAE/D,OADK,EAAK,EAAO,CAAe,CAAC,EAAE,SAAS,YAAY,EACjD,EADoD,MAE7D,CACF,EAEM,EAAa,GAAgB,CACjC,GAAI,EAAG,aAAa,CAAe,EAAG,OACtC,IAAM,EAAQ,EAAU,EAAI,CAAgB,EACtC,EAAa,EAAK,EAAI,CAAe,CAAC,EAAE,KAAK,EAC7C,EAAU,cAAc,EAAgB,oBAAoB,EAAa,EAAkB,EAAW,IAAI,GAAc,EAAM,0BAA0B,EAAiB,MAE/K,EAAK,EAAI,EAAY,EAAa,KAAO,CAAK,EAC9C,EAAK,EAAI,EAAiB,YAAY,EACtC,EAAK,EAAS,CAAE,CAClB,EAEA,EAAY,mBAAsB,CAChC,EAAG,SAAU,QAAS,EAAkB,EAAI,EAC5C,EAAG,SAAU,UAAW,CAAa,EACrC,EAAW,SAAU,EAAiB,CACpC,gBAAiB,CAAC,CAAgB,EAClC,WAAY,GACZ,UAAW,GACX,QAAS,EACX,CAAC,CACH,CAAC"}
@@ -1,2 +1,2 @@
1
- import{QUICK_EVENT as e,announce as t,attr as n,attrOrCSS as r,isBrowser as i,on as a,onHotReload as o,onMutation as s,tag as c,warn as l}from"../utils/utils.js";import"../popover/popover.js";let u,d,f=!1,p=0,m=0;const h=i()&&/iPad|iPhone|iPod/.test(navigator.userAgent),g=`data-tooltip`,_=`data-color`,v=`aria-label`,y=`aria-description`,b=`[${_}]`,x=`[${g}]`,S=`data-color-scheme`,C=`[${S}]`,w=e=>{e&&!(e instanceof HTMLElement)&&l(`setTooltipElement expects an HTMLElement, got: `,e),clearTimeout(m),clearTimeout(p),d=void 0,f=!1,u=e||void 0},T=()=>{for(let e of document.querySelectorAll(x)){let i=r(e,g);if(!i)return;if(i!==(e.getAttribute(v)||e.getAttribute(y))){let t=n(e,`role`)!==`img`&&e.textContent?.trim();n(e,g,i),n(e,v,t?null:i),n(e,y,t?i:null),e.matches(`a,button,input,label,select,textarea,[tabindex]`)||l(`Missing tabindex="0" attribute on: `,e)}let a=e===d&&u?.offsetHeight&&u?.offsetWidth,o=a&&i&&u?.textContent!==i;a&&o&&(u&&(u.textContent=i),document.activeElement===e&&t(i))}},E=e=>{let{type:t,target:r}=e;if(clearTimeout(p),r===u)return;let i=r?.closest?.(x);if(t===`blur`){let t=e.relatedTarget;i===d&&!t?.closest?.(x)&&D();return}if(t===`mouseover`&&!f&&!h){p=setTimeout(E,300,{target:r});return}if(i===d)return;if(!i)return D();u||=c(`div`,{class:`ds-tooltip`}),u.isConnected||document.body.appendChild(u);let a=i.closest(b),o=i.closest(C),s=a!==o&&a?.contains(o);clearTimeout(m),n(u,`popover`,`manual`),n(u,S,o?.getAttribute(S)||null),n(u,_,s&&a?.getAttribute(_)||null),u.textContent=n(i,g),u.showPopover(),u.dispatchEvent(new CustomEvent(`ds-toggle-source`,{detail:i})),f=!0,d=i},D=()=>u?.isConnected&&u.popover&&u.hidePopover(),O=e=>{if(e?.type===`keydown`)return e?.key===`Escape`&&D();e?e.target===u&&e.newState===`closed`&&(d=void 0,m=setTimeout(O,300)):f=!1};o(`tooltip`,()=>[a(document,`blur focus mouseover`,E,e),a(document,`toggle keydown`,O,e),s(document,T,{attributeFilter:[g],attributes:!0,childList:!0,subtree:!0})]);export{w as setTooltipElement};
1
+ import{ARIA_DESC as e,ARIA_LABEL as t,QUICK_EVENT as n,announce as r,attr as i,attrOrCSS as a,getComposedTarget as o,getRoot as s,isBrowser as c,on as l,onHotReload as u,onMutation as d,tag as f,warn as p}from"../utils/utils.js";import"../popover/popover.js";let m,h,g,_,v=0;const y=`data-color`,b=`data-color-scheme`,x=`data-size`,S=`data-tooltip`,C=c()&&/iPad|iPhone|iPod/.test(navigator.userAgent),w=`[${y}]`,T=`[${b}]`,E=`[${x}]`,D=`[${S}]`,O=e=>{e&&!(e instanceof HTMLElement)&&p(`setTooltipElement expects an HTMLElement, got: `,e),P(),v=0,m=e||void 0},k=(e=document)=>{for(let t of e?.querySelectorAll(D)||[])j(t)},A=(e,t)=>{if(!t)return k();for(let e of t)if(e.target!==m)if(e.attributeName===S)j(e.target);else for(let t of e.addedNodes)t.nodeType===1&&(t.hasAttribute(S)?j(t):k(t))},j=(n,o=!0)=>{let c=a(n,S)||``;if(c[0]===`#`&&(c=s(n).getElementById(c.slice(1))?.textContent?.trim()||``),c!==(n.getAttribute(`aria-label`)||n.getAttribute(`aria-description`))){let r=i(n,`role`)!==`img`&&n.textContent?.trim();i(n,S,c),i(n,t,r?null:c),i(n,e,r?c:null),n.tabIndex===-1&&p(`Missing tabindex="0" attribute on: `,n)}n===_&&m?.textContent!==c&&(m&&(m.textContent=c),c&&o&&document.activeElement===n&&r(c))},M=e=>{let t=o(e);if(!t||g===t||m?.contains(t))return;g=t;let n=g?.closest?.(D)||void 0;if(_!==n&&(_&&P(),_=n,n)){if(e.type===`focus`||C||300>Date.now()-v)return N();e.type===`mousemove`&&(h=setTimeout(N,300))}},N=()=>{if(!_)return P();m||=f(`div`,{class:`ds-tooltip`}),m.isConnected||document.body.appendChild(m);let e=_.closest(w),t=_.closest(T),n=_.closest(E),r=e!==t&&e?.contains(t);i(m,`popover`,`manual`),i(m,b,t?.getAttribute(b)||null),i(m,y,r&&e?.getAttribute(y)||null),i(m,x,n?.getAttribute(x)||null),j(_,!1),m.showPopover(),m.dispatchEvent(new CustomEvent(`ds-toggle-source`,{bubbles:!0,composed:!0,detail:_}))},P=e=>{(e?.type!==`keydown`||e?.key===`Escape`)&&(_&&m?.isConnected&&m.popover&&m.hidePopover(),e||(v=Date.now()),clearTimeout(h),_=void 0)};u(`tooltip`,()=>[l(document,`focus mousemove`,M,n),l(document,`keydown`,P,n),d(document,A,{attributeFilter:[S],attributes:!0,childList:!0,subtree:!0})]);export{k as initTooltips,O as setTooltipElement};
2
2
  //# sourceMappingURL=tooltip.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","names":[],"sources":["../../../src/tooltip/tooltip.ts"],"sourcesContent":["import '../popover/popover'; // Ensure popover is imported when using individual imports, since tooltip relies on it\nimport {\n announce,\n attr,\n attrOrCSS,\n isBrowser,\n on,\n onHotReload,\n onMutation,\n QUICK_EVENT,\n tag,\n warn,\n} from '../utils/utils';\n\nlet TIP: HTMLElement | undefined;\nlet SOURCE: Element | undefined;\nlet IS_HOVERING = false;\nlet HOVER_TIMER: number | ReturnType<typeof setTimeout> = 0;\nlet SKIP_TIMER: number | ReturnType<typeof setTimeout> = 0;\nconst IS_IOS = isBrowser() && /iPad|iPhone|iPod/.test(navigator.userAgent); // Needed to omit DELAY_HOVER since iOS triggers mouseover before click\nconst ATTR_TOOLTIP = 'data-tooltip';\nconst ATTR_COLOR = 'data-color';\nconst ARIA_LABEL = 'aria-label';\nconst ARIA_DESC = 'aria-description';\nconst SELECTOR_COLOR = `[${ATTR_COLOR}]`;\nconst SELECTOR_TOOLTIP = `[${ATTR_TOOLTIP}]`;\nconst ATTR_SCHEME = 'data-color-scheme';\nconst SELECTOR_SCHEME = `[${ATTR_SCHEME}]`;\nconst SELECTOR_INTERACTIVE = 'a,button,input,label,select,textarea,[tabindex]';\nconst DELAY_HOVER = 300;\nconst DELAY_SKIP = 300;\n\n/**\n * setTooltipElement\n * @description Allows setting a custom tooltip element. It does not need to, and should not, be injected to document.body, as we inject on hover to ensure React hydration works as expected.\n * @param el The HTMLElement to use as tooltip\n */\nexport const setTooltipElement = (el?: HTMLElement | null) => {\n if (el && !(el instanceof HTMLElement))\n warn('setTooltipElement expects an HTMLElement, got: ', el);\n clearTimeout(SKIP_TIMER); // Reset when changing source\n clearTimeout(HOVER_TIMER);\n SOURCE = undefined;\n IS_HOVERING = false;\n TIP = el || undefined;\n};\n\nconst handleAriaAttributes = () => {\n for (const el of document.querySelectorAll(SELECTOR_TOOLTIP)) {\n const text = attrOrCSS(el, ATTR_TOOLTIP);\n\n if (!text) return; // Early return if no tooltip text\n if (text !== (el.getAttribute(ARIA_LABEL) || el.getAttribute(ARIA_DESC))) {\n const hasText = attr(el, 'role') !== 'img' && el.textContent?.trim(); // If role=\"img\", ignore text\n attr(el, ATTR_TOOLTIP, text); // Set data-tooltip attribute to speed up future mutations\n attr(el, ARIA_LABEL, hasText ? null : text); // Set aria-label if element does not have text\n attr(el, ARIA_DESC, hasText ? text : null); // Set aria-description if element has text\n if (!el.matches(SELECTOR_INTERACTIVE))\n warn('Missing tabindex=\"0\" attribute on: ', el);\n }\n\n // If an existing tooltip has changed programmatically, update tooltip text and announce change\n const isCurrent = el === SOURCE && TIP?.offsetHeight && TIP?.offsetWidth; // Using offsetHeight+Width to check visibility as :popover-open is not well supported by JSDOM\n const isChanged = isCurrent && text && TIP?.textContent !== text; // Only update if mutation is on source element and tooltip is open to avoid unnecessary updates\n if (isCurrent && isChanged) {\n if (TIP) TIP.textContent = text;\n if (document.activeElement === el) announce(text); // Only announce if focus is on the button\n }\n }\n};\n\nconst handleInterest = (event: Event) => {\n const { type, target } = event;\n clearTimeout(HOVER_TIMER);\n\n if (target === TIP) return; // Allow tooltip to be hovered, following https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus\n\n const source = (target as Element)?.closest?.(SELECTOR_TOOLTIP);\n\n // This prevents the tooltip from reappearing on mousedown/click\n if (type === 'blur') {\n const next = (event as FocusEvent).relatedTarget as Element | null;\n if (source === SOURCE && !next?.closest?.(SELECTOR_TOOLTIP)) hideTooltip();\n return;\n }\n\n if (type === 'mouseover' && !IS_HOVERING && !IS_IOS) {\n HOVER_TIMER = setTimeout(handleInterest, DELAY_HOVER, { target }); // Delay mouse showing tooltip if not already shown\n return;\n }\n\n if (source === SOURCE) return; // No need to update\n if (!source) return hideTooltip(); // If no new anchor, cleanup previous autoUpdate\n if (!TIP) TIP = tag('div', { class: 'ds-tooltip' });\n if (!TIP.isConnected) document.body.appendChild(TIP); // Ensure connected\n\n const color = source.closest(SELECTOR_COLOR); // Match source color of source element\n const scheme = source.closest(SELECTOR_SCHEME); // Match source color-scheme of source element\n const isReset = color !== scheme && color?.contains(scheme as Node); // If data-scheme is closer to target, it will reset data-color\n\n clearTimeout(SKIP_TIMER);\n attr(TIP, 'popover', 'manual'); // Ensure popover behavior\n attr(TIP, ATTR_SCHEME, scheme?.getAttribute(ATTR_SCHEME) || null); // Fallback to null to reset if not scheme found\n attr(TIP, ATTR_COLOR, (isReset && color?.getAttribute(ATTR_COLOR)) || null); // Fallback to null to reset if not scheme found\n TIP.textContent = attr(source, ATTR_TOOLTIP);\n TIP.showPopover();\n TIP.dispatchEvent(new CustomEvent('ds-toggle-source', { detail: source })); // Since showPopover({ source }) is not supported in all browsers yet\n IS_HOVERING = true;\n SOURCE = source;\n};\n\nconst hideTooltip = () => TIP?.isConnected && TIP.popover && TIP.hidePopover(); // Only hide if connected and activated\n\nconst handleClose = (event?: Partial<ToggleEvent & KeyboardEvent>) => {\n if (event?.type === 'keydown')\n return event?.key === 'Escape' && hideTooltip();\n if (!event) IS_HOVERING = false;\n else if (event.target === TIP && event.newState === 'closed') {\n SOURCE = undefined;\n SKIP_TIMER = setTimeout(handleClose, DELAY_SKIP);\n }\n};\n\nonHotReload('tooltip', () => [\n on(document, 'blur focus mouseover', handleInterest, QUICK_EVENT),\n on(document, 'toggle keydown', handleClose, QUICK_EVENT),\n onMutation(document, handleAriaAttributes, {\n attributeFilter: [ATTR_TOOLTIP],\n attributes: true,\n childList: true,\n subtree: true,\n }),\n]);\n"],"mappings":"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"}
1
+ {"version":3,"file":"tooltip.js","names":[],"sources":["../../../src/tooltip/tooltip.ts"],"sourcesContent":["import '../popover/popover'; // Ensure popover is imported when using individual imports, since tooltip relies on it\nimport {\n ARIA_DESC,\n ARIA_LABEL,\n announce,\n attr,\n attrOrCSS,\n getComposedTarget,\n getRoot,\n isBrowser,\n on,\n onHotReload,\n onMutation,\n QUICK_EVENT,\n tag,\n warn,\n} from '../utils/utils';\n\nlet TIP: HTMLElement | undefined;\nlet TIMER: ReturnType<typeof setTimeout> | undefined;\nlet TARGET: Element | undefined; // Used to speed up mousemove handling\nlet OPEN: Element | undefined;\nlet LAST_HIDE = 0;\nconst ATTR_COLOR = 'data-color';\nconst ATTR_SCHEME = 'data-color-scheme';\nconst ATTR_SIZE = 'data-size';\nconst ATTR_TOOLTIP = 'data-tooltip';\nconst DELAY_HOVER = 300;\nconst DELAY_SKIP = 300;\nconst IS_IOS = isBrowser() && /iPad|iPhone|iPod/.test(navigator.userAgent); // Needed to omit DELAY_HOVER since iOS triggers mousemove before click\nconst SELECTOR_COLOR = `[${ATTR_COLOR}]`;\nconst SELECTOR_SCHEME = `[${ATTR_SCHEME}]`;\nconst SELECTOR_SIZE = `[${ATTR_SIZE}]`;\nconst SELECTOR_TOOLTIP = `[${ATTR_TOOLTIP}]`;\n\n/**\n * setTooltipElement\n * @description Allows setting a custom tooltip element. It does not need to, and should not, be injected to document.body, as we inject on hover to ensure React hydration works as expected.\n * @param el The HTMLElement to use as tooltip\n */\nexport const setTooltipElement = (el?: HTMLElement | null) => {\n if (el && !(el instanceof HTMLElement))\n warn('setTooltipElement expects an HTMLElement, got: ', el);\n hide(); // Reset when changing source\n LAST_HIDE = 0; // Reset last hide time to re-enable delay\n TIP = el || undefined;\n};\n\nexport const initTooltips = (\n scope: Element | ShadowRoot | Document | null = document,\n) => {\n for (const el of scope?.querySelectorAll(SELECTOR_TOOLTIP) || [])\n setupText(el);\n};\n\n// Initial run has no MutationRecords, so we set records to [null] to ensure we run the querySelectorAll for any existing elements with data-tooltip\nconst handleMutations = (_: Document, records?: MutationRecord[]) => {\n if (!records) return initTooltips();\n for (const r of records) {\n if (r.target === TIP) continue; // Ignore mutations on tooltip itself\n if (r.attributeName === ATTR_TOOLTIP) setupText(r.target as Element);\n else\n for (const el of r.addedNodes as NodeListOf<Element>) {\n if (el.nodeType !== 1) continue;\n if (el.hasAttribute(ATTR_TOOLTIP)) setupText(el);\n else initTooltips(el); // Check for any child elements with data-tooltip\n }\n }\n};\n\nconst setupText = (el: Element, canAnnounce = true) => {\n let text = attrOrCSS(el, ATTR_TOOLTIP) || '';\n\n // Allow using another element as source.\n // Note: Only checks on initial mutation, as we do not want to keep checking if the source element is removed or changed,\n // since this would be a performance issue. If the source element is removed, the tooltip will be empty and not shown.\n if (text[0] === '#')\n text = getRoot(el).getElementById(text.slice(1))?.textContent?.trim() || '';\n\n if (text !== (el.getAttribute(ARIA_LABEL) || el.getAttribute(ARIA_DESC))) {\n const hasText = attr(el, 'role') !== 'img' && el.textContent?.trim(); // If role=\"img\", ignore text\n attr(el, ATTR_TOOLTIP, text); // Set data-tooltip attribute to speed up future mutations\n attr(el, ARIA_LABEL, hasText ? null : text); // Set aria-label if element does not have text\n attr(el, ARIA_DESC, hasText ? text : null); // Set aria-description if element has text\n if ((el as HTMLElement).tabIndex === -1)\n warn('Missing tabindex=\"0\" attribute on: ', el);\n }\n if (el === OPEN && TIP?.textContent !== text) {\n if (TIP) TIP.textContent = text; // Update tooltip text if it is already open\n if (text && canAnnounce && document.activeElement === el) announce(text); // Only announce if focus is on the source\n }\n};\n\nconst handleInterest = (e: Event) => {\n const target = getComposedTarget(e);\n if (!target || TARGET === target || TIP?.contains(target as Node)) return; // Same target, or allow tooltip to be hovered (following https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus)\n TARGET = target;\n\n const source = TARGET?.closest?.(SELECTOR_TOOLTIP) || undefined;\n if (OPEN === source) return; // Same source, no need to update\n if (OPEN) hide(); // Reset previous tooltip, since we are moving to a new source\n OPEN = source;\n\n if (!source) return; // No source, no need to show tooltip\n if (e.type === 'focus' || IS_IOS || DELAY_SKIP > Date.now() - LAST_HIDE)\n return show(); // Instantly show if focus or if we just closed a tooltip\n if (e.type === 'mousemove') TIMER = setTimeout(show, DELAY_HOVER); // Delay mouse showing tooltip if not already shown\n};\n\nconst show = () => {\n if (!OPEN) return hide(); // If no new anchor, cleanup previous autoUpdate\n if (!TIP) TIP = tag('div', { class: 'ds-tooltip' });\n if (!TIP.isConnected) document.body.appendChild(TIP);\n\n const color = OPEN.closest(SELECTOR_COLOR); // Match source color of source element\n const scheme = OPEN.closest(SELECTOR_SCHEME); // Match source color-scheme of source element\n const size = OPEN.closest(SELECTOR_SIZE); // Match source size of source element\n const isReset = color !== scheme && color?.contains(scheme as Node); // If data-scheme is closer to target, it will reset data-color\n\n attr(TIP, 'popover', 'manual'); // Ensure popover behavior\n attr(TIP, ATTR_SCHEME, scheme?.getAttribute(ATTR_SCHEME) || null); // Fallback to null to reset if not scheme found\n attr(TIP, ATTR_COLOR, (isReset && color?.getAttribute(ATTR_COLOR)) || null); // Fallback to null to reset if not scheme found\n attr(TIP, ATTR_SIZE, size?.getAttribute(ATTR_SIZE) || null); // Fallback to null to reset if not size found\n setupText(OPEN, false); // If mutation observer is not triggered, ensure tooltip text is updated\n TIP.showPopover();\n TIP.dispatchEvent(\n new CustomEvent('ds-toggle-source', {\n bubbles: true,\n composed: true, // Enable bubbling out of shadow DOM boundaries\n detail: OPEN, // Since showPopover({ source }) is not supported in all browsers yet\n }),\n );\n};\n\nconst hide = (event?: Partial<KeyboardEvent>) => {\n if (event?.type === 'keydown' && event?.key !== 'Escape') return;\n if (OPEN && TIP?.isConnected && TIP.popover) TIP.hidePopover(); // Only hide if connected and activated\n if (!event) LAST_HIDE = Date.now(); // If closing with keyboard, do not show next tooltip instantly\n clearTimeout(TIMER);\n OPEN = undefined;\n};\n\nonHotReload('tooltip', () => [\n on(document, 'focus mousemove', handleInterest, QUICK_EVENT),\n on(document, 'keydown', hide, QUICK_EVENT),\n onMutation(document, handleMutations, {\n attributeFilter: [ATTR_TOOLTIP],\n attributes: true,\n childList: true,\n subtree: true,\n }),\n]);\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e={passive:!0,capture:!0},t=()=>typeof window<`u`&&typeof document<`u`,n=()=>t()&&/^Win/i.test(navigator.userAgentData?.platform||navigator.platform),r=typeof HTMLElement>`u`?class{}:HTMLElement;function i(e,t){let n;return function(...r){clearTimeout(n),n=setTimeout(()=>e.apply(this,r),t)}}const a=(e,...n)=>!t()||window.dsWarnings===!1||console.log(`\x1B[1mDesignsystemet:\x1B[m ${e}`,...n),o=(e,t,n)=>n===void 0?e.getAttribute(t)??null:(n===null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n),null),s=(e,t)=>getComputedStyle(e).getPropertyValue(t).trim(),c=/^["']|["']$/g,l=(e,t)=>{let n=o(e,t);return n||=s(e,`--_ds-${t}`).replace(c,``).trim(),n||a(`Missing ${t} on:`,e),n||null},u=(e,...t)=>{let[n,...r]=t;for(let t of n.split(` `))e.addEventListener(t,...r);return()=>d(e,...t)},d=(e,...t)=>{let[n,...r]=t;for(let t of n.split(` `))e.removeEventListener(t,...r)},f=(e,n)=>{t()&&(window._dsHotReloadCleanup||(window._dsHotReloadCleanup=new Map),window._dsHotReloadCleanup?.get(e)?.map(e=>e()),window._dsHotReloadCleanup?.set(e,n()))},p=(e,n,r)=>{let i=()=>a.disconnect(),a=new MutationObserver(r=>{if(!t()||!e.isConnected)return i();n(e,r)});return n(e),a.observe(e,r),i},m=(e,t)=>{let n=document.createElement(e);if(t)for(let[e,r]of Object.entries(t))o(n,e,r);return n},h={define:(e,n)=>!t()||window.customElements.get(e)||window.customElements.define(e,n)};let g=0;function _(e){return t()?(window.dsUseId||(window.dsUseId=0),e&&!e.id&&(e.id=`:ds:${++window.dsUseId}`),e?.id||``):`:ds:${++g}`}let v,y=0,b=0;const x=e=>{clearTimeout(b),v&&(v.textContent=`${e}${y++%2?`\xA0`:``}`),e&&(b=setTimeout(x,2e3,``))},S=()=>{document.readyState===`complete`&&(v||(v=m(`div`,{"aria-live":`assertive`}),v.style.overflow=`hidden`,v.style.position=`fixed`,v.style.whiteSpace=`nowrap`,v.style.width=`1px`),v.isConnected||document.body.appendChild(v))};f(`announce`,()=>[u(document,`focus mouseover`,S,e)]);export{r as DSElement,e as QUICK_EVENT,x as announce,o as attr,l as attrOrCSS,h as customElements,i as debounce,s as getCSSProp,t as isBrowser,n as isWindows,d as off,u as on,f as onHotReload,p as onMutation,m as tag,_ as useId,a as warn};
1
+ import{version as e}from"../packages/web/package.js";const t=`aria-description`,n=`aria-label`,r=`aria-labelledby`,i={passive:!0,capture:!0},a=()=>typeof window<`u`&&typeof document<`u`,o=()=>a()&&/^Win/i.test(navigator.userAgentData?.platform||navigator.platform),s=typeof HTMLElement>`u`?class{}:HTMLElement;function c(e,t){let n;return function(...r){clearTimeout(n),n=setTimeout(()=>e.apply(this,r),t)}}const l=(e,...t)=>!a()||window.dsWarnings===!1||console.log(`\x1B[1mDesignsystemet:\x1B[m ${e}`,...t),u=(e,t,n)=>n===void 0?e.getAttribute(t):(n===null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n),null),d=(e,t)=>getComputedStyle(e).getPropertyValue(t).trim(),f=/^["']|["']$/g,p=(e,t)=>{let n=u(e,t);return n||=d(e,`--_ds-${t}`).replace(f,``).trim(),n||null},m=e=>{let t=e?.getRootNode?.()||e?.ownerDocument;return t instanceof Document||t instanceof ShadowRoot?t:e?.ownerDocument||document},h=e=>{let t=e.target?.shadowRoot&&e.composedPath()[0]||e.target;return t?.nodeType===1?t:null},g=e=>{let t=new Set;for(;e;)t.add(e),e=e.nodeType===11?e.host:e.assignedSlot||e.parentNode;return t},_=(e,...t)=>{let[n,...r]=t;for(let t of n.split(` `))e.addEventListener(t,...r);return()=>v(e,...t)},v=(e,...t)=>{let[n,...r]=t;for(let t of n.split(` `))e.removeEventListener(t,...r)},y=`_dsHotReloadCleanup${e}`,b=(e,t)=>{if(a()){window[y]||(window[y]=new Map);for(let t of window[y]?.get(e)||[])t();window[y]?.set(e,t())}},x=(e,t,n)=>{let r=()=>i.disconnect(),i=new MutationObserver(n=>{if(!a()||!e.isConnected)return r();t(e,n)});return t(e),i.observe(e,n),r},S=(e,t)=>{let n=document.createElement(e);if(t)for(let[e,r]of Object.entries(t))u(n,e,r);return n},C={define:(e,t)=>!a()||window.customElements.get(e)||window.customElements.define(e,t)};let w=0;function T(e){return a()?(window.dsUseId||(window.dsUseId=0),e&&!e.id&&(e.id=`:ds:${++window.dsUseId}`),e?.id||``):`:ds:${++w}`}let E,D=0,O=0;const k=e=>{clearTimeout(O),E&&(E.textContent=`${e}${D++%2?`\xA0`:``}`),e&&(O=setTimeout(k,2e3,``))},A=()=>{document.readyState===`complete`&&(E||(E=S(`div`,{"aria-live":`assertive`}),E.style.overflow=`hidden`,E.style.position=`fixed`,E.style.whiteSpace=`nowrap`,E.style.width=`1px`),E.isConnected||document.body.appendChild(E))};b(`announce`,()=>[_(document,`focus mouseover`,A,i)]);export{t as ARIA_DESC,n as ARIA_LABEL,r as ARIA_LABELLEDBY,s as DSElement,i as QUICK_EVENT,k as announce,u as attr,p as attrOrCSS,C as customElements,c as debounce,d as getCSSProp,g as getComposedPath,h as getComposedTarget,m as getRoot,a as isBrowser,o as isWindows,v as off,_ as on,b as onHotReload,x as onMutation,S as tag,T as useId,l as warn};
2
2
  //# sourceMappingURL=utils.js.map