import oc from "open-color"; import { AppState, FlooredNumber, NormalizedZoomValue } from "./types"; import { DEFAULT_FONT_SIZE, DEFAULT_FONT_FAMILY, DEFAULT_TEXT_ALIGN, } from "./constants"; export const getDefaultAppState = (): Omit< AppState, "offsetTop" | "offsetLeft" > => { return { appearance: "light", isLoading: false, errorMessage: null, draggingElement: null, resizingElement: null, multiElement: null, editingElement: null, startBoundElement: null, editingLinearElement: null, elementType: "selection", elementLocked: false, exportBackground: true, exportEmbedScene: false, shouldAddWatermark: false, currentItemStrokeColor: oc.black, currentItemBackgroundColor: "transparent", currentItemFillStyle: "hachure", currentItemStrokeWidth: 1, currentItemStrokeStyle: "solid", currentItemRoughness: 1, currentItemOpacity: 100, currentItemFontSize: DEFAULT_FONT_SIZE, currentItemFontFamily: DEFAULT_FONT_FAMILY, currentItemTextAlign: DEFAULT_TEXT_ALIGN, currentItemStrokeSharpness: "sharp", currentItemLinearStrokeSharpness: "round", viewBackgroundColor: oc.white, scrollX: 0 as FlooredNumber, scrollY: 0 as FlooredNumber, cursorX: 0, cursorY: 0, cursorButton: "up", scrolledOutside: false, username: "", isBindingEnabled: true, isCollaborating: false, isResizing: false, isRotating: false, selectionElement: null, zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 }, }, openMenu: null, lastPointerDownWith: "mouse", selectedElementIds: {}, previousSelectedElementIds: {}, collaborators: new Map(), shouldCacheIgnoreZoom: false, showShortcutsDialog: false, suggestedBindings: [], zenModeEnabled: false, gridSize: null, editingGroupId: null, selectedGroupIds: {}, width: window.innerWidth, height: window.innerHeight, isLibraryOpen: false, fileHandle: null, }; }; /** * Config containing all AppState keys. Used to determine whether given state * prop should be stripped when exporting to given storage type. */ const APP_STATE_STORAGE_CONF = (< Values extends { /** whether to keep when storing to browser storage (localStorage/IDB) */ browser: boolean; /** whether to keep when exporting to file/database */ export: boolean; }, T extends Record >( config: { [K in keyof T]: K extends keyof AppState ? T[K] : never }, ) => config)({ appearance: { browser: true, export: false }, collaborators: { browser: false, export: false }, currentItemBackgroundColor: { browser: true, export: false }, currentItemFillStyle: { browser: true, export: false }, currentItemFontFamily: { browser: true, export: false }, currentItemFontSize: { browser: true, export: false }, currentItemOpacity: { browser: true, export: false }, currentItemRoughness: { browser: true, export: false }, currentItemStrokeColor: { browser: true, export: false }, currentItemStrokeStyle: { browser: true, export: false }, currentItemStrokeWidth: { browser: true, export: false }, currentItemTextAlign: { browser: true, export: false }, currentItemStrokeSharpness: { browser: true, export: false }, currentItemLinearStrokeSharpness: { browser: true, export: false }, cursorButton: { browser: true, export: false }, cursorX: { browser: true, export: false }, cursorY: { browser: true, export: false }, draggingElement: { browser: false, export: false }, editingElement: { browser: false, export: false }, startBoundElement: { browser: false, export: false }, editingGroupId: { browser: true, export: false }, editingLinearElement: { browser: false, export: false }, elementLocked: { browser: true, export: false }, elementType: { browser: true, export: false }, errorMessage: { browser: false, export: false }, exportBackground: { browser: true, export: false }, exportEmbedScene: { browser: true, export: false }, gridSize: { browser: true, export: true }, height: { browser: false, export: false }, isBindingEnabled: { browser: false, export: false }, isCollaborating: { browser: false, export: false }, isLibraryOpen: { browser: false, export: false }, isLoading: { browser: false, export: false }, isResizing: { browser: false, export: false }, isRotating: { browser: false, export: false }, lastPointerDownWith: { browser: true, export: false }, multiElement: { browser: false, export: false }, openMenu: { browser: true, export: false }, previousSelectedElementIds: { browser: true, export: false }, resizingElement: { browser: false, export: false }, scrolledOutside: { browser: true, export: false }, scrollX: { browser: true, export: false }, scrollY: { browser: true, export: false }, selectedElementIds: { browser: true, export: false }, selectedGroupIds: { browser: true, export: false }, selectionElement: { browser: false, export: false }, shouldAddWatermark: { browser: true, export: false }, shouldCacheIgnoreZoom: { browser: true, export: false }, showShortcutsDialog: { browser: false, export: false }, suggestedBindings: { browser: false, export: false }, username: { browser: true, export: false }, viewBackgroundColor: { browser: true, export: true }, width: { browser: false, export: false }, zenModeEnabled: { browser: true, export: false }, zoom: { browser: true, export: false }, offsetTop: { browser: false, export: false }, offsetLeft: { browser: false, export: false }, fileHandle: { browser: false, export: false }, }); const _clearAppStateForStorage = ( appState: Partial, exportType: ExportType, ) => { type ExportableKeys = { [K in keyof typeof APP_STATE_STORAGE_CONF]: typeof APP_STATE_STORAGE_CONF[K][ExportType] extends true ? K : never; }[keyof typeof APP_STATE_STORAGE_CONF]; const stateForExport = {} as { [K in ExportableKeys]?: typeof appState[K] }; for (const key of Object.keys(appState) as (keyof typeof appState)[]) { const propConfig = APP_STATE_STORAGE_CONF[key]; if (!propConfig) { console.error( `_clearAppStateForStorage: appState key "${key}" config doesn't exist for "${exportType}" export type`, ); } if (propConfig?.[exportType]) { // @ts-ignore see https://github.com/microsoft/TypeScript/issues/31445 stateForExport[key] = appState[key]; } } return stateForExport; }; export const clearAppStateForLocalStorage = (appState: Partial) => { return _clearAppStateForStorage(appState, "browser"); }; export const cleanAppStateForExport = (appState: Partial) => { return _clearAppStateForStorage(appState, "export"); };