@loke/ui
Version:
2 lines (1 loc) • 3.96 kB
JavaScript
import{composeEventHandlers}from"@loke/ui/compose-events";import{useComposedRefs}from"@loke/ui/compose-refs";import{createContextScope}from"@loke/ui/context";import{Presence}from"@loke/ui/presence";import{Primitive}from"@loke/ui/primitive";import{useControllableState}from"@loke/ui/use-controllable-state";import{useId}from"@loke/ui/use-id";import{useLayoutEffect}from"@loke/ui/use-layout-effect";import{forwardRef,useCallback,useEffect,useRef,useState}from"react";import{jsx}from"react/jsx-runtime";var COLLAPSIBLE_NAME="Collapsible",[createCollapsibleContext,createCollapsibleScope]=createContextScope(COLLAPSIBLE_NAME),[CollapsibleProvider,useCollapsibleContext]=createCollapsibleContext(COLLAPSIBLE_NAME),Collapsible=forwardRef((props,forwardedRef)=>{let{__scopeCollapsible,open:openProp,defaultOpen,disabled,onOpenChange,...collapsibleProps}=props,[open,setOpen]=useControllableState({caller:COLLAPSIBLE_NAME,defaultProp:defaultOpen??!1,onChange:onOpenChange,prop:openProp});return jsx(CollapsibleProvider,{contentId:useId(),disabled,onOpenToggle:useCallback(()=>setOpen((prevOpen)=>!prevOpen),[setOpen]),open,scope:__scopeCollapsible,children:jsx(Primitive.div,{"data-disabled":disabled?"":void 0,"data-state":getState(open),...collapsibleProps,ref:forwardedRef})})});Collapsible.displayName=COLLAPSIBLE_NAME;var TRIGGER_NAME="CollapsibleTrigger",CollapsibleTrigger=forwardRef((props,forwardedRef)=>{let{__scopeCollapsible,...triggerProps}=props,context=useCollapsibleContext(TRIGGER_NAME,__scopeCollapsible);return jsx(Primitive.button,{"aria-controls":context.contentId,"aria-expanded":context.open,"data-disabled":context.disabled?"":void 0,"data-state":getState(context.open),disabled:context.disabled,...triggerProps,type:"button",onClick:composeEventHandlers(props.onClick,context.onOpenToggle),ref:forwardedRef})});CollapsibleTrigger.displayName=TRIGGER_NAME;var CONTENT_NAME="CollapsibleContent",CollapsibleContent=forwardRef((props,forwardedRef)=>{let{forceMount,...contentProps}=props,context=useCollapsibleContext(CONTENT_NAME,props.__scopeCollapsible);return jsx(Presence,{present:forceMount||context.open,children:({present})=>jsx(CollapsibleContentImpl,{...contentProps,present,ref:forwardedRef})})});CollapsibleContent.displayName=CONTENT_NAME;var CollapsibleContentImpl=forwardRef((props,forwardedRef)=>{let{__scopeCollapsible,present,children,...contentProps}=props,context=useCollapsibleContext(CONTENT_NAME,__scopeCollapsible),[isPresent,setIsPresent]=useState(present),ref=useRef(null),composedRefs=useComposedRefs(forwardedRef,ref),heightRef=useRef(0),height=heightRef.current,widthRef=useRef(0),width=widthRef.current,isOpen=context.open||isPresent,isMountAnimationPreventedRef=useRef(isOpen),originalStylesRef=useRef(void 0);return useEffect(()=>{let rAF=requestAnimationFrame(()=>{isMountAnimationPreventedRef.current=!1});return()=>cancelAnimationFrame(rAF)},[]),useLayoutEffect(()=>{let node=ref.current;if(node){originalStylesRef.current=originalStylesRef.current||{animationName:node.style.animationName,transitionDuration:node.style.transitionDuration},node.style.transitionDuration="0s",node.style.animationName="none";let rect=node.getBoundingClientRect();if(heightRef.current=rect.height,widthRef.current=rect.width,!isMountAnimationPreventedRef.current)node.style.transitionDuration=originalStylesRef.current.transitionDuration,node.style.animationName=originalStylesRef.current.animationName;setIsPresent(present)}},[context.open,present]),jsx(Primitive.div,{"data-disabled":context.disabled?"":void 0,"data-state":getState(context.open),hidden:!isOpen,id:context.contentId,...contentProps,ref:composedRefs,style:{"--loke-collapsible-content-height":height?`${height}px`:void 0,"--loke-collapsible-content-width":width?`${width}px`:void 0,...props.style},children:isOpen&&children})});function getState(open){return open?"open":"closed"}export{createCollapsibleScope,CollapsibleTrigger,CollapsibleContent,Collapsible};