Skip to content

Instantly share code, notes, and snippets.

@nwhittaker
Created May 30, 2024 14:01
Show Gist options
  • Save nwhittaker/e07e4e0ec4dfb0ec0a52a1fe98dfff30 to your computer and use it in GitHub Desktop.
Save nwhittaker/e07e4e0ec4dfb0ec0a52a1fe98dfff30 to your computer and use it in GitHub Desktop.
Outside Calcite click workaround
import {
CalciteComboboxCustomEvent,
CalciteDropdownCustomEvent,
CalciteInputTimeZoneCustomEvent,
CalcitePopoverCustomEvent,
} from '@esri/calcite-components'
type CalciteOutsideClickCustomEvent<T> = CalciteComboboxCustomEvent<T>
| CalciteDropdownCustomEvent<T>
| CalciteInputTimeZoneCustomEvent<T>
| CalcitePopoverCustomEvent<T>
type HTMLCalciteOutsideClickElement = CalciteOutsideClickCustomEvent<void>['target']
// Workaround for Esri/calcite-design-system#9013.
export default function patchComponentsWithOutsideClickHandlers() {
// Track the opened targets and their outside-click handlers.
const targets = new WeakMap<HTMLCalciteOutsideClickElement, (event: MouseEvent) => void>()
window.addEventListener('calciteComboboxBeforeOpen', onBeforeOpen)
window.addEventListener('calciteComboboxBeforeClose', onBeforeClose)
window.addEventListener('calciteDropdownBeforeOpen', onBeforeOpen)
window.addEventListener('calciteDropdownBeforeClose', onBeforeClose)
window.addEventListener('calciteInputTimeZoneBeforeOpen', onBeforeOpen)
window.addEventListener('calciteInputTimeZoneBeforeClose', onBeforeClose)
window.addEventListener('calcitePopoverBeforeOpen', onBeforeOpen)
window.addEventListener('calcitePopoverBeforeClose', onBeforeClose)
// Register the opening target and add a one-off outside-click handler for it.
function onBeforeOpen(event: CalciteOutsideClickCustomEvent<void>) {
const target = event.composedPath()[0] as HTMLCalciteOutsideClickElement
if ('autoClose' in target && !target.autoClose) return
if (targets.has(target)) return
const referenceElement = referenceElementOf(target)
targets.set(target, onClick)
window.addEventListener('click', onClick, { capture: true })
function onClick(clickEvent: MouseEvent) {
const shouldKeepOpen = referenceElement === clickEvent.target || clickEvent.composedPath().includes(target)
// Known issue: triggers multiple close events on `input-time-zone` (Esri/calcite-design-system#9315)
target.open = shouldKeepOpen
}
}
// Unregister the closing target and remove the outside-click handler.
function onBeforeClose(event: CalciteOutsideClickCustomEvent<void>) {
const target = event.composedPath()[0] as HTMLCalciteOutsideClickElement,
onClick = targets.get(target)
if (!onClick) return
window.removeEventListener('click', onClick, { capture: true })
targets.delete(target)
}
}
function referenceElementOf(target: HTMLCalciteOutsideClickElement) {
if (!('referenceElement' in target)) return
const { referenceElement } = target
if (typeof referenceElement === 'string') {
const root = target.getRootNode() as Document | ShadowRoot
return root.getElementById(referenceElement)
}
return referenceElement
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment