{"version":3,"file":"react-collapse.mjs","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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"}