@digdir/designsystemet-web 1.20.0 → 1.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -1
- package/dist/cjs/field/field.cjs +1 -1
- package/dist/cjs/field/field.cjs.map +1 -1
- package/dist/cjs/packages/web/package.cjs +1 -1
- package/dist/cjs/popover/popover.cjs.map +1 -1
- package/dist/cjs/tooltip/tooltip.cjs +1 -1
- package/dist/cjs/tooltip/tooltip.cjs.map +1 -1
- package/dist/custom-elements.json +2 -2
- package/dist/esm/field/field.js +1 -1
- package/dist/esm/field/field.js.map +1 -1
- package/dist/esm/packages/web/package.js +1 -1
- package/dist/esm/tooltip/tooltip.js +1 -1
- package/dist/esm/tooltip/tooltip.js.map +1 -1
- package/dist/index.d.ts +102 -108
- package/dist/index.js +1 -1
- package/dist/umd/index.js +4 -4
- package/dist/umd/index.js.map +1 -1
- package/package.json +5 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));exports.__toESM=s;
|
package/dist/cjs/field/field.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../utils/utils.cjs"),t=`data-field`,n=`data-limit`,r=`aria-invalid`,i=`aria-describedby`,a=`data-indeterminate`,o=`:scope > [${t}="validation"]`,s=e.isWindows()?800:200,c=n=>{let s=[],c=[],l=n._descs||[],u=!1,p=!1;n._input?.hidden&&(n._input=void 0);for(let r of n.getElementsByTagName(`*`))if(r instanceof HTMLLabelElement&&s.push(r),!r.hidden)
|
|
1
|
+
const e=require("../utils/utils.cjs"),t=`data-field`,n=`data-limit`,r=`aria-invalid`,i=`aria-describedby`,a=`data-indeterminate`,o=`:scope > [${t}="validation"]`,s=e.isWindows()?800:200,c=n=>{let s=[],c=[],l=n._descs||[],u=!1,p=!1;n._input?.hidden&&(n._input=void 0);for(let r of n.getElementsByTagName(`*`))if(r instanceof HTMLLabelElement&&s.push(r),!r.hidden)if(f(r))n._input?.isConnected&&n._input!==r?e.warn(`Fields should only have one input element. Use <fieldset> to group multiple fields:`,n):n._input=r;else{let i=r.getAttribute(t);i===`counter`&&(n._counter=r),i===`validation`?(c.unshift(e.useId(r)),u=!0,p||=d(r)):i&&c.push(e.useId(r))}if(!n._input?.isConnected)return;let m=n._input;for(let t of s)e.attr(t,`for`,e.useId(m)||null);let h=n.closest(`fieldset`)?.querySelector(o);h&&!h?.hidden&&(u=!0,p||=d(h),c.unshift(e.useId(h)));let g=e.attr(m,a);g&&(m.indeterminate=g===`true`),(m.type===`radio`||m.type===`checkbox`)&&e.attr(n,`data-clickdelegatefor`,e.useId(m));let _=(e.attr(m,i)?.trim().split(/\s+/))?.filter(e=>!l.includes(e))||[];e.attr(m,i,[...c,..._].join(` `)||null),n._descs=c;let v=n._validation;u&&!v&&!m.hasAttribute(r)?(e.attr(m,r,p?`true`:null),n._validation=!0):!u&&v&&(e.attr(m,r,null),n._validation=void 0),n.handleEvent()},l={over:`%d tegn for mye`,under:`%d tegn igjen`},u=e.debounce((t,n)=>{document?.activeElement===t&&e.announce(n)},s),d=e=>{let t=e.getAttribute(`data-color`);return!t||t===`danger`},f=e=>e instanceof HTMLElement&&`validity`in e&&!(e instanceof HTMLButtonElement)&&e.type!==`hidden`;var p=class extends e.DSElement{_counter;_descs;_input;_validation;_unevents;_unmutate;connectedCallback(){this._unevents=e.on(this,`input`,this,e.QUICK_EVENT),this._unmutate=e.onMutation(this,()=>c(this),{attributeFilter:[t,n,`hidden`,`id`,`value`,a],attributes:!0,childList:!0,subtree:!0})}handleEvent(t){let{_counter:r,_input:i}=this;if(r?.isConnected&&i){let a=(Number(e.attr(r,n))||0)-i.value.length,o=a<0?`over`:`under`,s=(e.attrOrCSS(r,`data-${o}`)||l[o])?.replace(`%d`,`${Math.abs(a)}`);s||e.warn(`Missing data-${o} on:`,r),e.attr(r,`data-label`,s),e.attr(r,`data-state`,o),e.attr(r,`data-color`,a<0?`danger`:null),e.attr(i,`aria-invalid`,a<0?`true`:null),t?.type===`input`&&s&&u(i,s)}i instanceof HTMLTextAreaElement&&(i.style.setProperty(`--_ds-field-sizing`,`auto`),i.style.setProperty(`--_ds-field-sizing`,`${i.scrollHeight}px`))}disconnectedCallback(){this._unevents?.(),this._unmutate?.(),this._unevents=this._unmutate=void 0,this._input=this._counter=this._descs=this._validation=void 0}};e.customElements.define(`ds-field`,p),exports.DSFieldElement=p;
|
|
2
2
|
//# sourceMappingURL=field.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.cjs","names":["isWindows","useId","attr","debounce","DSElement","on","QUICK_EVENT","onMutation","attrOrCSS","customElements"],"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_FIELD = 'data-field';\nconst ATTR_LIMIT = 'data-limit';\nconst ATTR_INVALID = 'aria-invalid';\nconst ATTR_DESCRIBEDBY = 'aria-describedby';\nconst ATTR_INDETERMINATE = 'data-indeterminate';\nconst SELECTOR_VALIDATION = `:scope > [${ATTR_FIELD}=\"validation\"]`;\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(ATTR_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 || isInvalidColor(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>(SELECTOR_VALIDATION);\n\n // Connect fieldset validation to inputs\n if (fieldsetValidation && !fieldsetValidation?.hidden) {\n hasValidation = true;\n invalid = invalid || isInvalidColor(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, invalid ? 'true' : null);\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 // Using document?. as this function might run after document is unmounted by tests\n if (document?.activeElement === input) announce(text); // Only announce if input is still focused\n}, COUNTER_DEBOUNCE);\n\nconst isInvalidColor = (el: Element) => {\n const color = el.getAttribute('data-color');\n return !color || color === 'danger';\n};\nconst isInputLike = (el: unknown): el is HTMLInputElement =>\n el instanceof HTMLElement &&\n 'validity' in el && // Adds support for custom elements implemeted with attachInternals()\n !(el instanceof HTMLButtonElement) && // But skip <button> elements\n (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 ATTR_FIELD,\n ATTR_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, ATTR_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 attr(_input, 'aria-invalid', count < 0 ? 'true' : null); // Toggle aria-invalid of textarea\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
|
+
{"version":3,"file":"field.cjs","names":["isWindows","useId","attr","debounce","DSElement","on","QUICK_EVENT","onMutation","attrOrCSS","customElements"],"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_FIELD = 'data-field';\nconst ATTR_LIMIT = 'data-limit';\nconst ATTR_INVALID = 'aria-invalid';\nconst ATTR_DESCRIBEDBY = 'aria-describedby';\nconst ATTR_INDETERMINATE = 'data-indeterminate';\nconst SELECTOR_VALIDATION = `:scope > [${ATTR_FIELD}=\"validation\"]`;\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(ATTR_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 || isInvalidColor(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>(SELECTOR_VALIDATION);\n\n // Connect fieldset validation to inputs\n if (fieldsetValidation && !fieldsetValidation?.hidden) {\n hasValidation = true;\n invalid = invalid || isInvalidColor(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, invalid ? 'true' : null);\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 // Using document?. as this function might run after document is unmounted by tests\n if (document?.activeElement === input) announce(text); // Only announce if input is still focused\n}, COUNTER_DEBOUNCE);\n\nconst isInvalidColor = (el: Element) => {\n const color = el.getAttribute('data-color');\n return !color || color === 'danger';\n};\nconst isInputLike = (el: unknown): el is HTMLInputElement =>\n el instanceof HTMLElement &&\n 'validity' in el && // Adds support for custom elements implemeted with attachInternals()\n !(el instanceof HTMLButtonElement) && // But skip <button> elements\n (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 ATTR_FIELD,\n ATTR_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, ATTR_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 attr(_input, 'aria-invalid', count < 0 ? 'true' : null); // Toggle aria-invalid of textarea\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
|
-
Object.defineProperty(exports,"version",{enumerable:!0,get:function(){return`1.20.
|
|
1
|
+
Object.defineProperty(exports,"version",{enumerable:!0,get:function(){return`1.20.1`}});
|
|
2
2
|
//# sourceMappingURL=package.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.cjs","names":["isBrowser","isSupported","isPolyfilled","getComposedTarget","attr","getCSSProp","getRoot","offset","shift","limitShift","flip","size","autoUpdate","computePosition","on","QUICK_EVENT","onHotReload"],"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\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 let sized = false; // Only size once per open, so scrolling does not resize the popover\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 (sized) return;\n sized = true;\n const width = `${source.offsetWidth}px`; // Use offsetWidth to include padding, matching the width of the source element\n const maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;\n\n requestAnimationFrame(() => {\n // Avoid changing an observed element during ResizeObserver delivery.\n if (overscroll === 'fit' && el.style.width !== width)\n el.style.width = width;\n if (el.style.maxHeight !== maxHeight)\n el.style.maxHeight = maxHeight;\n });\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 descriptors = Object.getOwnPropertyDescriptors(HTMLElement.prototype);\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 Object.defineProperties(HTMLElement.prototype, {\n togglePopover: {\n ...descriptors.togglePopover,\n value(opt: TogglePopoverOptions) {\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))\n ? 'open'\n : '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 },\n showPopover: {\n ...descriptors.showPopover,\n value(opt: ShowPopoverOptions) {\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 },\n hidePopover: {\n ...descriptors.hidePopover,\n value() {\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 });\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 // Restore original methods on hot reload\n Object.defineProperties(HTMLElement.prototype, {\n togglePopover: { ...descriptors.togglePopover, value: togglePopover },\n showPopover: { ...descriptors.showPopover, value: showPopover },\n hidePopover: { ...descriptors.hidePopover, value: hidePopover },\n });\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
|
+
{"version":3,"file":"popover.cjs","names":["isBrowser","isSupported","isPolyfilled","getComposedTarget","attr","getCSSProp","getRoot","on","QUICK_EVENT","onHotReload"],"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\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 let sized = false; // Only size once per open, so scrolling does not resize the popover\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 (sized) return;\n sized = true;\n const width = `${source.offsetWidth}px`; // Use offsetWidth to include padding, matching the width of the source element\n const maxHeight = `${Math.max(50, availableHeight - padding * 2)}px`;\n\n requestAnimationFrame(() => {\n // Avoid changing an observed element during ResizeObserver delivery.\n if (overscroll === 'fit' && el.style.width !== width)\n el.style.width = width;\n if (el.style.maxHeight !== maxHeight)\n el.style.maxHeight = maxHeight;\n });\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 descriptors = Object.getOwnPropertyDescriptors(HTMLElement.prototype);\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 Object.defineProperties(HTMLElement.prototype, {\n togglePopover: {\n ...descriptors.togglePopover,\n value(opt: TogglePopoverOptions) {\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))\n ? 'open'\n : '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 },\n showPopover: {\n ...descriptors.showPopover,\n value(opt: ShowPopoverOptions) {\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 },\n hidePopover: {\n ...descriptors.hidePopover,\n value() {\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 });\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 // Restore original methods on hot reload\n Object.defineProperties(HTMLElement.prototype, {\n togglePopover: { ...descriptors.togglePopover, value: togglePopover },\n showPopover: { ...descriptors.showPopover, value: showPopover },\n hidePopover: { ...descriptors.hidePopover, value: hidePopover },\n });\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
|
-
const e=require("../utils/utils.cjs");require("../popover/popover.cjs");let t,n,r,i,a=0;const o=`data-color`,s=`data-color-scheme`,c=`data-size`,l=`data-tooltip`,u=e.isBrowser()&&/iPad|iPhone|iPod/.test(navigator.userAgent),d=`[${o}]`,f=`[${s}]`,p=`[${c}]`,m=`[${l}]`,h=n=>{n&&!(n instanceof HTMLElement)&&e.warn(`setTooltipElement expects an HTMLElement, got: `,n),x(),a=0,t=n||void 0},g=(e=document)=>{for(let t of e?.querySelectorAll(m)||[])v(t)},_=(e,n)=>{if(!n)return g();for(let e of n)if(e.target!==t)
|
|
1
|
+
const e=require("../utils/utils.cjs");require("../popover/popover.cjs");let t,n,r,i,a=0;const o=`data-color`,s=`data-color-scheme`,c=`data-size`,l=`data-tooltip`,u=e.isBrowser()&&/iPad|iPhone|iPod/.test(navigator.userAgent),d=`[${o}]`,f=`[${s}]`,p=`[${c}]`,m=`[${l}]`,h=n=>{n&&!(n instanceof HTMLElement)&&e.warn(`setTooltipElement expects an HTMLElement, got: `,n),x(),a=0,t=n||void 0},g=(e=document)=>{for(let t of e?.querySelectorAll(m)||[])v(t)},_=(e,n)=>{if(!n)return g();for(let e of n)if(e.target!==t)if(e.attributeName===l)v(e.target);else for(let t of e.addedNodes)t.nodeType===1&&(t.hasAttribute(l)?v(t):g(t))},v=(n,r=!0)=>{let a=e.attrOrCSS(n,l)||``;if(a[0]===`#`&&(a=e.getRoot(n).getElementById(a.slice(1))?.textContent?.trim()||``),a!==(n.getAttribute(`aria-label`)||n.getAttribute(`aria-description`))){let t=e.attr(n,`role`)!==`img`&&n.textContent?.trim();e.attr(n,l,a),e.attr(n,e.ARIA_LABEL,t?null:a),e.attr(n,e.ARIA_DESC,t?a:null),n.tabIndex===-1&&e.warn(`Missing tabindex="0" attribute on: `,n)}n===i&&t?.textContent!==a&&(t&&(t.textContent=a),a&&r&&document.activeElement===n&&e.announce(a))},y=o=>{let s=e.getComposedTarget(o);if(!s||r===s||t?.contains(s))return;r=s;let c=r?.closest?.(m)||void 0;if(i!==c&&(i&&x(),i=c,c)){if(o.type===`focus`||u||300>Date.now()-a)return b();o.type===`mousemove`&&(n=setTimeout(b,300))}},b=()=>{if(!i)return x();t||=e.tag(`div`,{class:`ds-tooltip`}),t.isConnected||document.body.appendChild(t);let n=i.closest(d),r=i.closest(f),a=i.closest(p),l=n!==r&&n?.contains(r);e.attr(t,`popover`,`manual`),e.attr(t,s,r?.getAttribute(s)||null),e.attr(t,o,l&&n?.getAttribute(o)||null),e.attr(t,c,a?.getAttribute(c)||null),v(i,!1),t.showPopover(),t.dispatchEvent(new CustomEvent(`ds-toggle-source`,{bubbles:!0,composed:!0,detail:i}))},x=e=>{(e?.type!==`keydown`||e?.key===`Escape`)&&(i&&t?.isConnected&&t.popover&&t.hidePopover(),e||(a=Date.now()),clearTimeout(n),i=void 0)};e.onHotReload(`tooltip`,()=>[e.on(document,`focus mousemove`,y,e.QUICK_EVENT),e.on(document,`keydown`,x,e.QUICK_EVENT),e.onMutation(document,_,{attributeFilter:[l],attributes:!0,childList:!0,subtree:!0})]),exports.initTooltips=g,exports.setTooltipElement=h;
|
|
2
2
|
//# sourceMappingURL=tooltip.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.cjs","names":["isBrowser","attrOrCSS","getRoot","attr","ARIA_LABEL","ARIA_DESC","getComposedTarget","tag","onHotReload","on","QUICK_EVENT","onMutation"],"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":"wEAkBA,IAAI,EACA,EACA,EACA,EACA,EAAY,EAChB,MAAM,EAAa,aACb,EAAc,oBACd,EAAY,YACZ,EAAe,eAGf,EAASA,EAAAA,UAAU,GAAK,mBAAmB,KAAK,UAAU,SAAS,EACnE,EAAiB,IAAI,EAAW,GAChC,EAAkB,IAAI,EAAY,GAClC,EAAgB,IAAI,EAAU,GAC9B,EAAmB,IAAI,EAAa,GAO7B,EAAqB,GAA4B,CACxD,GAAM,EAAE,aAAc,cACxB,EAAA,KAAK,kDAAmD,CAAE,EAC5D,EAAK,EACL,EAAY,EACZ,EAAM,GAAM,IAAA,EACd,EAEa,GACX,EAAgD,WAC7C,CACH,IAAK,IAAM,KAAM,GAAO,iBAAiB,CAAgB,GAAK,CAAC,EAC7D,EAAU,CAAE,CAChB,EAGM,GAAmB,EAAa,IAA+B,CACnE,GAAI,CAAC,EAAS,OAAO,EAAa,EAClC,IAAK,IAAM,KAAK,EACV,KAAE,SAAW,EACjB,
|
|
1
|
+
{"version":3,"file":"tooltip.cjs","names":["isBrowser","attrOrCSS","getRoot","attr","ARIA_LABEL","ARIA_DESC","getComposedTarget","tag","onHotReload","on","QUICK_EVENT","onMutation"],"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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"}
|
|
@@ -141,13 +141,13 @@
|
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"kind": "javascript-module",
|
|
144
|
-
"path": "src/
|
|
144
|
+
"path": "src/clickdelegatefor/clickdelegatefor.ts",
|
|
145
145
|
"declarations": [],
|
|
146
146
|
"exports": []
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
149
|
"kind": "javascript-module",
|
|
150
|
-
"path": "src/
|
|
150
|
+
"path": "src/details/details.ts",
|
|
151
151
|
"declarations": [],
|
|
152
152
|
"exports": []
|
|
153
153
|
},
|
package/dist/esm/field/field.js
CHANGED
|
@@ -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,onMutation as l,useId as u,warn as d}from"../utils/utils.js";const f=`data-field`,p=`data-limit`,m=`aria-invalid`,h=`aria-describedby`,g=`data-indeterminate`,_=`:scope > [${f}="validation"]`,v=s()?800:200,y=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)
|
|
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=`data-field`,p=`data-limit`,m=`aria-invalid`,h=`aria-describedby`,g=`data-indeterminate`,_=`:scope > [${f}="validation"]`,v=s()?800:200,y=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(C(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(f);t===`counter`&&(e._counter=r),t===`validation`?(n.unshift(u(r)),a=!0,o||=S(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(_);c&&!c?.hidden&&(a=!0,o||=S(c),n.unshift(u(c)));let l=r(s,g);l&&(s.indeterminate=l===`true`),(s.type===`radio`||s.type===`checkbox`)&&r(e,`data-clickdelegatefor`,u(s));let p=(r(s,h)?.trim().split(/\s+/))?.filter(e=>!i.includes(e))||[];r(s,h,[...n,...p].join(` `)||null),e._descs=n;let v=e._validation;a&&!v&&!s.hasAttribute(m)?(r(s,m,o?`true`:null),e._validation=!0):!a&&v&&(r(s,m,null),e._validation=void 0),e.handleEvent()},b={over:`%d tegn for mye`,under:`%d tegn igjen`},x=o((e,t)=>{document?.activeElement===e&&n(t)},v),S=e=>{let t=e.getAttribute(`data-color`);return!t||t===`danger`},C=e=>e instanceof HTMLElement&&`validity`in e&&!(e instanceof HTMLButtonElement)&&e.type!==`hidden`;var w=class extends e{_counter;_descs;_input;_validation;_unevents;_unmutate;connectedCallback(){this._unevents=c(this,`input`,this,t),this._unmutate=l(this,()=>y(this),{attributeFilter:[f,p,`hidden`,`id`,`value`,g],attributes:!0,childList:!0,subtree:!0})}handleEvent(e){let{_counter:t,_input:n}=this;if(t?.isConnected&&n){let a=(Number(r(t,p))||0)-n.value.length,o=a<0?`over`:`under`,s=(i(t,`data-${o}`)||b[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),r(n,`aria-invalid`,a<0?`true`:null),e?.type===`input`&&s&&x(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`,w);export{w 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 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_FIELD = 'data-field';\nconst ATTR_LIMIT = 'data-limit';\nconst ATTR_INVALID = 'aria-invalid';\nconst ATTR_DESCRIBEDBY = 'aria-describedby';\nconst ATTR_INDETERMINATE = 'data-indeterminate';\nconst SELECTOR_VALIDATION = `:scope > [${ATTR_FIELD}=\"validation\"]`;\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(ATTR_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 || isInvalidColor(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>(SELECTOR_VALIDATION);\n\n // Connect fieldset validation to inputs\n if (fieldsetValidation && !fieldsetValidation?.hidden) {\n hasValidation = true;\n invalid = invalid || isInvalidColor(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, invalid ? 'true' : null);\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 // Using document?. as this function might run after document is unmounted by tests\n if (document?.activeElement === input) announce(text); // Only announce if input is still focused\n}, COUNTER_DEBOUNCE);\n\nconst isInvalidColor = (el: Element) => {\n const color = el.getAttribute('data-color');\n return !color || color === 'danger';\n};\nconst isInputLike = (el: unknown): el is HTMLInputElement =>\n el instanceof HTMLElement &&\n 'validity' in el && // Adds support for custom elements implemeted with attachInternals()\n !(el instanceof HTMLButtonElement) && // But skip <button> elements\n (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 ATTR_FIELD,\n ATTR_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, ATTR_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 attr(_input, 'aria-invalid', count < 0 ? 'true' : null); // Toggle aria-invalid of textarea\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
|
+
{"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_FIELD = 'data-field';\nconst ATTR_LIMIT = 'data-limit';\nconst ATTR_INVALID = 'aria-invalid';\nconst ATTR_DESCRIBEDBY = 'aria-describedby';\nconst ATTR_INDETERMINATE = 'data-indeterminate';\nconst SELECTOR_VALIDATION = `:scope > [${ATTR_FIELD}=\"validation\"]`;\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(ATTR_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 || isInvalidColor(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>(SELECTOR_VALIDATION);\n\n // Connect fieldset validation to inputs\n if (fieldsetValidation && !fieldsetValidation?.hidden) {\n hasValidation = true;\n invalid = invalid || isInvalidColor(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, invalid ? 'true' : null);\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 // Using document?. as this function might run after document is unmounted by tests\n if (document?.activeElement === input) announce(text); // Only announce if input is still focused\n}, COUNTER_DEBOUNCE);\n\nconst isInvalidColor = (el: Element) => {\n const color = el.getAttribute('data-color');\n return !color || color === 'danger';\n};\nconst isInputLike = (el: unknown): el is HTMLInputElement =>\n el instanceof HTMLElement &&\n 'validity' in el && // Adds support for custom elements implemeted with attachInternals()\n !(el instanceof HTMLButtonElement) && // But skip <button> elements\n (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 ATTR_FIELD,\n ATTR_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, ATTR_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 attr(_input, 'aria-invalid', count < 0 ? 'true' : null); // Toggle aria-invalid of textarea\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
|
-
var e=`1.20.
|
|
1
|
+
var e=`1.20.1`;export{e as version};
|
|
2
2
|
//# sourceMappingURL=package.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
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)
|
|
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 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":"mQAkBA,IAAI,EACA,EACA,EACA,EACA,EAAY,EAChB,MAAM,EAAa,aACb,EAAc,oBACd,EAAY,YACZ,EAAe,eAGf,EAAS,EAAU,GAAK,mBAAmB,KAAK,UAAU,SAAS,EACnE,EAAiB,IAAI,EAAW,GAChC,EAAkB,IAAI,EAAY,GAClC,EAAgB,IAAI,EAAU,GAC9B,EAAmB,IAAI,EAAa,GAO7B,EAAqB,GAA4B,CACxD,GAAM,EAAE,aAAc,cACxB,EAAK,kDAAmD,CAAE,EAC5D,EAAK,EACL,EAAY,EACZ,EAAM,GAAM,IAAA,EACd,EAEa,GACX,EAAgD,WAC7C,CACH,IAAK,IAAM,KAAM,GAAO,iBAAiB,CAAgB,GAAK,CAAC,EAC7D,EAAU,CAAE,CAChB,EAGM,GAAmB,EAAa,IAA+B,CACnE,GAAI,CAAC,EAAS,OAAO,EAAa,EAClC,IAAK,IAAM,KAAK,EACV,KAAE,SAAW,EACjB,
|
|
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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"}
|