From 118270f7677304585ed8ef97fc1e8493fa979862 Mon Sep 17 00:00:00 2001 From: Harald Brunner Date: Mon, 6 Jun 2022 21:06:31 +0200 Subject: [PATCH] Track modals and apply class in central location. Automatically stop tracking destroyed modals. --- src/ComposedModal/ComposedModal.svelte | 19 ++++--------- src/Modal/Modal.svelte | 22 ++++----------- src/Modal/modalStore.js | 38 ++++++++++++++++++++++---- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/ComposedModal/ComposedModal.svelte b/src/ComposedModal/ComposedModal.svelte index 0907a78c..ff6bf99f 100644 --- a/src/ComposedModal/ComposedModal.svelte +++ b/src/ComposedModal/ComposedModal.svelte @@ -38,7 +38,7 @@ afterUpdate, } from "svelte"; import { writable } from "svelte/store"; - import { modalsOpen, addModalId, removeModalId } from "../Modal/modalStore"; + import { trackModal } from "../Modal/modalStore"; const dispatch = createEventDispatcher(); const label = writable(undefined); @@ -74,15 +74,14 @@ let opened = false; $: didOpen = open; + const openStore = writable(open); + $: $openStore = open; + trackModal(openStore); + onMount(() => { tick().then(() => { focus(); }); - - return () => { - removeModalId(id); - document.body.classList.remove("bx--body--with-modal-open"); - }; }); afterUpdate(() => { @@ -95,15 +94,7 @@ opened = true; dispatch("open"); } - - document.body.classList.toggle("bx--body--with-modal-open", $modalsOpen.length > 0); }); - - $: if (open) { - addModalId(id); - } else { - removeModalId(id); - } diff --git a/src/Modal/Modal.svelte b/src/Modal/Modal.svelte index 2dd397f6..522a7ec7 100644 --- a/src/Modal/Modal.svelte +++ b/src/Modal/Modal.svelte @@ -89,10 +89,11 @@ /** Obtain a reference to the top-level HTML element */ export let ref = null; - import { createEventDispatcher, onMount, afterUpdate } from "svelte"; + import { createEventDispatcher, afterUpdate } from "svelte"; import Close from "../icons/Close.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(); @@ -107,12 +108,9 @@ node.focus(); } - onMount(() => { - return () => { - removeModalId(id); - document.body.classList.remove("bx--body--with-modal-open"); - }; - }); + const openStore = writable(open); + $: $openStore = open; + trackModal(openStore); afterUpdate(() => { if (opened) { @@ -125,8 +123,6 @@ focus(); dispatch("open"); } - - document.body.classList.toggle("bx--body--with-modal-open", $modalsOpen.length > 0); }); $: modalLabelId = `bx--modal-header__label--modal-${id}`; @@ -134,12 +130,6 @@ $: modalBodyId = `bx--modal-body--${id}`; $: ariaLabel = modalLabel || $$props["aria-label"] || modalAriaLabel || modalHeading; - - $: if (open) { - addModalId(id); - } else { - removeModalId(id); - } diff --git a/src/Modal/modalStore.js b/src/Modal/modalStore.js index 2428919f..4206571a 100644 --- a/src/Modal/modalStore.js +++ b/src/Modal/modalStore.js @@ -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} 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); +});