mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-11 05:58:56 +08:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
import {
|
||||
ConnectorElementModel,
|
||||
EdgelessTextBlockModel,
|
||||
EmbedSyncedDocModel,
|
||||
MindmapElementModel,
|
||||
NoteBlockModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import type { GfxModel } from '@blocksuite/block-std/gfx';
|
||||
import { Bound } from '@blocksuite/global/utils';
|
||||
import chunk from 'lodash.chunk';
|
||||
|
||||
const ALIGN_HEIGHT = 200;
|
||||
const ALIGN_PADDING = 20;
|
||||
|
||||
import type { Command } from '@blocksuite/block-std';
|
||||
import type { BlockModel, BlockProps } from '@blocksuite/store';
|
||||
|
||||
import { updateXYWH } from '../utils/update-xywh.js';
|
||||
|
||||
/**
|
||||
* Automatically arrange elements according to fixed row and column rules
|
||||
*/
|
||||
export const autoArrangeElementsCommand: Command<never, never, {}> = (
|
||||
ctx,
|
||||
next
|
||||
) => {
|
||||
const { updateBlock } = ctx.std.doc;
|
||||
const rootService = ctx.std.getService('affine:page');
|
||||
// @ts-expect-error TODO: fix after edgeless refactor
|
||||
const elements = rootService?.selection.selectedElements;
|
||||
// @ts-expect-error TODO: fix after edgeless refactor
|
||||
const updateElement = rootService?.updateElement;
|
||||
if (elements && updateElement) {
|
||||
autoArrangeElements(elements, updateElement, updateBlock);
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* Adjust the height of the selected element to a fixed value and arrange the elements
|
||||
*/
|
||||
export const autoResizeElementsCommand: Command<never, never, {}> = (
|
||||
ctx,
|
||||
next
|
||||
) => {
|
||||
const { updateBlock } = ctx.std.doc;
|
||||
const rootService = ctx.std.getService('affine:page');
|
||||
// @ts-expect-error TODO: fix after edgeless refactor
|
||||
const elements = rootService?.selection.selectedElements;
|
||||
// @ts-expect-error TODO: fix after edgeless refactor
|
||||
const updateElement = rootService?.updateElement;
|
||||
if (elements && updateElement) {
|
||||
autoResizeElements(elements, updateElement, updateBlock);
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
function splitElementsToChunks(models: GfxModel[]) {
|
||||
const sortByCenterX = (a: GfxModel, b: GfxModel) =>
|
||||
a.elementBound.center[0] - b.elementBound.center[0];
|
||||
const sortByCenterY = (a: GfxModel, b: GfxModel) =>
|
||||
a.elementBound.center[1] - b.elementBound.center[1];
|
||||
const elements = models.filter(ele => {
|
||||
if (
|
||||
ele instanceof ConnectorElementModel &&
|
||||
(ele.target.id || ele.source.id)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
elements.sort(sortByCenterY);
|
||||
const chunks = chunk(elements, 4);
|
||||
chunks.forEach(items => items.sort(sortByCenterX));
|
||||
return chunks;
|
||||
}
|
||||
|
||||
function autoArrangeElements(
|
||||
elements: GfxModel[],
|
||||
updateElement: (id: string, props: Record<string, unknown>) => void,
|
||||
updateBlock: (
|
||||
model: BlockModel,
|
||||
callBackOrProps: (() => void) | Partial<BlockProps>
|
||||
) => void
|
||||
) {
|
||||
const chunks = splitElementsToChunks(elements);
|
||||
// update element XY
|
||||
const startX: number = chunks[0][0].elementBound.x;
|
||||
let startY: number = chunks[0][0].elementBound.y;
|
||||
chunks.forEach(items => {
|
||||
let posX = startX;
|
||||
let maxHeight = 0;
|
||||
items.forEach(ele => {
|
||||
const { x: eleX, y: eleY } = ele.elementBound;
|
||||
const bound = Bound.deserialize(ele.xywh);
|
||||
const xOffset = bound.x - eleX;
|
||||
const yOffset = bound.y - eleY;
|
||||
bound.x = posX + xOffset;
|
||||
bound.y = startY + yOffset;
|
||||
updateXYWH(ele, bound, updateElement, updateBlock);
|
||||
if (ele.elementBound.h > maxHeight) {
|
||||
maxHeight = ele.elementBound.h;
|
||||
}
|
||||
posX += ele.elementBound.w + ALIGN_PADDING;
|
||||
});
|
||||
startY += maxHeight + ALIGN_PADDING;
|
||||
});
|
||||
}
|
||||
|
||||
function autoResizeElements(
|
||||
elements: GfxModel[],
|
||||
updateElement: (id: string, props: Record<string, unknown>) => void,
|
||||
updateBlock: (
|
||||
model: BlockModel,
|
||||
callBackOrProps: (() => void) | Partial<BlockProps>
|
||||
) => void
|
||||
) {
|
||||
// resize or scale to fixed height
|
||||
elements.forEach(ele => {
|
||||
if (
|
||||
ele instanceof ConnectorElementModel ||
|
||||
ele instanceof MindmapElementModel
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ele instanceof NoteBlockModel) {
|
||||
const curScale = ele.edgeless.scale ?? 1;
|
||||
const nextScale = curScale * (ALIGN_HEIGHT / ele.elementBound.h);
|
||||
const bound = Bound.deserialize(ele.xywh);
|
||||
bound.h = bound.h * (nextScale / curScale);
|
||||
bound.w = bound.w * (nextScale / curScale);
|
||||
updateElement(ele.id, {
|
||||
edgeless: {
|
||||
...ele.edgeless,
|
||||
scale: nextScale,
|
||||
},
|
||||
xywh: bound.serialize(),
|
||||
});
|
||||
} else if (
|
||||
ele instanceof EdgelessTextBlockModel ||
|
||||
ele instanceof EmbedSyncedDocModel
|
||||
) {
|
||||
const curScale = ele.scale ?? 1;
|
||||
const nextScale = curScale * (ALIGN_HEIGHT / ele.elementBound.h);
|
||||
const bound = Bound.deserialize(ele.xywh);
|
||||
bound.h = bound.h * (nextScale / curScale);
|
||||
bound.w = bound.w * (nextScale / curScale);
|
||||
updateElement(ele.id, {
|
||||
scale: nextScale,
|
||||
xywh: bound.serialize(),
|
||||
});
|
||||
} else {
|
||||
const bound = Bound.deserialize(ele.xywh);
|
||||
const scale = ALIGN_HEIGHT / ele.elementBound.h;
|
||||
bound.h = scale * bound.h;
|
||||
bound.w = scale * bound.w;
|
||||
updateXYWH(ele, bound, updateElement, updateBlock);
|
||||
}
|
||||
});
|
||||
|
||||
// arrange
|
||||
autoArrangeElements(elements, updateElement, updateBlock);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { BlockCommands } from '@blocksuite/block-std';
|
||||
|
||||
import {
|
||||
autoArrangeElementsCommand,
|
||||
autoResizeElementsCommand,
|
||||
} from './auto-align.js';
|
||||
import { reassociateConnectorsCommand } from './reassociate-connectors.js';
|
||||
|
||||
export const commands: BlockCommands = {
|
||||
reassociateConnectors: reassociateConnectorsCommand,
|
||||
autoArrangeElements: autoArrangeElementsCommand,
|
||||
autoResizeElements: autoResizeElementsCommand,
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import type { Command } from '@blocksuite/block-std';
|
||||
|
||||
/**
|
||||
* Re-associate bindings for block that have been converted.
|
||||
*
|
||||
* @param oldId - the old block id
|
||||
* @param newId - the new block id
|
||||
*/
|
||||
export const reassociateConnectorsCommand: Command<
|
||||
never,
|
||||
never,
|
||||
{ oldId: string; newId: string }
|
||||
> = (ctx, next) => {
|
||||
const { oldId, newId } = ctx;
|
||||
const service = ctx.std.getService('affine:surface');
|
||||
if (!oldId || !newId || !service) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
const surface = service.surface;
|
||||
const connectors = surface.getConnectors(oldId);
|
||||
for (const { id, source, target } of connectors) {
|
||||
if (source.id === oldId) {
|
||||
surface.updateElement(id, {
|
||||
source: {
|
||||
...source,
|
||||
id: newId,
|
||||
},
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (target.id === oldId) {
|
||||
surface.updateElement(id, {
|
||||
target: {
|
||||
...target,
|
||||
id: newId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
Reference in New Issue
Block a user