From e550cf84c30c6cf3f63024e1e337bbdc90658701 Mon Sep 17 00:00:00 2001 From: 0xzion <0xyz.penx@gmail.com> Date: Tue, 19 Dec 2023 23:16:09 +0800 Subject: [PATCH] feat: can render list view --- .../database/src/ui/DatabaseContext.tsx | 30 +-- .../src/ui/Table/Cell/PrimaryCell.tsx | 179 ++++++++++-------- .../database/src/ui/ViewNav/AddViewBtn.tsx | 4 +- .../database/src/ui/ViewNav/ViewList.tsx | 18 +- .../database/src/ui/ViewNav/ViewMenu.tsx | 9 +- extensions/database/src/ui/views/ListView.tsx | 60 +++++- packages/local-db/src/db.ts | 2 + packages/model-types/src/interfaces/INode.ts | 1 + 8 files changed, 195 insertions(+), 108 deletions(-) diff --git a/extensions/database/src/ui/DatabaseContext.tsx b/extensions/database/src/ui/DatabaseContext.tsx index 1763b237..44b4ecd2 100644 --- a/extensions/database/src/ui/DatabaseContext.tsx +++ b/extensions/database/src/ui/DatabaseContext.tsx @@ -36,10 +36,10 @@ export interface IDatabaseContext { currentView: IViewNode - viewIndex: number - setViewIndex: Dispatch> + activeViewId: string + setActiveViewId: Dispatch> - addView(viewType: ViewType): Promise + addView(viewType: ViewType): Promise updateView(viewId: string, props: Partial): Promise deleteView(viewId: string): Promise @@ -91,17 +91,24 @@ export const DatabaseProvider = ({ databaseId, }: PropsWithChildren) => { const { Provider } = databaseContext - const [viewIndex, setViewIndex] = useState(0) const database = useDatabase(databaseId) + const [activeViewId, setActiveViewId] = useState(() => { + const view = database.views.find( + (v) => v.id === database.database.props.activeViewId, + ) + return view?.id || database.views[0].id + }) + async function reloadNodes() { const nodes = await db.listNodesBySpaceId(database.database.spaceId) store.node.setNodes(nodes) } async function addView(viewType: ViewType) { - await db.addView(databaseId, viewType) + const view = await db.addView(databaseId, viewType) reloadNodes() + return view } async function updateView( @@ -147,7 +154,7 @@ export const DatabaseProvider = ({ } async function moveColumn(fromIndex: number, toIndex: number) { - const view = database.views[viewIndex] + const view = database.views.find((v) => v.id === activeViewId)! await db.moveColumn(databaseId, view.id, fromIndex, toIndex) reloadNodes() } @@ -211,18 +218,17 @@ export const DatabaseProvider = ({ } const currentView = useMemo(() => { - const { viewIds = [] } = database.database.props - const viewId = viewIds[viewIndex] - return database.views.find((view) => view.id === viewId)! - }, [database, viewIndex]) + return database.views.find((view) => view.id === activeViewId)! + }, [database, activeViewId]) return ( = memo(function PrimaryCell(props) { - const { cell, updateCell } = props - const [value, setValue] = useState(null) - const editorRef = useRef(withCell(withReact(withHistory(createEditor())))) +interface Props extends Omit, 'column'> { + editorAtomicStyle?: string +} - const parentEditor = useEditor() +export const PrimaryCell: FC = memo( + function PrimaryCell(props) { + const { + cell, + index, + column, + width, + selected, + updateCell, + editorAtomicStyle = '', + ...rest + } = props + const [value, setValue] = useState(null) + const editorRef = useRef(withCell(withReact(withHistory(createEditor())))) - const nodeId = cell.props.ref + const parentEditor = useEditor() - useEffect(() => { - db.getNode(nodeId).then((node) => { - if (!node) { - return setValue([]) + const nodeId = cell.props.ref + + useEffect(() => { + db.getNode(nodeId).then((node) => { + if (!node) { + return setValue([]) + } + if (!isEqual(editorRef.current.children, node.element)) { + setValue(Array.isArray(node.element) ? node.element : [node.element]) + } + }) + }, [nodeId]) + + useEffect(() => { + emitter.on('REF_NODE_UPDATED', (node) => { + if (node.id === nodeId) { + if (isEqual(editorRef.current.children, node.element)) return + clearEditor(editorRef.current) + Transforms.insertNodes( + editorRef.current, + Array.isArray(node.element) ? node.element : [node.element], + ) + } + }) + return () => emitter.off('REF_NODE_UPDATED') + }, [nodeId]) + + const renderElement = useCallback((props: RenderElementProps) => { + const element = props.element as TElement + if (element.type === 'p') { + return } - if (!isEqual(editorRef.current.children, node.element)) { - setValue(Array.isArray(node.element) ? node.element : [node.element]) - } - }) - }, [nodeId]) - useEffect(() => { - emitter.on('REF_NODE_UPDATED', (node) => { - if (node.id === nodeId) { - if (isEqual(editorRef.current.children, node.element)) return - clearEditor(editorRef.current) - Transforms.insertNodes( - editorRef.current, - Array.isArray(node.element) ? node.element : [node.element], - ) + if (element.type === 'tag') { + return } - }) - return () => emitter.off('REF_NODE_UPDATED') - }, [nodeId]) - const renderElement = useCallback((props: RenderElementProps) => { - const element = props.element as TElement - if (element.type === 'p') { - return + return
{props.children}
+ }, []) + + function updateParentEditor(element: any) { + const entry = getNodeById(parentEditor, element.id) + if (!entry) return + + const [node, path] = entry + + if (!isEqual(node, element)) return + + Transforms.removeNodes(parentEditor, { at: path }) + Transforms.insertNodes(parentEditor, element, { + at: path, + select: true, + }) } - if (element.type === 'tag') { - return - } + if (!value) return null - return
{props.children}
- }, []) + return ( + + { + const element: any = value[0] - function updateParentEditor(element: any) { - const entry = getNodeById(parentEditor, element.id) - if (!entry) return - - const [node, path] = entry - - if (!isEqual(node, element)) return - - Transforms.removeNodes(parentEditor, { at: path }) - Transforms.insertNodes(parentEditor, element, { - at: path, - select: true, - }) - } - - if (!value) return null - - return ( - - { - const element: any = value[0] - - db.updateNode(nodeId, { element }) - db.updateCell(cell.id, {}) // update updatedAt - updateParentEditor(element) - }} - > - {/* */} - } - renderElement={renderElement} - onKeyDown={(e) => { - if (e.key === 'Enter') { - e.preventDefault() - } + db.updateNode(nodeId, { element }) + db.updateCell(cell.id, {}) // update updatedAt + updateParentEditor(element) }} - /> - - - ) -}) + > + {/* */} + } + renderElement={renderElement} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault() + } + }} + /> + + + ) + }, +) diff --git a/extensions/database/src/ui/ViewNav/AddViewBtn.tsx b/extensions/database/src/ui/ViewNav/AddViewBtn.tsx index 758a9941..a6de002a 100644 --- a/extensions/database/src/ui/ViewNav/AddViewBtn.tsx +++ b/extensions/database/src/ui/ViewNav/AddViewBtn.tsx @@ -20,8 +20,8 @@ function Item({ children, viewType, ...rest }: ItemProps) { const { close } = usePopoverContext() const ctx = useDatabaseContext() async function addColumn() { - await ctx.addView(viewType) - ctx.setViewIndex(ctx.views.length) + const view = await ctx.addView(viewType) + ctx.setActiveViewId(view.id) close() } diff --git a/extensions/database/src/ui/ViewNav/ViewList.tsx b/extensions/database/src/ui/ViewNav/ViewList.tsx index fa307451..f7a738a6 100644 --- a/extensions/database/src/ui/ViewNav/ViewList.tsx +++ b/extensions/database/src/ui/ViewNav/ViewList.tsx @@ -1,12 +1,14 @@ import { Box } from '@fower/react' +import { db } from '@penx/local-db' +import { IDatabaseNode } from '@penx/model-types' import { mappedByKey } from '@penx/shared' import { useDatabaseContext } from '../DatabaseContext' import { ViewIcon } from './ViewIcon' import { ViewMenu } from './ViewMenu' export const ViewList = () => { - const { views, database, viewIndex, setViewIndex } = useDatabaseContext() - + const { views, database, activeViewId, setActiveViewId } = + useDatabaseContext() const { viewIds = [] } = database.props const viewMap = mappedByKey(views, 'id') const sortedViews = viewIds.map((viewId) => viewMap[viewId]) @@ -14,7 +16,7 @@ export const ViewList = () => { return ( {sortedViews.map((view, index) => { - const active = index === viewIndex + const active = activeViewId === view.id return ( { pr={active ? 4 : 12} gray900={active} bgGray100={active} - onClick={() => setViewIndex(index)} + onClick={async () => { + setActiveViewId(view.id) + await db.updateNode(database.id, { + props: { + ...database.props, + activeViewId: view.id, + }, + }) + }} > {view.props.name} diff --git a/extensions/database/src/ui/ViewNav/ViewMenu.tsx b/extensions/database/src/ui/ViewNav/ViewMenu.tsx index cbf25c12..fb84aae2 100644 --- a/extensions/database/src/ui/ViewNav/ViewMenu.tsx +++ b/extensions/database/src/ui/ViewNav/ViewMenu.tsx @@ -37,7 +37,8 @@ export const ViewMenu = ({ view, index }: ViewMenuProps) => { function Content({ view, index }: ViewMenuProps) { const { close } = usePopoverContext() - const { updateView, deleteView, setViewIndex } = useDatabaseContext() + const { database, updateView, deleteView, views, setActiveViewId } = + useDatabaseContext() const [name, setName] = useState(view.props.name) return ( @@ -76,12 +77,12 @@ function Content({ view, index }: ViewMenuProps) { { - // if (index === 0) return + if (index === 0) return await deleteView(view.id) - setViewIndex(0) + setActiveViewId(database.props.viewIds[0]) close() }} > diff --git a/extensions/database/src/ui/views/ListView.tsx b/extensions/database/src/ui/views/ListView.tsx index 56b26f03..7182194f 100644 --- a/extensions/database/src/ui/views/ListView.tsx +++ b/extensions/database/src/ui/views/ListView.tsx @@ -1,12 +1,62 @@ -import { PropsWithChildren } from 'react' import { Box } from '@fower/react' +import { Bullet } from 'uikit' +import { db } from '@penx/local-db' +import { IRowNode } from '@penx/model-types' +import { store } from '@penx/store' +import { useDatabaseContext } from '../DatabaseContext' +import { PrimaryCell } from '../Table/Cell/PrimaryCell' -interface TableViewProps {} - -export const ListView = ({ children }: PropsWithChildren) => { +export const ListView = () => { + const { rows } = useDatabaseContext() return ( - List view, coming soon... + {rows.map((row) => ( + + ))} + + ) +} + +interface ListItemProps { + row: IRowNode +} +function ListItem({ row }: ListItemProps) { + const { cells, columns } = useDatabaseContext() + const primaryCell = cells.find( + (cell) => !!cell.props.ref && cell.props.rowId === row.id, + )! + + const column = columns.find( + (column) => column.id === primaryCell.props.columnId, + )! + + async function clickBullet() { + const node = await db.getNode(primaryCell?.props.ref!) + if (node) store.node.selectNode(node) + } + + return ( + + + + {}} + editorAtomicStyle="py-2" + flex + /> ) } diff --git a/packages/local-db/src/db.ts b/packages/local-db/src/db.ts index 94b45088..b7576c31 100644 --- a/packages/local-db/src/db.ts +++ b/packages/local-db/src/db.ts @@ -490,6 +490,7 @@ class DB { props: { color: getRandomColor(), name, + activeViewId: '', viewIds: [], }, }) @@ -543,6 +544,7 @@ class DB { await this.updateNode(database.id, { props: { ...database.props, + activeViewId: tableView.id, viewIds: [tableView.id, listView.id], }, }) diff --git a/packages/model-types/src/interfaces/INode.ts b/packages/model-types/src/interfaces/INode.ts index 670ea806..ecf0899b 100644 --- a/packages/model-types/src/interfaces/INode.ts +++ b/packages/model-types/src/interfaces/INode.ts @@ -88,6 +88,7 @@ export interface IDatabaseNode extends INode { props: { name: string // database name, same with tag name color: string + activeViewId: string viewIds: string[] } }