Skip to content
Snippets Groups Projects
useSplitsExpanded.jsx 2.71 KiB
Newer Older
  • Learn to ignore specific revisions
  • import React, {
      createContext,
      useMemo,
      useEffect,
      useContext,
      useReducer,
    } from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    
    const SplitsExpandedContext = createContext(null);
    
    export function useSplitsExpanded() {
      const data = useContext(SplitsExpandedContext);
    
      return useMemo(
        () => ({
          ...data,
          expanded: id =>
            data.state.mode === 'collapse'
              ? !data.state.ids.has(id)
              : data.state.ids.has(id),
        }),
        [data],
      );
    }
    
    export function SplitsExpandedProvider({ children, initialMode = 'expand' }) {
      const cachedState = useSelector(state => state.app.lastSplitState);
      const reduxDispatch = useDispatch();
    
      const [state, dispatch] = useReducer(
        (state, action) => {
          switch (action.type) {
            case 'toggle-split': {
              const ids = new Set([...state.ids]);
              const { id } = action;
              if (ids.has(id)) {
                ids.delete(id);
              } else {
                ids.add(id);
              }
              return { ...state, ids };
            }
            case 'open-split': {
              const ids = new Set([...state.ids]);
              const { id } = action;
              if (state.mode === 'collapse') {
                ids.delete(id);
              } else {
                ids.add(id);
              }
              return { ...state, ids };
            }
            case 'set-mode': {
              return {
                ...state,
                mode: action.mode,
                ids: new Set(),
                transitionId: null,
              };
            }
            case 'switch-mode':
              if (state.transitionId != null) {
                // You can only transition once at a time
                return state;
              }
    
              return {
                ...state,
                mode: state.mode === 'expand' ? 'collapse' : 'expand',
                transitionId: action.id,
                ids: new Set(),
              };
            case 'finish-switch-mode':
              return { ...state, transitionId: null };
            default:
              throw new Error('Unknown action type: ' + action.type);
          }
        },
        cachedState.current || { ids: new Set(), mode: initialMode },
      );
    
      useEffect(() => {
        if (state.transitionId != null) {
          // This timeout allows animations to finish
          setTimeout(() => {
            dispatch({ type: 'finish-switch-mode' });
          }, 250);
        }
      }, [state.transitionId]);
    
      useEffect(() => {
        // In a finished state, cache the state
        if (state.transitionId == null) {
          reduxDispatch({ type: 'SET_LAST_SPLIT_STATE', splitState: state });
        }
      }, [reduxDispatch, state]);
    
      const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);
    
      return (
        <SplitsExpandedContext.Provider value={value}>
          {children}
        </SplitsExpandedContext.Provider>
      );
    }