diff --git a/packages/blocky-core/css/blocky-core.css b/packages/blocky-core/css/blocky-core.css index 48c7cba9..5ae87bb7 100644 --- a/packages/blocky-core/css/blocky-core.css +++ b/packages/blocky-core/css/blocky-core.css @@ -19,7 +19,7 @@ justify-content: center; } -.blocky-editor-banner-delegate { +.blocky-editor-spanner-delegate { position: absolute; } diff --git a/packages/blocky-core/src/block/basic.ts b/packages/blocky-core/src/block/basic.ts index 58579ad9..de99be07 100644 --- a/packages/blocky-core/src/block/basic.ts +++ b/packages/blocky-core/src/block/basic.ts @@ -173,7 +173,7 @@ export class Block implements IDisposable { * Return the offset of the coordinate of the banner * relative to the top-right conner of the block. */ - getBannerOffset(): Position { + getSpannerOffset(): Position { return { x: 0, y: 0 }; } diff --git a/packages/blocky-core/src/block/textBlock.ts b/packages/blocky-core/src/block/textBlock.ts index 872ab19e..5658e1d0 100644 --- a/packages/blocky-core/src/block/textBlock.ts +++ b/packages/blocky-core/src/block/textBlock.ts @@ -221,7 +221,7 @@ export class TextBlock extends Block { return 18; } - override getBannerOffset(): Position { + override getSpannerOffset(): Position { const textType = this.getTextType(); const precedence = textTypePrecedence(textType); diff --git a/packages/blocky-core/src/index.ts b/packages/blocky-core/src/index.ts index 11334241..ebb85bc5 100644 --- a/packages/blocky-core/src/index.ts +++ b/packages/blocky-core/src/index.ts @@ -2,7 +2,10 @@ export * from "./view/editor"; export * from "./view/controller"; export * from "./block/basic"; export { makeDefaultIdGenerator, type IdGenerator } from "./helper/idHelper"; -export { type BannerFactory, type BannerInstance } from "./view/bannerDelegate"; +export { + type SpannerFactory, + type SpannerInstance, +} from "./view/spannerDelegate"; export { type ToolbarFactory, type Toolbar } from "./view/toolbarDelegate"; export { FollowerWidget } from "./view/followerWidget"; export { diff --git a/packages/blocky-core/src/view/controller.ts b/packages/blocky-core/src/view/controller.ts index 873bcdfe..b09a6e0e 100644 --- a/packages/blocky-core/src/view/controller.ts +++ b/packages/blocky-core/src/view/controller.ts @@ -21,7 +21,7 @@ import { PluginRegistry, type IPlugin } from "@pkg/registry/pluginRegistry"; import { SpanRegistry } from "@pkg/registry/spanRegistry"; import { EmbedRegistry } from "@pkg/registry/embedRegistry"; import { HTMLConverter } from "@pkg/helper/htmlConverter"; -import { type BannerFactory } from "@pkg/view/bannerDelegate"; +import { type SpannerFactory } from "@pkg/view/spannerDelegate"; import { type ToolbarFactory } from "@pkg/view/toolbarDelegate"; import { type IdGenerator, makeDefaultIdGenerator } from "@pkg/helper/idHelper"; import { BlockPasteEvent, TryParsePastedDOMEvent } from "@pkg/block/basic"; @@ -70,7 +70,7 @@ export interface IEditorControllerOptions { blockRegistry?: BlockRegistry; embedRegistry?: EmbedRegistry; idGenerator?: IdGenerator; - bannerFactory?: BannerFactory; + spannerFactory?: SpannerFactory; toolbarFactory?: ToolbarFactory; /** diff --git a/packages/blocky-core/src/view/editor.ts b/packages/blocky-core/src/view/editor.ts index b2b63002..c0a0a27b 100644 --- a/packages/blocky-core/src/view/editor.ts +++ b/packages/blocky-core/src/view/editor.ts @@ -44,7 +44,7 @@ import { SpanRegistry } from "@pkg/registry/spanRegistry"; import { BlockRegistry } from "@pkg/registry/blockRegistry"; import { type IdGenerator, makeDefaultIdGenerator } from "@pkg/helper/idHelper"; import { textToDeltaWithURL } from "@pkg/helper/urlHelper"; -import { BannerDelegate, type BannerFactory } from "./bannerDelegate"; +import { SpannerDelegate, type SpannerFactory } from "./spannerDelegate"; import { ToolbarDelegate, type ToolbarFactory } from "./toolbarDelegate"; import { TextBlock } from "@pkg/block/textBlock"; import { UndoManager } from "@pkg/model/undoManager"; @@ -80,7 +80,7 @@ export interface IEditorOptions { registry: EditorRegistry; container: HTMLDivElement; idGenerator?: IdGenerator; - bannerFactory?: BannerFactory; + spannerFactory?: SpannerFactory; toolbarFactory?: ToolbarFactory; padding?: Partial; collaborativeCursorFactory?: CollaborativeCursorFactory; @@ -147,7 +147,7 @@ export class Editor { readonly onEveryBlock: Subject = new Subject(); - readonly bannerDelegate: BannerDelegate; + readonly spannerDelegate?: SpannerDelegate; readonly toolbarDelegate: ToolbarDelegate; idGenerator: IdGenerator; @@ -180,7 +180,7 @@ export class Editor { block: controller.blockRegistry, }, state: controller.state, - bannerFactory: controller.options?.bannerFactory, + spannerFactory: controller.options?.spannerFactory, toolbarFactory: controller.options?.toolbarFactory, padding: controller.options?.padding, collaborativeCursorFactory: @@ -196,7 +196,7 @@ export class Editor { state, registry, idGenerator, - bannerFactory, + spannerFactory: bannerFactory, toolbarFactory, padding, collaborativeCursorFactory, @@ -216,9 +216,11 @@ export class Editor { ); this.collaborativeCursorManager.mount(this.#container); - this.bannerDelegate = new BannerDelegate(controller, bannerFactory); - this.bannerDelegate.mount(this.#container); - this.disposables.push(this.bannerDelegate); + if (bannerFactory) { + this.spannerDelegate = new SpannerDelegate(controller, bannerFactory); + this.spannerDelegate.mount(this.#container); + this.disposables.push(this.spannerDelegate); + } this.toolbarDelegate = new ToolbarDelegate({ controller, @@ -233,7 +235,7 @@ export class Editor { .pipe(takeUntil(this.dispose$)) .subscribe(this.handleCursorStateChanged); - this.disposables.push($on(container, "mouseleave", this.#hideBanner)); + this.disposables.push($on(container, "mouseleave", this.#hideSpanner)); this.registry.plugin.emitInitPlugins(this); @@ -783,7 +785,10 @@ export class Editor { return this.#searchContext; } - placeBannerAt(blockContainer: HTMLElement, node: BlockDataElement) { + placeSpannerAt(blockContainer: HTMLElement, node: BlockDataElement) { + if (!this.spannerDelegate) { + return; + } const block = this.state.blocks.get(node.id); if (!block) { return; @@ -791,15 +796,15 @@ export class Editor { let { x, y } = this.#getRelativeOffsetByDom(blockContainer); - const offset = block.getBannerOffset(); + const offset = block.getSpannerOffset(); x += offset.x; y += offset.y; - x -= this.bannerDelegate.width; + x -= this.spannerDelegate.width; - this.bannerDelegate.focusedNode = node; - this.bannerDelegate.show(); - this.bannerDelegate.setPosition(x, y); + this.spannerDelegate.focusedNode = node; + this.spannerDelegate.show(); + this.spannerDelegate.setPosition(x, y); } /** @@ -832,8 +837,8 @@ export class Editor { }; } - #hideBanner = () => { - this.bannerDelegate.hide(); + #hideSpanner = () => { + this.spannerDelegate?.hide(); }; #handleCompositionStart = () => { diff --git a/packages/blocky-core/src/view/renderer.ts b/packages/blocky-core/src/view/renderer.ts index e18cbac9..1b96d578 100644 --- a/packages/blocky-core/src/view/renderer.ts +++ b/packages/blocky-core/src/view/renderer.ts @@ -266,7 +266,7 @@ export class DocRenderer { editor.state.setDom(blockNode.id, blockContainer); blockContainer.setAttribute("data-type", blockDef.name); blockContainer.addEventListener("mouseenter", () => { - editor.placeBannerAt(blockContainer, blockNode); + editor.placeSpannerAt(blockContainer, blockNode); }); const block = editor.state.blocks.get(blockNode.id); diff --git a/packages/blocky-core/src/view/bannerDelegate.ts b/packages/blocky-core/src/view/spannerDelegate.ts similarity index 57% rename from packages/blocky-core/src/view/bannerDelegate.ts rename to packages/blocky-core/src/view/spannerDelegate.ts index e3a6598b..8485354d 100644 --- a/packages/blocky-core/src/view/bannerDelegate.ts +++ b/packages/blocky-core/src/view/spannerDelegate.ts @@ -3,17 +3,17 @@ import type { EditorController } from "@pkg/view/controller"; import type { BlockDataElement } from "blocky-data"; import { UIDelegate } from "./uiDelegate"; -export interface BannerInstance extends IDisposable { +export interface SpannerInstance extends IDisposable { onFocusedNodeChanged?(focusedNode: BlockDataElement | undefined): void; } -export type BannerFactory = ( +export type SpannerFactory = ( dom: HTMLDivElement, editorController: EditorController -) => BannerInstance | undefined; +) => SpannerInstance | undefined; -export class BannerDelegate extends UIDelegate { - #instance: BannerInstance | undefined; +export class SpannerDelegate extends UIDelegate { + #instance: SpannerInstance | undefined; #focusedNode: BlockDataElement | undefined; get focusedNode(): BlockDataElement | undefined { @@ -31,30 +31,20 @@ export class BannerDelegate extends UIDelegate { constructor( private editorController: EditorController, - private factory?: BannerFactory + private factory: SpannerFactory ) { - super("blocky-editor-banner-delegate blocky-cm-noselect"); + super("blocky-editor-spanner-delegate blocky-cm-noselect"); } override mount(parent: HTMLElement): void { super.mount(parent); - if (this.factory) { - this.#instance = this.factory(this.container, this.editorController); - if (this.#instance) { - this.disposables.push(this.#instance); - } - } else { - this.renderFallback(); + this.#instance = this.factory(this.container, this.editorController); + if (this.#instance) { + this.disposables.push(this.#instance); } } - renderFallback() { - this.container.style.width = "16px"; - this.container.style.height = "16px"; - this.container.style.backgroundColor = "grey"; - } - setPosition(x: number, y: number) { this.container.style.top = y + "px"; this.container.style.left = x + "px"; diff --git a/packages/blocky-example/src/app.tsx b/packages/blocky-example/src/app.tsx index 94bdbe90..867a37cd 100644 --- a/packages/blocky-example/src/app.tsx +++ b/packages/blocky-example/src/app.tsx @@ -2,9 +2,9 @@ import { Component, createRef, RefObject, useEffect, useState } from "react"; import { EditorController, darkTheme, type IPlugin } from "blocky-core"; import { BlockyEditor, - makeReactBanner, + makeReactSpanner, makeReactToolbar, - type BannerRenderProps, + type SpannerRenderProps, } from "blocky-react"; import makeStyledTextPlugin from "blocky-core/dist/plugins/styledTextPlugin"; import makeCodeTextPlugin from "blocky-core/dist/plugins/codeTextPlugin"; @@ -14,7 +14,7 @@ import SearchBox from "@pkg/components/searchBox"; import { makeImageBlockPlugin } from "./plugins/imageBlock"; import { makeCommandPanelPlugin } from "./plugins/commandPanel"; import { makeAtPanelPlugin } from "./plugins/atPanel"; -import BannerMenu from "./bannerMenu"; +import SpannerMenu from "./spannerMenu"; import ToolbarMenu from "./toolbarMenu"; import TianShuiWeiImage from "./tianshuiwei.jpg"; import { ReadMeContent } from "./readme"; @@ -70,9 +70,9 @@ function makeController( * Tell the editor how to render the banner. * We use a banner written in Preact here. */ - bannerFactory: makeReactBanner( - ({ editorController, focusedNode }: BannerRenderProps) => ( - ( + diff --git a/packages/blocky-example/src/docs/get-started.md b/packages/blocky-example/src/docs/get-started.md index cb880964..2d9d094d 100644 --- a/packages/blocky-example/src/docs/get-started.md +++ b/packages/blocky-example/src/docs/get-started.md @@ -34,7 +34,7 @@ You can define how the editor render the toolbar. ```tsx import { EditorController } from "blocky-core"; -import { makeReactBanner, makeReactToolbar } from "blocky-react"; +import { makeReactSpanner, makeReactToolbar } from "blocky-react"; import BannerMenu from "./bannerMenu"; import ToolbarMenu from "./toolbarMenu"; import makeStyledTextPlugin from "blocky-core/dist/plugins/styledTextPlugin"; @@ -60,7 +60,7 @@ function makeController(): EditorController { * Tell the editor how to render the banner. * We use a banner written in React here. */ - bannerFactory: makeReactBanner((editorController: EditorController) => ( + bannerFactory: makeReactSpanner((editorController: EditorController) => ( )), /** diff --git a/packages/blocky-example/src/noTitle/noTitle.tsx b/packages/blocky-example/src/noTitle/noTitle.tsx index 84b44277..00e56d3c 100644 --- a/packages/blocky-example/src/noTitle/noTitle.tsx +++ b/packages/blocky-example/src/noTitle/noTitle.tsx @@ -2,9 +2,9 @@ import { Component, ReactNode, createRef } from "react"; import Sidebar from "@pkg/components/sidebar"; import { BlockyEditor, - makeReactBanner, + makeReactSpanner, makeReactToolbar, - type BannerRenderProps, + type SpannerRenderProps, } from "blocky-react"; import { EditorController, IPlugin } from "blocky-core"; import makeStyledTextPlugin from "blocky-core/dist/plugins/styledTextPlugin"; @@ -14,7 +14,7 @@ import makeHeadingsPlugin from "blocky-core/dist/plugins/headingsPlugin"; import { makeImageBlockPlugin } from "@pkg/plugins/imageBlock"; import { makeCommandPanelPlugin } from "@pkg/plugins/commandPanel"; import { makeAtPanelPlugin } from "@pkg/plugins/atPanel"; -import BannerMenu from "@pkg/bannerMenu"; +import SpannerMenu from "@pkg/spannerMenu"; import ToolbarMenu from "@pkg/toolbarMenu"; import { timer, Subject, takeUntil } from "rxjs"; @@ -39,18 +39,6 @@ function makeController( * Define the plugins to implement customize features. */ plugins: makeEditorPlugins(), - /** - * Tell the editor how to render the banner. - * We use a banner written in Preact here. - */ - bannerFactory: makeReactBanner( - ({ editorController, focusedNode }: BannerRenderProps) => ( - - ) - ), /** * Tell the editor how to render the banner. * We use a toolbar written in Preact here. diff --git a/packages/blocky-example/src/bannerMenu.scss b/packages/blocky-example/src/spannerMenu.scss similarity index 100% rename from packages/blocky-example/src/bannerMenu.scss rename to packages/blocky-example/src/spannerMenu.scss diff --git a/packages/blocky-example/src/bannerMenu.tsx b/packages/blocky-example/src/spannerMenu.tsx similarity index 93% rename from packages/blocky-example/src/bannerMenu.tsx rename to packages/blocky-example/src/spannerMenu.tsx index 38ed3918..9f9b8cf3 100644 --- a/packages/blocky-example/src/bannerMenu.tsx +++ b/packages/blocky-example/src/spannerMenu.tsx @@ -5,21 +5,21 @@ import Dropdown from "@pkg/components/dropdown"; import { Menu, MenuItem, Divider } from "@pkg/components/menu"; import { ImageBlockName } from "@pkg/plugins/imageBlock"; import { Subject, takeUntil } from "rxjs"; -import "./bannerMenu.scss"; +import "./spannerMenu.scss"; -export interface BannerProps { +export interface SpannerProps { editorController: EditorController; focusedNode?: BlockDataElement; } -interface BannerState { +interface SpannerState { showDropdown: boolean; menuX: number; menuY: number; showDelete: boolean; } -const BannerIcon = ` +const SpannerIcon = ` @@ -27,11 +27,11 @@ const BannerIcon = ` `; -class BannerMenu extends Component { +class SpannerMenu extends Component { private bannerRef: RefObject = createRef(); private dispose$ = new Subject(); - constructor(props: BannerProps) { + constructor(props: SpannerProps) { super(props); this.state = { showDropdown: false, @@ -53,7 +53,7 @@ class BannerMenu extends Component { this.handleBlocksChanged(); - this.bannerRef.current!.innerHTML = BannerIcon; + this.bannerRef.current!.innerHTML = SpannerIcon; } override componentWillUnmount() { @@ -175,4 +175,4 @@ class BannerMenu extends Component { } } -export default BannerMenu; +export default SpannerMenu; diff --git a/packages/blocky-react/src/index.ts b/packages/blocky-react/src/index.ts index fccbe22b..48582f51 100644 --- a/packages/blocky-react/src/index.ts +++ b/packages/blocky-react/src/index.ts @@ -8,8 +8,8 @@ export * from "./reactBlock"; export * from "./reactFollowerWidget"; export * from "./blockActiveDetector"; export { - makeReactBanner, - type RenderProps as BannerRenderProps, -} from "./banner"; + makeReactSpanner, + type RenderProps as SpannerRenderProps, +} from "./spanner"; export { makeReactToolbar } from "./toolbar"; export { DefaultBlockOutline, type DefaultBlockOutlineProps }; diff --git a/packages/blocky-react/src/banner.tsx b/packages/blocky-react/src/spanner.tsx similarity index 87% rename from packages/blocky-react/src/banner.tsx rename to packages/blocky-react/src/spanner.tsx index 12d90ad8..4f2bda75 100644 --- a/packages/blocky-react/src/banner.tsx +++ b/packages/blocky-react/src/spanner.tsx @@ -2,9 +2,9 @@ import React from "react"; import { createRoot } from "react-dom/client"; import type { BlockDataElement } from "blocky-data"; import type { - BannerFactory, + SpannerFactory, EditorController, - BannerInstance, + SpannerInstance, } from "blocky-core"; import { once } from "lodash-es"; @@ -15,11 +15,11 @@ export interface RenderProps { export type Renderer = (props: RenderProps) => React.ReactNode; -export function makeReactBanner(renderer: Renderer): BannerFactory { +export function makeReactSpanner(renderer: Renderer): SpannerFactory { return ( container: HTMLDivElement, editorController: EditorController - ): BannerInstance => { + ): SpannerInstance => { let focusedNode: BlockDataElement | undefined; const root = createRoot(container); const renderFn = () => {