Accordion
sora-ui/accordionFreeComponent
An animated FAQ accordion with expandable panels, powered by Motion.
Install it
npx shadcn@latest add https://ui.soralabs.io.vn/r/accordion.jsonSource
1"use client";23import { cn } from "@/lib/utils";4import { cva, type VariantProps } from "class-variance-authority";5import {6 type AnimationSequence,7 stagger,8 useAnimate,9 useReducedMotion,10} from "motion/react";11import {12 type ComponentPropsWithoutRef,13 createContext,14 type ReactNode,15 type RefObject,16 useCallback,17 useContext,18 useEffect,19 useId,20 useRef,21 useState,22} from "react";2324import { useAutoHeight } from "@/hooks/use-auto-height";2526const EXPO_IN_OUT = [0.87, 0, 0.13, 1] as const;27const EXPO_OUT = [0.16, 1, 0.3, 1] as const;28const POWER2_IN_OUT = [0.45, 0, 0.55, 1] as const;2930type IconMode = "rotate" | "fade" | "both";31type DisabledBreakpoint = "mobile" | "tablet" | "desktop";32type AccordionEase = readonly [number, number, number, number];3334const DISABLE_MEDIA: Record<DisabledBreakpoint, string> = {35 mobile: "(max-width: 479px)",36 tablet: "(max-width: 991px)",37 desktop: "(min-width: 992px)",38};3940interface AccordionContextValue {41 allowMultiple: boolean;42 animationsPaused: boolean;43 defaultDuration: number;44 defaultEase: AccordionEase;45 defaultIconRotation: number;46 disabledOn?: DisabledBreakpoint[];47 iconMode: IconMode;48 notifyItemOpen: (id: string) => void;49 registerItem: (id: string, close: () => void) => void;50 unregisterItem: (id: string) => void;51}5253interface AccordionItemContextValue {54 contentId: string;55 contentPanelRef: RefObject<HTMLElement | null>;56 iconRef: RefObject<SVGSVGElement | null>;57 isDisabled: boolean;58 isOpen: boolean;59 setPanelHeight: (height: number) => void;60 toggle: () => void;61 triggerId: string;62 verticalBarRef: RefObject<SVGPathElement | null>;63}6465const AccordionContext = createContext<AccordionContextValue | null>(null);66const AccordionItemContext = createContext<AccordionItemContextValue | null>(67 null68);6970function useAccordionContext() {71 const context = useContext(AccordionContext);72 if (!context) {73 throw new Error("Accordion components must be used within <Accordion>.");74 }75 return context;76}7778function useAccordionItemContext() {79 const context = useContext(AccordionItemContext);80 if (!context) {81 throw new Error(82 "Accordion subcomponents must be used within <AccordionItem>."83 );84 }85 return context;86}8788function useDisabledOn(disabledOn?: DisabledBreakpoint[]) {89 const [disabled, setDisabled] = useState(false);9091 useEffect(() => {92 if (!disabledOn?.length) {93 setDisabled(false);94 return;95 }9697// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from Sora UI
All 97 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Border Trailborder-trail | Sora UI | Components | Free | 1 dep | |
| Bottom Sheetbottom-sheet | Sora UI | Components | Free | 2 deps | |
| Cursor Bubblecursor-bubble | Sora UI | Components | Free | 2 deps | |
| Cursor Trail Revealcursor-trail-reveal | Sora UI | Components | Free | 1 dep | |
| Custom Cursorcustom-cursor | Sora UI | Components | Free | 2 deps | |
| Accordion Demodemo-accordion | Sora UI | Components | Free | no deps | |
| Border Trail Demodemo-border-trail | Sora UI | Components | Free | no deps | |
| Border Trail Loading Card Demodemo-border-trail-loading | Sora UI | Components | Free | no deps |
