From b9e6d36da05e7c590fb38369cfc7d0c02e0cb8dc Mon Sep 17 00:00:00 2001 From: Mirone Date: Wed, 28 Jun 2023 13:33:19 +0800 Subject: [PATCH 1/5] chore: add page block service --- packages/block-std/src/event/pointer.ts | 18 +++++---- packages/block-std/src/service/index.ts | 17 ++++++++- packages/block-std/src/store/index.ts | 22 +++++++++-- .../page-block/default/default-page-block.ts | 23 +++++------ .../default/default-page-service.ts | 38 +++++++++++++++++++ .../selection-manager/selection-manager.ts | 6 +-- .../edgeless/edgeless-page-block.ts | 16 ++++---- .../edgeless/edgeless-page-service.ts | 30 +++++++++++++++ packages/blocks/src/page-block/index.ts | 23 +++++++++++ packages/blocks/src/preset/index.ts | 16 ++------ packages/lit/src/element/block-element.ts | 25 +++++++++--- packages/lit/src/element/lit-root.ts | 2 + 12 files changed, 180 insertions(+), 56 deletions(-) create mode 100644 packages/blocks/src/page-block/default/default-page-service.ts create mode 100644 packages/blocks/src/page-block/edgeless/edgeless-page-service.ts diff --git a/packages/block-std/src/event/pointer.ts b/packages/block-std/src/event/pointer.ts index 562c2a8c560f..d4c4377bf2c5 100644 --- a/packages/block-std/src/event/pointer.ts +++ b/packages/block-std/src/event/pointer.ts @@ -1,6 +1,6 @@ import { assertExists } from '@blocksuite/global/utils'; -import { UIEventStateContext } from './base.js'; +import { UIEventState, UIEventStateContext } from './base.js'; import type { UIEventDispatcher } from './dispatcher.js'; import { PointerEventState } from './state.js'; import { isFarEnough } from './utils.js'; @@ -45,6 +45,10 @@ export class PointerControl { this._dragging = false; }; + private _createContext(event: Event, pointerState: PointerEventState) { + return UIEventStateContext.from(new UIEventState(event), pointerState); + } + private _down = (event: PointerEvent) => { if ( this._lastPointerDownEvent && @@ -72,7 +76,7 @@ export class PointerControl { this._dispatcher.run( 'pointerDown', - UIEventStateContext.from(pointerEventState) + this._createContext(event, pointerEventState) ); this._dispatcher.disposables.addFromEvent( @@ -91,7 +95,7 @@ export class PointerControl { startY: this._startY, last: this._lastDragState, }); - const context = UIEventStateContext.from(pointerEventState); + const context = this._createContext(event, pointerEventState); const run = () => { if (this._dragging) { @@ -132,12 +136,12 @@ export class PointerControl { this._dragging = true; this._dispatcher.run( 'dragStart', - UIEventStateContext.from(this._startDragState) + this._createContext(event, this._startDragState) ); } if (this._dragging) { - this._dispatcher.run('dragMove', UIEventStateContext.from(state)); + this._dispatcher.run('dragMove', this._createContext(event, state)); } }; @@ -150,7 +154,7 @@ export class PointerControl { last: this._lastDragState, }); - this._dispatcher.run('pointerMove', UIEventStateContext.from(state)); + this._dispatcher.run('pointerMove', this._createContext(event, state)); }; private _out = (event: PointerEvent) => { @@ -162,6 +166,6 @@ export class PointerControl { last: null, }); - this._dispatcher.run('pointerOut', UIEventStateContext.from(state)); + this._dispatcher.run('pointerOut', this._createContext(event, state)); }; } diff --git a/packages/block-std/src/service/index.ts b/packages/block-std/src/service/index.ts index 419195bfffc5..e28f897e18de 100644 --- a/packages/block-std/src/service/index.ts +++ b/packages/block-std/src/service/index.ts @@ -2,6 +2,7 @@ import type { BaseBlockModel } from '@blocksuite/store'; import { DisposableGroup } from '@blocksuite/store'; import type { UIEventDispatcher } from '../event/index.js'; +import type { EventName, UIEventHandler } from '../event/index.js'; export interface BlockServiceOptions { // TODO: add these @@ -19,11 +20,25 @@ export class BlockService { this.uiEventDispatcher = options.uiEventDispatcher; } + // life cycle start dispose() { this.disposables.dispose(); } - // TODO: life cycle methods + mounted() { + // do nothing + } + + unmounted() { + // do nothing + } + // life cycle end + + // event handlers start + handleEvent(name: EventName, fn: UIEventHandler) { + this.disposables.add(this.uiEventDispatcher.add(name, fn)); + } + // event handlers end } export type BlockServiceConstructor = new ( diff --git a/packages/block-std/src/store/index.ts b/packages/block-std/src/store/index.ts index d5d0292f6799..6088f58154c8 100644 --- a/packages/block-std/src/store/index.ts +++ b/packages/block-std/src/store/index.ts @@ -24,6 +24,7 @@ export class BlockStore { dispose() { this._services.forEach(service => { service.dispose(); + service.unmounted(); }); this._services.clear(); } @@ -37,28 +38,41 @@ export class BlockStore { return spec.view; } + getService(flavour: string) { + return this._services.get(flavour); + } + private _diffServices( oldSpecs: Map>, newSpecs: Map> ) { oldSpecs.forEach((oldSpec, flavour) => { - if (newSpecs.has(flavour)) { + if ( + newSpecs.has(flavour) && + newSpecs.get(flavour)?.service === oldSpec.service + ) { return; } - const service = this._services.get(oldSpec.schema.model.flavour); + const service = this._services.get(flavour); if (service) { service.dispose(); + service.unmounted(); } - this._services.delete(oldSpec.schema.model.flavour); + this._services.delete(flavour); }); newSpecs.forEach((newSpec, flavour) => { - if (oldSpecs.has(flavour) || !newSpec.service) { + if (this._services.has(flavour)) { + return; + } + + if (!newSpec.service) { return; } const service = new newSpec.service(this._serviceOptions); this._services.set(flavour, service); + service.mounted(); }); } diff --git a/packages/blocks/src/page-block/default/default-page-block.ts b/packages/blocks/src/page-block/default/default-page-block.ts index 0dcee87feaea..e5a8aca447bf 100644 --- a/packages/blocks/src/page-block/default/default-page-block.ts +++ b/packages/blocks/src/page-block/default/default-page-block.ts @@ -39,7 +39,7 @@ import type { PageBlockModel } from '../page-model.js'; import { bindHotkeys, removeHotkeys } from '../utils/bind-hotkey.js'; import { tryUpdateNoteSize } from '../utils/index.js'; import { DraggingArea } from './components.js'; -import { DefaultSelectionManager } from './selection-manager/index.js'; +import type { DefaultPageService } from './default-page-service.js'; import { createDragHandle, getAllowSelectedBlocks } from './utils.js'; export interface DefaultSelectionSlots { @@ -57,7 +57,7 @@ export interface DefaultSelectionSlots { @customElement('affine-default-page') export class DefaultPageBlockComponent - extends BlockElement + extends BlockElement implements BlockHost { static override styles = css` @@ -126,8 +126,6 @@ export class DefaultPageBlockComponent clipboard = new PageClipboard(this); - selection!: DefaultSelectionManager; - getService = getService; lastSelectionPosition: SelectionPosition = 'start'; @@ -141,6 +139,12 @@ export class DefaultPageBlockComponent mouseRoot!: HTMLElement; + get selection() { + const selection = this.service.selection; + assertExists(selection, 'Selection should be initialized before used'); + return selection; + } + @state() private _draggingArea: DOMRect | null = null; @@ -493,13 +497,7 @@ export class DefaultPageBlockComponent this.clipboard.init(this.page); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.mouseRoot = this.parentElement!; - this.selection = new DefaultSelectionManager({ - page: this.page, - mouseRoot: this.mouseRoot, - slots: this.slots, - container: this, - dispatcher: this.root.uiEventDispatcher, - }); + this.service.mountSelectionManager(this, this.slots); } override disconnectedCallback() { @@ -509,8 +507,7 @@ export class DefaultPageBlockComponent this.components.dragHandle?.remove(); removeHotkeys(); - this.selection.clear(); - this.selection.dispose(); + this.service.unmountSelectionManager(); if (this._resizeObserver) { this._resizeObserver.disconnect(); this._resizeObserver = null; diff --git a/packages/blocks/src/page-block/default/default-page-service.ts b/packages/blocks/src/page-block/default/default-page-service.ts new file mode 100644 index 000000000000..a842311c2332 --- /dev/null +++ b/packages/blocks/src/page-block/default/default-page-service.ts @@ -0,0 +1,38 @@ +import { BlockService } from '@blocksuite/block-std'; + +import type { PageBlockModel } from '../page-model.js'; +import type { + DefaultPageBlockComponent, + DefaultSelectionSlots, +} from './default-page-block.js'; +import { DefaultSelectionManager } from './selection-manager/index.js'; + +export class DefaultPageService extends BlockService { + selection: DefaultSelectionManager | null = null; + + mountSelectionManager( + container: DefaultPageBlockComponent, + slots: DefaultSelectionSlots + ) { + if (this.selection) { + console.error('selection manager already exists'); + return; + } + this.selection = new DefaultSelectionManager({ + slots, + container, + dispatcher: this.uiEventDispatcher, + }); + } + + unmountSelectionManager() { + if (!this.selection) { + console.error('selection manager does not exist'); + return; + } + + this.selection.clear(); + this.selection.dispose(); + this.selection = null; + } +} diff --git a/packages/blocks/src/page-block/default/selection-manager/selection-manager.ts b/packages/blocks/src/page-block/default/selection-manager/selection-manager.ts index d87da576d932..b8f8673d36e2 100644 --- a/packages/blocks/src/page-block/default/selection-manager/selection-manager.ts +++ b/packages/blocks/src/page-block/default/selection-manager/selection-manager.ts @@ -9,7 +9,7 @@ import type { import { PAGE_BLOCK_CHILD_PADDING } from '@blocksuite/global/config'; import { assertExists, matchFlavours } from '@blocksuite/global/utils'; import type { FocusContext } from '@blocksuite/lit'; -import { type BaseBlockModel, type Page } from '@blocksuite/store'; +import { type BaseBlockModel } from '@blocksuite/store'; import { AbstractSelectionManager, @@ -84,14 +84,10 @@ export class DefaultSelectionManager extends AbstractSelectionManager + extends BlockElement implements EdgelessContainer, BlockHost { static override styles = css` @@ -229,7 +229,11 @@ export class EdgelessPageBlockComponent getService = getService; - selection!: EdgelessSelectionManager; + get selection() { + const selection = this.service.selection; + assertExists(selection, 'Selection should be initialized before used'); + return selection; + } snap!: EdgelessSnapManager; @@ -823,10 +827,7 @@ export class EdgelessPageBlockComponent override update(changedProperties: Map) { if (changedProperties.has('page')) { this._initSurface(); - this.selection = new EdgelessSelectionManager( - this, - this.root.uiEventDispatcher - ); + this.service.mountSelectionManager(this); this.snap = new EdgelessSnapManager(this); } if (changedProperties.has('edgelessTool')) { @@ -953,6 +954,7 @@ export class EdgelessPageBlockComponent this._resizeObserver.disconnect(); this._resizeObserver = null; } + this.service.unmountSelectionManager(); } override render() { diff --git a/packages/blocks/src/page-block/edgeless/edgeless-page-service.ts b/packages/blocks/src/page-block/edgeless/edgeless-page-service.ts new file mode 100644 index 000000000000..8f1c2a812b24 --- /dev/null +++ b/packages/blocks/src/page-block/edgeless/edgeless-page-service.ts @@ -0,0 +1,30 @@ +import { BlockService } from '@blocksuite/block-std'; + +import type { PageBlockModel } from '../page-model.js'; +import type { EdgelessPageBlockComponent } from './edgeless-page-block.js'; +import { EdgelessSelectionManager } from './utils/selection-manager.js'; + +export class EdgelessPageService extends BlockService { + selection: EdgelessSelectionManager | null = null; + + mountSelectionManager(container: EdgelessPageBlockComponent) { + if (this.selection) { + this.unmountSelectionManager(); + return; + } + this.selection = new EdgelessSelectionManager( + container, + this.uiEventDispatcher + ); + } + + unmountSelectionManager() { + if (!this.selection) { + return; + } + + this.selection.clear(); + this.selection.dispose(); + this.selection = null; + } +} diff --git a/packages/blocks/src/page-block/index.ts b/packages/blocks/src/page-block/index.ts index 4d0002b6d2fd..30ead240d542 100644 --- a/packages/blocks/src/page-block/index.ts +++ b/packages/blocks/src/page-block/index.ts @@ -1,4 +1,11 @@ import './default/backlink-popover.js'; + +import type { LitBlockSpec } from '@blocksuite/lit'; +import { literal } from 'lit/static-html.js'; + +import { DefaultPageService } from './default/default-page-service.js'; +import { EdgelessPageService } from './edgeless/edgeless-page-service.js'; +import { PageBlockSchema } from './page-model.js'; export * from './default/default-page-block.js'; export { getAllowSelectedBlocks } from './default/utils.js'; export { @@ -9,3 +16,19 @@ export * from './edgeless/edgeless-page-block.js'; export { type PageBlockModel, PageBlockSchema } from './page-model.js'; export * from './page-service.js'; export * from './utils/index.js'; + +export const pageBlockSpec: LitBlockSpec = { + schema: PageBlockSchema, + service: DefaultPageService, + view: { + component: literal`affine-default-page`, + }, +}; + +export const edgelessBlockSpec: LitBlockSpec = { + schema: PageBlockSchema, + service: EdgelessPageService, + view: { + component: literal`affine-edgeless-page`, + }, +}; diff --git a/packages/blocks/src/preset/index.ts b/packages/blocks/src/preset/index.ts index 02232cede198..d305078cb6b8 100644 --- a/packages/blocks/src/preset/index.ts +++ b/packages/blocks/src/preset/index.ts @@ -8,17 +8,12 @@ import { DividerBlockSchema } from '../divider-block/divider-model.js'; import { ImageBlockSchema } from '../image-block/image-model.js'; import { ListBlockSchema } from '../list-block/list-model.js'; import { NoteBlockSchema } from '../note-block/note-model.js'; -import { PageBlockSchema } from '../page-block/page-model.js'; +import { edgelessBlockSpec, pageBlockSpec } from '../page-block/index.js'; import { ParagraphBlockSchema } from '../paragraph-block/paragraph-model.js'; import { SurfaceBlockSchema } from '../surface-block/surface-model.js'; export const pagePreset: LitBlockSpec[] = [ - { - schema: PageBlockSchema, - view: { - component: literal`affine-default-page`, - }, - }, + pageBlockSpec, { schema: SurfaceBlockSchema, view: { @@ -76,12 +71,7 @@ export const pagePreset: LitBlockSpec[] = [ ]; export const edgelessPreset: LitBlockSpec[] = [ - { - schema: PageBlockSchema, - view: { - component: literal`affine-edgeless-page`, - }, - }, + edgelessBlockSpec, { schema: SurfaceBlockSchema, view: { diff --git a/packages/lit/src/element/block-element.ts b/packages/lit/src/element/block-element.ts index 789001f1bfd0..a988b443d9cc 100644 --- a/packages/lit/src/element/block-element.ts +++ b/packages/lit/src/element/block-element.ts @@ -1,3 +1,4 @@ +import type { BlockService } from '@blocksuite/block-std'; import type { BaseBlockModel } from '@blocksuite/store'; import type { Page } from '@blocksuite/store'; import type { TemplateResult } from 'lit'; @@ -7,7 +8,11 @@ import { WithDisposable } from '../width-disposable.js'; import type { BlockSuiteRoot } from './lit-root.js'; import { ShadowlessElement } from './shadowless-element.js'; -export type FocusContext = ( +// TODO: remove this +export type FocusContext< + Model extends BaseBlockModel = BaseBlockModel, + Service extends BlockService = BlockService +> = ( | { multi?: false; } @@ -42,9 +47,11 @@ export type FocusContext = ( } ); -export class BlockElement extends WithDisposable( - ShadowlessElement -) { +export class BlockElement< + Model extends BaseBlockModel, + Service extends BlockService = BlockService, + FocusCtx extends FocusContext = FocusContext +> extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) root!: BlockSuiteRoot; @@ -59,12 +66,18 @@ export class BlockElement extends WithDisposable( @property({ attribute: false }) page!: Page; - focusBlock(focusContext: FocusContext): boolean { + + @property({ attribute: false }) + service!: Service; + + // TODO: remove this + focusBlock(focusContext: FocusCtx): boolean { // Return false to prevent default focus behavior return true; } - blurBlock(focusContext: FocusContext): boolean { + // TODO: remove this + blurBlock(focusContext: FocusCtx): boolean { // Return false to prevent default focus behavior return true; } diff --git a/packages/lit/src/element/lit-root.ts b/packages/lit/src/element/lit-root.ts index 3f804a255501..455d08befdde 100644 --- a/packages/lit/src/element/lit-root.ts +++ b/packages/lit/src/element/lit-root.ts @@ -82,6 +82,7 @@ export class BlockSuiteRoot extends ShadowlessElement { return html`<${widget} .root=${this} .model=${model}>`; })}` : html`${nothing}`; + const service = this.blockStore.getService(flavour); this._onLoadModel(model); @@ -90,6 +91,7 @@ export class BlockSuiteRoot extends ShadowlessElement { .root=${this} .page=${this.page} .model=${model} + .service=${service} .widgets=${widgets} .content=${html`${repeat( children, From 4db273d50564ae9111011a8b99470a575e79ca03 Mon Sep 17 00:00:00 2001 From: Mirone Date: Wed, 28 Jun 2023 13:54:52 +0800 Subject: [PATCH 2/5] chore: fix e2e --- .../page-block/default/default-page-block.ts | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/blocks/src/page-block/default/default-page-block.ts b/packages/blocks/src/page-block/default/default-page-block.ts index e5a8aca447bf..9343996bf26e 100644 --- a/packages/blocks/src/page-block/default/default-page-block.ts +++ b/packages/blocks/src/page-block/default/default-page-block.ts @@ -428,13 +428,19 @@ export class DefaultPageBlockComponent private _initSlotEffects() { const { slots } = this; - slots.draggingAreaUpdated.on(rect => { - this._draggingArea = rect; - }); - slots.selectedRectsUpdated.on(rects => { - this._selectedRects = rects; - }); - this.model.childrenUpdated.on(() => this.requestUpdate()); + this._disposables.add( + slots.draggingAreaUpdated.on(rect => { + this._draggingArea = rect; + }) + ); + this._disposables.add( + slots.selectedRectsUpdated.on(rects => { + this._selectedRects = rects; + }) + ); + this._disposables.add( + this.model.childrenUpdated.on(() => this.requestUpdate()) + ); } private _initNoteSizeEffect() { @@ -514,11 +520,12 @@ export class DefaultPageBlockComponent } this.mouseRoot.removeEventListener('wheel', this._onWheel); this.viewportElement.removeEventListener('scroll', this._onScroll); + this._disposables.dispose(); } override render() { requestAnimationFrame(() => { - this.selection.refreshRemoteSelection(); + this.service.selection?.refreshRemoteSelection(); }); const { selection } = this; From 853a696929944e8f5f3d7a44eeffb9a57f312a14 Mon Sep 17 00:00:00 2001 From: Mirone Date: Wed, 28 Jun 2023 14:25:39 +0800 Subject: [PATCH 3/5] chore: fix e2e --- .../page-block/default/default-page-block.ts | 20 ++++++++++--------- .../default/default-page-service.ts | 5 +++++ packages/lit/src/element/block-element.ts | 7 +++++-- packages/lit/src/element/lit-root.ts | 2 -- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/blocks/src/page-block/default/default-page-block.ts b/packages/blocks/src/page-block/default/default-page-block.ts index 9343996bf26e..e916de236126 100644 --- a/packages/blocks/src/page-block/default/default-page-block.ts +++ b/packages/blocks/src/page-block/default/default-page-block.ts @@ -140,9 +140,7 @@ export class DefaultPageBlockComponent mouseRoot!: HTMLElement; get selection() { - const selection = this.service.selection; - assertExists(selection, 'Selection should be initialized before used'); - return selection; + return this.service?.selection; } @state() @@ -184,6 +182,7 @@ export class DefaultPageBlockComponent get innerRect() { const { left, width } = this.pageBlockContainer.getBoundingClientRect(); + assertExists(this.selection); const { clientHeight, top } = this.selection.state.viewport; return Rect.fromLWTH( left, @@ -294,6 +293,7 @@ export class DefaultPageBlockComponent // TODO: disable it on scroll's threshold private _onWheel = (e: WheelEvent) => { + assertExists(this.selection); const { selection } = this; const { state } = selection; const { type, viewport } = state; @@ -335,6 +335,7 @@ export class DefaultPageBlockComponent }; private _onScroll = (e: Event) => { + assertExists(this.selection); const { selection } = this; const { type, viewport } = selection.state; const { scrollLeft, scrollTop } = e.target as Element; @@ -456,8 +457,8 @@ export class DefaultPageBlockComponent (entries: ResizeObserverEntry[]) => { for (const { target } of entries) { if (target === this.viewportElement) { - this.selection.updateViewport(); - this.selection.updateRects(); + this.selection?.updateViewport(); + this.selection?.updateRects(); break; } } @@ -482,6 +483,7 @@ export class DefaultPageBlockComponent ); this._disposables.add(() => hotkey.deleteScope(scope)); hotkey.withScope(scope, () => { + assertExists(selection); bindHotkeys(page, selection); }); hotkey.enableHotkey(); @@ -503,7 +505,7 @@ export class DefaultPageBlockComponent this.clipboard.init(this.page); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.mouseRoot = this.parentElement!; - this.service.mountSelectionManager(this, this.slots); + this.service?.mountSelectionManager(this, this.slots); } override disconnectedCallback() { @@ -513,7 +515,7 @@ export class DefaultPageBlockComponent this.components.dragHandle?.remove(); removeHotkeys(); - this.service.unmountSelectionManager(); + this.service?.unmountSelectionManager(); if (this._resizeObserver) { this._resizeObserver.disconnect(); this._resizeObserver = null; @@ -525,11 +527,11 @@ export class DefaultPageBlockComponent override render() { requestAnimationFrame(() => { - this.service.selection?.refreshRemoteSelection(); + this.service?.selection?.refreshRemoteSelection(); }); const { selection } = this; - const { viewportOffset } = selection.state; + const viewportOffset = selection?.state.viewportOffset; const draggingArea = DraggingArea(this._draggingArea); const isEmpty = diff --git a/packages/blocks/src/page-block/default/default-page-service.ts b/packages/blocks/src/page-block/default/default-page-service.ts index a842311c2332..a716263a093e 100644 --- a/packages/blocks/src/page-block/default/default-page-service.ts +++ b/packages/blocks/src/page-block/default/default-page-service.ts @@ -35,4 +35,9 @@ export class DefaultPageService extends BlockService { this.selection.dispose(); this.selection = null; } + + override unmounted() { + super.unmounted(); + this.unmountSelectionManager(); + } } diff --git a/packages/lit/src/element/block-element.ts b/packages/lit/src/element/block-element.ts index a988b443d9cc..4ef5425208a5 100644 --- a/packages/lit/src/element/block-element.ts +++ b/packages/lit/src/element/block-element.ts @@ -67,8 +67,11 @@ export class BlockElement< @property({ attribute: false }) page!: Page; - @property({ attribute: false }) - service!: Service; + get service(): Service | undefined { + return this.root.blockStore.getService(this.model.flavour) as + | Service + | undefined; + } // TODO: remove this focusBlock(focusContext: FocusCtx): boolean { diff --git a/packages/lit/src/element/lit-root.ts b/packages/lit/src/element/lit-root.ts index 455d08befdde..3f804a255501 100644 --- a/packages/lit/src/element/lit-root.ts +++ b/packages/lit/src/element/lit-root.ts @@ -82,7 +82,6 @@ export class BlockSuiteRoot extends ShadowlessElement { return html`<${widget} .root=${this} .model=${model}>`; })}` : html`${nothing}`; - const service = this.blockStore.getService(flavour); this._onLoadModel(model); @@ -91,7 +90,6 @@ export class BlockSuiteRoot extends ShadowlessElement { .root=${this} .page=${this.page} .model=${model} - .service=${service} .widgets=${widgets} .content=${html`${repeat( children, From a841e7d0307c4a27d2fda6c44c5f35b13d444844 Mon Sep 17 00:00:00 2001 From: Mirone Date: Wed, 28 Jun 2023 14:35:24 +0800 Subject: [PATCH 4/5] chore: fix lint --- packages/blocks/src/__internal__/utils/block-range.ts | 3 +++ packages/blocks/src/__internal__/utils/selection.ts | 4 +++- packages/blocks/src/list-block/list-block.ts | 2 +- packages/blocks/src/page-block/default/utils.ts | 11 ++++++++--- .../src/page-block/edgeless/edgeless-page-block.ts | 6 +++--- .../src/page-block/utils/container-operations.ts | 4 ++-- packages/editor/src/components/editor-container.ts | 2 +- packages/editor/src/utils/editor.ts | 2 +- 8 files changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/blocks/src/__internal__/utils/block-range.ts b/packages/blocks/src/__internal__/utils/block-range.ts index 3caa73ee5aa2..078aa3695c67 100644 --- a/packages/blocks/src/__internal__/utils/block-range.ts +++ b/packages/blocks/src/__internal__/utils/block-range.ts @@ -59,6 +59,7 @@ export function getCurrentBlockRange(page: Page) { // check exist block selection const pageBlock = getDefaultPage(page); if (pageBlock) { + assertExists(pageBlock.selection); const selectedBlocks = pageBlock.selection.state.selectedBlocks; // Add embeds block to fix click image and delete case const selectedEmbeds = pageBlock.selection.state.selectedEmbed; @@ -193,6 +194,7 @@ export function restoreSelection(blockRange: BlockRange | ExtendBlockRange) { // In the default mode if (defaultPageBlock) { + assertExists(defaultPageBlock.selection); defaultPageBlock.selection.state.clearBlockSelection(); defaultPageBlock.selection.state.type = 'native'; } @@ -202,6 +204,7 @@ export function restoreSelection(blockRange: BlockRange | ExtendBlockRange) { if (blockRange.type === 'Block') { // In the default mode if (defaultPageBlock) { + assertExists(defaultPageBlock.selection); defaultPageBlock.selection.state.type = 'block'; defaultPageBlock.selection.refreshSelectedBlocksRectsByModels( blockRange.models diff --git a/packages/blocks/src/__internal__/utils/selection.ts b/packages/blocks/src/__internal__/utils/selection.ts index bc1e843a97a4..37ddce85983e 100644 --- a/packages/blocks/src/__internal__/utils/selection.ts +++ b/packages/blocks/src/__internal__/utils/selection.ts @@ -172,6 +172,7 @@ export function focusBlockByModel( 'affine:bookmark', ]) ) { + assertExists(pageBlock.selection); pageBlock.selection.state.clearSelection(); const rect = getBlockElementByModel(model)?.getBoundingClientRect(); rect && pageBlock.slots.selectedRectsUpdated.emit([rect]); @@ -196,6 +197,7 @@ export function focusBlockByModel( const editableContainer = element?.querySelector('[contenteditable]'); if (editableContainer) { if (isPageMode) { + assertExists(pageBlock.selection); pageBlock.selection.state.clearSelection(); pageBlock.selection.setFocusedBlock(element, { type: 'UNKNOWN' }); } @@ -258,7 +260,7 @@ export function resetNativeSelection(range: Range | null) { export function clearSelection(page: Page) { if (!page.root) return; - getPageBlock(page.root)?.selection.clear(); + getPageBlock(page.root)?.selection?.clear(); } /** diff --git a/packages/blocks/src/list-block/list-block.ts b/packages/blocks/src/list-block/list-block.ts index 527f14c15afe..eba30a0caa5f 100644 --- a/packages/blocks/src/list-block/list-block.ts +++ b/packages/blocks/src/list-block/list-block.ts @@ -89,7 +89,7 @@ export class ListBlockComponent extends BlockElement { const pageBlock = getPageBlock(this.model); assertExists(pageBlock); if (pageBlock instanceof DefaultPageBlockComponent) { - pageBlock.selection.selectOneBlock(this); + pageBlock.selection?.selectOneBlock(this); } } diff --git a/packages/blocks/src/page-block/default/utils.ts b/packages/blocks/src/page-block/default/utils.ts index 37aef5683343..dff3acfaf6bd 100644 --- a/packages/blocks/src/page-block/default/utils.ts +++ b/packages/blocks/src/page-block/default/utils.ts @@ -534,14 +534,14 @@ export function createDragHandle(pageBlock: DefaultPageBlockComponent) { } if (parent && matchFlavours(parent, ['affine:database'])) { - pageBlock.selection.clear(); + pageBlock.selection?.clear(); return; } // update selection rects // block may change its flavour after moved. requestAnimationFrame(() => { - pageBlock.selection.setSelectedBlocks( + pageBlock.selection?.setSelectedBlocks( blockElements .map(b => getBlockElementById(b.model.id)) .filter((b): b is BlockComponentElement => !!b) @@ -550,6 +550,7 @@ export function createDragHandle(pageBlock: DefaultPageBlockComponent) { }); }, setDragType(dragging: boolean) { + assertExists(pageBlock.selection); pageBlock.selection.state.type = dragging ? 'block:drag' : 'block'; }, setSelectedBlock(modelState: EditingState | null, element) { @@ -572,9 +573,13 @@ export function createDragHandle(pageBlock: DefaultPageBlockComponent) { return; } } - pageBlock.selection.selectOneBlock(modelState?.element, modelState?.rect); + pageBlock.selection?.selectOneBlock( + modelState?.element, + modelState?.rect + ); }, getSelectedBlocks() { + assertExists(pageBlock.selection); return pageBlock.selection.state.selectedBlocks; }, getClosestBlockElement(point: Point) { diff --git a/packages/blocks/src/page-block/edgeless/edgeless-page-block.ts b/packages/blocks/src/page-block/edgeless/edgeless-page-block.ts index 1581a4758453..5ed572a8caa9 100644 --- a/packages/blocks/src/page-block/edgeless/edgeless-page-block.ts +++ b/packages/blocks/src/page-block/edgeless/edgeless-page-block.ts @@ -230,7 +230,7 @@ export class EdgelessPageBlockComponent getService = getService; get selection() { - const selection = this.service.selection; + const selection = this.service?.selection; assertExists(selection, 'Selection should be initialized before used'); return selection; } @@ -827,7 +827,7 @@ export class EdgelessPageBlockComponent override update(changedProperties: Map) { if (changedProperties.has('page')) { this._initSurface(); - this.service.mountSelectionManager(this); + this.service?.mountSelectionManager(this); this.snap = new EdgelessSnapManager(this); } if (changedProperties.has('edgelessTool')) { @@ -954,7 +954,7 @@ export class EdgelessPageBlockComponent this._resizeObserver.disconnect(); this._resizeObserver = null; } - this.service.unmountSelectionManager(); + this.service?.unmountSelectionManager(); } override render() { diff --git a/packages/blocks/src/page-block/utils/container-operations.ts b/packages/blocks/src/page-block/utils/container-operations.ts index 0190ea133ca9..98764c040928 100644 --- a/packages/blocks/src/page-block/utils/container-operations.ts +++ b/packages/blocks/src/page-block/utils/container-operations.ts @@ -72,7 +72,7 @@ export function handleBlockSelectionBatchDelete( // In the edgeless mode return null; } - defaultPageBlock.selection.clear(); + defaultPageBlock.selection?.clear(); asyncFocusRichText(page, id); return newBlock; } @@ -536,7 +536,7 @@ export function handleKeydownAfterSelectBlocks({ requestAnimationFrame(() => { const defaultPage = getDefaultPage(page); const newBlock = page.getBlockById(id) as BaseBlockModel; - defaultPage?.selection.clear(); + defaultPage?.selection?.clear(); focusBlockByModel(newBlock, 'end'); // XXX: slash menu trigger probably shouldn't be here diff --git a/packages/editor/src/components/editor-container.ts b/packages/editor/src/components/editor-container.ts index d7ca661afdde..444df1f3c509 100644 --- a/packages/editor/src/components/editor-container.ts +++ b/packages/editor/src/components/editor-container.ts @@ -86,7 +86,7 @@ export class EditorContainer if (!pageModel) return; if (this.mode === 'page') { - getPageBlock(pageModel)?.selection.clear(); + getPageBlock(pageModel)?.selection?.clear(); } const selection = getSelection(); diff --git a/packages/editor/src/utils/editor.ts b/packages/editor/src/utils/editor.ts index 7fa33b7e1a69..3fe520b73a40 100644 --- a/packages/editor/src/utils/editor.ts +++ b/packages/editor/src/utils/editor.ts @@ -165,7 +165,7 @@ export const createBlockHub: ( if (editor.mode === 'page') { const defaultPageBlock = editor.querySelector('affine-default-page'); assertExists(defaultPageBlock); - defaultPageBlock.selection.clear(); + defaultPageBlock.selection?.clear(); } }, getAllowedBlocks: () => { From 7d80b91c45305ce4a2e488b01215dbf27eab3b9b Mon Sep 17 00:00:00 2001 From: Mirone Date: Wed, 28 Jun 2023 14:43:01 +0800 Subject: [PATCH 5/5] chore: fix e2e --- .../blocks/src/page-block/default/default-page-service.ts | 3 +-- .../blocks/src/page-block/edgeless/edgeless-page-service.ts | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/blocks/src/page-block/default/default-page-service.ts b/packages/blocks/src/page-block/default/default-page-service.ts index a716263a093e..5060df7dcd18 100644 --- a/packages/blocks/src/page-block/default/default-page-service.ts +++ b/packages/blocks/src/page-block/default/default-page-service.ts @@ -15,7 +15,7 @@ export class DefaultPageService extends BlockService { slots: DefaultSelectionSlots ) { if (this.selection) { - console.error('selection manager already exists'); + this.unmountSelectionManager(); return; } this.selection = new DefaultSelectionManager({ @@ -27,7 +27,6 @@ export class DefaultPageService extends BlockService { unmountSelectionManager() { if (!this.selection) { - console.error('selection manager does not exist'); return; } diff --git a/packages/blocks/src/page-block/edgeless/edgeless-page-service.ts b/packages/blocks/src/page-block/edgeless/edgeless-page-service.ts index 8f1c2a812b24..13ea345ba1d1 100644 --- a/packages/blocks/src/page-block/edgeless/edgeless-page-service.ts +++ b/packages/blocks/src/page-block/edgeless/edgeless-page-service.ts @@ -27,4 +27,9 @@ export class EdgelessPageService extends BlockService { this.selection.dispose(); this.selection = null; } + + override unmounted() { + super.unmounted(); + this.unmountSelectionManager(); + } }