S
SolidJS2mo ago
febri

solid-transition-group not working inside createRoot

What im trying to do I want a function that i can call such that i can just inject element where ever i want, this is useful for creating like confirmation. So currently if i want some kind of floating widget that is not always present AKA dynamically show up, im gonna need to create signal and then use that signal to fire <Show />, which is fine for form theyre sometime already huge so adding <Show /> wouldnt be much work. But in the case of simpler things like mere confirmation, i gotta first put that confirmation inside a <Show /> and then create 2 signal one for checking if the element is open and the other for confirmation answer (which is neccessary). and then i gotta make sure that everything is done after that confirmation closed (using effects and such). But now i have made myself a little function to create element outside of the current context so i can just call it where ever. this is the core function
interface IPopUpProps {
children: JSXElement
}

export default function popup(_children: () => JSXElement, on_close?: () => void) {
let close: () => void;

createRoot((dispose) => {
let _child: Element | undefined;
close = () => {
dispose()
_child?.remove();
on_close?.();
}
_child = children(_children)() as Element;
document.body.appendChild(_child);
})
return { close };
}
interface IPopUpProps {
children: JSXElement
}

export default function popup(_children: () => JSXElement, on_close?: () => void) {
let close: () => void;

createRoot((dispose) => {
let _child: Element | undefined;
close = () => {
dispose()
_child?.remove();
on_close?.();
}
_child = children(_children)() as Element;
document.body.appendChild(_child);
})
return { close };
}
2 Replies
febri
febri2mo ago
i use that to create a modal
export default function Modal(title: string, on_close: () => void, children: () => JSXElement) {
let close = () => { };
const pop = popup((
) => {
let bg: HTMLDivElement;
let modal: HTMLDivElement;
const animate_opt: KeyframeAnimationOptions = {
duration: 200,
fill: "forwards"
};
close = () => {
if (!bg || !modal) {
pop.close();
on_close();
return;
}

modal.animate([{ top: "50%", transform: "translate(-50%,-50%)" }, { top: "100vh", transform: "translate(-50%, 0)" }], { ...animate_opt, easing: "ease-in" })
on_close();
bg.animate([{ opacity: 1 }, { opacity: 0 }], { ...animate_opt, easing: "ease-in" }).finished.then(() => {
pop.close();
})
}

createEffect(() => {
if (!bg || !modal) {
return;
}
bg.animate([{ opacity: 0 }, { opacity: 1 }], { ...animate_opt, easing: "ease-out" });
modal.animate([{ top: "100vh", transform: "translate(-50%, 0)" }, { top: "50%", transform: "translate(-50%,-50%)" }], { ...animate_opt, easing: "ease-out" })
})

return (
<div class="absolute z-50 inset-0 overflow-hidden">
<div ref={bg} class="inset-0 absolute bg-[rgba(0,0,0,.3)]" onclick={
close
}>

</div>

<div ref={modal} class="border absolute left-1/2 rounded-md bg-white p-3 ">
<div class="flex justify-between items-center gap-4">
<span>{title}</span> <AiOutlineClose onclick={close} size="1.25em" class="cursor-pointer" />

</div>
<div class="mt-2">{children()}</div>
</div>
</div>
)
})
return { close };
}
export default function Modal(title: string, on_close: () => void, children: () => JSXElement) {
let close = () => { };
const pop = popup((
) => {
let bg: HTMLDivElement;
let modal: HTMLDivElement;
const animate_opt: KeyframeAnimationOptions = {
duration: 200,
fill: "forwards"
};
close = () => {
if (!bg || !modal) {
pop.close();
on_close();
return;
}

modal.animate([{ top: "50%", transform: "translate(-50%,-50%)" }, { top: "100vh", transform: "translate(-50%, 0)" }], { ...animate_opt, easing: "ease-in" })
on_close();
bg.animate([{ opacity: 1 }, { opacity: 0 }], { ...animate_opt, easing: "ease-in" }).finished.then(() => {
pop.close();
})
}

createEffect(() => {
if (!bg || !modal) {
return;
}
bg.animate([{ opacity: 0 }, { opacity: 1 }], { ...animate_opt, easing: "ease-out" });
modal.animate([{ top: "100vh", transform: "translate(-50%, 0)" }, { top: "50%", transform: "translate(-50%,-50%)" }], { ...animate_opt, easing: "ease-out" })
})

return (
<div class="absolute z-50 inset-0 overflow-hidden">
<div ref={bg} class="inset-0 absolute bg-[rgba(0,0,0,.3)]" onclick={
close
}>

</div>

<div ref={modal} class="border absolute left-1/2 rounded-md bg-white p-3 ">
<div class="flex justify-between items-center gap-4">
<span>{title}</span> <AiOutlineClose onclick={close} size="1.25em" class="cursor-pointer" />

</div>
<div class="mt-2">{children()}</div>
</div>
</div>
)
})
return { close };
}
and then i use that modal to create a prompt for inputting name
export default function save_quizz_form(): Promise<string> {
return new Promise((resolve, reject) => {
const modal = Modal("Save Quizz", () => { reject("Aborted.") }, () => {
let name = "";
return (
<form class="flex items-center gap-2" onsubmit={(e) => {
e.preventDefault();
e.stopImmediatePropagation();
resolve(name);
modal.close()
}}>
<input type="text" onchange={(e) => name = e.target.value} placeholder="Template name" class=" px-2 outline-none border rounded-md" />
<button type="submit"><AiOutlineSave size="1.5em" color="black" /></button>
</form>
)
})
});
}
export default function save_quizz_form(): Promise<string> {
return new Promise((resolve, reject) => {
const modal = Modal("Save Quizz", () => { reject("Aborted.") }, () => {
let name = "";
return (
<form class="flex items-center gap-2" onsubmit={(e) => {
e.preventDefault();
e.stopImmediatePropagation();
resolve(name);
modal.close()
}}>
<input type="text" onchange={(e) => name = e.target.value} placeholder="Template name" class=" px-2 outline-none border rounded-md" />
<button type="submit"><AiOutlineSave size="1.5em" color="black" /></button>
</form>
)
})
});
}
so now i can just call save_quizz_form().then(name => savesomewhere(name)).catch((err) => somerrorhandling) which is alot more make sense problem is solid-transition-group dont work, as you can see im animatiing it manually
Madaxen86
Madaxen862mo ago
You can have a look at kobalte.dev toast component and either use it or look at the implementation: https://kobalte.dev/docs/core/components/toast#toast
Want results from more Discord servers?
Add your server