mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 02:26:21 +08:00
feat(editor): gfx shape package (#11060)
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
"@blocksuite/affine-fragment-doc-title": "workspace:*",
|
||||
"@blocksuite/affine-fragment-frame-panel": "workspace:*",
|
||||
"@blocksuite/affine-fragment-outline": "workspace:*",
|
||||
"@blocksuite/affine-gfx-shape": "workspace:*",
|
||||
"@blocksuite/affine-gfx-text": "workspace:*",
|
||||
"@blocksuite/affine-gfx-turbo-renderer": "workspace:*",
|
||||
"@blocksuite/affine-inline-footnote": "workspace:*",
|
||||
@@ -112,6 +113,7 @@
|
||||
"./fragments/frame-panel": "./src/fragments/frame-panel.ts",
|
||||
"./fragments/outline": "./src/fragments/outline.ts",
|
||||
"./gfx/text": "./src/gfx/text.ts",
|
||||
"./gfx/shape": "./src/gfx/shape/index.ts",
|
||||
"./gfx/turbo-renderer": "./src/gfx/turbo-renderer.ts",
|
||||
"./components/block-selection": "./src/components/block-selection.ts",
|
||||
"./components/block-zero-width": "./src/components/block-zero-width.ts",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from '@blocksuite/affine-gfx-shape';
|
||||
@@ -30,6 +30,7 @@
|
||||
{ "path": "../fragments/fragment-doc-title" },
|
||||
{ "path": "../fragments/fragment-frame-panel" },
|
||||
{ "path": "../fragments/fragment-outline" },
|
||||
{ "path": "../gfx/shape" },
|
||||
{ "path": "../gfx/text" },
|
||||
{ "path": "../gfx/turbo-renderer" },
|
||||
{ "path": "../inlines/footnote" },
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"@blocksuite/affine-block-table": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-fragment-doc-title": "workspace:*",
|
||||
"@blocksuite/affine-gfx-shape": "workspace:*",
|
||||
"@blocksuite/affine-gfx-text": "workspace:*",
|
||||
"@blocksuite/affine-inline-latex": "workspace:*",
|
||||
"@blocksuite/affine-inline-link": "workspace:*",
|
||||
|
||||
+6
-6
@@ -4,6 +4,12 @@ import {
|
||||
EdgelessCRUDIdentifier,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { FontFamilyIcon } from '@blocksuite/affine-components/icons';
|
||||
import {
|
||||
mountShapeTextEditor,
|
||||
SHAPE_OVERLAY_HEIGHT,
|
||||
SHAPE_OVERLAY_WIDTH,
|
||||
ShapeComponentConfig,
|
||||
} from '@blocksuite/affine-gfx-shape';
|
||||
import { mountTextElementEditor } from '@blocksuite/affine-gfx-text';
|
||||
import type {
|
||||
Connection,
|
||||
@@ -53,12 +59,6 @@ import { styleMap } from 'lit/directives/style-map.js';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
||||
import {
|
||||
SHAPE_OVERLAY_HEIGHT,
|
||||
SHAPE_OVERLAY_WIDTH,
|
||||
} from '../../utils/consts.js';
|
||||
import { mountShapeTextEditor } from '../../utils/text.js';
|
||||
import { ShapeComponentConfig } from '../toolbar/shape/shape-menu-config.js';
|
||||
import {
|
||||
type AUTO_COMPLETE_TARGET_TYPE,
|
||||
AutoCompleteFrameOverlay,
|
||||
|
||||
+1
-1
@@ -8,6 +8,7 @@ import {
|
||||
OverlayIdentifier,
|
||||
type RoughCanvas,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { mountShapeTextEditor } from '@blocksuite/affine-gfx-shape';
|
||||
import type {
|
||||
Connection,
|
||||
ConnectorElementModel,
|
||||
@@ -42,7 +43,6 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
||||
import { mountShapeTextEditor } from '../../utils/text.js';
|
||||
import type { SelectedRect } from '../rects/edgeless-selected-rect.js';
|
||||
import { EdgelessAutoCompletePanel } from './auto-complete-panel.js';
|
||||
import {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Overlay,
|
||||
type RoughCanvas,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { type Shape, ShapeFactory } from '@blocksuite/affine-gfx-shape';
|
||||
import {
|
||||
type Connection,
|
||||
getShapeRadius,
|
||||
@@ -20,7 +21,6 @@ import { assertType } from '@blocksuite/global/utils';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
||||
import { type Shape, ShapeFactory } from '../../utils/tool-overlay.js';
|
||||
|
||||
export enum Direction {
|
||||
Right,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import {
|
||||
ShapeComponentConfig,
|
||||
type ShapeTool,
|
||||
} from '@blocksuite/affine-gfx-shape';
|
||||
import { ShapeStyle } from '@blocksuite/affine-model';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import type { ShapeTool } from '../../gfx-tool/shape-tool.js';
|
||||
import { ShapeComponentConfig } from '../toolbar/shape/shape-menu-config.js';
|
||||
|
||||
export class EdgelessShapePanel extends LitElement {
|
||||
static override styles = css`
|
||||
:host {
|
||||
|
||||
+6
-2
@@ -2,6 +2,12 @@ import {
|
||||
CanvasElementType,
|
||||
EdgelessCRUDIdentifier,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
ellipseSvg,
|
||||
roundedSvg,
|
||||
ShapeTool,
|
||||
triangleSvg,
|
||||
} from '@blocksuite/affine-gfx-shape';
|
||||
import {
|
||||
getShapeRadius,
|
||||
getShapeType,
|
||||
@@ -20,10 +26,8 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { ShapeTool } from '../../../gfx-tool/shape-tool.js';
|
||||
import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js';
|
||||
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
|
||||
import { ellipseSvg, roundedSvg, triangleSvg } from './icons.js';
|
||||
import type { DraggableShape } from './utils.js';
|
||||
import { buildVariablesObject } from './utils.js';
|
||||
|
||||
|
||||
+1
-1
@@ -1,8 +1,8 @@
|
||||
import { ShapeTool } from '@blocksuite/affine-gfx-shape';
|
||||
import { type ShapeName, ShapeType } from '@blocksuite/affine-model';
|
||||
import { SignalWatcher } from '@blocksuite/global/lit';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
|
||||
import { ShapeTool } from '../../../gfx-tool/shape-tool.js';
|
||||
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
|
||||
import type { DraggableShape } from './utils.js';
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { ShapeToolOption } from '@blocksuite/affine-gfx-shape';
|
||||
import { render, type TemplateResult } from 'lit';
|
||||
|
||||
import type { ShapeToolOption } from '../../../gfx-tool/shape-tool.js';
|
||||
|
||||
type TransformState = {
|
||||
/** horizental offset base on center */
|
||||
x?: number | string;
|
||||
|
||||
@@ -7,6 +7,11 @@ import {
|
||||
type PickColorEvent,
|
||||
} from '@blocksuite/affine-components/color-picker';
|
||||
import type { LineDetailType } from '@blocksuite/affine-components/edgeless-line-styles-panel';
|
||||
import {
|
||||
mountShapeTextEditor,
|
||||
ShapeComponentConfig,
|
||||
type ShapeToolOption,
|
||||
} from '@blocksuite/affine-gfx-shape';
|
||||
import {
|
||||
type Color,
|
||||
DefaultTheme,
|
||||
@@ -34,9 +39,6 @@ import { AddTextIcon, ShapeIcon } from '@blocksuite/icons/lit';
|
||||
import { html } from 'lit';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
|
||||
import type { ShapeToolOption } from '../..';
|
||||
import { ShapeComponentConfig } from '../../components/toolbar/shape/shape-menu-config';
|
||||
import { mountShapeTextEditor } from '../../utils/text';
|
||||
import { LINE_STYLE_LIST } from './consts';
|
||||
import {
|
||||
createMindmapLayoutActionMenu,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
PresentTool,
|
||||
} from '@blocksuite/affine-block-frame';
|
||||
import { ConnectionOverlay } from '@blocksuite/affine-block-surface';
|
||||
import { ShapeTool } from '@blocksuite/affine-gfx-shape';
|
||||
import { TextTool } from '@blocksuite/affine-gfx-text';
|
||||
import {
|
||||
CanvasEventHandler,
|
||||
@@ -25,7 +26,6 @@ import { EraserTool } from './gfx-tool/eraser-tool.js';
|
||||
import { LassoTool } from './gfx-tool/lasso-tool.js';
|
||||
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 { EditPropsMiddlewareBuilder } from './middlewares/base.js';
|
||||
import { SnapOverlay } from './utils/snap-manager.js';
|
||||
|
||||
@@ -2,6 +2,7 @@ import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmar
|
||||
import { EdgelessTextBlockComponent } from '@blocksuite/affine-block-edgeless-text';
|
||||
import { isNoteBlock } from '@blocksuite/affine-block-surface';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
import { mountShapeTextEditor, ShapeTool } from '@blocksuite/affine-gfx-shape';
|
||||
import {
|
||||
ConnectorElementModel,
|
||||
ConnectorMode,
|
||||
@@ -34,7 +35,6 @@ import { Bound, getCommonBound } from '@blocksuite/global/gfx';
|
||||
import { PageKeyboardManager } from '../keyboard/keyboard-manager.js';
|
||||
import type { EdgelessRootBlockComponent } from './edgeless-root-block.js';
|
||||
import { LassoTool } from './gfx-tool/lasso-tool.js';
|
||||
import { ShapeTool } from './gfx-tool/shape-tool.js';
|
||||
import {
|
||||
DEFAULT_NOTE_CHILD_FLAVOUR,
|
||||
DEFAULT_NOTE_CHILD_TYPE,
|
||||
@@ -48,10 +48,7 @@ import {
|
||||
isSingleMindMapNode,
|
||||
} from './utils/mindmap.js';
|
||||
import { isCanvasElement } from './utils/query.js';
|
||||
import {
|
||||
mountConnectorLabelEditor,
|
||||
mountShapeTextEditor,
|
||||
} from './utils/text.js';
|
||||
import { mountConnectorLabelEditor } from './utils/text.js';
|
||||
|
||||
export class EdgelessPageKeyboardManager extends PageKeyboardManager {
|
||||
get gfx() {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getBgGridGap,
|
||||
normalizeWheelDeltaY,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { mountShapeTextEditor } from '@blocksuite/affine-gfx-shape';
|
||||
import {
|
||||
NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
@@ -52,7 +53,6 @@ import { EdgelessPageKeyboardManager } from './edgeless-keyboard.js';
|
||||
import type { EdgelessRootService } from './edgeless-root-service.js';
|
||||
import { isSingleMindMapNode } from './utils/mindmap.js';
|
||||
import { isCanvasElement } from './utils/query.js';
|
||||
import { mountShapeTextEditor } from './utils/text.js';
|
||||
|
||||
export class EdgelessRootBlockComponent extends BlockComponent<
|
||||
RootBlockModel,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
isNoteBlock,
|
||||
OverlayIdentifier,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { mountShapeTextEditor } from '@blocksuite/affine-gfx-shape';
|
||||
import { addText, mountTextElementEditor } from '@blocksuite/affine-gfx-text';
|
||||
import type {
|
||||
EdgelessTextBlockModel,
|
||||
@@ -53,7 +54,6 @@ import { isCanvasElement, isEdgelessTextBlock } from '../utils/query.js';
|
||||
import {
|
||||
mountConnectorLabelEditor,
|
||||
mountGroupTitleEditor,
|
||||
mountShapeTextEditor,
|
||||
} from '../utils/text.js';
|
||||
import { DefaultModeDragType } from './default-tool-ext/ext.js';
|
||||
|
||||
|
||||
@@ -6,5 +6,4 @@ export { EraserTool } from './eraser-tool.js';
|
||||
export { LassoTool, type LassoToolOption } from './lasso-tool.js';
|
||||
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';
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import type { SurfaceBlockComponent } from '@blocksuite/affine-block-surface';
|
||||
import { addNote } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
addNote,
|
||||
EXCLUDING_MOUSE_OUT_CLASS_LIST,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
DEFAULT_NOTE_HEIGHT,
|
||||
DEFAULT_NOTE_WIDTH,
|
||||
@@ -12,7 +15,6 @@ import { BaseTool } from '@blocksuite/block-std/gfx';
|
||||
import { Point } from '@blocksuite/global/gfx';
|
||||
import { effect } from '@preact/signals-core';
|
||||
|
||||
import { EXCLUDING_MOUSE_OUT_CLASS_LIST } from '../utils/consts.js';
|
||||
import { DraggingNoteOverlay, NoteOverlay } from '../utils/tool-overlay.js';
|
||||
|
||||
export type NoteToolOption = {
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
import {
|
||||
DEFAULT_ROUGHNESS,
|
||||
LineWidth,
|
||||
StrokeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
|
||||
export const NOTE_OVERLAY_OFFSET_X = 6;
|
||||
export const NOTE_OVERLAY_OFFSET_Y = 6;
|
||||
export const NOTE_OVERLAY_WIDTH = 100;
|
||||
@@ -14,20 +8,6 @@ export const NOTE_OVERLAY_TEXT_COLOR = '--affine-icon-color';
|
||||
export const NOTE_OVERLAY_LIGHT_BACKGROUND_COLOR = 'rgba(252, 252, 253, 1)';
|
||||
export const NOTE_OVERLAY_DARK_BACKGROUND_COLOR = 'rgb(32, 32, 32)';
|
||||
|
||||
export const SHAPE_OVERLAY_WIDTH = 100;
|
||||
export const SHAPE_OVERLAY_HEIGHT = 100;
|
||||
export const SHAPE_OVERLAY_OFFSET_X = 6;
|
||||
export const SHAPE_OVERLAY_OFFSET_Y = 6;
|
||||
export const SHAPE_OVERLAY_OPTIONS = {
|
||||
seed: 666,
|
||||
roughness: DEFAULT_ROUGHNESS,
|
||||
strokeStyle: StrokeStyle.Solid,
|
||||
strokeLineDash: [] as number[],
|
||||
stroke: 'black',
|
||||
strokeWidth: LineWidth.Two,
|
||||
fill: 'transparent',
|
||||
};
|
||||
|
||||
export const DEFAULT_NOTE_CHILD_FLAVOUR = 'affine:paragraph';
|
||||
export const DEFAULT_NOTE_CHILD_TYPE = 'text';
|
||||
export const DEFAULT_NOTE_TIP = 'Text';
|
||||
@@ -36,12 +16,6 @@ export const FIT_TO_SCREEN_PADDING = 100;
|
||||
|
||||
export const ATTACHED_DISTANCE = 20;
|
||||
|
||||
export const EXCLUDING_MOUSE_OUT_CLASS_LIST = [
|
||||
'affine-note-mask',
|
||||
'edgeless-block-portal-note',
|
||||
'affine-block-children-container',
|
||||
];
|
||||
|
||||
export const SurfaceColor = '#6046FE';
|
||||
export const NoteColor = '#1E96EB';
|
||||
export const BlendColor = '#7D91FF';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { ShapeToolOption } from '@blocksuite/affine-gfx-shape';
|
||||
import { ShapeType } from '@blocksuite/affine-model';
|
||||
|
||||
import type { ShapeToolOption } from '../gfx-tool/shape-tool.js';
|
||||
|
||||
const shapeMap: Record<ShapeToolOption['shapeName'], number> = {
|
||||
[ShapeType.Rect]: 0,
|
||||
[ShapeType.Ellipse]: 1,
|
||||
|
||||
@@ -3,7 +3,6 @@ import type {
|
||||
ConnectorElementModel,
|
||||
GroupElementModel,
|
||||
} 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';
|
||||
@@ -13,50 +12,6 @@ import * as Y from 'yjs';
|
||||
|
||||
import { EdgelessConnectorLabelEditor } from '../components/text/edgeless-connector-label-editor.js';
|
||||
import { EdgelessGroupTitleEditor } from '../components/text/edgeless-group-title-editor.js';
|
||||
import { EdgelessShapeTextEditor } from '../components/text/edgeless-shape-text-editor.js';
|
||||
|
||||
export function mountShapeTextEditor(
|
||||
shapeElement: ShapeElementModel,
|
||||
edgeless: BlockComponent
|
||||
) {
|
||||
const mountElm = edgeless.querySelector('.edgeless-mount-point');
|
||||
if (!mountElm) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.ValueNotExists,
|
||||
"edgeless block's mount point does not exist"
|
||||
);
|
||||
}
|
||||
|
||||
const gfx = edgeless.std.get(GfxControllerIdentifier);
|
||||
const crud = edgeless.std.get(EdgelessCRUDIdentifier);
|
||||
|
||||
const updatedElement = crud.getElementById(shapeElement.id);
|
||||
|
||||
if (!(updatedElement instanceof ShapeElementModel)) {
|
||||
console.error('Cannot mount text editor on a non-shape element');
|
||||
return;
|
||||
}
|
||||
|
||||
gfx.tool.setTool('default');
|
||||
gfx.selection.set({
|
||||
elements: [shapeElement.id],
|
||||
editing: true,
|
||||
});
|
||||
|
||||
if (!shapeElement.text) {
|
||||
const text = new Y.Text();
|
||||
edgeless.std
|
||||
.get(EdgelessCRUDIdentifier)
|
||||
.updateElement(shapeElement.id, { text });
|
||||
}
|
||||
|
||||
const shapeEditor = new EdgelessShapeTextEditor();
|
||||
shapeEditor.element = updatedElement;
|
||||
shapeEditor.edgeless = edgeless;
|
||||
shapeEditor.mountEditor = mountShapeTextEditor;
|
||||
|
||||
mountElm.append(shapeEditor);
|
||||
}
|
||||
|
||||
export function mountGroupTitleEditor(
|
||||
group: GroupElementModel,
|
||||
|
||||
@@ -1,25 +1,14 @@
|
||||
import {
|
||||
type Options,
|
||||
Overlay,
|
||||
type RoughCanvas,
|
||||
type SurfaceBlockComponent,
|
||||
ToolOverlay,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
type Color,
|
||||
DefaultTheme,
|
||||
shapeMethods,
|
||||
type ShapeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { type Color, DefaultTheme } from '@blocksuite/affine-model';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { GfxController, GfxToolsMap } from '@blocksuite/block-std/gfx';
|
||||
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||
import type { XYWH } from '@blocksuite/global/gfx';
|
||||
import { Bound } from '@blocksuite/global/gfx';
|
||||
import { assertType, noop } from '@blocksuite/global/utils';
|
||||
import { effect } from '@preact/signals-core';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import type { ShapeTool } from '../gfx-tool/shape-tool.js';
|
||||
import {
|
||||
NOTE_OVERLAY_CORNER_RADIUS,
|
||||
NOTE_OVERLAY_HEIGHT,
|
||||
@@ -28,319 +17,8 @@ import {
|
||||
NOTE_OVERLAY_STOKE_COLOR,
|
||||
NOTE_OVERLAY_TEXT_COLOR,
|
||||
NOTE_OVERLAY_WIDTH,
|
||||
SHAPE_OVERLAY_HEIGHT,
|
||||
SHAPE_OVERLAY_OFFSET_X,
|
||||
SHAPE_OVERLAY_OFFSET_Y,
|
||||
SHAPE_OVERLAY_WIDTH,
|
||||
} from '../utils/consts.js';
|
||||
|
||||
const drawRoundedRect = (ctx: CanvasRenderingContext2D, xywh: XYWH) => {
|
||||
const [x, y, w, h] = xywh;
|
||||
const width = w;
|
||||
const height = h;
|
||||
const radius = 0.1;
|
||||
const cornerRadius = Math.min(width * radius, height * radius);
|
||||
ctx.moveTo(x + cornerRadius, y);
|
||||
ctx.arcTo(x + width, y, x + width, y + height, cornerRadius);
|
||||
ctx.arcTo(x + width, y + height, x, y + height, cornerRadius);
|
||||
ctx.arcTo(x, y + height, x, y, cornerRadius);
|
||||
ctx.arcTo(x, y, x + width, y, cornerRadius);
|
||||
};
|
||||
|
||||
const drawGeneralShape = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
type: string,
|
||||
xywh: XYWH,
|
||||
options: Options
|
||||
) => {
|
||||
ctx.setLineDash(options.strokeLineDash ?? []);
|
||||
ctx.strokeStyle = options.stroke ?? 'transparent';
|
||||
ctx.lineWidth = options.strokeWidth ?? 2;
|
||||
ctx.fillStyle = options.fill ?? 'transparent';
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
const bound = Bound.fromXYWH(xywh);
|
||||
switch (type) {
|
||||
case 'rect':
|
||||
shapeMethods.rect.draw(ctx, bound);
|
||||
break;
|
||||
case 'triangle':
|
||||
shapeMethods.triangle.draw(ctx, bound);
|
||||
break;
|
||||
case 'diamond':
|
||||
shapeMethods.diamond.draw(ctx, bound);
|
||||
break;
|
||||
case 'ellipse':
|
||||
shapeMethods.ellipse.draw(ctx, bound);
|
||||
break;
|
||||
case 'roundedRect':
|
||||
drawRoundedRect(ctx, xywh);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown shape type: ${type}`);
|
||||
}
|
||||
|
||||
ctx.closePath();
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
export abstract class Shape {
|
||||
options: Options;
|
||||
|
||||
shapeStyle: ShapeStyle;
|
||||
|
||||
type: string;
|
||||
|
||||
xywh: XYWH;
|
||||
|
||||
constructor(
|
||||
xywh: XYWH,
|
||||
type: string,
|
||||
options: Options,
|
||||
shapeStyle: ShapeStyle
|
||||
) {
|
||||
this.xywh = xywh;
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
this.shapeStyle = shapeStyle;
|
||||
}
|
||||
|
||||
abstract draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void;
|
||||
}
|
||||
|
||||
export class RectShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
rc.rectangle(x, y, w, h, this.options);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'rect', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TriangleShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
rc.polygon(
|
||||
[
|
||||
[x + w / 2, y],
|
||||
[x, y + h],
|
||||
[x + w, y + h],
|
||||
],
|
||||
this.options
|
||||
);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'triangle', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DiamondShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
rc.polygon(
|
||||
[
|
||||
[x + w / 2, y],
|
||||
[x + w, y + h / 2],
|
||||
[x + w / 2, y + h],
|
||||
[x, y + h / 2],
|
||||
],
|
||||
this.options
|
||||
);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'diamond', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class EllipseShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
rc.ellipse(x + w / 2, y + h / 2, w, h, this.options);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'ellipse', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RoundedRectShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
const radius = 0.1;
|
||||
const r = Math.min(w * radius, h * radius);
|
||||
const x0 = x + r;
|
||||
const x1 = x + w - r;
|
||||
const y0 = y + r;
|
||||
const y1 = y + h - r;
|
||||
const path = `
|
||||
M${x0},${y} L${x1},${y}
|
||||
A${r},${r} 0 0 1 ${x1},${y0}
|
||||
L${x1},${y1}
|
||||
A${r},${r} 0 0 1 ${x1 - r},${y1}
|
||||
L${x0 + r},${y1}
|
||||
A${r},${r} 0 0 1 ${x0},${y1 - r}
|
||||
L${x0},${y0}
|
||||
A${r},${r} 0 0 1 ${x0 + r},${y}
|
||||
`;
|
||||
|
||||
rc.path(path, this.options);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'roundedRect', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ShapeFactory {
|
||||
static createShape(
|
||||
xywh: XYWH,
|
||||
type: string,
|
||||
options: Options,
|
||||
shapeStyle: ShapeStyle
|
||||
): Shape {
|
||||
switch (type) {
|
||||
case 'rect':
|
||||
return new RectShape(xywh, type, options, shapeStyle);
|
||||
case 'triangle':
|
||||
return new TriangleShape(xywh, type, options, shapeStyle);
|
||||
case 'diamond':
|
||||
return new DiamondShape(xywh, type, options, shapeStyle);
|
||||
case 'ellipse':
|
||||
return new EllipseShape(xywh, type, options, shapeStyle);
|
||||
case 'roundedRect':
|
||||
return new RoundedRectShape(xywh, type, options, shapeStyle);
|
||||
default:
|
||||
throw new Error(`Unknown shape type: ${type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ToolOverlay extends Overlay {
|
||||
protected disposables = new DisposableGroup();
|
||||
|
||||
globalAlpha: number;
|
||||
|
||||
x: number;
|
||||
|
||||
y: number;
|
||||
|
||||
constructor(gfx: GfxController) {
|
||||
super(gfx);
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.globalAlpha = 0;
|
||||
this.gfx = gfx;
|
||||
this.disposables.add(
|
||||
this.gfx.viewport.viewportUpdated.subscribe(() => {
|
||||
// when viewport is updated, we should keep the overlay in the same position
|
||||
// to get last mouse position and convert it to model coordinates
|
||||
const pos = this.gfx.tool.lastMousePos$.value;
|
||||
const [x, y] = this.gfx.viewport.toModelCoord(pos.x, pos.y);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
|
||||
render(_ctx: CanvasRenderingContext2D, _rc: RoughCanvas): void {
|
||||
noop();
|
||||
}
|
||||
}
|
||||
|
||||
export class ShapeOverlay extends ToolOverlay {
|
||||
shape: Shape;
|
||||
|
||||
constructor(
|
||||
gfx: GfxController,
|
||||
type: string,
|
||||
options: Options,
|
||||
style: {
|
||||
shapeStyle: ShapeStyle;
|
||||
fillColor: Color;
|
||||
strokeColor: Color;
|
||||
}
|
||||
) {
|
||||
super(gfx);
|
||||
const xywh = [
|
||||
this.x,
|
||||
this.y,
|
||||
SHAPE_OVERLAY_WIDTH,
|
||||
SHAPE_OVERLAY_HEIGHT,
|
||||
] as XYWH;
|
||||
const { shapeStyle, fillColor, strokeColor } = style;
|
||||
const fill = this.gfx.std
|
||||
.get(ThemeProvider)
|
||||
.getColorValue(fillColor, DefaultTheme.shapeFillColor, true);
|
||||
const stroke = this.gfx.std
|
||||
.get(ThemeProvider)
|
||||
.getColorValue(strokeColor, DefaultTheme.shapeStrokeColor, true);
|
||||
|
||||
options.fill = fill;
|
||||
options.stroke = stroke;
|
||||
|
||||
this.shape = ShapeFactory.createShape(xywh, type, options, shapeStyle);
|
||||
this.disposables.add(
|
||||
effect(() => {
|
||||
const currentTool = this.gfx.tool.currentTool$.value;
|
||||
|
||||
if (currentTool?.toolName !== 'shape') return;
|
||||
|
||||
assertType<ShapeTool>(currentTool);
|
||||
|
||||
const { shapeName } = currentTool.activatedOption;
|
||||
const newOptions = {
|
||||
...options,
|
||||
};
|
||||
|
||||
let { x, y } = this;
|
||||
if (shapeName === 'roundedRect' || shapeName === 'rect') {
|
||||
x += SHAPE_OVERLAY_OFFSET_X;
|
||||
y += SHAPE_OVERLAY_OFFSET_Y;
|
||||
}
|
||||
const w =
|
||||
shapeName === 'roundedRect'
|
||||
? SHAPE_OVERLAY_WIDTH + 40
|
||||
: SHAPE_OVERLAY_WIDTH;
|
||||
const xywh = [x, y, w, SHAPE_OVERLAY_HEIGHT] as XYWH;
|
||||
this.shape = ShapeFactory.createShape(
|
||||
xywh,
|
||||
shapeName,
|
||||
newOptions,
|
||||
shapeStyle
|
||||
);
|
||||
|
||||
(this.gfx.surfaceComponent as SurfaceBlockComponent).refresh();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override render(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
ctx.globalAlpha = this.globalAlpha;
|
||||
let { x, y } = this;
|
||||
const { type } = this.shape;
|
||||
if (type === 'roundedRect' || type === 'rect') {
|
||||
x += SHAPE_OVERLAY_OFFSET_X;
|
||||
y += SHAPE_OVERLAY_OFFSET_Y;
|
||||
}
|
||||
const w =
|
||||
type === 'roundedRect' ? SHAPE_OVERLAY_WIDTH + 40 : SHAPE_OVERLAY_WIDTH;
|
||||
const xywh = [x, y, w, SHAPE_OVERLAY_HEIGHT] as XYWH;
|
||||
this.shape.xywh = xywh;
|
||||
this.shape.draw(ctx, rc);
|
||||
}
|
||||
}
|
||||
|
||||
export class NoteOverlay extends ToolOverlay {
|
||||
backgroundColor = 'transparent';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { effects as gfxShapeEffects } from '@blocksuite/affine-gfx-shape/effects';
|
||||
import { effects as gfxCanvasTextEffects } from '@blocksuite/affine-gfx-text/effects';
|
||||
|
||||
import { EdgelessAutoCompletePanel } from './edgeless/components/auto-complete/auto-complete-panel.js';
|
||||
@@ -28,7 +29,6 @@ import {
|
||||
} from './edgeless/components/rects/edgeless-selected-rect.js';
|
||||
import { EdgelessConnectorLabelEditor } from './edgeless/components/text/edgeless-connector-label-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 { 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';
|
||||
@@ -54,9 +54,7 @@ import { EdgelessNavigatorSettingButton } from './edgeless/components/toolbar/pr
|
||||
import { EdgelessPresentButton } from './edgeless/components/toolbar/present/present-button.js';
|
||||
import { PresentationToolbar } from './edgeless/components/toolbar/presentation-toolbar.js';
|
||||
import { EdgelessToolbarShapeDraggable } from './edgeless/components/toolbar/shape/shape-draggable.js';
|
||||
import { EdgelessShapeMenu } from './edgeless/components/toolbar/shape/shape-menu.js';
|
||||
import { EdgelessShapeToolButton } from './edgeless/components/toolbar/shape/shape-tool-button.js';
|
||||
import { EdgelessShapeToolElement } from './edgeless/components/toolbar/shape/shape-tool-element.js';
|
||||
import { OverlayScrollbar } from './edgeless/components/toolbar/template/overlay-scrollbar.js';
|
||||
import { AffineTemplateLoading } from './edgeless/components/toolbar/template/template-loading.js';
|
||||
import { EdgelessTemplatePanel } from './edgeless/components/toolbar/template/template-panel.js';
|
||||
@@ -128,6 +126,7 @@ function registerRootComponents() {
|
||||
|
||||
function registerGfxEffects() {
|
||||
gfxCanvasTextEffects();
|
||||
gfxShapeEffects();
|
||||
}
|
||||
|
||||
function registerWidgets() {
|
||||
@@ -180,7 +179,6 @@ function registerEdgelessToolbarComponents() {
|
||||
customElements.define('edgeless-frame-menu', EdgelessFrameMenu);
|
||||
customElements.define('edgeless-mindmap-menu', EdgelessMindmapMenu);
|
||||
customElements.define('edgeless-note-menu', EdgelessNoteMenu);
|
||||
customElements.define('edgeless-shape-menu', EdgelessShapeMenu);
|
||||
customElements.define('edgeless-text-menu', EdgelessTextMenu);
|
||||
customElements.define('edgeless-slide-menu', EdgelessSlideMenu);
|
||||
|
||||
@@ -231,7 +229,6 @@ function registerEdgelessEditorComponents() {
|
||||
'edgeless-connector-label-editor',
|
||||
EdgelessConnectorLabelEditor
|
||||
);
|
||||
customElements.define('edgeless-shape-text-editor', EdgelessShapeTextEditor);
|
||||
customElements.define(
|
||||
'edgeless-group-title-editor',
|
||||
EdgelessGroupTitleEditor
|
||||
@@ -283,12 +280,6 @@ function registerMiscComponents() {
|
||||
// Mindmap components
|
||||
customElements.define('mindmap-import-placeholder', MindMapPlaceholder);
|
||||
|
||||
// Shape components
|
||||
customElements.define(
|
||||
'edgeless-shape-tool-element',
|
||||
EdgelessShapeToolElement
|
||||
);
|
||||
|
||||
// Connector components
|
||||
customElements.define('edgeless-connector-handle', EdgelessConnectorHandle);
|
||||
}
|
||||
@@ -317,7 +308,6 @@ declare global {
|
||||
'edgeless-selected-rect': EdgelessSelectedRectWidget;
|
||||
'edgeless-connector-label-editor': EdgelessConnectorLabelEditor;
|
||||
'edgeless-group-title-editor': EdgelessGroupTitleEditor;
|
||||
'edgeless-shape-text-editor': EdgelessShapeTextEditor;
|
||||
'edgeless-toolbar-widget': EdgelessToolbarWidget;
|
||||
'presentation-toolbar': PresentationToolbar;
|
||||
'edgeless-brush-menu': EdgelessBrushMenu;
|
||||
@@ -341,9 +331,7 @@ declare global {
|
||||
'edgeless-navigator-setting-button': EdgelessNavigatorSettingButton;
|
||||
'edgeless-present-button': EdgelessPresentButton;
|
||||
'edgeless-toolbar-shape-draggable': EdgelessToolbarShapeDraggable;
|
||||
'edgeless-shape-menu': EdgelessShapeMenu;
|
||||
'edgeless-shape-tool-button': EdgelessShapeToolButton;
|
||||
'edgeless-shape-tool-element': EdgelessShapeToolElement;
|
||||
'overlay-scrollbar': OverlayScrollbar;
|
||||
'affine-template-loading': AffineTemplateLoading;
|
||||
'edgeless-templates-panel': EdgelessTemplatePanel;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
{ "path": "../block-table" },
|
||||
{ "path": "../../components" },
|
||||
{ "path": "../../fragments/fragment-doc-title" },
|
||||
{ "path": "../../gfx/shape" },
|
||||
{ "path": "../../gfx/text" },
|
||||
{ "path": "../../inlines/latex" },
|
||||
{ "path": "../../inlines/link" },
|
||||
|
||||
@@ -16,3 +16,9 @@ export interface IModelCoord {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export const EXCLUDING_MOUSE_OUT_CLASS_LIST = [
|
||||
'affine-note-mask',
|
||||
'edgeless-block-portal-note',
|
||||
'affine-block-children-container',
|
||||
];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// oxlint-disable-next-line @typescript-eslint/triple-slash-reference
|
||||
/// <reference path="./effects.ts" />
|
||||
export { type IModelCoord, ZOOM_MAX, ZOOM_MIN, ZOOM_STEP } from './consts.js';
|
||||
export * from './consts.js';
|
||||
export { GRID_GAP_MAX, GRID_GAP_MIN } from './consts.js';
|
||||
export {
|
||||
SurfaceElementModel,
|
||||
@@ -29,6 +29,7 @@ export {
|
||||
export { fitContent } from './renderer/elements/shape/utils.js';
|
||||
export * from './renderer/elements/type.js';
|
||||
export { Overlay, OverlayIdentifier } from './renderer/overlay.js';
|
||||
export { ToolOverlay } from './renderer/tool-overlay.js';
|
||||
export { MindMapView } from './view/mindmap.js';
|
||||
import {
|
||||
getCursorByCoord,
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { GfxController } from '@blocksuite/block-std/gfx';
|
||||
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||
import { noop } from '@blocksuite/global/utils';
|
||||
|
||||
import type { RoughCanvas } from '../utils/rough/canvas';
|
||||
import { Overlay } from './overlay';
|
||||
|
||||
export class ToolOverlay extends Overlay {
|
||||
protected disposables = new DisposableGroup();
|
||||
|
||||
globalAlpha: number;
|
||||
|
||||
x: number;
|
||||
|
||||
y: number;
|
||||
|
||||
constructor(gfx: GfxController) {
|
||||
super(gfx);
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.globalAlpha = 0;
|
||||
this.gfx = gfx;
|
||||
this.disposables.add(
|
||||
this.gfx.viewport.viewportUpdated.subscribe(() => {
|
||||
// when viewport is updated, we should keep the overlay in the same position
|
||||
// to get last mouse position and convert it to model coordinates
|
||||
const pos = this.gfx.tool.lastMousePos$.value;
|
||||
const [x, y] = this.gfx.viewport.toModelCoord(pos.x, pos.y);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
|
||||
render(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
noop([ctx, rc]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-gfx-shape",
|
||||
"description": "Gfx shape for BlockSuite.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [],
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-surface": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-rich-text": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.6",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"minimatch": "^10.0.1",
|
||||
"rxjs": "^7.8.1",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.20.0"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
DEFAULT_ROUGHNESS,
|
||||
LineWidth,
|
||||
StrokeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
|
||||
export const SHAPE_OVERLAY_WIDTH = 100;
|
||||
export const SHAPE_OVERLAY_HEIGHT = 100;
|
||||
export const SHAPE_OVERLAY_OFFSET_X = 6;
|
||||
export const SHAPE_OVERLAY_OFFSET_Y = 6;
|
||||
export const SHAPE_OVERLAY_OPTIONS = {
|
||||
seed: 666,
|
||||
roughness: DEFAULT_ROUGHNESS,
|
||||
strokeStyle: StrokeStyle.Solid,
|
||||
strokeLineDash: [] as number[],
|
||||
stroke: 'black',
|
||||
strokeWidth: LineWidth.Two,
|
||||
fill: 'transparent',
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './shape-menu';
|
||||
export * from './shape-tool-element';
|
||||
+6
-4
@@ -12,6 +12,8 @@ import {
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import type { ColorEvent } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
|
||||
import { StyleGeneralIcon, StyleScribbleIcon } from '@blocksuite/icons/lit';
|
||||
import { computed, effect, type Signal, signal } from '@preact/signals-core';
|
||||
@@ -19,8 +21,7 @@ import { css, html, LitElement } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../../edgeless-root-block.js';
|
||||
import { ShapeComponentConfig } from './shape-menu-config.js';
|
||||
import { ShapeComponentConfig } from '../toolbar';
|
||||
|
||||
export class EdgelessShapeMenu extends SignalWatcher(
|
||||
WithDisposable(LitElement)
|
||||
@@ -55,7 +56,7 @@ export class EdgelessShapeMenu extends SignalWatcher(
|
||||
private readonly _shapeName$: Signal<ShapeName> = signal(ShapeType.Rect);
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor edgeless!: EdgelessRootBlockComponent;
|
||||
accessor edgeless!: BlockComponent;
|
||||
|
||||
private readonly _props$ = computed(() => {
|
||||
const shapeName: ShapeName = this._shapeName$.value;
|
||||
@@ -109,9 +110,10 @@ export class EdgelessShapeMenu extends SignalWatcher(
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
const gfx = this.edgeless.std.get(GfxControllerIdentifier);
|
||||
this._disposables.add(
|
||||
effect(() => {
|
||||
const value = this.edgeless.gfx.tool.currentToolOption$.value;
|
||||
const value = gfx.tool.currentToolOption$.value;
|
||||
|
||||
if (value && value.type === 'shape') {
|
||||
this._shapeName$.value = value.shapeName;
|
||||
+14
-8
@@ -8,6 +8,8 @@ import {
|
||||
type ShapeName,
|
||||
type ShapeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { Bound } from '@blocksuite/global/gfx';
|
||||
import { WithDisposable } from '@blocksuite/global/lit';
|
||||
import { sleep } from '@blocksuite/global/utils';
|
||||
@@ -20,10 +22,9 @@ import {
|
||||
} from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../../edgeless-root-block.js';
|
||||
import { ShapeTool } from '../../../gfx-tool/shape-tool.js';
|
||||
import { ShapeTool } from '../shape-tool';
|
||||
|
||||
export interface Shape {
|
||||
interface Shape {
|
||||
name: ShapeName;
|
||||
svg: TemplateResult<1>;
|
||||
}
|
||||
@@ -76,13 +77,17 @@ export class EdgelessShapeToolElement extends WithDisposable(LitElement) {
|
||||
return this.edgeless.std.get(EdgelessCRUDIdentifier);
|
||||
}
|
||||
|
||||
get gfx() {
|
||||
return this.edgeless.std.get(GfxControllerIdentifier);
|
||||
}
|
||||
|
||||
private readonly _addShape = (coord: Coord, padding: Coord) => {
|
||||
const width = 100;
|
||||
const height = 100;
|
||||
const { x: edgelessX, y: edgelessY } =
|
||||
this.edgeless.getBoundingClientRect();
|
||||
const zoom = this.edgeless.service.viewport.zoom;
|
||||
const [modelX, modelY] = this.edgeless.service.viewport.toModelCoord(
|
||||
const zoom = this.gfx.viewport.zoom;
|
||||
const [modelX, modelY] = this.gfx.viewport.toModelCoord(
|
||||
coord.x - edgelessX - width * padding.x * zoom,
|
||||
coord.y - edgelessY - height * padding.y * zoom
|
||||
);
|
||||
@@ -104,7 +109,8 @@ export class EdgelessShapeToolElement extends WithDisposable(LitElement) {
|
||||
return;
|
||||
}
|
||||
this._dragging = false;
|
||||
this.edgeless.gfx.tool.setTool('default');
|
||||
// @ts-expect-error FIXME: resolve after gfx tool refactor
|
||||
this.gfx.tool.setTool('default');
|
||||
if (this._isOutside) {
|
||||
const rect = this._shapeElement.getBoundingClientRect();
|
||||
this._backupShapeElement.style.setProperty('transition', 'none');
|
||||
@@ -131,7 +137,7 @@ export class EdgelessShapeToolElement extends WithDisposable(LitElement) {
|
||||
if (!this._dragging) {
|
||||
return;
|
||||
}
|
||||
const controller = this.edgeless.gfx.tool.currentTool$.peek();
|
||||
const controller = this.gfx.tool.currentTool$.peek();
|
||||
if (controller instanceof ShapeTool) {
|
||||
controller.clearOverlay();
|
||||
}
|
||||
@@ -298,7 +304,7 @@ export class EdgelessShapeToolElement extends WithDisposable(LitElement) {
|
||||
private accessor _startCoord: Coord = { x: -1, y: -1 };
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor edgeless!: EdgelessRootBlockComponent;
|
||||
accessor edgeless!: BlockComponent;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor getContainerRect!: () => DOMRect;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { EdgelessShapeMenu, EdgelessShapeToolElement } from './draggable';
|
||||
import { EdgelessShapeTextEditor } from './text/edgeless-shape-text-editor';
|
||||
|
||||
export function effects() {
|
||||
customElements.define('edgeless-shape-text-editor', EdgelessShapeTextEditor);
|
||||
customElements.define('edgeless-shape-menu', EdgelessShapeMenu);
|
||||
customElements.define(
|
||||
'edgeless-shape-tool-element',
|
||||
EdgelessShapeToolElement
|
||||
);
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'edgeless-shape-text-editor': EdgelessShapeTextEditor;
|
||||
'edgeless-shape-menu': EdgelessShapeMenu;
|
||||
'edgeless-shape-tool-element': EdgelessShapeToolElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from './consts';
|
||||
export * from './draggable';
|
||||
export * from './overlay';
|
||||
export * from './shape-tool';
|
||||
export * from './text';
|
||||
export * from './toolbar';
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { RoughCanvas } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { Shape } from './shape';
|
||||
import { drawGeneralShape } from './utils';
|
||||
|
||||
export class DiamondShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
rc.polygon(
|
||||
[
|
||||
[x + w / 2, y],
|
||||
[x + w, y + h / 2],
|
||||
[x + w / 2, y + h],
|
||||
[x, y + h / 2],
|
||||
],
|
||||
this.options
|
||||
);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'diamond', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { RoughCanvas } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { Shape } from './shape';
|
||||
import { drawGeneralShape } from './utils';
|
||||
|
||||
export class EllipseShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
rc.ellipse(x + w / 2, y + h / 2, w, h, this.options);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'ellipse', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { Options } from '@blocksuite/affine-block-surface';
|
||||
import type { ShapeStyle } from '@blocksuite/affine-model';
|
||||
import type { XYWH } from '@blocksuite/global/gfx';
|
||||
|
||||
import { DiamondShape } from './diamond';
|
||||
import { EllipseShape } from './ellipse';
|
||||
import { RectShape } from './rect';
|
||||
import { RoundedRectShape } from './rounded-rect';
|
||||
import type { Shape } from './shape';
|
||||
import { TriangleShape } from './triangle';
|
||||
|
||||
export class ShapeFactory {
|
||||
static createShape(
|
||||
xywh: XYWH,
|
||||
type: string,
|
||||
options: Options,
|
||||
shapeStyle: ShapeStyle
|
||||
): Shape {
|
||||
switch (type) {
|
||||
case 'rect':
|
||||
return new RectShape(xywh, type, options, shapeStyle);
|
||||
case 'triangle':
|
||||
return new TriangleShape(xywh, type, options, shapeStyle);
|
||||
case 'diamond':
|
||||
return new DiamondShape(xywh, type, options, shapeStyle);
|
||||
case 'ellipse':
|
||||
return new EllipseShape(xywh, type, options, shapeStyle);
|
||||
case 'roundedRect':
|
||||
return new RoundedRectShape(xywh, type, options, shapeStyle);
|
||||
default:
|
||||
throw new Error(`Unknown shape type: ${type}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './factory';
|
||||
export * from './shape';
|
||||
export * from './shape-overlay';
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { RoughCanvas } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { Shape } from './shape';
|
||||
import { drawGeneralShape } from './utils';
|
||||
|
||||
export class RectShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
rc.rectangle(x, y, w, h, this.options);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'rect', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { RoughCanvas } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { Shape } from './shape';
|
||||
import { drawGeneralShape } from './utils';
|
||||
|
||||
export class RoundedRectShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
const radius = 0.1;
|
||||
const r = Math.min(w * radius, h * radius);
|
||||
const x0 = x + r;
|
||||
const x1 = x + w - r;
|
||||
const y0 = y + r;
|
||||
const y1 = y + h - r;
|
||||
const path = `
|
||||
M${x0},${y} L${x1},${y}
|
||||
A${r},${r} 0 0 1 ${x1},${y0}
|
||||
L${x1},${y1}
|
||||
A${r},${r} 0 0 1 ${x1 - r},${y1}
|
||||
L${x0 + r},${y1}
|
||||
A${r},${r} 0 0 1 ${x0},${y1 - r}
|
||||
L${x0},${y0}
|
||||
A${r},${r} 0 0 1 ${x0 + r},${y}
|
||||
`;
|
||||
|
||||
rc.path(path, this.options);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'roundedRect', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import {
|
||||
type Options,
|
||||
type RoughCanvas,
|
||||
type SurfaceBlockComponent,
|
||||
ToolOverlay,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
type Color,
|
||||
DefaultTheme,
|
||||
type ShapeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { GfxController } from '@blocksuite/block-std/gfx';
|
||||
import type { XYWH } from '@blocksuite/global/gfx';
|
||||
import { assertType } from '@blocksuite/global/utils';
|
||||
import { effect } from '@preact/signals-core';
|
||||
|
||||
import {
|
||||
SHAPE_OVERLAY_HEIGHT,
|
||||
SHAPE_OVERLAY_OFFSET_X,
|
||||
SHAPE_OVERLAY_OFFSET_Y,
|
||||
SHAPE_OVERLAY_WIDTH,
|
||||
} from '../consts';
|
||||
import type { ShapeTool } from '../shape-tool';
|
||||
import { ShapeFactory } from './factory';
|
||||
import type { Shape } from './shape';
|
||||
|
||||
export class ShapeOverlay extends ToolOverlay {
|
||||
shape: Shape;
|
||||
|
||||
constructor(
|
||||
gfx: GfxController,
|
||||
type: string,
|
||||
options: Options,
|
||||
style: {
|
||||
shapeStyle: ShapeStyle;
|
||||
fillColor: Color;
|
||||
strokeColor: Color;
|
||||
}
|
||||
) {
|
||||
super(gfx);
|
||||
const xywh = [
|
||||
this.x,
|
||||
this.y,
|
||||
SHAPE_OVERLAY_WIDTH,
|
||||
SHAPE_OVERLAY_HEIGHT,
|
||||
] as XYWH;
|
||||
const { shapeStyle, fillColor, strokeColor } = style;
|
||||
const fill = this.gfx.std
|
||||
.get(ThemeProvider)
|
||||
.getColorValue(fillColor, DefaultTheme.shapeFillColor, true);
|
||||
const stroke = this.gfx.std
|
||||
.get(ThemeProvider)
|
||||
.getColorValue(strokeColor, DefaultTheme.shapeStrokeColor, true);
|
||||
|
||||
options.fill = fill;
|
||||
options.stroke = stroke;
|
||||
|
||||
this.shape = ShapeFactory.createShape(xywh, type, options, shapeStyle);
|
||||
this.disposables.add(
|
||||
effect(() => {
|
||||
const currentTool = this.gfx.tool.currentTool$.value;
|
||||
|
||||
if (currentTool?.toolName !== 'shape') return;
|
||||
|
||||
assertType<ShapeTool>(currentTool);
|
||||
|
||||
const { shapeName } = currentTool.activatedOption;
|
||||
const newOptions = {
|
||||
...options,
|
||||
};
|
||||
|
||||
let { x, y } = this;
|
||||
if (shapeName === 'roundedRect' || shapeName === 'rect') {
|
||||
x += SHAPE_OVERLAY_OFFSET_X;
|
||||
y += SHAPE_OVERLAY_OFFSET_Y;
|
||||
}
|
||||
const w =
|
||||
shapeName === 'roundedRect'
|
||||
? SHAPE_OVERLAY_WIDTH + 40
|
||||
: SHAPE_OVERLAY_WIDTH;
|
||||
const xywh = [x, y, w, SHAPE_OVERLAY_HEIGHT] as XYWH;
|
||||
this.shape = ShapeFactory.createShape(
|
||||
xywh,
|
||||
shapeName,
|
||||
newOptions,
|
||||
shapeStyle
|
||||
);
|
||||
|
||||
(this.gfx.surfaceComponent as SurfaceBlockComponent).refresh();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override render(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
ctx.globalAlpha = this.globalAlpha;
|
||||
let { x, y } = this;
|
||||
const { type } = this.shape;
|
||||
if (type === 'roundedRect' || type === 'rect') {
|
||||
x += SHAPE_OVERLAY_OFFSET_X;
|
||||
y += SHAPE_OVERLAY_OFFSET_Y;
|
||||
}
|
||||
const w =
|
||||
type === 'roundedRect' ? SHAPE_OVERLAY_WIDTH + 40 : SHAPE_OVERLAY_WIDTH;
|
||||
const xywh = [x, y, w, SHAPE_OVERLAY_HEIGHT] as XYWH;
|
||||
this.shape.xywh = xywh;
|
||||
this.shape.draw(ctx, rc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { Options, RoughCanvas } from '@blocksuite/affine-block-surface';
|
||||
import type { ShapeStyle } from '@blocksuite/affine-model';
|
||||
import type { XYWH } from '@blocksuite/global/gfx';
|
||||
|
||||
export abstract class Shape {
|
||||
options: Options;
|
||||
|
||||
shapeStyle: ShapeStyle;
|
||||
|
||||
type: string;
|
||||
|
||||
xywh: XYWH;
|
||||
|
||||
constructor(
|
||||
xywh: XYWH,
|
||||
type: string,
|
||||
options: Options,
|
||||
shapeStyle: ShapeStyle
|
||||
) {
|
||||
this.xywh = xywh;
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
this.shapeStyle = shapeStyle;
|
||||
}
|
||||
|
||||
abstract draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { RoughCanvas } from '@blocksuite/affine-block-surface';
|
||||
|
||||
import { Shape } from './shape';
|
||||
import { drawGeneralShape } from './utils';
|
||||
|
||||
export class TriangleShape extends Shape {
|
||||
draw(ctx: CanvasRenderingContext2D, rc: RoughCanvas): void {
|
||||
if (this.shapeStyle === 'Scribbled') {
|
||||
const [x, y, w, h] = this.xywh;
|
||||
rc.polygon(
|
||||
[
|
||||
[x + w / 2, y],
|
||||
[x, y + h],
|
||||
[x + w, y + h],
|
||||
],
|
||||
this.options
|
||||
);
|
||||
} else {
|
||||
drawGeneralShape(ctx, 'triangle', this.xywh, this.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { Options } from '@blocksuite/affine-block-surface';
|
||||
import { shapeMethods } from '@blocksuite/affine-model';
|
||||
import { Bound, type XYWH } from '@blocksuite/global/gfx';
|
||||
|
||||
export const drawGeneralShape = (
|
||||
ctx: CanvasRenderingContext2D,
|
||||
type: string,
|
||||
xywh: XYWH,
|
||||
options: Options
|
||||
) => {
|
||||
ctx.setLineDash(options.strokeLineDash ?? []);
|
||||
ctx.strokeStyle = options.stroke ?? 'transparent';
|
||||
ctx.lineWidth = options.strokeWidth ?? 2;
|
||||
ctx.fillStyle = options.fill ?? 'transparent';
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
const bound = Bound.fromXYWH(xywh);
|
||||
switch (type) {
|
||||
case 'rect':
|
||||
shapeMethods.rect.draw(ctx, bound);
|
||||
break;
|
||||
case 'triangle':
|
||||
shapeMethods.triangle.draw(ctx, bound);
|
||||
break;
|
||||
case 'diamond':
|
||||
shapeMethods.diamond.draw(ctx, bound);
|
||||
break;
|
||||
case 'ellipse':
|
||||
shapeMethods.ellipse.draw(ctx, bound);
|
||||
break;
|
||||
case 'roundedRect':
|
||||
drawRoundedRect(ctx, xywh);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown shape type: ${type}`);
|
||||
}
|
||||
|
||||
ctx.closePath();
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
function drawRoundedRect(ctx: CanvasRenderingContext2D, xywh: XYWH): void {
|
||||
const [x, y, w, h] = xywh;
|
||||
const width = w;
|
||||
const height = h;
|
||||
const radius = 0.1;
|
||||
const cornerRadius = Math.min(width * radius, height * radius);
|
||||
ctx.moveTo(x + cornerRadius, y);
|
||||
ctx.arcTo(x + width, y, x + width, y + height, cornerRadius);
|
||||
ctx.arcTo(x + width, y + height, x, y + height, cornerRadius);
|
||||
ctx.arcTo(x, y + height, x, y, cornerRadius);
|
||||
ctx.arcTo(x, y, x + width, y, cornerRadius);
|
||||
}
|
||||
+5
-3
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
CanvasElementType,
|
||||
EXCLUDING_MOUSE_OUT_CLASS_LIST,
|
||||
type SurfaceBlockComponent,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import type { ShapeElementModel, ShapeName } from '@blocksuite/affine-model';
|
||||
@@ -17,12 +18,11 @@ import { Bound } from '@blocksuite/global/gfx';
|
||||
import { effect } from '@preact/signals-core';
|
||||
|
||||
import {
|
||||
EXCLUDING_MOUSE_OUT_CLASS_LIST,
|
||||
SHAPE_OVERLAY_HEIGHT,
|
||||
SHAPE_OVERLAY_OPTIONS,
|
||||
SHAPE_OVERLAY_WIDTH,
|
||||
} from '../utils/consts.js';
|
||||
import { ShapeOverlay } from '../utils/tool-overlay.js';
|
||||
} from './consts.js';
|
||||
import { ShapeOverlay } from './overlay/shape-overlay.js';
|
||||
|
||||
export type ShapeToolOption = {
|
||||
shapeName: ShapeName;
|
||||
@@ -181,6 +181,7 @@ export class ShapeTool extends BaseTool<ShapeToolOption> {
|
||||
const element = this.gfx.getElementById(id);
|
||||
if (!element) return;
|
||||
|
||||
// @ts-expect-error FIXME: resolve after gfx tool refactor
|
||||
this.gfx.tool.setTool('default');
|
||||
this.gfx.selection.set({
|
||||
elements: [element.id],
|
||||
@@ -257,6 +258,7 @@ export class ShapeTool extends BaseTool<ShapeToolOption> {
|
||||
const element = this.gfx.getElementById(id);
|
||||
if (!element) return;
|
||||
|
||||
// @ts-expect-error FIXME: resolve after gfx tool refactor
|
||||
this.controller.setTool('default');
|
||||
this.gfx.selection.set({
|
||||
elements: [element.id],
|
||||
@@ -0,0 +1 @@
|
||||
export * from './text';
|
||||
@@ -0,0 +1,52 @@
|
||||
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||
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 * as Y from 'yjs';
|
||||
|
||||
import { EdgelessShapeTextEditor } from './edgeless-shape-text-editor';
|
||||
|
||||
export function mountShapeTextEditor(
|
||||
shapeElement: ShapeElementModel,
|
||||
edgeless: BlockComponent
|
||||
) {
|
||||
const mountElm = edgeless.querySelector('.edgeless-mount-point');
|
||||
if (!mountElm) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.ValueNotExists,
|
||||
"edgeless block's mount point does not exist"
|
||||
);
|
||||
}
|
||||
|
||||
const gfx = edgeless.std.get(GfxControllerIdentifier);
|
||||
const crud = edgeless.std.get(EdgelessCRUDIdentifier);
|
||||
|
||||
const updatedElement = crud.getElementById(shapeElement.id);
|
||||
|
||||
if (!(updatedElement instanceof ShapeElementModel)) {
|
||||
console.error('Cannot mount text editor on a non-shape element');
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error FIXME: resolve after gfx tool refactor
|
||||
gfx.tool.setTool('default');
|
||||
gfx.selection.set({
|
||||
elements: [shapeElement.id],
|
||||
editing: true,
|
||||
});
|
||||
|
||||
if (!shapeElement.text) {
|
||||
const text = new Y.Text();
|
||||
edgeless.std
|
||||
.get(EdgelessCRUDIdentifier)
|
||||
.updateElement(shapeElement.id, { text });
|
||||
}
|
||||
|
||||
const shapeEditor = new EdgelessShapeTextEditor();
|
||||
shapeEditor.element = updatedElement;
|
||||
shapeEditor.edgeless = edgeless;
|
||||
shapeEditor.mountEditor = mountShapeTextEditor;
|
||||
|
||||
mountElm.append(shapeEditor);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './icons';
|
||||
export * from './shape-menu-config';
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
import type { ShapeToolOption } from '@blocksuite/affine-gfx-shape';
|
||||
import { ShapeType } from '@blocksuite/affine-model';
|
||||
import {
|
||||
DiamondIcon,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
} from '@blocksuite/icons/lit';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
import type { ShapeToolOption } from '../../../gfx-tool/shape-tool';
|
||||
import {
|
||||
ScribbledDiamondIcon,
|
||||
ScribbledEllipseIcon,
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../../blocks/block-surface" },
|
||||
{ "path": "../../components" },
|
||||
{ "path": "../../model" },
|
||||
{ "path": "../../rich-text" },
|
||||
{ "path": "../../shared" },
|
||||
{ "path": "../../../framework/block-std" },
|
||||
{ "path": "../../../framework/global" },
|
||||
{ "path": "../../../framework/store" }
|
||||
]
|
||||
}
|
||||
@@ -28,6 +28,7 @@ export const PackageList = [
|
||||
'blocksuite/affine/fragments/fragment-doc-title',
|
||||
'blocksuite/affine/fragments/fragment-frame-panel',
|
||||
'blocksuite/affine/fragments/fragment-outline',
|
||||
'blocksuite/affine/gfx/shape',
|
||||
'blocksuite/affine/gfx/text',
|
||||
'blocksuite/affine/gfx/turbo-renderer',
|
||||
'blocksuite/affine/inlines/footnote',
|
||||
@@ -303,6 +304,7 @@ export const PackageList = [
|
||||
'blocksuite/affine/blocks/block-table',
|
||||
'blocksuite/affine/components',
|
||||
'blocksuite/affine/fragments/fragment-doc-title',
|
||||
'blocksuite/affine/gfx/shape',
|
||||
'blocksuite/affine/gfx/text',
|
||||
'blocksuite/affine/inlines/latex',
|
||||
'blocksuite/affine/inlines/link',
|
||||
@@ -435,6 +437,20 @@ export const PackageList = [
|
||||
'blocksuite/framework/store',
|
||||
],
|
||||
},
|
||||
{
|
||||
location: 'blocksuite/affine/gfx/shape',
|
||||
name: '@blocksuite/affine-gfx-shape',
|
||||
workspaceDependencies: [
|
||||
'blocksuite/affine/blocks/block-surface',
|
||||
'blocksuite/affine/components',
|
||||
'blocksuite/affine/model',
|
||||
'blocksuite/affine/rich-text',
|
||||
'blocksuite/affine/shared',
|
||||
'blocksuite/framework/block-std',
|
||||
'blocksuite/framework/global',
|
||||
'blocksuite/framework/store',
|
||||
],
|
||||
},
|
||||
{
|
||||
location: 'blocksuite/affine/gfx/text',
|
||||
name: '@blocksuite/affine-gfx-text',
|
||||
@@ -1020,6 +1036,7 @@ export type PackageName =
|
||||
| '@blocksuite/affine-fragment-doc-title'
|
||||
| '@blocksuite/affine-fragment-frame-panel'
|
||||
| '@blocksuite/affine-fragment-outline'
|
||||
| '@blocksuite/affine-gfx-shape'
|
||||
| '@blocksuite/affine-gfx-text'
|
||||
| '@blocksuite/affine-gfx-turbo-renderer'
|
||||
| '@blocksuite/affine-inline-footnote'
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
{ "path": "./blocksuite/affine/fragments/fragment-doc-title" },
|
||||
{ "path": "./blocksuite/affine/fragments/fragment-frame-panel" },
|
||||
{ "path": "./blocksuite/affine/fragments/fragment-outline" },
|
||||
{ "path": "./blocksuite/affine/gfx/shape" },
|
||||
{ "path": "./blocksuite/affine/gfx/text" },
|
||||
{ "path": "./blocksuite/affine/gfx/turbo-renderer" },
|
||||
{ "path": "./blocksuite/affine/inlines/footnote" },
|
||||
|
||||
@@ -2739,6 +2739,7 @@ __metadata:
|
||||
"@blocksuite/affine-block-table": "workspace:*"
|
||||
"@blocksuite/affine-components": "workspace:*"
|
||||
"@blocksuite/affine-fragment-doc-title": "workspace:*"
|
||||
"@blocksuite/affine-gfx-shape": "workspace:*"
|
||||
"@blocksuite/affine-gfx-text": "workspace:*"
|
||||
"@blocksuite/affine-inline-latex": "workspace:*"
|
||||
"@blocksuite/affine-inline-link": "workspace:*"
|
||||
@@ -2972,6 +2973,32 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@blocksuite/affine-gfx-shape@workspace:*, @blocksuite/affine-gfx-shape@workspace:blocksuite/affine/gfx/shape":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@blocksuite/affine-gfx-shape@workspace:blocksuite/affine/gfx/shape"
|
||||
dependencies:
|
||||
"@blocksuite/affine-block-surface": "workspace:*"
|
||||
"@blocksuite/affine-components": "workspace:*"
|
||||
"@blocksuite/affine-model": "workspace:*"
|
||||
"@blocksuite/affine-rich-text": "workspace:*"
|
||||
"@blocksuite/affine-shared": "workspace:*"
|
||||
"@blocksuite/block-std": "workspace:*"
|
||||
"@blocksuite/global": "workspace:*"
|
||||
"@blocksuite/icons": "npm:^2.2.6"
|
||||
"@blocksuite/store": "workspace:*"
|
||||
"@lit/context": "npm:^1.1.2"
|
||||
"@preact/signals-core": "npm:^1.8.0"
|
||||
"@toeverything/theme": "npm:^1.1.12"
|
||||
"@types/lodash-es": "npm:^4.17.12"
|
||||
lit: "npm:^3.2.0"
|
||||
lodash-es: "npm:^4.17.21"
|
||||
minimatch: "npm:^10.0.1"
|
||||
rxjs: "npm:^7.8.1"
|
||||
yjs: "npm:^13.6.21"
|
||||
zod: "npm:^3.23.8"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@blocksuite/affine-gfx-text@workspace:*, @blocksuite/affine-gfx-text@workspace:blocksuite/affine/gfx/text":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@blocksuite/affine-gfx-text@workspace:blocksuite/affine/gfx/text"
|
||||
@@ -3433,6 +3460,7 @@ __metadata:
|
||||
"@blocksuite/affine-fragment-doc-title": "workspace:*"
|
||||
"@blocksuite/affine-fragment-frame-panel": "workspace:*"
|
||||
"@blocksuite/affine-fragment-outline": "workspace:*"
|
||||
"@blocksuite/affine-gfx-shape": "workspace:*"
|
||||
"@blocksuite/affine-gfx-text": "workspace:*"
|
||||
"@blocksuite/affine-gfx-turbo-renderer": "workspace:*"
|
||||
"@blocksuite/affine-inline-footnote": "workspace:*"
|
||||
|
||||
Reference in New Issue
Block a user