mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-21 03:51:45 +08:00
25227a09f7
fix #14433 #### PR Dependency Tree * **PR #14442** 👈 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 * **New Features** * Level-of-detail thumbnails for large images. * Adaptive pacing for snapping, distribution and other alignment work. * RAF coalescer utility to batch high-frequency updates. * Operation timing utility to measure synchronous work. * **Improvements** * Batch group/ungroup reparenting that preserves element order and selection. * Coalesced panning and drag updates to reduce jitter. * Connector/group indexing for more reliable updates, deletions and sync. * Throttled viewport refresh behavior. * **Documentation** * Docs added for RAF coalescer and measureOperation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
97 lines
2.9 KiB
TypeScript
97 lines
2.9 KiB
TypeScript
import {
|
|
type SurfaceBlockModel,
|
|
type SurfaceMiddleware,
|
|
surfaceMiddlewareExtension,
|
|
} from '@blocksuite/affine-block-surface';
|
|
import type { ConnectorElementModel } from '@blocksuite/affine-model';
|
|
import type { GfxModel } from '@blocksuite/std/gfx';
|
|
|
|
import { ConnectorPathGenerator } from './connector-manager';
|
|
|
|
export const connectorWatcher: SurfaceMiddleware = (
|
|
surface: SurfaceBlockModel
|
|
) => {
|
|
const hasElementById = (id: string) =>
|
|
surface.hasElementById(id) || surface.store.hasBlock(id);
|
|
const elementGetter = (id: string) =>
|
|
surface.getElementById(id) ?? (surface.store.getModelById(id) as GfxModel);
|
|
const updateConnectorPath = (connector: ConnectorElementModel) => {
|
|
if (
|
|
((connector.source?.id && hasElementById(connector.source.id)) ||
|
|
(!connector.source?.id && connector.source?.position)) &&
|
|
((connector.target?.id && hasElementById(connector.target.id)) ||
|
|
(!connector.target?.id && connector.target?.position))
|
|
) {
|
|
ConnectorPathGenerator.updatePath(connector, null, elementGetter);
|
|
}
|
|
};
|
|
const pendingList = new Set<ConnectorElementModel>();
|
|
let pendingFlag = false;
|
|
const addToUpdateList = (connector: ConnectorElementModel) => {
|
|
pendingList.add(connector);
|
|
|
|
if (!pendingFlag) {
|
|
pendingFlag = true;
|
|
queueMicrotask(() => {
|
|
pendingList.forEach(updateConnectorPath);
|
|
pendingList.clear();
|
|
pendingFlag = false;
|
|
});
|
|
}
|
|
};
|
|
|
|
const disposables = [
|
|
surface.elementAdded.subscribe(({ id }) => {
|
|
const element = elementGetter(id);
|
|
|
|
if (!element) return;
|
|
|
|
if ('type' in element && element.type === 'connector') {
|
|
addToUpdateList(element as ConnectorElementModel);
|
|
} else {
|
|
surface.getConnectors(id).forEach(addToUpdateList);
|
|
}
|
|
}),
|
|
surface.elementUpdated.subscribe(({ id, props }) => {
|
|
const element = elementGetter(id);
|
|
|
|
if (props['xywh'] || props['rotate']) {
|
|
surface.getConnectors(id).forEach(addToUpdateList);
|
|
}
|
|
|
|
if (
|
|
'type' in element &&
|
|
element.type === 'connector' &&
|
|
(props['mode'] !== undefined || props['target'] || props['source'])
|
|
) {
|
|
addToUpdateList(element as ConnectorElementModel);
|
|
}
|
|
}),
|
|
surface.store.slots.blockUpdated.subscribe(payload => {
|
|
if (
|
|
payload.type === 'add' ||
|
|
(payload.type === 'update' && payload.props.key === 'xywh')
|
|
) {
|
|
surface.getConnectors(payload.id).forEach(addToUpdateList);
|
|
}
|
|
}),
|
|
];
|
|
|
|
surface
|
|
.getElementsByType('connector')
|
|
.forEach(connector =>
|
|
updateConnectorPath(connector as ConnectorElementModel)
|
|
);
|
|
|
|
return () => {
|
|
pendingFlag = false;
|
|
pendingList.clear();
|
|
disposables.forEach(d => d.unsubscribe());
|
|
};
|
|
};
|
|
|
|
export const connectorWatcherExtension = surfaceMiddlewareExtension(
|
|
'connector-watcher',
|
|
connectorWatcher
|
|
);
|