mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-06 11:59:51 +08:00
feat(editor): improve select perf (#15353)
maybe fix #12675 #### PR Dependency Tree * **PR #15353** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved block selection updates so selected states refresh reliably. - Corrected selected-block ordering and duplicate handling. - Improved toolbar positioning accuracy and reduced unnecessary layout recalculations. - Adjusted toolbar animation behavior for surface-based tools. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||||
|
import { watch } from '@blocksuite/global/lit';
|
||||||
import { BlockComponent, type BlockService } from '@blocksuite/std';
|
import { BlockComponent, type BlockService } from '@blocksuite/std';
|
||||||
import type { BlockModel } from '@blocksuite/store';
|
import type { BlockModel } from '@blocksuite/store';
|
||||||
import { html, nothing } from 'lit';
|
import { html, nothing } from 'lit';
|
||||||
@@ -59,7 +60,7 @@ export class CaptionedBlockComponent<
|
|||||||
: nothing}
|
: nothing}
|
||||||
${this.selectedStyle === SelectedStyle.Background
|
${this.selectedStyle === SelectedStyle.Background
|
||||||
? html`<affine-block-selection
|
? html`<affine-block-selection
|
||||||
.selected=${this.selected$.value}
|
.selected=${watch(this.selected$)}
|
||||||
></affine-block-selection>`
|
></affine-block-selection>`
|
||||||
: null}
|
: null}
|
||||||
${this.useZeroWidth && !this.store.readonly
|
${this.useZeroWidth && !this.store.readonly
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type {
|
|||||||
TextSelection,
|
TextSelection,
|
||||||
} from '@blocksuite/std';
|
} from '@blocksuite/std';
|
||||||
import { BlockComponent } 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';
|
import type { ImageSelection } from '../../selection/index.js';
|
||||||
|
|
||||||
@@ -129,35 +129,33 @@ export const getSelectedBlocksCommand: Command<
|
|||||||
dirtyResult = dirtyResult.filter(ctx.filter);
|
dirtyResult = dirtyResult.filter(ctx.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getModelPath = (el: BlockComponent) => {
|
const seen = new Set<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;
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove duplicate elements
|
// remove duplicate elements
|
||||||
const result: BlockComponent[] = dirtyResult
|
const result: BlockComponent[] = dirtyResult.filter(el => {
|
||||||
.filter((el, index) => dirtyResult.indexOf(el) === index)
|
if (seen.has(el)) return false;
|
||||||
|
seen.add(el);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.length > 1) {
|
||||||
|
const modelOrder = new Map<string, number>();
|
||||||
|
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 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;
|
if (result.length === 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,16 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setReferenceElementWithBlocks(blocks: BlockComponent[]) {
|
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
|
this.referenceElement$.value = blocks.length
|
||||||
? () => ({
|
? () => ({
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export function autoUpdatePosition(
|
|||||||
flavour: string,
|
flavour: string,
|
||||||
placement: ToolbarPlacement,
|
placement: ToolbarPlacement,
|
||||||
sideOptions: Partial<SideObject> | null,
|
sideOptions: Partial<SideObject> | null,
|
||||||
options: AutoUpdateOptions = { elementResize: false, animationFrame: true }
|
options: AutoUpdateOptions = { elementResize: false }
|
||||||
) {
|
) {
|
||||||
const isInline = flavour === 'affine:note';
|
const isInline = flavour === 'affine:note';
|
||||||
const hasSurfaceScope = flavour.includes('surface');
|
const hasSurfaceScope = flavour.includes('surface');
|
||||||
@@ -147,7 +147,7 @@ export function autoUpdatePosition(
|
|||||||
() => {
|
() => {
|
||||||
update().catch(console.error);
|
update().catch(console.error);
|
||||||
},
|
},
|
||||||
options
|
{ animationFrame: hasSurfaceScope, ...options }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user