diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-hovering.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx
similarity index 81%
rename from packages/designer/src/builtin-simulator/bem-tools/border-hovering.tsx
rename to packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx
index fa0d3b1dc..f6bd20460 100644
--- a/packages/designer/src/builtin-simulator/bem-tools/border-hovering.tsx
+++ b/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx
@@ -1,11 +1,10 @@
import { Component, Fragment, PureComponent } from 'react';
import classNames from 'classnames';
import { computed, observer, Title } from '@ali/lowcode-editor-core';
-import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host';
import { TitleContent } from '@ali/lowcode-types';
-export class BorderHoveringInstance extends PureComponent<{
+export class BorderDetectingInstance extends PureComponent<{
title: TitleContent;
rect: DOMRect | null;
scale: number;
@@ -24,7 +23,7 @@ export class BorderHoveringInstance extends PureComponent<{
transform: `translate(${(scrollX + rect.left) * scale}px, ${(scrollY + rect.top) * scale}px)`,
};
- const className = classNames('lc-borders lc-borders-hovering');
+ const className = classNames('lc-borders lc-borders-detecting');
// TODO:
// 1. thinkof icon
@@ -39,7 +38,7 @@ export class BorderHoveringInstance extends PureComponent<{
}
@observer
-export class BorderHovering extends Component<{ host: BuiltinSimulatorHost }> {
+export class BorderDetecting extends Component<{ host: BuiltinSimulatorHost }> {
shouldComponentUpdate() {
return false;
}
@@ -60,7 +59,7 @@ export class BorderHovering extends Component<{ host: BuiltinSimulatorHost }> {
const host = this.props.host;
const doc = host.document;
const selection = doc.selection;
- const current = host.designer.hovering.current;
+ const current = host.designer.detecting.current;
if (!current || current.document !== doc || selection.has(current.id)) {
return null;
}
@@ -70,36 +69,36 @@ export class BorderHovering extends Component<{ host: BuiltinSimulatorHost }> {
render() {
const host = this.props.host;
const current = this.current;
- if (!current || host.viewport.scrolling) {
- return ;
+ if (!current || host.viewport.scrolling || host.liveEditing.editing) {
+ return null;
}
const instances = host.getComponentInstances(current);
if (!instances || instances.length < 1) {
- return ;
+ return null;
}
if (instances.length === 1) {
return (
-
);
}
return (
{instances.map((inst, i) => (
-
))}
diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx
index 53b051ca5..209fce326 100644
--- a/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx
+++ b/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx
@@ -12,7 +12,6 @@ import classNames from 'classnames';
import { observer, computed, Tip } from '@ali/lowcode-editor-core';
import { createIcon, isReactComponent } from '@ali/lowcode-utils';
import { ActionContentObject, isActionContentObject } from '@ali/lowcode-types';
-import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host';
import { OffsetObserver } from '../../designer';
import { Node } from '../../document';
@@ -186,7 +185,7 @@ export class BorderSelecting extends Component<{ host: BuiltinSimulatorHost }> {
@computed get selecting() {
const doc = this.host.document;
- if (doc.suspensed) {
+ if (doc.suspensed || this.host.liveEditing.editing) {
return null;
}
const selection = doc.selection;
@@ -200,8 +199,7 @@ export class BorderSelecting extends Component<{ host: BuiltinSimulatorHost }> {
render() {
const selecting = this.selecting;
if (!selecting || selecting.length < 1) {
- // DIRTY FIX, recore has a bug!
- return ;
+ return null;
}
return (
diff --git a/packages/designer/src/builtin-simulator/bem-tools/borders.less b/packages/designer/src/builtin-simulator/bem-tools/borders.less
index b602193b5..7effde36a 100644
--- a/packages/designer/src/builtin-simulator/bem-tools/borders.less
+++ b/packages/designer/src/builtin-simulator/bem-tools/borders.less
@@ -48,7 +48,7 @@
}
}
- &&-hovering {
+ &&-detecting {
z-index: 1;
border-style: dashed;
background: rgba(0,121,242,.04);
diff --git a/packages/designer/src/builtin-simulator/bem-tools/index.tsx b/packages/designer/src/builtin-simulator/bem-tools/index.tsx
index 99fbc57c9..881ff9396 100644
--- a/packages/designer/src/builtin-simulator/bem-tools/index.tsx
+++ b/packages/designer/src/builtin-simulator/bem-tools/index.tsx
@@ -1,6 +1,6 @@
import { Component } from 'react';
import { observer } from '@ali/lowcode-editor-core';
-import { BorderHovering } from './border-hovering';
+import { BorderDetecting } from './border-detecting';
import { BuiltinSimulatorHost } from '../host';
import { BorderSelecting } from './border-selecting';
import { InsertionView } from './insertion';
@@ -18,7 +18,7 @@ export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
const { scrollX, scrollY, scale } = host.viewport;
return (
-
+
diff --git a/packages/designer/src/builtin-simulator/bem-tools/insertion.tsx b/packages/designer/src/builtin-simulator/bem-tools/insertion.tsx
index f253d843b..90b65ecc8 100644
--- a/packages/designer/src/builtin-simulator/bem-tools/insertion.tsx
+++ b/packages/designer/src/builtin-simulator/bem-tools/insertion.tsx
@@ -106,7 +106,7 @@ function processDetail({ target, detail, document }: DropLocation): InsertionDat
if (!instances) {
return {};
}
- const edge = sim.computeComponentInstanceRect(instances[0], target.componentMeta.rectSelector);
+ const edge = sim.computeComponentInstanceRect(instances[0], target.componentMeta.rootSelector);
return edge ? { edge, insertType: 'cover', coverRect: edge } : {};
}
}
diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts
index 82e779759..6f9a2d9da 100644
--- a/packages/designer/src/builtin-simulator/host.ts
+++ b/packages/designer/src/builtin-simulator/host.ts
@@ -25,6 +25,7 @@ import { parseMetadata } from './utils/parse-metadata';
import { ComponentMetadata } from '@ali/lowcode-types';
import { BuiltinSimulatorRenderer } from './renderer';
import clipboard from '../designer/clipboard';
+import { LiveEditing } from './live-editing/live-editing';
export interface LibraryItem {
package: string;
@@ -224,7 +225,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost {
+ if (this.liveEditing.editing) {
+ return;
+ }
// stop response document focus event
downEvent.stopPropagation();
downEvent.preventDefault();
@@ -250,7 +255,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost {
- // stop response document dblclick event
- e.stopPropagation();
- e.preventDefault();
- // todo: quick editing
- },
- true,
- );
}
private disableHovering?: () => void;
/**
* 设置悬停处理
*/
- setupHovering() {
+ setupDetecting() {
const doc = this.contentDocument!;
- const hovering = this.document.designer.hovering;
+ const detecting = this.document.designer.detecting;
const hover = (e: MouseEvent) => {
- if (!hovering.enable) {
+ if (!detecting.enable) {
return;
}
const nodeInst = this.getNodeInstanceFromElement(e.target as Element);
- hovering.hover(nodeInst?.node || null);
+ detecting.capture(nodeInst?.node || null);
e.stopPropagation();
};
- const leave = () => hovering.leave(this.document);
+ const leave = () => detecting.leave(this.document);
doc.addEventListener('mouseover', hover, true);
doc.addEventListener('mouseleave', leave, false);
@@ -349,13 +342,47 @@ export class BuiltinSimulatorHost implements ISimulatorHost {
- hovering.leave(this.document);
+ detecting.leave(this.document);
doc.removeEventListener('mouseover', hover, true);
doc.removeEventListener('mouseleave', leave, false);
this.disableHovering = undefined;
};
}
+ readonly liveEditing = new LiveEditing();
+ setupLiveEditing() {
+ const doc = this.contentDocument!;
+ // cause edit
+ doc.addEventListener(
+ 'dblclick',
+ (e: MouseEvent) => {
+ // stop response document dblclick event
+ e.stopPropagation();
+ e.preventDefault();
+
+ const targetElement = e.target as HTMLElement;
+ const nodeInst = this.getNodeInstanceFromElement(targetElement);
+ if (!nodeInst) {
+ return;
+ }
+ const node = nodeInst.node || this.document.rootNode;
+
+ const rootElement = this.findDOMNodes(nodeInst.instance, node.componentMeta.rootSelector)?.find(item => item.contains(targetElement)) as HTMLElement;
+ if (!rootElement) {
+ return;
+ }
+
+ this.liveEditing.apply({
+ node,
+ rootElement,
+ event: e,
+ });
+ },
+ true,
+ );
+
+ }
+
/**
* @see ISimulator
*/
@@ -368,7 +395,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost | null {
- return this._renderer?.findDOMNodes(instance) || null;
+ findDOMNodes(instance: ComponentInstance, selector?: string): Array | null {
+ const elements = this._renderer?.findDOMNodes(instance);
+ if (!elements) {
+ return null;
+ }
+
+ if (selector) {
+ const matched = getMatched(elements, selector);
+ if (!matched) {
+ return null;
+ }
+ return [matched];
+ }
+ return elements;
}
/**
@@ -717,7 +749,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost this.getClosestNodeInstance(inst, container.id)?.instance === containerInstance)
: instances[0]
: null;
- const rect = inst ? this.computeComponentInstanceRect(inst, node.componentMeta.rectSelector) : null;
+ const rect = inst ? this.computeComponentInstanceRect(inst, node.componentMeta.rootSelector) : null;
if (!rect) {
continue;
diff --git a/packages/designer/src/builtin-simulator/live-editing/live-editing.ts b/packages/designer/src/builtin-simulator/live-editing/live-editing.ts
new file mode 100644
index 000000000..0ae54b380
--- /dev/null
+++ b/packages/designer/src/builtin-simulator/live-editing/live-editing.ts
@@ -0,0 +1,145 @@
+import { obx } from '@ali/lowcode-editor-core';
+import { Node, Prop } from '../../document';
+
+const EDITOR_KEY = 'data-setter-prop';
+
+function getSetterPropElement(ele: HTMLElement, root: HTMLElement): HTMLElement | null {
+ const box = ele.closest(`[${EDITOR_KEY}]`);
+ if (!box || !root.contains(box)) {
+ return null;
+ }
+ return box as HTMLElement;
+}
+
+function defaultSaveContent(content: string, prop: Prop) {
+ prop.setValue(content);
+}
+
+export class LiveEditing {
+ @obx.ref private _editing: Prop | null = null;
+ apply(target: { node: Node; rootElement: HTMLElement; event: MouseEvent }) {
+ const { node, event, rootElement } = target;
+ const targetElement = event.target as HTMLElement;
+ const liveTextEditing = node.componentMeta.getMetadata().experimental?.liveTextEditing || [];
+
+ const setterPropElement = getSetterPropElement(targetElement, rootElement);
+ const propTarget = setterPropElement?.dataset.setterProp;
+ if (setterPropElement && propTarget) {
+ // 已埋点命中 data-setter-prop="proptarget", 从 liveTextEditing 读取配置(mode|onSaveContent)
+ const config = liveTextEditing.find(config => config.propTarget == propTarget);
+ const prop = node.getProp(propTarget, true)!;
+
+ if (this._editing === prop) {
+ return;
+ }
+
+ // 进入编辑
+ // 1. 设置contentEditable="plaintext|..."
+ // 2. 添加类名
+ // 3. focus & cursor locate
+ // 4. 监听 blur 事件
+ // 5. 设置编辑锁定:disable hover | disable select | disable canvas drag
+
+ const onSaveContent = config?.onSaveContent || this.saveHandlers.find(item => item.condition(prop))?.onSaveContent || defaultSaveContent;
+
+ setterPropElement.setAttribute('contenteditable', config?.mode && config.mode !== 'plaintext' ? 'true' : 'plaintext-only');
+ setterPropElement.classList.add('engine-live-editing');
+ // be sure
+ setterPropElement.focus();
+ setCaret(event);
+
+ this._save = () => {
+ onSaveContent(setterPropElement.innerText, prop);
+ };
+
+ this._dispose = () => {
+ setterPropElement.removeAttribute('contenteditable');
+ setterPropElement.classList.remove('engine-live-editing');
+ };
+
+ setterPropElement.addEventListener('focusout', (e) => {
+ this.saveAndDispose();
+ });
+
+ this._editing = prop;
+
+ } else {
+
+ }
+ // 1) 自动纯文本编辑满足一下情况:
+ // 1. children 内容都是 Leaf 且都是文本(一期)
+ // 2. DOM 节点是单层容器,子集都是文本节点
+ // 2) children 内容都是 Leaf 且都是文本(一期), 且 children 命中 embedTextEditing 配置(必须配置 selector)
+ // 3)
+ // 4) 执行 embedTextEditing selector 规则,或得第一个节点 是否 contains e.target,若匹配,读取配置,若不匹配,parentNode,closeat?
+ /*
+ embedTextEditing: Array<{
+ propTarget: string;
+ selector?: string;
+ // 编辑模式 纯文本|段落编辑|文章编辑(默认纯文本,无跟随工具条)
+ mode?: 'plaintext' | 'paragraph' | 'article';
+ // 从 contentEditable 获取内容并设置到属性
+ onSaveContent?: (content: string, prop: any) => any;
+ }>;
+ */
+ // 进入编辑
+ // 1. 设置contentEditable="plaintext|..."
+ // 2. 添加类名
+ // 3. focus & cursor locate
+ // 4. 监听 blur 事件
+ // 5. 设置编辑锁定:disable hover | disable select | disable canvas drag
+
+ // 非文本编辑
+ // 国际化数据,改变当前
+ // JSExpression, 改变 mock 或 弹出绑定变量
+ }
+
+ get editing() {
+ return this._editing;
+ }
+
+ private _dispose?: () => void;
+ private _save?: () => void;
+ saveAndDispose() {
+ if (this._save) {
+ this._save();
+ this._save = undefined;
+ }
+ this.dispose();
+ }
+
+ dispose() {
+ if (this._dispose) {
+ this._dispose();
+ this._dispose = undefined;
+ }
+ this._editing = null;
+ }
+
+ private saveHandlers: SaveHandler[] = [];
+ setSaveHandler(handler: SaveHandler) {
+ this.saveHandlers.push(handler);
+ }
+}
+
+export interface SaveHandler {
+ condition: (prop: Prop) => boolean;
+ onSaveContent: (content: string, prop: Prop) => void;
+}
+
+function setCaret(event: MouseEvent) {
+ const doc = event.view?.document!;
+ const range = doc.caretRangeFromPoint(event.clientX, event.clientY);
+ if (range) {
+ selectRange(doc, range);
+ setTimeout(() => selectRange(doc, range), 1);
+ }
+}
+
+function selectRange(doc: Document, range: Range) {
+ const selection = doc.getSelection();
+ if (selection) {
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+}
diff --git a/packages/designer/src/component-meta.ts b/packages/designer/src/component-meta.ts
index 1410ea334..d389586fa 100644
--- a/packages/designer/src/component-meta.ts
+++ b/packages/designer/src/component-meta.ts
@@ -81,9 +81,9 @@ export class ComponentMeta {
get descriptor(): string | undefined {
return this._descriptor;
}
- private _rectSelector?: string;
- get rectSelector(): string | undefined {
- return this._rectSelector;
+ private _rootSelector?: string;
+ get rootSelector(): string | undefined {
+ return this._rootSelector;
}
private _transformedMetadata?: TransformedComponentMetadata;
get configure() {
@@ -158,7 +158,7 @@ export class ComponentMeta {
this._isContainer = component.isContainer ? true : false;
this._isModal = component.isModal ? true : false;
this._descriptor = component.descriptor;
- this._rectSelector = component.rectSelector;
+ this._rootSelector = component.rootSelector;
if (component.nestingRule) {
const { parentWhitelist, childWhitelist } = component.nestingRule;
this.parentWhitelist = buildFilter(parentWhitelist);
diff --git a/packages/designer/src/designer/active-tracker.ts b/packages/designer/src/designer/active-tracker.ts
index 5f287dfed..078969b65 100644
--- a/packages/designer/src/designer/active-tracker.ts
+++ b/packages/designer/src/designer/active-tracker.ts
@@ -1,17 +1,38 @@
import { EventEmitter } from 'events';
import { LocationDetail } from './location';
import { Node, isNode } from '../document/node/node';
+import { ComponentInstance } from '../simulator';
+import { obx } from '@ali/lowcode-editor-core';
export interface ActiveTarget {
node: Node;
detail?: LocationDetail;
+ instance?: ComponentInstance;
}
export class ActiveTracker {
private emitter = new EventEmitter();
+ @obx.ref private _target?: ActiveTarget;
+
track(target: ActiveTarget | Node) {
- this.emitter.emit('change', isNode(target) ? { node: target } : target);
+ if (isNode(target)) {
+ target = { node: target };
+ }
+ this._target = target;
+ this.emitter.emit('change', target);
+ }
+
+ get currentNode() {
+ return this._target?.node;
+ }
+
+ get detail() {
+ return this._target?.detail;
+ }
+
+ get intance() {
+ return this._target?.instance;
}
onChange(fn: (target: ActiveTarget) => void): () => void {
diff --git a/packages/designer/src/designer/builtin-hotkey.ts b/packages/designer/src/designer/builtin-hotkey.ts
index 8508f7479..8e01bbfff 100644
--- a/packages/designer/src/designer/builtin-hotkey.ts
+++ b/packages/designer/src/designer/builtin-hotkey.ts
@@ -66,6 +66,7 @@ function getPrevForSelect(prev: any, head?: any, parent?: any): any {
// hotkey binding
hotkey.bind(['backspace', 'del'], (e: KeyboardEvent) => {
+ // TODO: use focus-tracker
const doc = focusing.focusDesigner?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
@@ -101,14 +102,6 @@ hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => {
}
e.preventDefault();
- /*
- const doc = getCurrentDocument();
- if (isFormEvent(e) || !doc || !(focusing.id === 'outline' || focusing.id === 'canvas')) {
- return;
- }
- e.preventDefault();
- */
-
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) return;
@@ -119,7 +112,7 @@ hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => {
clipboard.setData(data);
- const cutMode = action.indexOf('x') > 0;
+ const cutMode = action && action.indexOf('x') > 0;
if (cutMode) {
selected.forEach((node) => {
const parentNode = node.getParent();
@@ -230,7 +223,7 @@ hotkey.bind(['option+left', 'option+right'], (e, action) => {
const parent = firstNode.getParent();
if (!parent) return;
- const isPrev = /(left)$/.test(action);
+ const isPrev = action && /(left)$/.test(action);
const silbing = isPrev ? firstNode.prevSibling : firstNode.nextSibling;
if (silbing) {
diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts
index e91592413..7bee2289b 100644
--- a/packages/designer/src/designer/designer.ts
+++ b/packages/designer/src/designer/designer.ts
@@ -16,7 +16,7 @@ import { INodeSelector, Component } from '../simulator';
import { Scroller, IScrollable } from './scroller';
import { Dragon, isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './dragon';
import { ActiveTracker } from './active-tracker';
-import { Hovering } from './hovering';
+import { Detecting } from './detecting';
import { DropLocation, LocationData, isLocationChildrenDetail } from './location';
import { OffsetObserver, createOffsetObserver } from './offset-observer';
import { focusing } from './focusing';
@@ -44,7 +44,7 @@ export interface DesignerProps {
export class Designer {
readonly dragon = new Dragon(this);
readonly activeTracker = new ActiveTracker();
- readonly hovering = new Hovering();
+ readonly detecting = new Detecting();
readonly project: Project;
readonly editor: IEditor;
@@ -68,7 +68,7 @@ export class Designer {
this.project = new Project(this, props.defaultSchema);
this.dragon.onDragstart((e) => {
- this.hovering.enable = false;
+ this.detecting.enable = false;
const { dragObject } = e;
if (isDragNodeObject(dragObject)) {
if (dragObject.nodes.length === 1) {
@@ -118,7 +118,7 @@ export class Designer {
this.props.onDragend(e, loc);
}
this.postEvent('dragend', e, loc);
- this.hovering.enable = true;
+ this.detecting.enable = true;
});
this.activeTracker.onChange(({ node, detail }) => {
diff --git a/packages/designer/src/designer/hovering.ts b/packages/designer/src/designer/detecting.ts
similarity index 89%
rename from packages/designer/src/designer/hovering.ts
rename to packages/designer/src/designer/detecting.ts
index 9c27801a7..11492f937 100644
--- a/packages/designer/src/designer/hovering.ts
+++ b/packages/designer/src/designer/detecting.ts
@@ -1,7 +1,7 @@
import { obx } from '@ali/lowcode-editor-core';
import { Node, DocumentModel } from '../document';
-export class Hovering {
+export class Detecting {
@obx.ref private _enable = true;
get enable() {
return this._enable;
@@ -19,11 +19,11 @@ export class Hovering {
return this._current;
}
- hover(node: Node | null) {
+ capture(node: Node | null) {
this._current = node;
}
- unhover(node: Node) {
+ release(node: Node) {
if (this._current === node) {
this._current = null;
}
diff --git a/packages/designer/src/designer/focusing.ts b/packages/designer/src/designer/focusing.ts
index 06a1495f3..66816bc03 100644
--- a/packages/designer/src/designer/focusing.ts
+++ b/packages/designer/src/designer/focusing.ts
@@ -1,7 +1,6 @@
import { Designer } from './designer';
-// TODO:
-// 当前激活区域管理
+// TODO: use focus-tracker replace
class Focusing {
focusDesigner?: Designer;
}
diff --git a/packages/designer/src/designer/index.ts b/packages/designer/src/designer/index.ts
index 1de0fb2ee..8bcf028c9 100644
--- a/packages/designer/src/designer/index.ts
+++ b/packages/designer/src/designer/index.ts
@@ -2,7 +2,7 @@ import './builtin-hotkey';
export * from './designer';
export * from './designer-view';
export * from './dragon';
-export * from './hovering';
+export * from './detecting';
export * from './location';
export * from './offset-observer';
export * from './scroller';
diff --git a/packages/designer/src/designer/offset-observer.ts b/packages/designer/src/designer/offset-observer.ts
index a6601373e..4f72691af 100644
--- a/packages/designer/src/designer/offset-observer.ts
+++ b/packages/designer/src/designer/offset-observer.ts
@@ -105,7 +105,7 @@ export class OffsetObserver {
return;
}
- const rect = host.computeComponentInstanceRect(instance!, node.componentMeta.rectSelector);
+ const rect = host.computeComponentInstanceRect(instance!, node.componentMeta.rootSelector);
if (!rect) {
this.hasOffset = false;
diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts
index eb326fd14..63a75421d 100644
--- a/packages/designer/src/document/node/node.ts
+++ b/packages/designer/src/document/node/node.ts
@@ -284,9 +284,9 @@ export class Node {
*/
hover(flag = true) {
if (flag) {
- this.document.designer.hovering.hover(this);
+ this.document.designer.detecting.capture(this);
} else {
- this.document.designer.hovering.unhover(this);
+ this.document.designer.detecting.release(this);
}
}
diff --git a/packages/designer/src/simulator.ts b/packages/designer/src/simulator.ts
index 9fe2af78a..fd3e7dcaf 100644
--- a/packages/designer/src/simulator.ts
+++ b/packages/designer/src/simulator.ts
@@ -134,7 +134,7 @@ export interface ISimulatorHost extends ISensor {
computeComponentInstanceRect(instance: ComponentInstance, selector?: string): DOMRect | null;
- findDOMNodes(instance: ComponentInstance): Array | null;
+ findDOMNodes(instance: ComponentInstance, selector?: string): Array | null;
/**
* 销毁
diff --git a/packages/plugin-outline-pane/src/tree-node.ts b/packages/plugin-outline-pane/src/tree-node.ts
index 47b667c14..893dda619 100644
--- a/packages/plugin-outline-pane/src/tree-node.ts
+++ b/packages/plugin-outline-pane/src/tree-node.ts
@@ -65,8 +65,8 @@ export default class TreeNode {
this._expanded = value;
}
- @computed get hovering() {
- return this.designer.hovering.current === this.node;
+ @computed get detecting() {
+ return this.designer.detecting.current === this.node;
}
@computed get hidden(): boolean {
diff --git a/packages/plugin-outline-pane/src/views/style.less b/packages/plugin-outline-pane/src/views/style.less
index 056061f11..c7c571174 100644
--- a/packages/plugin-outline-pane/src/views/style.less
+++ b/packages/plugin-outline-pane/src/views/style.less
@@ -260,7 +260,7 @@
}
}
- &.hovering > .tree-node-title {
+ &.detecting > .tree-node-title {
background: var(--color-block-background-light);
}
diff --git a/packages/plugin-outline-pane/src/views/tree-node.tsx b/packages/plugin-outline-pane/src/views/tree-node.tsx
index 7688b67c6..95728bdca 100644
--- a/packages/plugin-outline-pane/src/views/tree-node.tsx
+++ b/packages/plugin-outline-pane/src/views/tree-node.tsx
@@ -17,7 +17,7 @@ export default class TreeNodeView extends Component<{ treeNode: TreeNode }> {
// 是否展开
expanded: treeNode.expanded,
// 是否悬停中
- hovering: treeNode.hovering,
+ detecting: treeNode.detecting,
// 是否选中的
selected: treeNode.selected,
// 是否隐藏的
diff --git a/packages/plugin-outline-pane/src/views/tree.tsx b/packages/plugin-outline-pane/src/views/tree.tsx
index 04d98c496..b629d6c44 100644
--- a/packages/plugin-outline-pane/src/views/tree.tsx
+++ b/packages/plugin-outline-pane/src/views/tree.tsx
@@ -25,12 +25,12 @@ export default class TreeView extends Component<{ tree: Tree }> {
const { tree } = this.props;
const doc = tree.document;
- const hovering = doc.designer.hovering;
- if (!hovering.enable) {
+ const detecting = doc.designer.detecting;
+ if (!detecting.enable) {
return;
}
const node = this.getTreeNodeFromEvent(e)?.node;
- hovering.hover(node || null);
+ detecting.capture(node || null);
}
private onClick = (e: ReactMouseEvent) => {
@@ -129,7 +129,7 @@ export default class TreeView extends Component<{ tree: Tree }> {
private onMouseLeave = () => {
const { tree } = this.props;
const doc = tree.document;
- doc.designer.hovering.leave(doc);
+ doc.designer.detecting.leave(doc);
};
render() {
diff --git a/packages/react-simulator-renderer/src/renderer.less b/packages/react-simulator-renderer/src/renderer.less
index b22015e96..e2abf1153 100644
--- a/packages/react-simulator-renderer/src/renderer.less
+++ b/packages/react-simulator-renderer/src/renderer.less
@@ -85,6 +85,13 @@ body.engine-document {
}
}
+.engine-live-editing {
+ cursor: text;
+ outline: none;
+ box-shadow: 0 0 0px 4px rgba(23, 141, 247, 0.2);
+ user-select: text;
+}
+
#app {
height: 100vh;
}
diff --git a/packages/types/src/metadata.ts b/packages/types/src/metadata.ts
index b6fc5f0fc..82382d128 100644
--- a/packages/types/src/metadata.ts
+++ b/packages/types/src/metadata.ts
@@ -29,7 +29,7 @@ export interface ComponentConfigure {
descriptor?: string;
nestingRule?: NestingRule;
- rectSelector?: string;
+ rootSelector?: string;
// copy,move,delete | *
disableBehaviors?: string[] | string;
actions?: ComponentAction[];
@@ -74,6 +74,17 @@ export interface Experimental {
// 请求 hud 显示
// drag 时 计算 并 设置效果
// 更新控制柄位置
+
+ // 纯文本编辑:如果 children 内容是
+ // 文本编辑:配置
+ liveTextEditing?: Array<{
+ propTarget: string;
+ selector?: string;
+ // 编辑模式 纯文本|段落编辑|文章编辑(默认纯文本,无跟随工具条)
+ mode?: 'plaintext' | 'paragraph' | 'article';
+ // 从 contentEditable 获取内容并设置到属性
+ onSaveContent?: (content: string, prop: any) => any;
+ }>;
}
export interface Configure {
diff --git a/packages/vision-preset/src/bundle/upgrade-metadata.ts b/packages/vision-preset/src/bundle/upgrade-metadata.ts
index 45a1662ac..af3646d1f 100644
--- a/packages/vision-preset/src/bundle/upgrade-metadata.ts
+++ b/packages/vision-preset/src/bundle/upgrade-metadata.ts
@@ -583,7 +583,7 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
const component: any = {
isContainer,
- rectSelector,
+ rootSelector: rectSelector,
isModal,
isFloating,
descriptor,