我刚刚开始使用Headless UI。我试图使用Headless UI的Disclosure组件来呈现我的工作经验。基本上,我需要&;n&;披露的数量将动态呈现,每当一个披露被打开时,其他的应该关闭。
我能够动态地呈现披露,并且它们都有各自的状态。(开启/关闭一个披露不影响另一个披露)。我想做的就是每次只公开一个信息。开启另一个揭露应该关闭所有剩余的揭露。我已经浏览了他们的文档,但找不到一种方法来同时管理多个Disclosure状态。
下面是我的代码:import React, { useContext } from "react";
import { GlobalContext } from "../data/GlobalContext";
import { Tab, Disclosure } from "@headlessui/react";
import ReactMarkdown from "react-markdown";
const Experience = () => {
const { data } = useContext(GlobalContext);
const expData = data.pageContent.find(
(content) => content.__component === "page-content.experience-page-content"
);
return (
<div className="container h-screen">
<div className="flex h-full flex-col items-center justify-center">
<h3 className="">{expData.pageTitle}</h3>
<div className="flex min-h-[600px] flex-col">
{expData.jobs.map((job, i) => (
<Disclosure key={job.companyName} defaultOpen={i === 0}>
<Disclosure.Button
key={job.companyName + "_tab"}
className="px-4 py-3 dark:text-dark-primary"
>
{job.companyName}
</Disclosure.Button>
<Disclosure.Panel key={job.companyName + "_panel"}>
<p className="">
<span className="">{job.designation}</span>
<span className="">{" @ "}</span>
<span className="">{job.companyName}</span>
</p>
<p className="">{job.range}</p>
<ReactMarkdown className="">
{job.workDescription}
</ReactMarkdown>
</Disclosure.Panel>
</Disclosure>
))}
</div>
</div>
</div>
);
};
export default Experience;
如果有人能帮我做这件事就太有帮助了。谢谢。<template>
<div class="mx-auto w-full max-w-md space-y-3 rounded-2xl bg-white p-2">
<Disclosure v-for="(i, idx) in 3" :key="i" v-slot="{ open, close }">
<DisclosureButton
:ref="el => (disclosure[idx] = close)"
class="flex w-full justify-between rounded-lg bg-purple-100 px-4 py-2 text-left text-sm font-medium text-purple-900 hover:bg-purple-200 focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75"
@click="hideOther(idx)"
>
<span> What is your refund policy? {{ open }} </span>
</DisclosureButton>
<DisclosurePanel class="px-4 pt-4 pb-2 text-sm text-gray-500">
If you're unhappy with your purchase for any reason, email us within 90 days and we'll
refund you in full, no questions asked.
</DisclosurePanel>
</Disclosure>
</div>
</template>
<script setup>
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
const disclosure = ref([])
const hideOther = id => {
disclosure.value.filter((d, i) => i !== id).forEach(c => c())
}
</script>
这里是我在Vue中做的。
好吧,我从各种来源偷了东西,并设法破解了它。我还没有测试它的可访问性,但它有一些有趣的事情,因为它偏离了一点(如果你问我,相当有用)从React
心理模型。
tldr
是你需要通过ref.current?.click()
触发其他元素的点击。以下是步骤:1)创建ref:
这里我们不能使用钩子,因为你不能在循环或条件中调用钩子,我们使用React.createRef<HTMLButtonElement>()
代替
const refs = React.useMemo(() => {
return (
items.map(() => {
return React.createRef<HTMLButtonElement>();
}) ?? []
);
}, [items]);
2)将相应的ref
添加到Disclosure.Button
组件
{items.map((item, idx) => (
<Disclosure key={item.id}>
{({open}) => (
<>
{/* other relevant stuff */}
<Disclosure.Button ref={refs[idx]}>
Button
</Disclosure.Button>
<Disclosure.Panel>
{/* more stuff */}
</Disclosure.Panel>
</>
)}
</Disclosure>)
)}
3)使用数据属性(为了方便你自己)
这个在下一步中会特别有用
{items.map((item, idx) => (
<Disclosure key={item.id}>
{({open}) => (
<>
{/* other relevant stuff */}
<Disclosure.Button
ref={refs[idx]}
data-id={item.id}
data-open={open}
>
Button
</Disclosure.Button>
<Disclosure.Panel>
{/* more stuff */}
</Disclosure.Panel>
</>
)}
</Disclosure>)
)}
4)定义handleClosingOthers
函数(一个onClick处理程序)
基本上这里我们得到了所有不是用户点击的按钮,验证它们是否打开如果它们是,通过编程点击它们来关闭它们
function handleClosingOthers(id: string) {
const otherRefs = refs.filter((ref) => {
return ref.current?.getAttribute("data-id") !== id;
});
otherRefs.forEach((ref) => {
const isOpen = ref.current?.getAttribute("data-open") === "true";
if (isOpen) {
ref.current?.click();
}
});
}
5)最后我们将该函数添加到onClick
处理程序
{items.map((item, idx) => (
<Disclosure key={item.id}>
{({open}) => (
<>
{/* other relevant stuff */}
<Disclosure.Button
ref={refs[idx]}
data-id={item.id}
data-open={open}
onClick={() => handleClosingOthers(item.id)}
>
Button
</Disclosure.Button>
<Disclosure.Panel>
{/* more stuff */}
</Disclosure.Panel>
</>
)}
</Disclosure>)
)}
我有同样的问题,并通过使用Disclosure内部渲染道具和一些外部状态来解决。下面的代码使每个披露在打开(如果有的话)时切换自己并关闭其他披露。
export default function MyAccordion({ data }) {
const [activeDisclosurePanel, setActiveDisclosurePanel] = useState(null);
function togglePanels(newPanel) {
if (activeDisclosurePanel) {
if (activeDisclosurePanel.key !== newPanel.key && activeDisclosurePanel.open) {
activeDisclosurePanel.close();
}
}
setActiveDisclosurePanel({
...newPanel,
open: !newPanel.open
});
}
return (
<ul>
{
data.map((item, index) => (
<Disclosure as="li" key={ index }>
{
(panel) => {
const { open, close } = panel
return (<>
<Disclosure.Button onClick={ () => {
if (!open) {
// On the first click, the panel is opened but the "open" prop's value is still false. Therefore the falsey verification
// This will make so the panel close itself when we click it while open
close();
}
// Now we call the function to close the other opened panels (if any)
togglePanels({ ...panel, key: index });
}}>
</Disclosure.Button>
<Disclosure.Panel>
{ item }
</Disclosure.Panel>
</>)
}
}
</Disclosure>
))
}
</ul>
);
}
我使用了这种方法:
function Akkordion({ items }) {
const buttonRefs = useRef([]);
const openedRef = useRef(null);
const clickRecent = (index) => {
const clickedButton = buttonRefs.current[index];
if (clickedButton === openedRef.current) {
openedRef.current = null;
return;
}
if (Boolean(openedRef.current?.getAttribute("data-value"))) {
openedRef.current?.click();
}
openedRef.current = clickedButton;
};
return (
<div>
{items.map((item, idx) => (
<Disclosure key={item.id}>
{({ open }) => (
<div>
<Disclosure.Button as="div">
<button
data-value={open}
ref={(ref) => {
buttonRefs.current[idx] = ref;
}}
onClick={() => clickRecent(idx)}
>
{item.label}
</button>
</Disclosure.Button>
<Disclosure.Panel
>
{item.content}
</Disclosure.Panel>
</div>
)}
</Disclosure>
))}
</div>
);
}