feat(editor): group gfx extension (#11950)
Closes: BS-3208 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new group view extension, enhancing how group elements are handled and displayed. - Added new store and view modules for group-related features, improving modularity and integration. - **Refactor** - Updated the group element architecture to use extension-based registration for views, toolbars, and effects. - Simplified and reorganized exports for group and text modules. - **Chores** - Updated dependencies and project references to improve build consistency and compatibility. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
24
blocksuite/affine/gfx/group/src/element-view.ts
Normal file
24
blocksuite/affine/gfx/group/src/element-view.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { GroupElementModel } from '@blocksuite/affine-model';
|
||||
import { GfxElementModelView } from '@blocksuite/std/gfx';
|
||||
|
||||
import { mountGroupTitleEditor } from './text/edgeless-group-title-editor';
|
||||
|
||||
export class GroupElementView extends GfxElementModelView<GroupElementModel> {
|
||||
static override type: string = 'group';
|
||||
|
||||
override onCreated(): void {
|
||||
super.onCreated();
|
||||
|
||||
this._initDblClickToEdit();
|
||||
}
|
||||
|
||||
private _initDblClickToEdit(): void {
|
||||
this.on('dblclick', () => {
|
||||
const edgeless = this.std.view.getBlock(this.std.store.root!.id);
|
||||
|
||||
if (edgeless && !this.model.isLocked()) {
|
||||
mountGroupTitleEditor(this.model, edgeless);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
36
blocksuite/affine/gfx/group/src/group-watcher.ts
Normal file
36
blocksuite/affine/gfx/group/src/group-watcher.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
type SurfaceBlockModel,
|
||||
SurfaceGroupLikeModel,
|
||||
type SurfaceMiddleware,
|
||||
surfaceMiddlewareExtension,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
|
||||
const groupRelationWatcher: SurfaceMiddleware = (
|
||||
surface: SurfaceBlockModel
|
||||
) => {
|
||||
const disposables = [
|
||||
surface.elementUpdated.subscribe(({ id, props, local }) => {
|
||||
if (!local) return;
|
||||
|
||||
const element = surface.getElementById(id)!;
|
||||
|
||||
// remove the group if it has no children
|
||||
if (
|
||||
element instanceof SurfaceGroupLikeModel &&
|
||||
props['childIds'] &&
|
||||
element.childIds.length === 0
|
||||
) {
|
||||
surface.deleteElement(id);
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
return () => {
|
||||
disposables.forEach(d => d.unsubscribe());
|
||||
};
|
||||
};
|
||||
|
||||
export const groupRelationWatcherExtension = surfaceMiddlewareExtension(
|
||||
'group-relation-watcher',
|
||||
groupRelationWatcher
|
||||
);
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from './adapter';
|
||||
export * from './command';
|
||||
export * from './element-renderer';
|
||||
export * from './element-view';
|
||||
export * from './text/text';
|
||||
export * from './toolbar/config';
|
||||
export * from './view';
|
||||
|
||||
21
blocksuite/affine/gfx/group/src/store.ts
Normal file
21
blocksuite/affine/gfx/group/src/store.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
type StoreExtensionContext,
|
||||
StoreExtensionProvider,
|
||||
} from '@blocksuite/affine-ext-loader';
|
||||
|
||||
import {
|
||||
groupToMarkdownAdapterMatcher,
|
||||
groupToPlainTextAdapterMatcher,
|
||||
} from './adapter';
|
||||
import { groupRelationWatcherExtension } from './group-watcher';
|
||||
|
||||
export class GroupStoreExtension extends StoreExtensionProvider {
|
||||
override name = 'affine-group-gfx';
|
||||
|
||||
override setup(context: StoreExtensionContext) {
|
||||
super.setup(context);
|
||||
context.register(groupToPlainTextAdapterMatcher);
|
||||
context.register(groupToMarkdownAdapterMatcher);
|
||||
context.register(groupRelationWatcherExtension);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,27 @@
|
||||
import type { GroupElementModel } from '@blocksuite/affine-model';
|
||||
import { GfxElementModelView } from '@blocksuite/std/gfx';
|
||||
import {
|
||||
type ViewExtensionContext,
|
||||
ViewExtensionProvider,
|
||||
} from '@blocksuite/affine-ext-loader';
|
||||
|
||||
import { mountGroupTitleEditor } from './text/edgeless-group-title-editor';
|
||||
import { effects } from './effects';
|
||||
import { GroupElementRendererExtension } from './element-renderer';
|
||||
import { GroupElementView } from './element-view';
|
||||
import { groupToolbarExtension } from './toolbar/config';
|
||||
|
||||
export class GroupElementView extends GfxElementModelView<GroupElementModel> {
|
||||
static override type: string = 'group';
|
||||
export class GroupViewExtension extends ViewExtensionProvider {
|
||||
override name = 'affine-group-gfx';
|
||||
|
||||
override onCreated(): void {
|
||||
super.onCreated();
|
||||
|
||||
this._initDblClickToEdit();
|
||||
override effect(): void {
|
||||
super.effect();
|
||||
effects();
|
||||
}
|
||||
|
||||
private _initDblClickToEdit(): void {
|
||||
this.on('dblclick', () => {
|
||||
const edgeless = this.std.view.getBlock(this.std.store.root!.id);
|
||||
|
||||
if (edgeless && !this.model.isLocked()) {
|
||||
mountGroupTitleEditor(this.model, edgeless);
|
||||
}
|
||||
});
|
||||
override setup(context: ViewExtensionContext) {
|
||||
super.setup(context);
|
||||
context.register(GroupElementRendererExtension);
|
||||
context.register(GroupElementView);
|
||||
if (this.isEdgeless(context.scope)) {
|
||||
context.register(groupToolbarExtension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user