diff --git a/blocksuite/affine/components/src/caption/captioned-block-component.ts b/blocksuite/affine/components/src/caption/captioned-block-component.ts index 9c8621c0ef..3f84b4f000 100644 --- a/blocksuite/affine/components/src/caption/captioned-block-component.ts +++ b/blocksuite/affine/components/src/caption/captioned-block-component.ts @@ -1,4 +1,5 @@ import { ThemeProvider } from '@blocksuite/affine-shared/services'; +import { watch } from '@blocksuite/global/lit'; import { BlockComponent, type BlockService } from '@blocksuite/std'; import type { BlockModel } from '@blocksuite/store'; import { html, nothing } from 'lit'; @@ -59,7 +60,7 @@ export class CaptionedBlockComponent< : nothing} ${this.selectedStyle === SelectedStyle.Background ? html`` : null} ${this.useZeroWidth && !this.store.readonly diff --git a/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts b/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts index 446a30dc0a..c42ca33362 100644 --- a/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts +++ b/blocksuite/affine/shared/src/commands/block-crud/get-selected-blocks.ts @@ -5,7 +5,7 @@ import type { TextSelection, } from '@blocksuite/std'; import { BlockComponent } from '@blocksuite/std'; -import type { RoleType } from '@blocksuite/store'; +import type { BlockModel, RoleType } from '@blocksuite/store'; import type { ImageSelection } from '../../selection/index.js'; @@ -129,35 +129,33 @@ export const getSelectedBlocksCommand: Command< dirtyResult = dirtyResult.filter(ctx.filter); } - const getModelPath = (el: BlockComponent) => { - const path: number[] = []; - let model = el.model; - while (model) { - const parent = ctx.std.store.getParent(model.id); - if (!parent) break; - path.unshift(parent.children.findIndex(child => child.id === model.id)); - model = parent; - } - return path; - }; - - const compareByModelPath = (a: BlockComponent, b: BlockComponent) => { - if (a === b) return 0; - const aPath = getModelPath(a); - const bPath = getModelPath(b); - const length = Math.min(aPath.length, bPath.length); - for (let i = 0; i < length; i++) { - const diff = aPath[i] - bPath[i]; - if (diff !== 0) return diff; - } - return aPath.length - bPath.length; - }; + const seen = new Set(); // remove duplicate elements - const result: BlockComponent[] = dirtyResult - .filter((el, index) => dirtyResult.indexOf(el) === index) + const result: BlockComponent[] = dirtyResult.filter(el => { + if (seen.has(el)) return false; + seen.add(el); + return true; + }); + + if (result.length > 1) { + const modelOrder = new Map(); + const visit = (model: BlockModel) => { + modelOrder.set(model.id, modelOrder.size); + model.children.forEach(visit); + }; + const root = ctx.std.store.root; + if (root) { + visit(root); + } + // sort by model tree position, which is the order used for paste/export - .sort(compareByModelPath); + result.sort( + (a, b) => + (modelOrder.get(a.blockId) ?? Number.MAX_SAFE_INTEGER) - + (modelOrder.get(b.blockId) ?? Number.MAX_SAFE_INTEGER) + ); + } if (result.length === 0) return; diff --git a/blocksuite/affine/widgets/toolbar/src/toolbar.ts b/blocksuite/affine/widgets/toolbar/src/toolbar.ts index 8ce6201de3..7a2c4b593f 100644 --- a/blocksuite/affine/widgets/toolbar/src/toolbar.ts +++ b/blocksuite/affine/widgets/toolbar/src/toolbar.ts @@ -146,7 +146,16 @@ export class AffineToolbarWidget extends WidgetComponent { } setReferenceElementWithBlocks(blocks: BlockComponent[]) { - const getClientRects = () => blocks.map(e => e.getBoundingClientRect()); + let cachedClientRects: DOMRect[] | null = null; + const getClientRects = () => { + if (!cachedClientRects) { + cachedClientRects = blocks.map(e => e.getBoundingClientRect()); + requestAnimationFrame(() => { + cachedClientRects = null; + }); + } + return cachedClientRects; + }; this.referenceElement$.value = blocks.length ? () => ({ diff --git a/blocksuite/affine/widgets/toolbar/src/utils.ts b/blocksuite/affine/widgets/toolbar/src/utils.ts index d4688991f2..087484d17d 100644 --- a/blocksuite/affine/widgets/toolbar/src/utils.ts +++ b/blocksuite/affine/widgets/toolbar/src/utils.ts @@ -56,7 +56,7 @@ export function autoUpdatePosition( flavour: string, placement: ToolbarPlacement, sideOptions: Partial | null, - options: AutoUpdateOptions = { elementResize: false, animationFrame: true } + options: AutoUpdateOptions = { elementResize: false } ) { const isInline = flavour === 'affine:note'; const hasSurfaceScope = flavour.includes('surface'); @@ -147,7 +147,7 @@ export function autoUpdatePosition( () => { update().catch(console.error); }, - options + { animationFrame: hasSurfaceScope, ...options } ); }