mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 14:26:36 +08:00
111 lines
3.1 KiB
TypeScript
111 lines
3.1 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { Collapsible as CollapsiblePrimitive } from 'radix-ui';
|
|
import { AnimatePresence, type HTMLMotionProps, motion } from 'motion/react';
|
|
|
|
import { getStrictContext } from '@/lib/utils/get-strict-context';
|
|
import { useControlledState } from '@/hooks/use-controlled-state';
|
|
|
|
type CollapsibleContextType = {
|
|
isOpen: boolean;
|
|
setIsOpen: (isOpen: boolean) => void;
|
|
};
|
|
|
|
const [CollapsibleProvider, useCollapsible] =
|
|
getStrictContext<CollapsibleContextType>('CollapsibleContext');
|
|
|
|
type CollapsibleProps = React.ComponentProps<typeof CollapsiblePrimitive.Root>;
|
|
|
|
function Collapsible(props: CollapsibleProps) {
|
|
const [isOpen, setIsOpen] = useControlledState({
|
|
value: props?.open,
|
|
defaultValue: props?.defaultOpen,
|
|
onChange: props?.onOpenChange,
|
|
});
|
|
|
|
return (
|
|
<CollapsibleProvider value={{ isOpen, setIsOpen }}>
|
|
<CollapsiblePrimitive.Root
|
|
data-slot='collapsible'
|
|
{...props}
|
|
onOpenChange={setIsOpen}
|
|
/>
|
|
</CollapsibleProvider>
|
|
);
|
|
}
|
|
|
|
type CollapsibleTriggerProps = React.ComponentProps<
|
|
typeof CollapsiblePrimitive.Trigger
|
|
>;
|
|
|
|
function CollapsibleTrigger(props: CollapsibleTriggerProps) {
|
|
return (
|
|
<CollapsiblePrimitive.Trigger data-slot='collapsible-trigger' {...props} />
|
|
);
|
|
}
|
|
|
|
type CollapsibleContentProps = Omit<
|
|
React.ComponentProps<typeof CollapsiblePrimitive.Content>,
|
|
'asChild' | 'forceMount'
|
|
> &
|
|
HTMLMotionProps<'div'> & {
|
|
keepRendered?: boolean;
|
|
};
|
|
|
|
function CollapsibleContent({
|
|
keepRendered = false,
|
|
transition = { duration: 0.35, ease: 'easeInOut' },
|
|
...props
|
|
}: CollapsibleContentProps) {
|
|
const { isOpen } = useCollapsible();
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{keepRendered ? (
|
|
<CollapsiblePrimitive.Content asChild forceMount>
|
|
<motion.div
|
|
key='collapsible-content'
|
|
data-slot='collapsible-content'
|
|
layout
|
|
initial={{ opacity: 0, height: 0, overflow: 'hidden', y: 20 }}
|
|
animate={
|
|
isOpen
|
|
? { opacity: 1, height: 'auto', overflow: 'hidden', y: 0 }
|
|
: { opacity: 0, height: 0, overflow: 'hidden', y: 20 }
|
|
}
|
|
transition={transition}
|
|
{...props}
|
|
/>
|
|
</CollapsiblePrimitive.Content>
|
|
) : (
|
|
isOpen && (
|
|
<CollapsiblePrimitive.Content asChild forceMount>
|
|
<motion.div
|
|
key='collapsible-content'
|
|
data-slot='collapsible-content'
|
|
layout
|
|
initial={{ opacity: 0, height: 0, overflow: 'hidden', y: 20 }}
|
|
animate={{ opacity: 1, height: 'auto', overflow: 'hidden', y: 0 }}
|
|
exit={{ opacity: 0, height: 0, overflow: 'hidden', y: 20 }}
|
|
transition={transition}
|
|
{...props}
|
|
/>
|
|
</CollapsiblePrimitive.Content>
|
|
)
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
}
|
|
|
|
export {
|
|
Collapsible,
|
|
CollapsibleTrigger,
|
|
CollapsibleContent,
|
|
useCollapsible,
|
|
type CollapsibleProps,
|
|
type CollapsibleTriggerProps,
|
|
type CollapsibleContentProps,
|
|
type CollapsibleContextType,
|
|
};
|