a tool for shared writing and social publishing
1import { Block } from "components/Blocks/Block";
2import { create } from "zustand";
3import { combine, createJSONStorage, persist } from "zustand/middleware";
4
5type SelectedBlock = Pick<Block, "value" | "parent">;
6
7export type EditorIframePage = { type: "iframe"; url: string };
8export type EditorOpenPage = string | EditorIframePage;
9
10export const getEditorPageKey = (page: EditorOpenPage): string =>
11 typeof page === "string" ? page : `iframe:${page.url}`;
12
13export const useUIState = create(
14 combine(
15 {
16 lastUsedHighlight: "1" as "1" | "2" | "3",
17 focusedEntity: null as
18 | { entityType: "page"; entityID: string }
19 | { entityType: "block"; entityID: string; parent: string }
20 | { entityType: "footnote"; entityID: string; parent: string }
21 | null,
22 foldedBlocks: [] as string[],
23 openPages: [] as EditorOpenPage[],
24 selectedBlocks: [] as SelectedBlock[],
25 openPopover: null as string | null,
26 },
27 (set) => ({
28 setOpenPopover: (id: string | null) => {
29 set({ openPopover: id });
30 },
31 toggleFold: (entityID: string) => {
32 set((state) => {
33 return {
34 foldedBlocks: state.foldedBlocks.includes(entityID)
35 ? state.foldedBlocks.filter((b) => b !== entityID)
36 : [...state.foldedBlocks, entityID],
37 };
38 });
39 },
40 openPage: (parent: EditorOpenPage, page: EditorOpenPage) =>
41 set((state) => {
42 let parentKey = getEditorPageKey(parent);
43 let parentPosition = state.openPages.findIndex(
44 (s) => getEditorPageKey(s) === parentKey,
45 );
46 return {
47 openPages:
48 parentPosition === -1
49 ? [page]
50 : [...state.openPages.slice(0, parentPosition + 1), page],
51 };
52 }),
53 closePage: (pages: EditorOpenPage | EditorOpenPage[]) =>
54 set((s) => {
55 let keys = [pages].flat().map(getEditorPageKey);
56 return {
57 openPages: s.openPages.filter(
58 (c) => !keys.includes(getEditorPageKey(c)),
59 ),
60 };
61 }),
62 setFocusedBlock: (
63 b:
64 | { entityType: "page"; entityID: string }
65 | { entityType: "block"; entityID: string; parent: string }
66 | { entityType: "footnote"; entityID: string; parent: string }
67 | null,
68 ) => set(() => ({ focusedEntity: b })),
69 setSelectedBlock: (block: SelectedBlock) =>
70 set((state) => {
71 return { ...state, selectedBlocks: [block] };
72 }),
73 setSelectedBlocks: (blocks: SelectedBlock[]) =>
74 set((state) => {
75 return { ...state, selectedBlocks: blocks };
76 }),
77 addBlockToSelection: (block: SelectedBlock) =>
78 set((state) => {
79 if (state.selectedBlocks.find((b) => b.value === block.value))
80 return state;
81 return { ...state, selectedBlocks: [...state.selectedBlocks, block] };
82 }),
83 removeBlockFromSelection: (block: { value: string }) =>
84 set((state) => {
85 return {
86 ...state,
87 selectedBlocks: state.selectedBlocks.filter(
88 (f) => f.value !== block.value,
89 ),
90 };
91 }),
92 }),
93 ),
94);