-
Notifications
You must be signed in to change notification settings - Fork 877
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
[Accordion][Collapsible][Tabs][…] Consider adding an option to control how Content
is mounted/unmounted over time/activation cycle
#1155
Comments
We had a previous discussion about all this for reference #855 Notably a "lazy" state which could be useful where the tab contents start unmounted but remain mounted when opened and then closed #855 (reply in thread) |
Content
is mounted/unmounted over time/activation cycle
Hi, just looping back on this. I saw you mentioned about the previous discussion as reference, but is there a conclusion over this request? Seems that both behavior are useful, so I can think of this as a prop like Asking about this cause we are using this tabs and we have some text edit boxes implemented on our site that are placed over the tabs content that needs to retain its state if the other tab content is active, like when you are writing a big amount of text and it gets lost because of changing the tab. Happy to contribute to add this change if needed :) Thanks in advance! |
Based on @jjenzz 's solution from #855 (reply in thread) , const [tab, setActiveTab] = useState("0");
<Accordion value={tab} onValueChange={setActiveTab}>
// ...
<Accordion.Content forceMount={true} hidden={activeTab !== tab} />
// ...
</Accordion> The problem is that because now the visibility is controlled by |
Content
is mounted/unmounted over time/activation cycleContent
is mounted/unmounted over time/activation cycle
For those that use |
I personally don't have this issue. Animations would be nice though |
You could also use the state data attribute as the conditional. I personally find this a bit cleaner than having useState. <Tabs.Content
value='tab-1'
forceMount
className='data-[state=inactive]:hidden'
>
{children}
</Tabs.Content> |
It would be great to have an option for preserving content on Accordion Content hide
How do you use this? Super new to Radix, and would also like to preserve content when the accordion is collapsed without having to useState. Thank you! |
For Accordion in tailwind it should be |
How can this work with Navigation Menu (and also maintain the animations)? Lets say I have a basic menu with two Content components <NavigationMenuPrimitive.List>
<NavigationMenuPrimitive.Item>
<NavigationMenuPrimitive.Trigger />
<NavigationMenuPrimitive.Content forceMount className="data-[state=closed]:hidden">
{/* Menu 1 */}
</NavigationMenuPrimitive.Content>
</NavigationMenuPrimitive.Item>
<NavigationMenuPrimitive.Item>
<NavigationMenuPrimitive.Trigger />
<NavigationMenuPrimitive.Content forceMount className="data-[state=closed]:hidden">
{/* Menu 2 */}
</NavigationMenuPrimitive.Content>
</NavigationMenuPrimitive.Item>
</NavigationMenuPrimitive.List> The first problem is both menus are visible in the DOM when the menu is closed Edit: for anyone looking for a cheap hack so links are in the page during SSR... here's a cheap hack, I'm not saying it's great Force mount the content and keep it hidden until the user interacts w/ the menu for the first time, then revert to default behavior (forceMount off, remove const [forceMount, setForceMount] = React.useState(true);
return (
<NavigationMenuPrimitive.Root onValueChange={() => setForceMount(false)}>
<NavigationMenuPrimitive.List>
<NavigationMenuPrimitive.Item>
<NavigationMenuPrimitive.Trigger />
<NavigationMenuPrimitive.Content {...(forceMount && { forceMount })} className={cn(forceMount && "hidden")}>
{/* Menu 1 */}
</NavigationMenuPrimitive.Content>
</NavigationMenuPrimitive.Item>
<NavigationMenuPrimitive.Item>
<NavigationMenuPrimitive.Trigger />
<NavigationMenuPrimitive.Content {...(forceMount && { forceMount })} className={cn(forceMount && "hidden")}>
{/* Menu 2 */}
</NavigationMenuPrimitive.Content>
</NavigationMenuPrimitive.Item>
</NavigationMenuPrimitive.List>
</NavigationMenuPrimitive.Root>
); |
Simple solution, don't use React. |
thats why i choose react |
With Radix already calculates content height and stores it in .accordion--content {
height: 0;
&[data-state="open"] {
height: auto;
}
} We need to store the expanded height of the content to a React state variable. useEffect(() => {
if (!ref.current) {
return;
}
const currentHeight = ref.current.clientHeight;
const height = Math.max(collapsedHeight, currentHeight);
setCollapsedHeight(height);
}, [collapsedHeight, ref]); Now, we can restore the lost <RadixAccordion.Content
className={ACCORDION_CLASSES.content}
forceMount
style={{
"--radix-collapsible-content-height": `${collapsibleHeight}px`,
}}
>
{content}
</RadixAccordion.Content> |
const MegaMenu = () => {
const [initialMount, setInitialMount] = useState<true | undefined>(true);
useEffect(() => {
setInitialMount(undefined);
}, []);
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem className="[&>div]:hidden">
<NavigationMenuTrigger>Getting started</NavigationMenuTrigger>
<NavigationMenuContent forceMount={initialMount}>
{/* Menu 1 */}
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem className="[&>div]:hidden">
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
<NavigationMenuContent forceMount={initialMount}>
{/* Menu 2 */}
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
);
}; A possible workaround is to force the component to mount on first render and remove the property after the page loads. On my project this practice works and the content of the two menus is well rendered in the first html !
If we only use the CSS class and force the menus to be mounted at any time, the content is displayed in the first HTML rendering but all the menus opens when hovering over a single menu Tell me if you have another idea |
Is there any option to have this functionality for Dialog? We're currently using Vaul which is a wrapper over Radix Dialog and we would like to have this functionality to avoid recreating the DOM each time the drawer is open |
Looking for an answer to this question too |
I found a way to do it with pure css for the accordions when forceMount is true, I'm using Shadcn but i think the code should also work for normal radix .accordion {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
transition: grid-template-rows 200ms ease !important;
animation: unset;
}
.accordion[data-state="open"] {
grid-template-rows: 1fr;
}
.accordion > div {
min-height: 0px;
} explanation:
|
Are you putting this on the entire accordion ,or accordionItem, or accordionContent? I'm having the same issues, and I can't use a useEffect /listener as others have suggested, since i need this component to be server rendered in next. |
your demo link is broken and i would love to see an example :) ive been hard stuck on this for a while |
any updates? |
Any updates on how to keep the For When using A workaround is to use
As answered in #2476: (and also being closed)
The oldest post I found is probably this one from 2021 #998, but it was closed again. |
This is causing issues for my application as well. We were hoping to use the accordion to house search filters but because the contents are unmounted, any form elements within the accordion disappear and aren't applied on a search. It would be great if there was a solution to this so just wanted to add my own scenario and how this is currently causing issues. Happy to answer and further questions that might pop up. |
In case this helps someone, I managed a pretty gross hack but it works for my usecase: I wanted forceMount on all the time so as to not re fetch a bunch of images and state etc each time a nav item was triggered. Its hacky but works:
Note: the key in my case was the opacity-0 by default because they are all stacked on each other. You could also use visibility hidden / visible. |
My design dictates that I use Accordion on smaller screens and a 3-column grid on larger screens. I thought I could just manipulate the CSS for larger screens, but then I discovered that the content isn't mounted and thus there is nothing to "show" until a trigger mounts the content. This is also bad for SEO, no? By using |
OK, so I was able to achieve my design by using the (Ignore my primitive aliases...I'm using Astro)
¯\(ツ)/¯ |
Feature request
Overview
For the tabs I would like an option to keep the inactive tab contents around in the DOM. For components with a heavy mounting cost, or components with internal state, the current solution is suboptimal. Instead of unmounting, tabs could be set to
aria-hidden
.Examples in other libraries
Reach works like this: https://reach.tech/tabs/
Who does this impact? Who is this for?
Anyone using the tabs for components with internal state.
Additional context
n/a
Edit (April 5th 2022): People tend to ask for one or the other, so the gist is for us to see if we can provide an option to cater for both.
The text was updated successfully, but these errors were encountered: