From da7f2a9929dfd4d48260560b257bd8f9917643da Mon Sep 17 00:00:00 2001 From: Nathan Date: Mon, 17 Nov 2025 14:52:41 +0800 Subject: [PATCH] fix: displaying linked datbaase view --- package.json | 3 +- src/components/database/Database.tsx | 44 +++++++++++++---- .../components/board/column/Column.tsx | 5 +- .../components/board/column/useCardsDrag.ts | 48 ++++++++++--------- .../database/components/board/group/Group.tsx | 13 +++++ .../blocks/database/DatabaseBlock.tsx | 7 ++- .../database/components/DatabaseContent.tsx | 4 +- .../panels/slash-panel/SlashPanel.tsx | 6 +-- 8 files changed, 88 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 7b5d2b2f..966e798b 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "dev": "vite", "dev:coverage": "cross-env COVERAGE=true vite", "build": "vite build", + "clean": "rm -rf dist storybook-static coverage .nyc_output node_modules/.vite node_modules/.cache", "type-check": "tsc --noEmit --project tsconfig.web.json", "type-check:watch": "tsc --build --watch", "lint": "pnpm type-check && eslint --quiet --ext .js,.ts,.tsx . --ignore-path .eslintignore.web", @@ -283,4 +284,4 @@ "pnpm": "^10.9.0" }, "packageManager": "pnpm@10.9.0+sha512.0486e394640d3c1fb3c9d43d49cf92879ff74f8516959c235308f5a8f62e2e19528a65cdc2a3058f587cde71eba3d5b56327c8c33a97e4c4051ca48a10ca2d5f" -} \ No newline at end of file +} diff --git a/src/components/database/Database.tsx b/src/components/database/Database.tsx index b8925c48..07dc87dc 100644 --- a/src/components/database/Database.tsx +++ b/src/components/database/Database.tsx @@ -65,18 +65,47 @@ function Database(props: Database2Props) { loadView, navigateToView, modalRowId, + isDocumentBlock, } = props; - const database = doc.getMap(YjsEditorKey.data_section)?.get(YjsEditorKey.database) as YDatabase | null; - const view = database?.get(YjsDatabaseKey.views)?.get(iidIndex); + const database = doc.getMap(YjsEditorKey.data_section)?.get(YjsEditorKey.database) as YDatabase | null; + const views = database?.get(YjsDatabaseKey.views); + + // Find view by iid field (views map uses numeric keys like "0", "1", "2") + const findViewByIid = useCallback((viewsMap: typeof views, targetIid: string) => { + if (!viewsMap) return null; + + // Try direct access first (for standalone databases) + const directView = viewsMap.get(targetIid); + if (directView) return directView; + + // Search by iid field (for embedded databases) + const viewsData = viewsMap.toJSON(); + const keys = Object.keys(viewsData); + + for (const key of keys) { + const v = viewsMap.get(key); + const viewIid = v?.get?.(YjsDatabaseKey.iid); + if (viewIid === targetIid) { + return v; + } + } + + return null; + }, []); + + const view = findViewByIid(views, iidIndex); const rowOrders = view?.get(YjsDatabaseKey.row_orders); + const [rowIds, setRowIds] = useState([]); const [rowDocMap, setRowDocMap] = useState | null>(null); const updateRowMap = useCallback(async () => { const newRowMap: Record = {}; - if (!rowIds || !createRowDoc) return; + if (!rowIds || !createRowDoc) { + return; + } const promises = rowIds.map(async (id) => { if (!id) { @@ -108,10 +137,6 @@ function Database(props: Database2Props) { void debounceUpdateRowMap(); }, [debounceUpdateRowMap]); - useEffect(() => { - console.debug('Database.tsx: database', database?.toJSON()); - console.debug('Database.tsx: rowDocMap', rowDocMap); - }, [rowDocMap, database]); const createNewRowDoc = useCallback( async (rowKey: string) => { @@ -127,7 +152,10 @@ function Database(props: Database2Props) { ); const handleUpdateRowDocMap = useCallback(async () => { - setRowIds(rowOrders?.toJSON().map(({ id }: { id: string }) => id) || []); + const rowOrdersData = rowOrders?.toJSON() || []; + const ids = rowOrdersData.map(({ id }: { id: string }) => id); + + setRowIds(ids); }, [rowOrders]); useEffect(() => { diff --git a/src/components/database/components/board/column/Column.tsx b/src/components/database/components/board/column/Column.tsx index 6845fdc0..d0720c5f 100644 --- a/src/components/database/components/board/column/Column.tsx +++ b/src/components/database/components/board/column/Column.tsx @@ -38,8 +38,7 @@ export const Column = memo( }, [rows, readOnly]); const { columnRef, headerRef, state, isDragging } = useColumnHeaderDrag(id); - const [scrollerContainer, setScrollerContainer] = React.useState(null); - const { contextValue, columnInnerRef } = useCardsDrag(id, rows, scrollerContainer); + const { contextValue, columnInnerRef } = useCardsDrag(id, rows); const getCards = useCallback( (_columnId: string): Row[] => { @@ -76,8 +75,6 @@ export const Column = memo( columnId={id} data={data} fieldId={fieldId} - setScrollElement={setScrollerContainer} - /> {state.type === StateType.IS_COLUMN_OVER && state.closestEdge && ( diff --git a/src/components/database/components/board/column/useCardsDrag.ts b/src/components/database/components/board/column/useCardsDrag.ts index c505681f..58a67aaa 100644 --- a/src/components/database/components/board/column/useCardsDrag.ts +++ b/src/components/database/components/board/column/useCardsDrag.ts @@ -1,8 +1,6 @@ import { Row, useReadOnly } from '@/application/database-yjs'; import { useBoardDragContext } from '@/components/database/components/board/drag-and-drop/board-context'; import { ColumnDragContextProps } from '@/components/database/components/board/drag-and-drop/column-context'; -import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element'; -import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'; import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -23,41 +21,45 @@ type State = { type: StateType.IDLE } | { type: StateType.IS_CARD_OVER }; const idle: State = { type: StateType.IDLE }; const isCardOver: State = { type: StateType.IS_CARD_OVER }; -export function useCardsDrag(columnId: string, rows: Row[], scrollerContainer: HTMLDivElement | null) { +export function useCardsDrag(columnId: string, rows: Row[]) { const { instanceId, registerColumn } = useBoardDragContext(); const columnInnerRef = useRef(null); const [state, setState] = useState(idle); const stableItems = useRef(rows); const readOnly = useReadOnly(); + const cleanupRef = useRef<(() => void) | null>(null); useEffect(() => { stableItems.current = rows; }, [rows]); useEffect(() => { - if (!columnInnerRef.current || !scrollerContainer || readOnly) { + if (!columnInnerRef.current || readOnly) { return; } - return combine( - dropTargetForElements({ - element: columnInnerRef.current, - getData: () => ({ columnId }), - canDrop: ({ source }) => { - return source.data.instanceId === instanceId && source.data.type === 'card'; - }, - getIsSticky: () => true, - onDragEnter: () => setState(isCardOver), - onDragLeave: () => setState(idle), - onDragStart: () => setState(isCardOver), - onDrop: () => setState(idle), - }), - autoScrollForElements({ - element: scrollerContainer, - canScroll: ({ source }) => source.data.instanceId === instanceId && source.data.type === 'card', - }) - ); - }, [readOnly, columnId, instanceId, registerColumn, scrollerContainer]); + cleanupRef.current?.(); + + const teardown = dropTargetForElements({ + element: columnInnerRef.current, + getData: () => ({ columnId }), + canDrop: ({ source }) => { + return source.data.instanceId === instanceId && source.data.type === 'card'; + }, + getIsSticky: () => true, + onDragEnter: () => setState(isCardOver), + onDragLeave: () => setState(idle), + onDragStart: () => setState(isCardOver), + onDrop: () => setState(idle), + }); + + cleanupRef.current = teardown; + + return () => { + teardown(); + cleanupRef.current = null; + }; + }, [readOnly, columnId, instanceId, registerColumn]); const getCardIndex = useCallback((rowId: string) => { return stableItems.current.findIndex((item) => item.id === rowId); diff --git a/src/components/database/components/board/group/Group.tsx b/src/components/database/components/board/group/Group.tsx index 395915f3..b0ea0cb0 100644 --- a/src/components/database/components/board/group/Group.tsx +++ b/src/components/database/components/board/group/Group.tsx @@ -13,6 +13,7 @@ import DatabaseStickyBottomOverlay from '@/components/database/components/sticky import DatabaseStickyHorizontalScrollbar from '@/components/database/components/sticky-overlay/DatabaseStickyHorizontalScrollbar'; import DatabaseStickyTopOverlay from '@/components/database/components/sticky-overlay/DatabaseStickyTopOverlay'; import { getScrollParent } from '@/components/global-comment/utils'; +import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element'; import { useNavigationKey } from './useNavigationKey'; @@ -88,6 +89,18 @@ export const Group = ({ groupId }: GroupProps) => { const innerRef = useRef(null); const stickyHeaderRef = useRef(null); + // Auto-scroll for card dragging (registered once at Group level) + useEffect(() => { + if (!verticalScrollContainer || readOnly) return; + + const cleanup = autoScrollForElements({ + element: verticalScrollContainer, + canScroll: ({ source }) => source.data.instanceId === contextValue.instanceId && source.data.type === 'card', + }); + + return cleanup; + }, [verticalScrollContainer, readOnly, contextValue.instanceId]); + useEffect(() => { const inner = innerRef.current; const columnsEl = ref.current; diff --git a/src/components/editor/components/blocks/database/DatabaseBlock.tsx b/src/components/editor/components/blocks/database/DatabaseBlock.tsx index 4321797d..fcb46585 100644 --- a/src/components/editor/components/blocks/database/DatabaseBlock.tsx +++ b/src/components/editor/components/blocks/database/DatabaseBlock.tsx @@ -14,6 +14,7 @@ import { useResizePositioning } from './hooks/useResizePositioning'; export const DatabaseBlock = memo( forwardRef>(({ node, children, ...attributes }, ref) => { const viewId = node.data.view_id; + const parentId = node.data.parent_id; const context = useEditorContext(); const workspaceId = context.workspaceId; const navigateToView = context?.navigateToView; @@ -25,8 +26,12 @@ export const DatabaseBlock = memo( const editor = useSlateStatic(); const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); + // For linked databases, load the parent view (original database) + // For inline databases, parent_id === current document, so load view_id + const viewToLoad = parentId || viewId; + const { notFound, doc, selectedViewId, visibleViewIds, iidName, onChangeView, loadViewMeta } = useDatabaseLoading({ - viewId, + viewId: viewToLoad, loadView, loadViewMeta: context?.loadViewMeta, }); diff --git a/src/components/editor/components/blocks/database/components/DatabaseContent.tsx b/src/components/editor/components/blocks/database/components/DatabaseContent.tsx index ebe07abf..a035a2e9 100644 --- a/src/components/editor/components/blocks/database/components/DatabaseContent.tsx +++ b/src/components/editor/components/blocks/database/components/DatabaseContent.tsx @@ -63,14 +63,14 @@ export const DatabaseContent = ({ workspaceId={workspaceId} doc={doc} iidIndex={viewId} - viewId={selectedViewId} + viewId={viewId} createRowDoc={createRowDoc} loadView={loadView} navigateToView={navigateToView} onOpenRowPage={onOpenRowPage} loadViewMeta={loadViewMeta} iidName={iidName} - visibleViewIds={visibleViewIds} + visibleViewIds={[viewId]} onChangeView={onChangeView} showActions={true} paddingStart={paddingStart} diff --git a/src/components/editor/components/panels/slash-panel/SlashPanel.tsx b/src/components/editor/components/panels/slash-panel/SlashPanel.tsx index de2c6830..0c3a0f82 100644 --- a/src/components/editor/components/panels/slash-panel/SlashPanel.tsx +++ b/src/components/editor/components/panels/slash-panel/SlashPanel.tsx @@ -25,9 +25,9 @@ import { } from '@/application/types'; // import { ReactComponent as AIWriterIcon } from '@/assets/slash_menu_icon_ai_writer.svg'; import { ReactComponent as EmojiIcon } from '@/assets/icons/add_emoji.svg'; -import { ReactComponent as GridIcon } from '@/assets/slash_menu_icon_grid.svg'; -import { ReactComponent as BoardIcon } from '@/assets/slash_menu_icon_kanban.svg'; -import { ReactComponent as CalendarIcon } from '@/assets/slash_menu_icon_calendar.svg'; +import { ReactComponent as GridIcon } from '@/assets/icons/grid.svg'; +import { ReactComponent as BoardIcon } from '@/assets/icons/board.svg'; +import { ReactComponent as CalendarIcon } from '@/assets/icons/calendar.svg'; import { ReactComponent as AskAIIcon } from '@/assets/icons/ai.svg'; import { ReactComponent as BulletedListIcon } from '@/assets/icons/bulleted_list.svg'; import { ReactComponent as CalloutIcon } from '@/assets/icons/callout.svg';