Disclosure programmatically closing / opening #589
Replies: 12 comments 5 replies
-
try this
|
Beta Was this translation helpful? Give feedback.
-
It would be really helpful to be able to programmatically open and close, for example |
Beta Was this translation helpful? Give feedback.
-
Is this still not a thing? |
Beta Was this translation helpful? Give feedback.
-
Also ran into this. My use case is a tree view component where I have built the nodes using Everything works as expected on initial render, i.e. the first node opens, loads dynamic child nodes and then those open as well up to the correct node, based on the current URL. But when in a top node and going to a URL of a child node, the component itself knows that it should be active, and all the nodes between the top and active node know, but the As mentioned in #770, a workaround includes changing the |
Beta Was this translation helpful? Give feedback.
-
Adding a request for this feature here for accessibility purposes as well -- namely with print media. it would be useful to be able to bulk update state to open all before calling |
Beta Was this translation helpful? Give feedback.
-
how is this not a thing yet? :( |
Beta Was this translation helpful? Give feedback.
-
Need this. |
Beta Was this translation helpful? Give feedback.
-
At the moment (judging by the number and frequency of topics) this is one of the most demanded features, it is so limiting in the use of this component that it is easier to write your own than to try to invent a crutch that will fix the existing one :( |
Beta Was this translation helpful? Give feedback.
-
+1 on this. I've also created my FAQ section with disclosures and I'd like to see all the answers when I print the page, regardless of original user selected state. |
Beta Was this translation helpful? Give feedback.
-
if you guys want to use Disclosure fully programmatically,
this makes Whether the element should ignore the internally managed open/closed state.
take a look at Disclosure.Panel |
Beta Was this translation helpful? Give feedback.
-
A possible workaround for dynamically changing the open/close state of your Since the author @rafalolszewski94 of the discussion uses Vue, it's worth mentioning that we can force a re-render of a component by changing its
So if we programmatically mutate the value of Keep in mind this approach could be expensive if your |
Beta Was this translation helpful? Give feedback.
-
I was able to achive with import React from "react";
import { cn } from "../../utils/cn";
import { Disclosure, Transition } from "@headlessui/react";
import { Button } from "./Button";
interface AccordionItem {
title?: string;
titleClassName?: string;
content?: React.ReactNode;
contentClassName?: string;
isOpen?: boolean;
onToggle?: (isOpen: boolean) => void;
}
interface AccordionProps {
items?: AccordionItem[];
allowMultiple?: boolean;
defaultOpenIndices?: number[];
className?: string;
}
interface AccordionButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children?: React.ReactNode;
className?: string;
open?: boolean;
}
const AccordionButton = React.forwardRef<
HTMLButtonElement,
AccordionButtonProps
>(({ children, className, open, ...props }, ref) => {
return (
<button
ref={ref}
className={cn(
"flex w-full items-center justify-between rounded-none px-0 py-2 font-medium text-gray-800",
"focus:outline-none focus-visible:ring focus-visible:ring-blue-500 focus-visible:ring-opacity-50",
"border-b",
className,
)}
{...props}
>
{children}
<svg
className={`h-4 w-4 transition-transform duration-200 ${
open ? "rotate-180 transform" : ""
}`}
fill="none"
viewBox="0 0 25 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
);
});
AccordionButton.displayName = "AccordionButton";
const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItem>(
(
{
title,
titleClassName,
content,
contentClassName,
isOpen = false,
onToggle,
},
ref,
) => {
if (!isOpen) {
return (
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform opacity-0"
enterTo="transform opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform opacity-100"
leaveTo="transform opacity-0"
>
<AccordionButton
open={isOpen}
onClick={() => {
onToggle?.(!isOpen);
}}
>
<span>{title}</span>
</AccordionButton>
</Transition>
);
}
return (
<Disclosure ref={ref} as="div" className="accordion-item" defaultOpen>
{({ open }) => (
<>
<Disclosure.Button
as={AccordionButton}
open={open}
onClick={() => {
onToggle?.(!isOpen);
}}
>
<span>{title}</span>
</Disclosure.Button>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform opacity-0"
enterTo="transform opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform opacity-100"
leaveTo="transform opacity-0"
>
<Disclosure.Panel
className={cn(
"py-3 transition-all duration-200",
contentClassName,
)}
>
{content}
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
);
},
);
AccordionItem.displayName = "AccordionItem";
const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
(
{ items, allowMultiple = false, className, defaultOpenIndices = [] },
ref,
) => {
const [openIndices, setOpenIndices] =
React.useState<number[]>(defaultOpenIndices);
const toggleAccordion = React.useCallback(
(isOpen: boolean, index: number) => {
if (allowMultiple) {
setOpenIndices((prev) =>
isOpen ? prev.filter((i) => i !== index) : [...prev, index],
);
} else {
setOpenIndices(isOpen ? [index] : []);
}
},
[allowMultiple],
);
return (
<div className={cn("mx-auto w-full", className)}>
{items?.map((item, index) => {
return (
<AccordionItem
key={index}
{...item}
onToggle={(isOpen) => toggleAccordion(isOpen, index)}
isOpen={openIndices.includes(index)}
/>
);
})}
</div>
);
},
);
Accordion.displayName = "Accordion";
export { Accordion };
export type { AccordionProps, AccordionItem }; Use Case example ...
<Accordion
className="..."
items={
[
{
title: "Item 1",
content: <>Test 1</>,
},
{
title: "Item 2",
content: <>Test 2</>,
},
]}
/>
... |
Beta Was this translation helpful? Give feedback.
-
Hi, I'm trying to do F.A.Q. section with a toggle-all button (as shown below)
I've tried it with
:default-open="show"
but doesn't seem to work. Is it possible to achieve it with current components?Using
@headlessui/vue
v1.2.0
Beta Was this translation helpful? Give feedback.
All reactions