mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-12 17:08:14 +00:00
fix reducers
This commit is contained in:
parent
7552ca8a44
commit
0c81d96bf1
@ -1,6 +1,6 @@
|
||||
import * as EventEmitter from 'events';
|
||||
import { ISimulatorHost, isSimulatorHost } from '../../../../designer/src/simulator';
|
||||
import { Designer } from '../../../../designer/src/designer/designer';
|
||||
import { EventEmitter } from 'events';
|
||||
import { ISimulatorHost, isSimulatorHost } from '../../simulator';
|
||||
import { Designer, Point } from '../../designer';
|
||||
import { setNativeSelection, cursor } from '@ali/lowcode-utils';
|
||||
// import Cursor from './cursor';
|
||||
// import Pages from './pages';
|
||||
@ -66,21 +66,37 @@ export default class DragResizeEngine {
|
||||
*/
|
||||
from(shell: Element, direction: string, boost: (e: MouseEvent) => any) {
|
||||
let node: any;
|
||||
let startEvent: MouseEvent;
|
||||
let startEvent: Point;
|
||||
|
||||
if (!shell) {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
const move = (e: MouseEvent) => {
|
||||
const moveX = e.clientX - startEvent.clientX;
|
||||
const moveY = e.clientY - startEvent.clientY;
|
||||
const x = createResizeEvent(e);
|
||||
const moveX = x.clientX - startEvent.clientX;
|
||||
const moveY = x.clientY - startEvent.clientY;
|
||||
|
||||
this.emitter.emit('resize', e, direction, node, moveX, moveY);
|
||||
};
|
||||
|
||||
const masterSensors = this.getMasterSensors();
|
||||
|
||||
const createResizeEvent = (e: MouseEvent | DragEvent): Point => {
|
||||
const evt: any = {};
|
||||
|
||||
const sourceDocument = e.view?.document;
|
||||
|
||||
if (!sourceDocument || sourceDocument === document) {
|
||||
return e;
|
||||
}
|
||||
const srcSim = masterSensors.find((sim) => sim.contentDocument === sourceDocument);
|
||||
if (srcSim) {
|
||||
return srcSim.viewport.toGlobalPoint(e);
|
||||
}
|
||||
return e;
|
||||
};
|
||||
|
||||
const over = (e: MouseEvent) => {
|
||||
const handleEvents = makeEventsHandler(e, masterSensors);
|
||||
handleEvents((doc) => {
|
||||
@ -96,7 +112,7 @@ export default class DragResizeEngine {
|
||||
|
||||
const mousedown = (e: MouseEvent) => {
|
||||
node = boost(e);
|
||||
startEvent = e;
|
||||
startEvent = createResizeEvent(e);
|
||||
const handleEvents = makeEventsHandler(e, masterSensors);
|
||||
handleEvents((doc) => {
|
||||
doc.addEventListener('mousemove', move, true);
|
||||
|
||||
@ -247,6 +247,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
downEvent.stopPropagation();
|
||||
downEvent.preventDefault();
|
||||
|
||||
// FIXME: dirty fix remove label-for fro liveEditing
|
||||
(downEvent.target as HTMLElement).removeAttribute('for');
|
||||
|
||||
const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element);
|
||||
const node = nodeInst?.node || this.document.rootNode;
|
||||
const isMulti = downEvent.metaKey || downEvent.ctrlKey;
|
||||
|
||||
@ -98,7 +98,6 @@ export class LiveEditing {
|
||||
const onSaveContent = matched?.onSaveContent || saveHandlers.find(item => item.condition(prop))?.onSaveContent || defaultSaveContent;
|
||||
|
||||
setterPropElement.setAttribute('contenteditable', matched?.mode && matched.mode !== 'plaintext' ? 'true' : 'plaintext-only');
|
||||
setterPropElement.removeAttribute('for');
|
||||
setterPropElement.classList.add('engine-live-editing');
|
||||
// be sure
|
||||
setterPropElement.focus();
|
||||
@ -110,6 +109,13 @@ export class LiveEditing {
|
||||
|
||||
const keydown = (e: KeyboardEvent) => {
|
||||
console.info(e.code);
|
||||
switch (e.code) {
|
||||
case 'Enter':
|
||||
// TODO: check is richtext?
|
||||
case 'Escape':
|
||||
case 'Tab':
|
||||
setterPropElement?.blur();
|
||||
}
|
||||
// esc
|
||||
// enter
|
||||
// tab
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
FieldConfig,
|
||||
} from '@ali/lowcode-types';
|
||||
import { computed } from '@ali/lowcode-editor-core';
|
||||
import { Node, ParentalNode } from './document';
|
||||
import { Node, ParentalNode, TransformStage } from './document';
|
||||
import { Designer } from './designer';
|
||||
import { intlNode } from './locale';
|
||||
import { IconContainer } from './icons/container';
|
||||
@ -379,8 +379,8 @@ const builtinComponentActions: ComponentAction[] = [
|
||||
title: intlNode('copy'),
|
||||
action(node: Node) {
|
||||
// node.remove();
|
||||
const { document: doc, parent, schema, index } = node;
|
||||
parent && doc.insertNode(parent, schema, index);
|
||||
const { document: doc, parent, index } = node;
|
||||
parent && doc.insertNode(parent, node, index, true);
|
||||
},
|
||||
},
|
||||
important: true,
|
||||
|
||||
@ -106,7 +106,7 @@ hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => {
|
||||
if (!selected || selected.length < 1) return;
|
||||
|
||||
const componentsMap = {};
|
||||
const componentsTree = selected.map((item) => item.export(TransformStage.Save));
|
||||
const componentsTree = selected.map((item) => item.export(TransformStage.Clone));
|
||||
|
||||
// FIXME: clear node.id
|
||||
|
||||
|
||||
@ -159,20 +159,15 @@ function makeEventsHandler(
|
||||
const topDoc = window.top.document;
|
||||
const sourceDoc = boostEvent.view?.document || topDoc;
|
||||
// TODO: optimize this logic, reduce listener
|
||||
// const boostPrevented = boostEvent.defaultPrevented;
|
||||
const docs = new Set<Document>();
|
||||
// if (boostPrevented || isDragEvent(boostEvent)) {
|
||||
docs.add(topDoc);
|
||||
// }
|
||||
docs.add(sourceDoc);
|
||||
// if (sourceDoc !== topDoc || isDragEvent(boostEvent)) {
|
||||
sensors.forEach((sim) => {
|
||||
const sdoc = sim.contentDocument;
|
||||
if (sdoc) {
|
||||
docs.add(sdoc);
|
||||
}
|
||||
});
|
||||
// }
|
||||
|
||||
return (handle: (sdoc: Document) => void) => {
|
||||
docs.forEach((doc) => handle(doc));
|
||||
|
||||
@ -88,7 +88,7 @@ export class DocumentModel {
|
||||
);
|
||||
|
||||
this.history = new History(
|
||||
() => this.schema,
|
||||
() => this.export(TransformStage.Serilize),
|
||||
(schema) => this.import(schema as RootSchema, true),
|
||||
);
|
||||
this.setupListenActiveNodes();
|
||||
|
||||
@ -481,15 +481,13 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
* 导出 schema
|
||||
*/
|
||||
export(stage: TransformStage = TransformStage.Save): Schema {
|
||||
// run transducers
|
||||
// run
|
||||
const baseSchema: any = {
|
||||
componentName: this.componentName,
|
||||
};
|
||||
|
||||
// if (stage !== TransformStage.Save) {
|
||||
if (stage !== TransformStage.Clone) {
|
||||
baseSchema.id = this.id;
|
||||
// }
|
||||
}
|
||||
|
||||
if (this.isLeaf()) {
|
||||
baseSchema.children = this.props.get('children')?.export(stage);
|
||||
@ -827,7 +825,7 @@ export function comparePosition(node1: Node, node2: Node): PositionNO {
|
||||
export function insertChild(container: ParentalNode, thing: Node | NodeData, at?: number | null, copy?: boolean): Node {
|
||||
let node: Node;
|
||||
if (isNode(thing) && (copy || thing.isSlot())) {
|
||||
thing = thing.export(TransformStage.Save);
|
||||
thing = thing.export(TransformStage.Clone);
|
||||
}
|
||||
if (isNode(thing)) {
|
||||
node = thing;
|
||||
|
||||
@ -20,6 +20,27 @@ export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | '
|
||||
export class Prop implements IPropParent {
|
||||
readonly isProp = true;
|
||||
|
||||
/**
|
||||
* @see SettingTarget
|
||||
*/
|
||||
getPropValue(propName: string | number): any {
|
||||
return this.get(propName)!.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SettingTarget
|
||||
*/
|
||||
setPropValue(propName: string | number, value: any): void {
|
||||
this.set(propName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SettingTarget
|
||||
*/
|
||||
clearPropValue(propName: string | number): void {
|
||||
this.get(propName, false)?.unset();
|
||||
}
|
||||
|
||||
readonly id = uniqueId('prop$');
|
||||
|
||||
@obx.ref private _type: ValueTypes = 'unset';
|
||||
@ -341,7 +362,7 @@ export class Prop implements IPropParent {
|
||||
* 获取某个属性
|
||||
* @param stash 如果不存在,临时获取一个待写入
|
||||
*/
|
||||
get(path: string | number, stash = true): Prop | null {
|
||||
get(path: string | number, stash: boolean = true): Prop | null {
|
||||
const type = this._type;
|
||||
if (type !== 'map' && type !== 'list' && type !== 'unset' && !stash) {
|
||||
return null;
|
||||
@ -588,6 +609,10 @@ export class Prop implements IPropParent {
|
||||
return isMap ? fn(item, item.key) : fn(item, index);
|
||||
});
|
||||
}
|
||||
|
||||
getProps() {
|
||||
return this.parent;
|
||||
}
|
||||
}
|
||||
|
||||
export function isProp(obj: any): obj is Prop {
|
||||
|
||||
@ -2,5 +2,6 @@ export enum TransformStage {
|
||||
Render = 1,
|
||||
Serilize = 2,
|
||||
Save = 3,
|
||||
Init = 4,
|
||||
Clone = 4,
|
||||
Init = 5,
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Tab, Breadcrumb } from '@alifd/next';
|
||||
import { Title, observer, Editor } from '@ali/lowcode-editor-core';
|
||||
import { Title, observer, Editor, obx } from '@ali/lowcode-editor-core';
|
||||
import { Node, isSettingField, SettingField } from '@ali/lowcode-designer';
|
||||
import { SettingsMain } from './main';
|
||||
import { SettingsPane } from './settings-pane';
|
||||
@ -10,6 +10,8 @@ import { createIcon } from '@ali/lowcode-utils';
|
||||
export class SettingsPrimaryPane extends Component<{ editor: Editor }> {
|
||||
private main = new SettingsMain(this.props.editor);
|
||||
|
||||
@obx.ref private _activeKey?: any;
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
@ -26,7 +28,7 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor }> {
|
||||
if (settings.isMultiple) {
|
||||
return (
|
||||
<div className="lc-settings-navigator">
|
||||
{createIcon(settings.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})}
|
||||
{createIcon(settings.componentMeta?.icon, { className: 'lc-settings-navigator-icon' })}
|
||||
<Title title={settings.componentMeta!.title} />
|
||||
<span>x {settings.nodes.length}</span>
|
||||
</div>
|
||||
@ -45,13 +47,17 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor }> {
|
||||
onMouseOut: hoverNode.bind(null, node, false),
|
||||
onClick: selectNode.bind(null, node),
|
||||
};
|
||||
items.unshift(<Breadcrumb.Item {...props} key={node.id}><Title title={node.title} /></Breadcrumb.Item>);
|
||||
items.unshift(
|
||||
<Breadcrumb.Item {...props} key={node.id}>
|
||||
<Title title={node.title} />
|
||||
</Breadcrumb.Item>,
|
||||
);
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lc-settings-navigator">
|
||||
{createIcon(this.main.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})}
|
||||
{createIcon(this.main.componentMeta?.icon, { className: 'lc-settings-navigator-icon' })}
|
||||
<Breadcrumb className="lc-settings-node-breadcrumb">{items}</Breadcrumb>
|
||||
</div>
|
||||
);
|
||||
@ -82,7 +88,7 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor }> {
|
||||
}
|
||||
|
||||
const { items } = settings;
|
||||
if (items.length > 5 || items.some(item => !isSettingField(item) || !item.isGroup)) {
|
||||
if (items.length > 5 || items.some((item) => !isSettingField(item) || !item.isGroup)) {
|
||||
return (
|
||||
<div className="lc-settings-main">
|
||||
{this.renderBreadcrumb()}
|
||||
@ -93,21 +99,33 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor }> {
|
||||
);
|
||||
}
|
||||
|
||||
let matched = false;
|
||||
const tabs = (items as SettingField[]).map((field) => {
|
||||
if (this._activeKey === field.name) {
|
||||
matched = true;
|
||||
}
|
||||
return (
|
||||
<Tab.Item className="lc-settings-tab-item" title={<Title title={field.title} />} key={field.name}>
|
||||
<SettingsPane target={field} key={field.id} />
|
||||
</Tab.Item>
|
||||
);
|
||||
});
|
||||
const activeKey = matched ? this._activeKey : (items[0] as SettingField).name;
|
||||
|
||||
return (
|
||||
<div className="lc-settings-main">
|
||||
<Tab
|
||||
key={settings.id}
|
||||
activeKey={activeKey}
|
||||
onChange={(tabKey) => {
|
||||
this._activeKey = tabKey;
|
||||
}}
|
||||
navClassName="lc-settings-tabs"
|
||||
animation={false}
|
||||
excessMode="dropdown"
|
||||
contentClassName="lc-settings-tabs-content"
|
||||
extra={this.renderBreadcrumb()}
|
||||
>
|
||||
{(items as SettingField[]).map(field => (
|
||||
<Tab.Item className="lc-settings-tab-item" title={<Title title={field.title} />} key={field.name}>
|
||||
<SettingsPane target={field} key={field.id} />
|
||||
</Tab.Item>
|
||||
))}
|
||||
{tabs}
|
||||
</Tab>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -52,7 +52,7 @@ export default class TreeView extends Component<{ tree: Tree }> {
|
||||
const doc = node.document;
|
||||
const selection = doc.selection;
|
||||
const id = node.id;
|
||||
const isMulti = e.metaKey || e.ctrlKey;
|
||||
const isMulti = e.metaKey || e.ctrlKey || e.shiftKey;
|
||||
designer.activeTracker.track(node);
|
||||
if (isMulti && !isRootNode(node) && selection.has(id)) {
|
||||
if (!isFormEvent(e.nativeEvent)) {
|
||||
@ -96,7 +96,8 @@ export default class TreeView extends Component<{ tree: Tree }> {
|
||||
const doc = node.document;
|
||||
const selection = doc.selection;
|
||||
|
||||
const isMulti = e.metaKey || e.ctrlKey;
|
||||
// TODO: shift selection
|
||||
const isMulti = e.metaKey || e.ctrlKey || e.shiftKey;
|
||||
const isLeftButton = e.button === 0;
|
||||
|
||||
if (isLeftButton && !isRootNode(node)) {
|
||||
|
||||
@ -83,6 +83,15 @@ body.engine-document {
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.next-input-group,
|
||||
.next-checkbox-group,.next-date-picker,.next-input,.next-month-picker,
|
||||
.next-number-picker,.next-radio-group,.next-range,.next-range-picker,
|
||||
.next-rating,.next-select,.next-switch,.next-time-picker,.next-upload,
|
||||
.next-year-picker,
|
||||
.next-breadcrumb-item,.next-calendar-header,.next-calendar-table {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.engine-live-editing {
|
||||
|
||||
@ -302,6 +302,7 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec
|
||||
const setterInitial = getInitialFromSetter(setter);
|
||||
|
||||
collector.addInitial({
|
||||
// FIXME! name should be "xxx.xxx"
|
||||
name: slotName || name,
|
||||
initial: (field: Field, currentValue: any) => {
|
||||
// FIXME! read from prototype.defaultProps
|
||||
@ -323,6 +324,7 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec
|
||||
|
||||
if (ignore != null || disabled != null) {
|
||||
collector.addFilter({
|
||||
// FIXME! name should be "xxx.xxx"
|
||||
name: slotName || name,
|
||||
filter: (field: Field, currentValue: any) => {
|
||||
let disabledValue: boolean;
|
||||
@ -396,7 +398,7 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec
|
||||
const initials: InitialItem[] = [];
|
||||
const filters: FilterItem[] = [];
|
||||
const objItems = items
|
||||
? upgradeConfigure(items,
|
||||
? upgradeConfigure(items,
|
||||
{
|
||||
addInitial: (item) => {
|
||||
initials.push(item);
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { isJSBlock, isJSExpression, isJSSlot } from '@ali/lowcode-types';
|
||||
import { isPlainObject } from '@ali/lowcode-utils';
|
||||
import { isJSBlock, isJSExpression, isJSSlot, isI18nData } from '@ali/lowcode-types';
|
||||
import { isPlainObject, hasOwnProperty } from '@ali/lowcode-utils';
|
||||
import { globalContext, Editor } from '@ali/lowcode-editor-core';
|
||||
import { Designer, LiveEditing, TransformStage, addBuiltinComponentAction, Node } from '@ali/lowcode-designer';
|
||||
import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
|
||||
import { toCss } from '@ali/vu-css-style';
|
||||
import logger from '@ali/vu-logger';
|
||||
|
||||
import DesignerPlugin from '@ali/lowcode-plugin-designer';
|
||||
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
|
||||
@ -31,9 +32,16 @@ designer.addPropsReducer((props, node) => {
|
||||
const newProps: any = {};
|
||||
initials.forEach((item) => {
|
||||
// FIXME! this implements SettingTarget
|
||||
const v = item.initial(node as any, props[item.name]);
|
||||
if (v !== undefined) {
|
||||
newProps[item.name] = v;
|
||||
try {
|
||||
// FIXME! item.name could be 'xxx.xxx'
|
||||
const v = item.initial(node as any, props[item.name]);
|
||||
if (v !== undefined) {
|
||||
newProps[item.name] = v;
|
||||
}
|
||||
} catch (e) {
|
||||
if (hasOwnProperty(props, item.name)) {
|
||||
newProps[item.name] = props[item.name];
|
||||
}
|
||||
}
|
||||
});
|
||||
return newProps;
|
||||
@ -49,9 +57,17 @@ function filterReducer(props: any, node: Node): any {
|
||||
if (filters && filters.length) {
|
||||
const newProps = { ...props };
|
||||
filters.forEach((item) => {
|
||||
const v = item.filter(node as any, props[item.name]);
|
||||
if (!v) {
|
||||
delete newProps[item.name];
|
||||
// FIXME! item.name could be 'xxx.xxx'
|
||||
if (!hasOwnProperty(newProps, item.name)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (item.filter(node.getProp(item.name) as any, props[item.name]) === false) {
|
||||
delete newProps[item.name];
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
logger.trace(e);
|
||||
}
|
||||
});
|
||||
return newProps;
|
||||
@ -138,6 +154,7 @@ designer.addPropsReducer(stylePropsReducer, TransformStage.Render);
|
||||
|
||||
// FIXME: 表达式使用 mock 值,未来live 模式直接使用原始值
|
||||
function expressionReducer(obj?: any): any {
|
||||
// TODO: merge with i18nReducer for optimize
|
||||
if (!obj) {
|
||||
return obj;
|
||||
}
|
||||
@ -148,7 +165,7 @@ function expressionReducer(obj?: any): any {
|
||||
if (isJSExpression(obj)) {
|
||||
return obj.mock;
|
||||
}
|
||||
if (isJSSlot(obj)) {
|
||||
if (isJSSlot(obj) || isI18nData(obj)) {
|
||||
return obj;
|
||||
}
|
||||
const out: any = {};
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import Env from './env';
|
||||
import { isJSSlot, isI18nData, isJSExpression } from '@ali/lowcode-types';
|
||||
import { isPlainObject } from '@ali/lowcode-utils';
|
||||
const I18nUtil = require('@ali/ve-i18n-util');
|
||||
|
||||
interface I18nObject {
|
||||
@ -15,8 +17,8 @@ export function i18nReducer(obj?: any): any {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => i18nReducer(item));
|
||||
}
|
||||
if (typeof obj === 'object') {
|
||||
if (obj.type === 'i18n') {
|
||||
if (isPlainObject(obj)) {
|
||||
if (isI18nData(obj)) {
|
||||
// FIXME! use editor.get
|
||||
let locale = Env.getLocale();
|
||||
if (obj.key) {
|
||||
@ -28,6 +30,9 @@ export function i18nReducer(obj?: any): any {
|
||||
}
|
||||
return obj[obj.use || locale] || obj.zh_CN;
|
||||
}
|
||||
if (isJSSlot(obj) || isJSExpression(obj)) {
|
||||
return obj;
|
||||
}
|
||||
const out: I18nObject = {};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
out[key] = i18nReducer(obj[key]);
|
||||
|
||||
@ -161,11 +161,10 @@ export {
|
||||
Symbols,
|
||||
};
|
||||
|
||||
const version = '6.0.0(LowcodeEngine 0.9.0-beta)';
|
||||
|
||||
/*
|
||||
console.log(
|
||||
`%cLowcodeEngine %cv${VERSION}`,
|
||||
`%cVisionEngine %cv${version}`,
|
||||
"color:#000;font-weight:bold;",
|
||||
"color:green;font-weight:bold;"
|
||||
);
|
||||
*/
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
class Project {
|
||||
export class Project {
|
||||
private schema: any;
|
||||
|
||||
constructor() {
|
||||
|
||||
@ -53,8 +53,7 @@ export function liveEditingRule(target: EditingTarget) {
|
||||
|
||||
const innerText = targetElement.innerText;
|
||||
const propTarget = ['title', 'label', 'text', 'content'].find(prop => {
|
||||
// TODO: enhance compare text logic
|
||||
return getText(node, prop) === innerText;
|
||||
return equalText(getText(node, prop), innerText);
|
||||
});
|
||||
|
||||
if (propTarget) {
|
||||
@ -66,6 +65,14 @@ export function liveEditingRule(target: EditingTarget) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function equalText(v: any, innerText: string) {
|
||||
// TODO: enhance compare text logic
|
||||
if (typeof v !== 'string') {
|
||||
return false;
|
||||
}
|
||||
return v.trim() === innerText
|
||||
}
|
||||
|
||||
// TODO:
|
||||
export function liveEditingSaveHander() {
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user