From b9f9c58d7df73a3a4c2c1aaec0b119c85f53345c Mon Sep 17 00:00:00 2001 From: j4rviscmd Date: Sat, 28 Mar 2026 21:56:13 +0900 Subject: [PATCH] feat: wrap formatting toolbar settings section in collapsible accordion The Formatting Toolbar section in the settings dialog was vertically long due to 13 sortable items. Wrap it with a shadcn Accordion (defaults to collapsed) to keep the settings dialog compact. Co-Authored-By: Claude Opus 4.6 --- src/components/ui/accordion.tsx | 114 +++++++++++++++++++++ src/features/settings/ui/ToolbarOption.tsx | 85 +++++++-------- 2 files changed, 159 insertions(+), 40 deletions(-) create mode 100644 src/components/ui/accordion.tsx diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 0000000..7924623 --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,114 @@ +import { Accordion as AccordionPrimitive } from '@base-ui/react/accordion' +import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react' +import { cn } from '@/lib/utils' + +/** + * Root accordion container that manages the expanded/collapsed state of its items. + * + * Wraps the Base UI `Accordion.Root` primitive with a flex-column layout and + * forwards all native props. + * + * @param props - Props passed through to `AccordionPrimitive.Root`. + * @param props.className - Additional CSS classes merged via `cn`. + */ +function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) { + return ( + + ) +} + +/** + * A single collapsible section within an `Accordion`. + * + * Renders a bottom border between sibling items (except the last one). + * + * @param props - Props passed through to `AccordionPrimitive.Item`. + * @param props.className - Additional CSS classes merged via `cn`. + */ +function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) { + return ( + + ) +} + +/** + * Clickable header that toggles the visibility of an `AccordionContent` panel. + * + * Displays a chevron icon that rotates based on the expanded state, and + * supports hover, focus-visible, and disabled visual states. + * + * @param props - Props passed through to `AccordionPrimitive.Trigger`. + * @param props.className - Additional CSS classes merged via `cn`. + * @param props.children - Label content rendered inside the trigger button. + */ +function AccordionTrigger({ + className, + children, + ...props +}: AccordionPrimitive.Trigger.Props) { + return ( + + + {children} + + + + + ) +} + +/** + * Collapsible panel that reveals the content of an `AccordionItem`. + * + * Animates open/close with height-based transitions and renders children + * inside an inner container with consistent spacing. + * + * @param props - Props passed through to `AccordionPrimitive.Panel`. + * @param props.className - Additional CSS classes merged via `cn`. + * @param props.children - Content displayed when the accordion item is expanded. + */ +function AccordionContent({ + className, + children, + ...props +}: AccordionPrimitive.Panel.Props) { + return ( + +
+ {children} +
+
+ ) +} + +export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } diff --git a/src/features/settings/ui/ToolbarOption.tsx b/src/features/settings/ui/ToolbarOption.tsx index 71a8649..35e6ccd 100644 --- a/src/features/settings/ui/ToolbarOption.tsx +++ b/src/features/settings/ui/ToolbarOption.tsx @@ -38,6 +38,12 @@ import { RiUnderline, } from 'react-icons/ri' import { useToolbarConfig } from '@/app/providers/toolbar-config-provider' +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion' import { Button } from '@/components/ui/button' import { Label } from '@/components/ui/label' import { Switch } from '@/components/ui/switch' @@ -98,15 +104,10 @@ function SortableToolbarItem({ isDragging, } = useSortable({ id: item.key }) - const style = { - transform: CSS.Transform.toString(transform), - transition, - } - return (
-
-

+ + + Formatting Toolbar -

- {isCustomized && ( - +
+ )} + - - Reset - - )} -
- - i.key)} - strategy={verticalListSortingStrategy} - > - {items.map((item) => ( - - ))} - - - + i.key)} + strategy={verticalListSortingStrategy} + > + {items.map((item) => ( + + ))} + + + + + ) }