mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 04:48:53 +00:00
fix: block should not be selectable when dragged into note (#10664)
### Changed - Fixed the issue that block can still be selected when dragged into note - Rewrite grid and layer with extension infra
This commit is contained in:
@@ -38,18 +38,22 @@ export class GfxController extends LifeCycleWatcher {
|
||||
|
||||
private readonly _disposables: DisposableGroup = new DisposableGroup();
|
||||
|
||||
private _surface: SurfaceBlockModel | null = null;
|
||||
private readonly _surface$ = new Signal<SurfaceBlockModel | null>(null);
|
||||
|
||||
readonly cursor$ = new Signal<CursorType>();
|
||||
|
||||
readonly grid: GridManager;
|
||||
|
||||
readonly keyboard: KeyboardController;
|
||||
|
||||
readonly layer: LayerManager;
|
||||
|
||||
readonly viewport: Viewport = new Viewport();
|
||||
|
||||
get grid() {
|
||||
return this.std.get(GridManager);
|
||||
}
|
||||
|
||||
get layer() {
|
||||
return this.std.get(LayerManager);
|
||||
}
|
||||
|
||||
get doc() {
|
||||
return this.std.store;
|
||||
}
|
||||
@@ -62,8 +66,12 @@ export class GfxController extends LifeCycleWatcher {
|
||||
return [...this.layer.blocks, ...this.layer.canvasElements];
|
||||
}
|
||||
|
||||
get surface$() {
|
||||
return this._surface$;
|
||||
}
|
||||
|
||||
get surface() {
|
||||
return this._surface;
|
||||
return this._surface$.peek();
|
||||
}
|
||||
|
||||
get surfaceComponent(): BlockComponent | null {
|
||||
@@ -75,22 +83,13 @@ export class GfxController extends LifeCycleWatcher {
|
||||
constructor(std: BlockStdScope) {
|
||||
super(std);
|
||||
|
||||
this.grid = new GridManager();
|
||||
this.layer = new LayerManager(this.doc, null);
|
||||
this.keyboard = new KeyboardController(std);
|
||||
|
||||
this._disposables.add(
|
||||
onSurfaceAdded(this.doc, surface => {
|
||||
this._surface = surface;
|
||||
|
||||
if (surface) {
|
||||
this._disposables.add(this.grid.watch({ surface }));
|
||||
this.layer.watch({ surface });
|
||||
}
|
||||
this._surface$.value = surface;
|
||||
})
|
||||
);
|
||||
this._disposables.add(this.grid.watch({ doc: this.doc }));
|
||||
this._disposables.add(this.layer);
|
||||
this._disposables.add(this.viewport);
|
||||
this._disposables.add(this.keyboard);
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ import {
|
||||
getBoundWithRotation,
|
||||
intersects,
|
||||
} from '@blocksuite/global/gfx';
|
||||
import type { BlockModel, Store } from '@blocksuite/store';
|
||||
import { DisposableGroup } from '@blocksuite/global/slot';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
|
||||
import { compare } from '../utils/layer.js';
|
||||
import { GfxExtension } from './extension.js';
|
||||
import { GfxBlockElementModel } from './model/gfx-block-model.js';
|
||||
import type { GfxModel } from './model/model.js';
|
||||
import { GfxPrimitiveElementModel } from './model/surface/element-model.js';
|
||||
@@ -65,7 +67,9 @@ const typeFilters = {
|
||||
|
||||
type FilterFunc = (model: GfxModel | GfxLocalElementModel) => boolean;
|
||||
|
||||
export class GridManager {
|
||||
export class GridManager extends GfxExtension {
|
||||
static override key = 'grid';
|
||||
|
||||
private readonly _elementToGrids = new Map<
|
||||
GfxModel | GfxLocalElementModel,
|
||||
Set<Set<GfxModel | GfxLocalElementModel>>
|
||||
@@ -361,10 +365,16 @@ export class GridManager {
|
||||
this.add(element);
|
||||
}
|
||||
|
||||
watch(blocks: { doc?: Store; surface?: SurfaceBlockModel | null }) {
|
||||
const disposables: { dispose: () => void }[] = [];
|
||||
const { doc, surface } = blocks;
|
||||
const isRenderableBlock = (
|
||||
private readonly _disposables = new DisposableGroup();
|
||||
|
||||
override unmounted(): void {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
override mounted() {
|
||||
const disposables = this._disposables;
|
||||
const { store } = this.std;
|
||||
const canBeRenderedAsGfxBlock = (
|
||||
block: BlockModel
|
||||
): block is GfxBlockElementModel => {
|
||||
return (
|
||||
@@ -374,63 +384,75 @@ export class GridManager {
|
||||
);
|
||||
};
|
||||
|
||||
if (doc) {
|
||||
disposables.push(
|
||||
doc.slots.blockUpdated.on(payload => {
|
||||
if (payload.type === 'add' && isRenderableBlock(payload.model)) {
|
||||
this.add(payload.model);
|
||||
disposables.add(
|
||||
store.slots.blockUpdated.on(payload => {
|
||||
if (payload.type === 'add' && canBeRenderedAsGfxBlock(payload.model)) {
|
||||
this.add(payload.model);
|
||||
}
|
||||
|
||||
if (payload.type === 'update') {
|
||||
const model = store.getBlock(payload.id)
|
||||
?.model as GfxBlockElementModel;
|
||||
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload.type === 'update') {
|
||||
const model = doc.getBlock(payload.id)
|
||||
?.model as GfxBlockElementModel;
|
||||
if (payload.props.key === 'xywh' && canBeRenderedAsGfxBlock(model)) {
|
||||
this.update(
|
||||
store.getBlock(payload.id)?.model as GfxBlockElementModel
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!model) {
|
||||
if (
|
||||
payload.type === 'delete' &&
|
||||
payload.model instanceof GfxBlockElementModel
|
||||
) {
|
||||
this.remove(payload.model);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
Object.values(store.blocks.peek()).forEach(block => {
|
||||
if (canBeRenderedAsGfxBlock(block.model)) {
|
||||
this.add(block.model);
|
||||
}
|
||||
});
|
||||
|
||||
const watchSurface = (surface: SurfaceBlockModel) => {
|
||||
let lastChildMap = new Map(surface.childMap.peek());
|
||||
disposables.add(
|
||||
surface.childMap.subscribe(val => {
|
||||
val.forEach((_, id) => {
|
||||
if (lastChildMap.has(id)) {
|
||||
lastChildMap.delete(id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._elementToGrids.has(model) && !isRenderableBlock(model)) {
|
||||
this.remove(model as GfxBlockElementModel);
|
||||
} else if (
|
||||
payload.props.key === 'xywh' &&
|
||||
isRenderableBlock(model)
|
||||
) {
|
||||
this.update(
|
||||
doc.getBlock(payload.id)?.model as GfxBlockElementModel
|
||||
);
|
||||
});
|
||||
lastChildMap.forEach((_, id) => {
|
||||
const block = store.getBlock(id);
|
||||
if (block?.model) {
|
||||
this.remove(block.model as GfxBlockElementModel);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'delete' &&
|
||||
payload.model instanceof GfxBlockElementModel
|
||||
) {
|
||||
this.remove(payload.model);
|
||||
}
|
||||
});
|
||||
lastChildMap = new Map(val);
|
||||
})
|
||||
);
|
||||
|
||||
Object.values(doc.blocks.peek()).forEach(block => {
|
||||
if (isRenderableBlock(block.model)) {
|
||||
this.add(block.model);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
disposables.push(
|
||||
disposables.add(
|
||||
surface.elementAdded.on(payload => {
|
||||
this.add(surface.getElementById(payload.id)!);
|
||||
})
|
||||
);
|
||||
|
||||
disposables.push(
|
||||
disposables.add(
|
||||
surface.elementRemoved.on(payload => {
|
||||
this.remove(payload.model);
|
||||
})
|
||||
);
|
||||
|
||||
disposables.push(
|
||||
disposables.add(
|
||||
surface.elementUpdated.on(payload => {
|
||||
if (
|
||||
payload.props['xywh'] ||
|
||||
@@ -442,13 +464,13 @@ export class GridManager {
|
||||
})
|
||||
);
|
||||
|
||||
disposables.push(
|
||||
disposables.add(
|
||||
surface.localElementAdded.on(elm => {
|
||||
this.add(elm);
|
||||
})
|
||||
);
|
||||
|
||||
disposables.push(
|
||||
disposables.add(
|
||||
surface.localElementUpdated.on(payload => {
|
||||
if (payload.props['xywh'] || payload.props['responseExtension']) {
|
||||
this.update(payload.model);
|
||||
@@ -456,7 +478,7 @@ export class GridManager {
|
||||
})
|
||||
);
|
||||
|
||||
disposables.push(
|
||||
disposables.add(
|
||||
surface.localElementDeleted.on(elm => {
|
||||
this.remove(elm);
|
||||
})
|
||||
@@ -468,10 +490,18 @@ export class GridManager {
|
||||
surface.localElementModels.forEach(model => {
|
||||
this.add(model);
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
disposables.forEach(d => d.dispose());
|
||||
};
|
||||
|
||||
if (this.gfx.surface) {
|
||||
watchSurface(this.gfx.surface);
|
||||
} else {
|
||||
disposables.add(
|
||||
this.gfx.surface$.subscribe(surface => {
|
||||
if (surface) {
|
||||
watchSurface(surface);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Bound } from '@blocksuite/global/gfx';
|
||||
import { DisposableGroup, Slot } from '@blocksuite/global/slot';
|
||||
import { assertType } from '@blocksuite/global/utils';
|
||||
import type { Store } from '@blocksuite/store';
|
||||
import { generateKeyBetween } from 'fractional-indexing';
|
||||
import last from 'lodash-es/last';
|
||||
|
||||
@@ -16,6 +15,8 @@ import {
|
||||
ungroupIndex,
|
||||
updateLayersZIndex,
|
||||
} from '../utils/layer.js';
|
||||
import { GfxExtension } from './extension.js';
|
||||
import type { GfxController } from './index.js';
|
||||
import {
|
||||
type GfxGroupCompatibleInterface,
|
||||
isGfxGroupCompatibleModel,
|
||||
@@ -65,11 +66,21 @@ export type CanvasLayer = BaseLayer<GfxPrimitiveElementModel> & {
|
||||
|
||||
export type Layer = BlockLayer | CanvasLayer;
|
||||
|
||||
export class LayerManager {
|
||||
export class LayerManager extends GfxExtension {
|
||||
static override key = 'layerManager';
|
||||
|
||||
static INITIAL_INDEX = 'a0';
|
||||
|
||||
private readonly _disposable = new DisposableGroup();
|
||||
|
||||
private get _doc() {
|
||||
return this.std.store;
|
||||
}
|
||||
|
||||
private get _surface() {
|
||||
return this.gfx.surface;
|
||||
}
|
||||
|
||||
blocks: GfxBlockElementModel[] = [];
|
||||
|
||||
canvasElements: GfxPrimitiveElementModel[] = [];
|
||||
@@ -96,21 +107,9 @@ export class LayerManager {
|
||||
}>(),
|
||||
};
|
||||
|
||||
constructor(
|
||||
private readonly _doc: Store,
|
||||
private _surface: SurfaceBlockModel | null,
|
||||
options: {
|
||||
watch: boolean;
|
||||
} = { watch: true }
|
||||
) {
|
||||
constructor(gfx: GfxController) {
|
||||
super(gfx);
|
||||
this._reset();
|
||||
|
||||
if (options?.watch) {
|
||||
this.watch({
|
||||
doc: _doc,
|
||||
surface: _surface,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _buildCanvasLayers() {
|
||||
@@ -614,9 +613,8 @@ export class LayerManager {
|
||||
* @returns
|
||||
*/
|
||||
createIndexGenerator() {
|
||||
const manager = new LayerManager(this._doc, this._surface, {
|
||||
watch: false,
|
||||
});
|
||||
const manager = new LayerManager(this.gfx);
|
||||
manager._reset();
|
||||
|
||||
return () => {
|
||||
const idx = manager.generateIndex();
|
||||
@@ -681,8 +679,9 @@ export class LayerManager {
|
||||
});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
override unmounted() {
|
||||
this.slots.layerUpdated.dispose();
|
||||
this._disposable.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -792,62 +791,67 @@ export class LayerManager {
|
||||
}
|
||||
}
|
||||
|
||||
watch(blocks: { doc?: Store; surface: SurfaceBlockModel | null }) {
|
||||
const { doc, surface } = blocks;
|
||||
override mounted() {
|
||||
const store = this._doc;
|
||||
|
||||
if (doc) {
|
||||
this._disposable.add(
|
||||
store.slots.blockUpdated.on(payload => {
|
||||
if (payload.type === 'add') {
|
||||
const block = store.getBlockById(payload.id)!;
|
||||
|
||||
if (
|
||||
block instanceof GfxBlockElementModel &&
|
||||
(block.parent instanceof SurfaceBlockModel ||
|
||||
block.parent?.role === 'root') &&
|
||||
this.blocks.indexOf(block) === -1
|
||||
) {
|
||||
this.add(block as GfxBlockElementModel);
|
||||
}
|
||||
}
|
||||
if (payload.type === 'update') {
|
||||
const block = store.getBlockById(payload.id)!;
|
||||
|
||||
if (
|
||||
(payload.props.key === 'index' ||
|
||||
payload.props.key === 'childElementIds') &&
|
||||
block instanceof GfxBlockElementModel &&
|
||||
(block.parent instanceof SurfaceBlockModel ||
|
||||
block.parent?.role === 'root')
|
||||
) {
|
||||
this.update(block as GfxBlockElementModel, {
|
||||
[payload.props.key]: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (payload.type === 'delete') {
|
||||
const block = store.getBlockById(payload.id);
|
||||
|
||||
if (block instanceof GfxBlockElementModel) {
|
||||
this.delete(block as GfxBlockElementModel);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const watchSurface = (surface: SurfaceBlockModel) => {
|
||||
let lastChildMap = new Map(surface.childMap.peek());
|
||||
this._disposable.add(
|
||||
doc.slots.blockUpdated.on(payload => {
|
||||
if (payload.type === 'add') {
|
||||
const block = doc.getBlockById(payload.id)!;
|
||||
|
||||
if (
|
||||
block instanceof GfxBlockElementModel &&
|
||||
(block.parent instanceof SurfaceBlockModel ||
|
||||
block.parent?.role === 'root') &&
|
||||
this.blocks.indexOf(block) === -1
|
||||
) {
|
||||
this.add(block as GfxBlockElementModel);
|
||||
surface.childMap.subscribe(val => {
|
||||
val.forEach((_, id) => {
|
||||
if (lastChildMap.has(id)) {
|
||||
lastChildMap.delete(id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (payload.type === 'update') {
|
||||
const block = doc.getBlockById(payload.id)!;
|
||||
|
||||
if (
|
||||
(payload.props.key === 'index' ||
|
||||
payload.props.key === 'childElementIds') &&
|
||||
block instanceof GfxBlockElementModel &&
|
||||
(block.parent instanceof SurfaceBlockModel ||
|
||||
block.parent?.role === 'root')
|
||||
) {
|
||||
this.update(block as GfxBlockElementModel, {
|
||||
[payload.props.key]: true,
|
||||
});
|
||||
} else if (
|
||||
this.blocks.includes(block as GfxBlockElementModel) &&
|
||||
!(
|
||||
block.parent instanceof SurfaceBlockModel ||
|
||||
block.parent?.role === 'root'
|
||||
)
|
||||
) {
|
||||
this.delete(block as GfxBlockElementModel);
|
||||
});
|
||||
lastChildMap.forEach((_, id) => {
|
||||
const block = this._doc.getBlock(id);
|
||||
if (block?.model) {
|
||||
this.delete(block.model as GfxBlockElementModel);
|
||||
}
|
||||
}
|
||||
if (payload.type === 'delete') {
|
||||
const block = doc.getBlockById(payload.id);
|
||||
|
||||
if (block instanceof GfxBlockElementModel) {
|
||||
this.delete(block as GfxBlockElementModel);
|
||||
}
|
||||
}
|
||||
});
|
||||
lastChildMap = new Map(val);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
if (this._surface !== surface) {
|
||||
this._surface = surface;
|
||||
}
|
||||
|
||||
this._disposable.add(
|
||||
surface.elementAdded.on(payload =>
|
||||
@@ -870,7 +874,7 @@ export class LayerManager {
|
||||
})
|
||||
);
|
||||
this._disposable.add(
|
||||
this._surface.localElementUpdated.on(payload => {
|
||||
surface.localElementUpdated.on(payload => {
|
||||
if (payload.props['index'] || payload.props['groupId']) {
|
||||
this.update(payload.model, payload.props);
|
||||
}
|
||||
@@ -881,8 +885,18 @@ export class LayerManager {
|
||||
this.delete(elm);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
surface.elementModels.forEach(el => this.add(el));
|
||||
if (this.gfx.surface) {
|
||||
watchSurface(this.gfx.surface);
|
||||
} else {
|
||||
this._disposable.add(
|
||||
this.gfx.surface$.subscribe(surface => {
|
||||
if (surface) {
|
||||
watchSurface(surface);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { UIEventDispatcher } from '../event/index.js';
|
||||
import { DndController } from '../extension/dnd/index.js';
|
||||
import { EditorLifeCycleExtension } from '../extension/editor-life-cycle.js';
|
||||
import { GfxController } from '../gfx/controller.js';
|
||||
import { GridManager, LayerManager } from '../gfx/index.js';
|
||||
import { GfxSelectionManager } from '../gfx/selection.js';
|
||||
import { SurfaceMiddlewareExtension } from '../gfx/surface-middleware.js';
|
||||
import { ViewManager } from '../gfx/view/view-manager.js';
|
||||
@@ -39,6 +40,8 @@ const internalExtensions = [
|
||||
Clipboard,
|
||||
GfxController,
|
||||
GfxSelectionManager,
|
||||
GridManager,
|
||||
LayerManager,
|
||||
SurfaceMiddlewareExtension,
|
||||
ViewManager,
|
||||
DndController,
|
||||
|
||||
Reference in New Issue
Block a user