refactor(editor): gfx text package (#10738)
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
"@blocksuite/affine-block-table": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-fragment-doc-title": "workspace:*",
|
||||
"@blocksuite/affine-gfx-text": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-rich-text": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
EdgelessCRUDIdentifier,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { FontFamilyIcon } from '@blocksuite/affine-components/icons';
|
||||
import { mountTextElementEditor } from '@blocksuite/affine-gfx-text';
|
||||
import type {
|
||||
Connection,
|
||||
ConnectorElementModel,
|
||||
@@ -56,10 +57,7 @@ import {
|
||||
SHAPE_OVERLAY_HEIGHT,
|
||||
SHAPE_OVERLAY_WIDTH,
|
||||
} from '../../utils/consts.js';
|
||||
import {
|
||||
mountShapeTextEditor,
|
||||
mountTextElementEditor,
|
||||
} from '../../utils/text.js';
|
||||
import { mountShapeTextEditor } from '../../utils/text.js';
|
||||
import { ShapeComponentConfig } from '../toolbar/shape/shape-menu-config.js';
|
||||
import {
|
||||
type AUTO_COMPLETE_TARGET_TYPE,
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
import {
|
||||
EdgelessCRUDIdentifier,
|
||||
TextUtils,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import type { TextElementModel } from '@blocksuite/affine-model';
|
||||
import type { RichText } from '@blocksuite/affine-rich-text';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { getSelectedRect } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
RANGE_SYNC_EXCLUDE_ATTR,
|
||||
ShadowlessElement,
|
||||
} from '@blocksuite/block-std';
|
||||
import { Bound, toRadian, Vec } from '@blocksuite/global/gfx';
|
||||
import { WithDisposable } from '@blocksuite/global/lit';
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
||||
import { deleteElements } from '../../utils/crud.js';
|
||||
|
||||
export class EdgelessTextEditor extends WithDisposable(ShadowlessElement) {
|
||||
get crud() {
|
||||
return this.edgeless.std.get(EdgelessCRUDIdentifier);
|
||||
}
|
||||
|
||||
static BORDER_WIDTH = 1;
|
||||
|
||||
static PADDING_HORIZONTAL = 10;
|
||||
|
||||
static PADDING_VERTICAL = 6;
|
||||
|
||||
static PLACEHOLDER_TEXT = 'Type from here';
|
||||
|
||||
static override styles = css`
|
||||
.edgeless-text-editor {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
transform-origin: left top;
|
||||
font-kerning: none;
|
||||
border: ${EdgelessTextEditor.BORDER_WIDTH}px solid
|
||||
var(--affine-primary-color, #1e96eb);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 0px 0px 2px rgba(30, 150, 235, 0.3);
|
||||
padding: ${EdgelessTextEditor.PADDING_VERTICAL}px
|
||||
${EdgelessTextEditor.PADDING_HORIZONTAL}px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.edgeless-text-editor .inline-editor {
|
||||
white-space: pre-wrap !important;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.edgeless-text-editor .inline-editor span {
|
||||
word-break: normal !important;
|
||||
overflow-wrap: anywhere !important;
|
||||
}
|
||||
|
||||
.edgeless-text-editor-placeholder {
|
||||
pointer-events: none;
|
||||
color: var(--affine-text-disable-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
`;
|
||||
|
||||
private _isComposition = false;
|
||||
|
||||
private _keeping = false;
|
||||
|
||||
private readonly _updateRect = () => {
|
||||
const edgeless = this.edgeless;
|
||||
const element = this.element;
|
||||
|
||||
if (!edgeless || !element || !this.inlineEditorContainer) return;
|
||||
|
||||
const newWidth = this.inlineEditorContainer.scrollWidth;
|
||||
const newHeight = this.inlineEditorContainer.scrollHeight;
|
||||
const bound = new Bound(element.x, element.y, newWidth, newHeight);
|
||||
const { x, y, w, h, rotate } = element;
|
||||
|
||||
switch (element.textAlign) {
|
||||
case 'left':
|
||||
{
|
||||
const newPos = this.getCoordsOnLeftAlign(
|
||||
{
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
r: toRadian(rotate),
|
||||
},
|
||||
newWidth,
|
||||
newHeight
|
||||
);
|
||||
|
||||
bound.x = newPos.x;
|
||||
bound.y = newPos.y;
|
||||
}
|
||||
break;
|
||||
case 'center':
|
||||
{
|
||||
const newPos = this.getCoordsOnCenterAlign(
|
||||
{
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
r: toRadian(rotate),
|
||||
},
|
||||
newWidth,
|
||||
newHeight
|
||||
);
|
||||
|
||||
bound.x = newPos.x;
|
||||
bound.y = newPos.y;
|
||||
}
|
||||
break;
|
||||
case 'right':
|
||||
{
|
||||
const newPos = this.getCoordsOnRightAlign(
|
||||
{
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
r: toRadian(rotate),
|
||||
},
|
||||
newWidth,
|
||||
newHeight
|
||||
);
|
||||
|
||||
bound.x = newPos.x;
|
||||
bound.y = newPos.y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.crud.updateElement(element.id, {
|
||||
xywh: bound.serialize(),
|
||||
});
|
||||
};
|
||||
|
||||
get inlineEditor() {
|
||||
return this.richText.inlineEditor;
|
||||
}
|
||||
|
||||
get inlineEditorContainer() {
|
||||
return this.inlineEditor?.rootElement;
|
||||
}
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (!this.edgeless) {
|
||||
console.error('edgeless is not set.');
|
||||
return;
|
||||
}
|
||||
if (!this.element) {
|
||||
console.error('text element is not set.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
|
||||
}
|
||||
|
||||
override firstUpdated(): void {
|
||||
const edgeless = this.edgeless;
|
||||
const element = this.element;
|
||||
const { dispatcher } = this.edgeless;
|
||||
|
||||
this.updateComplete
|
||||
.then(() => {
|
||||
if (!this.inlineEditor) return;
|
||||
this.inlineEditor.slots.renderComplete.on(() => {
|
||||
this._updateRect();
|
||||
this.requestUpdate();
|
||||
});
|
||||
|
||||
this.disposables.add(
|
||||
edgeless.service.surface.elementUpdated.on(({ id }) => {
|
||||
if (id === element.id) this.requestUpdate();
|
||||
})
|
||||
);
|
||||
|
||||
this.disposables.add(
|
||||
edgeless.service.viewport.viewportUpdated.on(() => {
|
||||
this.requestUpdate();
|
||||
})
|
||||
);
|
||||
|
||||
this.disposables.add(dispatcher.add('click', () => true));
|
||||
this.disposables.add(dispatcher.add('doubleClick', () => true));
|
||||
|
||||
this.disposables.add(() => {
|
||||
element.display = true;
|
||||
|
||||
if (element.text.length === 0) {
|
||||
deleteElements(edgeless, [element]);
|
||||
}
|
||||
|
||||
edgeless.service.selection.set({
|
||||
elements: [],
|
||||
editing: false,
|
||||
});
|
||||
});
|
||||
|
||||
if (!this.inlineEditorContainer) return;
|
||||
this.disposables.addFromEvent(
|
||||
this.inlineEditorContainer,
|
||||
'blur',
|
||||
() => !this._keeping && this.remove()
|
||||
);
|
||||
|
||||
this.disposables.addFromEvent(
|
||||
this.inlineEditorContainer,
|
||||
'compositionstart',
|
||||
() => {
|
||||
this._isComposition = true;
|
||||
this.requestUpdate();
|
||||
}
|
||||
);
|
||||
this.disposables.addFromEvent(
|
||||
this.inlineEditorContainer,
|
||||
'compositionend',
|
||||
() => {
|
||||
this._isComposition = false;
|
||||
this.requestUpdate();
|
||||
}
|
||||
);
|
||||
|
||||
element.display = false;
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
getContainerOffset() {
|
||||
const { PADDING_VERTICAL, PADDING_HORIZONTAL, BORDER_WIDTH } =
|
||||
EdgelessTextEditor;
|
||||
return `-${PADDING_HORIZONTAL + BORDER_WIDTH}px, -${
|
||||
PADDING_VERTICAL + BORDER_WIDTH
|
||||
}px`;
|
||||
}
|
||||
|
||||
getCoordsOnCenterAlign(
|
||||
rect: { w: number; h: number; r: number; x: number; y: number },
|
||||
w1: number,
|
||||
h1: number
|
||||
): { x: number; y: number } {
|
||||
const centerX = rect.x + rect.w / 2;
|
||||
const centerY = rect.y + rect.h / 2;
|
||||
|
||||
let deltaXPrime = 0;
|
||||
let deltaYPrime = (-rect.h / 2) * Math.cos(rect.r);
|
||||
|
||||
const vX = centerX + deltaXPrime;
|
||||
const vY = centerY + deltaYPrime;
|
||||
|
||||
deltaXPrime = 0;
|
||||
deltaYPrime = (-h1 / 2) * Math.cos(rect.r);
|
||||
|
||||
const newCenterX = vX - deltaXPrime;
|
||||
const newCenterY = vY - deltaYPrime;
|
||||
|
||||
return { x: newCenterX - w1 / 2, y: newCenterY - h1 / 2 };
|
||||
}
|
||||
|
||||
getCoordsOnLeftAlign(
|
||||
rect: { w: number; h: number; r: number; x: number; y: number },
|
||||
w1: number,
|
||||
h1: number
|
||||
): { x: number; y: number } {
|
||||
const cX = rect.x + rect.w / 2;
|
||||
const cY = rect.y + rect.h / 2;
|
||||
|
||||
let deltaXPrime =
|
||||
(-rect.w / 2) * Math.cos(rect.r) + (rect.h / 2) * Math.sin(rect.r);
|
||||
let deltaYPrime =
|
||||
(-rect.w / 2) * Math.sin(rect.r) - (rect.h / 2) * Math.cos(rect.r);
|
||||
|
||||
const vX = cX + deltaXPrime;
|
||||
const vY = cY + deltaYPrime;
|
||||
|
||||
deltaXPrime = (-w1 / 2) * Math.cos(rect.r) + (h1 / 2) * Math.sin(rect.r);
|
||||
deltaYPrime = (-w1 / 2) * Math.sin(rect.r) - (h1 / 2) * Math.cos(rect.r);
|
||||
|
||||
const newCenterX = vX - deltaXPrime;
|
||||
const newCenterY = vY - deltaYPrime;
|
||||
|
||||
return { x: newCenterX - w1 / 2, y: newCenterY - h1 / 2 };
|
||||
}
|
||||
|
||||
getCoordsOnRightAlign(
|
||||
rect: { w: number; h: number; r: number; x: number; y: number },
|
||||
w1: number,
|
||||
h1: number
|
||||
): { x: number; y: number } {
|
||||
const centerX = rect.x + rect.w / 2;
|
||||
const centerY = rect.y + rect.h / 2;
|
||||
|
||||
let deltaXPrime =
|
||||
(rect.w / 2) * Math.cos(rect.r) - (-rect.h / 2) * Math.sin(rect.r);
|
||||
let deltaYPrime =
|
||||
(rect.w / 2) * Math.sin(rect.r) + (-rect.h / 2) * Math.cos(rect.r);
|
||||
|
||||
const vX = centerX + deltaXPrime;
|
||||
const vY = centerY + deltaYPrime;
|
||||
|
||||
deltaXPrime = (w1 / 2) * Math.cos(rect.r) - (-h1 / 2) * Math.sin(rect.r);
|
||||
deltaYPrime = (w1 / 2) * Math.sin(rect.r) + (-h1 / 2) * Math.cos(rect.r);
|
||||
|
||||
const newCenterX = vX - deltaXPrime;
|
||||
const newCenterY = vY - deltaYPrime;
|
||||
|
||||
return { x: newCenterX - w1 / 2, y: newCenterY - h1 / 2 };
|
||||
}
|
||||
|
||||
override async getUpdateComplete(): Promise<boolean> {
|
||||
const result = await super.getUpdateComplete();
|
||||
await this.richText?.updateComplete;
|
||||
return result;
|
||||
}
|
||||
|
||||
getVisualPosition(element: TextElementModel) {
|
||||
const { x, y, w, h, rotate } = element;
|
||||
return Vec.rotWith([x, y], [x + w / 2, y + h / 2], toRadian(rotate));
|
||||
}
|
||||
|
||||
override render() {
|
||||
const {
|
||||
text,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight,
|
||||
fontStyle,
|
||||
textAlign,
|
||||
rotate,
|
||||
hasMaxWidth,
|
||||
w,
|
||||
} = this.element;
|
||||
const lineHeight = TextUtils.getLineHeight(
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight
|
||||
);
|
||||
const rect = getSelectedRect([this.element]);
|
||||
|
||||
const { translateX, translateY, zoom } = this.edgeless.service.viewport;
|
||||
const [visualX, visualY] = this.getVisualPosition(this.element);
|
||||
const containerOffset = this.getContainerOffset();
|
||||
const transformOperation = [
|
||||
`translate(${translateX}px, ${translateY}px)`,
|
||||
`translate(${visualX * zoom}px, ${visualY * zoom}px)`,
|
||||
`scale(${zoom})`,
|
||||
`rotate(${rotate}deg)`,
|
||||
`translate(${containerOffset})`,
|
||||
];
|
||||
|
||||
const isEmpty = !text.length && !this._isComposition;
|
||||
const color = this.edgeless.std
|
||||
.get(ThemeProvider)
|
||||
.generateColorProperty(this.element.color, '#000000');
|
||||
|
||||
return html`<div
|
||||
style=${styleMap({
|
||||
transform: transformOperation.join(' '),
|
||||
minWidth: hasMaxWidth ? `${rect.width}px` : 'none',
|
||||
maxWidth: hasMaxWidth ? `${w}px` : 'none',
|
||||
fontFamily: TextUtils.wrapFontFamily(fontFamily),
|
||||
fontSize: `${fontSize}px`,
|
||||
fontWeight,
|
||||
fontStyle,
|
||||
color,
|
||||
textAlign,
|
||||
lineHeight: `${lineHeight}px`,
|
||||
boxSizing: 'content-box',
|
||||
})}
|
||||
class="edgeless-text-editor"
|
||||
>
|
||||
<rich-text
|
||||
.yText=${text}
|
||||
.enableFormat=${false}
|
||||
.enableAutoScrollHorizontally=${false}
|
||||
style=${isEmpty
|
||||
? styleMap({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
padding: `${EdgelessTextEditor.PADDING_VERTICAL}px
|
||||
${EdgelessTextEditor.PADDING_HORIZONTAL}px`,
|
||||
})
|
||||
: nothing}
|
||||
></rich-text>
|
||||
${isEmpty
|
||||
? html`<span class="edgeless-text-editor-placeholder">
|
||||
Type from here
|
||||
</span>`
|
||||
: nothing}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
setKeeping(keeping: boolean) {
|
||||
this._keeping = keeping;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor edgeless!: EdgelessRootBlockComponent;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor element!: TextElementModel;
|
||||
|
||||
@query('rich-text')
|
||||
accessor richText!: RichText;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { addAttachments } from '@blocksuite/affine-block-attachment';
|
||||
import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text';
|
||||
import { addImages } from '@blocksuite/affine-block-image';
|
||||
import { CanvasElementType } from '@blocksuite/affine-block-surface';
|
||||
import { mountTextElementEditor } from '@blocksuite/affine-gfx-text';
|
||||
import {
|
||||
MAX_IMAGE_WIDTH,
|
||||
type MindmapStyle,
|
||||
@@ -18,7 +19,6 @@ import * as Y from 'yjs';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../../edgeless-root-block.js';
|
||||
import type { EdgelessRootService } from '../../../edgeless-root-service.js';
|
||||
import { mountTextElementEditor } from '../../../utils/text.js';
|
||||
|
||||
export type ConfigProperty = 'x' | 'y' | 'r' | 's' | 'z' | 'o';
|
||||
export type ConfigState = 'default' | 'active' | 'hover' | 'next';
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
PresentTool,
|
||||
} from '@blocksuite/affine-block-frame';
|
||||
import { ConnectionOverlay } from '@blocksuite/affine-block-surface';
|
||||
import { TextTool } from '@blocksuite/affine-gfx-text';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { EdgelessRootBlockSpec } from './edgeless-root-spec.js';
|
||||
@@ -19,7 +20,6 @@ import { NoteTool } from './gfx-tool/note-tool.js';
|
||||
import { PanTool } from './gfx-tool/pan-tool.js';
|
||||
import { ShapeTool } from './gfx-tool/shape-tool.js';
|
||||
import { TemplateTool } from './gfx-tool/template-tool.js';
|
||||
import { TextTool } from './gfx-tool/text-tool.js';
|
||||
import { EditPropsMiddlewareBuilder } from './middlewares/base.js';
|
||||
import { SnapManager } from './utils/snap-manager.js';
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
isNoteBlock,
|
||||
OverlayIdentifier,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { addText, mountTextElementEditor } from '@blocksuite/affine-gfx-text';
|
||||
import type {
|
||||
EdgelessTextBlockModel,
|
||||
FrameBlockModel,
|
||||
@@ -55,12 +56,10 @@ import { calPanDelta } from '../utils/panning-utils.js';
|
||||
import { isCanvasElement, isEdgelessTextBlock } from '../utils/query.js';
|
||||
import type { SnapManager } from '../utils/snap-manager.js';
|
||||
import {
|
||||
addText,
|
||||
mountConnectorLabelEditor,
|
||||
mountFrameTitleEditor,
|
||||
mountGroupTitleEditor,
|
||||
mountShapeTextEditor,
|
||||
mountTextElementEditor,
|
||||
} from '../utils/text.js';
|
||||
import { CanvasElementEventExt } from './default-tool-ext/event-ext.js';
|
||||
import type { DefaultToolExt } from './default-tool-ext/ext.js';
|
||||
|
||||
@@ -9,4 +9,3 @@ export { NoteTool, type NoteToolOption } from './note-tool.js';
|
||||
export { PanTool, type PanToolOption } from './pan-tool.js';
|
||||
export { ShapeTool, type ShapeToolOption } from './shape-tool.js';
|
||||
export { TemplateTool } from './template-tool.js';
|
||||
export { TextTool } from './text-tool.js';
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text';
|
||||
import type { TextElementModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
FeatureFlagService,
|
||||
TelemetryProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import type { PointerEventState } from '@blocksuite/block-std';
|
||||
import { BaseTool, type GfxController } from '@blocksuite/block-std/gfx';
|
||||
import { Bound } from '@blocksuite/global/gfx';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
|
||||
import { mountTextElementEditor } from '../utils/text.js';
|
||||
|
||||
export function addText(gfx: GfxController, event: PointerEventState) {
|
||||
const [x, y] = gfx.viewport.toModelCoord(event.x, event.y);
|
||||
const selected = gfx.getElementByPoint(x, y);
|
||||
|
||||
if (!selected) {
|
||||
const [modelX, modelY] = gfx.viewport.toModelCoord(event.x, event.y);
|
||||
|
||||
if (!gfx.surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = gfx.surface.addElement({
|
||||
type: 'text',
|
||||
xywh: new Bound(modelX, modelY, 32, 32).serialize(),
|
||||
text: new Y.Text(),
|
||||
});
|
||||
gfx.doc.captureSync();
|
||||
const textElement = gfx.getElementById(id) as TextElementModel;
|
||||
const edgelessView = gfx.std.view.getBlock(gfx.std.store.root!.id);
|
||||
mountTextElementEditor(
|
||||
textElement,
|
||||
edgelessView as EdgelessRootBlockComponent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TextTool extends BaseTool {
|
||||
static override toolName: string = 'text';
|
||||
|
||||
override click(e: PointerEventState): void {
|
||||
const textFlag = this.gfx.doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_edgeless_text');
|
||||
|
||||
if (textFlag) {
|
||||
const [x, y] = this.gfx.viewport.toModelCoord(e.x, e.y);
|
||||
this.gfx.std.command.exec(insertEdgelessTextCommand, { x, y });
|
||||
this.gfx.tool.setTool('default');
|
||||
} else {
|
||||
addText(this.gfx, e);
|
||||
}
|
||||
|
||||
this.gfx.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
|
||||
control: 'canvas:draw',
|
||||
page: 'whiteboard editor',
|
||||
module: 'toolbar',
|
||||
segment: 'toolbar',
|
||||
type: 'text',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@blocksuite/block-std/gfx' {
|
||||
interface GfxToolsMap {
|
||||
text: TextTool;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,10 @@
|
||||
import {
|
||||
CanvasElementType,
|
||||
EdgelessCRUDIdentifier,
|
||||
type IModelCoord,
|
||||
TextUtils,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import type {
|
||||
ConnectorElementModel,
|
||||
FrameBlockModel,
|
||||
GroupElementModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { ShapeElementModel, TextElementModel } from '@blocksuite/affine-model';
|
||||
import type { PointerEventState } from '@blocksuite/block-std';
|
||||
import { ShapeElementModel } from '@blocksuite/affine-model';
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
import type { IVec } from '@blocksuite/global/gfx';
|
||||
import { Bound } from '@blocksuite/global/gfx';
|
||||
@@ -20,47 +14,8 @@ import { EdgelessConnectorLabelEditor } from '../components/text/edgeless-connec
|
||||
import { EdgelessFrameTitleEditor } from '../components/text/edgeless-frame-title-editor.js';
|
||||
import { EdgelessGroupTitleEditor } from '../components/text/edgeless-group-title-editor.js';
|
||||
import { EdgelessShapeTextEditor } from '../components/text/edgeless-shape-text-editor.js';
|
||||
import { EdgelessTextEditor } from '../components/text/edgeless-text-editor.js';
|
||||
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
|
||||
|
||||
export function mountTextElementEditor(
|
||||
textElement: TextElementModel,
|
||||
edgeless: EdgelessRootBlockComponent,
|
||||
focusCoord?: IModelCoord
|
||||
) {
|
||||
if (!edgeless.mountElm) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.ValueNotExists,
|
||||
"edgeless block's mount point does not exist"
|
||||
);
|
||||
}
|
||||
|
||||
let cursorIndex = textElement.text.length;
|
||||
if (focusCoord) {
|
||||
cursorIndex = Math.min(
|
||||
TextUtils.getCursorByCoord(textElement, focusCoord),
|
||||
cursorIndex
|
||||
);
|
||||
}
|
||||
|
||||
const textEditor = new EdgelessTextEditor();
|
||||
textEditor.edgeless = edgeless;
|
||||
textEditor.element = textElement;
|
||||
|
||||
edgeless.append(textEditor);
|
||||
textEditor.updateComplete
|
||||
.then(() => {
|
||||
textEditor.inlineEditor?.focusIndex(cursorIndex);
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
edgeless.gfx.tool.setTool('default');
|
||||
edgeless.gfx.selection.set({
|
||||
elements: [textElement.id],
|
||||
editing: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function mountShapeTextEditor(
|
||||
shapeElement: ShapeElementModel,
|
||||
edgeless: EdgelessRootBlockComponent
|
||||
@@ -145,39 +100,6 @@ export function mountGroupTitleEditor(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Canvas Text has been deprecated
|
||||
*/
|
||||
export function addText(
|
||||
edgeless: EdgelessRootBlockComponent,
|
||||
event: PointerEventState
|
||||
) {
|
||||
const [x, y] = edgeless.service.viewport.toModelCoord(event.x, event.y);
|
||||
const selected = edgeless.service.gfx.getElementByPoint(x, y);
|
||||
|
||||
if (!selected) {
|
||||
const [modelX, modelY] = edgeless.service.viewport.toModelCoord(
|
||||
event.x,
|
||||
event.y
|
||||
);
|
||||
const id = edgeless.std
|
||||
.get(EdgelessCRUDIdentifier)
|
||||
.addElement(CanvasElementType.TEXT, {
|
||||
xywh: new Bound(modelX, modelY, 32, 32).serialize(),
|
||||
text: new Y.Text(),
|
||||
});
|
||||
if (!id) return;
|
||||
edgeless.doc.captureSync();
|
||||
const textElement = edgeless.service.crud.getElementById(id);
|
||||
if (!textElement) return;
|
||||
if (textElement instanceof TextElementModel) {
|
||||
mountTextElementEditor(textElement, edgeless);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mountConnectorLabelEditor(
|
||||
connector: ConnectorElementModel,
|
||||
edgeless: EdgelessRootBlockComponent,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { effects as gfxCanvasTextEffects } from '@blocksuite/affine-gfx-text/effects';
|
||||
|
||||
import { EdgelessAutoCompletePanel } from './edgeless/components/auto-complete/auto-complete-panel.js';
|
||||
import { EdgelessAutoComplete } from './edgeless/components/auto-complete/edgeless-auto-complete.js';
|
||||
import { EdgelessToolIconButton } from './edgeless/components/buttons/tool-icon-button.js';
|
||||
@@ -36,7 +38,6 @@ import { EdgelessConnectorLabelEditor } from './edgeless/components/text/edgeles
|
||||
import { EdgelessFrameTitleEditor } from './edgeless/components/text/edgeless-frame-title-editor.js';
|
||||
import { EdgelessGroupTitleEditor } from './edgeless/components/text/edgeless-group-title-editor.js';
|
||||
import { EdgelessShapeTextEditor } from './edgeless/components/text/edgeless-shape-text-editor.js';
|
||||
import { EdgelessTextEditor } from './edgeless/components/text/edgeless-text-editor.js';
|
||||
import { EdgelessBrushMenu } from './edgeless/components/toolbar/brush/brush-menu.js';
|
||||
import { EdgelessBrushToolButton } from './edgeless/components/toolbar/brush/brush-tool-button.js';
|
||||
import { EdgelessSlideMenu } from './edgeless/components/toolbar/common/slide-menu.js';
|
||||
@@ -122,6 +123,7 @@ export function effects() {
|
||||
|
||||
// Register components by category
|
||||
registerRootComponents();
|
||||
registerGfxEffects();
|
||||
registerWidgets();
|
||||
registerEdgelessToolbarComponents();
|
||||
registerEdgelessPanelComponents();
|
||||
@@ -139,6 +141,10 @@ function registerRootComponents() {
|
||||
);
|
||||
}
|
||||
|
||||
function registerGfxEffects() {
|
||||
gfxCanvasTextEffects();
|
||||
}
|
||||
|
||||
function registerWidgets() {
|
||||
customElements.define(AFFINE_INNER_MODAL_WIDGET, AffineInnerModalWidget);
|
||||
customElements.define(AFFINE_MODAL_WIDGET, AffineModalWidget);
|
||||
@@ -258,7 +264,6 @@ function registerEdgelessEditorComponents() {
|
||||
'edgeless-frame-title-editor',
|
||||
EdgelessFrameTitleEditor
|
||||
);
|
||||
customElements.define('edgeless-text-editor', EdgelessTextEditor);
|
||||
}
|
||||
|
||||
function registerMiscComponents() {
|
||||
@@ -348,7 +353,6 @@ declare global {
|
||||
'edgeless-frame-title-editor': EdgelessFrameTitleEditor;
|
||||
'edgeless-group-title-editor': EdgelessGroupTitleEditor;
|
||||
'edgeless-shape-text-editor': EdgelessShapeTextEditor;
|
||||
'edgeless-text-editor': EdgelessTextEditor;
|
||||
'edgeless-toolbar-widget': EdgelessToolbarWidget;
|
||||
'presentation-toolbar': PresentationToolbar;
|
||||
'edgeless-brush-menu': EdgelessBrushMenu;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
{ "path": "../block-table" },
|
||||
{ "path": "../../components" },
|
||||
{ "path": "../../fragments/fragment-doc-title" },
|
||||
{ "path": "../../gfx/text" },
|
||||
{ "path": "../../model" },
|
||||
{ "path": "../../rich-text" },
|
||||
{ "path": "../../shared" },
|
||||
|
||||
Reference in New Issue
Block a user