feat: double outline & ZH_EN support

This commit is contained in:
kangwei 2020-03-30 04:11:05 +08:00
parent 2c285f8fa1
commit b379bd7c0c
24 changed files with 284 additions and 17 deletions

View File

@ -10,7 +10,7 @@
- 使用 `camelCase` 为属性或本地变量命名 - 使用 `camelCase` 为属性或本地变量命名
- 不要为私有属性名添加 `_` 前缀 - 不要为私有属性名添加 `_` 前缀
- 尽可能使用完整的单词拼写命名 - 尽可能使用完整的单词拼写命名
- 文件夹命名统一使用小写 - 文件夹/文件命名统一使用小写 `get-custom-data.ts`
### 组件 ### 组件

View File

@ -15,6 +15,7 @@
"@ali/lowcode-plugin-settings-pane": "^0.8.0", "@ali/lowcode-plugin-settings-pane": "^0.8.0",
"@ali/lowcode-plugin-outline-pane": "^0.8.0", "@ali/lowcode-plugin-outline-pane": "^0.8.0",
"@ali/lowcode-plugin-undo-redo": "^0.8.0", "@ali/lowcode-plugin-undo-redo": "^0.8.0",
"@ali/lowcode-plugin-zh-en": "^0.8.0",
"@ali/lowcode-plugin-sample-logo": "^0.8.0", "@ali/lowcode-plugin-sample-logo": "^0.8.0",
"@ali/lowcode-plugin-sample-preview": "^0.8.0", "@ali/lowcode-plugin-sample-preview": "^0.8.0",
"@alife/theme-lowcode-dark": "^0.1.0", "@alife/theme-lowcode-dark": "^0.1.0",

View File

@ -1,4 +1,5 @@
import undoRedo from '@ali/lowcode-plugin-undo-redo'; import undoRedo from '@ali/lowcode-plugin-undo-redo';
import zhEn from '@ali/lowcode-plugin-zh-en';
import logo from '@ali/lowcode-plugin-sample-logo'; import logo from '@ali/lowcode-plugin-sample-logo';
import SamplePreview from '@ali/lowcode-plugin-sample-preview'; import SamplePreview from '@ali/lowcode-plugin-sample-preview';
import Designer from '@ali/lowcode-plugin-designer'; import Designer from '@ali/lowcode-plugin-designer';
@ -8,11 +9,12 @@ import OutlinePane from '@ali/lowcode-plugin-outline-pane';
import { PluginFactory } from '@ali/lowcode-editor-core'; import { PluginFactory } from '@ali/lowcode-editor-core';
export default { export default {
logo: PluginFactory(logo),
samplePreview: PluginFactory(SamplePreview),
undoRedo: PluginFactory(undoRedo), undoRedo: PluginFactory(undoRedo),
zhEn: PluginFactory(zhEn),
designer: PluginFactory(Designer), designer: PluginFactory(Designer),
componentsPane: PluginFactory(componentsPane), componentsPane: PluginFactory(componentsPane),
settingsPane: PluginFactory(SettingsPane), settingsPane: PluginFactory(SettingsPane),
outlinePane: PluginFactory(OutlinePane), outlinePane: PluginFactory(OutlinePane),
logo: PluginFactory(logo),
samplePreview: PluginFactory(SamplePreview),
}; };

View File

@ -91,6 +91,18 @@ export default {
version: '^1.0.0' version: '^1.0.0'
}, },
pluginProps: {} pluginProps: {}
},
{
pluginKey: 'zhEn',
type: 'Custom',
props: {
align: 'bottom',
},
config: {
package: '@ali/lowcode-plugin-zh-en',
version: '^1.0.0'
},
pluginProps: {}
} }
], ],
rightArea: [ rightArea: [

View File

@ -12,6 +12,7 @@ export default class EmbedTip extends Component<TipConfig> {
render() { render() {
saveTips(this.id, this.props); saveTips(this.id, this.props);
console.info(this.props);
return <meta data-role="tip" data-tip-id={this.id} />; return <meta data-role="tip" data-tip-id={this.id} />;
} }
} }

View File

@ -16,7 +16,7 @@ function findTip(target: HTMLElement | null): TipOptions | null {
if (target.dataset && target.dataset.tip) { if (target.dataset && target.dataset.tip) {
return { return {
children: target.dataset.tip, children: target.dataset.tip,
direction: target.dataset.direction || target.dataset.dir, direction: (target.dataset.direction || target.dataset.dir) as any,
theme: target.dataset.theme, theme: target.dataset.theme,
target, target,
}; };

View File

@ -5,7 +5,7 @@ export interface TipConfig {
className?: string; className?: string;
children?: I18nData | ReactNode; children?: I18nData | ReactNode;
theme?: string; theme?: string;
direction?: string; // 'n|s|w|e|top|bottom|left|right'; direction?: 'top' | 'bottom' | 'left' | 'right';
} }
export type TipContent = string | I18nData | ReactElement | TipConfig; export type TipContent = string | I18nData | ReactElement | TipConfig;

View File

@ -11,4 +11,6 @@ export default {
}; };
*/ */
export { getTreeMaster } from './main';
export default Pane; export default Pane;

View File

@ -21,12 +21,15 @@ import { Tree } from './tree';
import TreeNode from './tree-node'; import TreeNode from './tree-node';
import { IndentTrack } from './helper/indent-track'; import { IndentTrack } from './helper/indent-track';
import DwellTimer from './helper/dwell-timer'; import DwellTimer from './helper/dwell-timer';
import { EventEmitter } from 'events';
export interface IScrollBoard { export interface IScrollBoard {
scrollToNode(treeNode: TreeNode, detail?: any): void; scrollToNode(treeNode: TreeNode, detail?: any): void;
} }
class TreeMaster { class TreeMaster {
private emitter = new EventEmitter();
private currentFixed?: OutlineMain;
constructor(readonly designer: Designer) { constructor(readonly designer: Designer) {
designer.dragon.onDragstart((e) => { designer.dragon.onDragstart((e) => {
const tree = this.currentTree; const tree = this.currentTree;
@ -35,6 +38,9 @@ class TreeMaster {
tree.getTreeNode(node).setExpanded(false); tree.getTreeNode(node).setExpanded(false);
}); });
} }
if (!this.currentFixed) {
this.emitter.emit('enable-builtin');
}
}); });
designer.activeTracker.onChange(({ node, detail }) => { designer.activeTracker.onChange(({ node, detail }) => {
const tree = this.currentTree; const tree = this.currentTree;
@ -55,6 +61,23 @@ class TreeMaster {
}); });
} }
setFixed(entry: OutlineMain) {
this.currentFixed = entry;
}
unFixed(entry: OutlineMain) {
if (entry === this.currentFixed) {
this.currentFixed = undefined;
}
}
onceEnableBuiltin(fn: () => void): () => void {
this.emitter.once('enable-builtin', fn);
return () => {
this.emitter.removeListener('enable-builtin', fn);
}
}
private boards = new Set<IScrollBoard>(); private boards = new Set<IScrollBoard>();
addBoard(board: IScrollBoard) { addBoard(board: IScrollBoard) {
this.boards.add(board); this.boards.add(board);
@ -63,12 +86,16 @@ class TreeMaster {
this.boards.delete(board); this.boards.delete(board);
} }
purge() {
this.emitter.removeAllListeners();
// todo others purge
}
private treeMap = new Map<string, Tree>(); private treeMap = new Map<string, Tree>();
@computed get currentTree(): Tree | null { @computed get currentTree(): Tree | null {
const doc = this.designer?.currentDocument; const doc = this.designer?.currentDocument;
if (doc) { if (doc) {
const id = doc.id; const id = doc.id;
console.info(id);
if (this.treeMap.has(id)) { if (this.treeMap.has(id)) {
return this.treeMap.get(id)!; return this.treeMap.get(id)!;
} }
@ -82,7 +109,7 @@ class TreeMaster {
} }
const mastersMap = new Map<Designer, TreeMaster>(); const mastersMap = new Map<Designer, TreeMaster>();
function getTreeMaster(designer: Designer): TreeMaster { export function getTreeMaster(designer: Designer): TreeMaster {
let master = mastersMap.get(designer); let master = mastersMap.get(designer);
if (!master) { if (!master) {
master = new TreeMaster(designer); master = new TreeMaster(designer);
@ -102,13 +129,45 @@ export class OutlineMain implements ISensor, IScrollBoard, IScrollable {
} }
readonly id = uniqueId('outline'); readonly id = uniqueId('outline');
constructor(readonly editor: any) { private fixed = false;
if (editor.designer) { constructor(readonly editor: any, at?: string) {
this.setupDesigner(editor.designer); let inited = false;
const setup = () => {
if (inited) {
return false;
}
inited = true;
if (editor.designer) {
this.setupDesigner(editor.designer);
} else {
editor.once('designer.mount', (designer: Designer) => {
this.setupDesigner(designer);
});
}
};
if (at === '__IN_SETTINGS__') {
setup();
} else { } else {
editor.once('designer.mount', (designer: Designer) => { editor.on('leftPanel.show', (key: string) => {
this.setupDesigner(designer); if (key === at) {
setup();
if (this.master) {
this.master.setFixed(this);
} else {
this.fixed = true;
}
document.documentElement.classList.add('lowcode-has-fixed-tree');
} else {
document.documentElement.classList.remove('lowcode-has-fixed-tree');
if (this.master) {
this.master.unFixed(this);
} else {
this.fixed = false;
}
}
}); });
// editor.once('outlinePane.visible', setup);
} }
} }
@ -570,6 +629,9 @@ export class OutlineMain implements ISensor, IScrollBoard, IScrollable {
this._designer = designer; this._designer = designer;
this._master = getTreeMaster(designer); this._master = getTreeMaster(designer);
this._master.addBoard(this); this._master.addBoard(this);
if (this.fixed) {
this._master.setFixed(this);
}
designer.dragon.addSensor(this); designer.dragon.addSensor(this);
this.scroller = designer.createScroller(this); this.scroller = designer.createScroller(this);
} }

View File

@ -6,8 +6,11 @@ import TreeView from './tree';
import './style.less'; import './style.less';
@observer @observer
export default class OutlinePane extends Component<{ editor: any }> { export default class OutlinePane extends Component<{ config?: any; editor: any; inSettings?: boolean }> {
private main = new OutlineMain(this.props.editor); private main = new OutlineMain(
this.props.editor,
this.props.config ? this.props.config.pluginKey : this.props.inSettings ? '__IN_SETTINGS__' : null,
);
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false;
@ -20,8 +23,6 @@ export default class OutlinePane extends Component<{ editor: any }> {
render() { render() {
const tree = this.main.currentTree; const tree = this.main.currentTree;
console.info('tree', tree);
if (!tree) { if (!tree) {
return ( return (
<div className="lc-outline-pane"> <div className="lc-outline-pane">

View File

@ -1,7 +1,8 @@
import React, { Component } from 'react'; import React, { Component, PureComponent } from 'react';
import { Tab, Breadcrumb } from '@alifd/next'; import { Tab, Breadcrumb } from '@alifd/next';
import { Title, createIcon } from '@ali/lowcode-globals'; import { Title, createIcon } from '@ali/lowcode-globals';
import { Node } from '@ali/lowcode-designer'; import { Node } from '@ali/lowcode-designer';
import OutlinePane, { getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
import { SettingsMain, SettingField, isSettingField } from './main'; import { SettingsMain, SettingField, isSettingField } from './main';
import SettingsPane, { createSettingFieldView } from './settings-pane'; import SettingsPane, { createSettingFieldView } from './settings-pane';
import './transducers/register'; import './transducers/register';
@ -67,6 +68,7 @@ export default class SettingsMainView extends Component {
// 未选中节点,提示选中 或者 显示根节点设置 // 未选中节点,提示选中 或者 显示根节点设置
return ( return (
<div className="lc-settings-main"> <div className="lc-settings-main">
<OutlinePaneEntry main={this.main} />
<div className="lc-settings-notice"> <div className="lc-settings-notice">
<p></p> <p></p>
</div> </div>
@ -78,6 +80,7 @@ export default class SettingsMainView extends Component {
// todo: future support 获取设置项交集编辑 // todo: future support 获取设置项交集编辑
return ( return (
<div className="lc-settings-main"> <div className="lc-settings-main">
<OutlinePaneEntry main={this.main} />
<div className="lc-settings-notice"> <div className="lc-settings-notice">
<p></p> <p></p>
</div> </div>
@ -89,6 +92,7 @@ export default class SettingsMainView extends Component {
if (items.length > 5 || items.some(item => !isSettingField(item) || !item.isGroup)) { if (items.length > 5 || items.some(item => !isSettingField(item) || !item.isGroup)) {
return ( return (
<div className="lc-settings-main"> <div className="lc-settings-main">
<OutlinePaneEntry main={this.main} />
{this.renderBreadcrumb()} {this.renderBreadcrumb()}
<div className="lc-settings-body"> <div className="lc-settings-body">
<SettingsPane target={this.main} /> <SettingsPane target={this.main} />
@ -99,6 +103,7 @@ export default class SettingsMainView extends Component {
return ( return (
<div className="lc-settings-main"> <div className="lc-settings-main">
<OutlinePaneEntry main={this.main} />
<Tab <Tab
navClassName="lc-settings-tabs" navClassName="lc-settings-tabs"
animation={false} animation={false}
@ -117,6 +122,26 @@ export default class SettingsMainView extends Component {
} }
} }
class OutlinePaneEntry extends PureComponent<{ main: SettingsMain }> {
state = {
outlineInited: false,
};
private dispose = this.props.main.onceOutlineVisible(() => {
this.setState({
outlineInited: true,
});
});
componentWillUnmount() {
this.dispose();
}
render() {
if (!this.state.outlineInited) {
return null;
}
return <OutlinePane editor={this.props.main.editor} inSettings />;
}
}
function hoverNode(node: Node, flag: boolean) { function hoverNode(node: Node, flag: boolean) {
node.hover(flag); node.hover(flag);
} }

View File

@ -2,6 +2,7 @@ import { EventEmitter } from 'events';
import { uniqueId } from '@ali/lowcode-globals'; import { uniqueId } from '@ali/lowcode-globals';
import { ComponentMeta, Node, Designer, Selection } from '@ali/lowcode-designer'; import { ComponentMeta, Node, Designer, Selection } from '@ali/lowcode-designer';
import { TitleContent, FieldExtraProps, SetterType, CustomView, FieldConfig, isCustomView } from '@ali/lowcode-globals'; import { TitleContent, FieldExtraProps, SetterType, CustomView, FieldConfig, isCustomView } from '@ali/lowcode-globals';
import { getTreeMaster } from 'plugin-outline-pane/src/main';
export interface SettingTarget { export interface SettingTarget {
// 所设置的节点集,至少一个 // 所设置的节点集,至少一个
@ -337,8 +338,16 @@ export class SettingsMain implements SettingTarget {
} }
}; };
editor.on('designer.selection-change', setupSelection); editor.on('designer.selection-change', setupSelection);
const connectTree = (designer: any) => {
getTreeMaster(designer).onceEnableBuiltin(() => {
this.emitter.emit('outline-visible');
});
}
if (editor.designer) { if (editor.designer) {
connectTree(editor.designer);
setupSelection(editor.designer.currentSelection); setupSelection(editor.designer.currentSelection);
} else {
editor.once('designer.mount', connectTree);
} }
this.disposeListener = () => { this.disposeListener = () => {
editor.removeListener('designer.selection-change', setupSelection); editor.removeListener('designer.selection-change', setupSelection);
@ -350,6 +359,13 @@ export class SettingsMain implements SettingTarget {
return this.onNodesChange(action); return this.onNodesChange(action);
} }
onceOutlineVisible(fn: () => void): () => void {
this.emitter.on('outline-visible', fn);
return () => {
this.emitter.removeListener('outline-visible', fn);
};
}
/** /**
* *
*/ */

View File

@ -129,4 +129,18 @@
overflow-y: auto; overflow-y: auto;
} }
} }
.lc-outline-pane {
position: absolute;
z-index: 100;
background-color: white;
top: 0;
bottom: 0;
display: none;
}
}
html.lc-cursor-dragging:not(.lowcode-has-fixed-tree) {
.lc-settings-main .lc-outline-pane {
display: block;
}
} }

View File

@ -0,0 +1 @@
## todo

View File

@ -0,0 +1,5 @@
{
"plugins": [
"build-plugin-component"
]
}

View File

@ -0,0 +1,31 @@
{
"name": "@ali/lowcode-plugin-zh-en",
"version": "0.8.0",
"description": "alibaba lowcode editor zhong english plugin",
"files": [
"es",
"lib"
],
"main": "lib/index.js",
"module": "es/index.js",
"scripts": {
"build": "build-scripts build --skip-demo",
"test": "ava",
"test:snapshot": "ava --update-snapshots"
},
"dependencies": {
"@ali/lowcode-globals": "^0.8.0",
"@ali/lowcode-editor-core": "^0.8.0",
"react": "^16.8.1",
"react-dom": "^16.8.1"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.3",
"@types/react": "^16.9.13",
"@types/react-dom": "^16.9.4",
"build-plugin-component": "^0.2.11"
},
"publishConfig": {
"registry": "https://registry.npm.alibaba-inc.com"
}
}

View File

@ -0,0 +1,11 @@
import { IconBase, IconProps } from '@ali/lowcode-globals';
export function IconEn(props: IconProps) {
return (
<IconBase viewBox="0 0 1024 1024" {...props}>
<path d="M885.7 796.7h-37.3v81.4c0 45-33.4 81.4-74.7 81.4H138.9c-41.2 0-74.7-36.5-74.7-81.4v-570c0-45 33.4-81.4 74.7-81.4h37.3v-81.4c0-45 33.4-81.4 74.7-81.4h634.8c41.2 0 74.7 36.4 74.7 81.4v570c0 45-33.5 81.4-74.7 81.4zM138.9 267.4c-20.6 0-37.3 18.2-37.3 40.7v570c0 22.5 16.7 40.7 37.3 40.7h634.8c20.6 0 37.3-18.2 37.3-40.7v-570c0-22.5-16.7-40.7-37.3-40.7H138.9zM923 145.3c0-22.5-16.7-40.7-37.3-40.7H250.9c-20.6 0-37.3 18.2-37.3 40.7v81.4h560.1c41.2 0 74.7 36.5 74.7 81.4V756h37.3c20.6 0 37.3-18.2 37.3-40.7v-570zM624.3 593.1s4.3-40.7-37.3-40.7c-17 0-37.3 8.9-37.3 40.7V756H475V511.7h149.4c-2.2 0 74.7-5 74.7 81.4V756h-74.7l-0.1-162.9zM213.6 389.6h224v40.7H288.2v122.1h112v40.7h-112v122.1h149.4V756h-224V389.6z" />
</IconBase>
);
}
IconEn.displayName = 'IconEn';

View File

@ -0,0 +1,11 @@
import { IconBase, IconProps } from '@ali/lowcode-globals';
export function IconZh(props: IconProps) {
return (
<IconBase viewBox="0 0 1024 1024" {...props}>
<path d="M885.6 789.8h-37.4v80.6c0 44.5-33.5 80.6-74.8 80.6H138.1c-41.3 0-74.8-36.1-74.8-80.6V306.1c0-44.5 33.5-80.6 74.8-80.6h37.4v-80.6c0-44.5 33.5-80.6 74.8-80.6h635.4c41.3 0 74.8 36.1 74.8 80.6v564.3c-0.1 44.5-33.6 80.6-74.9 80.6z m-747.5-524c-20.7 0-37.4 18.1-37.4 40.3v564.3c0 22.3 16.7 40.3 37.4 40.3h635.4c20.6 0 37.4-18.1 37.4-40.3V306.1c0-22.3-16.7-40.3-37.4-40.3H138.1z m784.9-121c0-22.3-16.7-40.3-37.4-40.3H250.2c-20.6 0-37.4 18-37.4 40.3v80.6h560.6c41.3 0 74.8 36.1 74.8 80.6v443.4h37.4c20.6 0 37.4-18 37.4-40.3V144.8zM586.6 628.5h-74.8v161.2H399.7V628.5H325v40.3h-74.8V427h149.5v-80.6h112.1V427h149.5v241.8h-74.8l0.1-40.3zM399.7 467.3H325v120.9h74.8l-0.1-120.9z m186.9 0h-74.8v120.9h74.8V467.3z" />
</IconBase>
);
}
IconZh.displayName = 'IconZh';

View File

@ -0,0 +1,11 @@
.lowcode-plugin-zh-en {
display: flex;
align-items: center;
justify-content: center;
height: 30px;
cursor: pointer;
color: #8F9BB3;
&:hover {
color: #1F3858;
}
}

View File

@ -0,0 +1,36 @@
import { PureComponent } from 'react';
import { globalLocale, EmbedTip } from '@ali/lowcode-globals';
import { PluginProps } from '@ali/lowcode-editor-core';
import { intl } from './locale';
import { IconZh } from './icons/zh';
import { IconEn } from './icons/en';
import './index.less';
export default class ZhEn extends PureComponent<PluginProps> {
static displayName = 'LowcodeZhEn';
state = {
locale: globalLocale.getLocale(),
};
private dispose = globalLocale.onLocaleChange((locale) => {
this.setState({
locale
});
});
componentWillUnmount() {
this.dispose();
}
render() {
const isZh = this.state.locale === 'zh-CN';
return (
<div className="lowcode-plugin-zh-en" onClick={() => {
globalLocale.setLocale(isZh ? 'en-US' : 'zh-CN');
}}>
{isZh ? <IconZh size={20} /> : <IconEn size={20} />}
<EmbedTip direction="right">{intl('To Locale')}</EmbedTip>
</div>
);
}
}

View File

@ -0,0 +1,3 @@
{
"To Locale": "中/En切换"
}

View File

@ -0,0 +1,10 @@
import { createIntl } from '@ali/lowcode-globals';
import en_US from './en-US.json';
import zh_CN from './zh-CN.json';
const { intl, getLocale, setLocale } = createIntl({
'en-US': en_US,
'zh-CN': zh_CN,
});
export { intl, getLocale, setLocale };

View File

@ -0,0 +1,3 @@
{
"To Locale": "中/En切换"
}

View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "lib"
},
"include": [
"./src/"
]
}