[web] interviews/sidebar: allow single and multiple types
This commit is contained in:
parent
597ce3711f
commit
bf59fe68ee
|
|
@ -83,5 +83,5 @@ function useBlogSidebarNavigation() {
|
|||
export default function BlogSidebar() {
|
||||
const navigation = useBlogSidebarNavigation();
|
||||
|
||||
return <SidebarLinksSection items={navigation} size="md" />;
|
||||
return <SidebarLinksSection items={navigation} size="md" type="single" />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,14 +189,14 @@ export function SidebarExpanded({
|
|||
<div className="h-0 grow overflow-auto">
|
||||
<ScrollArea>
|
||||
{renderTopAddonElements?.(fadeInClass)}
|
||||
<SidebarLinksSection items={startItems} size="md" />
|
||||
<SidebarLinksSection items={startItems} size="md" type="single" />
|
||||
</ScrollArea>
|
||||
</div>
|
||||
<div className={clsx('flex flex-col gap-y-4', fadeInClass)}>
|
||||
{endItems.length > 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<SidebarLinksSection items={endItems} size="md" />
|
||||
<SidebarLinksSection items={endItems} size="md" type="single" />
|
||||
</>
|
||||
)}
|
||||
{renderBottomAddonElements?.(fadeInClass)}
|
||||
|
|
|
|||
|
|
@ -209,13 +209,25 @@ function SidebarLinks({
|
|||
export default function SidebarLinksSection({
|
||||
items,
|
||||
size,
|
||||
type,
|
||||
}: Readonly<{
|
||||
items: ReadonlyArray<SidebarLinkEntity>;
|
||||
size: SidebarSize;
|
||||
type: React.ComponentProps<typeof AccordionPrimitive.Root>['type'];
|
||||
}>) {
|
||||
const { pathname } = useI18nPathname();
|
||||
|
||||
// `single` type
|
||||
const [openSection, setOpenSection] = useState<string | null>(null);
|
||||
|
||||
// `multiple` type
|
||||
const [manuallyOpenSections, setManuallyOpenSections] = useState<
|
||||
ReadonlyArray<string>
|
||||
>([]);
|
||||
const [automaticOpenSections, setAutomaticOpenSections] = useState<
|
||||
ReadonlyArray<string>
|
||||
>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const activeValue = (() => {
|
||||
for (const item of items) {
|
||||
|
|
@ -230,29 +242,70 @@ export default function SidebarLinksSection({
|
|||
})();
|
||||
|
||||
if (activeValue) {
|
||||
setOpenSection(activeValue);
|
||||
if (type === 'single') {
|
||||
setOpenSection(activeValue);
|
||||
} else {
|
||||
setAutomaticOpenSections([activeValue]);
|
||||
}
|
||||
}
|
||||
}, [items, pathname]);
|
||||
}, [items, pathname, type]);
|
||||
|
||||
const className = clsx('flex flex-col gap-y-2');
|
||||
const contents = (
|
||||
<ul>
|
||||
{items.map((item) => (
|
||||
<SidebarLinks
|
||||
key={item.label}
|
||||
item={item}
|
||||
size={size}
|
||||
onToggle={() => {
|
||||
if (type === 'single') {
|
||||
setOpenSection(openSection === item.label ? null : item.label);
|
||||
} else {
|
||||
const inAutomaticOpen = automaticOpenSections.includes(
|
||||
item.label,
|
||||
);
|
||||
|
||||
if (inAutomaticOpen) {
|
||||
setAutomaticOpenSections(
|
||||
automaticOpenSections.filter((label) => label !== item.label),
|
||||
);
|
||||
}
|
||||
|
||||
if (manuallyOpenSections.includes(item.label)) {
|
||||
setManuallyOpenSections(
|
||||
manuallyOpenSections.filter((label) => label !== item.label),
|
||||
);
|
||||
} else if (!inAutomaticOpen) {
|
||||
setManuallyOpenSections([...manuallyOpenSections, item.label]);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
||||
if (type === 'single') {
|
||||
return (
|
||||
<AccordionPrimitive.Root
|
||||
asChild={true}
|
||||
className={className}
|
||||
type="single"
|
||||
// Fake null value to force everything to close.
|
||||
value={openSection ?? '__FAKE_NULL_VALUE__'}>
|
||||
{contents}
|
||||
</AccordionPrimitive.Root>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AccordionPrimitive.Root
|
||||
asChild={true}
|
||||
className={clsx('flex flex-col gap-y-2')}
|
||||
type="single"
|
||||
// Fake null value to force everything to close.
|
||||
value={openSection ?? '__FAKE_NULL_VALUE__'}>
|
||||
<ul>
|
||||
{items.map((item) => (
|
||||
<SidebarLinks
|
||||
key={item.label}
|
||||
item={item}
|
||||
size={size}
|
||||
onToggle={() => {
|
||||
setOpenSection(openSection === item.label ? null : item.label);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
className={className}
|
||||
type="multiple"
|
||||
value={[...automaticOpenSections, ...manuallyOpenSections]}>
|
||||
{contents}
|
||||
</AccordionPrimitive.Root>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,11 @@ export function GuidesSidebar({
|
|||
isSidebar && 'vignette-scroll',
|
||||
)}>
|
||||
<ScrollArea viewportClass={clsx('p-4')}>
|
||||
<SidebarLinksSection items={navigation.items} size="sm" />
|
||||
<SidebarLinksSection
|
||||
items={navigation.items}
|
||||
size="sm"
|
||||
type="multiple"
|
||||
/>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
Loading…
Reference in New Issue