feat(editor): replace slot with rxjs subject (#10768)

This commit is contained in:
Mirone
2025-03-12 11:29:24 +09:00
committed by GitHub
parent 19f978d9aa
commit cd63e0ed8b
302 changed files with 1405 additions and 1251 deletions
@@ -1,10 +1,10 @@
import { DisposableGroup } from '@blocksuite/global/disposable';
import {
Bound,
getCommonBound,
getCommonBoundWithRotation,
type IBound,
} from '@blocksuite/global/gfx';
import { DisposableGroup } from '@blocksuite/global/slot';
import { assertType } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store';
import { Signal } from '@preact/signals-core';
@@ -1,10 +1,10 @@
import { DisposableGroup } from '@blocksuite/global/disposable';
import type { IBound } from '@blocksuite/global/gfx';
import {
Bound,
getBoundWithRotation,
intersects,
} from '@blocksuite/global/gfx';
import { DisposableGroup } from '@blocksuite/global/slot';
import type { BlockModel } from '@blocksuite/store';
import { compare } from '../utils/layer.js';
@@ -385,7 +385,7 @@ export class GridManager extends GfxExtension {
};
disposables.add(
store.slots.blockUpdated.on(payload => {
store.slots.blockUpdated.subscribe(payload => {
if (payload.type === 'add' && canBeRenderedAsGfxBlock(payload.model)) {
this.add(payload.model);
}
@@ -441,19 +441,19 @@ export class GridManager extends GfxExtension {
);
disposables.add(
surface.elementAdded.on(payload => {
surface.elementAdded.subscribe(payload => {
this.add(surface.getElementById(payload.id)!);
})
);
disposables.add(
surface.elementRemoved.on(payload => {
surface.elementRemoved.subscribe(payload => {
this.remove(payload.model);
})
);
disposables.add(
surface.elementUpdated.on(payload => {
surface.elementUpdated.subscribe(payload => {
if (
payload.props['xywh'] ||
payload.props['externalXYWH'] ||
@@ -465,13 +465,13 @@ export class GridManager extends GfxExtension {
);
disposables.add(
surface.localElementAdded.on(elm => {
surface.localElementAdded.subscribe(elm => {
this.add(elm);
})
);
disposables.add(
surface.localElementUpdated.on(payload => {
surface.localElementUpdated.subscribe(payload => {
if (payload.props['xywh'] || payload.props['responseExtension']) {
this.update(payload.model);
}
@@ -479,7 +479,7 @@ export class GridManager extends GfxExtension {
);
disposables.add(
surface.localElementDeleted.on(elm => {
surface.localElementDeleted.subscribe(elm => {
this.remove(elm);
})
);
@@ -1,4 +1,4 @@
import { DisposableGroup } from '@blocksuite/global/slot';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { Signal } from '@preact/signals-core';
import type { BlockStdScope } from '../scope/block-std-scope.js';
+15 -14
View File
@@ -1,8 +1,9 @@
import { DisposableGroup } from '@blocksuite/global/disposable';
import { Bound } from '@blocksuite/global/gfx';
import { DisposableGroup, Slot } from '@blocksuite/global/slot';
import { assertType } from '@blocksuite/global/utils';
import { generateKeyBetween } from 'fractional-indexing';
import last from 'lodash-es/last';
import { Subject } from 'rxjs';
import {
compare,
@@ -101,7 +102,7 @@ export class LayerManager extends GfxExtension {
layers: Layer[] = [];
slots = {
layerUpdated: new Slot<{
layerUpdated: new Subject<{
type: 'delete' | 'add' | 'update';
initiatingElement: GfxModel | GfxLocalElementModel;
}>(),
@@ -588,7 +589,7 @@ export class LayerManager extends GfxExtension {
element.childElements.forEach(child => child && this._updateLayer(child));
}
this._buildCanvasLayers();
this.slots.layerUpdated.emit({
this.slots.layerUpdated.next({
type: 'add',
initiatingElement: element,
});
@@ -650,7 +651,7 @@ export class LayerManager extends GfxExtension {
if (isGroup) {
this._reset();
this.slots.layerUpdated.emit({
this.slots.layerUpdated.next({
type: 'delete',
initiatingElement: element as GfxModel,
});
@@ -673,14 +674,14 @@ export class LayerManager extends GfxExtension {
this._removeFromLayer(element, deleteType);
this._buildCanvasLayers();
this.slots.layerUpdated.emit({
this.slots.layerUpdated.next({
type: 'delete',
initiatingElement: element,
});
}
override unmounted() {
this.slots.layerUpdated.dispose();
this.slots.layerUpdated.complete();
this._disposable.dispose();
}
@@ -784,7 +785,7 @@ export class LayerManager extends GfxExtension {
) {
if (this._updateLayer(element, props)) {
this._buildCanvasLayers();
this.slots.layerUpdated.emit({
this.slots.layerUpdated.next({
type: 'update',
initiatingElement: element,
});
@@ -795,7 +796,7 @@ export class LayerManager extends GfxExtension {
const store = this._doc;
this._disposable.add(
store.slots.blockUpdated.on(payload => {
store.slots.blockUpdated.subscribe(payload => {
if (payload.type === 'add') {
const block = store.getBlockById(payload.id)!;
@@ -854,34 +855,34 @@ export class LayerManager extends GfxExtension {
);
this._disposable.add(
surface.elementAdded.on(payload =>
surface.elementAdded.subscribe(payload =>
this.add(surface.getElementById(payload.id)!)
)
);
this._disposable.add(
surface.elementUpdated.on(payload => {
surface.elementUpdated.subscribe(payload => {
if (payload.props['index'] || payload.props['childIds']) {
this.update(surface.getElementById(payload.id)!, payload.props);
}
})
);
this._disposable.add(
surface.elementRemoved.on(payload => this.delete(payload.model!))
surface.elementRemoved.subscribe(payload => this.delete(payload.model!))
);
this._disposable.add(
surface.localElementAdded.on(elm => {
surface.localElementAdded.subscribe(elm => {
this.add(elm);
})
);
this._disposable.add(
surface.localElementUpdated.on(payload => {
surface.localElementUpdated.subscribe(payload => {
if (payload.props['index'] || payload.props['groupId']) {
this.update(payload.model, payload.props);
}
})
);
this._disposable.add(
surface.localElementDeleted.on(elm => {
surface.localElementDeleted.subscribe(elm => {
this.delete(elm);
})
);
@@ -39,7 +39,7 @@ function startWatch(prop: string | symbol, receiver: GfxPrimitiveElementModel) {
if (!watchFn) return;
receiver['_disposable'].add(
receiver.surface.elementUpdated.on(payload => {
receiver.surface.elementUpdated.subscribe(payload => {
if (payload.id === receiver.id && prop in payload.props) {
watchFn(payload.oldValues[prop as string], receiver, payload.local);
}
@@ -1,3 +1,4 @@
import { DisposableGroup } from '@blocksuite/global/disposable';
import {
Bound,
deserializeXYWH,
@@ -13,9 +14,9 @@ import {
type SerializedXYWH,
type XYWH,
} from '@blocksuite/global/gfx';
import { DisposableGroup, Slot } from '@blocksuite/global/slot';
import { createMutex } from 'lib0/mutex';
import isEqual from 'lodash-es/isEqual';
import { Subject } from 'rxjs';
import * as Y from 'yjs';
import {
@@ -86,7 +87,7 @@ export abstract class GfxPrimitiveElementModel<
protected _stashed: Map<keyof Props | string, unknown>;
propsUpdated = new Slot<{ key: string }>();
propsUpdated = new Subject<{ key: string }>();
abstract rotate: number;
@@ -262,7 +263,7 @@ export abstract class GfxPrimitiveElementModel<
onDestroyed() {
this._disposable.dispose();
this.propsUpdated.dispose();
this.propsUpdated.complete();
}
pop(prop: keyof Props | string) {
@@ -142,7 +142,7 @@ export abstract class GfxLocalElementModel implements GfxCompatibleInterface {
if (surfaceModel.localElementModels.has(p)) {
this._mutex(() => {
surfaceModel.localElementUpdated.emit({
surfaceModel.localElementUpdated.next({
model: p,
props: {
[prop as string]: value,
@@ -1,8 +1,9 @@
import { DisposableGroup, Slot } from '@blocksuite/global/slot';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { assertType, type Constructor } from '@blocksuite/global/utils';
import type { Boxed } from '@blocksuite/store';
import { BlockModel, nanoid } from '@blocksuite/store';
import { signal } from '@preact/signals-core';
import { Subject } from 'rxjs';
import * as Y from 'yjs';
import {
@@ -77,24 +78,24 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
protected _surfaceBlockModel = true;
elementAdded = new Slot<{ id: string; local: boolean }>();
protected localElements = new Set<GfxLocalElementModel>();
elementRemoved = new Slot<{
elementAdded = new Subject<{ id: string; local: boolean }>();
elementRemoved = new Subject<{
id: string;
type: string;
model: GfxPrimitiveElementModel;
local: boolean;
}>();
elementUpdated = new Slot<ElementUpdatedData>();
elementUpdated = new Subject<ElementUpdatedData>();
localElementAdded = new Slot<GfxLocalElementModel>();
localElementAdded = new Subject<GfxLocalElementModel>();
localElementDeleted = new Slot<GfxLocalElementModel>();
localElementDeleted = new Subject<GfxLocalElementModel>();
protected localElements = new Set<GfxLocalElementModel>();
localElementUpdated = new Slot<{
localElementUpdated = new Subject<{
model: GfxLocalElementModel;
props: Record<string, unknown>;
oldValues: Record<string, unknown>;
@@ -122,7 +123,10 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
constructor() {
super();
this.created.once(() => this._init());
const subscription = this.created.subscribe(() => {
this._init();
subscription.unsubscribe();
});
}
private _createElementFromProps(
@@ -296,9 +300,9 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
element,
{
onChange: payload => {
this.elementUpdated.emit(payload);
this.elementUpdated.next(payload);
Object.keys(payload.props).forEach(key => {
model.model.propsUpdated.emit({ key });
model.model.propsUpdated.next({ key });
});
},
skipFieldInit: true,
@@ -323,11 +327,11 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
addedElements.forEach(({ mount, model }) => {
mount();
this.elementAdded.emit({ id: model.id, local: transaction.local });
this.elementAdded.next({ id: model.id, local: transaction.local });
});
deletedElements.forEach(({ unmount, model }) => {
unmount();
this.elementRemoved.emit({
this.elementRemoved.next({
id: model.id,
type: model.type,
model,
@@ -343,9 +347,9 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
val,
{
onChange: payload => {
this.elementUpdated.emit(payload),
this.elementUpdated.next(payload),
Object.keys(payload.props).forEach(key => {
model.model.propsUpdated.emit({ key });
model.model.propsUpdated.next({ key });
});
},
skipFieldInit: true,
@@ -368,7 +372,7 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
elementsYMap.observe(onElementsMapChange);
const disposable = this.doc.slots.blockUpdated.on(payload => {
const subscription = this.doc.slots.blockUpdated.subscribe(payload => {
switch (payload.type) {
case 'add':
if (isGfxGroupCompatibleModel(payload.model)) {
@@ -392,9 +396,9 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
}
});
this.deleted.on(() => {
this.deleted.subscribe(() => {
elementsYMap.unobserve(onElementsMapChange);
disposable.dispose();
subscription.unsubscribe();
});
}
@@ -435,7 +439,7 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
): element is GfxGroupLikeElementModel =>
element instanceof GfxGroupLikeElementModel;
const disposable = this.elementUpdated.on(({ id, oldValues }) => {
const disposable = this.elementUpdated.subscribe(({ id, oldValues }) => {
const element = this.getElementById(id)!;
if (
@@ -446,8 +450,8 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
this.deleteElement(id);
}
});
this.deleted.on(() => {
disposable.dispose();
this.deleted.subscribe(() => {
disposable.unsubscribe();
});
}
@@ -458,14 +462,14 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
};
const disposables = new DisposableGroup();
disposables.add(this.elementAdded.on(updateIsEmpty));
disposables.add(this.elementRemoved.on(updateIsEmpty));
this.doc.slots.blockUpdated.on(payload => {
disposables.add(this.elementAdded.subscribe(updateIsEmpty));
disposables.add(this.elementRemoved.subscribe(updateIsEmpty));
this.doc.slots.blockUpdated.subscribe(payload => {
if (['add', 'delete'].includes(payload.type)) {
updateIsEmpty();
}
});
this.deleted.on(() => {
this.deleted.subscribe(() => {
disposables.dispose();
});
}
@@ -518,9 +522,9 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
const elementModel = this._createElementFromProps(props, {
onChange: payload => {
this.elementUpdated.emit(payload);
this.elementUpdated.next(payload);
Object.keys(payload.props).forEach(key => {
elementModel.model.propsUpdated.emit({ key });
elementModel.model.propsUpdated.next({ key });
});
},
});
@@ -536,7 +540,7 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
addLocalElement(elem: GfxLocalElementModel) {
this.localElements.add(elem);
this.localElementAdded.emit(elem);
this.localElementAdded.next(elem);
}
applyMiddlewares(middlewares: SurfaceMiddleware[]) {
@@ -575,16 +579,16 @@ export class SurfaceBlockModel extends BlockModel<SurfaceBlockProps> {
deleteLocalElement(elem: GfxLocalElementModel) {
if (this.localElements.delete(elem)) {
this.localElementDeleted.emit(elem);
this.localElementDeleted.next(elem);
}
}
override dispose(): void {
super.dispose();
this.elementAdded.dispose();
this.elementRemoved.dispose();
this.elementUpdated.dispose();
this.elementAdded.complete();
this.elementRemoved.complete();
this.elementUpdated.complete();
this._elementModels.forEach(({ unmount }) => unmount());
this._elementModels.clear();
@@ -1,10 +1,11 @@
import { DisposableGroup } from '@blocksuite/global/disposable';
import {
getCommonBoundWithRotation,
type IPoint,
} from '@blocksuite/global/gfx';
import { DisposableGroup, Slot } from '@blocksuite/global/slot';
import { assertType } from '@blocksuite/global/utils';
import groupBy from 'lodash-es/groupBy';
import { Subject } from 'rxjs';
import {
BlockSelection,
@@ -60,11 +61,11 @@ export class GfxSelectionManager extends GfxExtension {
disposable: DisposableGroup = new DisposableGroup();
readonly slots = {
updated: new Slot<SurfaceSelection[]>(),
remoteUpdated: new Slot(),
updated: new Subject<SurfaceSelection[]>(),
remoteUpdated: new Subject<void>(),
cursorUpdated: new Slot<CursorSelection>(),
remoteCursorUpdated: new Slot(),
cursorUpdated: new Subject<CursorSelection>(),
remoteCursorUpdated: new Subject<void>(),
};
get activeGroup() {
@@ -217,7 +218,7 @@ export class GfxSelectionManager extends GfxExtension {
override mounted() {
this.disposable.add(
this.stdSelection.slots.changed.on(selections => {
this.stdSelection.slots.changed.subscribe(selections => {
const { cursor = [], surface = [] } = groupBy(selections, sel => {
if (sel.is(SurfaceSelection)) {
return 'surface';
@@ -233,7 +234,7 @@ export class GfxSelectionManager extends GfxExtension {
if (cursor[0] && !this.cursorSelection?.equals(cursor[0])) {
this._cursorSelection = cursor[0];
this.slots.cursorUpdated.emit(cursor[0]);
this.slots.cursorUpdated.next(cursor[0]);
}
if ((surface.length === 0 && this.empty) || this.equals(surface)) {
@@ -250,12 +251,12 @@ export class GfxSelectionManager extends GfxExtension {
})
);
this.slots.updated.emit(this.surfaceSelections);
this.slots.updated.next(this.surfaceSelections);
})
);
this.disposable.add(
this.stdSelection.slots.remoteChanged.on(states => {
this.stdSelection.slots.remoteChanged.subscribe(states => {
const surfaceMap = new Map<number, SurfaceSelection[]>();
const cursorMap = new Map<number, CursorSelection>();
const selectedSet = new Set<string>();
@@ -299,8 +300,8 @@ export class GfxSelectionManager extends GfxExtension {
this._remoteSurfaceSelectionsMap = surfaceMap;
this._remoteSelectedSet = selectedSet;
this.slots.remoteUpdated.emit();
this.slots.remoteCursorUpdated.emit();
this.slots.remoteUpdated.next();
this.slots.remoteCursorUpdated.next();
})
);
}
@@ -1,8 +1,9 @@
import type { ServiceIdentifier } from '@blocksuite/global/di';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import type { IBound, IPoint } from '@blocksuite/global/gfx';
import { DisposableGroup, Slot } from '@blocksuite/global/slot';
import { Signal } from '@preact/signals-core';
import { Subject } from 'rxjs';
import type { PointerEventState } from '../../event/index.js';
import type { GfxController } from '../controller.js';
@@ -79,7 +80,7 @@ export const eventTarget = Symbol('eventTarget');
export class ToolController extends GfxExtension {
static override key = 'ToolController';
private readonly _builtInHookSlot = new Slot<BuiltInSlotContext>();
private readonly _builtInHookSlot = new Subject<BuiltInSlotContext>();
private readonly _disposableGroup = new DisposableGroup();
@@ -441,7 +442,7 @@ export class ToolController extends GfxExtension {
);
});
this._builtInHookSlot.on(evt => {
this._builtInHookSlot.subscribe(evt => {
hooks[evt.event]?.forEach(hook => hook(evt));
});
@@ -499,7 +500,7 @@ export class ToolController extends GfxExtension {
const beforeUpdateCtx = this._createBuiltInHookCtx('beforeToolUpdate', {
toolName: toolNameStr,
});
this._builtInHookSlot.emit(beforeUpdateCtx.slotCtx);
this._builtInHookSlot.next(beforeUpdateCtx.slotCtx);
if (beforeUpdateCtx.prevented) {
return;
@@ -528,7 +529,7 @@ export class ToolController extends GfxExtension {
const afterUpdateCtx = this._createBuiltInHookCtx('toolUpdate', {
toolName: toolNameStr,
});
this._builtInHookSlot.emit(afterUpdateCtx.slotCtx);
this._builtInHookSlot.next(afterUpdateCtx.slotCtx);
}
override unmounted(): void {
@@ -537,7 +538,7 @@ export class ToolController extends GfxExtension {
tool.unmounted();
tool['disposable'].dispose();
});
this._builtInHookSlot.dispose();
this._builtInHookSlot.complete();
}
}
@@ -1,6 +1,6 @@
import { type Container, createIdentifier } from '@blocksuite/global/di';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { DisposableGroup } from '@blocksuite/global/slot';
import { Extension } from '@blocksuite/store';
import type { PointerEventState } from '../../event/index.js';
@@ -1,4 +1,4 @@
import { DisposableGroup } from '@blocksuite/global/slot';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { onSurfaceAdded } from '../../utils/gfx.js';
import type { GfxController } from '../controller.js';
@@ -70,14 +70,14 @@ export class ViewManager extends GfxExtension {
};
this._disposable.add(
surface.elementAdded.on(payload => {
surface.elementAdded.subscribe(payload => {
const model = surface.getElementById(payload.id)!;
createView(model);
})
);
this._disposable.add(
surface.elementRemoved.on(elem => {
surface.elementRemoved.subscribe(elem => {
const view = this._viewMap.get(elem.id);
this._viewMap.delete(elem.id);
view?.onDestroyed();
@@ -85,13 +85,13 @@ export class ViewManager extends GfxExtension {
);
this._disposable.add(
surface.localElementAdded.on(model => {
surface.localElementAdded.subscribe(model => {
createView(model);
})
);
this._disposable.add(
surface.localElementDeleted.on(model => {
surface.localElementDeleted.subscribe(model => {
const view = this._viewMap.get(model.id);
this._viewMap.delete(model.id);
view?.onDestroyed();
@@ -1,7 +1,7 @@
import { type Container, createIdentifier } from '@blocksuite/global/di';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import type { Bound, IVec } from '@blocksuite/global/gfx';
import { DisposableGroup } from '@blocksuite/global/slot';
import type { Extension } from '@blocksuite/store';
import type { PointerEventState } from '../../event/index.js';
@@ -131,10 +131,10 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
this._hideOutsideBlock();
this.disposables.add(
this.viewport.viewportUpdated.on(() => viewportUpdateCallback())
this.viewport.viewportUpdated.subscribe(() => viewportUpdateCallback())
);
this.disposables.add(
this.viewport.sizeUpdated.on(() => viewportUpdateCallback())
this.viewport.sizeUpdated.subscribe(() => viewportUpdateCallback())
);
}
@@ -5,9 +5,9 @@ import {
type IVec,
Vec,
} from '@blocksuite/global/gfx';
import { Slot } from '@blocksuite/global/slot';
import { signal } from '@preact/signals-core';
import debounce from 'lodash-es/debounce';
import { Subject } from 'rxjs';
import type { GfxViewportElement } from '.';
@@ -74,18 +74,18 @@ export class Viewport {
protected _zoom: number = 1.0;
elementReady = new Slot<GfxViewportElement>();
elementReady = new Subject<GfxViewportElement>();
sizeUpdated = new Slot<{
sizeUpdated = new Subject<{
width: number;
height: number;
left: number;
top: number;
}>();
viewportMoved = new Slot<IVec>();
viewportMoved = new Subject<IVec>();
viewportUpdated = new Slot<{
viewportUpdated = new Subject<{
zoom: number;
center: IVec;
}>();
@@ -106,7 +106,10 @@ export class Viewport {
}, 200);
constructor() {
this.elementReady.once(el => (this._element = el));
const subscription = this.elementReady.subscribe(el => {
this._element = el;
subscription.unsubscribe();
});
}
get boundingClientRect() {
@@ -233,9 +236,9 @@ export class Viewport {
dispose() {
this.clearViewportElement();
this.sizeUpdated.dispose();
this.viewportMoved.dispose();
this.viewportUpdated.dispose();
this.sizeUpdated.complete();
this.viewportMoved.complete();
this.viewportUpdated.complete();
this.zooming$.value = false;
this.panning$.value = false;
}
@@ -296,7 +299,7 @@ export class Viewport {
this._center.x = centerX;
this._center.y = centerY;
this.panning$.value = true;
this.viewportUpdated.emit({
this.viewportUpdated.next({
zoom: this.zoom,
center: Vec.toVec(this.center) as IVec,
});
@@ -306,7 +309,7 @@ export class Viewport {
setRect(left: number, top: number, width: number, height: number) {
this._left = left;
this._top = top;
this.sizeUpdated.emit({
this.sizeUpdated.next({
left,
top,
width,
@@ -420,7 +423,7 @@ export class Viewport {
this.zooming$.value = true;
}
this.setCenter(newCenter[0], newCenter[1]);
this.viewportUpdated.emit({
this.viewportUpdated.next({
zoom: this.zoom,
center: Vec.toVec(this.center) as IVec,
});