mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 05:14:54 +00:00
refactor: rewrite blocksuite dnd (#9595)
### Changed
- Refactored BlockSuite drag-and-drop using @atlaskit/pragmatic-drag-and-drop/element/adapter.
- Updated block dragging to use the new drag-and-drop infrastructure.
### BlockSuite DND API
Access the BlockSuite drag-and-drop API via `std.dnd`. This is a lightweight wrapper around pragmatic-drag-and-drop, offering convenient generic types and more intuitive option names.
#### Drag payload structure
There's some constrain about drag payload. The whole drag payload looks like this:
```typescript
type DragPayload = {
entity: {
type: string
},
from: {
at: 'blocksuite',
docId: string
}
}
```
- The `from` field is auto-generated—no need for manual handling.
- The `entity` field is customizable, but it must include a `type`.
All drag-and-drop methods accept a generic type for entity, ensuring more accurate payloads in event handlers.
```typescript
type BlockEntity = {
type: 'blocks',
blockIds: string[]
}
dnd.draggable<BlockEntity>({
element: someElement,
setDragData: () => {
// the return type must satisfy the generic type
// in this case, it's BlockEntity
return {
type: 'blocks',
blockIds: []
}
}
});
dnd.monitor<BlockEntity>({
// the arguments is same for other event handler
onDrag({ source }) {
// the type of this is BlockEntity
source.data.entity
}
})
```
#### Drop payload
When hover on droppable target. You can set drop payload as well. All drag-and-drop methods accept a second generic type for drop payload.
The drop payload is customizable. Additionally, the DND system will add an `edge` field to the final payload object, indicating the nearest edge of the drop target relative to the current drag position.
```typescript
type DropPayload = {
blockId: string;
}
dnd.dropTarget<BlockEntity, DropPayload>({
getData() {
// the type should be DropPayload
return {
blockId: 'someId'
}
}
});
dnd.monitor<BlockEntity, DropPayload>({
// drag over on drop target
onDrag({ location }) {
const target = location.current.dropTargets[0];
// the type is DropPayload
target.data;
// retrieve the nearest edge of the drop target relative to the current drop position.
target.data.edge;
}
})
```
This commit is contained in:
@@ -14,6 +14,9 @@
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
|
||||
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.1.0",
|
||||
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
|
||||
@@ -368,6 +368,17 @@ class DragController extends PointerControllerBase {
|
||||
disposables.addFromEvent(host, 'pointerdown', this._down);
|
||||
this._applyScribblePatch();
|
||||
|
||||
disposables.add(
|
||||
host.std.dnd.monitor({
|
||||
onDragStart: () => {
|
||||
this._nativeDragging = true;
|
||||
},
|
||||
onDrop: () => {
|
||||
this._nativeDragging = false;
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
disposables.addFromEvent(host, 'dragstart', this._nativeDragStart);
|
||||
disposables.addFromEvent(host, 'dragend', this._nativeDragEnd);
|
||||
disposables.addFromEvent(host, 'drag', this._nativeDragMove);
|
||||
|
||||
317
blocksuite/framework/block-std/src/extension/dnd/index.ts
Normal file
317
blocksuite/framework/block-std/src/extension/dnd/index.ts
Normal file
@@ -0,0 +1,317 @@
|
||||
import {
|
||||
draggable,
|
||||
dropTargetForElements,
|
||||
type ElementGetFeedbackArgs,
|
||||
monitorForElements,
|
||||
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview';
|
||||
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
|
||||
import type { DropTargetRecord } from '@atlaskit/pragmatic-drag-and-drop/types';
|
||||
import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element';
|
||||
import {
|
||||
attachClosestEdge,
|
||||
type Edge,
|
||||
extractClosestEdge,
|
||||
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
||||
import type { ServiceIdentifier } from '@blocksuite/global/di';
|
||||
|
||||
import { LifeCycleWatcherIdentifier } from '../../identifier.js';
|
||||
import { LifeCycleWatcher } from '../lifecycle-watcher.js';
|
||||
import type {
|
||||
ElementDragEventBaseArgs,
|
||||
ElementDragEventMap,
|
||||
ElementDropEventMap,
|
||||
ElementDropTargetFeedbackArgs,
|
||||
ElementMonitorFeedbackArgs,
|
||||
OriginalAutoScrollOption,
|
||||
OriginalDraggableOption,
|
||||
OriginalDropTargetOption,
|
||||
OriginalMonitorOption,
|
||||
} from './types.js';
|
||||
|
||||
export type DragEntity = { type: string };
|
||||
|
||||
export type DragFrom = { at: string };
|
||||
|
||||
export type DragFromBlockSuite = {
|
||||
at: 'blocksuite-editor';
|
||||
docId: string;
|
||||
};
|
||||
|
||||
export type DragPayload<
|
||||
E extends DragEntity = DragEntity,
|
||||
F extends DragFrom = DragFromBlockSuite,
|
||||
> = {
|
||||
bsEntity?: E;
|
||||
from?: F;
|
||||
};
|
||||
|
||||
export type DropPayload<T extends {} = {}> = {
|
||||
edge?: Edge;
|
||||
} & T;
|
||||
|
||||
export type DropEdge = Edge;
|
||||
|
||||
export interface DNDEntity {
|
||||
basic: DragEntity;
|
||||
}
|
||||
|
||||
export type DraggableOption<
|
||||
PayloadEntity extends DragEntity,
|
||||
PayloadFrom extends DragFrom,
|
||||
DropPayload extends {},
|
||||
> = Pick<OriginalDraggableOption, 'element' | 'dragHandle' | 'canDrag'> & {
|
||||
/**
|
||||
* Set drag data for the draggable element.
|
||||
* @see {@link ElementGetFeedbackArgs} for callback arguments
|
||||
* @param callback - callback to set drag
|
||||
*/
|
||||
setDragData?: (args: ElementGetFeedbackArgs) => PayloadEntity;
|
||||
|
||||
/**
|
||||
* Set external drag data for the draggable element.
|
||||
* @param callback - callback to set external drag data
|
||||
* @see {@link ElementGetFeedbackArgs} for callback arguments
|
||||
*/
|
||||
setExternalDragData?: (
|
||||
args: ElementGetFeedbackArgs
|
||||
) => ReturnType<
|
||||
Required<OriginalDraggableOption>['getInitialDataForExternal']
|
||||
>;
|
||||
|
||||
/**
|
||||
* Set custom drag preview for the draggable element.
|
||||
*
|
||||
* `setDragPreview` is a function that will be called with a `container` element and other drag data as parameter when the drag preview is generating.
|
||||
* Append the custom element to the `container` which will be used to generate the preview. Once the drag preview is generated, the
|
||||
* `container` element and its children will be removed automatically.
|
||||
*
|
||||
* If you want to completely disable the drag preview, just set `setDragPreview` to `false`.
|
||||
*
|
||||
* @example
|
||||
* dnd.draggable{
|
||||
* // ...
|
||||
* setDragPreview: ({ container }) => {
|
||||
* const preview = document.createElement('div');
|
||||
* preview.style.width = '100px';
|
||||
* preview.style.height = '100px';
|
||||
* preview.style.backgroundColor = 'red';
|
||||
* preview.innerText = 'Custom Drag Preview';
|
||||
* container.appendChild(preview);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param callback - callback to set custom drag preview
|
||||
* @returns
|
||||
*/
|
||||
setDragPreview?:
|
||||
| false
|
||||
| ((
|
||||
options: ElementDragEventBaseArgs<
|
||||
DragPayload<PayloadEntity, PayloadFrom>
|
||||
> & {
|
||||
/**
|
||||
* Allows you to use the native `setDragImage` function if you want
|
||||
* Although, we recommend using alternative techniques (see element adapter docs)
|
||||
*/
|
||||
nativeSetDragImage: DataTransfer['setDragImage'] | null;
|
||||
container: HTMLElement;
|
||||
}
|
||||
) => void);
|
||||
} & ElementDragEventMap<DragPayload<PayloadEntity, PayloadFrom>, DropPayload>;
|
||||
|
||||
export type DropTargetOption<
|
||||
PayloadEntity extends DragEntity,
|
||||
PayloadFrom extends DragFrom,
|
||||
DropPayload extends {},
|
||||
> = {
|
||||
/**
|
||||
* {@link OriginalDropTargetOption.element}
|
||||
*/
|
||||
element: HTMLElement;
|
||||
|
||||
/**
|
||||
* Allow drop position for the drop target.
|
||||
*/
|
||||
allowDropPosition?: Edge[];
|
||||
|
||||
/**
|
||||
* {@link OriginalDropTargetOption.getDropEffect}
|
||||
*/
|
||||
getDropEffect?: (
|
||||
args: ElementDropTargetFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
|
||||
) => DropTargetRecord['dropEffect'];
|
||||
|
||||
/**
|
||||
* {@link OriginalDropTargetOption.canDrop}
|
||||
*/
|
||||
canDrop?: (
|
||||
args: ElementDropTargetFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
|
||||
) => boolean;
|
||||
|
||||
/**
|
||||
* {@link OriginalDropTargetOption.getData}
|
||||
*/
|
||||
setDropData?: (
|
||||
args: ElementDropTargetFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
|
||||
) => DropPayload;
|
||||
|
||||
/**
|
||||
* {@link OriginalDropTargetOption.getIsSticky}
|
||||
*/
|
||||
getIsSticky?: (
|
||||
args: ElementDropTargetFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
|
||||
) => boolean;
|
||||
} & ElementDropEventMap<DragPayload<PayloadEntity, PayloadFrom>, DropPayload>;
|
||||
|
||||
export type MonitorOption<
|
||||
PayloadEntity extends DragEntity,
|
||||
PayloadFrom extends DragFrom,
|
||||
DropPayload extends {},
|
||||
> = {
|
||||
/**
|
||||
* {@link OriginalMonitorOption.canMonitor}
|
||||
*/
|
||||
canMonitor?: (
|
||||
args: ElementMonitorFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
|
||||
) => boolean;
|
||||
} & ElementDragEventMap<DragPayload<PayloadEntity, PayloadFrom>, DropPayload>;
|
||||
|
||||
export type AutoScroll<
|
||||
PayloadEntity extends DragEntity,
|
||||
PayloadFrom extends DragFrom,
|
||||
> = {
|
||||
element: HTMLElement;
|
||||
canScroll?: (
|
||||
args: ElementDragEventBaseArgs<DragPayload<PayloadEntity, PayloadFrom>>
|
||||
) => void;
|
||||
getAllowedAxis?: (
|
||||
args: ElementDragEventBaseArgs<DragPayload<PayloadEntity, PayloadFrom>>
|
||||
) => ReturnType<Required<OriginalAutoScrollOption>['getAllowedAxis']>;
|
||||
getConfiguration?: (
|
||||
args: ElementDragEventBaseArgs<DragPayload<PayloadEntity, PayloadFrom>>
|
||||
) => ReturnType<Required<OriginalAutoScrollOption>['getConfiguration']>;
|
||||
};
|
||||
|
||||
export const DndExtensionIdentifier = LifeCycleWatcherIdentifier(
|
||||
'DndController'
|
||||
) as ServiceIdentifier<DndController>;
|
||||
|
||||
export class DndController extends LifeCycleWatcher {
|
||||
static override key = 'DndController';
|
||||
|
||||
/**
|
||||
* Make an element draggable.
|
||||
*/
|
||||
draggable<
|
||||
PayloadEntity extends DragEntity = DragEntity,
|
||||
DropData extends {} = {},
|
||||
>(
|
||||
args: DraggableOption<
|
||||
PayloadEntity,
|
||||
DragFromBlockSuite,
|
||||
DropPayload<DropData>
|
||||
>
|
||||
) {
|
||||
const {
|
||||
setDragData,
|
||||
setExternalDragData,
|
||||
setDragPreview,
|
||||
element,
|
||||
dragHandle,
|
||||
...rest
|
||||
} = args;
|
||||
|
||||
return draggable({
|
||||
...(rest as Partial<OriginalDraggableOption>),
|
||||
element,
|
||||
dragHandle,
|
||||
onGenerateDragPreview: options => {
|
||||
if (setDragPreview) {
|
||||
setCustomNativeDragPreview({
|
||||
render: renderOption => {
|
||||
setDragPreview({
|
||||
...options,
|
||||
...renderOption,
|
||||
});
|
||||
},
|
||||
nativeSetDragImage: options.nativeSetDragImage,
|
||||
});
|
||||
} else if (setDragPreview === false) {
|
||||
disableNativeDragPreview({
|
||||
nativeSetDragImage: options.nativeSetDragImage,
|
||||
});
|
||||
}
|
||||
},
|
||||
getInitialData: options => {
|
||||
const bsEntity = setDragData?.(options) ?? {};
|
||||
|
||||
return {
|
||||
bsEntity,
|
||||
from: {
|
||||
at: 'blocksuite-editor',
|
||||
docId: this.std.store.doc.id,
|
||||
},
|
||||
};
|
||||
},
|
||||
getInitialDataForExternal: setExternalDragData
|
||||
? options => {
|
||||
return setExternalDragData?.(options);
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an element a drop target.
|
||||
*/
|
||||
dropTarget<
|
||||
PayloadEntity extends DragEntity = DragEntity,
|
||||
DropData extends {} = {},
|
||||
PayloadFrom extends DragFrom = DragFromBlockSuite,
|
||||
>(args: DropTargetOption<PayloadEntity, PayloadFrom, DropPayload<DropData>>) {
|
||||
const {
|
||||
element,
|
||||
setDropData,
|
||||
allowDropPosition = ['bottom', 'left', 'top', 'right'],
|
||||
...rest
|
||||
} = args;
|
||||
|
||||
return dropTargetForElements({
|
||||
element,
|
||||
getData: options => {
|
||||
const data = setDropData?.(options) ?? {};
|
||||
const edge = extractClosestEdge(
|
||||
attachClosestEdge(data, {
|
||||
element: options.element,
|
||||
input: options.input,
|
||||
allowedEdges: allowDropPosition,
|
||||
})
|
||||
);
|
||||
|
||||
return edge
|
||||
? {
|
||||
...data,
|
||||
edge,
|
||||
}
|
||||
: data;
|
||||
},
|
||||
...(rest as Partial<OriginalDropTargetOption>),
|
||||
});
|
||||
}
|
||||
|
||||
monitor<
|
||||
PayloadEntity extends DragEntity = DragEntity,
|
||||
DropData extends {} = {},
|
||||
PayloadFrom extends DragFrom = DragFromBlockSuite,
|
||||
>(args: MonitorOption<PayloadEntity, PayloadFrom, DropPayload<DropData>>) {
|
||||
return monitorForElements(args as OriginalMonitorOption);
|
||||
}
|
||||
|
||||
autoScroll<
|
||||
PayloadEntity extends DragEntity = DragEntity,
|
||||
PayloadFrom extends DragFrom = DragFromBlockSuite,
|
||||
>(options: AutoScroll<PayloadEntity, PayloadFrom>) {
|
||||
return autoScrollForElements(options as OriginalAutoScrollOption);
|
||||
}
|
||||
}
|
||||
118
blocksuite/framework/block-std/src/extension/dnd/types.ts
Normal file
118
blocksuite/framework/block-std/src/extension/dnd/types.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type {
|
||||
draggable,
|
||||
dropTargetForElements,
|
||||
ElementDropTargetGetFeedbackArgs,
|
||||
ElementMonitorGetFeedbackArgs,
|
||||
monitorForElements,
|
||||
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||
import type {
|
||||
DragLocation,
|
||||
// oxlint-disable-next-line no-unused-vars
|
||||
DragLocationHistory,
|
||||
DropTargetRecord,
|
||||
ElementDragType,
|
||||
} from '@atlaskit/pragmatic-drag-and-drop/types';
|
||||
import type { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element';
|
||||
|
||||
export type ElementDragEventBaseArgs<Payload, DropPayload = {}> = {
|
||||
/**
|
||||
* {@link DragLocationHistory} of the drag
|
||||
*/
|
||||
location: {
|
||||
/**
|
||||
* {@link DragLocationHistory.initial}
|
||||
*/
|
||||
initial: DragLocationWithPayload<DropPayload>;
|
||||
/**
|
||||
* {@link DragLocationHistory.current}
|
||||
*/
|
||||
current: DragLocationWithPayload<DropPayload>;
|
||||
/**
|
||||
* {@link DragLocationHistory.previous}
|
||||
*/
|
||||
previous: Pick<DragLocationWithPayload<DropPayload>, 'dropTargets'>;
|
||||
};
|
||||
source: Omit<ElementDragType['payload'], 'data'> & { data: Payload };
|
||||
};
|
||||
|
||||
export type DragLocationWithPayload<Payload> = Omit<
|
||||
DragLocation,
|
||||
'dropTargets'
|
||||
> & {
|
||||
dropTargets: DropTargetRecordWithPayload<Payload>[];
|
||||
};
|
||||
|
||||
export type DropTargetRecordWithPayload<Payload> = Omit<
|
||||
DropTargetRecord,
|
||||
'data'
|
||||
> & {
|
||||
data: Payload;
|
||||
};
|
||||
|
||||
export type ElementDragEventMap<DragPayload, DropPayload> = {
|
||||
onDragStart?: (
|
||||
data: ElementDragEventBaseArgs<DragPayload, DropPayload>
|
||||
) => void;
|
||||
onDrag?: (data: ElementDragEventBaseArgs<DragPayload, DropPayload>) => void;
|
||||
onDrop?: (data: ElementDragEventBaseArgs<DragPayload, DropPayload>) => void;
|
||||
onDropTargetChange?: (
|
||||
data: ElementDragEventBaseArgs<DragPayload, DropPayload>
|
||||
) => void;
|
||||
};
|
||||
|
||||
type DropTargetLocalizedData = {
|
||||
self: DropTargetRecord;
|
||||
};
|
||||
|
||||
export type ElementDropTargetFeedbackArgs<Payload> = Omit<
|
||||
ElementDropTargetGetFeedbackArgs,
|
||||
'source'
|
||||
> & {
|
||||
source: Omit<ElementDragType['payload'], 'data'> & { data: Payload };
|
||||
};
|
||||
|
||||
export type ElementDropEventMap<DragPayload, DropPayload> = {
|
||||
onDragStart?: (
|
||||
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
|
||||
DropTargetLocalizedData
|
||||
) => void;
|
||||
onDrag?: (
|
||||
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
|
||||
DropTargetLocalizedData
|
||||
) => void;
|
||||
onDrop?: (
|
||||
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
|
||||
DropTargetLocalizedData
|
||||
) => void;
|
||||
onDropTargetChange?: (
|
||||
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
|
||||
DropTargetLocalizedData
|
||||
) => void;
|
||||
onDragEnter?: (
|
||||
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
|
||||
DropTargetLocalizedData
|
||||
) => void;
|
||||
onDragLeave?: (
|
||||
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
|
||||
DropTargetLocalizedData
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type ElementMonitorFeedbackArgs<Payload> = Omit<
|
||||
ElementMonitorGetFeedbackArgs,
|
||||
'source'
|
||||
> & {
|
||||
source: Omit<ElementDragType['payload'], 'data'> & { data: Payload };
|
||||
};
|
||||
|
||||
export type OriginalDraggableOption = Parameters<typeof draggable>[0];
|
||||
|
||||
export type OriginalDropTargetOption = Parameters<
|
||||
typeof dropTargetForElements
|
||||
>[0];
|
||||
|
||||
export type OriginalMonitorOption = Parameters<typeof monitorForElements>[0];
|
||||
|
||||
export type OriginalAutoScrollOption = Parameters<
|
||||
typeof autoScrollForElements
|
||||
>[0];
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './block-view.js';
|
||||
export * from './command.js';
|
||||
export * from './config.js';
|
||||
export * from './dnd/index.js';
|
||||
export * from './flavour.js';
|
||||
export * from './keymap.js';
|
||||
export * from './lifecycle-watcher.js';
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { Clipboard } from '../clipboard/index.js';
|
||||
import { CommandManager } from '../command/index.js';
|
||||
import { UIEventDispatcher } from '../event/index.js';
|
||||
import { DndController } from '../extension/dnd/index.js';
|
||||
import type { BlockService } from '../extension/index.js';
|
||||
import { GfxController } from '../gfx/controller.js';
|
||||
import { GfxSelectionManager } from '../gfx/selection.js';
|
||||
@@ -44,6 +45,7 @@ const internalExtensions = [
|
||||
GfxSelectionManager,
|
||||
SurfaceMiddlewareExtension,
|
||||
ViewManager,
|
||||
DndController,
|
||||
];
|
||||
|
||||
export class BlockStdScope {
|
||||
@@ -63,6 +65,10 @@ export class BlockStdScope {
|
||||
return this.provider.getAll(LifeCycleWatcherIdentifier);
|
||||
}
|
||||
|
||||
get dnd() {
|
||||
return this.get(DndController);
|
||||
}
|
||||
|
||||
get clipboard() {
|
||||
return this.get(Clipboard);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
import { Slot } from '@blocksuite/global/utils';
|
||||
|
||||
import { LifeCycleWatcher } from '../extension/index.js';
|
||||
import type { BlockComponent, WidgetComponent } from './element/index.js';
|
||||
|
||||
type ViewUpdatePayload =
|
||||
| {
|
||||
id: string;
|
||||
type: 'delete';
|
||||
view: BlockComponent;
|
||||
}
|
||||
| {
|
||||
id: string;
|
||||
type: 'add';
|
||||
view: BlockComponent;
|
||||
};
|
||||
|
||||
export class ViewStore extends LifeCycleWatcher {
|
||||
static override readonly key = 'viewStore';
|
||||
|
||||
private readonly _blockMap = new Map<string, BlockComponent>();
|
||||
|
||||
viewUpdated: Slot<ViewUpdatePayload> = new Slot();
|
||||
|
||||
get views() {
|
||||
return Array.from(this._blockMap.values());
|
||||
}
|
||||
|
||||
private readonly _fromId = (
|
||||
blockId: string | undefined | null
|
||||
): BlockComponent | null => {
|
||||
@@ -19,7 +39,12 @@ export class ViewStore extends LifeCycleWatcher {
|
||||
private readonly _widgetMap = new Map<string, WidgetComponent>();
|
||||
|
||||
deleteBlock = (node: BlockComponent) => {
|
||||
this._blockMap.delete(node.id);
|
||||
this._blockMap.delete(node.model.id);
|
||||
this.viewUpdated.emit({
|
||||
id: node.model.id,
|
||||
type: 'delete',
|
||||
view: node,
|
||||
});
|
||||
};
|
||||
|
||||
deleteWidget = (node: WidgetComponent) => {
|
||||
@@ -41,7 +66,15 @@ export class ViewStore extends LifeCycleWatcher {
|
||||
};
|
||||
|
||||
setBlock = (node: BlockComponent) => {
|
||||
if (this._blockMap.has(node.model.id)) {
|
||||
this.deleteBlock(node);
|
||||
}
|
||||
this._blockMap.set(node.model.id, node);
|
||||
this.viewUpdated.emit({
|
||||
id: node.model.id,
|
||||
type: 'add',
|
||||
view: node,
|
||||
});
|
||||
};
|
||||
|
||||
setWidget = (node: WidgetComponent) => {
|
||||
|
||||
Reference in New Issue
Block a user