Last active
September 30, 2020 02:31
-
-
Save dimfeld/daa98d61dbb378197098287a3d8ba186 to your computer and use it in GitHub Desktop.
Very simplified version of nested dropdown tracking.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- This is missing a bunch of the code that interacts with tippy to position the element, but | |
hopefully it makes sense --> | |
<script> | |
// The popup tracker makes sure that "outside click" handling doesn't trigger when a click occurs | |
// in a child dropdown, and the child dropdown's popup is fixed. | |
function makePopupTracker(parent) { | |
let contained: HTMLElement[] = []; | |
return { | |
contains(target) { | |
return contained.some((e) => e.contains(target)); | |
}, | |
register(element: HTMLElement) { | |
if (parent) { | |
parent.register(element); | |
} | |
contained.push(element); | |
}, | |
unregister(element: HTMLElement) { | |
if (parent) { | |
parent.unregister(element); | |
} | |
contained = contained.filter((e) => e !== element); | |
}, | |
}; | |
} | |
// Get the popup tracker from the parent context, if any, | |
// and create a new popup tracker that inherits it. | |
let parentTracker = getContext('parent-dropdown-tracker'); | |
let popupTracker = makePopupTracker(parentTracker); | |
setContext('parent-dropdown-tracker', popupTracker); | |
// Svelte action to add and remove the popup Element in the popup tracker. | |
function addToPopupTracker(node: HTMLElement) { | |
let elementExists = true; | |
if (closeOnClickOutside) { | |
setTimeout(() => { | |
if (elementExists) { | |
window.addEventListener('click', handleDocumentClick, { | |
capture: true, | |
}); | |
} | |
}); | |
} | |
popupTracker.register(node); | |
return { | |
destroy() { | |
elementExists = false; | |
if (closeOnClickOutside) { | |
window.removeEventListener('click', handleDocumentClick, { | |
capture: true, | |
}); | |
} | |
popupTracker.unregister(node); | |
}, | |
}; | |
} | |
let open = false; | |
function handleDocumentClick(e: MouseEvent) { | |
if ( | |
!popupTracker.contains(e.target as HTMLElement) && | |
(e.target as Node).getRootNode() === document | |
) { | |
open = false; | |
// Don't let anything else handle this. | |
e.stopImmediatePropagation(); | |
} | |
} | |
// Code to set this up omitted for brevity. | |
$: if (tippyInstance) { | |
if (open) { | |
tippyInstance.show(); | |
} else { | |
tippyInstance.hide(); | |
} | |
} | |
</script> | |
<slot name="button"><button type="button" on:click={() => (open = !open)}>{label}</button></slot> | |
{#if open} | |
<div use:addToPopupTracker> | |
<slot /> | |
</div> | |
{/if} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment