UNPKG

@mantine/hooks

Version:

A collection of 50+ hooks for state and UI management

1 lines 7.08 kB
{"version":3,"file":"use-collapse.cjs","names":["mergeRefs"],"sources":["../../src/use-collapse/use-collapse.ts"],"sourcesContent":["import React, { CSSProperties, useEffectEvent, useRef, useState } from 'react';\nimport { flushSync } from 'react-dom';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\nimport { mergeRefs } from '../use-merged-ref/use-merged-ref';\n\nfunction getAutoHeightDuration(height: number | string) {\n if (!height || typeof height === 'string') {\n return 0;\n }\n const constant = height / 36;\n return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);\n}\n\nexport function getElementHeight(elementRef: React.RefObject<HTMLElement | null>) {\n return elementRef.current ? elementRef.current.scrollHeight : 'auto';\n}\n\nexport interface UseCollapseInput {\n /** Expanded state */\n expanded: boolean;\n\n /** Transition duration in milliseconds, by default calculated based on content height */\n transitionDuration?: number;\n\n /** Transition timing function, `ease` by default */\n transitionTimingFunction?: string;\n\n /** Called when transition ends */\n onTransitionEnd?: () => void;\n\n /** Called when transition starts */\n onTransitionStart?: () => void;\n\n /** If true, collapsed content is kept in the DOM and hidden with `display: none` styles */\n keepMounted?: boolean;\n}\n\ninterface GetCollapsePropsInput {\n style?: CSSProperties;\n ref?: React.Ref<HTMLDivElement>;\n}\n\ninterface GetCollapsePropsReturnValue {\n 'aria-hidden': boolean;\n inert: boolean;\n ref: React.RefCallback<HTMLDivElement>;\n onTransitionEnd: (event: React.TransitionEvent<Element>) => void;\n style: React.CSSProperties;\n}\n\nexport type UseCollapseState = 'entering' | 'entered' | 'exiting' | 'exited';\n\nexport interface UseCollapseReturnValue {\n /** Current transition state */\n state: UseCollapseState;\n\n /** Props to pass down to the collapsible element */\n getCollapseProps: (input?: GetCollapsePropsInput) => GetCollapsePropsReturnValue;\n}\n\nexport function useCollapse({\n transitionDuration,\n transitionTimingFunction = 'ease',\n onTransitionEnd,\n onTransitionStart,\n expanded,\n keepMounted,\n}: UseCollapseInput): UseCollapseReturnValue {\n const collapsedStyles = {\n height: 0,\n overflow: 'hidden',\n ...(keepMounted ? {} : { display: 'none' }),\n };\n\n const onTransitionStartEvent = useEffectEvent(() => onTransitionStart?.());\n\n const elementRef = useRef<HTMLElement>(null);\n const [styles, setStylesRaw] = useState<CSSProperties>(expanded ? {} : collapsedStyles);\n const [state, setState] = useState<UseCollapseState>(expanded ? 'entered' : 'exited');\n const setStyles = (newStyles: React.SetStateAction<CSSProperties>) => {\n flushSync(() => setStylesRaw(newStyles));\n };\n\n const mergeStyles = (newStyles: CSSProperties) => {\n setStyles((oldStyles) => ({ ...oldStyles, ...newStyles }));\n };\n\n const getTransitionStyles = (height: number | string) => {\n const duration = transitionDuration ?? getAutoHeightDuration(height);\n return {\n transition: `height ${duration}ms ${transitionTimingFunction}, opacity ${duration}ms ${transitionTimingFunction}`,\n };\n };\n\n useDidUpdate(() => {\n const shouldTransition = transitionDuration !== 0;\n\n if (shouldTransition) {\n onTransitionStartEvent();\n }\n\n if (expanded) {\n window.requestAnimationFrame(() => {\n flushSync(() => setState('entering'));\n mergeStyles({ willChange: 'height', display: 'block', overflow: 'hidden' });\n window.requestAnimationFrame(() => {\n const height = getElementHeight(elementRef);\n mergeStyles({ ...getTransitionStyles(height), height });\n });\n });\n } else {\n window.requestAnimationFrame(() => {\n flushSync(() => setState('exiting'));\n const height = getElementHeight(elementRef);\n mergeStyles({ ...getTransitionStyles(height), willChange: 'height', height });\n window.requestAnimationFrame(() => mergeStyles({ height: 0, overflow: 'hidden' }));\n });\n }\n }, [expanded]);\n\n const handleTransitionEnd = (event: React.TransitionEvent): void => {\n if (event.target !== elementRef.current || event.propertyName !== 'height') {\n return;\n }\n\n if (expanded) {\n const height = getElementHeight(elementRef);\n\n if (height === styles.height) {\n setStyles({});\n } else {\n mergeStyles({ height });\n }\n\n setState('entered');\n onTransitionEnd?.();\n } else if (styles.height === 0) {\n setStyles(collapsedStyles);\n setState('exited');\n onTransitionEnd?.();\n }\n };\n\n return {\n state,\n getCollapseProps: (input) => ({\n 'aria-hidden': !expanded,\n inert: !expanded,\n ref: mergeRefs(elementRef, input?.ref),\n onTransitionEnd: handleTransitionEnd,\n style: { boxSizing: 'border-box', ...input?.style, ...styles },\n }),\n };\n}\n\nexport namespace useCollapse {\n export type Input = UseCollapseInput;\n export type ReturnValue = UseCollapseReturnValue;\n export type State = UseCollapseState;\n}\n"],"mappings":";;;;;;AAKA,SAAS,sBAAsB,QAAyB;CACtD,IAAI,CAAC,UAAU,OAAO,WAAW,UAC/B,OAAO;CAET,MAAM,WAAW,SAAS;CAC1B,OAAO,KAAK,OAAO,IAAI,KAAK,YAAY,MAAO,WAAW,KAAK,EAAE;AACnE;AAEA,SAAgB,iBAAiB,YAAiD;CAChF,OAAO,WAAW,UAAU,WAAW,QAAQ,eAAe;AAChE;AA6CA,SAAgB,YAAY,EAC1B,oBACA,2BAA2B,QAC3B,iBACA,mBACA,UACA,eAC2C;CAC3C,MAAM,kBAAkB;EACtB,QAAQ;EACR,UAAU;EACV,GAAI,cAAc,CAAC,IAAI,EAAE,SAAS,OAAO;CAC3C;CAEA,MAAM,0BAAA,GAAA,MAAA,sBAA8C,oBAAoB,CAAC;CAEzE,MAAM,cAAA,GAAA,MAAA,QAAiC,IAAI;CAC3C,MAAM,CAAC,QAAQ,iBAAA,GAAA,MAAA,UAAwC,WAAW,CAAC,IAAI,eAAe;CACtF,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,UAAuC,WAAW,YAAY,QAAQ;CACpF,MAAM,aAAa,cAAmD;EACpE,CAAA,GAAA,UAAA,iBAAgB,aAAa,SAAS,CAAC;CACzC;CAEA,MAAM,eAAe,cAA6B;EAChD,WAAW,eAAe;GAAE,GAAG;GAAW,GAAG;EAAU,EAAE;CAC3D;CAEA,MAAM,uBAAuB,WAA4B;EACvD,MAAM,WAAW,sBAAsB,sBAAsB,MAAM;EACnE,OAAO,EACL,YAAY,UAAU,SAAS,KAAK,yBAAyB,YAAY,SAAS,KAAK,2BACzF;CACF;CAEA,uBAAA,mBAAmB;EAGjB,IAFyB,uBAAuB,GAG9C,uBAAuB;EAGzB,IAAI,UACF,OAAO,4BAA4B;GACjC,CAAA,GAAA,UAAA,iBAAgB,SAAS,UAAU,CAAC;GACpC,YAAY;IAAE,YAAY;IAAU,SAAS;IAAS,UAAU;GAAS,CAAC;GAC1E,OAAO,4BAA4B;IACjC,MAAM,SAAS,iBAAiB,UAAU;IAC1C,YAAY;KAAE,GAAG,oBAAoB,MAAM;KAAG;IAAO,CAAC;GACxD,CAAC;EACH,CAAC;OAED,OAAO,4BAA4B;GACjC,CAAA,GAAA,UAAA,iBAAgB,SAAS,SAAS,CAAC;GACnC,MAAM,SAAS,iBAAiB,UAAU;GAC1C,YAAY;IAAE,GAAG,oBAAoB,MAAM;IAAG,YAAY;IAAU;GAAO,CAAC;GAC5E,OAAO,4BAA4B,YAAY;IAAE,QAAQ;IAAG,UAAU;GAAS,CAAC,CAAC;EACnF,CAAC;CAEL,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,uBAAuB,UAAuC;EAClE,IAAI,MAAM,WAAW,WAAW,WAAW,MAAM,iBAAiB,UAChE;EAGF,IAAI,UAAU;GACZ,MAAM,SAAS,iBAAiB,UAAU;GAE1C,IAAI,WAAW,OAAO,QACpB,UAAU,CAAC,CAAC;QAEZ,YAAY,EAAE,OAAO,CAAC;GAGxB,SAAS,SAAS;GAClB,kBAAkB;EACpB,OAAO,IAAI,OAAO,WAAW,GAAG;GAC9B,UAAU,eAAe;GACzB,SAAS,QAAQ;GACjB,kBAAkB;EACpB;CACF;CAEA,OAAO;EACL;EACA,mBAAmB,WAAW;GAC5B,eAAe,CAAC;GAChB,OAAO,CAAC;GACR,KAAKA,uBAAAA,UAAU,YAAY,OAAO,GAAG;GACrC,iBAAiB;GACjB,OAAO;IAAE,WAAW;IAAc,GAAG,OAAO;IAAO,GAAG;GAAO;EAC/D;CACF;AACF"}