{"version":3,"names":["labelClickEvent","labelConnectedEvent","labelDisconnectedEvent","labelTagName","labelToLabelables","WeakMap","onLabelClickMap","onLabelConnectedMap","onLabelDisconnectedMap","unlabeledComponents","Set","findLabelForComponent","componentEl","id","forLabel","queryElementRoots","selector","parentLabel","closestElementCrossShadowBoundary","hasAncestorCustomElements","label","traversedElements","customElementAncestorCheckEventType","listener","event","stopImmediatePropagation","composedPath","slice","indexOf","addEventListener","once","dispatchEvent","CustomEvent","composed","bubbles","removeEventListener","ancestorCustomElements","filter","el","tagName","includes","length","connectLabel","component","labelEl","has","boundOnLabelDisconnected","onLabelDisconnected","bind","labelables","get","push","set","sort","sortByDOMOrder","onLabelClick","delete","document","disconnectLabel","labelable","a","b","isBefore","getLabelText","textContent","trim","labelClickTarget","detail","sourceEvent","target","this","clickedLabelable","find","labelableChildClicked","firstLabelable","disabled","onLabelConnected","add","boundOnLabelConnected","async","associateExplicitLabelToUnlabeledComponent","componentOnReady","alreadyLabeled","forComponentEl","ownerDocument","getElementById","for","requestAnimationFrame"],"sources":["src/utils/label.ts"],"sourcesContent":["import { closestElementCrossShadowBoundary, isBefore, queryElementRoots } from \"./dom\";\nimport { componentOnReady } from \"./component\";\n\nexport interface LabelableComponent {\n /**\n * When true, disabled prevents interaction.\n */\n disabled: boolean;\n\n /**\n * The host element.\n */\n readonly el: HTMLElement;\n\n /**\n * Text label.\n */\n label?: string;\n\n /**\n * The label this component is associated with.\n */\n labelEl: HTMLCalciteLabelElement;\n\n /**\n * Hook for components to provide custom label click behavior.\n */\n onLabelClick: (event: CustomEvent) => void;\n}\n\n/**\n * Exported for testing purposes only\n *\n * @internal\n */\nexport const labelClickEvent = \"calciteInternalLabelClick\";\nexport const labelConnectedEvent = \"calciteInternalLabelConnected\";\nexport const labelDisconnectedEvent = \"calciteInternalLabelDisconnected\";\n\nconst labelTagName = \"calcite-label\";\nconst labelToLabelables = new WeakMap();\nconst onLabelClickMap = new WeakMap();\nconst onLabelConnectedMap = new WeakMap();\nconst onLabelDisconnectedMap = new WeakMap();\nconst unlabeledComponents = new Set();\n\nconst findLabelForComponent = (componentEl: HTMLElement): HTMLCalciteLabelElement | null => {\n const { id } = componentEl;\n\n const forLabel =\n id && queryElementRoots(componentEl, { selector: `${labelTagName}[for=\"${id}\"]` });\n\n if (forLabel) {\n return forLabel;\n }\n\n const parentLabel = closestElementCrossShadowBoundary(componentEl, labelTagName);\n\n if (\n !parentLabel ||\n // labelable components within other custom elements are not considered labelable\n hasAncestorCustomElements(parentLabel, componentEl)\n ) {\n return null;\n }\n\n return parentLabel;\n};\n\nfunction hasAncestorCustomElements(label: HTMLCalciteLabelElement, componentEl: HTMLElement): boolean {\n let traversedElements: HTMLElement[];\n const customElementAncestorCheckEventType = \"custom-element-ancestor-check\";\n\n const listener = (event: CustomEvent) => {\n event.stopImmediatePropagation();\n const composedPath = event.composedPath() as HTMLElement[];\n traversedElements = composedPath.slice(composedPath.indexOf(componentEl), composedPath.indexOf(label));\n };\n\n label.addEventListener(customElementAncestorCheckEventType, listener, { once: true });\n\n componentEl.dispatchEvent(new CustomEvent(customElementAncestorCheckEventType, { composed: true, bubbles: true }));\n label.removeEventListener(customElementAncestorCheckEventType, listener);\n\n const ancestorCustomElements = traversedElements\n .filter((el) => el !== componentEl && el !== label)\n .filter((el) => el.tagName?.includes(\"-\"));\n\n return ancestorCustomElements.length > 0;\n}\n\n/**\n * Helper to set up label interactions on connectedCallback.\n *\n * @param component\n */\nexport function connectLabel(component: LabelableComponent): void {\n if (!component) {\n return;\n }\n\n const labelEl = findLabelForComponent(component.el);\n\n if (\n (onLabelClickMap.has(labelEl) && labelEl === component.labelEl) ||\n (!labelEl && unlabeledComponents.has(component))\n ) {\n return;\n }\n\n const boundOnLabelDisconnected = onLabelDisconnected.bind(component);\n\n if (labelEl) {\n component.labelEl = labelEl;\n\n const labelables = labelToLabelables.get(labelEl) || [];\n labelables.push(component);\n labelToLabelables.set(labelEl, labelables.sort(sortByDOMOrder));\n\n if (!onLabelClickMap.has(component.labelEl)) {\n onLabelClickMap.set(component.labelEl, onLabelClick);\n component.labelEl.addEventListener(labelClickEvent, onLabelClick);\n }\n\n unlabeledComponents.delete(component);\n document.removeEventListener(labelConnectedEvent, onLabelConnectedMap.get(component));\n onLabelDisconnectedMap.set(component, boundOnLabelDisconnected);\n document.addEventListener(labelDisconnectedEvent, boundOnLabelDisconnected);\n } else if (!unlabeledComponents.has(component)) {\n boundOnLabelDisconnected();\n document.removeEventListener(labelDisconnectedEvent, onLabelDisconnectedMap.get(component));\n }\n}\n/**\n * Helper to tear down label interactions on disconnectedCallback on labelable components.\n *\n * @param component\n */\nexport function disconnectLabel(component: LabelableComponent): void {\n if (!component) {\n return;\n }\n\n unlabeledComponents.delete(component);\n document.removeEventListener(labelConnectedEvent, onLabelConnectedMap.get(component));\n document.removeEventListener(labelDisconnectedEvent, onLabelDisconnectedMap.get(component));\n onLabelConnectedMap.delete(component);\n onLabelDisconnectedMap.delete(component);\n\n if (!component.labelEl) {\n return;\n }\n\n const labelables = labelToLabelables.get(component.labelEl);\n\n if (labelables.length === 1) {\n component.labelEl.removeEventListener(labelClickEvent, onLabelClickMap.get(component.labelEl));\n onLabelClickMap.delete(component.labelEl);\n }\n\n labelToLabelables.set(\n component.labelEl,\n labelables.filter((labelable) => labelable !== component).sort(sortByDOMOrder),\n );\n\n component.labelEl = null;\n}\n\nfunction sortByDOMOrder(a: LabelableComponent, b: LabelableComponent): number {\n return isBefore(a.el, b.el) ? -1 : 1;\n}\n\n/**\n * Helper to get the label text from a component.\n *\n * @param component\n */\nexport function getLabelText(component: LabelableComponent): string {\n return component.label || component.labelEl?.textContent?.trim() || \"\";\n}\n\nfunction onLabelClick(this: HTMLCalciteLabelElement, event: CustomEvent<{ sourceEvent: MouseEvent }>): void {\n const labelClickTarget = event.detail.sourceEvent.target as HTMLElement;\n const labelables = labelToLabelables.get(this);\n const clickedLabelable = labelables.find((labelable) => labelable.el === labelClickTarget);\n const labelableChildClicked = labelables.includes(clickedLabelable);\n\n if (labelableChildClicked) {\n // no need to forward click as labelable will receive focus\n return;\n }\n\n const firstLabelable = labelables[0];\n\n if (firstLabelable.disabled) {\n return;\n }\n\n firstLabelable.onLabelClick(event);\n}\n\nfunction onLabelConnected(this: LabelableComponent): void {\n if (unlabeledComponents.has(this)) {\n connectLabel(this);\n }\n}\n\nfunction onLabelDisconnected(this: LabelableComponent): void {\n unlabeledComponents.add(this);\n const boundOnLabelConnected = onLabelConnectedMap.get(this) || onLabelConnected.bind(this);\n onLabelConnectedMap.set(this, boundOnLabelConnected);\n document.addEventListener(labelConnectedEvent, boundOnLabelConnected);\n}\n\n/**\n * Helper to associate an explicit label (i.e., using `for`) with a labelable component that does not have an associated label.\n *\n * @param label - the label element\n */\nexport async function associateExplicitLabelToUnlabeledComponent(label: HTMLCalciteLabelElement): Promise {\n await componentOnReady(label);\n\n const alreadyLabeled = labelToLabelables.has(label);\n\n if (alreadyLabeled) {\n return;\n }\n\n const forComponentEl = label.ownerDocument?.getElementById(label.for);\n\n if (!forComponentEl) {\n return;\n }\n\n requestAnimationFrame(() => {\n for (const labelable of unlabeledComponents) {\n if (labelable.el === forComponentEl) {\n connectLabel(labelable);\n break;\n }\n }\n });\n}\n"],"mappings":"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","ignoreList":[]}