From 94b7f77f8fffc738766e45dd3e9daf0559e0b0bc Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Mon, 18 Jul 2022 19:09:34 +0800 Subject: [PATCH] feat: basic view api --- .../src/recast-block/types/view.ts | 14 ++- .../editor-core/src/recast-block/view.ts | 113 ++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 libs/components/editor-core/src/recast-block/view.ts diff --git a/libs/components/editor-core/src/recast-block/types/view.ts b/libs/components/editor-core/src/recast-block/types/view.ts index b5e2839acd..01f986c55f 100644 --- a/libs/components/editor-core/src/recast-block/types/view.ts +++ b/libs/components/editor-core/src/recast-block/types/view.ts @@ -10,7 +10,15 @@ export enum RecastScene { Whiteboard = 'whiteboard', } +export type RecastViewId = string & { + /** + * Type differentiator only. + */ + readonly __isViewId: true; +}; + type BaseView = { + id: RecastViewId; name: string; // TODO: design this // order?: string[]; @@ -25,4 +33,8 @@ export interface KanbanView extends BaseView { groupBy: RecastPropertyId; } -export type RecastView = PageView | KanbanView; +export interface TableView extends BaseView { + type: RecastScene.Kanban; +} + +export type RecastView = PageView | KanbanView | TableView; diff --git a/libs/components/editor-core/src/recast-block/view.ts b/libs/components/editor-core/src/recast-block/view.ts new file mode 100644 index 0000000000..95828b9782 --- /dev/null +++ b/libs/components/editor-core/src/recast-block/view.ts @@ -0,0 +1,113 @@ +import { nanoid } from 'nanoid'; +import { useCallback } from 'react'; +import { useRecastBlock } from './Context'; +import { META_VIEWS_KEY, RecastScene, RecastView, RecastViewId } from './types'; + +/** + * Generate a unique id for a recast view + */ +const genViewId = () => nanoid(16) as RecastViewId; // This is a safe type cast + +const DEFAULT_VIEWS: RecastView[] = [ + { + id: genViewId(), + name: 'ToDo List', + type: RecastScene.Page, + }, + { + id: genViewId(), + name: 'Kanban', + type: RecastScene.Kanban, + }, +]; + +export const useRecastView = () => { + const recastBlock = useRecastBlock(); + const recastViews = recastBlock.getProperty(META_VIEWS_KEY) ?? []; + + const getView = useCallback( + (id: RecastViewId) => { + const view = recastViews.find(v => v.id === id); + if (!view) { + throw new Error('Failed to find view with id ' + id); + } + return view; + }, + [recastViews] + ); + + const setViews = useCallback( + (views: RecastView[]) => { + return recastBlock.setProperty(META_VIEWS_KEY, views); + }, + [recastBlock] + ); + + const addView = useCallback( + async (newView: Omit) => { + await setViews([ + ...recastViews, + { ...newView, id: genViewId() } as RecastView, + ]); + }, + [recastViews, setViews] + ); + + const updateView = useCallback( + async (newView: RecastView) => { + const idx = recastViews.findIndex(v => v.id === newView.id); + if (!idx) { + throw new Error('Failed to find view with id ' + newView.id); + } + await setViews([ + ...recastViews.slice(0, idx), + newView, + ...recastViews.slice(idx + 1), + ]); + }, + [recastViews, setViews] + ); + + const renameView = useCallback( + async (id: RecastViewId, newName: string) => { + const curView = getView(id); + curView.name = newName; + await setViews(recastViews); + }, + [getView, recastViews, setViews] + ); + + const removeView = useCallback( + async (id: RecastViewId) => { + await setViews(recastViews.filter(v => v.id !== id)); + }, + [recastViews, setViews] + ); + + /** + * @deprecated Use updateView instead + */ + const changeScene = useCallback( + async ( + id: RecastViewId, + newScene: RecastScene.Page | RecastScene.Kanban + ) => { + const curView = getView(id); + if (curView.type === newScene) { + return; + } + curView.type = newScene; + await setViews(recastViews); + }, + [getView, recastViews, setViews] + ); + + return { + recastViews, + addView, + updateView, + renameView, + removeView, + // TODO API to reorder views + }; +};