mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 06:18:45 +08:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
import type { Point } from '@blocksuite/global/utils';
|
||||
|
||||
export function wait(time: number = 0) {
|
||||
return new Promise(resolve => {
|
||||
requestAnimationFrame(() => {
|
||||
setTimeout(resolve, time);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* simulate click event
|
||||
* @param target
|
||||
* @param position position relative to the target
|
||||
*/
|
||||
export function click(target: HTMLElement, position: { x: number; y: number }) {
|
||||
const element = target.getBoundingClientRect();
|
||||
const clientX = element.x + position.x;
|
||||
const clientY = element.y + position.y;
|
||||
|
||||
target.dispatchEvent(
|
||||
new PointerEvent('pointerdown', {
|
||||
clientX,
|
||||
clientY,
|
||||
bubbles: true,
|
||||
pointerId: 1,
|
||||
isPrimary: true,
|
||||
})
|
||||
);
|
||||
target.dispatchEvent(
|
||||
new PointerEvent('pointerup', {
|
||||
clientX,
|
||||
clientY,
|
||||
bubbles: true,
|
||||
pointerId: 1,
|
||||
isPrimary: true,
|
||||
})
|
||||
);
|
||||
target.dispatchEvent(
|
||||
new MouseEvent('click', {
|
||||
clientX,
|
||||
clientY,
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
type PointerOptions = {
|
||||
isPrimary?: boolean;
|
||||
pointerId?: number;
|
||||
pointerType?: string;
|
||||
};
|
||||
|
||||
const defaultPointerOptions: PointerOptions = {
|
||||
isPrimary: true,
|
||||
pointerId: 1,
|
||||
pointerType: 'mouse',
|
||||
};
|
||||
|
||||
/**
|
||||
* simulate pointerdown event
|
||||
* @param target
|
||||
* @param position position relative to the target
|
||||
*/
|
||||
export function pointerdown(
|
||||
target: HTMLElement,
|
||||
position: { x: number; y: number },
|
||||
options: PointerOptions = defaultPointerOptions
|
||||
) {
|
||||
const element = target.getBoundingClientRect();
|
||||
const clientX = element.x + position.x;
|
||||
const clientY = element.y + position.y;
|
||||
|
||||
target.dispatchEvent(
|
||||
new PointerEvent('pointerdown', {
|
||||
clientX,
|
||||
clientY,
|
||||
bubbles: true,
|
||||
...options,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* simulate pointerup event
|
||||
* @param target
|
||||
* @param position position relative to the target
|
||||
*/
|
||||
export function pointerup(
|
||||
target: HTMLElement,
|
||||
position: { x: number; y: number },
|
||||
options: PointerOptions = defaultPointerOptions
|
||||
) {
|
||||
const element = target.getBoundingClientRect();
|
||||
const clientX = element.x + position.x;
|
||||
const clientY = element.y + position.y;
|
||||
|
||||
target.dispatchEvent(
|
||||
new PointerEvent('pointerup', {
|
||||
clientX,
|
||||
clientY,
|
||||
bubbles: true,
|
||||
...options,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* simulate pointermove event
|
||||
* @param target
|
||||
* @param position position relative to the target
|
||||
*/
|
||||
export function pointermove(
|
||||
target: HTMLElement,
|
||||
position: { x: number; y: number },
|
||||
options: PointerOptions = defaultPointerOptions
|
||||
) {
|
||||
const element = target.getBoundingClientRect();
|
||||
const clientX = element.x + position.x;
|
||||
const clientY = element.y + position.y;
|
||||
|
||||
target.dispatchEvent(
|
||||
new PointerEvent('pointermove', {
|
||||
clientX,
|
||||
clientY,
|
||||
bubbles: true,
|
||||
...options,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function drag(
|
||||
target: HTMLElement,
|
||||
start: { x: number; y: number },
|
||||
end: { x: number; y: number },
|
||||
step: number = 5
|
||||
) {
|
||||
pointerdown(target, start);
|
||||
pointermove(target, start);
|
||||
|
||||
if (step !== 0) {
|
||||
const xStep = (end.x - start.x) / step;
|
||||
const yStep = (end.y - start.y) / step;
|
||||
|
||||
for (const [i] of Array.from({ length: step }).entries()) {
|
||||
pointermove(target, {
|
||||
x: start.x + xStep * (i + 1),
|
||||
y: start.y + yStep * (i + 1),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pointermove(target, end);
|
||||
pointerup(target, end);
|
||||
}
|
||||
|
||||
export function multiTouchDown(target: Element, points: Point[]) {
|
||||
points.forEach((point, index) => {
|
||||
pointerdown(target as HTMLElement, point, {
|
||||
isPrimary: index === 0,
|
||||
pointerId: index,
|
||||
pointerType: 'touch',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function multiTouchMove(
|
||||
target: Element,
|
||||
from: Point[],
|
||||
to: Point[],
|
||||
step = 5
|
||||
) {
|
||||
if (from.length !== to.length) {
|
||||
throw new Error('from and to should have the same length');
|
||||
}
|
||||
|
||||
if (step !== 0) {
|
||||
for (const [i] of Array.from({ length: step }).entries()) {
|
||||
const stepPoints = from.map((point, index) => ({
|
||||
x: point.x + ((to[index].x - point.x) / step) * (i + 1),
|
||||
y: point.y + ((to[index].y - point.y) / step) * (i + 1),
|
||||
}));
|
||||
from.forEach((_, index) => {
|
||||
pointermove(target as HTMLElement, stepPoints[index], {
|
||||
isPrimary: index === 0,
|
||||
pointerId: index,
|
||||
pointerType: 'touch',
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function multiTouchUp(target: Element, points: Point[]) {
|
||||
points.forEach((point, index) => {
|
||||
pointerup(target as HTMLElement, point, {
|
||||
isPrimary: index === 0,
|
||||
pointerId: index,
|
||||
pointerType: 'touch',
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type {
|
||||
EdgelessRootBlockComponent,
|
||||
PageRootBlockComponent,
|
||||
SurfaceBlockComponent,
|
||||
} from '@blocksuite/blocks';
|
||||
import type { Doc } from '@blocksuite/store';
|
||||
|
||||
import type { AffineEditorContainer } from '../../index.js';
|
||||
|
||||
export function getSurface(doc: Doc, editor: AffineEditorContainer) {
|
||||
const surfaceModel = doc.getBlockByFlavour('affine:surface');
|
||||
|
||||
return editor.host!.view.getBlock(
|
||||
surfaceModel[0]!.id
|
||||
) as SurfaceBlockComponent;
|
||||
}
|
||||
|
||||
export function getDocRootBlock(
|
||||
doc: Doc,
|
||||
editor: AffineEditorContainer,
|
||||
mode: 'page'
|
||||
): PageRootBlockComponent;
|
||||
export function getDocRootBlock(
|
||||
doc: Doc,
|
||||
editor: AffineEditorContainer,
|
||||
mode: 'edgeless'
|
||||
): EdgelessRootBlockComponent;
|
||||
export function getDocRootBlock(
|
||||
doc: Doc,
|
||||
editor: AffineEditorContainer,
|
||||
_?: 'edgeless' | 'page'
|
||||
) {
|
||||
return editor.host!.view.getBlock(doc.root!.id) as
|
||||
| EdgelessRootBlockComponent
|
||||
| PageRootBlockComponent;
|
||||
}
|
||||
|
||||
export function addNote(doc: Doc, props: Record<string, any> = {}) {
|
||||
const noteId = doc.addBlock(
|
||||
'affine:note',
|
||||
{
|
||||
xywh: '[0, 0, 800, 100]',
|
||||
...props,
|
||||
},
|
||||
doc.root
|
||||
);
|
||||
|
||||
doc.addBlock('affine:paragraph', {}, noteId);
|
||||
|
||||
return noteId;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { replaceIdMiddleware } from '@blocksuite/blocks';
|
||||
import { type DocCollection, type DocSnapshot, Job } from '@blocksuite/store';
|
||||
|
||||
export async function importFromSnapshot(
|
||||
collection: DocCollection,
|
||||
snapshot: DocSnapshot
|
||||
) {
|
||||
const job = new Job({
|
||||
collection,
|
||||
middlewares: [replaceIdMiddleware],
|
||||
});
|
||||
|
||||
return job.snapshotToDoc(snapshot);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import { effects as blocksEffects } from '@blocksuite/blocks/effects';
|
||||
import type { BlockCollection } from '@blocksuite/store';
|
||||
|
||||
import { effects } from '../../effects.js';
|
||||
|
||||
blocksEffects();
|
||||
effects();
|
||||
|
||||
import {
|
||||
CommunityCanvasTextFonts,
|
||||
type DocMode,
|
||||
FontConfigExtension,
|
||||
} from '@blocksuite/blocks';
|
||||
import { AffineSchemas } from '@blocksuite/blocks/schemas';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import {
|
||||
DocCollection,
|
||||
IdGeneratorType,
|
||||
Schema,
|
||||
Text,
|
||||
} from '@blocksuite/store';
|
||||
|
||||
import { AffineEditorContainer } from '../../index.js';
|
||||
|
||||
function createCollectionOptions() {
|
||||
const schema = new Schema();
|
||||
const room = Math.random().toString(16).slice(2, 8);
|
||||
|
||||
schema.register(AffineSchemas);
|
||||
|
||||
const idGenerator: IdGeneratorType = IdGeneratorType.AutoIncrement; // works only in single user mode
|
||||
|
||||
return {
|
||||
id: room,
|
||||
schema,
|
||||
idGenerator,
|
||||
defaultFlags: {
|
||||
enable_synced_doc_block: true,
|
||||
enable_pie_menu: true,
|
||||
readonly: {
|
||||
'doc:home': false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function initCollection(collection: DocCollection) {
|
||||
const doc = collection.createDoc({ id: 'doc:home' });
|
||||
|
||||
doc.load(() => {
|
||||
const rootId = doc.addBlock('affine:page', {
|
||||
title: new Text(),
|
||||
});
|
||||
doc.addBlock('affine:surface', {}, rootId);
|
||||
});
|
||||
doc.resetHistory();
|
||||
}
|
||||
|
||||
async function createEditor(collection: DocCollection, mode: DocMode = 'page') {
|
||||
const app = document.createElement('div');
|
||||
const blockCollection = collection.docs.values().next().value as
|
||||
| BlockCollection
|
||||
| undefined;
|
||||
assertExists(blockCollection, 'Need to create a doc first');
|
||||
const doc = blockCollection.getDoc();
|
||||
const editor = new AffineEditorContainer();
|
||||
editor.doc = doc;
|
||||
editor.mode = mode;
|
||||
editor.pageSpecs = editor.pageSpecs.concat([
|
||||
FontConfigExtension(CommunityCanvasTextFonts),
|
||||
]);
|
||||
editor.edgelessSpecs = editor.edgelessSpecs.concat([
|
||||
FontConfigExtension(CommunityCanvasTextFonts),
|
||||
]);
|
||||
app.append(editor);
|
||||
|
||||
window.editor = editor;
|
||||
window.doc = doc;
|
||||
|
||||
app.style.width = '100%';
|
||||
app.style.height = '1280px';
|
||||
|
||||
document.body.append(app);
|
||||
await editor.updateComplete;
|
||||
return app;
|
||||
}
|
||||
|
||||
export async function setupEditor(mode: DocMode = 'page') {
|
||||
const collection = new DocCollection(createCollectionOptions());
|
||||
collection.meta.initialize();
|
||||
|
||||
window.collection = collection;
|
||||
|
||||
initCollection(collection);
|
||||
const appElement = await createEditor(collection, mode);
|
||||
|
||||
return () => {
|
||||
appElement.remove();
|
||||
cleanup();
|
||||
};
|
||||
}
|
||||
|
||||
export function cleanup() {
|
||||
window.editor.remove();
|
||||
|
||||
delete (window as any).collection;
|
||||
|
||||
delete (window as any).editor;
|
||||
|
||||
delete (window as any).doc;
|
||||
}
|
||||
Reference in New Issue
Block a user