All files / src store.tsx

100% Statements 17/17
80% Branches 12/15
100% Functions 6/6
100% Lines 14/14

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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95                                                                        9x                         9x 9x     7x                 7x         7x         7x         7x     9x 30x             9x 17x 17x     9x  
import React, { PropsWithChildren, createContext, useContext, useReducer } from 'react';
 
export interface StoreContextValue<Tag extends BlockTagType> extends InitialState<Tag> {
  dispatch: React.Dispatch<Partial<InitialState<Tag>>>;
}
 
export type BlockTagType = keyof JSX.IntrinsicElements;
 
export type Index = Record<string, number>;
export type Fields = React.ReactElement<HTMLInputElement & { index?: number }>;
export type Buttons = React.ReactElement<HTMLButtonElement & { index?: number }>;
export type Blocks<Tag extends BlockTagType = 'div'> = React.ReactElement<Tag & { index?: number }>;
 
export interface RenderStateProps<T extends BlockTagType = 'div'> {
  $$index?: Record<string, number>;
  fields?: Record<string, Fields | null>;
  buttons?: Record<string, Buttons | null>;
  blocks?: Record<string, Blocks<T> | null>;
  extra?: Record<string, React.ReactNode>;
  [keyname: string]: any;
}
 
interface Control<T> {
  name: string;
  index: number;
  children?: T | null;
}
 
export interface InitialState<Tag extends BlockTagType = 'div'> extends RenderStateProps<Tag> {
  data: {
    fields: Control<Fields>[];
    buttons: Control<Buttons>[];
    blocks: Control<Blocks<Tag>>[];
  };
}
 
export const initialState: InitialState = {
  index: {},
  fields: {},
  buttons: {},
  blocks: {},
  extra: {},
  data: {
    fields: [],
    buttons: [],
    blocks: [],
  },
};
 
export const Context = createContext<StoreContextValue<'div'>>(initialState as StoreContextValue<'div'>);
Context.displayName = 'Login.Context';
 
export function reducer(state: InitialState, action: Partial<RenderStateProps>): InitialState {
  const result = {
    ...state,
    ...action,
    $$index: { ...state.$$index, ...action.$$index },
    fields: { ...state.fields, ...action.fields },
    buttons: { ...state.buttons, ...action.buttons },
    blocks: { ...state.blocks, ...action.blocks },
    extra: { ...state.extra, ...action.extra },
  };
  const fieldsArray = Object.keys(result.fields).map((key) => ({
    name: key,
    index: result.fields[key]?.props?.index || (result.$$index || {})[key] || 0,
    children: result.fields[key],
  }));
  const buttonsArray = Object.keys(result.buttons).map((key) => ({
    name: key,
    index: result.buttons[key]?.props?.index || (result.$$index || {})[key] || 0,
    children: result.buttons[key],
  }));
  const blocksArray = Object.keys(result.blocks).map((key) => ({
    name: key,
    index: result.blocks[key]?.props?.index || (result.$$index || {})[key] || 0,
    children: result.blocks[key],
  }));
  return { ...result, data: { ...result.data, fields: fieldsArray, buttons: buttonsArray, blocks: blocksArray } };
}
 
export const useStore = () => {
  return useContext(Context);
};
 
interface ProviderProps {
  render?: any;
}
 
export const Provider: React.FC<PropsWithChildren<ProviderProps>> = ({ children, render }) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return <Context.Provider value={{ ...state, dispatch }}>{children}</Context.Provider>;
};
 
Provider.displayName = 'Login.Provider';