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:
Saul-Mirone
2025-04-24 03:21:53 +00:00
parent 2d3130eac9
commit 9d90899344
22 changed files with 101 additions and 50 deletions
+3 -1
View File
@@ -188,7 +188,9 @@
"./gfx/connector": "./src/gfx/connector/index.ts",
"./gfx/connector/store": "./src/gfx/connector/store.ts",
"./gfx/connector/view": "./src/gfx/connector/view.ts",
"./gfx/group": "./src/gfx/group.ts",
"./gfx/group": "./src/gfx/group/index.ts",
"./gfx/group/store": "./src/gfx/group/store.ts",
"./gfx/group/view": "./src/gfx/group/view.ts",
"./gfx/template": "./src/gfx/template/index.ts",
"./gfx/template/view": "./src/gfx/template/view.ts",
"./gfx/turbo-renderer": "./src/gfx/turbo-renderer.ts",
@@ -17,6 +17,7 @@ import { SurfaceRefStoreExtension } from '@blocksuite/affine-block-surface-ref/s
import { TableStoreExtension } from '@blocksuite/affine-block-table/store';
import { BrushStoreExtension } from '@blocksuite/affine-gfx-brush/store';
import { ConnectorStoreExtension } from '@blocksuite/affine-gfx-connector/store';
import { GroupStoreExtension } from '@blocksuite/affine-gfx-group/store';
import { MindmapStoreExtension } from '@blocksuite/affine-gfx-mindmap/store';
import { ShapeStoreExtension } from '@blocksuite/affine-gfx-shape/store';
import { FootnoteStoreExtension } from '@blocksuite/affine-inline-footnote/store';
@@ -57,6 +58,7 @@ export function getInternalStoreExtensions() {
ShapeStoreExtension,
MindmapStoreExtension,
ConnectorStoreExtension,
GroupStoreExtension,
MigratingStoreExtension,
];
@@ -17,6 +17,7 @@ import { SurfaceRefViewExtension } from '@blocksuite/affine-block-surface-ref/vi
import { TableViewExtension } from '@blocksuite/affine-block-table/view';
import { BrushViewExtension } from '@blocksuite/affine-gfx-brush/view';
import { ConnectorViewExtension } from '@blocksuite/affine-gfx-connector/view';
import { GroupViewExtension } from '@blocksuite/affine-gfx-group/view';
import { MindmapViewExtension } from '@blocksuite/affine-gfx-mindmap/view';
import { NoteViewExtension as GfxNoteViewExtension } from '@blocksuite/affine-gfx-note/view';
import { ShapeViewExtension } from '@blocksuite/affine-gfx-shape/view';
@@ -32,11 +33,13 @@ import { MigratingViewExtension } from './migrating-view';
export function getInternalViewExtensions() {
return [
// Gfx
GfxNoteViewExtension,
BrushViewExtension,
ShapeViewExtension,
MindmapViewExtension,
ConnectorViewExtension,
GroupViewExtension,
TemplateViewExtension,
// Block
@@ -0,0 +1 @@
export * from '@blocksuite/affine-gfx-group/store';
@@ -0,0 +1 @@
export * from '@blocksuite/affine-gfx-group/view';
@@ -1,12 +1,4 @@
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
import {
ConnectorElementRendererExtension,
ConnectorElementView,
} from '@blocksuite/affine-gfx-connector';
import {
GroupElementRendererExtension,
GroupElementView,
} from '@blocksuite/affine-gfx-group';
import {
TextElementRendererExtension,
TextElementView,
@@ -42,16 +34,10 @@ import { viewportOverlayWidget } from './widgets';
* Because in some cases we need to create edgeless elements in page mode.
* And these view may contain some logic when elements initialize.
*/
const EdgelessElementViews = [
ConnectorElementView,
GroupElementView,
TextElementView,
];
const EdgelessElementViews = [TextElementView];
export const EdgelessElementRendererExtension: ExtensionType[] = [
TextElementRendererExtension,
ConnectorElementRendererExtension,
GroupElementRendererExtension,
];
export const CommonSpecs: ExtensionType[] = [
@@ -1,4 +1,3 @@
import { groupToolbarExtension } from '@blocksuite/affine-gfx-group';
import { textToolbarExtension } from '@blocksuite/affine-gfx-text';
import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
import { BlockFlavourIdentifier } from '@blocksuite/std';
@@ -7,8 +6,6 @@ import type { ExtensionType } from '@blocksuite/store';
import { builtinLockedToolbarConfig, builtinMiscToolbarConfig } from './misc';
export const EdgelessElementToolbarExtension: ExtensionType[] = [
groupToolbarExtension,
textToolbarExtension,
ToolbarModuleExtension({
@@ -1,4 +1,3 @@
import { effects as gfxGroupEffects } from '@blocksuite/affine-gfx-group/effects';
import { effects as gfxCanvasTextEffects } from '@blocksuite/affine-gfx-text/effects';
import { effects as widgetEdgelessToolbarEffects } from '@blocksuite/affine-widget-edgeless-toolbar/effects';
import { effects as widgetMobileToolbarEffects } from '@blocksuite/affine-widget-keyboard-toolbar/effects';
@@ -73,7 +72,6 @@ function registerRootComponents() {
function registerGfxEffects() {
gfxCanvasTextEffects();
gfxGroupEffects();
}
function registerWidgets() {
@@ -11,7 +11,6 @@ import * as Y from 'yjs';
import { elementsCtorMap } from './element-model/index.js';
import { surfaceMiddlewareIdentifier } from './extensions/surface-middleware.js';
import { SurfaceBlockTransformer } from './surface-transformer.js';
import { groupRelationWatcher } from './watchers/group.js';
export const SurfaceBlockSchema = defineBlockSchema({
flavour: 'affine:surface',
@@ -50,10 +49,6 @@ export class SurfaceBlockModel extends BaseSurfaceModel {
.forEach(({ middleware }) => {
this._disposables.add(middleware(this));
});
[groupRelationWatcher(this)].forEach(disposable =>
this._disposables.add(disposable)
);
}
getConnectors(id: string) {
@@ -6,9 +6,11 @@ import {
import { ConnectionOverlay } from './connector-manager';
import { ConnectorTool } from './connector-tool';
import { effects } from './effects';
import { ConnectorElementRendererExtension } from './element-renderer';
import { ConnectorFilter } from './element-transform';
import { connectorToolbarExtension } from './toolbar/config';
import { connectorQuickTool } from './toolbar/quick-tool';
import { ConnectorElementView } from './view/view';
export class ConnectorViewExtension extends ViewExtensionProvider {
override name = 'affine-connector-gfx';
@@ -20,6 +22,8 @@ export class ConnectorViewExtension extends ViewExtensionProvider {
override setup(context: ViewExtensionContext) {
super.setup(context);
context.register(ConnectorElementView);
context.register(ConnectorElementRendererExtension);
if (this.isEdgeless(context.scope)) {
context.register(ConnectorTool);
context.register(ConnectorFilter);
+4 -1
View File
@@ -12,6 +12,7 @@
"dependencies": {
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-gfx-text": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*",
@@ -34,7 +35,9 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts"
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},
"files": [
"src",
@@ -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);
}
});
}
}
@@ -1,8 +1,11 @@
import { SurfaceGroupLikeModel } from '../element-model/base.js';
import type { SurfaceMiddleware } from '../extensions/surface-middleware.js';
import type { SurfaceBlockModel } from '../surface-model.js';
import {
type SurfaceBlockModel,
SurfaceGroupLikeModel,
type SurfaceMiddleware,
surfaceMiddlewareExtension,
} from '@blocksuite/affine-block-surface';
export const groupRelationWatcher: SurfaceMiddleware = (
const groupRelationWatcher: SurfaceMiddleware = (
surface: SurfaceBlockModel
) => {
const disposables = [
@@ -26,3 +29,8 @@ export const groupRelationWatcher: SurfaceMiddleware = (
disposables.forEach(d => d.unsubscribe());
};
};
export const groupRelationWatcherExtension = surfaceMiddlewareExtension(
'group-relation-watcher',
groupRelationWatcher
);
+1 -1
View File
@@ -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
View 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);
}
}
+20 -17
View File
@@ -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);
}
}
}
@@ -9,6 +9,7 @@
"references": [
{ "path": "../../blocks/surface" },
{ "path": "../../components" },
{ "path": "../../ext-loader" },
{ "path": "../text" },
{ "path": "../../model" },
{ "path": "../../rich-text" },
+1 -1
View File
@@ -2,6 +2,6 @@ export * from './adapter';
export * from './commands';
export * from './edgeless-text-editor';
export * from './element-renderer';
export * from './element-view';
export * from './tool';
export * from './toolbar';
export * from './view';
+1
View File
@@ -530,6 +530,7 @@ export const PackageList = [
workspaceDependencies: [
'blocksuite/affine/blocks/surface',
'blocksuite/affine/components',
'blocksuite/affine/ext-loader',
'blocksuite/affine/gfx/text',
'blocksuite/affine/model',
'blocksuite/affine/rich-text',
+1
View File
@@ -3159,6 +3159,7 @@ __metadata:
dependencies:
"@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-ext-loader": "workspace:*"
"@blocksuite/affine-gfx-text": "workspace:*"
"@blocksuite/affine-model": "workspace:*"
"@blocksuite/affine-rich-text": "workspace:*"