diff --git a/blocksuite/affine/fragments/outline/src/body/outline-notice.ts b/blocksuite/affine/fragments/outline/src/body/outline-notice.ts index bdeccc161d..db9873efaa 100644 --- a/blocksuite/affine/fragments/outline/src/body/outline-notice.ts +++ b/blocksuite/affine/fragments/outline/src/body/outline-notice.ts @@ -7,7 +7,7 @@ import { effect, signal } from '@preact/signals-core'; import { html, nothing } from 'lit'; import { type TocContext, tocContext } from '../config'; -import { getNotesFromDoc } from '../utils/query'; +import { getNotesFromStore } from '../utils/query'; import * as styles from './outline-notice.css'; export const AFFINE_OUTLINE_NOTICE = 'affine-outline-notice'; @@ -31,7 +31,7 @@ export class OutlineNotice extends SignalWatcher( } const shouldShowNotice = - getNotesFromDoc(this._context.editor$.value.store, [ + getNotesFromStore(this._context.editor$.value.store, [ NoteDisplayMode.DocOnly, ]).length > 0; diff --git a/blocksuite/affine/fragments/outline/src/body/outline-panel-body.ts b/blocksuite/affine/fragments/outline/src/body/outline-panel-body.ts index 5862bbc0d2..2e934e2bad 100644 --- a/blocksuite/affine/fragments/outline/src/body/outline-panel-body.ts +++ b/blocksuite/affine/fragments/outline/src/body/outline-panel-body.ts @@ -35,7 +35,7 @@ import type { import type { NoteCardEntity, NoteDropPayload } from '../utils/drag'; import { getHeadingBlocksFromDoc, - getNotesFromDoc, + getNotesFromStore, isHeadingBlock, } from '../utils/query'; import { @@ -91,7 +91,7 @@ export class OutlinePanelBody extends SignalWatcher( return this._context.editor$.value; } - private get doc() { + private get store() { return this.editor.store; } @@ -154,11 +154,11 @@ export class OutlinePanelBody extends SignalWatcher( } private _moveSelectedNotes(insertIndex: number) { - if (!this.doc.root) return; + if (!this.store.root) return; const pageVisibleNotes = this._pageVisibleNotes$.peek(); const selected = this._allSelectedNotes$.peek(); - const children = this.doc.root.children.slice(); + const children = this.store.root.children.slice(); const noteIndex = new Map(); children.forEach((block, index) => { @@ -189,14 +189,14 @@ export class OutlinePanelBody extends SignalWatcher( const newChildren = [...leftPart, ...selected, ...rightPart]; - this.doc.updateBlock(this.doc.root, { + this.store.updateBlock(this.store.root, { children: newChildren, }); } private async _scrollToBlock(blockId: string) { // if focus title - if (blockId === this.doc.root?.id) { + if (blockId === this.store.root?.id) { this.editor.std.selection.setGroup('note', []); this.editor.std.event.active = false; focusTitle(this.editor); @@ -221,7 +221,7 @@ export class OutlinePanelBody extends SignalWatcher( const { selected, id, multiselect } = e.detail; const gfx = this.editor.std.get(GfxControllerIdentifier); const editorMode = this.editor.std.get(DocModeProvider).getEditorMode(); - const note = this.doc.getBlock(id)?.model; + const note = this.store.getBlock(id)?.model; if (!note || !matchModels(note, [NoteBlockModel])) return; // map from signal to value @@ -302,12 +302,12 @@ export class OutlinePanelBody extends SignalWatcher( return hasHeadings || this._context.enableSorting$.value; }; - this._pageVisibleNotes$.value = getNotesFromDoc(this.doc, [ + this._pageVisibleNotes$.value = getNotesFromStore(this.store, [ NoteDisplayMode.DocAndEdgeless, NoteDisplayMode.DocOnly, ]).filter(isRenderableNote); - this._edgelessOnlyNotes$.value = getNotesFromDoc(this.doc, [ + this._edgelessOnlyNotes$.value = getNotesFromStore(this.store, [ NoteDisplayMode.EdgelessOnly, ]).filter(isRenderableNote); }) @@ -379,23 +379,23 @@ export class OutlinePanelBody extends SignalWatcher( } private _renderDocTitle() { - if (!this.doc.root) return nothing; + if (!this.store.root) return nothing; const hasNotEmptyHeadings = getHeadingBlocksFromDoc( - this.doc, + this.store, [NoteDisplayMode.DocOnly, NoteDisplayMode.DocAndEdgeless], true ).length > 0; if (!hasNotEmptyHeadings) return nothing; - const rootId = this.doc.root.id; + const rootId = this.store.root.id; const active = rootId === this._activeHeadingId$.value; return html` { this._scrollToBlock(rootId).catch(console.error); }} diff --git a/blocksuite/affine/fragments/outline/src/outline-viewer.ts b/blocksuite/affine/fragments/outline/src/outline-viewer.ts index 37afeec738..c762d97ca4 100644 --- a/blocksuite/affine/fragments/outline/src/outline-viewer.ts +++ b/blocksuite/affine/fragments/outline/src/outline-viewer.ts @@ -200,6 +200,7 @@ export class OutlineViewer extends SignalWatcher( ) ); + // title update this.disposables.add( this.editor.store.workspace.meta.docMetaUpdated.subscribe(() => { this.requestUpdate(); diff --git a/blocksuite/affine/fragments/outline/src/utils/query.ts b/blocksuite/affine/fragments/outline/src/utils/query.ts index fb34595059..c5230d45ce 100644 --- a/blocksuite/affine/fragments/outline/src/utils/query.ts +++ b/blocksuite/affine/fragments/outline/src/utils/query.ts @@ -9,15 +9,15 @@ import type { BlockModel, Store } from '@blocksuite/store'; import { headingKeys } from '../config.js'; -export function getNotesFromDoc( - doc: Store, +export function getNotesFromStore( + store: Store, modes: NoteDisplayMode[] = [ NoteDisplayMode.DocAndEdgeless, NoteDisplayMode.DocOnly, NoteDisplayMode.EdgelessOnly, ] ) { - const rootModel = doc.root; + const rootModel = store.root; if (!rootModel) return []; const notes: NoteBlockModel[] = []; @@ -59,7 +59,7 @@ export function getHeadingBlocksFromNote( } export function getHeadingBlocksFromDoc( - doc: Store, + store: Store, modes: NoteDisplayMode[] = [ NoteDisplayMode.DocAndEdgeless, NoteDisplayMode.DocOnly, @@ -67,6 +67,6 @@ export function getHeadingBlocksFromDoc( ], ignoreEmpty = false ) { - const notes = getNotesFromDoc(doc, modes); + const notes = getNotesFromStore(store, modes); return notes.map(note => getHeadingBlocksFromNote(note, ignoreEmpty)).flat(); } diff --git a/packages/frontend/core/src/blocksuite/outline-viewer/index.tsx b/packages/frontend/core/src/blocksuite/outline-viewer/index.tsx index d50e808048..2eb3373f2d 100644 --- a/packages/frontend/core/src/blocksuite/outline-viewer/index.tsx +++ b/packages/frontend/core/src/blocksuite/outline-viewer/index.tsx @@ -15,31 +15,22 @@ export const EditorOutlineViewer = ({ }) => { const outlineViewerRef = useRef(null); - const onRefChange = useCallback((container: HTMLDivElement | null) => { - if (container) { - if (outlineViewerRef.current === null) { - console.error('outline viewer should be initialized'); - return; + const onRefChange = useCallback( + (container: HTMLDivElement | null) => { + if (container && editor) { + if (outlineViewerRef.current) { + outlineViewerRef.current.remove(); + } + outlineViewerRef.current = new OutlineViewer(); + outlineViewerRef.current.editor = editor; + outlineViewerRef.current.toggleOutlinePanel = openOutlinePanel ?? null; + container.append(outlineViewerRef.current); } + }, + [editor, openOutlinePanel] + ); - container.append(outlineViewerRef.current); - } - }, []); - - if (!editor || !show) return; - - if (!outlineViewerRef.current) { - outlineViewerRef.current = new OutlineViewer(); - } - if (outlineViewerRef.current.editor !== editor) { - outlineViewerRef.current.editor = editor; - } - if ( - outlineViewerRef.current.toggleOutlinePanel !== openOutlinePanel && - openOutlinePanel - ) { - outlineViewerRef.current.toggleOutlinePanel = openOutlinePanel; - } + if (!editor || !show) return null; return
; }; diff --git a/tests/affine-local/e2e/blocksuite/outline/outline-viewer.spec.ts b/tests/affine-local/e2e/blocksuite/outline/outline-viewer.spec.ts index d463df870b..6270134def 100644 --- a/tests/affine-local/e2e/blocksuite/outline/outline-viewer.spec.ts +++ b/tests/affine-local/e2e/blocksuite/outline/outline-viewer.spec.ts @@ -179,6 +179,25 @@ test('should hide edgeless-only note headings', async ({ page }) => { await expect(h1InPanel).toContainText(['Heading 1']); }); +test('outline viewer should update after change heading in edgeless mode', async ({ + page, +}) => { + await createTitle(page); + await pressEnter(page); + + await type(page, '# '); + await type(page, 'Heading 1'); + + await clickEdgelessModeButton(page); + const note = page.locator('affine-edgeless-note'); + await note.dblclick(); + await type(page, '# New Heading'); + await clickPageModeButton(page); + + const indicators = getIndicators(page); + await expect(indicators).toHaveCount(3); +}); + test('outline viewer should be useable in doc peek preview', async ({ page, }) => {