import React, { Dispatch } from 'react'; import { PropsWithChildren, Reducer, useCallback, useReducer } from 'react'; import { Platform } from 'react-native'; import { createContext, useContextSelector, useContext } from 'use-context-selector'; export const olListTypes = [ 'default', 'decimal', 'lower-alpha', 'upper-alpha', 'lower-greek', 'lower-roman', 'upper-roman', 'decimal-leading-zero', 'none' ] as const; export const ulListTypes = [ 'default', 'disk', 'circle', 'square', 'disclosure-open', 'disclosure-closed', 'none' ] as const; type OlListType = typeof olListTypes[number]; type UlListType = typeof ulListTypes[number]; export interface PlaygroundMutableState { fontSize: number; color: string | undefined; lineHeight: number; olListType: OlListType; ulListType: UlListType; isBold: boolean; isItalic: boolean; dynamicMarkerBox: boolean; fontFamily: string; selectedSource: Sk; } export interface PlaygroundState extends PlaygroundMutableState { sourceMap: SourceMap; } type Mutate> = { target: K; value: PlaygroundMutableState[K]; }; export type SourceMap = Record< Sk, { label: string; source: string; } >; export interface PlaygroundInitParams { sourceMap: SourceMap; initialSource: Sk; } const playgroundStoreContext = createContext>( {} as any ); const playgroundDispatchContext = createContext< Dispatch>> >(() => {}); function getInitialState({ sourceMap, initialSource }: PlaygroundInitParams): PlaygroundState { return { fontSize: 14, lineHeight: 1.2, olListType: 'default', ulListType: 'default', color: undefined, fontFamily: Platform.select({ android: 'Roboto', ios: 'San Francisco', macos: 'San Francisco', windows: 'Segoe UI', default: 'sans-serif' }), isBold: false, isItalic: false, dynamicMarkerBox: false, selectedSource: initialSource, sourceMap }; } export function usePlaygroundStateSlice< Sk extends string, K extends keyof PlaygroundState >(k: K): PlaygroundState[K] { const selector = useCallback((s: PlaygroundState) => s[k], [k]); return useContextSelector(playgroundStoreContext as any, selector); } export function usePlaygroundSource() { const selectedSource = usePlaygroundStateSlice('selectedSource'); const sourceMap = usePlaygroundStateSlice('sourceMap'); return sourceMap[selectedSource]; } export function usePlaygroundState() { return useContext(playgroundStoreContext); } export function usePlaygroundSetter< Sk extends string, K extends keyof PlaygroundMutableState >(k: K): (v: PlaygroundMutableState[K]) => void { const dispatch = useContext(playgroundDispatchContext); return useCallback( (v: PlaygroundMutableState[K]) => dispatch({ value: v, target: k }), [k, dispatch] ); } const reducer: Reducer< PlaygroundMutableState, Mutate> > = function reducer(state, action) { return { ...state, [action.target]: action.value }; }; export default function PlaygroundStoreProvider({ children, ...initParams }: PropsWithChildren>) { const [state, dispatch] = useReducer< typeof reducer, PlaygroundInitParams >(reducer, initParams, getInitialState); return ( {children} ); }