Track modals and apply class in central location.

Automatically stop tracking destroyed modals.
This commit is contained in:
Harald Brunner 2022-06-06 21:06:31 +02:00
commit 118270f767
3 changed files with 44 additions and 35 deletions

View file

@ -38,7 +38,7 @@
afterUpdate, afterUpdate,
} from "svelte"; } from "svelte";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { modalsOpen, addModalId, removeModalId } from "../Modal/modalStore"; import { trackModal } from "../Modal/modalStore";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const label = writable(undefined); const label = writable(undefined);
@ -74,15 +74,14 @@
let opened = false; let opened = false;
$: didOpen = open; $: didOpen = open;
const openStore = writable(open);
$: $openStore = open;
trackModal(openStore);
onMount(() => { onMount(() => {
tick().then(() => { tick().then(() => {
focus(); focus();
}); });
return () => {
removeModalId(id);
document.body.classList.remove("bx--body--with-modal-open");
};
}); });
afterUpdate(() => { afterUpdate(() => {
@ -95,15 +94,7 @@
opened = true; opened = true;
dispatch("open"); dispatch("open");
} }
document.body.classList.toggle("bx--body--with-modal-open", $modalsOpen.length > 0);
}); });
$: if (open) {
addModalId(id);
} else {
removeModalId(id);
}
</script> </script>
<!-- svelte-ignore a11y-mouse-events-have-key-events --> <!-- svelte-ignore a11y-mouse-events-have-key-events -->

View file

@ -89,10 +89,11 @@
/** Obtain a reference to the top-level HTML element */ /** Obtain a reference to the top-level HTML element */
export let ref = null; export let ref = null;
import { createEventDispatcher, onMount, afterUpdate } from "svelte"; import { createEventDispatcher, afterUpdate } from "svelte";
import Close from "../icons/Close.svelte"; import Close from "../icons/Close.svelte";
import Button from "../Button/Button.svelte"; import Button from "../Button/Button.svelte";
import { modalsOpen, addModalId, removeModalId } from "./modalStore"; import { trackModal } from "./modalStore";
import { writable } from "svelte/store";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@ -107,12 +108,9 @@
node.focus(); node.focus();
} }
onMount(() => { const openStore = writable(open);
return () => { $: $openStore = open;
removeModalId(id); trackModal(openStore);
document.body.classList.remove("bx--body--with-modal-open");
};
});
afterUpdate(() => { afterUpdate(() => {
if (opened) { if (opened) {
@ -125,8 +123,6 @@
focus(); focus();
dispatch("open"); dispatch("open");
} }
document.body.classList.toggle("bx--body--with-modal-open", $modalsOpen.length > 0);
}); });
$: modalLabelId = `bx--modal-header__label--modal-${id}`; $: modalLabelId = `bx--modal-header__label--modal-${id}`;
@ -134,12 +130,6 @@
$: modalBodyId = `bx--modal-body--${id}`; $: modalBodyId = `bx--modal-body--${id}`;
$: ariaLabel = $: ariaLabel =
modalLabel || $$props["aria-label"] || modalAriaLabel || modalHeading; modalLabel || $$props["aria-label"] || modalAriaLabel || modalHeading;
$: if (open) {
addModalId(id);
} else {
removeModalId(id);
}
</script> </script>
<!-- svelte-ignore a11y-mouse-events-have-key-events --> <!-- svelte-ignore a11y-mouse-events-have-key-events -->

View file

@ -1,8 +1,36 @@
import { writable } from "svelte/store"; import { onMount } from "svelte";
import { get, writable } from "svelte/store";
export const modalsOpen = writable([]); /** A set of stores indicating whether a modal is open. */
const stores = new Set();
export const addModalId = (id) => modalsOpen.update((ids) => [...ids, id]); /** Store for the number of open modals. */
const modalsOpen = writable(0);
export const removeModalId = (id) => /**
modalsOpen.update((ids) => ids.filter((_id) => _id !== id)); * Adds a modal's store to the open modal tracking.
* Has to be called during component initialization.
* Modal is automatically removed on destroy.
* @param {import('svelte/store').Readable<boolean>} openStore
* Store that indicates whether the modal is opened.
*/
export const trackModal = (openStore) =>
onMount(() => {
stores.add(openStore);
const unsubscribe = openStore.subscribe(() =>
modalsOpen.set([...stores].filter((open) => get(open)).length)
);
return () => {
unsubscribe();
if (get(openStore)) modalsOpen.update((count) => count - 1);
stores.delete(openStore);
};
});
modalsOpen.subscribe((openCount) => {
if (typeof document !== "undefined")
document.body.classList.toggle("bx--body--with-modal-open", openCount > 0);
});