All files / src/features/tree genericTree.ts

92.31% Statements 12/13
71.43% Branches 5/7
100% Functions 5/5
92.31% Lines 12/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67                                        1x                       1x         4x   3x   1x     1x 1x   4x               6x     1x   5x       5x    
import { createSlice, PayloadAction, SliceCaseReducers, ValidateSliceCaseReducers } from '@reduxjs/toolkit';
 
export interface TreeState<T> {
  root?: string;
  childMap: { [id: string]: string[] };
  parentMap: { [id: string]: string };
  nodeMap: { [id: string]: T };
}
export interface AppendChildPayload<T> {
  parent?: string;
  id: string;
  data: T;
}
 
/**
 * Generic createSlice wrapper for tree state management
 * @param name - slice name
 * @param initialState
 * @param reducers - extended reducers upon instantiation
 */
export const createGenericSlice = <
  T,
  Reducers extends SliceCaseReducers<TreeState<T>>
  >({
      name = '',
      initialState,
      reducers
    }: {
  name: string
  initialState: TreeState<T>
  reducers: ValidateSliceCaseReducers<TreeState<T>, Reducers>
}) => {
  return createSlice({
    name,
    initialState,
    reducers: {
      appendChild(state: TreeState<T>, action: PayloadAction<AppendChildPayload<T>>) {
        if (action.payload.parent === undefined) {
          // no parent == root
          state.root = action.payload.id;
        } else {
          Iif (!state.childMap[action.payload.parent]) {
            state.childMap[action.payload.parent] = [];
          }
          state.childMap[action.payload.parent].push(action.payload.id);
          state.parentMap[action.payload.id] = action.payload.parent;
        }
        state.nodeMap[action.payload.id] = action.payload.data;
      },
      ...reducers
    }
  })
}
 
export function selectNode<T> (state: TreeState<T>, id: string): T | undefined {
  return state.nodeMap[id];
}
 
const emptyList: [] = [];
export function selectChildren (state: TreeState<unknown>, id: string): string[] {
  return state.childMap[id] || emptyList; // using [] instead of 'emptyList' will invoke unnecessary updates
}
 
export function selectRoot(state: TreeState<unknown>): string | undefined {
  return state.root;
}