Fixes to issues with Dialog, FocusTrap, and StackContextProvider

* StackContextProvider had some reactivity that didn't make sense and would cause an infinite loop if it ever triggered.
* FocusTrap and Dialog did not work together properly all of the time. A top-level <Dialog> with initial state of closed would not have the FocusTrap triggered correctly when it was opened. Needed to make it reactive to changes in `containers`
This commit is contained in:
Ryan Gossiaux
2021-12-26 16:28:37 -08:00
parent 10f2eab08d
commit a2745e1770
3 changed files with 12 additions and 14 deletions

View File

@@ -253,10 +253,11 @@
onUpdate={(message, element) => { onUpdate={(message, element) => {
return match(message, { return match(message, {
[StackMessage.Add]() { [StackMessage.Add]() {
containers.add(element); containers = new Set([...containers, element]);
}, },
[StackMessage.Remove]() { [StackMessage.Remove]() {
containers.delete(element); containers.delete(element);
containers = new Set([...containers]);
}, },
}); });
}} }}

View File

@@ -7,7 +7,7 @@
FocusResult, FocusResult,
} from "$lib/utils/focus-management"; } from "$lib/utils/focus-management";
import { contains } from "$lib/internal/dom-containers"; import { contains } from "$lib/internal/dom-containers";
import { afterUpdate, onMount, onDestroy, tick } from "svelte"; import { onMount, onDestroy, tick } from "svelte";
export let containers: Set<HTMLElement>; export let containers: Set<HTMLElement>;
export let enabled: boolean = true; export let enabled: boolean = true;
@@ -74,7 +74,7 @@
// Handle initial focus // Handle initial focus
onMount(handleFocus); onMount(handleFocus);
afterUpdate(() => (enabled ? handleFocus() : restore())); $: enabled && containers ? handleFocus() : restore();
// When this component is being destroyed, focusElement is called // When this component is being destroyed, focusElement is called
// before handleWindowFocus is removed, so in the svelte port we add this // before handleWindowFocus is removed, so in the svelte port we add this

View File

@@ -9,24 +9,21 @@
<script lang="ts"> <script lang="ts">
import { getContext, onDestroy, setContext } from "svelte"; import { getContext, onDestroy, setContext } from "svelte";
import { Readable, writable, Writable } from "svelte/store";
type OnUpdate = (message: StackMessage, element: HTMLElement) => void; type OnUpdate = (message: StackMessage, element: HTMLElement) => void;
export let onUpdate: OnUpdate | undefined; export let onUpdate: OnUpdate | undefined;
export let element: HTMLElement | null; export let element: HTMLElement | null;
let parentUpdateStore: Readable<OnUpdate> | undefined = function notify(...args: Parameters<OnUpdate>) {
getContext(STACK_CONTEXT_NAME);
let notifyStore: Writable<OnUpdate> = writable(() => {});
setContext(STACK_CONTEXT_NAME, notifyStore);
$: notifyStore.set((...args: Parameters<OnUpdate>) => {
// Notify our layer // Notify our layer
onUpdate?.(...args); onUpdate?.(...args);
// Notify the parent // Notify the parent
$parentUpdateStore?.(...args); parentUpdate?.(...args);
}); }
let parentUpdate: OnUpdate | undefined = getContext(STACK_CONTEXT_NAME);
setContext(STACK_CONTEXT_NAME, notify);
$: _cleanup = (() => { $: _cleanup = (() => {
if (_cleanup) { if (_cleanup) {
@@ -34,8 +31,8 @@
} }
if (!element) return null; if (!element) return null;
let savedElement = element; let savedElement = element;
$notifyStore(StackMessage.Add, savedElement); notify(StackMessage.Add, savedElement);
return () => $notifyStore(StackMessage.Remove, savedElement); return () => notify(StackMessage.Remove, savedElement);
})(); })();
onDestroy(() => { onDestroy(() => {