fix reducers

This commit is contained in:
kangwei 2020-05-16 20:27:13 +08:00
parent 7552ca8a44
commit 0c81d96bf1
19 changed files with 158 additions and 56 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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));

View File

@ -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();

View File

@ -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;

View File

@ -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 {

View File

@ -2,5 +2,6 @@ export enum TransformStage {
Render = 1,
Serilize = 2,
Save = 3,
Init = 4,
Clone = 4,
Init = 5,
}

View File

@ -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>
);

View File

@ -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)) {

View File

@ -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 {

View File

@ -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);

View File

@ -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 = {};

View File

@ -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]);

View File

@ -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;"
);
*/

View File

@ -1,4 +1,4 @@
class Project {
export class Project {
private schema: any;
constructor() {

View File

@ -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() {