UNPKG

@kunukn/react-collapse

Version:

Collapse library based on CSS transition for React

1 lines 10 kB
{"version":3,"file":"react-collapse.cjs","sources":["../lib/Collapse.tsx"],"sourcesContent":["import { useCallback, useEffect, useReducer, useRef, useState } from 'react'\r\n\r\nimport type { CollapseProps } from '../types/index.d.ts'\r\n\r\nconst COLLAPSED = 'collapsed'\r\nconst COLLAPSING = 'collapsing'\r\nconst EXPANDING = 'expanding'\r\nconst EXPANDED = 'expanded'\r\n\r\nconst defaultClassName = 'collapse-css-transition'\r\nconst defaultElementType = 'div'\r\nconst defaultCollapseHeight = '0px'\r\n\r\nfunction nextFrame(callback: FrameRequestCallback) {\r\n requestAnimationFrame(function () {\r\n //setTimeout(callback, 0); // NOT used because can be jumpy if click-spamming.\r\n requestAnimationFrame(callback) // This is used.\r\n })\r\n}\r\n\r\nexport const Collapse: React.FunctionComponent<CollapseProps> = ({\r\n children,\r\n transition,\r\n style,\r\n render,\r\n elementType = defaultElementType,\r\n isOpen,\r\n collapseHeight = defaultCollapseHeight,\r\n onInit,\r\n onChange,\r\n className = defaultClassName,\r\n addState,\r\n noAnim,\r\n overflowOnExpanded,\r\n ...rest\r\n}: CollapseProps): React.JSX.Element => {\r\n const getCollapsedVisibility = () =>\r\n collapseHeight === '0px' ? 'hidden' : undefined\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const [_, forceUpdate] = useReducer((_) => _ + 1, 0)\r\n\r\n const elementRef = useRef()\r\n const [callbackTick, setCallbackTick] = useState(0)\r\n\r\n // Avoiding setState to control when stuff are updated.\r\n // Might not be needed.\r\n const state = useRef({\r\n collapse: isOpen ? EXPANDED : COLLAPSED,\r\n style: {\r\n height: isOpen ? undefined : collapseHeight,\r\n visibility: isOpen ? undefined : getCollapsedVisibility(),\r\n },\r\n }).current\r\n\r\n useEffect(() => {\r\n // Invoke callback when data are updated, use Effect to sync state.\r\n callbackTick && onCallback(onChange)\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [callbackTick])\r\n\r\n const onCallback = (callback, params = {}) => {\r\n if (callback) {\r\n callback({ state: state.collapse, style: state.style, ...params })\r\n }\r\n }\r\n\r\n function setCollapsed() {\r\n if (!elementRef.current) return // might be redundant\r\n\r\n // Update state\r\n state.collapse = COLLAPSED\r\n\r\n state.style = {\r\n height: collapseHeight,\r\n visibility: getCollapsedVisibility(),\r\n }\r\n forceUpdate()\r\n\r\n setTimeout(() => setCallbackTick(Date.now), 0) // callback and re-render\r\n }\r\n\r\n function setCollapsing() {\r\n if (!elementRef.current) return // might be redundant\r\n\r\n if (noAnim) {\r\n return setCollapsed()\r\n }\r\n\r\n // Update state\r\n state.collapse = COLLAPSING\r\n\r\n state.style = {\r\n height: getElementHeight(),\r\n visibility: undefined,\r\n }\r\n forceUpdate()\r\n\r\n nextFrame(() => {\r\n if (!elementRef.current) return\r\n if (state.collapse !== COLLAPSING) return\r\n\r\n state.style = {\r\n height: collapseHeight,\r\n visibility: undefined,\r\n }\r\n\r\n setCallbackTick(Date.now) // callback and re-render\r\n })\r\n }\r\n\r\n function setExpanding() {\r\n if (!elementRef.current) return // might be redundant\r\n\r\n if (noAnim) {\r\n return setExpanded()\r\n }\r\n\r\n // Updatetate\r\n state.collapse = EXPANDING\r\n\r\n nextFrame(() => {\r\n if (!elementRef.current) return // might be redundant\r\n if (state.collapse !== EXPANDING) return\r\n\r\n state.style = {\r\n height: getElementHeight(),\r\n visibility: undefined,\r\n }\r\n\r\n setCallbackTick(Date.now) // callback and re-render\r\n })\r\n }\r\n\r\n function setExpanded() {\r\n if (!elementRef.current) return // might be redundant\r\n\r\n // Update state\r\n state.collapse = EXPANDED\r\n\r\n state.style = {\r\n height: undefined,\r\n visibility: undefined,\r\n }\r\n forceUpdate()\r\n\r\n setTimeout(() => setCallbackTick(Date.now), 0) // callback and re-render\r\n }\r\n\r\n function getElementHeight() {\r\n // @ts-ignore\r\n return `${elementRef.current.scrollHeight}px`\r\n }\r\n\r\n function onTransitionEnd({ target, propertyName }) {\r\n if (target === elementRef.current && propertyName === 'height') {\r\n const styleHeight = target.style.height\r\n\r\n switch (state.collapse) {\r\n case EXPANDING:\r\n if (styleHeight === undefined || styleHeight === collapseHeight)\r\n // This is stale, a newer event has happened before this could execute\r\n console.warn(\r\n `onTransitionEnd height unexpected ${styleHeight}`,\r\n 'ignore setExpanded'\r\n )\r\n else setExpanded()\r\n break\r\n case COLLAPSING:\r\n if (styleHeight === undefined || styleHeight !== collapseHeight)\r\n // This is stale, a newer event has happened before this could execute\r\n console.warn(\r\n `onTransitionEnd height unexpected ${styleHeight}`,\r\n 'ignore setCollapsed'\r\n )\r\n else setCollapsed()\r\n break\r\n default:\r\n console.warn('Ignored in onTransitionEnd', state.collapse)\r\n }\r\n }\r\n }\r\n\r\n // getDerivedStateFromProps\r\n const didOpen = state.collapse === EXPANDED || state.collapse === EXPANDING\r\n\r\n if (!didOpen && isOpen) setExpanding()\r\n\r\n if (didOpen && !isOpen) setCollapsing()\r\n // END getDerivedStateFromProps\r\n\r\n const overflow =\r\n state.collapse === EXPANDED && overflowOnExpanded ? undefined : 'hidden'\r\n\r\n const computedStyle = {\r\n overflow,\r\n transition,\r\n ...style,\r\n ...state.style,\r\n }\r\n const ElementType = elementType\r\n\r\n const callbackRef = useCallback(\r\n (node) => {\r\n if (node) {\r\n elementRef.current = node\r\n onCallback(onInit, { node })\r\n }\r\n },\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [elementType]\r\n )\r\n\r\n const collapseClassName = addState\r\n ? `${className} --c-${state.collapse}`\r\n : className\r\n\r\n return (\r\n // @ts-ignore\r\n <ElementType\r\n ref={callbackRef}\r\n style={computedStyle}\r\n onTransitionEnd={onTransitionEnd}\r\n className={collapseClassName}\r\n {...rest}\r\n >\r\n {typeof children === 'function'\r\n ? children(state.collapse)\r\n : typeof render === 'function'\r\n ? render(state.collapse)\r\n : children}\r\n </ElementType>\r\n )\r\n}\r\n"],"names":["COLLAPSED","COLLAPSING","EXPANDING","EXPANDED","defaultClassName","defaultElementType","defaultCollapseHeight","nextFrame","callback","Collapse","children","transition","style","render","elementType","isOpen","collapseHeight","onInit","onChange","className","addState","noAnim","overflowOnExpanded","rest","getCollapsedVisibility","_","forceUpdate","useReducer","elementRef","useRef","callbackTick","setCallbackTick","useState","state","useEffect","onCallback","params","setCollapsed","setCollapsing","getElementHeight","setExpanding","setExpanded","onTransitionEnd","target","propertyName","styleHeight","didOpen","computedStyle","ElementType","callbackRef","useCallback","node","collapseClassName","jsx"],"mappings":"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"}