@spectrum-web-components/overlay 0.19.6-overlay.11 → 0.19.6-overlay.13
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/package.json +6 -6
- package/src/Overlay.dev.js +15 -1
- package/src/Overlay.dev.js.map +2 -2
- package/src/Overlay.js +1 -1
- package/src/Overlay.js.map +3 -3
- package/src/OverlayBase.d.ts +4 -2
- package/src/OverlayBase.dev.js +31 -9
- package/src/OverlayBase.dev.js.map +2 -2
- package/src/OverlayBase.js +4 -4
- package/src/OverlayBase.js.map +3 -3
- package/src/OverlayDialog.dev.js +3 -3
- package/src/OverlayDialog.dev.js.map +2 -2
- package/src/OverlayDialog.js +1 -1
- package/src/OverlayDialog.js.map +2 -2
- package/src/OverlayNoPopover.dev.js +3 -3
- package/src/OverlayNoPopover.dev.js.map +2 -2
- package/src/OverlayNoPopover.js +1 -1
- package/src/OverlayNoPopover.js.map +2 -2
- package/src/OverlayPopover.dev.js +7 -4
- package/src/OverlayPopover.dev.js.map +2 -2
- package/src/OverlayPopover.js +1 -1
- package/src/OverlayPopover.js.map +2 -2
- package/src/OverlayTrigger.d.ts +1 -1
- package/src/OverlayTrigger.dev.js +0 -16
- package/src/OverlayTrigger.dev.js.map +2 -2
- package/src/OverlayTrigger.js +2 -2
- package/src/OverlayTrigger.js.map +2 -2
- package/src/PlacementController.dev.js +11 -7
- package/src/PlacementController.dev.js.map +2 -2
- package/src/PlacementController.js.map +2 -2
- package/src/overlay-base.css.dev.js +1 -1
- package/src/overlay-base.css.dev.js.map +1 -1
- package/src/overlay-base.css.js +1 -1
- package/src/overlay-base.css.js.map +1 -1
- package/stories/overlay-element.stories.js +2 -1
- package/stories/overlay-element.stories.js.map +2 -2
- package/test/overlay-element.test.js +27 -12
- package/test/overlay-element.test.js.map +2 -2
- package/test/overlay.test.js +4 -21
- package/test/overlay.test.js.map +2 -2
package/src/OverlayBase.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["OverlayBase.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport type { Placement } from '@floating-ui/dom';\nimport {\n html,\n PropertyValues,\n SpectrumElement,\n TemplateResult,\n} from '@spectrum-web-components/base';\nimport {\n property,\n query,\n queryAssignedElements,\n state,\n} from '@spectrum-web-components/base/src/decorators.js';\nimport { conditionAttributeWithId } from '@spectrum-web-components/base/src/condition-attribute-with-id.js';\nimport {\n ElementResolutionController,\n elementResolverUpdatedSymbol,\n} from '@spectrum-web-components/reactive-controllers/src/ElementResolution.js';\nimport type { VirtualTrigger } from './VirtualTrigger.js';\nimport {\n ifDefined,\n styleMap,\n} from '@spectrum-web-components/base/src/directives.js';\nimport styles from './overlay-base.css.js';\nimport { overlayStack } from './OverlayStack.js';\nimport { PlacementController } from './PlacementController.js';\n\nexport type OpenableElement = HTMLElement & {\n open: boolean;\n tipElement?: HTMLElement;\n updateComplete?: Promise<void>;\n};\n\nexport type OverlayTypes = 'auto' | 'hint' | 'manual' | 'modal' | 'page';\n\nconst LONGPRESS_DURATION = 300;\n\nexport type LongpressEvent = {\n source: 'pointer' | 'keyboard';\n};\n\nexport const LONGPRESS_INSTRUCTIONS = {\n touch: 'Double tap and long press for additional options',\n keyboard: 'Press Space or Alt+Down Arrow for additional options',\n mouse: 'Click and hold for additional options',\n};\n\nexport class BeforetoggleClosedEvent extends Event {\n currentState = 'open';\n newState = 'closed';\n constructor() {\n super('beforetoggle', {\n bubbles: false,\n composed: false,\n });\n }\n}\n\nexport class BeforetoggleOpenEvent extends Event {\n currentState = 'closed';\n newState = 'open';\n constructor() {\n super('beforetoggle', {\n bubbles: false,\n composed: false,\n });\n }\n}\n\nexport const noop = (): void => {\n return;\n};\n\n/**\n * Apply a \"transitionend\" listener to an element that may not transition but\n * guarantee the callback will be fired either way.\n *\n * @param el {HTMLElement} - Target of the \"transition\" listeners.\n * @param action {Function} - Method to trigger the \"transition\".\n * @param cb {Function} - Callback to trigger when the \"transition\" has ended.\n */\nexport const guaranteedTransitionend = (\n el: HTMLElement,\n action: () => void,\n cb: () => void\n): void => {\n const cleanup = (): void => {\n el.removeEventListener('transitionrun', handleTransitionrun);\n el.removeEventListener('transitionend', handleTransitionend);\n cb();\n };\n let guarantee2: number;\n let guarantee3: number;\n // WebKit fires `transitionrun` a little earlier, so the inner/outer relationship\n // here allows WebKit to be caught, but doesn't remove the animation listener until\n // after it would have fired in Chromium.\n const guarantee1 = requestAnimationFrame(() => {\n guarantee2 = requestAnimationFrame(() => {\n guarantee3 = requestAnimationFrame(() => {\n cleanup();\n });\n });\n });\n const handleTransitionend = (event: TransitionEvent): void => {\n if (event.propertyName === 'visibility') {\n // Ignore \"visibility\" transitions because they often happen before/after a\n // larger transition and don't represent the overall transition duration.\n return;\n }\n cleanup();\n };\n const handleTransitionrun = (event: TransitionEvent): void => {\n if (event.propertyName === 'visibility') {\n // Ignore \"visibility\" transitions because they often happen before/after a\n // larger transition and don't represent the overall transition duration.\n return;\n }\n cancelAnimationFrame(guarantee1);\n cancelAnimationFrame(guarantee2);\n cancelAnimationFrame(guarantee3);\n el.removeEventListener('transitionrun', handleTransitionrun);\n el.addEventListener('transitionend', handleTransitionend);\n };\n el.addEventListener('transitionrun', handleTransitionrun);\n action();\n};\n\n/**\n * @element sp-overlay\n */\nexport class OverlayBase extends SpectrumElement {\n static override styles = [styles];\n\n @query('dialog')\n dialogEl!: HTMLDialogElement & {\n showPopover(): void;\n hidePopover(): void;\n };\n\n @property({ type: Boolean })\n get disabled(): boolean {\n return this._disabled;\n }\n\n set disabled(disabled: boolean) {\n this._disabled = disabled;\n if (disabled) {\n this.wasOpen = this.open;\n this.open = false;\n } else {\n this.open = this.open || this.wasOpen;\n this.wasOpen = false;\n }\n }\n\n private _disabled = false;\n\n protected dispose = noop;\n\n @queryAssignedElements({ flatten: true })\n elements!: OpenableElement[];\n\n public parentOverlayToForceClose?: OverlayBase;\n\n protected longpressed = false;\n\n private longressTimeout!: ReturnType<typeof setTimeout>;\n\n @property()\n offset: number | [number, number] = 6;\n\n public placementController = new PlacementController(this);\n\n @property({ type: Boolean, reflect: true })\n get open(): boolean {\n return this._open;\n }\n\n set open(open: boolean) {\n if (open && this.disabled) return;\n if (open === this.open) return;\n this._open = open;\n this.requestUpdate('open', !this.open);\n }\n\n private _open = false;\n\n static openCount = 1;\n\n @property()\n placement?: Placement;\n\n @property({ attribute: 'receives-focus' })\n receivesFocus: 'true' | 'false' | 'auto' = 'auto';\n\n private releaseAriaDescribedby = noop;\n\n @query('slot')\n slotEl!: HTMLSlotElement;\n\n @property()\n trigger?: string;\n\n @state()\n triggerElement: HTMLElement | VirtualTrigger | null = null;\n\n @state()\n triggerInteraction?: 'click' | 'longpress' | 'hover';\n\n @property()\n type: OverlayTypes = 'hint';\n\n protected wasOpen = false;\n\n private elementResolver = new ElementResolutionController(this);\n\n private get usesDialog(): boolean {\n return this.type === 'modal' || this.type === 'page';\n }\n\n private get popoverValue(): 'auto' | 'manual' | undefined {\n switch (this.type) {\n case 'modal':\n case 'page':\n return undefined;\n case 'hint':\n return 'manual';\n default:\n return this.type;\n }\n }\n\n /* c8 ignore next 12 */\n protected async manageDialogOpen(): Promise<void> {\n console.warn(\n 'Implement the `manageDialogOpen` method in a class extension.'\n );\n }\n\n protected async managePopoverOpen(): Promise<void> {\n console.warn(\n 'Implement the `managePopoverOpen` method in a class extension.'\n );\n }\n\n protected get requiresPosition(): boolean {\n // Do no position \"page\" overlays as they should block the entrie UI.\n if (this.type === 'page' || !this.open) return false;\n // Do not position content without a trigger element, what would you position it in relation to?\n // Do not automaticallyu position contnent, unless it is a \"hint\".\n if (!this.triggerElement || (!this.placement && this.type !== 'hint'))\n return false;\n return true;\n }\n\n protected managePosition(): void {\n if (!this.requiresPosition || !this.open) return;\n\n const offset = this.offset || 0;\n const trigger = this.triggerElement as HTMLElement;\n const placement = (this.placement as Placement) || 'right';\n\n this.placementController.placeOverlay(this.dialogEl, {\n // delayed?: boolean,\n offset,\n placement,\n // notImmediatelyClosable?: boolean, // rename or place behind other API options\n // receivesFocus?: 'auto';\n // root?: HTMLElement;\n trigger,\n type: this.type,\n });\n }\n\n protected async manageOpen(oldOpen: boolean): Promise<void> {\n if (!this.isConnected) return;\n\n if (!this.hasUpdated) {\n await this.updateComplete;\n }\n\n if (this.open) {\n overlayStack.add(this);\n } else {\n if (oldOpen) {\n this.dispose();\n }\n overlayStack.remove(this);\n }\n\n if (this.usesDialog) {\n this.manageDialogOpen();\n } else {\n this.managePopoverOpen();\n }\n if (this.open) {\n OverlayBase.openCount += 1;\n } else {\n // If the focus remains inside of the overlay or\n // a slotted descendent of the overlay you need to return\n // focus back to the trigger.\n const getAncestors = (): HTMLElement[] => {\n const ancestors: HTMLElement[] = [];\n // eslint-disable-next-line @spectrum-web-components/document-active-element\n let currentNode = document.activeElement;\n while (\n currentNode?.shadowRoot &&\n currentNode.shadowRoot.activeElement\n ) {\n currentNode = currentNode.shadowRoot.activeElement;\n }\n while (currentNode) {\n const ancestor =\n currentNode.assignedSlot ||\n currentNode.parentElement ||\n (currentNode.getRootNode() as ShadowRoot)?.host;\n if (ancestor) {\n ancestors.push(ancestor as HTMLElement);\n }\n currentNode = ancestor;\n }\n return ancestors;\n };\n if (\n (this.triggerElement as HTMLElement)?.focus &&\n (this.contains(\n (this.getRootNode() as Document).activeElement\n ) ||\n !!getAncestors().find((el) => el === this))\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n }\n }\n\n protected manageTriggerElement(triggerElement: HTMLElement | null): void {\n if (triggerElement) {\n triggerElement.removeEventListener('click', this.handleClick);\n triggerElement.removeEventListener('focusin', this.handleFocusin);\n triggerElement.removeEventListener('focusout', this.handleFocusout);\n triggerElement.removeEventListener(\n 'pointerenter',\n this.handlePointerenter\n );\n triggerElement.removeEventListener(\n 'pointerleave',\n this.handlePointerleave\n );\n triggerElement.addEventListener(\n 'pointerdown',\n this.handlePointerdown\n );\n triggerElement.removeEventListener('keydown', this.handleKeydown);\n triggerElement.removeEventListener('keyup', this.handleKeyup);\n triggerElement.removeEventListener(\n 'longpress',\n this.handleLongpress\n );\n this.releaseAriaDescribedby();\n }\n if (\n !this.triggerElement ||\n !!(this.triggerElement as VirtualTrigger).updateBoundingClientRect\n )\n return;\n const nextTriggerElement = this.triggerElement as HTMLElement;\n switch (this.triggerInteraction) {\n case 'click':\n nextTriggerElement.addEventListener('click', this.handleClick);\n return;\n case 'longpress':\n nextTriggerElement.addEventListener(\n 'pointerdown',\n this.handlePointerdown\n );\n nextTriggerElement.addEventListener(\n 'keydown',\n this.handleKeydown\n );\n nextTriggerElement.addEventListener('keyup', this.handleKeyup);\n nextTriggerElement.addEventListener(\n 'longpress',\n this.handleLongpress\n );\n return;\n case 'hover':\n nextTriggerElement.addEventListener(\n 'focusin',\n this.handleFocusin\n );\n nextTriggerElement.addEventListener(\n 'focusout',\n this.handleFocusout\n );\n nextTriggerElement.addEventListener(\n 'pointerenter',\n this.handlePointerenter\n );\n nextTriggerElement.addEventListener(\n 'pointerleave',\n this.handlePointerleave\n );\n if (this.receivesFocus === 'true') return;\n\n this.prepareAriaDescribedby(nextTriggerElement);\n return;\n }\n }\n\n private elementIds: string[] = [];\n\n private prepareAriaDescribedby(trigger: HTMLElement): void {\n if (\n // only \"hover\" relationships establed described by content\n this.triggerInteraction !== 'hover' ||\n // do not reapply until target is recycled\n this.releaseAriaDescribedby !== noop ||\n // require \" hover content\" to apply relationship\n !this.elements.length\n )\n return;\n\n const triggerRoot = trigger.getRootNode();\n const contentRoot = this.elements[0].getRootNode();\n const overlayRoot = this.getRootNode();\n if (triggerRoot == overlayRoot) {\n const releaseAriaDescribedby = conditionAttributeWithId(\n trigger,\n 'aria-describedby',\n [this.id]\n );\n this.releaseAriaDescribedby = () => {\n releaseAriaDescribedby();\n this.releaseAriaDescribedby = noop;\n };\n } else if (triggerRoot === contentRoot) {\n this.elementIds = this.elements.map((el) => el.id);\n const appliedIds = this.elements.map((el) => {\n if (!el.id) {\n el.id = `${this.tagName.toLowerCase()}-helper-${crypto\n .randomUUID()\n .slice(0, 8)}`;\n }\n return el.id;\n });\n const releaseAriaDescribedby = conditionAttributeWithId(\n trigger,\n 'aria-describedby',\n appliedIds\n );\n this.releaseAriaDescribedby = () => {\n releaseAriaDescribedby();\n this.elements.map((el, index) => {\n el.id = this.elementIds[index];\n });\n this.releaseAriaDescribedby = noop;\n };\n }\n }\n\n private handlePointerdown = (event: PointerEvent): void => {\n if (!this.triggerElement) return;\n if (event.button !== 0) return;\n const triggerElement = this.triggerElement as HTMLElement;\n this.longpressed = false;\n triggerElement.addEventListener('pointerup', this.handlePointerup);\n triggerElement.addEventListener('pointercancel', this.handlePointerup);\n this.longressTimeout = setTimeout(() => {\n if (!triggerElement) return;\n triggerElement.dispatchEvent(\n new CustomEvent<LongpressEvent>('longpress', {\n bubbles: true,\n composed: true,\n detail: {\n source: 'pointer',\n },\n })\n );\n }, LONGPRESS_DURATION);\n };\n\n private handlePointerup = (): void => {\n clearTimeout(this.longressTimeout);\n if (!this.triggerElement) return;\n if (this.longpressed) {\n this.open = true;\n }\n setTimeout(() => {\n this.longpressed = false;\n });\n const triggerElement = this.triggerElement as HTMLElement;\n triggerElement.removeEventListener('pointerup', this.handlePointerup);\n triggerElement.removeEventListener(\n 'pointercancel',\n this.handlePointerup\n );\n };\n\n /**\n * @private\n */\n protected handleKeydown = (event: KeyboardEvent): void => {\n const { code, altKey } = event;\n if (code === 'Space' || (altKey && code === 'ArrowDown')) {\n if (code === 'ArrowDown') {\n event.stopPropagation();\n event.stopImmediatePropagation();\n }\n }\n };\n\n protected handleKeyup = (event: KeyboardEvent): void => {\n const { code, altKey } = event;\n if (code === 'Space' || (altKey && code === 'ArrowDown')) {\n event.stopPropagation();\n this.dispatchEvent(\n new CustomEvent<LongpressEvent>('longpress', {\n bubbles: true,\n composed: true,\n detail: {\n source: 'keyboard',\n },\n })\n );\n }\n };\n\n protected handleClick = (): void => {\n if (this.longpressed) return;\n this.open = !this.open;\n };\n\n private focusedin = false;\n\n protected handleFocusin = (): void => {\n this.open = true;\n this.focusedin = true;\n };\n\n protected handleFocusout = (): void => {\n this.focusedin = false;\n if (this.pointerentered) return;\n this.open = false;\n };\n\n private pointerentered = false;\n\n protected handlePointerenter = (): void => {\n this.open = true;\n this.pointerentered = true;\n };\n\n protected handlePointerleave = (): void => {\n this.pointerentered = false;\n const triggerElement = this.triggerElement as HTMLElement;\n if (this.focusedin && triggerElement.matches(':focus-visible')) return;\n this.open = false;\n };\n\n protected handleLongpress = (): void => {\n this.open = true;\n this.longpressed = true;\n };\n\n protected handleBeforetoggle({\n newState: open,\n }: Event & { newState: string }): void {\n if (open === 'open') {\n this.handlePopovershow();\n } else {\n this.handlePopoverhide();\n }\n }\n\n protected handlePopoverhide(): void {\n this.open = false;\n this.dispatchEvent(new BeforetoggleClosedEvent());\n }\n\n protected handlePopovershow(): void {\n this.dispatchEvent(new BeforetoggleOpenEvent());\n }\n\n public willPreventClose = false;\n\n public shouldPreventClose(): boolean {\n const shouldPreventClose = this.willPreventClose;\n this.willPreventClose = false;\n return shouldPreventClose;\n }\n\n override willUpdate(changes: PropertyValues): void {\n if (!this.hasUpdated) {\n this.addEventListener('focusout', (event: FocusEvent) => {\n if (this.type !== 'auto') {\n return;\n }\n if (!event.relatedTarget) {\n this.open = false;\n return;\n }\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n event.relatedTarget.addEventListener(\n relationEvent.type,\n (event: Event) => {\n if (!event.composedPath().includes(this)) {\n this.open = false;\n }\n }\n );\n event.relatedTarget.dispatchEvent(relationEvent);\n });\n }\n if (!this.hasAttribute('id')) {\n this.setAttribute(\n 'id',\n `${this.tagName.toLowerCase()}-${crypto\n .randomUUID()\n .slice(0, 8)}`\n );\n }\n if (\n changes.has('open') &&\n (typeof changes.get('open') !== 'undefined' || this.open)\n ) {\n this.manageOpen(changes.get('open'));\n }\n if (changes.has('trigger')) {\n const [id, interaction] = this.trigger?.split('@') || [];\n this.elementResolver.selector = id ? `#${id}` : '';\n this.triggerInteraction = interaction as\n | 'click'\n | 'longpress'\n | 'hover'\n | undefined;\n }\n const oldTrigger = this.triggerElement as HTMLElement;\n if (changes.has(elementResolverUpdatedSymbol)) {\n this.triggerElement = this.elementResolver.element;\n this.manageTriggerElement(oldTrigger);\n }\n if (changes.has('triggerElement')) {\n this.manageTriggerElement(changes.get('triggerElement'));\n }\n }\n\n protected override updated(changes: PropertyValues): void {\n super.updated(changes);\n if (changes.has('placement')) {\n if (this.placement) {\n this.dialogEl.setAttribute('actual-placement', this.placement);\n } else {\n this.dialogEl.removeAttribute('actual-placement');\n }\n if (typeof changes.get('placement') !== 'undefined') {\n this.managePosition();\n }\n }\n }\n\n public override render(): TemplateResult {\n const hasPopoverAttribute = 'popover' in this;\n const popoverValue = hasPopoverAttribute\n ? this.popoverValue\n : undefined;\n return html`\n <dialog\n part=\"dialog\"\n popover=${ifDefined(popoverValue)}\n @close=${() => {\n this.open = false;\n }}\n @cancel=${() => {\n this.open = false;\n }}\n @beforetoggle=${this.handleBeforetoggle}\n @popovershow=${this.handlePopovershow}\n style=${styleMap({\n '--swc-overlay-z-index': OverlayBase.openCount.toString(),\n })}\n >\n <div part=\"content\">\n <slot\n @slotchange=${() => {\n if (this.triggerElement) {\n this.prepareAriaDescribedby(\n this.triggerElement as HTMLElement\n );\n }\n }}\n ></slot>\n </div>\n </dialog>\n `;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n // this.manageOpen();\n this.addEventListener('close', () => {\n this.open = false;\n });\n }\n}\n"],
|
|
5
|
-
"mappings": "qNAaA,OACI,QAAAA,EAEA,mBAAAC,MAEG,gCACP,OACI,YAAAC,EACA,SAAAC,EACA,yBAAAC,EACA,SAAAC,MACG,kDACP,OAAS,4BAAAC,MAAgC,mEACzC,OACI,+BAAAC,EACA,gCAAAC,MACG,
|
|
6
|
-
"names": ["html", "SpectrumElement", "property", "query", "queryAssignedElements", "state", "conditionAttributeWithId", "ElementResolutionController", "elementResolverUpdatedSymbol", "ifDefined", "styleMap", "styles", "overlayStack", "PlacementController", "LONGPRESS_DURATION", "el", "action", "cb", "cleanup", "handleTransitionrun", "handleTransitionend", "guarantee2", "guarantee3", "guarantee1", "event", "_OverlayBase", "triggerElement", "code", "altKey", "disabled", "open", "offset", "trigger", "placement", "oldOpen", "_a", "getAncestors", "ancestors", "currentNode", "ancestor", "nextTriggerElement", "triggerRoot", "contentRoot", "overlayRoot", "releaseAriaDescribedby", "appliedIds", "index", "shouldPreventClose", "changes", "relationEvent", "id", "interaction", "oldTrigger", "popoverValue", "__decorateClass"]
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\n\nimport type { Placement } from '@floating-ui/dom';\nimport {\n html,\n PropertyValues,\n SpectrumElement,\n TemplateResult,\n} from '@spectrum-web-components/base';\nimport {\n property,\n query,\n queryAssignedElements,\n state,\n} from '@spectrum-web-components/base/src/decorators.js';\nimport { conditionAttributeWithId } from '@spectrum-web-components/base/src/condition-attribute-with-id.js';\nimport {\n ElementResolutionController,\n elementResolverUpdatedSymbol,\n} from '@spectrum-web-components/reactive-controllers/src/ElementResolution.js';\nimport { VirtualTrigger } from './VirtualTrigger.js';\nimport {\n ifDefined,\n styleMap,\n} from '@spectrum-web-components/base/src/directives.js';\nimport styles from './overlay-base.css.js';\nimport { overlayStack } from './OverlayStack.js';\nimport { PlacementController } from './PlacementController.js';\n\nexport type OpenableElement = HTMLElement & {\n open: boolean;\n tipElement?: HTMLElement;\n updateComplete?: Promise<void>;\n};\n\nexport type OverlayTypes = 'auto' | 'hint' | 'manual' | 'modal' | 'page';\n\nconst LONGPRESS_DURATION = 300;\n\nexport type LongpressEvent = {\n source: 'pointer' | 'keyboard';\n};\n\nexport const LONGPRESS_INSTRUCTIONS = {\n touch: 'Double tap and long press for additional options',\n keyboard: 'Press Space or Alt+Down Arrow for additional options',\n mouse: 'Click and hold for additional options',\n};\n\nexport class BeforetoggleClosedEvent extends Event {\n currentState = 'open';\n newState = 'closed';\n constructor() {\n super('beforetoggle', {\n bubbles: false,\n composed: false,\n });\n }\n}\n\nexport class BeforetoggleOpenEvent extends Event {\n currentState = 'closed';\n newState = 'open';\n constructor() {\n super('beforetoggle', {\n bubbles: false,\n composed: false,\n });\n }\n}\n\nexport const noop = (): void => {\n return;\n};\n\n/**\n * Apply a \"transitionend\" listener to an element that may not transition but\n * guarantee the callback will be fired either way.\n *\n * @param el {HTMLElement} - Target of the \"transition\" listeners.\n * @param action {Function} - Method to trigger the \"transition\".\n * @param cb {Function} - Callback to trigger when the \"transition\" has ended.\n */\nexport const guaranteedTransitionend = (\n el: HTMLElement,\n action: () => void,\n cb: () => void\n): void => {\n const cleanup = (): void => {\n el.removeEventListener('transitionrun', handleTransitionrun);\n el.removeEventListener('transitionend', handleTransitionend);\n cb();\n };\n let guarantee2: number;\n let guarantee3: number;\n // WebKit fires `transitionrun` a little earlier, so the inner/outer relationship\n // here allows WebKit to be caught, but doesn't remove the animation listener until\n // after it would have fired in Chromium.\n const guarantee1 = requestAnimationFrame(() => {\n guarantee2 = requestAnimationFrame(() => {\n guarantee3 = requestAnimationFrame(() => {\n cleanup();\n });\n });\n });\n const handleTransitionend = (event: TransitionEvent): void => {\n if (event.propertyName === 'visibility') {\n // Ignore \"visibility\" transitions because they often happen before/after a\n // larger transition and don't represent the overall transition duration.\n return;\n }\n cleanup();\n };\n const handleTransitionrun = (event: TransitionEvent): void => {\n if (event.propertyName === 'visibility') {\n // Ignore \"visibility\" transitions because they often happen before/after a\n // larger transition and don't represent the overall transition duration.\n return;\n }\n cancelAnimationFrame(guarantee1);\n cancelAnimationFrame(guarantee2);\n cancelAnimationFrame(guarantee3);\n el.removeEventListener('transitionrun', handleTransitionrun);\n el.addEventListener('transitionend', handleTransitionend);\n };\n el.addEventListener('transitionrun', handleTransitionrun);\n action();\n};\n\n/**\n * @element sp-overlay\n */\nexport class OverlayBase extends SpectrumElement {\n static override styles = [styles];\n\n @query('dialog')\n dialogEl!: HTMLDialogElement & {\n showPopover(): void;\n hidePopover(): void;\n };\n\n @property({ type: Boolean })\n get disabled(): boolean {\n return this._disabled;\n }\n\n set disabled(disabled: boolean) {\n this._disabled = disabled;\n if (disabled) {\n this.wasOpen = this.open;\n this.open = false;\n } else {\n this.open = this.open || this.wasOpen;\n this.wasOpen = false;\n }\n }\n\n private _disabled = false;\n\n protected dispose = noop;\n\n @queryAssignedElements({ flatten: true })\n elements!: OpenableElement[];\n\n public parentOverlayToForceClose?: OverlayBase;\n\n protected longpressed = false;\n\n private longressTimeout!: ReturnType<typeof setTimeout>;\n\n @property()\n offset: number | [number, number] = 6;\n\n public placementController = new PlacementController(this);\n\n @property({ type: Boolean, reflect: true })\n get open(): boolean {\n return this._open;\n }\n\n set open(open: boolean) {\n if (open && this.disabled) return;\n if (open === this.open) return;\n this._open = open;\n this.requestUpdate('open', !this.open);\n }\n\n private _open = false;\n\n static openCount = 1;\n\n @property()\n placement?: Placement;\n\n @property({ attribute: 'receives-focus' })\n receivesFocus: 'true' | 'false' | 'auto' = 'auto';\n\n private releaseAriaDescribedby = noop;\n\n @query('slot')\n slotEl!: HTMLSlotElement;\n\n @property()\n trigger?: string;\n\n @state()\n triggerElement: HTMLElement | VirtualTrigger | null = null;\n\n @state()\n triggerInteraction?: 'click' | 'longpress' | 'hover';\n\n @property()\n type: OverlayTypes = 'hint';\n\n protected wasOpen = false;\n\n private elementResolver = new ElementResolutionController(this);\n\n private get usesDialog(): boolean {\n return this.type === 'modal' || this.type === 'page';\n }\n\n private get popoverValue(): 'auto' | 'manual' | undefined {\n switch (this.type) {\n case 'modal':\n case 'page':\n return undefined;\n case 'hint':\n return 'manual';\n default:\n return this.type;\n }\n }\n\n /* c8 ignore next 12 */\n protected async manageDialogOpen(): Promise<void> {\n console.warn(\n 'Implement the `manageDialogOpen` method in a class extension.'\n );\n }\n\n protected async managePopoverOpen(): Promise<void> {\n console.warn(\n 'Implement the `managePopoverOpen` method in a class extension.'\n );\n }\n\n protected get requiresPosition(): boolean {\n // Do no position \"page\" overlays as they should block the entrie UI.\n if (this.type === 'page' || !this.open) return false;\n // Do not position content without a trigger element, what would you position it in relation to?\n // Do not automaticallyu position contnent, unless it is a \"hint\".\n if (!this.triggerElement || (!this.placement && this.type !== 'hint'))\n return false;\n return true;\n }\n\n protected managePosition(): void {\n if (!this.requiresPosition || !this.open) return;\n\n const offset = this.offset || 0;\n const trigger = this.triggerElement as HTMLElement;\n const placement = (this.placement as Placement) || 'right';\n\n this.placementController.placeOverlay(this.dialogEl, {\n // delayed?: boolean,\n offset,\n placement,\n // notImmediatelyClosable?: boolean, // rename or place behind other API options\n // receivesFocus?: 'auto';\n // root?: HTMLElement;\n trigger,\n type: this.type,\n });\n }\n\n protected async manageOpen(oldOpen: boolean): Promise<void> {\n if (!this.isConnected) return;\n\n if (!this.hasUpdated) {\n await this.updateComplete;\n }\n\n if (this.open) {\n overlayStack.add(this);\n } else {\n if (oldOpen) {\n this.dispose();\n }\n overlayStack.remove(this);\n }\n\n if (this.usesDialog) {\n this.manageDialogOpen();\n } else {\n this.managePopoverOpen();\n }\n if (this.open) {\n OverlayBase.openCount += 1;\n } else {\n // If the focus remains inside of the overlay or\n // a slotted descendent of the overlay you need to return\n // focus back to the trigger.\n const getAncestors = (): HTMLElement[] => {\n const ancestors: HTMLElement[] = [];\n // eslint-disable-next-line @spectrum-web-components/document-active-element\n let currentNode = document.activeElement;\n while (\n currentNode?.shadowRoot &&\n currentNode.shadowRoot.activeElement\n ) {\n currentNode = currentNode.shadowRoot.activeElement;\n }\n while (currentNode) {\n const ancestor =\n currentNode.assignedSlot ||\n currentNode.parentElement ||\n (currentNode.getRootNode() as ShadowRoot)?.host;\n if (ancestor) {\n ancestors.push(ancestor as HTMLElement);\n }\n currentNode = ancestor;\n }\n return ancestors;\n };\n if (\n (this.triggerElement as HTMLElement)?.focus &&\n (this.contains(\n (this.getRootNode() as Document).activeElement\n ) ||\n !!getAncestors().find((el) => el === this))\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n }\n }\n\n protected manageTriggerElement(triggerElement: HTMLElement | null): void {\n if (triggerElement) {\n triggerElement.removeEventListener('click', this.handleClick);\n triggerElement.removeEventListener('focusin', this.handleFocusin);\n triggerElement.removeEventListener('focusout', this.handleFocusout);\n triggerElement.removeEventListener(\n 'pointerenter',\n this.handlePointerenter\n );\n triggerElement.removeEventListener(\n 'pointerleave',\n this.handlePointerleave\n );\n this.removeEventListener(\n 'pointerleave',\n this.handleOverlayPointerleave\n );\n triggerElement.addEventListener(\n 'pointerdown',\n this.handlePointerdown\n );\n triggerElement.removeEventListener('keydown', this.handleKeydown);\n triggerElement.removeEventListener('keyup', this.handleKeyup);\n triggerElement.removeEventListener(\n 'longpress',\n this.handleLongpress\n );\n this.releaseAriaDescribedby();\n }\n if (\n !this.triggerElement ||\n !!(this.triggerElement as VirtualTrigger).updateBoundingClientRect\n )\n return;\n const nextTriggerElement = this.triggerElement as HTMLElement;\n switch (this.triggerInteraction) {\n case 'click':\n nextTriggerElement.addEventListener('click', this.handleClick);\n return;\n case 'longpress':\n nextTriggerElement.addEventListener(\n 'pointerdown',\n this.handlePointerdown\n );\n nextTriggerElement.addEventListener(\n 'keydown',\n this.handleKeydown\n );\n nextTriggerElement.addEventListener('keyup', this.handleKeyup);\n nextTriggerElement.addEventListener(\n 'longpress',\n this.handleLongpress\n );\n return;\n case 'hover':\n nextTriggerElement.addEventListener(\n 'focusin',\n this.handleFocusin\n );\n nextTriggerElement.addEventListener(\n 'focusout',\n this.handleFocusout\n );\n nextTriggerElement.addEventListener(\n 'pointerenter',\n this.handlePointerenter\n );\n nextTriggerElement.addEventListener(\n 'pointerleave',\n this.handlePointerleave\n );\n this.addEventListener(\n 'pointerleave',\n this.handleOverlayPointerleave\n );\n if (this.receivesFocus === 'true') return;\n\n this.prepareAriaDescribedby(nextTriggerElement);\n return;\n }\n }\n\n private elementIds: string[] = [];\n\n private prepareAriaDescribedby(trigger: HTMLElement): void {\n if (\n // only \"hover\" relationships establed described by content\n this.triggerInteraction !== 'hover' ||\n // do not reapply until target is recycled\n this.releaseAriaDescribedby !== noop ||\n // require \" hover content\" to apply relationship\n !this.elements.length\n )\n return;\n\n const triggerRoot = trigger.getRootNode();\n const contentRoot = this.elements[0].getRootNode();\n const overlayRoot = this.getRootNode();\n if (triggerRoot == overlayRoot) {\n const releaseAriaDescribedby = conditionAttributeWithId(\n trigger,\n 'aria-describedby',\n [this.id]\n );\n this.releaseAriaDescribedby = () => {\n releaseAriaDescribedby();\n this.releaseAriaDescribedby = noop;\n };\n } else if (triggerRoot === contentRoot) {\n this.elementIds = this.elements.map((el) => el.id);\n const appliedIds = this.elements.map((el) => {\n if (!el.id) {\n el.id = `${this.tagName.toLowerCase()}-helper-${crypto\n .randomUUID()\n .slice(0, 8)}`;\n }\n return el.id;\n });\n const releaseAriaDescribedby = conditionAttributeWithId(\n trigger,\n 'aria-describedby',\n appliedIds\n );\n this.releaseAriaDescribedby = () => {\n releaseAriaDescribedby();\n this.elements.map((el, index) => {\n el.id = this.elementIds[index];\n });\n this.releaseAriaDescribedby = noop;\n };\n }\n }\n\n private handlePointerdown = (event: PointerEvent): void => {\n if (!this.triggerElement) return;\n if (event.button !== 0) return;\n const triggerElement = this.triggerElement as HTMLElement;\n this.longpressed = false;\n triggerElement.addEventListener('pointerup', this.handlePointerup);\n triggerElement.addEventListener('pointercancel', this.handlePointerup);\n this.longressTimeout = setTimeout(() => {\n if (!triggerElement) return;\n triggerElement.dispatchEvent(\n new CustomEvent<LongpressEvent>('longpress', {\n bubbles: true,\n composed: true,\n detail: {\n source: 'pointer',\n },\n })\n );\n }, LONGPRESS_DURATION);\n };\n\n private handlePointerup = (): void => {\n clearTimeout(this.longressTimeout);\n if (!this.triggerElement) return;\n if (this.longpressed) {\n this.open = true;\n }\n setTimeout(() => {\n this.longpressed = false;\n });\n const triggerElement = this.triggerElement as HTMLElement;\n triggerElement.removeEventListener('pointerup', this.handlePointerup);\n triggerElement.removeEventListener(\n 'pointercancel',\n this.handlePointerup\n );\n };\n\n /**\n * @private\n */\n protected handleKeydown = (event: KeyboardEvent): void => {\n const { code, altKey } = event;\n if (code === 'Space' || (altKey && code === 'ArrowDown')) {\n if (code === 'ArrowDown') {\n event.stopPropagation();\n event.stopImmediatePropagation();\n }\n }\n };\n\n protected handleKeyup = (event: KeyboardEvent): void => {\n const { code, altKey } = event;\n if (code === 'Space' || (altKey && code === 'ArrowDown')) {\n event.stopPropagation();\n this.dispatchEvent(\n new CustomEvent<LongpressEvent>('longpress', {\n bubbles: true,\n composed: true,\n detail: {\n source: 'keyboard',\n },\n })\n );\n }\n };\n\n protected handleClick = (): void => {\n if (this.longpressed) return;\n this.open = !this.open;\n };\n\n private focusedin = false;\n\n protected handleFocusin = (): void => {\n this.open = true;\n this.focusedin = true;\n };\n\n protected handleFocusout = (): void => {\n this.focusedin = false;\n if (this.pointerentered) return;\n this.open = false;\n };\n\n private pointerentered = false;\n\n protected handlePointerenter = (): void => {\n this.open = true;\n this.pointerentered = true;\n };\n\n protected handlePointerleave = (event: PointerEvent): void => {\n if (\n this === event.relatedTarget ||\n this.contains(event.relatedTarget as Node)\n ) {\n return;\n }\n this.doDointerleave();\n };\n\n protected handleOverlayPointerleave = (event: PointerEvent): void => {\n const hasNonVirtualTrigger =\n this.triggerElement &&\n this.triggerElement instanceof VirtualTrigger;\n if (\n this.triggerElement === event.relatedTarget ||\n (hasNonVirtualTrigger &&\n (this.triggerElement as HTMLElement).contains(\n event.relatedTarget as Node\n ))\n ) {\n return;\n }\n this.doDointerleave();\n };\n\n protected doDointerleave(): void {\n this.pointerentered = false;\n const triggerElement = this.triggerElement as HTMLElement;\n if (this.focusedin && triggerElement.matches(':focus-visible')) return;\n this.open = false;\n }\n\n protected handleLongpress = (): void => {\n this.open = true;\n this.longpressed = true;\n };\n\n protected handleBeforetoggle(event: Event & { newState: string }): void {\n if (event.newState === 'open') {\n this.handlePopovershow();\n } else {\n this.handlePopoverhide();\n }\n }\n\n protected handlePopoverhide(): void {\n this.open = false;\n this.dispatchEvent(new BeforetoggleClosedEvent());\n }\n\n protected handlePopovershow(): void {\n this.dispatchEvent(new BeforetoggleOpenEvent());\n }\n\n public willPreventClose = false;\n\n public shouldPreventClose(): boolean {\n const shouldPreventClose = this.willPreventClose;\n this.willPreventClose = false;\n return shouldPreventClose;\n }\n\n override willUpdate(changes: PropertyValues): void {\n if (!this.hasUpdated) {\n this.addEventListener('focusout', (event: FocusEvent) => {\n if (this.type !== 'auto') {\n return;\n }\n if (!event.relatedTarget) {\n this.open = false;\n return;\n }\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n event.relatedTarget.addEventListener(\n relationEvent.type,\n (event: Event) => {\n if (!event.composedPath().includes(this)) {\n this.open = false;\n }\n }\n );\n event.relatedTarget.dispatchEvent(relationEvent);\n });\n }\n if (!this.hasAttribute('id')) {\n this.setAttribute(\n 'id',\n `${this.tagName.toLowerCase()}-${crypto\n .randomUUID()\n .slice(0, 8)}`\n );\n }\n if (\n changes.has('open') &&\n (typeof changes.get('open') !== 'undefined' || this.open)\n ) {\n this.manageOpen(changes.get('open'));\n }\n if (changes.has('trigger')) {\n const [id, interaction] = this.trigger?.split('@') || [];\n this.elementResolver.selector = id ? `#${id}` : '';\n this.triggerInteraction = interaction as\n | 'click'\n | 'longpress'\n | 'hover'\n | undefined;\n }\n const oldTrigger = this.triggerElement as HTMLElement;\n if (changes.has(elementResolverUpdatedSymbol)) {\n this.triggerElement = this.elementResolver.element;\n this.manageTriggerElement(oldTrigger);\n }\n if (changes.has('triggerElement')) {\n this.manageTriggerElement(changes.get('triggerElement'));\n }\n }\n\n protected override updated(changes: PropertyValues): void {\n super.updated(changes);\n if (changes.has('placement')) {\n if (this.placement) {\n this.dialogEl.setAttribute('actual-placement', this.placement);\n } else {\n this.dialogEl.removeAttribute('actual-placement');\n }\n if (typeof changes.get('placement') !== 'undefined') {\n this.managePosition();\n }\n }\n }\n\n public override render(): TemplateResult {\n const hasPopoverAttribute = 'popover' in this;\n const popoverValue = hasPopoverAttribute\n ? this.popoverValue\n : undefined;\n return html`\n <dialog\n part=\"dialog\"\n popover=${ifDefined(popoverValue)}\n @close=${() => {\n this.open = false;\n }}\n @cancel=${() => {\n this.open = false;\n }}\n @beforetoggle=${this.handleBeforetoggle}\n @popovershow=${this.handlePopovershow}\n style=${styleMap({\n '--swc-overlay-z-index': OverlayBase.openCount.toString(),\n })}\n >\n <div part=\"content\">\n <slot\n @slotchange=${() => {\n if (this.triggerElement) {\n this.prepareAriaDescribedby(\n this.triggerElement as HTMLElement\n );\n }\n }}\n ></slot>\n </div>\n </dialog>\n `;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n // this.manageOpen();\n this.addEventListener('close', () => {\n this.open = false;\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["html", "SpectrumElement", "property", "query", "queryAssignedElements", "state", "conditionAttributeWithId", "ElementResolutionController", "elementResolverUpdatedSymbol", "VirtualTrigger", "ifDefined", "styleMap", "styles", "overlayStack", "PlacementController", "LONGPRESS_DURATION", "el", "action", "cb", "cleanup", "handleTransitionrun", "handleTransitionend", "guarantee2", "guarantee3", "guarantee1", "event", "_OverlayBase", "triggerElement", "code", "altKey", "hasNonVirtualTrigger", "disabled", "open", "offset", "trigger", "placement", "oldOpen", "_a", "getAncestors", "ancestors", "currentNode", "ancestor", "nextTriggerElement", "triggerRoot", "contentRoot", "overlayRoot", "releaseAriaDescribedby", "appliedIds", "index", "shouldPreventClose", "changes", "relationEvent", "id", "interaction", "oldTrigger", "popoverValue", "__decorateClass"]
|
|
7
7
|
}
|
package/src/OverlayDialog.dev.js
CHANGED
|
@@ -19,7 +19,7 @@ export function OverlayDialog(constructor) {
|
|
|
19
19
|
await this.managePosition();
|
|
20
20
|
await this.dialogEnsureOnDOM();
|
|
21
21
|
const focusEl = await this.dialogMakeTransition(targetOpenState);
|
|
22
|
-
await this.dialogApplyFocus(focusEl);
|
|
22
|
+
await this.dialogApplyFocus(targetOpenState, focusEl);
|
|
23
23
|
}
|
|
24
24
|
async dialogEnsureOnDOM() {
|
|
25
25
|
await nextFrame();
|
|
@@ -122,13 +122,13 @@ export function OverlayDialog(constructor) {
|
|
|
122
122
|
});
|
|
123
123
|
return focusEl;
|
|
124
124
|
}
|
|
125
|
-
async dialogApplyFocus(focusEl) {
|
|
125
|
+
async dialogApplyFocus(targetOpenState, focusEl) {
|
|
126
126
|
if (this.receivesFocus === "false") {
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
await nextFrame();
|
|
130
130
|
await nextFrame();
|
|
131
|
-
if (!this.open) {
|
|
131
|
+
if (targetOpenState === this.open && !this.open) {
|
|
132
132
|
if (
|
|
133
133
|
// Do not return focus to trigger when overlay is a "hint" (tooltip)
|
|
134
134
|
this.type !== "hint" && // Only return focus when the trigger is not "virtual"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["OverlayDialog.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { ReactiveElement } from 'lit';\nimport {\n firstFocusableIn,\n firstFocusableSlottedIn,\n} from '@spectrum-web-components/shared/src/first-focusable-in.js';\nimport {\n BeforetoggleClosedEvent,\n BeforetoggleOpenEvent,\n guaranteedTransitionend,\n OpenableElement,\n OverlayBase,\n} from './OverlayBase.dev.js'\nimport { VirtualTrigger } from './VirtualTrigger.dev.js'\n\ntype Constructor<T = Record<string, unknown>> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]): T;\n prototype: T;\n};\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n\nexport function OverlayDialog<T extends Constructor<OverlayBase>>(\n constructor: T\n): T & Constructor<ReactiveElement> {\n class OverlayWithDialog extends constructor {\n protected override async manageDialogOpen(): Promise<void> {\n const targetOpenState = this.open;\n await this.managePosition();\n await this.dialogEnsureOnDOM();\n const focusEl = await this.dialogMakeTransition(targetOpenState);\n await this.dialogApplyFocus(focusEl);\n }\n\n protected async dialogEnsureOnDOM(): Promise<void> {\n await nextFrame();\n await nextFrame();\n await nextFrame();\n await nextFrame();\n }\n\n protected async dialogMakeTransition(\n targetOpenState: boolean\n ): Promise<HTMLElement | null> {\n let focusEl = null as HTMLElement | null;\n const start =\n (el: OpenableElement, index: number) =>\n async (): Promise<void> => {\n if (typeof el.open !== 'undefined') {\n el.open = targetOpenState;\n }\n if (!targetOpenState) {\n const close = (): void => {\n el.removeEventListener('close', close);\n finish(el, index);\n };\n el.addEventListener('close', close);\n }\n if (index > 0 || !targetOpenState) {\n return;\n }\n const event = targetOpenState\n ? BeforetoggleOpenEvent\n : BeforetoggleClosedEvent;\n this.dispatchEvent(new event());\n focusEl = focusEl || firstFocusableIn(el);\n if (!focusEl) {\n const childSlots = el.querySelectorAll('slot');\n childSlots.forEach((slot) => {\n if (!focusEl) {\n focusEl = firstFocusableSlottedIn(slot);\n }\n });\n }\n if (!this.isConnected || this.dialogEl.open) {\n return;\n }\n this.dialogEl.showModal();\n };\n const finish = (el: OpenableElement, index: number) => (): void => {\n if (this.open !== targetOpenState) {\n return;\n }\n const eventName = targetOpenState ? 'sp-opened' : 'sp-closed';\n if (index > 0) {\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n return;\n }\n if (!this.isConnected || targetOpenState !== this.open) {\n return;\n }\n const reportChange = (): void => {\n const hasVirtualTrigger =\n this.triggerElement instanceof VirtualTrigger;\n this.dispatchEvent(\n new Event(eventName, {\n bubbles: hasVirtualTrigger,\n composed: hasVirtualTrigger,\n })\n );\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n if (this.triggerElement && !hasVirtualTrigger) {\n (this.triggerElement as HTMLElement).dispatchEvent(\n new CustomEvent(eventName, {\n bubbles: true,\n composed: true,\n detail: { interaction: this.type },\n })\n );\n }\n };\n if (!targetOpenState && this.dialogEl.open) {\n this.dialogEl.addEventListener(\n 'close',\n () => {\n reportChange();\n },\n { once: true }\n );\n this.dialogEl.close();\n } else {\n reportChange();\n }\n };\n this.elements.forEach((el, index) => {\n guaranteedTransitionend(\n el,\n start(el, index),\n finish(el, index)\n );\n });\n return focusEl;\n }\n\n protected async dialogApplyFocus(\n focusEl: HTMLElement | null\n ): Promise<void> {\n /**\n * Focus should handled natively in `<dialog>` elements when leveraging `.showModal()`, but it's NOT.\n * - webkit bug: https://bugs.webkit.org/show_bug.cgi?id=255507\n * - firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1828398\n **/\n if (this.receivesFocus === 'false') {\n return;\n }\n\n await nextFrame();\n await nextFrame();\n if (!this.open) {\n if (\n // Do not return focus to trigger when overlay is a \"hint\" (tooltip)\n this.type !== 'hint' &&\n // Only return focus when the trigger is not \"virtual\"\n this.triggerElement &&\n !(this.triggerElement instanceof VirtualTrigger)\n ) {\n // This has a bug where the current overlay and the focused content could share the same `activeElement` shadow root...\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n this.addEventListener(\n relationEvent.type,\n (event: Event) => {\n /* eslint-disable @spectrum-web-components/document-active-element */\n if (\n document.activeElement &&\n event\n .composedPath()\n .includes(document.activeElement)\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n /* eslint-enable @spectrum-web-components/document-active-element */\n }\n );\n this.dispatchEvent(relationEvent);\n }\n return;\n }\n\n focusEl?.focus();\n }\n }\n return OverlayWithDialog;\n}\n"],
|
|
5
|
-
"mappings": ";AAYA;AAAA,EACI;AAAA,EACA;AAAA,OACG;AACP;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OAGG;AACP,SAAS,sBAAsB;AAQ/B,SAAS,YAA2B;AAChC,SAAO,IAAI,QAAQ,CAAC,QAAQ,sBAAsB,MAAM,IAAI,CAAC,CAAC;AAClE;AAEO,gBAAS,cACZ,aACgC;AAChC,QAAM,0BAA0B,YAAY;AAAA,IACxC,MAAyB,mBAAkC;AACvD,YAAM,kBAAkB,KAAK;AAC7B,YAAM,KAAK,eAAe;AAC1B,YAAM,KAAK,kBAAkB;AAC7B,YAAM,UAAU,MAAM,KAAK,qBAAqB,eAAe;AAC/D,YAAM,KAAK,iBAAiB,OAAO;AAAA,
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { ReactiveElement } from 'lit';\nimport {\n firstFocusableIn,\n firstFocusableSlottedIn,\n} from '@spectrum-web-components/shared/src/first-focusable-in.js';\nimport {\n BeforetoggleClosedEvent,\n BeforetoggleOpenEvent,\n guaranteedTransitionend,\n OpenableElement,\n OverlayBase,\n} from './OverlayBase.dev.js'\nimport { VirtualTrigger } from './VirtualTrigger.dev.js'\n\ntype Constructor<T = Record<string, unknown>> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]): T;\n prototype: T;\n};\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n\nexport function OverlayDialog<T extends Constructor<OverlayBase>>(\n constructor: T\n): T & Constructor<ReactiveElement> {\n class OverlayWithDialog extends constructor {\n protected override async manageDialogOpen(): Promise<void> {\n const targetOpenState = this.open;\n await this.managePosition();\n await this.dialogEnsureOnDOM();\n const focusEl = await this.dialogMakeTransition(targetOpenState);\n await this.dialogApplyFocus(targetOpenState, focusEl);\n }\n\n protected async dialogEnsureOnDOM(): Promise<void> {\n await nextFrame();\n await nextFrame();\n await nextFrame();\n await nextFrame();\n }\n\n protected async dialogMakeTransition(\n targetOpenState: boolean\n ): Promise<HTMLElement | null> {\n let focusEl = null as HTMLElement | null;\n const start =\n (el: OpenableElement, index: number) =>\n async (): Promise<void> => {\n if (typeof el.open !== 'undefined') {\n el.open = targetOpenState;\n }\n if (!targetOpenState) {\n const close = (): void => {\n el.removeEventListener('close', close);\n finish(el, index);\n };\n el.addEventListener('close', close);\n }\n if (index > 0 || !targetOpenState) {\n return;\n }\n const event = targetOpenState\n ? BeforetoggleOpenEvent\n : BeforetoggleClosedEvent;\n this.dispatchEvent(new event());\n focusEl = focusEl || firstFocusableIn(el);\n if (!focusEl) {\n const childSlots = el.querySelectorAll('slot');\n childSlots.forEach((slot) => {\n if (!focusEl) {\n focusEl = firstFocusableSlottedIn(slot);\n }\n });\n }\n if (!this.isConnected || this.dialogEl.open) {\n return;\n }\n this.dialogEl.showModal();\n };\n const finish = (el: OpenableElement, index: number) => (): void => {\n if (this.open !== targetOpenState) {\n return;\n }\n const eventName = targetOpenState ? 'sp-opened' : 'sp-closed';\n if (index > 0) {\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n return;\n }\n if (!this.isConnected || targetOpenState !== this.open) {\n return;\n }\n const reportChange = (): void => {\n const hasVirtualTrigger =\n this.triggerElement instanceof VirtualTrigger;\n this.dispatchEvent(\n new Event(eventName, {\n bubbles: hasVirtualTrigger,\n composed: hasVirtualTrigger,\n })\n );\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n if (this.triggerElement && !hasVirtualTrigger) {\n (this.triggerElement as HTMLElement).dispatchEvent(\n new CustomEvent(eventName, {\n bubbles: true,\n composed: true,\n detail: { interaction: this.type },\n })\n );\n }\n };\n if (!targetOpenState && this.dialogEl.open) {\n this.dialogEl.addEventListener(\n 'close',\n () => {\n reportChange();\n },\n { once: true }\n );\n this.dialogEl.close();\n } else {\n reportChange();\n }\n };\n this.elements.forEach((el, index) => {\n guaranteedTransitionend(\n el,\n start(el, index),\n finish(el, index)\n );\n });\n return focusEl;\n }\n\n protected async dialogApplyFocus(\n targetOpenState: boolean,\n focusEl: HTMLElement | null\n ): Promise<void> {\n /**\n * Focus should handled natively in `<dialog>` elements when leveraging `.showModal()`, but it's NOT.\n * - webkit bug: https://bugs.webkit.org/show_bug.cgi?id=255507\n * - firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1828398\n **/\n if (this.receivesFocus === 'false') {\n return;\n }\n\n await nextFrame();\n await nextFrame();\n if (targetOpenState === this.open && !this.open) {\n if (\n // Do not return focus to trigger when overlay is a \"hint\" (tooltip)\n this.type !== 'hint' &&\n // Only return focus when the trigger is not \"virtual\"\n this.triggerElement &&\n !(this.triggerElement instanceof VirtualTrigger)\n ) {\n // This has a bug where the current overlay and the focused content could share the same `activeElement` shadow root...\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n this.addEventListener(\n relationEvent.type,\n (event: Event) => {\n /* eslint-disable @spectrum-web-components/document-active-element */\n if (\n document.activeElement &&\n event\n .composedPath()\n .includes(document.activeElement)\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n /* eslint-enable @spectrum-web-components/document-active-element */\n }\n );\n this.dispatchEvent(relationEvent);\n }\n return;\n }\n\n focusEl?.focus();\n }\n }\n return OverlayWithDialog;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/src/OverlayDialog.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";import{firstFocusableIn as h,firstFocusableSlottedIn as
|
|
1
|
+
"use strict";import{firstFocusableIn as h,firstFocusableSlottedIn as p}from"@spectrum-web-components/shared/src/first-focusable-in.js";import{BeforetoggleClosedEvent as E,BeforetoggleOpenEvent as v,guaranteedTransitionend as f}from"./OverlayBase.js";import{VirtualTrigger as m}from"./VirtualTrigger.js";function r(){return new Promise(d=>requestAnimationFrame(()=>d()))}export function OverlayDialog(d){class u extends d{async manageDialogOpen(){const t=this.open;await this.managePosition(),await this.dialogEnsureOnDOM();const n=await this.dialogMakeTransition(t);await this.dialogApplyFocus(t,n)}async dialogEnsureOnDOM(){await r(),await r(),await r(),await r()}async dialogMakeTransition(t){let n=null;const l=(e,i)=>async()=>{if(typeof e.open!="undefined"&&(e.open=t),!t){const s=()=>{e.removeEventListener("close",s),c(e,i)};e.addEventListener("close",s)}if(i>0||!t)return;const o=t?v:E;this.dispatchEvent(new o),n=n||h(e),n||e.querySelectorAll("slot").forEach(a=>{n||(n=p(a))}),!(!this.isConnected||this.dialogEl.open)&&this.dialogEl.showModal()},c=(e,i)=>()=>{if(this.open!==t)return;const o=t?"sp-opened":"sp-closed";if(i>0){e.dispatchEvent(new Event(o,{bubbles:!1,composed:!1}));return}if(!this.isConnected||t!==this.open)return;const s=()=>{const a=this.triggerElement instanceof m;this.dispatchEvent(new Event(o,{bubbles:a,composed:a})),e.dispatchEvent(new Event(o,{bubbles:!1,composed:!1})),this.triggerElement&&!a&&this.triggerElement.dispatchEvent(new CustomEvent(o,{bubbles:!0,composed:!0,detail:{interaction:this.type}}))};!t&&this.dialogEl.open?(this.dialogEl.addEventListener("close",()=>{s()},{once:!0}),this.dialogEl.close()):s()};return this.elements.forEach((e,i)=>{f(e,l(e,i),c(e,i))}),n}async dialogApplyFocus(t,n){if(this.receivesFocus!=="false"){if(await r(),await r(),t===this.open&&!this.open){if(this.type!=="hint"&&this.triggerElement&&!(this.triggerElement instanceof m)){const l=new Event("overlay-relation-query",{bubbles:!0,composed:!0});this.addEventListener(l.type,c=>{document.activeElement&&c.composedPath().includes(document.activeElement)&&this.triggerElement.focus()}),this.dispatchEvent(l)}return}n==null||n.focus()}}}return u}
|
|
2
2
|
//# sourceMappingURL=OverlayDialog.js.map
|
package/src/OverlayDialog.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["OverlayDialog.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { ReactiveElement } from 'lit';\nimport {\n firstFocusableIn,\n firstFocusableSlottedIn,\n} from '@spectrum-web-components/shared/src/first-focusable-in.js';\nimport {\n BeforetoggleClosedEvent,\n BeforetoggleOpenEvent,\n guaranteedTransitionend,\n OpenableElement,\n OverlayBase,\n} from './OverlayBase.js';\nimport { VirtualTrigger } from './VirtualTrigger.js';\n\ntype Constructor<T = Record<string, unknown>> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]): T;\n prototype: T;\n};\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n\nexport function OverlayDialog<T extends Constructor<OverlayBase>>(\n constructor: T\n): T & Constructor<ReactiveElement> {\n class OverlayWithDialog extends constructor {\n protected override async manageDialogOpen(): Promise<void> {\n const targetOpenState = this.open;\n await this.managePosition();\n await this.dialogEnsureOnDOM();\n const focusEl = await this.dialogMakeTransition(targetOpenState);\n await this.dialogApplyFocus(focusEl);\n }\n\n protected async dialogEnsureOnDOM(): Promise<void> {\n await nextFrame();\n await nextFrame();\n await nextFrame();\n await nextFrame();\n }\n\n protected async dialogMakeTransition(\n targetOpenState: boolean\n ): Promise<HTMLElement | null> {\n let focusEl = null as HTMLElement | null;\n const start =\n (el: OpenableElement, index: number) =>\n async (): Promise<void> => {\n if (typeof el.open !== 'undefined') {\n el.open = targetOpenState;\n }\n if (!targetOpenState) {\n const close = (): void => {\n el.removeEventListener('close', close);\n finish(el, index);\n };\n el.addEventListener('close', close);\n }\n if (index > 0 || !targetOpenState) {\n return;\n }\n const event = targetOpenState\n ? BeforetoggleOpenEvent\n : BeforetoggleClosedEvent;\n this.dispatchEvent(new event());\n focusEl = focusEl || firstFocusableIn(el);\n if (!focusEl) {\n const childSlots = el.querySelectorAll('slot');\n childSlots.forEach((slot) => {\n if (!focusEl) {\n focusEl = firstFocusableSlottedIn(slot);\n }\n });\n }\n if (!this.isConnected || this.dialogEl.open) {\n return;\n }\n this.dialogEl.showModal();\n };\n const finish = (el: OpenableElement, index: number) => (): void => {\n if (this.open !== targetOpenState) {\n return;\n }\n const eventName = targetOpenState ? 'sp-opened' : 'sp-closed';\n if (index > 0) {\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n return;\n }\n if (!this.isConnected || targetOpenState !== this.open) {\n return;\n }\n const reportChange = (): void => {\n const hasVirtualTrigger =\n this.triggerElement instanceof VirtualTrigger;\n this.dispatchEvent(\n new Event(eventName, {\n bubbles: hasVirtualTrigger,\n composed: hasVirtualTrigger,\n })\n );\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n if (this.triggerElement && !hasVirtualTrigger) {\n (this.triggerElement as HTMLElement).dispatchEvent(\n new CustomEvent(eventName, {\n bubbles: true,\n composed: true,\n detail: { interaction: this.type },\n })\n );\n }\n };\n if (!targetOpenState && this.dialogEl.open) {\n this.dialogEl.addEventListener(\n 'close',\n () => {\n reportChange();\n },\n { once: true }\n );\n this.dialogEl.close();\n } else {\n reportChange();\n }\n };\n this.elements.forEach((el, index) => {\n guaranteedTransitionend(\n el,\n start(el, index),\n finish(el, index)\n );\n });\n return focusEl;\n }\n\n protected async dialogApplyFocus(\n focusEl: HTMLElement | null\n ): Promise<void> {\n /**\n * Focus should handled natively in `<dialog>` elements when leveraging `.showModal()`, but it's NOT.\n * - webkit bug: https://bugs.webkit.org/show_bug.cgi?id=255507\n * - firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1828398\n **/\n if (this.receivesFocus === 'false') {\n return;\n }\n\n await nextFrame();\n await nextFrame();\n if (!this.open) {\n if (\n // Do not return focus to trigger when overlay is a \"hint\" (tooltip)\n this.type !== 'hint' &&\n // Only return focus when the trigger is not \"virtual\"\n this.triggerElement &&\n !(this.triggerElement instanceof VirtualTrigger)\n ) {\n // This has a bug where the current overlay and the focused content could share the same `activeElement` shadow root...\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n this.addEventListener(\n relationEvent.type,\n (event: Event) => {\n /* eslint-disable @spectrum-web-components/document-active-element */\n if (\n document.activeElement &&\n event\n .composedPath()\n .includes(document.activeElement)\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n /* eslint-enable @spectrum-web-components/document-active-element */\n }\n );\n this.dispatchEvent(relationEvent);\n }\n return;\n }\n\n focusEl?.focus();\n }\n }\n return OverlayWithDialog;\n}\n"],
|
|
5
|
-
"mappings": "aAYA,OACI,oBAAAA,EACA,2BAAAC,MACG,4DACP,OACI,2BAAAC,EACA,yBAAAC,EACA,2BAAAC,MAGG,mBACP,OAAS,kBAAAC,MAAsB,sBAQ/B,SAASC,GAA2B,CAChC,OAAO,IAAI,QAASC,GAAQ,sBAAsB,IAAMA,EAAI,CAAC,CAAC,CAClE,CAEO,gBAAS,cACZC,EACgC,CAChC,MAAMC,UAA0BD,CAAY,CACxC,MAAyB,kBAAkC,CACvD,MAAME,EAAkB,KAAK,KAC7B,MAAM,KAAK,eAAe,EAC1B,MAAM,KAAK,kBAAkB,EAC7B,MAAMC,EAAU,MAAM,KAAK,qBAAqBD,CAAe,EAC/D,MAAM,KAAK,
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { ReactiveElement } from 'lit';\nimport {\n firstFocusableIn,\n firstFocusableSlottedIn,\n} from '@spectrum-web-components/shared/src/first-focusable-in.js';\nimport {\n BeforetoggleClosedEvent,\n BeforetoggleOpenEvent,\n guaranteedTransitionend,\n OpenableElement,\n OverlayBase,\n} from './OverlayBase.js';\nimport { VirtualTrigger } from './VirtualTrigger.js';\n\ntype Constructor<T = Record<string, unknown>> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]): T;\n prototype: T;\n};\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n\nexport function OverlayDialog<T extends Constructor<OverlayBase>>(\n constructor: T\n): T & Constructor<ReactiveElement> {\n class OverlayWithDialog extends constructor {\n protected override async manageDialogOpen(): Promise<void> {\n const targetOpenState = this.open;\n await this.managePosition();\n await this.dialogEnsureOnDOM();\n const focusEl = await this.dialogMakeTransition(targetOpenState);\n await this.dialogApplyFocus(targetOpenState, focusEl);\n }\n\n protected async dialogEnsureOnDOM(): Promise<void> {\n await nextFrame();\n await nextFrame();\n await nextFrame();\n await nextFrame();\n }\n\n protected async dialogMakeTransition(\n targetOpenState: boolean\n ): Promise<HTMLElement | null> {\n let focusEl = null as HTMLElement | null;\n const start =\n (el: OpenableElement, index: number) =>\n async (): Promise<void> => {\n if (typeof el.open !== 'undefined') {\n el.open = targetOpenState;\n }\n if (!targetOpenState) {\n const close = (): void => {\n el.removeEventListener('close', close);\n finish(el, index);\n };\n el.addEventListener('close', close);\n }\n if (index > 0 || !targetOpenState) {\n return;\n }\n const event = targetOpenState\n ? BeforetoggleOpenEvent\n : BeforetoggleClosedEvent;\n this.dispatchEvent(new event());\n focusEl = focusEl || firstFocusableIn(el);\n if (!focusEl) {\n const childSlots = el.querySelectorAll('slot');\n childSlots.forEach((slot) => {\n if (!focusEl) {\n focusEl = firstFocusableSlottedIn(slot);\n }\n });\n }\n if (!this.isConnected || this.dialogEl.open) {\n return;\n }\n this.dialogEl.showModal();\n };\n const finish = (el: OpenableElement, index: number) => (): void => {\n if (this.open !== targetOpenState) {\n return;\n }\n const eventName = targetOpenState ? 'sp-opened' : 'sp-closed';\n if (index > 0) {\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n return;\n }\n if (!this.isConnected || targetOpenState !== this.open) {\n return;\n }\n const reportChange = (): void => {\n const hasVirtualTrigger =\n this.triggerElement instanceof VirtualTrigger;\n this.dispatchEvent(\n new Event(eventName, {\n bubbles: hasVirtualTrigger,\n composed: hasVirtualTrigger,\n })\n );\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n if (this.triggerElement && !hasVirtualTrigger) {\n (this.triggerElement as HTMLElement).dispatchEvent(\n new CustomEvent(eventName, {\n bubbles: true,\n composed: true,\n detail: { interaction: this.type },\n })\n );\n }\n };\n if (!targetOpenState && this.dialogEl.open) {\n this.dialogEl.addEventListener(\n 'close',\n () => {\n reportChange();\n },\n { once: true }\n );\n this.dialogEl.close();\n } else {\n reportChange();\n }\n };\n this.elements.forEach((el, index) => {\n guaranteedTransitionend(\n el,\n start(el, index),\n finish(el, index)\n );\n });\n return focusEl;\n }\n\n protected async dialogApplyFocus(\n targetOpenState: boolean,\n focusEl: HTMLElement | null\n ): Promise<void> {\n /**\n * Focus should handled natively in `<dialog>` elements when leveraging `.showModal()`, but it's NOT.\n * - webkit bug: https://bugs.webkit.org/show_bug.cgi?id=255507\n * - firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1828398\n **/\n if (this.receivesFocus === 'false') {\n return;\n }\n\n await nextFrame();\n await nextFrame();\n if (targetOpenState === this.open && !this.open) {\n if (\n // Do not return focus to trigger when overlay is a \"hint\" (tooltip)\n this.type !== 'hint' &&\n // Only return focus when the trigger is not \"virtual\"\n this.triggerElement &&\n !(this.triggerElement instanceof VirtualTrigger)\n ) {\n // This has a bug where the current overlay and the focused content could share the same `activeElement` shadow root...\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n this.addEventListener(\n relationEvent.type,\n (event: Event) => {\n /* eslint-disable @spectrum-web-components/document-active-element */\n if (\n document.activeElement &&\n event\n .composedPath()\n .includes(document.activeElement)\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n /* eslint-enable @spectrum-web-components/document-active-element */\n }\n );\n this.dispatchEvent(relationEvent);\n }\n return;\n }\n\n focusEl?.focus();\n }\n }\n return OverlayWithDialog;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["firstFocusableIn", "firstFocusableSlottedIn", "BeforetoggleClosedEvent", "BeforetoggleOpenEvent", "guaranteedTransitionend", "VirtualTrigger", "nextFrame", "res", "constructor", "OverlayWithDialog", "targetOpenState", "focusEl", "start", "el", "index", "close", "finish", "event", "slot", "eventName", "reportChange", "hasVirtualTrigger", "relationEvent"]
|
|
7
7
|
}
|
|
@@ -19,7 +19,7 @@ export function OverlayNoPopover(constructor) {
|
|
|
19
19
|
await this.managePosition();
|
|
20
20
|
await this.ensureOnDOM();
|
|
21
21
|
const focusEl = await this.makeTransition(targetOpenState);
|
|
22
|
-
await this.applyFocus(focusEl);
|
|
22
|
+
await this.applyFocus(targetOpenState, focusEl);
|
|
23
23
|
}
|
|
24
24
|
async ensureOnDOM() {
|
|
25
25
|
await nextFrame();
|
|
@@ -92,13 +92,13 @@ export function OverlayNoPopover(constructor) {
|
|
|
92
92
|
});
|
|
93
93
|
return focusEl;
|
|
94
94
|
}
|
|
95
|
-
async applyFocus(focusEl) {
|
|
95
|
+
async applyFocus(targetOpenState, focusEl) {
|
|
96
96
|
if (this.receivesFocus === "false") {
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
await nextFrame();
|
|
100
100
|
await nextFrame();
|
|
101
|
-
if (!this.open) {
|
|
101
|
+
if (targetOpenState === this.open && !this.open) {
|
|
102
102
|
if (
|
|
103
103
|
// Do not return focus to trigger when overlay is a "hint" (tooltip)
|
|
104
104
|
this.type !== "hint" && // Only return focus when the trigger is not "virtual"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["OverlayNoPopover.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport {\n firstFocusableIn,\n firstFocusableSlottedIn,\n} from '@spectrum-web-components/shared/src/first-focusable-in.js';\nimport { ReactiveElement } from 'lit';\nimport {\n BeforetoggleClosedEvent,\n BeforetoggleOpenEvent,\n guaranteedTransitionend,\n OpenableElement,\n OverlayBase,\n} from './OverlayBase.dev.js'\nimport { VirtualTrigger } from './VirtualTrigger.dev.js'\n\ntype Constructor<T = Record<string, unknown>> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]): T;\n prototype: T;\n};\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n\nexport function OverlayNoPopover<T extends Constructor<OverlayBase>>(\n constructor: T\n): T & Constructor<ReactiveElement> {\n class OverlayWithNoPopover extends constructor {\n protected override async managePopoverOpen(): Promise<void> {\n const targetOpenState = this.open;\n await this.managePosition();\n await this.ensureOnDOM();\n const focusEl = await this.makeTransition(targetOpenState);\n await this.applyFocus(focusEl);\n }\n\n private async ensureOnDOM(): Promise<void> {\n await nextFrame();\n await nextFrame();\n }\n\n private async makeTransition(\n targetOpenState: boolean\n ): Promise<HTMLElement | null> {\n let focusEl = null as HTMLElement | null;\n const start = (el: OpenableElement, index: number) => (): void => {\n if (targetOpenState !== this.open) {\n return;\n }\n if (typeof el.open !== 'undefined') {\n el.open = targetOpenState;\n }\n if (index === 0) {\n const event = targetOpenState\n ? BeforetoggleOpenEvent\n : BeforetoggleClosedEvent;\n this.dispatchEvent(new event());\n }\n if (targetOpenState !== true) {\n return;\n }\n focusEl = focusEl || firstFocusableIn(el);\n if (focusEl) {\n return;\n }\n const childSlots = el.querySelectorAll('slot');\n childSlots.forEach((slot) => {\n if (!focusEl) {\n focusEl = firstFocusableSlottedIn(slot);\n }\n });\n };\n const finish = (el: OpenableElement, index: number) => (): void => {\n if (this.open !== targetOpenState) {\n return;\n }\n const eventName = targetOpenState ? 'sp-opened' : 'sp-closed';\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n if (index > 0) {\n return;\n }\n const hasVirtualTrigger =\n this.triggerElement instanceof VirtualTrigger;\n this.dispatchEvent(\n new Event(eventName, {\n bubbles: hasVirtualTrigger,\n composed: hasVirtualTrigger,\n })\n );\n if (this.triggerElement && !hasVirtualTrigger) {\n (this.triggerElement as HTMLElement).dispatchEvent(\n new CustomEvent(eventName, {\n bubbles: true,\n composed: true,\n detail: { interaction: this.type },\n })\n );\n }\n };\n this.elements.forEach((el, index) => {\n guaranteedTransitionend(\n el,\n start(el, index),\n finish(el, index)\n );\n });\n return focusEl;\n }\n\n private async applyFocus(focusEl: HTMLElement | null): Promise<void> {\n if (this.receivesFocus === 'false') {\n return;\n }\n\n await nextFrame();\n await nextFrame();\n if (!this.open) {\n if (\n // Do not return focus to trigger when overlay is a \"hint\" (tooltip)\n this.type !== 'hint' &&\n // Only return focus when the trigger is not \"virtual\"\n this.triggerElement &&\n !(this.triggerElement instanceof VirtualTrigger)\n ) {\n // This has a bug where the current overlay and the focused content could share the same `activeElement` shadow root...\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n this.addEventListener(\n relationEvent.type,\n (event: Event) => {\n /* eslint-disable @spectrum-web-components/document-active-element */\n if (\n document.activeElement &&\n event\n .composedPath()\n .includes(document.activeElement)\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n /* eslint-enable @spectrum-web-components/document-active-element */\n }\n );\n this.dispatchEvent(relationEvent);\n }\n return;\n }\n\n focusEl?.focus();\n }\n }\n return OverlayWithNoPopover;\n}\n"],
|
|
5
|
-
"mappings": ";AAWA;AAAA,EACI;AAAA,EACA;AAAA,OACG;AAEP;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OAGG;AACP,SAAS,sBAAsB;AAQ/B,SAAS,YAA2B;AAChC,SAAO,IAAI,QAAQ,CAAC,QAAQ,sBAAsB,MAAM,IAAI,CAAC,CAAC;AAClE;AAEO,gBAAS,iBACZ,aACgC;AAChC,QAAM,6BAA6B,YAAY;AAAA,IAC3C,MAAyB,oBAAmC;AACxD,YAAM,kBAAkB,KAAK;AAC7B,YAAM,KAAK,eAAe;AAC1B,YAAM,KAAK,YAAY;AACvB,YAAM,UAAU,MAAM,KAAK,eAAe,eAAe;AACzD,YAAM,KAAK,WAAW,OAAO;AAAA,
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport {\n firstFocusableIn,\n firstFocusableSlottedIn,\n} from '@spectrum-web-components/shared/src/first-focusable-in.js';\nimport { ReactiveElement } from 'lit';\nimport {\n BeforetoggleClosedEvent,\n BeforetoggleOpenEvent,\n guaranteedTransitionend,\n OpenableElement,\n OverlayBase,\n} from './OverlayBase.dev.js'\nimport { VirtualTrigger } from './VirtualTrigger.dev.js'\n\ntype Constructor<T = Record<string, unknown>> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]): T;\n prototype: T;\n};\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n\nexport function OverlayNoPopover<T extends Constructor<OverlayBase>>(\n constructor: T\n): T & Constructor<ReactiveElement> {\n class OverlayWithNoPopover extends constructor {\n protected override async managePopoverOpen(): Promise<void> {\n const targetOpenState = this.open;\n await this.managePosition();\n await this.ensureOnDOM();\n const focusEl = await this.makeTransition(targetOpenState);\n await this.applyFocus(targetOpenState, focusEl);\n }\n\n private async ensureOnDOM(): Promise<void> {\n await nextFrame();\n await nextFrame();\n }\n\n private async makeTransition(\n targetOpenState: boolean\n ): Promise<HTMLElement | null> {\n let focusEl = null as HTMLElement | null;\n const start = (el: OpenableElement, index: number) => (): void => {\n if (targetOpenState !== this.open) {\n return;\n }\n if (typeof el.open !== 'undefined') {\n el.open = targetOpenState;\n }\n if (index === 0) {\n const event = targetOpenState\n ? BeforetoggleOpenEvent\n : BeforetoggleClosedEvent;\n this.dispatchEvent(new event());\n }\n if (targetOpenState !== true) {\n return;\n }\n focusEl = focusEl || firstFocusableIn(el);\n if (focusEl) {\n return;\n }\n const childSlots = el.querySelectorAll('slot');\n childSlots.forEach((slot) => {\n if (!focusEl) {\n focusEl = firstFocusableSlottedIn(slot);\n }\n });\n };\n const finish = (el: OpenableElement, index: number) => (): void => {\n if (this.open !== targetOpenState) {\n return;\n }\n const eventName = targetOpenState ? 'sp-opened' : 'sp-closed';\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n if (index > 0) {\n return;\n }\n const hasVirtualTrigger =\n this.triggerElement instanceof VirtualTrigger;\n this.dispatchEvent(\n new Event(eventName, {\n bubbles: hasVirtualTrigger,\n composed: hasVirtualTrigger,\n })\n );\n if (this.triggerElement && !hasVirtualTrigger) {\n (this.triggerElement as HTMLElement).dispatchEvent(\n new CustomEvent(eventName, {\n bubbles: true,\n composed: true,\n detail: { interaction: this.type },\n })\n );\n }\n };\n this.elements.forEach((el, index) => {\n guaranteedTransitionend(\n el,\n start(el, index),\n finish(el, index)\n );\n });\n return focusEl;\n }\n\n private async applyFocus(targetOpenState: boolean, focusEl: HTMLElement | null): Promise<void> {\n if (this.receivesFocus === 'false') {\n return;\n }\n\n await nextFrame();\n await nextFrame();\n if (targetOpenState === this.open && !this.open) {\n if (\n // Do not return focus to trigger when overlay is a \"hint\" (tooltip)\n this.type !== 'hint' &&\n // Only return focus when the trigger is not \"virtual\"\n this.triggerElement &&\n !(this.triggerElement instanceof VirtualTrigger)\n ) {\n // This has a bug where the current overlay and the focused content could share the same `activeElement` shadow root...\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n this.addEventListener(\n relationEvent.type,\n (event: Event) => {\n /* eslint-disable @spectrum-web-components/document-active-element */\n if (\n document.activeElement &&\n event\n .composedPath()\n .includes(document.activeElement)\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n /* eslint-enable @spectrum-web-components/document-active-element */\n }\n );\n this.dispatchEvent(relationEvent);\n }\n return;\n }\n\n focusEl?.focus();\n }\n }\n return OverlayWithNoPopover;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/src/OverlayNoPopover.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";import{firstFocusableIn as p,firstFocusableSlottedIn as v}from"@spectrum-web-components/shared/src/first-focusable-in.js";import{BeforetoggleClosedEvent as
|
|
1
|
+
"use strict";import{firstFocusableIn as p,firstFocusableSlottedIn as v}from"@spectrum-web-components/shared/src/first-focusable-in.js";import{BeforetoggleClosedEvent as h,BeforetoggleOpenEvent as d,guaranteedTransitionend as f}from"./OverlayBase.js";import{VirtualTrigger as m}from"./VirtualTrigger.js";function a(){return new Promise(l=>requestAnimationFrame(()=>l()))}export function OverlayNoPopover(l){class u extends l{async managePopoverOpen(){const t=this.open;await this.managePosition(),await this.ensureOnDOM();const e=await this.makeTransition(t);await this.applyFocus(t,e)}async ensureOnDOM(){await a(),await a()}async makeTransition(t){let e=null;const r=(n,o)=>()=>{if(t!==this.open)return;if(typeof n.open!="undefined"&&(n.open=t),o===0){const i=t?d:h;this.dispatchEvent(new i)}if(t!==!0||(e=e||p(n),e))return;n.querySelectorAll("slot").forEach(i=>{e||(e=v(i))})},c=(n,o)=>()=>{if(this.open!==t)return;const s=t?"sp-opened":"sp-closed";if(n.dispatchEvent(new Event(s,{bubbles:!1,composed:!1})),o>0)return;const i=this.triggerElement instanceof m;this.dispatchEvent(new Event(s,{bubbles:i,composed:i})),this.triggerElement&&!i&&this.triggerElement.dispatchEvent(new CustomEvent(s,{bubbles:!0,composed:!0,detail:{interaction:this.type}}))};return this.elements.forEach((n,o)=>{f(n,r(n,o),c(n,o))}),e}async applyFocus(t,e){if(this.receivesFocus!=="false"){if(await a(),await a(),t===this.open&&!this.open){if(this.type!=="hint"&&this.triggerElement&&!(this.triggerElement instanceof m)){const r=new Event("overlay-relation-query",{bubbles:!0,composed:!0});this.addEventListener(r.type,c=>{document.activeElement&&c.composedPath().includes(document.activeElement)&&this.triggerElement.focus()}),this.dispatchEvent(r)}return}e==null||e.focus()}}}return u}
|
|
2
2
|
//# sourceMappingURL=OverlayNoPopover.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["OverlayNoPopover.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport {\n firstFocusableIn,\n firstFocusableSlottedIn,\n} from '@spectrum-web-components/shared/src/first-focusable-in.js';\nimport { ReactiveElement } from 'lit';\nimport {\n BeforetoggleClosedEvent,\n BeforetoggleOpenEvent,\n guaranteedTransitionend,\n OpenableElement,\n OverlayBase,\n} from './OverlayBase.js';\nimport { VirtualTrigger } from './VirtualTrigger.js';\n\ntype Constructor<T = Record<string, unknown>> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]): T;\n prototype: T;\n};\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n\nexport function OverlayNoPopover<T extends Constructor<OverlayBase>>(\n constructor: T\n): T & Constructor<ReactiveElement> {\n class OverlayWithNoPopover extends constructor {\n protected override async managePopoverOpen(): Promise<void> {\n const targetOpenState = this.open;\n await this.managePosition();\n await this.ensureOnDOM();\n const focusEl = await this.makeTransition(targetOpenState);\n await this.applyFocus(focusEl);\n }\n\n private async ensureOnDOM(): Promise<void> {\n await nextFrame();\n await nextFrame();\n }\n\n private async makeTransition(\n targetOpenState: boolean\n ): Promise<HTMLElement | null> {\n let focusEl = null as HTMLElement | null;\n const start = (el: OpenableElement, index: number) => (): void => {\n if (targetOpenState !== this.open) {\n return;\n }\n if (typeof el.open !== 'undefined') {\n el.open = targetOpenState;\n }\n if (index === 0) {\n const event = targetOpenState\n ? BeforetoggleOpenEvent\n : BeforetoggleClosedEvent;\n this.dispatchEvent(new event());\n }\n if (targetOpenState !== true) {\n return;\n }\n focusEl = focusEl || firstFocusableIn(el);\n if (focusEl) {\n return;\n }\n const childSlots = el.querySelectorAll('slot');\n childSlots.forEach((slot) => {\n if (!focusEl) {\n focusEl = firstFocusableSlottedIn(slot);\n }\n });\n };\n const finish = (el: OpenableElement, index: number) => (): void => {\n if (this.open !== targetOpenState) {\n return;\n }\n const eventName = targetOpenState ? 'sp-opened' : 'sp-closed';\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n if (index > 0) {\n return;\n }\n const hasVirtualTrigger =\n this.triggerElement instanceof VirtualTrigger;\n this.dispatchEvent(\n new Event(eventName, {\n bubbles: hasVirtualTrigger,\n composed: hasVirtualTrigger,\n })\n );\n if (this.triggerElement && !hasVirtualTrigger) {\n (this.triggerElement as HTMLElement).dispatchEvent(\n new CustomEvent(eventName, {\n bubbles: true,\n composed: true,\n detail: { interaction: this.type },\n })\n );\n }\n };\n this.elements.forEach((el, index) => {\n guaranteedTransitionend(\n el,\n start(el, index),\n finish(el, index)\n );\n });\n return focusEl;\n }\n\n private async applyFocus(focusEl: HTMLElement | null): Promise<void> {\n if (this.receivesFocus === 'false') {\n return;\n }\n\n await nextFrame();\n await nextFrame();\n if (!this.open) {\n if (\n // Do not return focus to trigger when overlay is a \"hint\" (tooltip)\n this.type !== 'hint' &&\n // Only return focus when the trigger is not \"virtual\"\n this.triggerElement &&\n !(this.triggerElement instanceof VirtualTrigger)\n ) {\n // This has a bug where the current overlay and the focused content could share the same `activeElement` shadow root...\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n this.addEventListener(\n relationEvent.type,\n (event: Event) => {\n /* eslint-disable @spectrum-web-components/document-active-element */\n if (\n document.activeElement &&\n event\n .composedPath()\n .includes(document.activeElement)\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n /* eslint-enable @spectrum-web-components/document-active-element */\n }\n );\n this.dispatchEvent(relationEvent);\n }\n return;\n }\n\n focusEl?.focus();\n }\n }\n return OverlayWithNoPopover;\n}\n"],
|
|
5
|
-
"mappings": "aAWA,OACI,oBAAAA,EACA,2BAAAC,MACG,4DAEP,OACI,2BAAAC,EACA,yBAAAC,EACA,2BAAAC,MAGG,mBACP,OAAS,kBAAAC,MAAsB,sBAQ/B,SAASC,GAA2B,CAChC,OAAO,IAAI,QAASC,GAAQ,sBAAsB,IAAMA,EAAI,CAAC,CAAC,CAClE,CAEO,gBAAS,iBACZC,EACgC,CAChC,MAAMC,UAA6BD,CAAY,CAC3C,MAAyB,mBAAmC,CACxD,MAAME,EAAkB,KAAK,KAC7B,MAAM,KAAK,eAAe,EAC1B,MAAM,KAAK,YAAY,EACvB,MAAMC,EAAU,MAAM,KAAK,eAAeD,CAAe,EACzD,MAAM,KAAK,
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport {\n firstFocusableIn,\n firstFocusableSlottedIn,\n} from '@spectrum-web-components/shared/src/first-focusable-in.js';\nimport { ReactiveElement } from 'lit';\nimport {\n BeforetoggleClosedEvent,\n BeforetoggleOpenEvent,\n guaranteedTransitionend,\n OpenableElement,\n OverlayBase,\n} from './OverlayBase.js';\nimport { VirtualTrigger } from './VirtualTrigger.js';\n\ntype Constructor<T = Record<string, unknown>> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (...args: any[]): T;\n prototype: T;\n};\n\nfunction nextFrame(): Promise<void> {\n return new Promise((res) => requestAnimationFrame(() => res()));\n}\n\nexport function OverlayNoPopover<T extends Constructor<OverlayBase>>(\n constructor: T\n): T & Constructor<ReactiveElement> {\n class OverlayWithNoPopover extends constructor {\n protected override async managePopoverOpen(): Promise<void> {\n const targetOpenState = this.open;\n await this.managePosition();\n await this.ensureOnDOM();\n const focusEl = await this.makeTransition(targetOpenState);\n await this.applyFocus(targetOpenState, focusEl);\n }\n\n private async ensureOnDOM(): Promise<void> {\n await nextFrame();\n await nextFrame();\n }\n\n private async makeTransition(\n targetOpenState: boolean\n ): Promise<HTMLElement | null> {\n let focusEl = null as HTMLElement | null;\n const start = (el: OpenableElement, index: number) => (): void => {\n if (targetOpenState !== this.open) {\n return;\n }\n if (typeof el.open !== 'undefined') {\n el.open = targetOpenState;\n }\n if (index === 0) {\n const event = targetOpenState\n ? BeforetoggleOpenEvent\n : BeforetoggleClosedEvent;\n this.dispatchEvent(new event());\n }\n if (targetOpenState !== true) {\n return;\n }\n focusEl = focusEl || firstFocusableIn(el);\n if (focusEl) {\n return;\n }\n const childSlots = el.querySelectorAll('slot');\n childSlots.forEach((slot) => {\n if (!focusEl) {\n focusEl = firstFocusableSlottedIn(slot);\n }\n });\n };\n const finish = (el: OpenableElement, index: number) => (): void => {\n if (this.open !== targetOpenState) {\n return;\n }\n const eventName = targetOpenState ? 'sp-opened' : 'sp-closed';\n el.dispatchEvent(\n new Event(eventName, {\n bubbles: false,\n composed: false,\n })\n );\n if (index > 0) {\n return;\n }\n const hasVirtualTrigger =\n this.triggerElement instanceof VirtualTrigger;\n this.dispatchEvent(\n new Event(eventName, {\n bubbles: hasVirtualTrigger,\n composed: hasVirtualTrigger,\n })\n );\n if (this.triggerElement && !hasVirtualTrigger) {\n (this.triggerElement as HTMLElement).dispatchEvent(\n new CustomEvent(eventName, {\n bubbles: true,\n composed: true,\n detail: { interaction: this.type },\n })\n );\n }\n };\n this.elements.forEach((el, index) => {\n guaranteedTransitionend(\n el,\n start(el, index),\n finish(el, index)\n );\n });\n return focusEl;\n }\n\n private async applyFocus(targetOpenState: boolean, focusEl: HTMLElement | null): Promise<void> {\n if (this.receivesFocus === 'false') {\n return;\n }\n\n await nextFrame();\n await nextFrame();\n if (targetOpenState === this.open && !this.open) {\n if (\n // Do not return focus to trigger when overlay is a \"hint\" (tooltip)\n this.type !== 'hint' &&\n // Only return focus when the trigger is not \"virtual\"\n this.triggerElement &&\n !(this.triggerElement instanceof VirtualTrigger)\n ) {\n // This has a bug where the current overlay and the focused content could share the same `activeElement` shadow root...\n const relationEvent = new Event('overlay-relation-query', {\n bubbles: true,\n composed: true,\n });\n this.addEventListener(\n relationEvent.type,\n (event: Event) => {\n /* eslint-disable @spectrum-web-components/document-active-element */\n if (\n document.activeElement &&\n event\n .composedPath()\n .includes(document.activeElement)\n ) {\n (this.triggerElement as HTMLElement).focus();\n }\n /* eslint-enable @spectrum-web-components/document-active-element */\n }\n );\n this.dispatchEvent(relationEvent);\n }\n return;\n }\n\n focusEl?.focus();\n }\n }\n return OverlayWithNoPopover;\n}\n"],
|
|
5
|
+
"mappings": "aAWA,OACI,oBAAAA,EACA,2BAAAC,MACG,4DAEP,OACI,2BAAAC,EACA,yBAAAC,EACA,2BAAAC,MAGG,mBACP,OAAS,kBAAAC,MAAsB,sBAQ/B,SAASC,GAA2B,CAChC,OAAO,IAAI,QAASC,GAAQ,sBAAsB,IAAMA,EAAI,CAAC,CAAC,CAClE,CAEO,gBAAS,iBACZC,EACgC,CAChC,MAAMC,UAA6BD,CAAY,CAC3C,MAAyB,mBAAmC,CACxD,MAAME,EAAkB,KAAK,KAC7B,MAAM,KAAK,eAAe,EAC1B,MAAM,KAAK,YAAY,EACvB,MAAMC,EAAU,MAAM,KAAK,eAAeD,CAAe,EACzD,MAAM,KAAK,WAAWA,EAAiBC,CAAO,CAClD,CAEA,MAAc,aAA6B,CACvC,MAAML,EAAU,EAChB,MAAMA,EAAU,CACpB,CAEA,MAAc,eACVI,EAC2B,CAC3B,IAAIC,EAAU,KACd,MAAMC,EAAQ,CAACC,EAAqBC,IAAkB,IAAY,CAC9D,GAAIJ,IAAoB,KAAK,KACzB,OAKJ,GAHI,OAAOG,EAAG,MAAS,cACnBA,EAAG,KAAOH,GAEVI,IAAU,EAAG,CACb,MAAMC,EAAQL,EACRP,EACAD,EACN,KAAK,cAAc,IAAIa,CAAO,CAClC,CAKA,GAJIL,IAAoB,KAGxBC,EAAUA,GAAWX,EAAiBa,CAAE,EACpCF,GACA,OAEeE,EAAG,iBAAiB,MAAM,EAClC,QAASG,GAAS,CACpBL,IACDA,EAAUV,EAAwBe,CAAI,EAE9C,CAAC,CACL,EACMC,EAAS,CAACJ,EAAqBC,IAAkB,IAAY,CAC/D,GAAI,KAAK,OAASJ,EACd,OAEJ,MAAMQ,EAAYR,EAAkB,YAAc,YAOlD,GANAG,EAAG,cACC,IAAI,MAAMK,EAAW,CACjB,QAAS,GACT,SAAU,EACd,CAAC,CACL,EACIJ,EAAQ,EACR,OAEJ,MAAMK,EACF,KAAK,0BAA0Bd,EACnC,KAAK,cACD,IAAI,MAAMa,EAAW,CACjB,QAASC,EACT,SAAUA,CACd,CAAC,CACL,EACI,KAAK,gBAAkB,CAACA,GACvB,KAAK,eAA+B,cACjC,IAAI,YAAYD,EAAW,CACvB,QAAS,GACT,SAAU,GACV,OAAQ,CAAE,YAAa,KAAK,IAAK,CACrC,CAAC,CACL,CAER,EACA,YAAK,SAAS,QAAQ,CAACL,EAAIC,IAAU,CACjCV,EACIS,EACAD,EAAMC,EAAIC,CAAK,EACfG,EAAOJ,EAAIC,CAAK,CACpB,CACJ,CAAC,EACMH,CACX,CAEA,MAAc,WAAWD,EAA0BC,EAA4C,CAC3F,GAAI,KAAK,gBAAkB,QAM3B,IAFA,MAAML,EAAU,EAChB,MAAMA,EAAU,EACZI,IAAoB,KAAK,MAAQ,CAAC,KAAK,KAAM,CAC7C,GAEI,KAAK,OAAS,QAEd,KAAK,gBACL,EAAE,KAAK,0BAA0BL,GACnC,CAEE,MAAMe,EAAgB,IAAI,MAAM,yBAA0B,CACtD,QAAS,GACT,SAAU,EACd,CAAC,EACD,KAAK,iBACDA,EAAc,KACbL,GAAiB,CAGV,SAAS,eACTA,EACK,aAAa,EACb,SAAS,SAAS,aAAa,GAEnC,KAAK,eAA+B,MAAM,CAGnD,CACJ,EACA,KAAK,cAAcK,CAAa,CACpC,CACA,MACJ,CAEAT,GAAA,MAAAA,EAAS,QACb,CACJ,CACA,OAAOF,CACX",
|
|
6
6
|
"names": ["firstFocusableIn", "firstFocusableSlottedIn", "BeforetoggleClosedEvent", "BeforetoggleOpenEvent", "guaranteedTransitionend", "VirtualTrigger", "nextFrame", "res", "constructor", "OverlayWithNoPopover", "targetOpenState", "focusEl", "start", "el", "index", "event", "slot", "finish", "eventName", "hasVirtualTrigger", "relationEvent"]
|
|
7
7
|
}
|
|
@@ -19,7 +19,7 @@ export function OverlayPopover(constructor) {
|
|
|
19
19
|
await this.managePosition();
|
|
20
20
|
await this.ensureOnDOM(targetOpenState);
|
|
21
21
|
const focusEl = await this.makeTransition(targetOpenState);
|
|
22
|
-
await this.applyFocus(focusEl);
|
|
22
|
+
await this.applyFocus(targetOpenState, focusEl);
|
|
23
23
|
}
|
|
24
24
|
async ensureOnDOM(targetOpenState) {
|
|
25
25
|
await nextFrame();
|
|
@@ -94,7 +94,10 @@ export function OverlayPopover(constructor) {
|
|
|
94
94
|
);
|
|
95
95
|
}
|
|
96
96
|
};
|
|
97
|
-
if (
|
|
97
|
+
if (this.open !== targetOpenState) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (targetOpenState !== true && this.dialogEl.matches(":open") && this.isConnected) {
|
|
98
101
|
this.dialogEl.addEventListener(
|
|
99
102
|
"beforetoggle",
|
|
100
103
|
() => {
|
|
@@ -116,13 +119,13 @@ export function OverlayPopover(constructor) {
|
|
|
116
119
|
});
|
|
117
120
|
return focusEl;
|
|
118
121
|
}
|
|
119
|
-
async applyFocus(focusEl) {
|
|
122
|
+
async applyFocus(targetOpenState, focusEl) {
|
|
120
123
|
if (this.receivesFocus === "false") {
|
|
121
124
|
return;
|
|
122
125
|
}
|
|
123
126
|
await nextFrame();
|
|
124
127
|
await nextFrame();
|
|
125
|
-
if (!this.open) {
|
|
128
|
+
if (targetOpenState === this.open && !this.open) {
|
|
126
129
|
if (
|
|
127
130
|
// Do not return focus to trigger when overlay is a "hint" (tooltip)
|
|
128
131
|
this.type !== "hint" && // Only return focus when the trigger is not "virtual"
|