-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Portal Support #8
Comments
Yeah that's tricky. Since the element returned from the portal isn't actually the inserted element, which is actually being inserted elsewhere. The approach here will never work like that. You're better off putting the transition and show logic in the portal. <Portal>
<Transition name="dialog">
{show() && <Dialog />}
</Transition>
</Portal> |
@ryansolid It works. But it’s not very perfect. For example, I have 100 tooltip components on my page, because the Portal always rendered whether it shows up or not. so it will creates 100 empty div elements in Portal mount container. |
May I suggest a feature that introduces the ability to pass a ref call-back as a child? It should solve similar issues with deeply nested elements. <Transition name="dialog">
{ref => show() && <Portal><Dialog ref={ref} /></Portal>}
</Transition> This API might introduce new mount/unmount edge cases to consider but might be worth it? |
A solution for transitioning portaled elements in and out without having to mount portal elements ahead of time would be great. I've been attempting to implement something similar to #8 (comment) but I'm rather new to Solid and I'm hitting behavior that I don't quite understand. |
Yeah I don't think that would work with a ref. Transition inserts in that part of the tree not where the Portal is. |
I wound up with this wrapper around Code Snippetimport {
createEffect,
createSignal,
Show,
children,
type JSX,
type Setter,
} from 'solid-js';
function nextFrame(fn: () => unknown) {
requestAnimationFrame(() => {
requestAnimationFrame(fn);
});
}
export type Props = {
when: boolean;
children: (ref: Setter<HTMLElement>) => JSX.Element;
classEnterBase: string;
classEnterFrom: string;
classEnterTo: string;
classExitBase: string;
classExitFrom: string;
classExitTo: string;
};
export default function ShowTransition(props: Props) {
const [ref, setRef] = createSignal<HTMLElement | null>(null);
const [renderChildren, setRenderChildren] = createSignal(false);
const resolved = children(() => renderChildren() && props.children(setRef));
function doTransition(
el: HTMLElement,
entering: boolean,
fromClasses: Array<string>,
activeClasses: Array<string>,
toClasses: Array<string>,
) {
function endTransition(e?: Event) {
if (!e || e.target === el) {
el.removeEventListener('transitionend', endTransition);
el.removeEventListener('animationend', endTransition);
el.classList.remove(...activeClasses);
el.classList.remove(...toClasses);
}
setRenderChildren(entering);
if (!entering) {
setRef(null);
}
}
el.addEventListener('transitionend', endTransition);
el.addEventListener('animationend', endTransition);
el.classList.add(...fromClasses);
el.classList.add(...activeClasses);
nextFrame(() => {
el.classList.remove(...fromClasses);
el.classList.add(...toClasses);
});
}
createEffect(() => {
if (props.when && !ref()) {
setRenderChildren(true);
}
});
createEffect(() => {
const el = ref();
const when = props.when;
if (el) {
doTransition(
el,
when,
...(when
? ([
props.classEnterFrom.split(' '),
props.classEnterBase.split(' '),
props.classEnterTo.split(' '),
] as const)
: ([
props.classExitFrom.split(' '),
props.classExitBase.split(' '),
props.classExitTo.split(' '),
] as const)),
);
}
});
return <Show when={renderChildren()}>{resolved()}</Show>;
} |
I'm pretty sure that this is solvable in userland just by changing the approach. const content = children(() => (
<Transition>
<MyDialogComponent />
</Transition>
));
<Show when={content.toArray().length}>
<Portal>
{content()}
</Portal>
</Show> |
https://codesandbox.io/s/solid-transition-group-demo-yo0zv?file=/src/index.js
How to use this in Portal?
Transition's children is a Portal, not a HTMLElement, so Transition can not add classes to inner dialog element.
The text was updated successfully, but these errors were encountered: