refactor(editor): improve edgeless editors (#11019)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
EdgelessCRUDIdentifier,
|
EdgelessCRUDIdentifier,
|
||||||
|
getSurfaceBlock,
|
||||||
TextUtils,
|
TextUtils,
|
||||||
} from '@blocksuite/affine-block-surface';
|
} from '@blocksuite/affine-block-surface';
|
||||||
import type { ConnectorElementModel } from '@blocksuite/affine-model';
|
import type { ConnectorElementModel } from '@blocksuite/affine-model';
|
||||||
@@ -7,9 +8,11 @@ import type { RichText } from '@blocksuite/affine-rich-text';
|
|||||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||||
import { almostEqual } from '@blocksuite/affine-shared/utils';
|
import { almostEqual } from '@blocksuite/affine-shared/utils';
|
||||||
import {
|
import {
|
||||||
|
type BlockComponent,
|
||||||
RANGE_SYNC_EXCLUDE_ATTR,
|
RANGE_SYNC_EXCLUDE_ATTR,
|
||||||
ShadowlessElement,
|
ShadowlessElement,
|
||||||
} from '@blocksuite/block-std';
|
} from '@blocksuite/block-std';
|
||||||
|
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||||
import { Bound, Vec } from '@blocksuite/global/gfx';
|
import { Bound, Vec } from '@blocksuite/global/gfx';
|
||||||
import { WithDisposable } from '@blocksuite/global/lit';
|
import { WithDisposable } from '@blocksuite/global/lit';
|
||||||
import { css, html, nothing } from 'lit';
|
import { css, html, nothing } from 'lit';
|
||||||
@@ -17,8 +20,6 @@ import { property, query } from 'lit/decorators.js';
|
|||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
|
||||||
|
|
||||||
const HORIZONTAL_PADDING = 2;
|
const HORIZONTAL_PADDING = 2;
|
||||||
const VERTICAL_PADDING = 2;
|
const VERTICAL_PADDING = 2;
|
||||||
const BORDER_WIDTH = 1;
|
const BORDER_WIDTH = 1;
|
||||||
@@ -63,6 +64,14 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
|
|||||||
return this.edgeless.std.get(EdgelessCRUDIdentifier);
|
return this.edgeless.std.get(EdgelessCRUDIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get gfx() {
|
||||||
|
return this.edgeless.std.get(GfxControllerIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
get selection() {
|
||||||
|
return this.gfx.selection;
|
||||||
|
}
|
||||||
|
|
||||||
private _isComposition = false;
|
private _isComposition = false;
|
||||||
|
|
||||||
private _keeping = false;
|
private _keeping = false;
|
||||||
@@ -113,8 +122,9 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override firstUpdated() {
|
override firstUpdated() {
|
||||||
const { edgeless, connector } = this;
|
const { edgeless, connector, selection } = this;
|
||||||
const { dispatcher } = edgeless;
|
const dispatcher = edgeless.std.event;
|
||||||
|
const store = edgeless.std.store;
|
||||||
|
|
||||||
this._resizeObserver = new ResizeObserver(() => {
|
this._resizeObserver = new ResizeObserver(() => {
|
||||||
this._updateLabelRect();
|
this._updateLabelRect();
|
||||||
@@ -142,7 +152,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
|
|||||||
if (!isComposing && (isModEnter || isEscape)) {
|
if (!isComposing && (isModEnter || isEscape)) {
|
||||||
this.inlineEditorContainer?.blur();
|
this.inlineEditorContainer?.blur();
|
||||||
|
|
||||||
edgeless.service.selection.set({
|
selection.set({
|
||||||
elements: [connector.id],
|
elements: [connector.id],
|
||||||
editing: false,
|
editing: false,
|
||||||
});
|
});
|
||||||
@@ -152,14 +162,18 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.disposables.add(
|
const surface = getSurfaceBlock(store);
|
||||||
edgeless.service.surface.elementUpdated.subscribe(({ id }) => {
|
|
||||||
if (id === connector.id) this.requestUpdate();
|
if (surface) {
|
||||||
})
|
this.disposables.add(
|
||||||
);
|
surface.elementUpdated.subscribe(({ id }) => {
|
||||||
|
if (id === connector.id) this.requestUpdate();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.disposables.add(
|
this.disposables.add(
|
||||||
edgeless.service.viewport.viewportUpdated.subscribe(() => {
|
this.gfx.viewport.viewportUpdated.subscribe(() => {
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -189,7 +203,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
|
|||||||
|
|
||||||
connector.lableEditing = false;
|
connector.lableEditing = false;
|
||||||
|
|
||||||
edgeless.service.selection.set({
|
selection.set({
|
||||||
elements: [],
|
elements: [],
|
||||||
editing: false,
|
editing: false,
|
||||||
});
|
});
|
||||||
@@ -254,7 +268,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
|
|||||||
fontSize,
|
fontSize,
|
||||||
fontWeight
|
fontWeight
|
||||||
);
|
);
|
||||||
const { translateX, translateY, zoom } = this.edgeless.service.viewport;
|
const { translateX, translateY, zoom } = this.gfx.viewport;
|
||||||
const [x, y] = Vec.mul(connector.getPointByOffsetDistance(distance), zoom);
|
const [x, y] = Vec.mul(connector.getPointByOffsetDistance(distance), zoom);
|
||||||
const transformOperation = [
|
const transformOperation = [
|
||||||
'translate(-50%, -50%)',
|
'translate(-50%, -50%)',
|
||||||
@@ -316,7 +330,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
|
|||||||
accessor connector!: ConnectorElementModel;
|
accessor connector!: ConnectorElementModel;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor edgeless!: EdgelessRootBlockComponent;
|
accessor edgeless!: BlockComponent;
|
||||||
|
|
||||||
@query('rich-text')
|
@query('rich-text')
|
||||||
accessor richText!: RichText;
|
accessor richText!: RichText;
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import {
|
|||||||
frameTitleStyleVars,
|
frameTitleStyleVars,
|
||||||
} from '@blocksuite/affine-widget-frame-title';
|
} from '@blocksuite/affine-widget-frame-title';
|
||||||
import {
|
import {
|
||||||
|
type BlockComponent,
|
||||||
RANGE_SYNC_EXCLUDE_ATTR,
|
RANGE_SYNC_EXCLUDE_ATTR,
|
||||||
ShadowlessElement,
|
ShadowlessElement,
|
||||||
} from '@blocksuite/block-std';
|
} from '@blocksuite/block-std';
|
||||||
|
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||||
import { Bound } from '@blocksuite/global/gfx';
|
import { Bound } from '@blocksuite/global/gfx';
|
||||||
import { WithDisposable } from '@blocksuite/global/lit';
|
import { WithDisposable } from '@blocksuite/global/lit';
|
||||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
@@ -16,8 +18,6 @@ import { css, html, nothing } from 'lit';
|
|||||||
import { property, query } from 'lit/decorators.js';
|
import { property, query } from 'lit/decorators.js';
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
|
|
||||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
|
||||||
|
|
||||||
export class EdgelessFrameTitleEditor extends WithDisposable(
|
export class EdgelessFrameTitleEditor extends WithDisposable(
|
||||||
ShadowlessElement
|
ShadowlessElement
|
||||||
) {
|
) {
|
||||||
@@ -46,10 +46,18 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
|
|||||||
return this.richText?.inlineEditor;
|
return this.richText?.inlineEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get gfx() {
|
||||||
|
return this.edgeless.std.get(GfxControllerIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
get selection() {
|
||||||
|
return this.gfx.selection;
|
||||||
|
}
|
||||||
|
|
||||||
private _unmount() {
|
private _unmount() {
|
||||||
// dispose in advance to avoid execute `this.remove()` twice
|
// dispose in advance to avoid execute `this.remove()` twice
|
||||||
this.disposables.dispose();
|
this.disposables.dispose();
|
||||||
this.edgeless.service.selection.set({
|
this.selection.set({
|
||||||
elements: [],
|
elements: [],
|
||||||
editing: false,
|
editing: false,
|
||||||
});
|
});
|
||||||
@@ -62,7 +70,7 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override firstUpdated(): void {
|
override firstUpdated(): void {
|
||||||
const dispatcher = this.edgeless.dispatcher;
|
const dispatcher = this.edgeless.std.event;
|
||||||
this.updateComplete
|
this.updateComplete
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (!this.inlineEditor) return;
|
if (!this.inlineEditor) return;
|
||||||
@@ -87,7 +95,7 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.disposables.add(
|
this.disposables.add(
|
||||||
this.edgeless.service.viewport.viewportUpdated.subscribe(() => {
|
this.gfx.viewport.viewportUpdated.subscribe(() => {
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -117,10 +125,10 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
|
|||||||
const rootBlockId = this.editorHost.doc.root?.id;
|
const rootBlockId = this.editorHost.doc.root?.id;
|
||||||
if (!rootBlockId) return nothing;
|
if (!rootBlockId) return nothing;
|
||||||
|
|
||||||
const viewport = this.edgeless.service.viewport;
|
const viewport = this.gfx.viewport;
|
||||||
const bound = Bound.deserialize(this.frameModel.xywh);
|
const bound = Bound.deserialize(this.frameModel.xywh);
|
||||||
const [x, y] = viewport.toViewCoord(bound.x, bound.y);
|
const [x, y] = viewport.toViewCoord(bound.x, bound.y);
|
||||||
const isInner = this.edgeless.service.gfx.grid.has(
|
const isInner = this.gfx.grid.has(
|
||||||
this.frameModel.elementBound,
|
this.frameModel.elementBound,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
@@ -167,7 +175,7 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor edgeless!: EdgelessRootBlockComponent;
|
accessor edgeless!: BlockComponent;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor frameModel!: FrameBlockModel;
|
accessor frameModel!: FrameBlockModel;
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import {
|
|||||||
import type { GroupElementModel } from '@blocksuite/affine-model';
|
import type { GroupElementModel } from '@blocksuite/affine-model';
|
||||||
import type { RichText } from '@blocksuite/affine-rich-text';
|
import type { RichText } from '@blocksuite/affine-rich-text';
|
||||||
import {
|
import {
|
||||||
|
type BlockComponent,
|
||||||
RANGE_SYNC_EXCLUDE_ATTR,
|
RANGE_SYNC_EXCLUDE_ATTR,
|
||||||
ShadowlessElement,
|
ShadowlessElement,
|
||||||
} from '@blocksuite/block-std';
|
} from '@blocksuite/block-std';
|
||||||
|
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||||
import { Bound } from '@blocksuite/global/gfx';
|
import { Bound } from '@blocksuite/global/gfx';
|
||||||
import { WithDisposable } from '@blocksuite/global/lit';
|
import { WithDisposable } from '@blocksuite/global/lit';
|
||||||
import { html, nothing } from 'lit';
|
import { html, nothing } from 'lit';
|
||||||
import { property, query } from 'lit/decorators.js';
|
import { property, query } from 'lit/decorators.js';
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
|
|
||||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
|
||||||
|
|
||||||
export class EdgelessGroupTitleEditor extends WithDisposable(
|
export class EdgelessGroupTitleEditor extends WithDisposable(
|
||||||
ShadowlessElement
|
ShadowlessElement
|
||||||
) {
|
) {
|
||||||
@@ -28,11 +28,19 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
|
|||||||
return this.inlineEditor?.rootElement;
|
return this.inlineEditor?.rootElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get gfx() {
|
||||||
|
return this.edgeless.std.get(GfxControllerIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
get selection() {
|
||||||
|
return this.gfx.selection;
|
||||||
|
}
|
||||||
|
|
||||||
private _unmount() {
|
private _unmount() {
|
||||||
// dispose in advance to avoid execute `this.remove()` twice
|
// dispose in advance to avoid execute `this.remove()` twice
|
||||||
this.disposables.dispose();
|
this.disposables.dispose();
|
||||||
this.group.showTitle = true;
|
this.group.showTitle = true;
|
||||||
this.edgeless.service.selection.set({
|
this.selection.set({
|
||||||
elements: [this.group.id],
|
elements: [this.group.id],
|
||||||
editing: false,
|
editing: false,
|
||||||
});
|
});
|
||||||
@@ -45,7 +53,7 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override firstUpdated(): void {
|
override firstUpdated(): void {
|
||||||
const dispatcher = this.edgeless.dispatcher;
|
const dispatcher = this.edgeless.std.event;
|
||||||
|
|
||||||
this.updateComplete
|
this.updateComplete
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -72,7 +80,7 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.disposables.add(
|
this.disposables.add(
|
||||||
this.edgeless.service.viewport.viewportUpdated.subscribe(() => {
|
this.gfx.viewport.viewportUpdated.subscribe(() => {
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -103,7 +111,7 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
|
|||||||
console.error('group.externalXYWH is not set');
|
console.error('group.externalXYWH is not set');
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
const viewport = this.edgeless.service.viewport;
|
const viewport = this.gfx.viewport;
|
||||||
const bound = Bound.deserialize(this.group.externalXYWH);
|
const bound = Bound.deserialize(this.group.externalXYWH);
|
||||||
const [x, y] = viewport.toViewCoord(bound.x, bound.y);
|
const [x, y] = viewport.toViewCoord(bound.x, bound.y);
|
||||||
|
|
||||||
@@ -137,7 +145,7 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor edgeless!: EdgelessRootBlockComponent;
|
accessor edgeless!: BlockComponent;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor group!: GroupElementModel;
|
accessor group!: GroupElementModel;
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import type { RichText } from '@blocksuite/affine-rich-text';
|
|||||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||||
import { getSelectedRect } from '@blocksuite/affine-shared/utils';
|
import { getSelectedRect } from '@blocksuite/affine-shared/utils';
|
||||||
import {
|
import {
|
||||||
|
type BlockComponent,
|
||||||
RANGE_SYNC_EXCLUDE_ATTR,
|
RANGE_SYNC_EXCLUDE_ATTR,
|
||||||
ShadowlessElement,
|
ShadowlessElement,
|
||||||
} from '@blocksuite/block-std';
|
} from '@blocksuite/block-std';
|
||||||
|
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||||
import { Bound, toRadian, Vec } from '@blocksuite/global/gfx';
|
import { Bound, toRadian, Vec } from '@blocksuite/global/gfx';
|
||||||
import { WithDisposable } from '@blocksuite/global/lit';
|
import { WithDisposable } from '@blocksuite/global/lit';
|
||||||
import { html, nothing } from 'lit';
|
import { html, nothing } from 'lit';
|
||||||
@@ -18,13 +20,7 @@ import { property, query } from 'lit/decorators.js';
|
|||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
|
||||||
|
|
||||||
export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
||||||
get crud() {
|
|
||||||
return this.edgeless.std.get(EdgelessCRUDIdentifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _keeping = false;
|
private _keeping = false;
|
||||||
|
|
||||||
private _lastXYWH = '';
|
private _lastXYWH = '';
|
||||||
@@ -35,6 +31,18 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
return this.richText.inlineEditor;
|
return this.richText.inlineEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get crud() {
|
||||||
|
return this.edgeless.std.get(EdgelessCRUDIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
get gfx() {
|
||||||
|
return this.edgeless.std.get(GfxControllerIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
get selection() {
|
||||||
|
return this.gfx.selection;
|
||||||
|
}
|
||||||
|
|
||||||
get inlineEditorContainer() {
|
get inlineEditorContainer() {
|
||||||
return this.inlineEditor?.rootElement;
|
return this.inlineEditor?.rootElement;
|
||||||
}
|
}
|
||||||
@@ -47,8 +55,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
if (!this.isMindMapNode) {
|
if (!this.isMindMapNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const selection = this.selection;
|
||||||
const service = this.edgeless.service;
|
|
||||||
|
|
||||||
this._disposables.addFromEvent(this, 'keydown', evt => {
|
this._disposables.addFromEvent(this, 'keydown', evt => {
|
||||||
switch (evt.key) {
|
switch (evt.key) {
|
||||||
@@ -57,7 +64,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
if (evt.shiftKey || evt.isComposing) return;
|
if (evt.shiftKey || evt.isComposing) return;
|
||||||
|
|
||||||
(this.ownerDocument.activeElement as HTMLElement).blur();
|
(this.ownerDocument.activeElement as HTMLElement).blur();
|
||||||
service.selection.set({
|
selection.set({
|
||||||
elements: [this.element.id],
|
elements: [this.element.id],
|
||||||
editing: false,
|
editing: false,
|
||||||
});
|
});
|
||||||
@@ -67,7 +74,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
case 'Tab': {
|
case 'Tab': {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
(this.ownerDocument.activeElement as HTMLElement).blur();
|
(this.ownerDocument.activeElement as HTMLElement).blur();
|
||||||
service.selection.set({
|
selection.set({
|
||||||
elements: [this.element.id],
|
elements: [this.element.id],
|
||||||
editing: false,
|
editing: false,
|
||||||
});
|
});
|
||||||
@@ -109,7 +116,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
this.element.textDisplay = true;
|
this.element.textDisplay = true;
|
||||||
|
|
||||||
this.remove();
|
this.remove();
|
||||||
this.edgeless.service.selection.set({
|
this.selection.set({
|
||||||
elements: [],
|
elements: [],
|
||||||
editing: false,
|
editing: false,
|
||||||
});
|
});
|
||||||
@@ -134,8 +141,10 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
toRadian(-this.element.rotate)
|
toRadian(-this.element.rotate)
|
||||||
);
|
);
|
||||||
|
|
||||||
const [modelLeftTopX, modelLeftTopY] =
|
const [modelLeftTopX, modelLeftTopY] = this.gfx.viewport.toModelCoord(
|
||||||
this.edgeless.service.viewport.toModelCoord(leftTopX, leftTopY);
|
leftTopX,
|
||||||
|
leftTopY
|
||||||
|
);
|
||||||
|
|
||||||
this.crud.updateElement(this.element.id, {
|
this.crud.updateElement(this.element.id, {
|
||||||
xywh: new Bound(
|
xywh: new Bound(
|
||||||
@@ -161,7 +170,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
this.richText.style.minHeight = `${containerHeight}px`;
|
this.richText.style.minHeight = `${containerHeight}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.edgeless.service.selection.set({
|
this.selection.set({
|
||||||
elements: [this.element.id],
|
elements: [this.element.id],
|
||||||
editing: true,
|
editing: true,
|
||||||
});
|
});
|
||||||
@@ -173,12 +182,12 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override firstUpdated(): void {
|
override firstUpdated(): void {
|
||||||
const dispatcher = this.edgeless.dispatcher;
|
const dispatcher = this.edgeless.std.event;
|
||||||
|
|
||||||
this.element.textDisplay = false;
|
this.element.textDisplay = false;
|
||||||
|
|
||||||
this.disposables.add(
|
this.disposables.add(
|
||||||
this.edgeless.service.viewport.viewportUpdated.subscribe(() => {
|
this.gfx.viewport.viewportUpdated.subscribe(() => {
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
this.updateComplete
|
this.updateComplete
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -228,7 +237,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
this.disposables.addFromEvent(this, 'keydown', evt => {
|
this.disposables.addFromEvent(this, 'keydown', evt => {
|
||||||
if (evt.key === 'Escape') {
|
if (evt.key === 'Escape') {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this.edgeless.service.selection.set({
|
this.selection.set({
|
||||||
elements: [this.element.id],
|
elements: [this.element.id],
|
||||||
editing: false,
|
editing: false,
|
||||||
});
|
});
|
||||||
@@ -256,7 +265,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
|
|
||||||
const [verticalPadding, horiPadding] = this.element.padding;
|
const [verticalPadding, horiPadding] = this.element.padding;
|
||||||
const textResizing = this.element.textResizing;
|
const textResizing = this.element.textResizing;
|
||||||
const viewport = this.edgeless.service.viewport;
|
const viewport = this.gfx.viewport;
|
||||||
const zoom = viewport.zoom;
|
const zoom = viewport.zoom;
|
||||||
const rect = getSelectedRect([this.element]);
|
const rect = getSelectedRect([this.element]);
|
||||||
const rotate = this.element.rotate;
|
const rotate = this.element.rotate;
|
||||||
@@ -265,10 +274,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
[rect.left + rect.width / 2, rect.top + rect.height / 2],
|
[rect.left + rect.width / 2, rect.top + rect.height / 2],
|
||||||
toRadian(rotate)
|
toRadian(rotate)
|
||||||
);
|
);
|
||||||
const [x, y] = this.edgeless.service.viewport.toViewCoord(
|
const [x, y] = this.gfx.viewport.toViewCoord(leftTopX, leftTopY);
|
||||||
leftTopX,
|
|
||||||
leftTopY
|
|
||||||
);
|
|
||||||
const autoWidth = textResizing === TextResizing.AUTO_WIDTH_AND_HEIGHT;
|
const autoWidth = textResizing === TextResizing.AUTO_WIDTH_AND_HEIGHT;
|
||||||
const color = this.edgeless.std
|
const color = this.edgeless.std
|
||||||
.get(ThemeProvider)
|
.get(ThemeProvider)
|
||||||
@@ -344,17 +350,14 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor edgeless!: EdgelessRootBlockComponent;
|
accessor edgeless!: BlockComponent;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor element!: ShapeElementModel;
|
accessor element!: ShapeElementModel;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor mountEditor:
|
accessor mountEditor:
|
||||||
| ((
|
| ((element: ShapeElementModel, edgeless: BlockComponent) => void)
|
||||||
element: ShapeElementModel,
|
|
||||||
edgeless: EdgelessRootBlockComponent
|
|
||||||
) => void)
|
|
||||||
| undefined = undefined;
|
| undefined = undefined;
|
||||||
|
|
||||||
@query('rich-text')
|
@query('rich-text')
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
handleNativeRangeAtPoint,
|
handleNativeRangeAtPoint,
|
||||||
resetNativeSelection,
|
resetNativeSelection,
|
||||||
} from '@blocksuite/affine-shared/utils';
|
} from '@blocksuite/affine-shared/utils';
|
||||||
import type { PointerEventState } from '@blocksuite/block-std';
|
import type { BlockComponent, PointerEventState } from '@blocksuite/block-std';
|
||||||
import {
|
import {
|
||||||
BaseTool,
|
BaseTool,
|
||||||
getTopElements,
|
getTopElements,
|
||||||
@@ -41,12 +41,11 @@ import {
|
|||||||
import { DisposableGroup } from '@blocksuite/global/disposable';
|
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||||
import type { IVec } from '@blocksuite/global/gfx';
|
import type { IVec } from '@blocksuite/global/gfx';
|
||||||
import { Bound, getCommonBoundWithRotation, Vec } from '@blocksuite/global/gfx';
|
import { Bound, getCommonBoundWithRotation, Vec } from '@blocksuite/global/gfx';
|
||||||
import { noop } from '@blocksuite/global/utils';
|
|
||||||
import { effect } from '@preact/signals-core';
|
import { effect } from '@preact/signals-core';
|
||||||
import clamp from 'lodash-es/clamp';
|
import clamp from 'lodash-es/clamp';
|
||||||
import last from 'lodash-es/last';
|
import last from 'lodash-es/last';
|
||||||
|
|
||||||
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
|
import type { EdgelessRootBlockComponent } from '../index.js';
|
||||||
import { prepareCloneData } from '../utils/clone-utils.js';
|
import { prepareCloneData } from '../utils/clone-utils.js';
|
||||||
import { calPanDelta } from '../utils/panning-utils.js';
|
import { calPanDelta } from '../utils/panning-utils.js';
|
||||||
import { isCanvasElement, isEdgelessTextBlock } from '../utils/query.js';
|
import { isCanvasElement, isEdgelessTextBlock } from '../utils/query.js';
|
||||||
@@ -191,10 +190,8 @@ export class DefaultTool extends BaseTool {
|
|||||||
|
|
||||||
enableHover = true;
|
enableHover = true;
|
||||||
|
|
||||||
private get _edgeless(): EdgelessRootBlockComponent | null {
|
private get _edgeless(): BlockComponent | null {
|
||||||
const block = this.std.view.getBlock(this.doc.root!.id);
|
return this.std.view.getBlock(this.doc.root!.id);
|
||||||
|
|
||||||
return (block as EdgelessRootBlockComponent) ?? null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -243,7 +240,11 @@ export class DefaultTool extends BaseTool {
|
|||||||
private async _cloneContent() {
|
private async _cloneContent() {
|
||||||
if (!this._edgeless) return;
|
if (!this._edgeless) return;
|
||||||
|
|
||||||
const clipboardController = this._edgeless?.clipboardController;
|
// FIXME: edgeless clipboard should be an extension
|
||||||
|
const clipboardController = (
|
||||||
|
this._edgeless as EdgelessRootBlockComponent | null
|
||||||
|
)?.clipboardController;
|
||||||
|
if (!clipboardController) return;
|
||||||
const snapshot = prepareCloneData(this._toBeMoved, this.std);
|
const snapshot = prepareCloneData(this._toBeMoved, this.std);
|
||||||
|
|
||||||
const bound = getCommonBoundWithRotation(this._toBeMoved);
|
const bound = getCommonBoundWithRotation(this._toBeMoved);
|
||||||
@@ -551,7 +552,6 @@ export class DefaultTool extends BaseTool {
|
|||||||
this._stopAutoPanning();
|
this._stopAutoPanning();
|
||||||
this._clearDisposable();
|
this._clearDisposable();
|
||||||
this._accumulateDelta = [0, 0];
|
this._accumulateDelta = [0, 0];
|
||||||
noop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override doubleClick(e: PointerEventState) {
|
override doubleClick(e: PointerEventState) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
EdgelessFrameManager,
|
EdgelessFrameManager,
|
||||||
|
EdgelessFrameManagerIdentifier,
|
||||||
isFrameBlock,
|
isFrameBlock,
|
||||||
} from '@blocksuite/affine-block-frame';
|
} from '@blocksuite/affine-block-frame';
|
||||||
import { isNoteBlock } from '@blocksuite/affine-block-surface';
|
import { isNoteBlock } from '@blocksuite/affine-block-surface';
|
||||||
@@ -13,6 +14,7 @@ import type {
|
|||||||
ShapeElementModel,
|
ShapeElementModel,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import { getElementsWithoutGroup } from '@blocksuite/affine-shared/utils';
|
import { getElementsWithoutGroup } from '@blocksuite/affine-shared/utils';
|
||||||
|
import type { BlockComponent } from '@blocksuite/block-std';
|
||||||
import {
|
import {
|
||||||
generateKeyBetweenV2,
|
generateKeyBetweenV2,
|
||||||
type GfxModel,
|
type GfxModel,
|
||||||
@@ -99,7 +101,7 @@ type FrameSnapshot = BlockSnapshot & {
|
|||||||
|
|
||||||
export function createNewPresentationIndexes(
|
export function createNewPresentationIndexes(
|
||||||
raw: (SerializedElement | BlockSnapshot)[],
|
raw: (SerializedElement | BlockSnapshot)[],
|
||||||
edgeless: EdgelessRootBlockComponent
|
edgeless: BlockComponent
|
||||||
) {
|
) {
|
||||||
const frames = raw
|
const frames = raw
|
||||||
.filter((block): block is FrameSnapshot => {
|
.filter((block): block is FrameSnapshot => {
|
||||||
@@ -108,7 +110,7 @@ export function createNewPresentationIndexes(
|
|||||||
})
|
})
|
||||||
.sort((a, b) => EdgelessFrameManager.framePresentationComparator(a, b));
|
.sort((a, b) => EdgelessFrameManager.framePresentationComparator(a, b));
|
||||||
|
|
||||||
const frameMgr = edgeless.service.frame;
|
const frameMgr = edgeless.std.get(EdgelessFrameManagerIdentifier);
|
||||||
let before = frameMgr.generatePresentationIndex();
|
let before = frameMgr.generatePresentationIndex();
|
||||||
const result = new Map<string, string>();
|
const result = new Map<string, string>();
|
||||||
frames.forEach(frame => {
|
frames.forEach(frame => {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import type {
|
|||||||
GroupElementModel,
|
GroupElementModel,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import { ShapeElementModel } from '@blocksuite/affine-model';
|
import { ShapeElementModel } from '@blocksuite/affine-model';
|
||||||
|
import type { BlockComponent } from '@blocksuite/block-std';
|
||||||
|
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||||
import type { IVec } from '@blocksuite/global/gfx';
|
import type { IVec } from '@blocksuite/global/gfx';
|
||||||
import { Bound } from '@blocksuite/global/gfx';
|
import { Bound } from '@blocksuite/global/gfx';
|
||||||
@@ -14,28 +16,31 @@ import { EdgelessConnectorLabelEditor } from '../components/text/edgeless-connec
|
|||||||
import { EdgelessFrameTitleEditor } from '../components/text/edgeless-frame-title-editor.js';
|
import { EdgelessFrameTitleEditor } from '../components/text/edgeless-frame-title-editor.js';
|
||||||
import { EdgelessGroupTitleEditor } from '../components/text/edgeless-group-title-editor.js';
|
import { EdgelessGroupTitleEditor } from '../components/text/edgeless-group-title-editor.js';
|
||||||
import { EdgelessShapeTextEditor } from '../components/text/edgeless-shape-text-editor.js';
|
import { EdgelessShapeTextEditor } from '../components/text/edgeless-shape-text-editor.js';
|
||||||
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
|
|
||||||
|
|
||||||
export function mountShapeTextEditor(
|
export function mountShapeTextEditor(
|
||||||
shapeElement: ShapeElementModel,
|
shapeElement: ShapeElementModel,
|
||||||
edgeless: EdgelessRootBlockComponent
|
edgeless: BlockComponent
|
||||||
) {
|
) {
|
||||||
if (!edgeless.mountElm) {
|
const mountElm = edgeless.querySelector('.edgeless-mount-point');
|
||||||
|
if (!mountElm) {
|
||||||
throw new BlockSuiteError(
|
throw new BlockSuiteError(
|
||||||
ErrorCode.ValueNotExists,
|
ErrorCode.ValueNotExists,
|
||||||
"edgeless block's mount point does not exist"
|
"edgeless block's mount point does not exist"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedElement = edgeless.service.crud.getElementById(shapeElement.id);
|
const gfx = edgeless.std.get(GfxControllerIdentifier);
|
||||||
|
const crud = edgeless.std.get(EdgelessCRUDIdentifier);
|
||||||
|
|
||||||
|
const updatedElement = crud.getElementById(shapeElement.id);
|
||||||
|
|
||||||
if (!(updatedElement instanceof ShapeElementModel)) {
|
if (!(updatedElement instanceof ShapeElementModel)) {
|
||||||
console.error('Cannot mount text editor on a non-shape element');
|
console.error('Cannot mount text editor on a non-shape element');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeless.gfx.tool.setTool('default');
|
gfx.tool.setTool('default');
|
||||||
edgeless.gfx.selection.set({
|
gfx.selection.set({
|
||||||
elements: [shapeElement.id],
|
elements: [shapeElement.id],
|
||||||
editing: true,
|
editing: true,
|
||||||
});
|
});
|
||||||
@@ -52,22 +57,25 @@ export function mountShapeTextEditor(
|
|||||||
shapeEditor.edgeless = edgeless;
|
shapeEditor.edgeless = edgeless;
|
||||||
shapeEditor.mountEditor = mountShapeTextEditor;
|
shapeEditor.mountEditor = mountShapeTextEditor;
|
||||||
|
|
||||||
edgeless.mountElm.append(shapeEditor);
|
mountElm.append(shapeEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mountFrameTitleEditor(
|
export function mountFrameTitleEditor(
|
||||||
frame: FrameBlockModel,
|
frame: FrameBlockModel,
|
||||||
edgeless: EdgelessRootBlockComponent
|
edgeless: BlockComponent
|
||||||
) {
|
) {
|
||||||
if (!edgeless.mountElm) {
|
const mountElm = edgeless.querySelector('.edgeless-mount-point');
|
||||||
|
if (!mountElm) {
|
||||||
throw new BlockSuiteError(
|
throw new BlockSuiteError(
|
||||||
ErrorCode.ValueNotExists,
|
ErrorCode.ValueNotExists,
|
||||||
"edgeless block's mount point does not exist"
|
"edgeless block's mount point does not exist"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeless.gfx.tool.setTool('default');
|
const gfx = edgeless.std.get(GfxControllerIdentifier);
|
||||||
edgeless.gfx.selection.set({
|
|
||||||
|
gfx.tool.setTool('default');
|
||||||
|
gfx.selection.set({
|
||||||
elements: [frame.id],
|
elements: [frame.id],
|
||||||
editing: true,
|
editing: true,
|
||||||
});
|
});
|
||||||
@@ -76,22 +84,25 @@ export function mountFrameTitleEditor(
|
|||||||
frameEditor.frameModel = frame;
|
frameEditor.frameModel = frame;
|
||||||
frameEditor.edgeless = edgeless;
|
frameEditor.edgeless = edgeless;
|
||||||
|
|
||||||
edgeless.mountElm.append(frameEditor);
|
mountElm.append(frameEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mountGroupTitleEditor(
|
export function mountGroupTitleEditor(
|
||||||
group: GroupElementModel,
|
group: GroupElementModel,
|
||||||
edgeless: EdgelessRootBlockComponent
|
edgeless: BlockComponent
|
||||||
) {
|
) {
|
||||||
if (!edgeless.mountElm) {
|
const mountElm = edgeless.querySelector('.edgeless-mount-point');
|
||||||
|
if (!mountElm) {
|
||||||
throw new BlockSuiteError(
|
throw new BlockSuiteError(
|
||||||
ErrorCode.ValueNotExists,
|
ErrorCode.ValueNotExists,
|
||||||
"edgeless block's mount point does not exist"
|
"edgeless block's mount point does not exist"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeless.gfx.tool.setTool('default');
|
const gfx = edgeless.std.get(GfxControllerIdentifier);
|
||||||
edgeless.gfx.selection.set({
|
|
||||||
|
gfx.tool.setTool('default');
|
||||||
|
gfx.selection.set({
|
||||||
elements: [group.id],
|
elements: [group.id],
|
||||||
editing: true,
|
editing: true,
|
||||||
});
|
});
|
||||||
@@ -100,23 +111,26 @@ export function mountGroupTitleEditor(
|
|||||||
groupEditor.group = group;
|
groupEditor.group = group;
|
||||||
groupEditor.edgeless = edgeless;
|
groupEditor.edgeless = edgeless;
|
||||||
|
|
||||||
edgeless.mountElm.append(groupEditor);
|
mountElm.append(groupEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mountConnectorLabelEditor(
|
export function mountConnectorLabelEditor(
|
||||||
connector: ConnectorElementModel,
|
connector: ConnectorElementModel,
|
||||||
edgeless: EdgelessRootBlockComponent,
|
edgeless: BlockComponent,
|
||||||
point?: IVec
|
point?: IVec
|
||||||
) {
|
) {
|
||||||
if (!edgeless.mountElm) {
|
const mountElm = edgeless.querySelector('.edgeless-mount-point');
|
||||||
|
if (!mountElm) {
|
||||||
throw new BlockSuiteError(
|
throw new BlockSuiteError(
|
||||||
ErrorCode.ValueNotExists,
|
ErrorCode.ValueNotExists,
|
||||||
"edgeless block's mount point does not exist"
|
"edgeless block's mount point does not exist"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeless.gfx.tool.setTool('default');
|
const gfx = edgeless.std.get(GfxControllerIdentifier);
|
||||||
edgeless.gfx.selection.set({
|
|
||||||
|
gfx.tool.setTool('default');
|
||||||
|
gfx.selection.set({
|
||||||
elements: [connector.id],
|
elements: [connector.id],
|
||||||
editing: true,
|
editing: true,
|
||||||
});
|
});
|
||||||
@@ -146,7 +160,7 @@ export function mountConnectorLabelEditor(
|
|||||||
editor.connector = connector;
|
editor.connector = connector;
|
||||||
editor.edgeless = edgeless;
|
editor.edgeless = edgeless;
|
||||||
|
|
||||||
edgeless.mountElm.append(editor);
|
mountElm.append(editor);
|
||||||
editor.updateComplete
|
editor.updateComplete
|
||||||
.then(() => {
|
.then(() => {
|
||||||
editor.inlineEditor?.focusEnd();
|
editor.inlineEditor?.focusEnd();
|
||||||
|
|||||||
Reference in New Issue
Block a user