Skip to content

Commit 00dbb2f

Browse files
committed
fix(modal): Fixed modal animations not always playing when closing modal
1 parent a67a230 commit 00dbb2f

File tree

3 files changed

+41
-34
lines changed

3 files changed

+41
-34
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Component, createEffect } from 'solid-js';
1+
import { Component, Show } from 'solid-js';
22
import { Modal } from './modal.service';
3+
import { Dynamic } from 'solid-js/web';
34

45
/**
56
* Place this component anywhere in your application to make it the portal node for modals.
@@ -11,13 +12,9 @@ import { Modal } from './modal.service';
1112
* ```
1213
*/
1314
export const ModalPortal: Component = () => {
14-
let portal: HTMLDivElement | undefined;
15-
16-
createEffect(() => {
17-
if (portal) {
18-
Modal.setPortal(portal);
19-
}
20-
});
21-
22-
return <div ref={portal} class="spx-modal-portal" />;
15+
return (
16+
<Show when={Modal.state.component}>
17+
<Dynamic component={Modal.state.component as Component} {...Modal.state.options} />
18+
</Show>
19+
);
2320
};
+20-20
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { ServiceMixin } from '@spuxx/js-utils';
2-
import { Component, ComponentProps } from 'solid-js';
32
import { ModalComponent, ModalOptions } from './modal.types';
4-
import { render } from 'solid-js/web';
3+
import { createSignal } from 'solid-js';
4+
5+
interface ModalState {
6+
open: boolean;
7+
component: ModalComponent<never> | null;
8+
options?: ModalOptions;
9+
}
510

611
/**
712
* The `Modal` service provides global access to modal dialogs.
@@ -16,43 +21,38 @@ import { render } from 'solid-js/web';
1621
* ```
1722
*/
1823
export class Modal extends ServiceMixin<Modal>() {
19-
protected portal: Node | undefined;
20-
protected dispose = () => {};
24+
state = createSignal<ModalState>({ open: false, component: null });
2125

2226
/**
2327
* Shows the modal dialog of the given type and with the given options.
2428
* @param key The key of the modal to open.
2529
* @param options The options to pass to the modal.
2630
*/
2731
static show<TOptions extends ModalOptions>(modal: ModalComponent<TOptions>, options: TOptions) {
28-
if (!this.portal) {
29-
throw new Error(
30-
'No modal portal node has been registered. Please register a portal node first.',
31-
);
32-
}
33-
this.instance.dispose = render(() => modal(options), this.portal);
32+
this.setState({ open: true, component: modal, options });
3433
}
3534

3635
/**
3736
* Closes the modal dialog that is currently open, if there is one.
3837
*/
39-
static close() {
40-
this.instance.dispose();
38+
static async close() {
39+
this.setState({ ...this.state, open: false });
4140
}
4241

4342
/**
44-
* Returns the portal node that is used to render the modal.
43+
* Returns the current state of the modal. The state is read-only.
4544
*/
46-
static get portal(): Node | undefined {
47-
return this.instance.portal;
45+
static get state(): ModalState {
46+
const [state] = this.instance.state;
47+
return state();
4848
}
4949

5050
/**
51-
* Registers the given node as the portal node that is used to render the modal.
52-
* Only one portal node can be registered at a time.
53-
* @param node The node to register as the portal node.
51+
* Sets the current state of the modal. You should avoid manipulating the modal state directly
52+
* and instead use `Modal.show()` and `Modal.close()`.
5453
*/
55-
static setPortal(node: Node) {
56-
this.instance.portal = node;
54+
static setState(newState: ModalState): void {
55+
const [_state, setModalState] = this.instance.state;
56+
setModalState(newState);
5757
}
5858
}

packages/solid/src/modal/template/modal-template.component.tsx

+14-4
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,32 @@ interface Props extends ModalOptions, ParentProps {}
2727
export const ModalTemplate: Component<Props> = (options) => {
2828
const { size = 'auto', preventClose = false, onClose } = options;
2929

30-
const handleContentPresentChange = (value: boolean) => {
31-
if (!value) Modal.close();
30+
const handleOpenChange = (value: boolean) => {
31+
if (value) return;
32+
Modal.close();
3233
if (typeof onClose === 'function') onClose();
3334
};
3435

36+
const handleContentPresentChange = (value: boolean) => {
37+
if (value) return;
38+
Modal.setState({
39+
open: false,
40+
component: null,
41+
});
42+
};
43+
3544
return (
3645
<Root
37-
initialOpen={true}
46+
open={Modal.state.open}
3847
modal={true}
3948
trapFocus={true}
4049
preventScroll={true}
4150
closeOnEscapeKeyDown={!preventClose}
4251
closeOnOutsidePointer={!preventClose}
52+
onOpenChange={handleOpenChange}
4353
onContentPresentChange={handleContentPresentChange}
4454
>
45-
<Portal mount={Modal.portal} forceMount={true}>
55+
<Portal forceMount={true}>
4656
<Overlay class="spx-modal-overlay" />
4757
<Content class="spx-modal" data-size={size}>
4858
{options.children}

0 commit comments

Comments
 (0)