{"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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"}