Accordion
Accordion in the Migrating from shadcn/ui and Radix guide.
src/ui/accordion.tsxtsx
import { Accordion as AccordionPrimitive, mergeElementProps } from "@shadow-js/primitives";
import { splitProps } from "@shadow-js/core";
import type { JSX } from "@shadow-js/web";
function ChevronDownIcon(props: { class?: string }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={props.class} aria-hidden="true">
<path d="m6 9 6 6 6-6" />
</svg>
);
}
export const Accordion = AccordionPrimitive;
export function AccordionItem(props: JSX.IntrinsicElements["div"] & { value: string }) {
return <AccordionPrimitive.Item {...mergeElementProps({ class: "border-b" }, props)} value={props.value} />;
}
export function AccordionTrigger(props: JSX.IntrinsicElements["button"]) {
const [own, rest] = splitProps(props, ["children"]);
return (
<AccordionPrimitive.Header class="flex">
<AccordionPrimitive.Trigger
{...mergeElementProps(
{
class:
"flex flex-1 items-center justify-between py-4 text-left text-sm font-medium transition-all hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
},
rest,
)}
>
{own.children}
<ChevronDownIcon class="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
);
}
export function AccordionContent(props: JSX.IntrinsicElements["div"]) {
const [own, rest] = splitProps(props, ["children"]);
return (
<AccordionPrimitive.Content
{...mergeElementProps(
{ class: "overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down" },
rest,
)}
>
<div class="pb-4 pt-0">{own.children}</div>
</AccordionPrimitive.Content>
);
}