mirror of
https://github.com/NetEase/tango.git
synced 2026-08-31 20:06:59 +08:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 897fe0a5a1 | |||
| b52bbad547 | |||
| f7c837a8a9 | |||
| f64d178ab8 | |||
| f2dcc942ed | |||
| ac48a8880e | |||
| 0f1711868d | |||
| 06cd3b3a18 | |||
| 2664613ab2 | |||
| e39a913a87 | |||
| 52864b5400 | |||
| 27166ec949 | |||
| 1a8d15c9da | |||
| 43b99de01c | |||
| 1c1ad9e5eb | |||
| 776837079a | |||
| 14375284e4 | |||
| cb6aabf6ce | |||
| 3f42516816 | |||
| 93608d1037 | |||
| 41a61cce4e | |||
| 47364e4219 | |||
| 9ed6a7d1a4 | |||
| 6f2c8f239a | |||
| 6215ed96f2 | |||
| 190aa1d8ee | |||
| bceb7a5c9c | |||
| d64042922b | |||
| 9ada3fecb3 | |||
| 07febb385f |
@@ -7,6 +7,7 @@ const resolvePackageIndex = (relativeEntry: string) =>
|
||||
export default defineConfig({
|
||||
routes: [
|
||||
{ path: '/', component: 'index' },
|
||||
{ path: '/mail', component: 'mail' },
|
||||
{ path: '/docs', component: 'docs' },
|
||||
],
|
||||
npmClient: 'yarn',
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"author": "Wells <ww.sun@outlook.com>",
|
||||
"scripts": {
|
||||
"dev": "HOST=local.netease.com PORT=8001 umi dev",
|
||||
"dev": "cross-env HOST=local.netease.com PORT=8001 umi dev",
|
||||
"build": "umi build",
|
||||
"postinstall": "umi setup",
|
||||
"setup": "umi setup",
|
||||
@@ -12,13 +12,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/antd": "^0.2.4",
|
||||
"antd": "^4.24.2",
|
||||
"coral-system": "^1.0.5",
|
||||
"umi": "^4.0.89"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.33",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"typescript": "^5.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Box, Group } from 'coral-system';
|
||||
import { Avatar, Space, Switch } from 'antd';
|
||||
import { BranchesOutlined, MenuOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { registerSetter } from '@music163/tango-designer';
|
||||
import type { ComponentPrototypeType, IVariableTreeNode } from '@music163/tango-helpers';
|
||||
import type { IVariableTreeNode } from '@music163/tango-helpers';
|
||||
import { FooSetter } from '../components';
|
||||
|
||||
export * from './mock-files';
|
||||
@@ -37,113 +37,6 @@ export const bootHelperVariables: IVariableTreeNode[] = [
|
||||
},
|
||||
];
|
||||
|
||||
// folder-name
|
||||
// 物料列表定义
|
||||
const bizToggleButtonPrototype: ComponentPrototypeType = {
|
||||
name: 'CtPcToggleButton',
|
||||
exportType: 'defaultExport',
|
||||
title: '示例业务组件',
|
||||
icon: 'icon-tupian',
|
||||
type: 'element',
|
||||
docs: 'https://redstone.fn.netease.com/mt/fe-comp/w8bq8px7n5/toggle-button',
|
||||
hasChildren: false,
|
||||
props: [
|
||||
{
|
||||
name: 'checked',
|
||||
title: '是否选中',
|
||||
setter: 'boolSetter',
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
name: 'children',
|
||||
title: '文本',
|
||||
setter: 'textSetter',
|
||||
initValue: '按钮',
|
||||
},
|
||||
],
|
||||
package: '@music/ct-pc-toggle-button',
|
||||
};
|
||||
|
||||
const sampleBlockCode = `
|
||||
<Section>
|
||||
<Result
|
||||
status="success"
|
||||
title="Successfully Purchased Cloud Server ECS!"
|
||||
subTitle="Order number: 2017182818828182881 Cloud server configuration takes 1-5 minutes, please wait."
|
||||
extra={[
|
||||
<Button type="primary" key="console">
|
||||
Go Console
|
||||
</Button>,
|
||||
<Button key="buy">Buy Again</Button>,
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
`;
|
||||
|
||||
const SnippetSuccessResult: ComponentPrototypeType = {
|
||||
name: 'SnippetSuccessResult',
|
||||
title: '成功结果',
|
||||
icon: 'icon-tupian',
|
||||
type: 'snippet',
|
||||
package: '@music163/antd',
|
||||
initChildren: sampleBlockCode,
|
||||
relatedImports: ['Section', 'Result', 'Button'],
|
||||
};
|
||||
|
||||
const Snippet2ColumnLayout: ComponentPrototypeType = {
|
||||
name: 'Snippet2ColumnLayout',
|
||||
title: '两列布局',
|
||||
icon: 'icon-columns',
|
||||
type: 'snippet',
|
||||
package: '@music163/antd',
|
||||
initChildren: `
|
||||
<Columns columns={12}>
|
||||
<Column colSpan={6}></Column>
|
||||
<Column colSpan={6}></Column>
|
||||
</Columns>
|
||||
`,
|
||||
relatedImports: ['Columns', 'Column'],
|
||||
};
|
||||
|
||||
const Snippet3ColumnLayout: ComponentPrototypeType = {
|
||||
name: 'Snippet3ColumnLayout',
|
||||
title: '三列布局',
|
||||
icon: 'icon-column3',
|
||||
type: 'snippet',
|
||||
package: '@music163/antd',
|
||||
initChildren: `
|
||||
<Columns columns={12}>
|
||||
<Column colSpan={4}></Column>
|
||||
<Column colSpan={4}></Column>
|
||||
<Column colSpan={4}></Column>
|
||||
</Columns>
|
||||
`,
|
||||
relatedImports: ['Columns', 'Column'],
|
||||
};
|
||||
|
||||
const SnippetButtonGroup: ComponentPrototypeType = {
|
||||
name: 'SnippetButtonGroup',
|
||||
title: '按钮组',
|
||||
icon: 'icon-anniuzu',
|
||||
type: 'snippet',
|
||||
package: '@music163/antd',
|
||||
initChildren: `
|
||||
<Space>
|
||||
<Button type="primary">按钮1</Button>
|
||||
<Button>按钮2</Button>
|
||||
</Space>
|
||||
`,
|
||||
relatedImports: ['Space', 'Button'],
|
||||
};
|
||||
|
||||
export const prototypes = {
|
||||
CtPcToggleButton: bizToggleButtonPrototype,
|
||||
SnippetSuccessResult,
|
||||
Snippet2ColumnLayout,
|
||||
Snippet3ColumnLayout,
|
||||
SnippetButtonGroup,
|
||||
};
|
||||
|
||||
// 注册自定义 setter
|
||||
registerSetter({
|
||||
name: 'fooSetter',
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
const packageJson = {
|
||||
name: 'demo',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'@music163/tango-mail': '0.1.1',
|
||||
'@music163/tango-boot': '0.2.5',
|
||||
react: '17.0.2',
|
||||
'react-dom': '17.0.2',
|
||||
'prop-types': '15.7.2',
|
||||
tslib: '2.5.0',
|
||||
},
|
||||
};
|
||||
|
||||
const tangoConfigJson = {
|
||||
designerConfig: {
|
||||
autoGenerateComponentId: true,
|
||||
},
|
||||
packages: {
|
||||
react: {
|
||||
version: '17.0.2',
|
||||
library: 'React',
|
||||
type: 'dependency',
|
||||
resources: ['https://unpkg.com/react@{{version}}/umd/react.development.js'],
|
||||
},
|
||||
'react-dom': {
|
||||
version: '17.0.2',
|
||||
library: 'ReactDOM',
|
||||
type: 'dependency',
|
||||
resources: ['https://unpkg.com/react-dom@{{version}}/umd/react-dom.development.js'],
|
||||
},
|
||||
'react-is': {
|
||||
version: '16.13.1',
|
||||
library: 'ReactIs',
|
||||
type: 'dependency',
|
||||
resources: ['https://unpkg.com/react-is@{{version}}/umd/react-is.production.min.js'],
|
||||
},
|
||||
'@music163/tango-boot': {
|
||||
description: '云音乐低代码运行时框架',
|
||||
version: '0.2.5',
|
||||
library: 'TangoBoot',
|
||||
type: 'baseDependency',
|
||||
resources: ['https://unpkg.com/@music163/tango-boot@{{version}}/dist/boot.js'],
|
||||
// resources: ['http://localhost:9001/boot.js'],
|
||||
},
|
||||
'@music163/tango-mail': {
|
||||
description: 'TangoMail 基础物料',
|
||||
version: '0.1.1',
|
||||
library: 'TangoMail',
|
||||
type: 'baseDependency',
|
||||
resources: ['https://unpkg.com/@music163/tango-mail@{{version}}/dist/index.js'],
|
||||
designerResources: ['https://unpkg.com/@music163/tango-mail@{{version}}/dist/designer.js'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const routesCode = `
|
||||
import Mail from "./pages/mail";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
exact: true,
|
||||
component: Mail,
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
`;
|
||||
|
||||
const entryCode = `
|
||||
import { runApp } from '@music163/tango-boot';
|
||||
import routes from './routes';
|
||||
|
||||
runApp({
|
||||
boot: {
|
||||
mountElement: document.querySelector('#root'),
|
||||
qiankun: false,
|
||||
},
|
||||
|
||||
router: {
|
||||
type: 'browser',
|
||||
config: routes,
|
||||
},
|
||||
});
|
||||
`;
|
||||
|
||||
const viewHomeCode = `
|
||||
import React from 'react';
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Hr,
|
||||
Html,
|
||||
Preview,
|
||||
Section,
|
||||
Text,
|
||||
} from '@music163/tango-mail';
|
||||
|
||||
const WelcomeEmail = () => (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>The sales intelligence platform that helps you uncover qualified leads.</Preview>
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
<Text style={paragraph}>Hi wells,</Text>
|
||||
<Text style={paragraph}>
|
||||
Welcome to Koala, the sales intelligence platform that helps you uncover qualified leads
|
||||
and close deals faster.
|
||||
</Text>
|
||||
<Section style={btnContainer}>
|
||||
<Button style={button} href="https://getkoala.com">
|
||||
Get started
|
||||
</Button>
|
||||
</Section>
|
||||
<Text style={paragraph}>
|
||||
Best,
|
||||
<br />
|
||||
The Koala team
|
||||
</Text>
|
||||
<Hr style={hr} />
|
||||
<Text style={footer}>470 Noor Ave STE B #1148, South San Francisco, CA 94080</Text>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
|
||||
const main = {
|
||||
backgroundColor: '#ffffff',
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
|
||||
};
|
||||
|
||||
const container = {
|
||||
margin: '0 auto',
|
||||
padding: '20px 0 48px',
|
||||
};
|
||||
|
||||
const paragraph = {
|
||||
fontSize: '16px',
|
||||
lineHeight: '26px',
|
||||
};
|
||||
|
||||
const btnContainer = {
|
||||
textAlign: 'center',
|
||||
};
|
||||
|
||||
const button = {
|
||||
backgroundColor: '#5F51E8',
|
||||
borderRadius: '3px',
|
||||
color: '#fff',
|
||||
fontSize: '16px',
|
||||
textDecoration: 'none',
|
||||
textAlign: 'center',
|
||||
display: 'block',
|
||||
padding: '12px',
|
||||
};
|
||||
|
||||
const hr = {
|
||||
borderColor: '#cccccc',
|
||||
margin: '20px 0',
|
||||
};
|
||||
|
||||
const footer = {
|
||||
color: '#8898aa',
|
||||
fontSize: '12px',
|
||||
};
|
||||
|
||||
export default WelcomeEmail;
|
||||
`;
|
||||
|
||||
export const mailFiles = [
|
||||
{ filename: '/package.json', code: JSON.stringify(packageJson) },
|
||||
{ filename: '/tango.config.json', code: JSON.stringify(tangoConfigJson) },
|
||||
{ filename: '/README.md', code: '# readme' },
|
||||
{ filename: '/src/index.js', code: entryCode },
|
||||
{ filename: '/src/pages/mail.js', code: viewHomeCode },
|
||||
{ filename: '/src/routes.js', code: routesCode },
|
||||
];
|
||||
@@ -2,7 +2,7 @@ const packageJson = {
|
||||
name: 'demo',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'@music163/antd': '0.2.1',
|
||||
'@music163/antd': '0.2.2',
|
||||
'@music163/tango-boot': '0.2.5',
|
||||
react: '17.0.2',
|
||||
'react-dom': '17.0.2',
|
||||
@@ -56,7 +56,7 @@ const tangoConfigJson = {
|
||||
},
|
||||
'@music163/antd': {
|
||||
description: '云音乐低代码中后台应用基础物料',
|
||||
version: '0.2.1',
|
||||
version: '0.2.2',
|
||||
library: 'TangoAntd',
|
||||
type: 'baseDependency',
|
||||
resources: [
|
||||
@@ -149,29 +149,88 @@ import {
|
||||
Button,
|
||||
Input,
|
||||
FormilyForm,
|
||||
FormilyFormItem,
|
||||
} from "@music163/antd";
|
||||
import { Space } from '@music163/antd';
|
||||
import { LocalButton } from '../components';
|
||||
|
||||
import { Space } from "@music163/antd";
|
||||
import { LocalButton } from "../components";
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Page title={tango.stores.app.title}>
|
||||
<Section tid="section1" title="Section Title">
|
||||
your input: <Input tid="input1" defaultValue="hello" />
|
||||
copy input: <Input value={tango.page.input1?.value} />
|
||||
</Section>
|
||||
<Section tid="section2">
|
||||
<Space tid="space1">
|
||||
<LocalButton />
|
||||
<Button tid="button1">button</Button>
|
||||
<Section tid="section1" title="Section Title">
|
||||
your input: <Input tid="input1" defaultValue="hello" />
|
||||
copy input: <Input value={tango.page.input1?.value} />
|
||||
</Section>
|
||||
<Section tid="section2">
|
||||
<Space tid="space1">
|
||||
<LocalButton />
|
||||
<Button tid="button1">button</Button>
|
||||
</Space>
|
||||
</Section>
|
||||
<Section title="区块标题" tid="section3">
|
||||
<FormilyForm tid="formilyForm1">
|
||||
<FormilyFormItem name="input1" component="Input" label="表单项" />
|
||||
<FormilyFormItem name="select1" component="Select" label="表单项" />
|
||||
</FormilyForm>
|
||||
</Section>
|
||||
<Section title="原生 DOM" tid="section4">
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid #ccc",
|
||||
borderRadius: "5px",
|
||||
backgroundColor: "#f4f4f4",
|
||||
}}
|
||||
>
|
||||
<form onSubmit={tango.stores.app.submitForm} style={{
|
||||
padding: "10px",
|
||||
}}>
|
||||
<div>
|
||||
<label>Username:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
style={{ margin: "5px" }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Password:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
style={{ margin: "5px" }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Role:</label>
|
||||
<select id="role" name="role" style={{ margin: "5px" }}>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="user">User</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
style={{
|
||||
marginTop: '5px',
|
||||
padding: "3px 10px",
|
||||
backgroundColor: "#007bff",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "5px",
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Section>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default definePage(App);
|
||||
`;
|
||||
|
||||
@@ -222,38 +281,21 @@ const storeApp = `
|
||||
import { defineStore } from '@music163/tango-boot';
|
||||
|
||||
export default defineStore({
|
||||
|
||||
title: 'Page Title',
|
||||
|
||||
array: [1, 2, 3],
|
||||
|
||||
test: function() {
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
},
|
||||
|
||||
submitForm: function(event) {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(event.target);
|
||||
const username = formData.get("username");
|
||||
const password = formData.get("password");
|
||||
const role = formData.get("role");
|
||||
console.log("Submitted Data:", { username, password, role });
|
||||
}
|
||||
}, 'app');
|
||||
`;
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
import * as basePrototypes from '@music163/antd/prototypes';
|
||||
import type { IComponentPrototype, Dict } from '@music163/tango-helpers';
|
||||
|
||||
const sampleBlockCode = `
|
||||
<Section>
|
||||
<Result
|
||||
status="success"
|
||||
title="Successfully Purchased Cloud Server ECS!"
|
||||
subTitle="Order number: 2017182818828182881 Cloud server configuration takes 1-5 minutes, please wait."
|
||||
extra={[
|
||||
<Button type="primary" key="console">
|
||||
Go Console
|
||||
</Button>,
|
||||
<Button key="buy">Buy Again</Button>,
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
`;
|
||||
|
||||
const SnippetSuccessResult: IComponentPrototype = {
|
||||
name: 'SnippetSuccessResult',
|
||||
title: '成功结果',
|
||||
icon: 'icon-tupian',
|
||||
type: 'snippet',
|
||||
package: '@music163/antd',
|
||||
initChildren: sampleBlockCode,
|
||||
relatedImports: ['Section', 'Result', 'Button'],
|
||||
};
|
||||
|
||||
const Snippet2ColumnLayout: IComponentPrototype = {
|
||||
name: 'Snippet2ColumnLayout',
|
||||
title: '两列布局',
|
||||
icon: 'icon-columns',
|
||||
type: 'snippet',
|
||||
package: '@music163/antd',
|
||||
initChildren: `
|
||||
<Columns columns={12}>
|
||||
<Column colSpan={6}></Column>
|
||||
<Column colSpan={6}></Column>
|
||||
</Columns>
|
||||
`,
|
||||
relatedImports: ['Columns', 'Column'],
|
||||
};
|
||||
|
||||
const Snippet3ColumnLayout: IComponentPrototype = {
|
||||
name: 'Snippet3ColumnLayout',
|
||||
title: '三列布局',
|
||||
icon: 'icon-column3',
|
||||
type: 'snippet',
|
||||
package: '@music163/antd',
|
||||
initChildren: `
|
||||
<Columns columns={12}>
|
||||
<Column colSpan={4}></Column>
|
||||
<Column colSpan={4}></Column>
|
||||
<Column colSpan={4}></Column>
|
||||
</Columns>
|
||||
`,
|
||||
relatedImports: ['Columns', 'Column'],
|
||||
};
|
||||
|
||||
const SnippetButtonGroup: IComponentPrototype = {
|
||||
name: 'SnippetButtonGroup',
|
||||
title: '按钮组',
|
||||
icon: 'icon-anniuzu',
|
||||
type: 'snippet',
|
||||
package: '@music163/antd',
|
||||
initChildren: `
|
||||
<Space>
|
||||
<Button type="primary">按钮1</Button>
|
||||
<Button>按钮2</Button>
|
||||
</Space>
|
||||
`,
|
||||
relatedImports: ['Space', 'Button'],
|
||||
};
|
||||
|
||||
// hack some prototypes
|
||||
basePrototypes['Section'].siblingNames = [
|
||||
'SnippetButtonGroup',
|
||||
'Section',
|
||||
'Section',
|
||||
'Section',
|
||||
'Section',
|
||||
'Section',
|
||||
'Section',
|
||||
'Section',
|
||||
];
|
||||
|
||||
export const nativeDomPrototypes = () => {
|
||||
const doms = [
|
||||
'div',
|
||||
'span',
|
||||
'h1',
|
||||
'h2',
|
||||
'p',
|
||||
'a',
|
||||
'img',
|
||||
'ul',
|
||||
'ol',
|
||||
'li',
|
||||
'input',
|
||||
'button',
|
||||
'form',
|
||||
'table',
|
||||
'tr',
|
||||
'td',
|
||||
'header',
|
||||
'footer',
|
||||
'nav',
|
||||
'section',
|
||||
'article',
|
||||
'aside',
|
||||
'main',
|
||||
'video',
|
||||
'audio',
|
||||
'label',
|
||||
'select',
|
||||
'option',
|
||||
'textarea',
|
||||
'iframe',
|
||||
];
|
||||
const componentPrototypes: Dict<IComponentPrototype> = doms.reduce(
|
||||
(acc: Dict<IComponentPrototype>, tag) => {
|
||||
acc[tag] = {
|
||||
name: tag,
|
||||
title: tag,
|
||||
hasChildren: true,
|
||||
package: '',
|
||||
type: 'element',
|
||||
props: [
|
||||
{
|
||||
name: 'style',
|
||||
title: '样式',
|
||||
group: 'style',
|
||||
setter: 'expressionSetter',
|
||||
},
|
||||
{
|
||||
name: 'className',
|
||||
title: '类名',
|
||||
setter: 'textSetter',
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
title: 'ID',
|
||||
setter: 'textSetter',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '点击事件',
|
||||
setter: 'actionSetter',
|
||||
group: 'event',
|
||||
},
|
||||
],
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
return componentPrototypes;
|
||||
};
|
||||
|
||||
// iconfont: https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=2891794
|
||||
const prototypes: Dict<IComponentPrototype> = {
|
||||
...nativeDomPrototypes(),
|
||||
...(basePrototypes as any),
|
||||
SnippetSuccessResult,
|
||||
Snippet2ColumnLayout,
|
||||
Snippet3ColumnLayout,
|
||||
SnippetButtonGroup,
|
||||
Box: {
|
||||
name: 'Box',
|
||||
title: '盒子',
|
||||
icon: 'icon-mianban',
|
||||
type: 'container',
|
||||
package: '@music163/antd',
|
||||
hasChildren: true,
|
||||
siblingNames: ['Box'],
|
||||
props: [
|
||||
{
|
||||
name: 'aaa',
|
||||
title: 'aaa',
|
||||
setter: 'textSetter',
|
||||
},
|
||||
{
|
||||
name: 'bbb',
|
||||
title: 'bbb',
|
||||
setter: 'textSetter',
|
||||
deprecated: true,
|
||||
},
|
||||
{
|
||||
name: 'ccc',
|
||||
title: 'ccc',
|
||||
setter: 'textSetter',
|
||||
deprecated: true,
|
||||
},
|
||||
{
|
||||
name: 'd',
|
||||
title: 'd',
|
||||
setter: 'textSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
Columns: {
|
||||
name: 'Columns',
|
||||
type: 'container',
|
||||
icon: 'icon-column-4',
|
||||
package: '@music163/antd',
|
||||
hasChildren: true,
|
||||
childrenNames: ['Column'],
|
||||
},
|
||||
Column: {
|
||||
name: 'Column',
|
||||
type: 'container',
|
||||
icon: 'icon-juxing',
|
||||
package: '@music163/antd',
|
||||
hasChildren: true,
|
||||
siblingNames: ['Column'],
|
||||
},
|
||||
Text: {
|
||||
name: 'Text',
|
||||
type: 'element',
|
||||
icon: 'icon-wenzi',
|
||||
package: '@music163/antd',
|
||||
initChildren: '文本内容',
|
||||
},
|
||||
Placeholder: {
|
||||
name: 'Placeholder',
|
||||
type: 'element',
|
||||
package: '@music163/antd',
|
||||
},
|
||||
ButtonGroup: {
|
||||
name: 'ButtonGroup',
|
||||
type: 'element',
|
||||
package: '@music163/antd',
|
||||
hasChildren: true,
|
||||
childrenNames: ['Button'],
|
||||
},
|
||||
};
|
||||
|
||||
export default prototypes;
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import React, { StrictMode } from 'react';
|
||||
import { Outlet } from 'umi';
|
||||
import './index.less';
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<div>
|
||||
<StrictMode>
|
||||
<Outlet />
|
||||
</div>
|
||||
</StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { Button, Space } from 'antd';
|
||||
import {
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
themeLight,
|
||||
} from '@music163/tango-designer';
|
||||
import { createEngine, Workspace } from '@music163/tango-core';
|
||||
import prototypes from '../helpers/prototypes';
|
||||
import { Logo, ProjectDetail, bootHelperVariables, sampleFiles } from '../helpers';
|
||||
import {
|
||||
ApiOutlined,
|
||||
@@ -29,8 +29,12 @@ import {
|
||||
const workspace = new Workspace({
|
||||
entry: '/src/index.js',
|
||||
files: sampleFiles,
|
||||
prototypes,
|
||||
});
|
||||
|
||||
// inject workspace to window for debug
|
||||
(window as any).__workspace__ = workspace;
|
||||
|
||||
// 2. 引擎初始化
|
||||
const engine = createEngine({
|
||||
workspace,
|
||||
@@ -46,15 +50,43 @@ const sandboxQuery = new DndQuery({
|
||||
|
||||
// 4. 图标库初始化(物料面板和组件树使用了 iconfont 里的图标)
|
||||
createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_2891794_cou9i7556tl.js',
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_2891794_6d4hj5u0bjx.js',
|
||||
});
|
||||
|
||||
const menuData = {
|
||||
common: [
|
||||
{
|
||||
title: '常用',
|
||||
items: [
|
||||
'Button',
|
||||
'Section',
|
||||
'Columns',
|
||||
'Column',
|
||||
'Box',
|
||||
'Text',
|
||||
'Space',
|
||||
'Typography',
|
||||
'Title',
|
||||
'Paragraph',
|
||||
'Table',
|
||||
'Each',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '输入',
|
||||
items: ['Input', 'InputNumber', 'Select'],
|
||||
},
|
||||
{
|
||||
title: 'Formily表单',
|
||||
items: ['FormilyForm', 'FormilyFormItem', 'FormilySubmit', 'FormilyReset'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* 5. 平台初始化,访问 https://local.netease.com:6006/
|
||||
*/
|
||||
export default function App() {
|
||||
const [menuLoading, setMenuLoading] = useState(true);
|
||||
const [menuData, setMenuData] = useState(false);
|
||||
return (
|
||||
<Designer
|
||||
theme={themeLight}
|
||||
@@ -92,8 +124,7 @@ export default function App() {
|
||||
label="组件"
|
||||
icon={<AppstoreAddOutlined />}
|
||||
widgetProps={{
|
||||
menuData: menuData as any,
|
||||
loading: menuLoading,
|
||||
menuData,
|
||||
}}
|
||||
/>
|
||||
<Sidebar.Item key="outline" label="结构" icon={<BuildOutlined />} />
|
||||
@@ -119,18 +150,17 @@ export default function App() {
|
||||
onMessage={(e) => {
|
||||
if (e.type === 'done') {
|
||||
const sandboxWindow: any = sandboxQuery.window;
|
||||
if (sandboxWindow.TangoAntd) {
|
||||
if (sandboxWindow.TangoAntd.menuData) {
|
||||
setMenuData(sandboxWindow.TangoAntd.menuData);
|
||||
}
|
||||
if (sandboxWindow.TangoAntd.prototypes) {
|
||||
workspace.setComponentPrototypes(sandboxWindow.TangoAntd.prototypes);
|
||||
}
|
||||
}
|
||||
// if (sandboxWindow.TangoAntd) {
|
||||
// if (sandboxWindow.TangoAntd.menuData) {
|
||||
// setMenuData(sandboxWindow.TangoAntd.menuData);
|
||||
// }
|
||||
// if (sandboxWindow.TangoAntd.prototypes) {
|
||||
// workspace.setComponentPrototypes(sandboxWindow.TangoAntd.prototypes);
|
||||
// }
|
||||
// }
|
||||
if (sandboxWindow.localTangoComponentPrototypes) {
|
||||
workspace.setComponentPrototypes(sandboxWindow.localTangoComponentPrototypes);
|
||||
}
|
||||
setMenuLoading(false);
|
||||
}
|
||||
}}
|
||||
navigatorExtra={<Button size="small">hello world</Button>}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { Button, Space } from 'antd';
|
||||
import {
|
||||
Designer,
|
||||
DesignerPanel,
|
||||
SettingPanel,
|
||||
Sidebar,
|
||||
Toolbar,
|
||||
WorkspacePanel,
|
||||
WorkspaceView,
|
||||
CodeEditor,
|
||||
Sandbox,
|
||||
DndQuery,
|
||||
themeLight,
|
||||
} from '@music163/tango-designer';
|
||||
import { createEngine, Workspace } from '@music163/tango-core';
|
||||
import { Logo, ProjectDetail, bootHelperVariables } from '@/helpers';
|
||||
import {
|
||||
AppstoreAddOutlined,
|
||||
BuildOutlined,
|
||||
ClusterOutlined,
|
||||
createFromIconfontCN,
|
||||
} from '@ant-design/icons';
|
||||
import { mailFiles } from '@/helpers/mail-files';
|
||||
|
||||
// 1. 实例化工作区
|
||||
const workspace = new Workspace({
|
||||
entry: '/src/index.js',
|
||||
files: mailFiles,
|
||||
});
|
||||
|
||||
// 2. 引擎初始化
|
||||
const engine = createEngine({
|
||||
workspace,
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
window.__mailWorkspace__ = workspace;
|
||||
|
||||
// 3. 沙箱初始化
|
||||
const sandboxQuery = new DndQuery({
|
||||
context: 'iframe',
|
||||
});
|
||||
|
||||
// 4. 图标库初始化(物料面板和组件树使用了 iconfont 里的图标)
|
||||
createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_2891794_cou9i7556tl.js',
|
||||
});
|
||||
|
||||
/**
|
||||
* 5. 平台初始化,访问 https://local.netease.com:6006/
|
||||
*/
|
||||
export default function App() {
|
||||
const [menuLoading, setMenuLoading] = useState(true);
|
||||
const [menuData, setMenuData] = useState(false);
|
||||
return (
|
||||
<Designer
|
||||
theme={themeLight}
|
||||
engine={engine}
|
||||
config={{
|
||||
customActionVariables: bootHelperVariables,
|
||||
customExpressionVariables: bootHelperVariables,
|
||||
}}
|
||||
sandboxQuery={sandboxQuery}
|
||||
>
|
||||
<DesignerPanel
|
||||
logo={<Logo />}
|
||||
description={<ProjectDetail />}
|
||||
actions={
|
||||
<Box px="l">
|
||||
<Toolbar>
|
||||
<Toolbar.Item key="history" placement="left" />
|
||||
<Toolbar.Item key="preview" placement="left" />
|
||||
<Toolbar.Item key="modeSwitch" placement="right" />
|
||||
<Toolbar.Item key="togglePanel" placement="right" />
|
||||
<Toolbar.Separator />
|
||||
<Toolbar.Item placement="right">
|
||||
<Space>
|
||||
<Button type="primary">发布</Button>
|
||||
</Space>
|
||||
</Toolbar.Item>
|
||||
</Toolbar>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Sidebar>
|
||||
<Sidebar.Item
|
||||
key="components"
|
||||
label="组件"
|
||||
icon={<AppstoreAddOutlined />}
|
||||
widgetProps={{
|
||||
menuData: menuData as any,
|
||||
loading: menuLoading,
|
||||
}}
|
||||
/>
|
||||
<Sidebar.Item key="outline" label="结构" icon={<BuildOutlined />} />
|
||||
<Sidebar.Item
|
||||
key="dependency"
|
||||
label="依赖"
|
||||
icon={<ClusterOutlined />}
|
||||
isFloat
|
||||
width={800}
|
||||
/>
|
||||
</Sidebar>
|
||||
<WorkspacePanel>
|
||||
<WorkspaceView mode="design">
|
||||
<Sandbox
|
||||
onMessage={(e) => {
|
||||
if (e.type === 'done') {
|
||||
const sandboxWindow: any = sandboxQuery.window;
|
||||
if (sandboxWindow.TangoMail) {
|
||||
if (sandboxWindow.TangoMail.menuData) {
|
||||
setMenuData(sandboxWindow.TangoMail.menuData);
|
||||
}
|
||||
if (sandboxWindow.TangoMail.prototypes) {
|
||||
workspace.setComponentPrototypes(sandboxWindow.TangoMail.prototypes);
|
||||
}
|
||||
}
|
||||
setMenuLoading(false);
|
||||
}
|
||||
}}
|
||||
navigatorExtra={<Button size="small">hello world</Button>}
|
||||
/>
|
||||
</WorkspaceView>
|
||||
<WorkspaceView mode="code">
|
||||
<CodeEditor />
|
||||
</WorkspaceView>
|
||||
</WorkspacePanel>
|
||||
<SettingPanel />
|
||||
</DesignerPanel>
|
||||
</Designer>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import React from 'react';
|
||||
import { FormModel, SettingForm, register } from '@music163/tango-setting-form';
|
||||
import { ComponentPrototypeType } from '@music163/tango-helpers';
|
||||
import { IComponentPrototype } from '@music163/tango-helpers';
|
||||
import { BorderSetter, DisplaySetter } from '@music163/tango-designer/src/setters/style-setter';
|
||||
import { JsxSetter } from '@music163/tango-designer/src/setters/jsx-setter';
|
||||
import { RenderSetter, TableCellSetter } from '@music163/tango-designer/src/setters/render-setter';
|
||||
import { NumberSetter } from '@music163/tango-designer/src/setters/number-setter';
|
||||
import { Box } from 'coral-system';
|
||||
import { JsonView } from '@music163/tango-ui';
|
||||
import { toJS } from 'mobx';
|
||||
@@ -20,6 +23,26 @@ register({
|
||||
component: DisplaySetter,
|
||||
});
|
||||
|
||||
register({
|
||||
name: 'jsxSetter',
|
||||
component: JsxSetter,
|
||||
});
|
||||
|
||||
register({
|
||||
name: 'renderSetter',
|
||||
component: RenderSetter,
|
||||
});
|
||||
|
||||
register({
|
||||
name: 'tableCellSetter',
|
||||
component: TableCellSetter,
|
||||
});
|
||||
|
||||
register({
|
||||
name: 'numberSetter',
|
||||
component: NumberSetter,
|
||||
});
|
||||
|
||||
createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_2891794_cou9i7556tl.js',
|
||||
});
|
||||
@@ -28,7 +51,7 @@ export default {
|
||||
title: 'SettingForm',
|
||||
};
|
||||
|
||||
const prototype: ComponentPrototypeType = {
|
||||
const prototype: IComponentPrototype = {
|
||||
name: 'Test',
|
||||
exportType: 'namedExport',
|
||||
title: '测试',
|
||||
@@ -56,6 +79,28 @@ const prototype: ComponentPrototypeType = {
|
||||
title: 'borderSetter',
|
||||
setter: 'borderSetter',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
title: 'eventSetter',
|
||||
tip: '当点击按钮时',
|
||||
setter: 'eventSetter',
|
||||
group: 'event',
|
||||
},
|
||||
{
|
||||
name: 'children',
|
||||
title: 'jsxSetter',
|
||||
setter: 'jsxSetter',
|
||||
},
|
||||
{
|
||||
name: 'render',
|
||||
title: 'renderSetter',
|
||||
setter: 'renderSetter',
|
||||
},
|
||||
{
|
||||
name: 'cell',
|
||||
title: 'tableCellSetter',
|
||||
setter: 'tableCellSetter',
|
||||
},
|
||||
{
|
||||
name: 'router',
|
||||
title: 'routerSetter',
|
||||
@@ -127,13 +172,7 @@ const prototype: ComponentPrototypeType = {
|
||||
title: 'iconTypeSetter',
|
||||
setter: 'iconTypeSetter',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
title: 'eventSetter',
|
||||
tip: '当点击按钮时',
|
||||
setter: 'eventSetter',
|
||||
group: 'event',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'onClick2',
|
||||
title: 'actionSetter',
|
||||
@@ -241,6 +280,22 @@ const prototype: ComponentPrototypeType = {
|
||||
title: 'invalidSetter',
|
||||
setter: 'invalidSetter',
|
||||
},
|
||||
{
|
||||
name: 'validate',
|
||||
title: 'validate',
|
||||
setter: 'numberSetter',
|
||||
validate: (value) => {
|
||||
if (!value && value !== 0) {
|
||||
return '必填';
|
||||
}
|
||||
if (value < 0) {
|
||||
return '必须大于 0';
|
||||
}
|
||||
if (value > 10) {
|
||||
return '必须小于等于 10';
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -268,13 +323,15 @@ export function Basic() {
|
||||
|
||||
return (
|
||||
<Box display="flex">
|
||||
<SettingForm
|
||||
model={model}
|
||||
prototype={prototype}
|
||||
showIdentifier={{
|
||||
identifierKey: 'tid',
|
||||
}}
|
||||
/>
|
||||
<Box flex="0 0 400px" overflow="hidden">
|
||||
<SettingForm
|
||||
model={model}
|
||||
prototype={prototype}
|
||||
showIdentifier={{
|
||||
identifierKey: 'tid',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box position="relative">
|
||||
<Card title="表单状态预览" style={{ position: 'sticky', top: 0 }}>
|
||||
<FormValuePreview model={model} />
|
||||
|
||||
+4
-3
@@ -40,6 +40,7 @@
|
||||
"@typescript-eslint/parser": "^6.13.2",
|
||||
"conventional-changelog-cli": "^4.1.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-config-ali": "^14.0.2",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
@@ -51,9 +52,9 @@
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"lerna": "^8.0.0",
|
||||
"lint-staged": "^15.2.0",
|
||||
"prettier": "^3.1.0",
|
||||
"lerna": "^8.1.2",
|
||||
"lint-staged": "^15.2.2",
|
||||
"prettier": "^3.2.5",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"styled-components": "^5.3.6",
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.1](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0...@music163/tango-context@1.0.1) (2024-04-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- allow reload state tree & display pageStore in variable tree ([#135](https://github.com/netease/tango/issues/135)) ([2664613](https://github.com/netease/tango/commit/2664613ab263aead2a5239fa012454fc7fd5ff99))
|
||||
|
||||
# [1.0.0-alpha.10](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0-alpha.9...@music163/tango-context@1.0.0-alpha.10) (2024-04-09)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
# [1.0.0-alpha.9](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0-alpha.8...@music163/tango-context@1.0.0-alpha.9) (2024-03-26)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
# [1.0.0-alpha.8](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0-alpha.7...@music163/tango-context@1.0.0-alpha.8) (2024-03-18)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-context",
|
||||
"version": "1.0.0-alpha.8",
|
||||
"version": "1.0.2",
|
||||
"description": "react context for tango-apps",
|
||||
"keywords": [
|
||||
"react",
|
||||
@@ -31,8 +31,8 @@
|
||||
"react": ">= 16.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@music163/tango-core": "^1.0.0-alpha.8",
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.4",
|
||||
"@music163/tango-core": "^1.0.2",
|
||||
"@music163/tango-helpers": "^1.0.0",
|
||||
"mobx-react-lite": "4.0.5"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -35,8 +35,14 @@ export const useWorkspaceData = () => {
|
||||
const modelVariables: IVariableTreeNode[] = []; // 绑定变量列表
|
||||
const storeActionVariables: IVariableTreeNode[] = []; // 模型中的所有 actions
|
||||
const storeVariables: IVariableTreeNode[] = []; // 模型中的所有变量
|
||||
const pageStoreVariables: IVariableTreeNode[] = []; // 页面中的组件实例变量
|
||||
const serviceVariables: IVariableTreeNode[] = []; // 服务中的所有变量
|
||||
|
||||
pageStoreVariables.push({
|
||||
title: 'pageStore',
|
||||
key: 'pageStore',
|
||||
});
|
||||
|
||||
Object.values(workspace.storeModules).forEach((file) => {
|
||||
const prefix = `stores.${file.name}`;
|
||||
const states: IVariableTreeNode[] = file.states.map((item) => ({
|
||||
@@ -109,6 +115,7 @@ export const useWorkspaceData = () => {
|
||||
}
|
||||
|
||||
let expressionVariables: IVariableTreeNode[] = [
|
||||
buildVariableOptions('当前页面组件实例', '$pageStore', pageStoreVariables),
|
||||
buildVariableOptions('数据模型', '$stores', storeVariables),
|
||||
buildVariableOptions('服务函数', '$services', serviceVariables),
|
||||
];
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.1](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0...@music163/tango-core@1.0.1) (2024-04-22)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-core
|
||||
|
||||
# [1.0.0-alpha.10](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0-alpha.9...@music163/tango-core@1.0.0-alpha.10) (2024-04-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- update setters and use tabOptions to filter props ([#129](https://github.com/netease/tango/issues/129)) ([93608d1](https://github.com/netease/tango/commit/93608d1037327afa4f755976b86427b6128ae3d0))
|
||||
|
||||
# [1.0.0-alpha.9](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0-alpha.8...@music163/tango-core@1.0.0-alpha.9) (2024-03-26)
|
||||
|
||||
### Features
|
||||
|
||||
- quick add sibling nodes ([#127](https://github.com/netease/tango/issues/127)) ([9ed6a7d](https://github.com/netease/tango/commit/9ed6a7d1a4944d69d96e034f243b61531862e317))
|
||||
|
||||
# [1.0.0-alpha.8](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0-alpha.7...@music163/tango-core@1.0.0-alpha.8) (2024-03-18)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-core
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-core",
|
||||
"version": "1.0.0-alpha.8",
|
||||
"version": "1.0.2",
|
||||
"description": "tango core",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"homepage": "",
|
||||
@@ -28,7 +28,7 @@
|
||||
"@babel/parser": "^7.23.5",
|
||||
"@babel/traverse": "^7.23.5",
|
||||
"@babel/types": "^7.23.5",
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.4",
|
||||
"@music163/tango-helpers": "^1.0.0",
|
||||
"@types/babel__generator": "^7.6.7",
|
||||
"@types/babel__traverse": "^7.20.4",
|
||||
"mobx": "6.12.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
ComponentPropType,
|
||||
ComponentPrototypeType,
|
||||
IComponentProp,
|
||||
IComponentPrototype,
|
||||
Dict,
|
||||
isNil,
|
||||
logger,
|
||||
@@ -13,7 +13,7 @@ import { code2expression } from './ast';
|
||||
import { isWrappedByExpressionContainer } from './assert';
|
||||
|
||||
export function prototype2importDeclarationData(
|
||||
prototype: ComponentPrototypeType,
|
||||
prototype: IComponentPrototype,
|
||||
relativeFilepath?: string,
|
||||
): { source: string; specifiers: IImportSpecifierData[] } {
|
||||
let source = prototype.package;
|
||||
@@ -51,7 +51,7 @@ export function prototype2importDeclarationData(
|
||||
* @deprecated
|
||||
*/
|
||||
export function getImportDeclarationPayloadByPrototype(
|
||||
prototype: ComponentPrototypeType,
|
||||
prototype: IComponentPrototype,
|
||||
relativeFilepath?: string,
|
||||
): IImportDeclarationPayload {
|
||||
let defaultSpecifier;
|
||||
@@ -88,7 +88,7 @@ export function getImportDeclarationPayloadByPrototype(
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
function getPropKeyValuePair(item: ComponentPropType, generateValue: (...args: any[]) => string) {
|
||||
function getPropKeyValuePair(item: IComponentProp, generateValue: (...args: any[]) => string) {
|
||||
const key = item.name;
|
||||
|
||||
let value = item.initValue;
|
||||
@@ -141,21 +141,21 @@ function getPropKeyValuePair(item: ComponentPropType, generateValue: (...args: a
|
||||
* @param prototype
|
||||
* @param extraProps
|
||||
*/
|
||||
export function prototype2code(prototype: ComponentPrototypeType, extraProps?: Dict) {
|
||||
export function prototype2code(prototype: IComponentPrototype, extraProps?: Dict) {
|
||||
let code;
|
||||
switch (prototype.type) {
|
||||
case 'snippet':
|
||||
code = prototype.initChildren || prototype.defaultChildren;
|
||||
break;
|
||||
default: {
|
||||
const propList: ComponentPropType[] = extraProps
|
||||
const propList: IComponentProp[] = extraProps
|
||||
? Object.keys(extraProps).map((key) => ({
|
||||
name: key,
|
||||
initValue: extraProps[key],
|
||||
}))
|
||||
: [];
|
||||
// merge extraProps to props
|
||||
const props = [...prototype.props, ...propList];
|
||||
const props = [...(prototype.props || []), ...propList];
|
||||
|
||||
const keys =
|
||||
props.reduce((acc, item) => {
|
||||
@@ -186,7 +186,7 @@ export function prototype2code(prototype: ComponentPrototypeType, extraProps?: D
|
||||
* @param prototype 组件的配置信息
|
||||
* @param props 额外的属性集
|
||||
*/
|
||||
export function prototype2jsxElement(prototype: ComponentPrototypeType, props?: Dict) {
|
||||
export function prototype2jsxElement(prototype: IComponentPrototype, props?: Dict) {
|
||||
const code = prototype2code(prototype, props);
|
||||
return code2expression(code) as t.JSXElement;
|
||||
}
|
||||
|
||||
@@ -75,15 +75,20 @@ export function inferFileType(filename: string): FileType {
|
||||
/**
|
||||
* 判断组件名是否合法
|
||||
* @example Button -> valid
|
||||
* @example div -> invalid
|
||||
* @example isStrict ? div -> invalid : div -> valid
|
||||
* @param name
|
||||
* @param isStrict 是否严格模式(严格模式不匹配原生标签)
|
||||
* @returns
|
||||
*/
|
||||
export function isValidComponentName(name: string) {
|
||||
export function isValidComponentName(name: string, isStrict = false) {
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isStrict) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const firstChar = name.charAt(0);
|
||||
return firstChar === firstChar.toUpperCase();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IWorkspace } from './interfaces';
|
||||
|
||||
export type SimulatorNameType = 'desktop' | 'phone';
|
||||
|
||||
export type DesignerViewType = 'design' | 'code';
|
||||
export type DesignerViewType = 'design' | 'code' | 'dual';
|
||||
|
||||
interface ISimulatorType {
|
||||
name: SimulatorNameType;
|
||||
|
||||
@@ -11,6 +11,8 @@ export enum HistoryMessage {
|
||||
ReplaceNode = 'replaceNode',
|
||||
CloneNode = 'cloneNode',
|
||||
InsertNode = 'insertNode',
|
||||
InsertBeforeNode = 'insertBeforeNode',
|
||||
InsertAfterNode = 'insertAfterNode',
|
||||
DropNode = 'dropNode',
|
||||
UpdateAttribute = 'updateAttribute',
|
||||
UpdateCode = 'updateCode',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentPrototypeType, Dict, ITangoConfigJson } from '@music163/tango-helpers';
|
||||
import { IComponentPrototype, Dict, ITangoConfigJson } from '@music163/tango-helpers';
|
||||
import { TangoHistory } from './history';
|
||||
import { SelectSource } from './select-source';
|
||||
import { DragSource } from './drag-source';
|
||||
@@ -141,7 +141,7 @@ export interface IWorkspace {
|
||||
dragSource: DragSource;
|
||||
|
||||
files: Map<string, TangoFile>;
|
||||
componentPrototypes: Map<string, ComponentPrototypeType>;
|
||||
componentPrototypes: Map<string, IComponentPrototype>;
|
||||
|
||||
entry: string;
|
||||
activeFile: string;
|
||||
@@ -171,8 +171,8 @@ export interface IWorkspace {
|
||||
setActiveRoute: (path: string) => void;
|
||||
setActiveFile: (filename: string) => void;
|
||||
|
||||
setComponentPrototypes: (prototypes: Record<string, ComponentPrototypeType>) => void;
|
||||
getPrototype: (name: string | ComponentPrototypeType) => ComponentPrototypeType;
|
||||
setComponentPrototypes: (prototypes: Record<string, IComponentPrototype>) => void;
|
||||
getPrototype: (name: string | IComponentPrototype) => IComponentPrototype;
|
||||
|
||||
// ----------------- 文件操作 -----------------
|
||||
addFiles: (files: IFileConfig[]) => void;
|
||||
@@ -204,16 +204,12 @@ export interface IWorkspace {
|
||||
cloneSelectedNode: () => void;
|
||||
copySelectedNode: () => void;
|
||||
pasteSelectedNode: () => void;
|
||||
insertToSelectedNode: (childNameOrPrototype: string | ComponentPrototypeType) => void;
|
||||
insertToSelectedNode: (childNameOrPrototype: string | IComponentPrototype) => void;
|
||||
insertBeforeSelectedNode: (sourceNameOrPrototype: string | IComponentPrototype) => void;
|
||||
insertAfterSelectedNode: (sourceNameOrPrototype: string | IComponentPrototype) => void;
|
||||
dropNode: () => void;
|
||||
insertToNode: (
|
||||
targetNodeId: string,
|
||||
sourceNameOrPrototype: string | ComponentPrototypeType,
|
||||
) => void;
|
||||
replaceNode: (
|
||||
targetNodeId: string,
|
||||
sourceNameOrPrototype: string | ComponentPrototypeType,
|
||||
) => void;
|
||||
insertToNode: (targetNodeId: string, sourceNameOrPrototype: string | IComponentPrototype) => void;
|
||||
replaceNode: (targetNodeId: string, sourceNameOrPrototype: string | IComponentPrototype) => void;
|
||||
updateSelectedNodeAttributes: (
|
||||
attributes: Record<string, any>,
|
||||
relatedImports?: string[],
|
||||
@@ -289,9 +285,16 @@ export interface IWorkspace {
|
||||
* 解析后的项目配置信息
|
||||
*/
|
||||
get projectConfig(): ITangoConfigJson;
|
||||
/**
|
||||
* 当前活动的视图文件
|
||||
*/
|
||||
get activeViewModule(): IViewFile;
|
||||
get pages(): any[];
|
||||
get bizComps(): string[];
|
||||
get baseComps(): string[];
|
||||
get localComps(): string[];
|
||||
/**
|
||||
* 是否是合法的项目
|
||||
*/
|
||||
get isValid(): boolean;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { action, computed, makeObservable, observable } from 'mobx';
|
||||
import { JSXElement } from '@babel/types';
|
||||
import {
|
||||
ComponentPrototypeType,
|
||||
IComponentPrototype,
|
||||
Dict,
|
||||
ITangoConfigJson,
|
||||
hasFileExtension,
|
||||
@@ -52,7 +52,7 @@ export interface IWorkspaceOptions {
|
||||
/**
|
||||
* 组件描述列表
|
||||
*/
|
||||
prototypes?: Record<string, ComponentPrototypeType>;
|
||||
prototypes?: Record<string, IComponentPrototype>;
|
||||
/**
|
||||
* 工作区文件变更事件
|
||||
*/
|
||||
@@ -75,7 +75,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
/**
|
||||
* 组件配置
|
||||
*/
|
||||
componentPrototypes: Map<string, ComponentPrototypeType>;
|
||||
componentPrototypes: Map<string, IComponentPrototype>;
|
||||
|
||||
/**
|
||||
* 入口文件
|
||||
@@ -144,6 +144,13 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
*/
|
||||
private copyTempNodes: TangoNode[];
|
||||
|
||||
/**
|
||||
* 是否是合法的项目
|
||||
*/
|
||||
get isValid() {
|
||||
return !!this.tangoConfigJson && !!this.activeViewModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目配置,返回解析后的 tango.config.json 文件
|
||||
*/
|
||||
@@ -249,11 +256,11 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
});
|
||||
}
|
||||
|
||||
getPrototype(name: string | ComponentPrototypeType) {
|
||||
getPrototype(name: string | IComponentPrototype) {
|
||||
if (isString(name)) {
|
||||
return this.componentPrototypes.get(name);
|
||||
}
|
||||
return name as ComponentPrototypeType;
|
||||
return name as IComponentPrototype;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,7 +306,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
}
|
||||
}
|
||||
|
||||
setComponentPrototypes(prototypes: Record<string, ComponentPrototypeType>) {
|
||||
setComponentPrototypes(prototypes: Record<string, IComponentPrototype>) {
|
||||
Object.keys(prototypes).forEach((name) => {
|
||||
this.componentPrototypes.set(name, prototypes[name]);
|
||||
});
|
||||
@@ -954,7 +961,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* @param sourceName 插入的组件名称
|
||||
* @returns
|
||||
*/
|
||||
insertToNode(targetNodeId: string, sourceName: string | ComponentPrototypeType) {
|
||||
insertToNode(targetNodeId: string, sourceName: string | IComponentPrototype) {
|
||||
if (!targetNodeId || !sourceName) {
|
||||
return;
|
||||
}
|
||||
@@ -978,7 +985,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
/**
|
||||
* 替换目标节点
|
||||
*/
|
||||
replaceNode(targetNodeId: string, sourceName: string | ComponentPrototypeType) {
|
||||
replaceNode(targetNodeId: string, sourceName: string | IComponentPrototype) {
|
||||
if (!targetNodeId || !sourceName) {
|
||||
return;
|
||||
}
|
||||
@@ -1000,7 +1007,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* 在选中节点中插入子节点
|
||||
* @param childName 节点名
|
||||
*/
|
||||
insertToSelectedNode(childName: string | ComponentPrototypeType) {
|
||||
insertToSelectedNode(childName: string | IComponentPrototype) {
|
||||
const insertedPrototype = this.getPrototype(childName);
|
||||
if (insertedPrototype) {
|
||||
const newNode = prototype2jsxElement(insertedPrototype);
|
||||
@@ -1022,6 +1029,46 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
}
|
||||
}
|
||||
|
||||
insertBeforeSelectedNode(sourceName: string | IComponentPrototype) {
|
||||
if (!sourceName) {
|
||||
return;
|
||||
}
|
||||
const targetNodeId = this.selectSource.first.id;
|
||||
const sourcePrototype = this.getPrototype(sourceName);
|
||||
const newNode = prototype2jsxElement(sourcePrototype);
|
||||
const file = this.getNode(targetNodeId).file;
|
||||
const { source, specifiers } = prototype2importDeclarationData(sourcePrototype, file.filename);
|
||||
|
||||
file.insertBefore(targetNodeId, newNode).addImportSpecifiers(source, specifiers).update();
|
||||
|
||||
this.history.push({
|
||||
message: HistoryMessage.InsertBeforeNode,
|
||||
data: {
|
||||
[file.filename]: file.code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
insertAfterSelectedNode(sourceName: string | IComponentPrototype) {
|
||||
if (!sourceName) {
|
||||
return;
|
||||
}
|
||||
const targetNodeId = this.selectSource.first.id;
|
||||
const sourcePrototype = this.getPrototype(sourceName);
|
||||
const newNode = prototype2jsxElement(sourcePrototype);
|
||||
const file = this.getNode(targetNodeId).file;
|
||||
const { source, specifiers } = prototype2importDeclarationData(sourcePrototype, file.filename);
|
||||
|
||||
file.insertAfter(targetNodeId, newNode).addImportSpecifiers(source, specifiers).update();
|
||||
|
||||
this.history.push({
|
||||
message: HistoryMessage.InsertAfterNode,
|
||||
data: {
|
||||
[file.filename]: file.code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
updateSelectedNodeAttributes(
|
||||
attributes: Record<string, any> = {},
|
||||
relatedImports: string[] = [],
|
||||
|
||||
@@ -161,6 +161,10 @@ describe('string helpers', () => {
|
||||
it('isValidComponentName', () => {
|
||||
expect(isValidComponentName('Button')).toBeTruthy();
|
||||
expect(isValidComponentName('Button.Group')).toBeTruthy();
|
||||
expect(isValidComponentName('div')).toBeTruthy();
|
||||
expect(isValidComponentName('')).toBeFalsy();
|
||||
expect(isValidComponentName(null)).toBeFalsy();
|
||||
expect(isValidComponentName(undefined)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('inferFileType', () => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.2](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.1...@music163/tango-designer@1.0.2) (2024-04-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- allow reload state tree & display pageStore in variable tree ([#135](https://github.com/netease/tango/issues/135)) ([2664613](https://github.com/netease/tango/commit/2664613ab263aead2a5239fa012454fc7fd5ff99))
|
||||
|
||||
## [1.0.1](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.0...@music163/tango-designer@1.0.1) (2024-04-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- render-setter expression value bug ([#133](https://github.com/netease/tango/issues/133)) ([27166ec](https://github.com/netease/tango/commit/27166ec94976979cb59130788399d90977cf0aec))
|
||||
- variable tree remove button event handling ([#134](https://github.com/netease/tango/issues/134)) ([52864b5](https://github.com/netease/tango/commit/52864b540095025232ae237134d6be5701d20549))
|
||||
|
||||
# [1.0.0-alpha.18](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.0-alpha.17...@music163/tango-designer@1.0.0-alpha.18) (2024-04-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- render invalid icon in inserted dropdown ([#128](https://github.com/netease/tango/issues/128)) ([41a61cc](https://github.com/netease/tango/commit/41a61cce4e6a1f9d42c7a3f4e357c5acdcd6db5f))
|
||||
- update setters and use tabOptions to filter props ([#129](https://github.com/netease/tango/issues/129)) ([93608d1](https://github.com/netease/tango/commit/93608d1037327afa4f755976b86427b6128ae3d0))
|
||||
- update SettingForm style ([#125](https://github.com/netease/tango/issues/125)) ([3f42516](https://github.com/netease/tango/commit/3f42516816968888554f719332d3cef7d8576aa8))
|
||||
|
||||
# [1.0.0-alpha.17](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.0-alpha.16...@music163/tango-designer@1.0.0-alpha.17) (2024-03-26)
|
||||
|
||||
### Features
|
||||
|
||||
- quick add sibling nodes ([#127](https://github.com/netease/tango/issues/127)) ([9ed6a7d](https://github.com/netease/tango/commit/9ed6a7d1a4944d69d96e034f243b61531862e317))
|
||||
|
||||
# [1.0.0-alpha.16](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.0-alpha.15...@music163/tango-designer@1.0.0-alpha.16) (2024-03-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- copy node path in variable tree ([6215ed9](https://github.com/netease/tango/commit/6215ed96f241adf6b67aa3019745bf7d40751787))
|
||||
|
||||
# [1.0.0-alpha.15](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.0-alpha.14...@music163/tango-designer@1.0.0-alpha.15) (2024-03-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- quick insert child ([d640429](https://github.com/netease/tango/commit/d64042922bc21723025b1a59586ef23f37f17ffc))
|
||||
- update selection helper style ([bceb7a5](https://github.com/netease/tango/commit/bceb7a5c9cea9dc2acb091bf7967e5d3b7ba5210))
|
||||
|
||||
# [1.0.0-alpha.14](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.0-alpha.13...@music163/tango-designer@1.0.0-alpha.14) (2024-03-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- select outline node & render dependency list ([#118](https://github.com/netease/tango/issues/118)) ([07febb3](https://github.com/netease/tango/commit/07febb385f710f9a0bc43639ce22787bb05e97ed))
|
||||
|
||||
# [1.0.0-alpha.13](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.0-alpha.12...@music163/tango-designer@1.0.0-alpha.13) (2024-03-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-designer",
|
||||
"version": "1.0.0-alpha.13",
|
||||
"version": "1.0.3",
|
||||
"description": "lowcode designer",
|
||||
"keywords": [
|
||||
"react"
|
||||
@@ -33,12 +33,12 @@
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/request": "^0.1.2",
|
||||
"@music163/tango-context": "^1.0.0-alpha.8",
|
||||
"@music163/tango-core": "^1.0.0-alpha.8",
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.4",
|
||||
"@music163/tango-sandbox": "^1.0.0-alpha.8",
|
||||
"@music163/tango-setting-form": "^1.0.0-alpha.11",
|
||||
"@music163/tango-ui": "^1.0.0-alpha.9",
|
||||
"@music163/tango-context": "^1.0.2",
|
||||
"@music163/tango-core": "^1.0.2",
|
||||
"@music163/tango-helpers": "^1.0.0",
|
||||
"@music163/tango-sandbox": "^1.0.2",
|
||||
"@music163/tango-setting-form": "^1.1.0",
|
||||
"@music163/tango-ui": "^1.0.3",
|
||||
"antd": "^4.24.2",
|
||||
"cash-dom": "^8.1.2",
|
||||
"classnames": "^2.3.2",
|
||||
|
||||
@@ -184,6 +184,12 @@ export function VariableTree(props: VariableTreeProps) {
|
||||
|
||||
if (isLeaf) {
|
||||
const showView = node.showViewButton ?? showViewButton;
|
||||
|
||||
let nodePath = node.key;
|
||||
if (/^(stores|services)\./.test(nodePath)) {
|
||||
nodePath = `tango.${node.key.replaceAll('.', '?.').replace('?.', '.')}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Text flex="1" truncated>
|
||||
@@ -198,7 +204,8 @@ export function VariableTree(props: VariableTreeProps) {
|
||||
{node.showRemoveButton && (
|
||||
<Popconfirm
|
||||
title={`确认删除吗 ${node.title}?该操作会导致引用此模型的代码报错,请谨慎操作!`}
|
||||
onConfirm={() => {
|
||||
onConfirm={(e) => {
|
||||
e.stopPropagation();
|
||||
if (isStoreVariablePath(node.key)) {
|
||||
onRemoveStoreVariable(node.key);
|
||||
} else {
|
||||
@@ -216,7 +223,7 @@ export function VariableTree(props: VariableTreeProps) {
|
||||
/>
|
||||
</Popconfirm>
|
||||
)}
|
||||
<CopyClipboard text={`tango.${node.key.replaceAll('.', '?.')}`}>
|
||||
<CopyClipboard text={nodePath}>
|
||||
{({ copied, onClick }) => {
|
||||
const label = copied ? '已复制' : '复制变量路径';
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { observer, useDesigner } from '@music163/tango-context';
|
||||
import { SelectAction } from '@music163/tango-ui';
|
||||
import { AimOutlined } from '@ant-design/icons';
|
||||
import { SelectAction, CodeOutlined } from '@music163/tango-ui';
|
||||
|
||||
export const ViewSourceAction = observer(() => {
|
||||
const designer = useDesigner();
|
||||
@@ -12,7 +11,7 @@ export const ViewSourceAction = observer(() => {
|
||||
designer.setActiveView('code');
|
||||
}}
|
||||
>
|
||||
<AimOutlined />
|
||||
<CodeOutlined />
|
||||
</SelectAction>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ExpressionModal, getWrappedExpressionCode } from './expression-setter';
|
||||
|
||||
enum EventAction {
|
||||
NoAction = 'noAction',
|
||||
ConsoleLog = 'consoleLog',
|
||||
BindExpression = 'bindExpression',
|
||||
OpenModal = 'openModal',
|
||||
CloseModal = 'closeModal',
|
||||
@@ -24,6 +25,7 @@ const wrapperStyle = css`
|
||||
|
||||
const options = [
|
||||
{ label: '无动作', value: EventAction.NoAction },
|
||||
{ label: '打印事件', value: EventAction.ConsoleLog },
|
||||
{ label: '绑定 JS 表达式', value: EventAction.BindExpression },
|
||||
{ label: '打开页面', value: EventAction.NavigateTo },
|
||||
{ label: '打开弹窗', value: EventAction.OpenModal },
|
||||
@@ -63,6 +65,9 @@ export function EventSetter(props: EventSetterProps) {
|
||||
case EventAction.BindExpression:
|
||||
setExpModalVisible(true);
|
||||
break;
|
||||
case EventAction.ConsoleLog:
|
||||
handleChange('(...args) => console.log(...args)');
|
||||
break;
|
||||
case EventAction.NoAction:
|
||||
handleChange(undefined);
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { Box, Text, css } from 'coral-system';
|
||||
import { Modal } from 'antd';
|
||||
import { Dropdown, Modal } from 'antd';
|
||||
import {
|
||||
isValidExpressionCode,
|
||||
isWrappedByExpressionContainer,
|
||||
@@ -13,8 +13,14 @@ import {
|
||||
getValue,
|
||||
IVariableTreeNode,
|
||||
} from '@music163/tango-helpers';
|
||||
import { CloseCircleFilled, ExpandAltOutlined } from '@ant-design/icons';
|
||||
import { Panel, InputCode, Action } from '@music163/tango-ui';
|
||||
import {
|
||||
CloseCircleFilled,
|
||||
ExpandAltOutlined,
|
||||
InfoOutlined,
|
||||
KeyOutlined,
|
||||
MenuOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Panel, InputCode, Action, CodeOutlined } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||
import { VariableTree } from '../components';
|
||||
@@ -60,6 +66,8 @@ export function getWrappedExpressionCode(code: string) {
|
||||
}
|
||||
|
||||
const suffixStyle = css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.anticon-close-circle {
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
&:hover {
|
||||
@@ -73,6 +81,7 @@ export interface ExpressionSetterProps extends FormItemComponentProps<string> {
|
||||
modalTip?: string;
|
||||
autoCompleteOptions?: string[];
|
||||
allowClear?: boolean;
|
||||
showOptionsDropDown?: boolean;
|
||||
}
|
||||
|
||||
export function ExpressionSetter(props: ExpressionSetterProps) {
|
||||
@@ -81,11 +90,12 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
||||
modalTitle,
|
||||
modalTip,
|
||||
autoCompleteOptions,
|
||||
placeholder = '输入JS表达式代码',
|
||||
placeholder = '输入JS代码',
|
||||
value: valueProp,
|
||||
status,
|
||||
allowClear = true,
|
||||
newStoreTemplate,
|
||||
showOptionsDropDown = true,
|
||||
} = props;
|
||||
const [visible, { on, off }] = useBoolean();
|
||||
const [inputValue, setInputValue] = useState(() => {
|
||||
@@ -118,6 +128,7 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
||||
|
||||
return (
|
||||
<Box className="ExpressionSetter">
|
||||
{/* 同时支持下拉框展示 */}
|
||||
<InputCode
|
||||
placeholder={placeholder}
|
||||
suffix={
|
||||
@@ -130,6 +141,26 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showOptionsDropDown && autoCompleteOptions?.length && (
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: autoCompleteOptions.map((option) => ({
|
||||
key: option,
|
||||
label: (
|
||||
<Text
|
||||
onClick={() => {
|
||||
change(option);
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
</Text>
|
||||
),
|
||||
})),
|
||||
}}
|
||||
>
|
||||
<Action tooltip="使用预设代码片段" icon={<MenuOutlined />} size="small" />
|
||||
</Dropdown>
|
||||
)}
|
||||
<Action
|
||||
tooltip="打开表达式变量选择面板"
|
||||
icon={<ExpandAltOutlined />}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ListSetter } from './list-setter';
|
||||
import { ModelSetter } from './model-setter';
|
||||
import { OptionSetter } from './option-setter';
|
||||
import { PickerSetter } from './picker-setter';
|
||||
import { RenderSetter, TableCellSetter, TableExpandableSetter } from './render-props-setter';
|
||||
import { RenderSetter, TableCellSetter, TableExpandableSetter } from './render-setter';
|
||||
import { RouterSetter } from './router-setter';
|
||||
import {
|
||||
SpacingSetter,
|
||||
@@ -43,6 +43,7 @@ const setters: IFormItemCreateOptions[] = [
|
||||
name: 'expSetter',
|
||||
alias: ['expressionSetter'],
|
||||
component: ExpressionSetter,
|
||||
disableVariableSetter: true,
|
||||
},
|
||||
{
|
||||
name: 'numberSetter',
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { isPlainString } from '@music163/tango-helpers';
|
||||
import { ActionSelect } from '@music163/tango-ui';
|
||||
import { ActionSelect, InputCode } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { Box } from 'coral-system';
|
||||
|
||||
const options = [
|
||||
{ label: '取消自定义', value: '' },
|
||||
@@ -25,26 +26,27 @@ interface JsxSetterProps extends FormItemComponentProps {
|
||||
}
|
||||
|
||||
const defaultGetTemplate = (key: string) => {
|
||||
let ret;
|
||||
const deps = [];
|
||||
let ret: [string, string[]];
|
||||
switch (key) {
|
||||
case 'Box':
|
||||
ret = '<Box></Box>';
|
||||
ret = ['<Box></Box>', ['Box']];
|
||||
break;
|
||||
case 'Text':
|
||||
ret = '<Text>示例文本</Text>';
|
||||
ret = ['<Text>示例文本</Text>', ['Text']];
|
||||
break;
|
||||
case 'Placeholder':
|
||||
ret = '<Placeholder text="拖拽到此进行替换" />';
|
||||
ret = ['<Placeholder text="拖拽到此进行替换" />', ['Placeholder']];
|
||||
break;
|
||||
case 'ButtonGroup':
|
||||
ret = '<ButtonGroup><Button>按钮1</Button><Button>按钮2</Button></ButtonGroup>';
|
||||
deps.push('Button');
|
||||
ret = [
|
||||
'<ButtonGroup><Button>按钮1</Button><Button>按钮2</Button></ButtonGroup>',
|
||||
['ButtonGroup', 'Button'],
|
||||
];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return [ret, deps];
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -53,20 +55,23 @@ const defaultGetTemplate = (key: string) => {
|
||||
export function JsxSetter(props: JsxSetterProps) {
|
||||
const { showInput, getTemplate = defaultGetTemplate, value, onChange } = props;
|
||||
return (
|
||||
<ActionSelect
|
||||
showInput={showInput}
|
||||
defaultInputValue={isPlainString(value) ? value : undefined}
|
||||
options={options}
|
||||
text="将此区域初始化为"
|
||||
onInputChange={onChange}
|
||||
onSelect={(key) => {
|
||||
const [tpl, deps] = getTemplate(key);
|
||||
if (tpl) {
|
||||
onChange(`{${tpl}}`, { relatedImports: [...deps, key] });
|
||||
} else {
|
||||
onChange(undefined);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<ActionSelect
|
||||
showInput={showInput}
|
||||
defaultInputValue={isPlainString(value) ? value : undefined}
|
||||
options={options}
|
||||
text="设置此区域为"
|
||||
onInputChange={onChange}
|
||||
onSelect={(key) => {
|
||||
const [tpl, deps] = getTemplate(key);
|
||||
if (tpl) {
|
||||
onChange(`{${tpl}}`, { relatedImports: deps });
|
||||
} else {
|
||||
onChange(undefined);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{value && <InputCode value={value} readOnly editable={false} />}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
+24
-3
@@ -1,6 +1,8 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { ActionSelect } from '@music163/tango-ui';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ActionSelect, InputCode } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { Box } from 'coral-system';
|
||||
import { value2expressionCode } from '@music163/tango-core';
|
||||
|
||||
interface IRenderOption {
|
||||
label: string;
|
||||
@@ -19,11 +21,19 @@ export interface RenderSetterProps {
|
||||
* Render Props Setters
|
||||
*/
|
||||
export function RenderSetter({
|
||||
value,
|
||||
onChange,
|
||||
text = '自定义渲染为',
|
||||
options = [],
|
||||
fallbackOption,
|
||||
}: FormItemComponentProps & RenderSetterProps) {
|
||||
const [inputValue, setInputValue] = useState(() => {
|
||||
return value2expressionCode(value);
|
||||
});
|
||||
useEffect(() => {
|
||||
setInputValue(value2expressionCode(value));
|
||||
}, [value]);
|
||||
|
||||
const optionsMap = useMemo(() => {
|
||||
return options.reduce((prev, cur) => {
|
||||
prev[cur.value] = cur;
|
||||
@@ -42,7 +52,18 @@ export function RenderSetter({
|
||||
},
|
||||
[optionsMap, fallbackOption, onChange],
|
||||
);
|
||||
return <ActionSelect text={text} options={options} onSelect={onSelect} />;
|
||||
return (
|
||||
<Box>
|
||||
<ActionSelect text={text} options={options} onSelect={onSelect} />
|
||||
{inputValue && (
|
||||
<InputCode
|
||||
value={inputValue}
|
||||
onChange={(val) => setInputValue(val)}
|
||||
onBlur={() => onChange(inputValue)}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const getRender = (content: string, type?: 'tableCell' | 'tableExpandable') => {
|
||||
@@ -91,6 +91,9 @@ export const SettingPanel = observer((props: SettingPanelProps) => {
|
||||
borderLeftColor="line2"
|
||||
bg="white"
|
||||
headerProps={headerProps}
|
||||
bodyProps={{
|
||||
overflowY: 'hidden',
|
||||
}}
|
||||
className="SettingPanel"
|
||||
>
|
||||
{prototype ? (
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useMemo, useState } from 'react';
|
||||
import { Box, Grid, Text } from 'coral-system';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
ComponentPrototypeType,
|
||||
IComponentPrototype,
|
||||
logger,
|
||||
PartialRecord,
|
||||
upperCamelCase,
|
||||
@@ -194,7 +194,7 @@ function MaterialList({ data, filterKeyword, type = 'common' }: MaterialListProp
|
||||
}
|
||||
|
||||
interface MaterialProps {
|
||||
data: ComponentPrototypeType;
|
||||
data: IComponentPrototype;
|
||||
}
|
||||
|
||||
const StyledCommonGridItem = styled.div`
|
||||
@@ -295,7 +295,7 @@ function MaterialGrid({ data }: MaterialProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function CommonMaterialInfoBox({ help, docs }: ComponentPrototypeType) {
|
||||
function CommonMaterialInfoBox({ help, docs }: IComponentPrototype) {
|
||||
return (
|
||||
<Box maxWidth={300}>
|
||||
{!!help && <Box mb="m">{help}</Box>}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
Popconfirm,
|
||||
Radio,
|
||||
message,
|
||||
Tag,
|
||||
Checkbox,
|
||||
ModalProps,
|
||||
} from 'antd';
|
||||
@@ -18,7 +17,7 @@ import { FormListProps } from 'antd/lib/form';
|
||||
import { Box, Text } from 'coral-system';
|
||||
import semverLt from 'semver/functions/lt';
|
||||
import semverValid from 'semver/functions/valid';
|
||||
import { ConfigGroup, ConfigItem } from '@music163/tango-ui';
|
||||
import { ColorTag, ConfigGroup, ConfigItem } from '@music163/tango-ui';
|
||||
import { MinusCircleOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { useBoolean } from '@music163/tango-helpers';
|
||||
import { isUndefined } from 'lodash-es';
|
||||
@@ -101,8 +100,8 @@ export const DependencyPanel = observer(
|
||||
/>
|
||||
<ConfigGroup
|
||||
title={
|
||||
<>
|
||||
基础包
|
||||
<div>
|
||||
基础包
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
@@ -115,7 +114,7 @@ export const DependencyPanel = observer(
|
||||
>
|
||||
<QuestionCircleOutlined />
|
||||
</Tooltip>
|
||||
</>
|
||||
</div>
|
||||
}
|
||||
key={DependencyItemType.基础包}
|
||||
>
|
||||
@@ -332,25 +331,19 @@ function RenderItem({
|
||||
const packageJsonIncluded = !!dependencies?.[name];
|
||||
const tangoConfigJsonIncluded = !!packageInfo;
|
||||
|
||||
return [
|
||||
hasUmd && (
|
||||
<Tooltip title="该依赖拥有 UMD 资源,可优化加载速度">
|
||||
<Tag key="hasUmd" color="green">
|
||||
return (
|
||||
<Box display="inline-block">
|
||||
{hasUmd && (
|
||||
<ColorTag key="hasUmd" color="green" tooltip="该依赖拥有 UMD 资源,可优化加载速度">
|
||||
UMD
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
),
|
||||
packageJsonIncluded && (
|
||||
<Tooltip title="此依赖在 package.json 中存在">
|
||||
<Tag key="packageJson">package</Tag>
|
||||
</Tooltip>
|
||||
),
|
||||
tangoConfigJsonIncluded && (
|
||||
<Tooltip title="此依赖在 tango.config.json 中存在">
|
||||
<Tag key="tangoConfigJson">tango.config</Tag>
|
||||
</Tooltip>
|
||||
),
|
||||
].filter((e) => e);
|
||||
</ColorTag>
|
||||
)}
|
||||
{packageJsonIncluded && <ColorTag tooltip="此依赖在 package.json 中存在">package</ColorTag>}
|
||||
{tangoConfigJsonIncluded && (
|
||||
<ColorTag tooltip="此依赖在 tango.config.json 中存在">tango.config</ColorTag>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}, [showTags, workspace, record]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -172,12 +172,7 @@ export const ComponentsTree: React.FC<ComponentsTreeProps> = observer(
|
||||
const slotKey = keys?.[0] as string;
|
||||
const data = sandboxQuery.getDraggableParentsData(buildQueryBySlotId(slotKey), true);
|
||||
if (data && data.id) {
|
||||
workspace.selectSource.select({
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
bounding: data.bounding,
|
||||
parents: data.parents,
|
||||
});
|
||||
workspace.selectSource.select(data);
|
||||
}
|
||||
// export selected
|
||||
onSelect(slotKey);
|
||||
|
||||
@@ -23,11 +23,7 @@ export function OutlinePanel({ showStateView = true, ...treeProps }: OutlineView
|
||||
>
|
||||
<ComponentsTree {...treeProps} />
|
||||
</CollapsePanel>
|
||||
{showStateView && (
|
||||
<CollapsePanel title="页面状态" stickyHeader maxHeight="90%" overflowY="auto">
|
||||
<StateTree />
|
||||
</CollapsePanel>
|
||||
)}
|
||||
{showStateView && <StateTree />}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useEffect, useReducer } from 'react';
|
||||
import { observer } from '@music163/tango-context';
|
||||
import { isFunction, isObject, pick } from '@music163/tango-helpers';
|
||||
import { JsonView } from '@music163/tango-ui';
|
||||
import { CollapsePanel, IconButton, JsonView } from '@music163/tango-ui';
|
||||
import { ReloadOutlined } from '@ant-design/icons';
|
||||
import { useSandboxQuery } from '../../context';
|
||||
|
||||
function processTango(obj: any = {}, index = 0) {
|
||||
@@ -26,7 +27,7 @@ export const StateTree = observer(() => {
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
const tangoContext = pick(sandboxQuery.window['tango'] || {}, [
|
||||
'stores',
|
||||
'page',
|
||||
'pageStore',
|
||||
'services',
|
||||
'config',
|
||||
]);
|
||||
@@ -34,6 +35,7 @@ export const StateTree = observer(() => {
|
||||
forceUpdate();
|
||||
};
|
||||
useEffect(() => {
|
||||
// FIXME: 优化这段代码,应该监听 tangoContext 的变化,来刷新
|
||||
const settingForm = document.querySelector('.SettingPanel');
|
||||
const ob = new MutationObserver(callback);
|
||||
if (settingForm) {
|
||||
@@ -48,13 +50,31 @@ export const StateTree = observer(() => {
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<JsonView
|
||||
src={processTango(tangoContext)}
|
||||
collapsed={1}
|
||||
name="tango"
|
||||
displayObjectSize={false}
|
||||
indentWidth={2}
|
||||
enableCopy
|
||||
/>
|
||||
<CollapsePanel
|
||||
title="页面状态"
|
||||
stickyHeader
|
||||
maxHeight="90%"
|
||||
overflowY="auto"
|
||||
extra={
|
||||
<IconButton
|
||||
tooltip="同步状态"
|
||||
size="small"
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
forceUpdate();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<JsonView
|
||||
src={processTango(tangoContext)}
|
||||
collapsed={1}
|
||||
name="tango"
|
||||
displayObjectSize={false}
|
||||
indentWidth={2}
|
||||
enableCopy
|
||||
/>
|
||||
</CollapsePanel>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import styled, { css, keyframes } from 'styled-components';
|
||||
import { Box, Button, Group, HTMLCoralProps } from 'coral-system';
|
||||
import { Dropdown, DropdownProps } from 'antd';
|
||||
import { PlusSquareOutlined, HolderOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Dropdown, DropdownProps, Tooltip } from 'antd';
|
||||
import { HolderOutlined, InfoCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { ISelectedItemData, isString, noop } from '@music163/tango-helpers';
|
||||
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||
import { Action, IconFont } from '@music163/tango-ui';
|
||||
import { IconFont } from '@music163/tango-ui';
|
||||
import { getDragGhostElement } from '../helpers';
|
||||
import { getWidget } from '../widgets';
|
||||
|
||||
@@ -59,6 +59,26 @@ const selectionBoxStyle = css`
|
||||
top: 0;
|
||||
`;
|
||||
|
||||
const topAddSiblingBtnStyle = css`
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
top: 0;
|
||||
border-radius: 32px;
|
||||
z-index: 999;
|
||||
pointer-events: auto;
|
||||
`;
|
||||
|
||||
const bottomAddSiblingBtnStyle = css`
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 50%);
|
||||
bottom: 0;
|
||||
border-radius: 32px;
|
||||
z-index: 999;
|
||||
pointer-events: auto;
|
||||
`;
|
||||
|
||||
interface IInsertedData {
|
||||
name: string;
|
||||
label: string;
|
||||
@@ -114,6 +134,19 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
||||
});
|
||||
}
|
||||
|
||||
let siblingList: IInsertedData[] = [];
|
||||
if (prototype?.siblingNames) {
|
||||
siblingList = prototype.siblingNames?.map((item) => {
|
||||
const proto = workspace.componentPrototypes.get(item);
|
||||
return {
|
||||
name: item,
|
||||
label: proto?.title || item,
|
||||
icon: proto?.icon,
|
||||
description: proto?.help,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let selectionHelpersAlign: SelectionHelperAlignType = 'top-right';
|
||||
if (data.bounding) {
|
||||
if (data.bounding.left + data.bounding.width + boundingOffset < designer.viewport.width) {
|
||||
@@ -150,6 +183,32 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
||||
css={selectionBoxStyle}
|
||||
style={style}
|
||||
>
|
||||
{siblingList.length > 0 ? (
|
||||
<>
|
||||
<InsertedDropdown
|
||||
title="在当前节点的前方添加兄弟节点"
|
||||
options={siblingList}
|
||||
onSelect={(name) => {
|
||||
workspace.insertBeforeSelectedNode(name);
|
||||
}}
|
||||
>
|
||||
<Tooltip title="在当前节点的前方添加兄弟节点">
|
||||
<SelectionHelper icon={<PlusOutlined />} css={topAddSiblingBtnStyle} />
|
||||
</Tooltip>
|
||||
</InsertedDropdown>
|
||||
<InsertedDropdown
|
||||
title="在当前节点的后方添加兄弟节点"
|
||||
options={siblingList}
|
||||
onSelect={(name) => {
|
||||
workspace.insertAfterSelectedNode(name);
|
||||
}}
|
||||
>
|
||||
<Tooltip title="在当前节点的后方添加兄弟节点">
|
||||
<SelectionHelper icon={<PlusOutlined />} css={bottomAddSiblingBtnStyle} />
|
||||
</Tooltip>
|
||||
</InsertedDropdown>
|
||||
</>
|
||||
) : null}
|
||||
{showActions && (
|
||||
<SelectionHelpers align={selectionHelpersAlign}>
|
||||
<SelectionHelper
|
||||
@@ -179,13 +238,9 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
||||
}}
|
||||
/>
|
||||
{!isFromCurrentFile && (
|
||||
<Action
|
||||
as="span"
|
||||
color="white"
|
||||
tooltip={`该节点来自其他文件(${data.filename})`}
|
||||
icon={<InfoCircleOutlined />}
|
||||
size="small"
|
||||
/>
|
||||
<Tooltip title={`该节点来自其他文件(${data.filename})`}>
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
@@ -198,7 +253,9 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
||||
workspace.insertToSelectedNode(name);
|
||||
}}
|
||||
>
|
||||
<SelectionHelper icon={<PlusSquareOutlined />} />
|
||||
<Tooltip title="快捷添加子元素">
|
||||
<SelectionHelper icon={<PlusOutlined />} />
|
||||
</Tooltip>
|
||||
</InsertedDropdown>
|
||||
)}
|
||||
</SelectionHelpers>
|
||||
@@ -248,10 +305,6 @@ const selectionHelperStyle = css`
|
||||
cursor: pointer;
|
||||
background: var(--color-active, var(--tango-colors-brand));
|
||||
|
||||
svg {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--color-active-hover, var(--tango-colors-primary-50));
|
||||
}
|
||||
@@ -262,7 +315,13 @@ interface SelectionHelperProps extends HTMLCoralProps<'button'> {
|
||||
label?: React.ReactNode;
|
||||
}
|
||||
|
||||
const SelectionHelper = ({ icon, label, children, ...rest }: SelectionHelperProps) => {
|
||||
const SelectionHelper = ({
|
||||
icon,
|
||||
label,
|
||||
children,
|
||||
css: customCss,
|
||||
...rest
|
||||
}: SelectionHelperProps) => {
|
||||
return (
|
||||
<Button
|
||||
position="relative"
|
||||
@@ -272,7 +331,7 @@ const SelectionHelper = ({ icon, label, children, ...rest }: SelectionHelperProp
|
||||
border="0"
|
||||
borderRadius="s"
|
||||
fontSize="12px"
|
||||
css={selectionHelperStyle}
|
||||
css={[selectionHelperStyle, customCss]}
|
||||
{...rest}
|
||||
>
|
||||
{icon}
|
||||
@@ -372,11 +431,17 @@ const NameSelector = ({ label, parents = [], onSelect = noop }: NameSelectorProp
|
||||
};
|
||||
|
||||
interface InsertedDropdownProps extends DropdownProps {
|
||||
title?: string;
|
||||
options?: IInsertedData[];
|
||||
onSelect?: (name: string) => void;
|
||||
}
|
||||
|
||||
function InsertedDropdown({ options = [], onSelect, ...props }: InsertedDropdownProps) {
|
||||
function InsertedDropdown({
|
||||
title = '为当前节点添加子元素',
|
||||
options = [],
|
||||
onSelect,
|
||||
...props
|
||||
}: InsertedDropdownProps) {
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
@@ -389,17 +454,18 @@ function InsertedDropdown({ options = [], onSelect, ...props }: InsertedDropdown
|
||||
border="solid"
|
||||
borderColor="line2"
|
||||
overflow="hidden"
|
||||
width="320px"
|
||||
>
|
||||
<Box px="l" py="m" color="text2">
|
||||
为当前节点添加子元素
|
||||
{title}
|
||||
</Box>
|
||||
<Box>
|
||||
<Box maxHeight={360} overflowY="auto">
|
||||
{options.map((item) => (
|
||||
<InsertedItem
|
||||
key={item.name}
|
||||
label={item.label}
|
||||
icon={item.icon}
|
||||
description={item.description}
|
||||
description={item.description || '暂无组件描述'}
|
||||
onClick={() => onSelect?.(item.name)}
|
||||
/>
|
||||
))}
|
||||
@@ -414,6 +480,7 @@ function InsertedDropdown({ options = [], onSelect, ...props }: InsertedDropdown
|
||||
|
||||
const insertedItemStyle = css`
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tango-colors-fill2);
|
||||
@@ -422,11 +489,29 @@ const insertedItemStyle = css`
|
||||
|
||||
function InsertedItem({
|
||||
label,
|
||||
icon = 'icon-placeholder',
|
||||
icon,
|
||||
description,
|
||||
...rest
|
||||
}: HTMLCoralProps<'div'> & Omit<IInsertedData, 'name'>) {
|
||||
let iconNode;
|
||||
if (!icon) {
|
||||
iconNode = <IconFont className="material-icon" type="icon-placeholder" />;
|
||||
} else if (icon.startsWith('icon-')) {
|
||||
iconNode = <IconFont className="material-icon" type={icon} />;
|
||||
} else {
|
||||
iconNode = <img src={icon} alt={label} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box display="flex" columnGap="m" px="l" py="m" fontSize="12px" css={insertedItemStyle}>
|
||||
<Box
|
||||
display="flex"
|
||||
columnGap="m"
|
||||
px="l"
|
||||
py="m"
|
||||
fontSize="12px"
|
||||
css={insertedItemStyle}
|
||||
{...rest}
|
||||
>
|
||||
<Box
|
||||
size="35px"
|
||||
fontSize="32px"
|
||||
@@ -437,11 +522,7 @@ function InsertedItem({
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
{icon.startsWith('icon-') ? (
|
||||
<IconFont className="material-icon" type={icon} />
|
||||
) : (
|
||||
<img src={icon} alt={label} />
|
||||
)}
|
||||
{iconNode}
|
||||
</Box>
|
||||
<Box>
|
||||
<Box fontWeight="500">{label}</Box>
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Group } from 'coral-system';
|
||||
import { Modal } from 'antd';
|
||||
import { ToggleButton, CodeOutlined } from '@music163/tango-ui';
|
||||
import { ToggleButton, CodeOutlined, DualOutlined } from '@music163/tango-ui';
|
||||
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||
import { BorderOutlined } from '@ant-design/icons';
|
||||
|
||||
export const ModeSwitchTool = observer(() => {
|
||||
const workspace = useWorkspace();
|
||||
const designer = useDesigner();
|
||||
|
||||
const activeFileCheck = useCallback(() => {
|
||||
if (workspace.activeFile !== workspace.activeViewFile) {
|
||||
Modal.confirm({
|
||||
title: '当前打开的文件与视图不匹配,是否切换到当前视图对应的文件?',
|
||||
onOk: () => {
|
||||
workspace.setActiveFile(workspace.activeViewFile);
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [workspace]);
|
||||
|
||||
return (
|
||||
<Group attached>
|
||||
<ToggleButton
|
||||
@@ -26,19 +38,24 @@ export const ModeSwitchTool = observer(() => {
|
||||
onClick={() => {
|
||||
designer.setActiveView('code'); // 切换到源码视图
|
||||
designer.setActiveSidebarPanel(''); // 关闭左侧面板
|
||||
if (workspace.activeFile !== workspace.activeViewFile) {
|
||||
Modal.confirm({
|
||||
title: '当前打开的文件与视图不匹配,是否切换到当前视图对应的文件?',
|
||||
onOk: () => {
|
||||
workspace.setActiveFile(workspace.activeViewFile);
|
||||
},
|
||||
});
|
||||
}
|
||||
activeFileCheck();
|
||||
}}
|
||||
tooltip="源码视图"
|
||||
>
|
||||
<CodeOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
shape="ghost"
|
||||
selected={designer.activeView === 'dual'}
|
||||
onClick={() => {
|
||||
designer.setActiveView('dual'); // 切换到双屏视图
|
||||
designer.setActiveSidebarPanel(''); // 关闭左侧面板
|
||||
activeFileCheck();
|
||||
}}
|
||||
tooltip="双屏视图"
|
||||
>
|
||||
<DualOutlined />
|
||||
</ToggleButton>
|
||||
</Group>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ export const PreviewTool = observer(() => {
|
||||
onClick={() => {
|
||||
const nextIsPreview = !designer.isPreview;
|
||||
designer.toggleIsPreview(nextIsPreview);
|
||||
if (nextIsPreview) {
|
||||
if (nextIsPreview && designer.activeView === 'code') {
|
||||
designer.setActiveView('design');
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -9,7 +9,6 @@ export function WorkspacePanel({ children }: WorkspacePanelProps) {
|
||||
<Box
|
||||
flex="1"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
overflow="hidden"
|
||||
bg="colors.custom.viewportBg"
|
||||
position="relative"
|
||||
|
||||
@@ -21,7 +21,7 @@ export const WorkspaceView = observer((props: WorkspaceViewProps) => {
|
||||
return (
|
||||
<Box
|
||||
className={`ViewPanel ${mode}`}
|
||||
display={display}
|
||||
display={designer.activeView === 'dual' ? 'block' : display}
|
||||
flex="1"
|
||||
overflow={overflow}
|
||||
position="relative"
|
||||
|
||||
@@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.0.0-alpha.7](https://github.com/netease/tango/compare/@music163/tango-helpers@1.0.0-alpha.6...@music163/tango-helpers@1.0.0-alpha.7) (2024-04-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- allow reload state tree & display pageStore in variable tree ([#135](https://github.com/netease/tango/issues/135)) ([2664613](https://github.com/netease/tango/commit/2664613ab263aead2a5239fa012454fc7fd5ff99))
|
||||
|
||||
# [1.0.0-alpha.6](https://github.com/netease/tango/compare/@music163/tango-helpers@1.0.0-alpha.5...@music163/tango-helpers@1.0.0-alpha.6) (2024-04-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- check is in design mode ([cb6aabf](https://github.com/netease/tango/commit/cb6aabf6ce45a33ba9055c45734f7db0fba540ec))
|
||||
- update setters and use tabOptions to filter props ([#129](https://github.com/netease/tango/issues/129)) ([93608d1](https://github.com/netease/tango/commit/93608d1037327afa4f755976b86427b6128ae3d0))
|
||||
|
||||
# [1.0.0-alpha.5](https://github.com/netease/tango/compare/@music163/tango-helpers@1.0.0-alpha.4...@music163/tango-helpers@1.0.0-alpha.5) (2024-03-26)
|
||||
|
||||
### Features
|
||||
|
||||
- quick add sibling nodes ([#127](https://github.com/netease/tango/issues/127)) ([9ed6a7d](https://github.com/netease/tango/commit/9ed6a7d1a4944d69d96e034f243b61531862e317))
|
||||
|
||||
# [1.0.0-alpha.4](https://github.com/netease/tango/compare/@music163/tango-helpers@1.0.0-alpha.3...@music163/tango-helpers@1.0.0-alpha.4) (2024-03-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-helpers",
|
||||
"version": "1.0.0-alpha.4",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared types, helpers, and hooks of tango-apps",
|
||||
"keywords": [
|
||||
"shared",
|
||||
|
||||
@@ -50,7 +50,7 @@ export function isNil(val: any) {
|
||||
* @returns
|
||||
*/
|
||||
export function isStoreVariablePath(key: string) {
|
||||
return /^stores\.[a-zA-Z0-9]+\.\w+$/.test(key);
|
||||
return key === 'pageStore' || /^stores\.[a-zA-Z0-9]+\.\w+$/.test(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,5 +66,5 @@ export function isServiceVariablePath(key: string) {
|
||||
* 是否在 Tango 设计模式
|
||||
*/
|
||||
export function isInTangoDesignMode() {
|
||||
return '__TANGO_DESIGNER__' in window;
|
||||
return !!(window as any).__TANGO_DESIGNER__;
|
||||
}
|
||||
|
||||
@@ -132,8 +132,3 @@ export interface ISelectedItemData {
|
||||
element?: HTMLElement;
|
||||
parents?: ISelectedItemData[];
|
||||
}
|
||||
|
||||
export type SetterOnChangeDetailType = {
|
||||
relatedImports?: string[];
|
||||
isRawCode?: boolean;
|
||||
};
|
||||
|
||||
@@ -4,10 +4,34 @@
|
||||
|
||||
import { OptionType } from './advanced';
|
||||
|
||||
/**
|
||||
* @deprecated 请使用 IComponentProp 代替
|
||||
*/
|
||||
export type ComponentPropType<T = any> = IComponentProp<T>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export type ComponentDndRulesType = IComponentDndRules;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export type ComponentPrototypeType = IComponentPrototype;
|
||||
|
||||
/**
|
||||
* 组件校验规则
|
||||
*/
|
||||
export type ComponentPropValidate = (
|
||||
value: any,
|
||||
field: any,
|
||||
trigger: string,
|
||||
) => string | Promise<string>;
|
||||
|
||||
/**
|
||||
* 组件属性类型
|
||||
*/
|
||||
export type ComponentPropType<T = any> = {
|
||||
export interface IComponentProp<T = any> {
|
||||
/**
|
||||
* 属性名
|
||||
*/
|
||||
@@ -23,10 +47,10 @@ export type ComponentPropType<T = any> = {
|
||||
/**
|
||||
* 属性的分组
|
||||
* - basic 常用属性
|
||||
* - event 事件属性
|
||||
* - data 数据属性
|
||||
* - style 样式属性
|
||||
* - advanced 高级属性
|
||||
* - event 事件属性
|
||||
* - data 数据属性
|
||||
*/
|
||||
group?: 'basic' | 'event' | 'style' | 'data' | 'advanced';
|
||||
/**
|
||||
@@ -50,7 +74,7 @@ export type ComponentPropType<T = any> = {
|
||||
/**
|
||||
* 如果是对象属性,这里声明子属性列表
|
||||
*/
|
||||
props?: ComponentPropType[];
|
||||
props?: IComponentProp[];
|
||||
/**
|
||||
* 输入提示
|
||||
*/
|
||||
@@ -79,7 +103,7 @@ export type ComponentPropType<T = any> = {
|
||||
/**
|
||||
* 动态设置属性,覆盖已有的 prop 对象
|
||||
*/
|
||||
getProp?: (form: any) => Partial<Omit<ComponentPropType<T>, 'getProp'>>;
|
||||
getProp?: (form: any) => Partial<Omit<IComponentProp<T>, 'getProp'>>;
|
||||
/**
|
||||
* 配置项的可选值,setterProps.options 的简写
|
||||
*/
|
||||
@@ -92,16 +116,20 @@ export type ComponentPropType<T = any> = {
|
||||
* 动态设置表单项是否展示
|
||||
*/
|
||||
getVisible?: (form: any) => boolean;
|
||||
/**
|
||||
* 表单值校验逻辑
|
||||
*/
|
||||
validate?: ComponentPropValidate;
|
||||
/**
|
||||
* 标记属性是否已废弃
|
||||
*/
|
||||
deprecated?: boolean | string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件拖拽规则类型
|
||||
*/
|
||||
export type ComponentDndRulesType = {
|
||||
export interface IComponentDndRules {
|
||||
/**
|
||||
* 当前节点是否可以被拖拽
|
||||
*/
|
||||
@@ -122,12 +150,12 @@ export type ComponentDndRulesType = {
|
||||
* 子节点的容器选择器,用于快速定位子节点容器,适合组件存在多个可搭建区域时使用
|
||||
*/
|
||||
childrenContainerSelector?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件原型类型
|
||||
*/
|
||||
export type ComponentPrototypeType = {
|
||||
export interface IComponentPrototype {
|
||||
/**
|
||||
* 组件名
|
||||
* @example Button
|
||||
@@ -233,20 +261,24 @@ export type ComponentPrototypeType = {
|
||||
* 直接子节点的类型
|
||||
*/
|
||||
childrenName?: string | string[];
|
||||
/**
|
||||
* 兄弟节点的类型
|
||||
*/
|
||||
siblingNames?: string[];
|
||||
/**
|
||||
* 组件的可配置属性集
|
||||
*/
|
||||
props?: ComponentPropType[];
|
||||
props?: IComponentProp[];
|
||||
/**
|
||||
* 组件的拖拽规则
|
||||
*/
|
||||
rules?: ComponentDndRulesType;
|
||||
rules?: IComponentDndRules;
|
||||
/**
|
||||
* 用法
|
||||
* @example import { Button } from 'antd';
|
||||
*/
|
||||
usage?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TangoConfigJson 类型配置
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.1](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.0...@music163/tango-sandbox@1.0.1) (2024-04-22)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-sandbox
|
||||
|
||||
# [1.0.0-alpha.10](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.0-alpha.9...@music163/tango-sandbox@1.0.0-alpha.10) (2024-04-09)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-sandbox
|
||||
|
||||
# [1.0.0-alpha.9](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.0-alpha.8...@music163/tango-sandbox@1.0.0-alpha.9) (2024-03-26)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-sandbox
|
||||
|
||||
# [1.0.0-alpha.8](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.0-alpha.7...@music163/tango-sandbox@1.0.0-alpha.8) (2024-03-18)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-sandbox
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-sandbox",
|
||||
"version": "1.0.0-alpha.8",
|
||||
"version": "1.0.2",
|
||||
"description": "sandbox of tango apps",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"homepage": "",
|
||||
@@ -29,8 +29,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/tango-core": "^1.0.0-alpha.8",
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.4",
|
||||
"@music163/tango-core": "^1.0.2",
|
||||
"@music163/tango-helpers": "^1.0.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"lodash.isequal": "4.5.0",
|
||||
"react-frame-component": "^5.2.4"
|
||||
|
||||
@@ -3,6 +3,31 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.2](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.0.1...@music163/tango-setting-form@1.0.2) (2024-04-22)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-setting-form
|
||||
|
||||
## [1.0.1](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.0.0...@music163/tango-setting-form@1.0.1) (2024-04-16)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-setting-form
|
||||
|
||||
# [1.0.0-alpha.14](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.0.0-alpha.13...@music163/tango-setting-form@1.0.0-alpha.14) (2024-04-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- update setters and use tabOptions to filter props ([#129](https://github.com/netease/tango/issues/129)) ([93608d1](https://github.com/netease/tango/commit/93608d1037327afa4f755976b86427b6128ae3d0))
|
||||
- update SettingForm style ([#125](https://github.com/netease/tango/issues/125)) ([3f42516](https://github.com/netease/tango/commit/3f42516816968888554f719332d3cef7d8576aa8))
|
||||
|
||||
# [1.0.0-alpha.13](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.0.0-alpha.12...@music163/tango-setting-form@1.0.0-alpha.13) (2024-03-26)
|
||||
|
||||
### Features
|
||||
|
||||
- quick add sibling nodes ([#127](https://github.com/netease/tango/issues/127)) ([9ed6a7d](https://github.com/netease/tango/commit/9ed6a7d1a4944d69d96e034f243b61531862e317))
|
||||
|
||||
# [1.0.0-alpha.12](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.0.0-alpha.11...@music163/tango-setting-form@1.0.0-alpha.12) (2024-03-19)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-setting-form
|
||||
|
||||
# [1.0.0-alpha.11](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.0.0-alpha.10...@music163/tango-setting-form@1.0.0-alpha.11) (2024-03-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-setting-form",
|
||||
"version": "1.0.0-alpha.11",
|
||||
"version": "1.1.0",
|
||||
"description": "setting form of tango-apps",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"homepage": "",
|
||||
@@ -28,9 +28,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/tango-core": "^1.0.0-alpha.8",
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.4",
|
||||
"@music163/tango-ui": "^1.0.0-alpha.9",
|
||||
"@music163/tango-core": "^1.0.2",
|
||||
"@music163/tango-helpers": "^1.0.0",
|
||||
"@music163/tango-ui": "^1.0.3",
|
||||
"antd": "^4.24.2",
|
||||
"coral-system": "^1.0.5",
|
||||
"mobx": "6.12.0",
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import React from 'react';
|
||||
import { toJS } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import {
|
||||
clone,
|
||||
ComponentPropType,
|
||||
SetterOnChangeDetailType,
|
||||
useBoolean,
|
||||
} from '@music163/tango-helpers';
|
||||
import { clone, ComponentPropValidate, IComponentProp, useBoolean } from '@music163/tango-helpers';
|
||||
import { isWrappedByExpressionContainer } from '@music163/tango-core';
|
||||
import { ToggleButton, CodeOutlined } from '@music163/tango-ui';
|
||||
import { InputProps } from 'antd';
|
||||
import { useFormModel, useFormVariable } from './context';
|
||||
import { FormControl } from './form-ui';
|
||||
import { Box, Text } from 'coral-system';
|
||||
import { ISetterOnChangeCallbackDetail } from './types';
|
||||
|
||||
export interface FormItemProps extends ComponentPropType {
|
||||
export interface FormItemProps extends IComponentProp {
|
||||
/**
|
||||
* 无样式模式,仅返回 setter 组件
|
||||
*/
|
||||
@@ -31,7 +27,7 @@ export interface FormItemProps extends ComponentPropType {
|
||||
|
||||
export interface FormItemComponentProps<T = any> {
|
||||
value?: T;
|
||||
onChange: (value: T, detail?: SetterOnChangeDetailType) => void;
|
||||
onChange: (value: T, detail?: ISetterOnChangeCallbackDetail) => void;
|
||||
readOnly?: boolean;
|
||||
disabled?: boolean;
|
||||
status?: InputProps['status'];
|
||||
@@ -62,7 +58,7 @@ export interface IFormItemCreateOptions {
|
||||
/**
|
||||
* 默认的表单值校验逻辑
|
||||
*/
|
||||
validate?: (value: string) => string | Promise<any>;
|
||||
validate?: ComponentPropValidate;
|
||||
}
|
||||
|
||||
const defaultGetSetterProps = () => ({});
|
||||
@@ -90,12 +86,13 @@ export function createFormItem(options: IFormItemCreateOptions) {
|
||||
extra,
|
||||
footer,
|
||||
noStyle,
|
||||
validate,
|
||||
}: FormItemProps) {
|
||||
const { disableSwitchExpressionSetter, showItemSubtitle } = useFormVariable();
|
||||
const model = useFormModel();
|
||||
const field = model.getField(name);
|
||||
const value = toJS(field.value ?? defaultValue);
|
||||
const disableVariableSetter = disableSwitchExpressionSetter ?? disableVariableSetterProp;
|
||||
const disableVariableSetter = disableSwitchExpressionSetter ?? disableVariableSetterProp; // Form 的设置优先
|
||||
const [isVariable, { toggle: toggleIsVariable }] = useBoolean(
|
||||
() => !disableVariableSetter && isWrappedByExpressionContainer(value),
|
||||
);
|
||||
@@ -103,7 +100,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
|
||||
const setterName = isVariable ? 'expressionSetter' : setter;
|
||||
|
||||
field.setConfig({
|
||||
validate: options.validate,
|
||||
validate: validate || options.validate,
|
||||
});
|
||||
|
||||
const baseComponentProps = clone(
|
||||
@@ -170,7 +167,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
|
||||
<ToggleButton
|
||||
borderRadius="s"
|
||||
size="s"
|
||||
shape="outline"
|
||||
shape="text"
|
||||
type="primary"
|
||||
tooltip={isVariable ? '关闭 JS 表达式' : '使用 JS 表达式'}
|
||||
tooltipPlacement="left"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { makeObservable, observable, computed, action, toJS } from 'mobx';
|
||||
import { SetterOnChangeDetailType } from '@music163/tango-helpers';
|
||||
import { observableGetIn, observableSetIn } from './helpers';
|
||||
import { ISetterOnChangeCallbackDetail } from './types';
|
||||
|
||||
export interface IFormModel {
|
||||
values: any;
|
||||
@@ -171,7 +171,7 @@ export class Field {
|
||||
|
||||
state: FieldStateType = {};
|
||||
|
||||
detail: SetterOnChangeDetailType = {};
|
||||
detail: ISetterOnChangeCallbackDetail = {};
|
||||
|
||||
get value() {
|
||||
return this.parent.getValue(this.name);
|
||||
@@ -234,11 +234,11 @@ export class Field {
|
||||
this.state.validating = false;
|
||||
this.state.error = error;
|
||||
|
||||
// 校验错误,不触发 onChange
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
// 校验错误,不触发 onChange
|
||||
this.parent?.onChange?.(this.name, this.value, this);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { ComponentPropType } from '@music163/tango-helpers';
|
||||
import { IComponentProp } from '@music163/tango-helpers';
|
||||
import { SettingFormItem } from './form-item';
|
||||
import { FormModelProvider, useFormModel } from './context';
|
||||
import { FormControlGroup } from './form-ui';
|
||||
import { isValidNestProps } from './helpers';
|
||||
|
||||
export type SettingFormObjectProps = ComponentPropType;
|
||||
export type SettingFormObjectProps = IComponentProp;
|
||||
|
||||
const defaultGetVisible = () => true;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { css, Box, HTMLCoralProps, Link } from 'coral-system';
|
||||
import { Checkbox, Tooltip } from 'antd';
|
||||
import { CollapsePanel } from '@music163/tango-ui';
|
||||
import { isNil, isString } from '@music163/tango-helpers';
|
||||
import { isString } from '@music163/tango-helpers';
|
||||
import { WarningOutlined } from '@ant-design/icons';
|
||||
|
||||
export interface FormControlProps extends Omit<FormLabelProps, 'type'> {
|
||||
@@ -62,10 +62,6 @@ export interface FormControlGroupProps extends FormLabelProps {
|
||||
* 是否勾选
|
||||
*/
|
||||
checked?: boolean;
|
||||
/**
|
||||
* checkbox 默认是否勾选
|
||||
*/
|
||||
defaultChecked?: boolean;
|
||||
}
|
||||
|
||||
export function FormControlGroup({
|
||||
@@ -78,14 +74,15 @@ export function FormControlGroup({
|
||||
children,
|
||||
onCheck,
|
||||
checked,
|
||||
defaultChecked = false,
|
||||
}: FormControlGroupProps) {
|
||||
const [collapsed, setCollapsed] = useState(isNil(checked) ? !defaultChecked : !checked);
|
||||
// 默认折叠
|
||||
const [collapsed, setCollapsed] = useState(true);
|
||||
return (
|
||||
<CollapsePanel
|
||||
collapsed={collapsed}
|
||||
onCollapse={setCollapsed}
|
||||
className="FormControlGroup"
|
||||
stickyHeader
|
||||
title={
|
||||
<Box display="flex" columnGap="s">
|
||||
<Checkbox
|
||||
@@ -97,7 +94,7 @@ export function FormControlGroup({
|
||||
onCheck?.(nextChecked);
|
||||
}}
|
||||
/>
|
||||
<FormLabel type="secondary" label={label} note={note} tip={tip} docs={docs} />
|
||||
<FormLabel label={label} note={note} tip={tip} docs={docs} />
|
||||
</Box>
|
||||
}
|
||||
extra={
|
||||
@@ -109,6 +106,7 @@ export function FormControlGroup({
|
||||
borderColor="line.normal"
|
||||
headerProps={{
|
||||
bg: 'fill1',
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@@ -150,11 +148,6 @@ const tipStyle = css`
|
||||
`;
|
||||
|
||||
interface FormLabelProps extends HTMLCoralProps<'div'> {
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
type?: 'normal' | 'secondary';
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
@@ -178,15 +171,7 @@ interface FormLabelProps extends HTMLCoralProps<'div'> {
|
||||
deprecated?: boolean | string;
|
||||
}
|
||||
|
||||
function FormLabel({
|
||||
type = 'normal',
|
||||
label,
|
||||
note,
|
||||
tip,
|
||||
docs,
|
||||
deprecated,
|
||||
...rest
|
||||
}: FormLabelProps) {
|
||||
function FormLabel({ label, note, tip, docs, deprecated, ...rest }: FormLabelProps) {
|
||||
let help: React.ReactNode;
|
||||
if (deprecated || docs) {
|
||||
help = (
|
||||
@@ -211,16 +196,11 @@ function FormLabel({
|
||||
help = tip;
|
||||
}
|
||||
|
||||
const labelColor = {
|
||||
normal: 'text.body',
|
||||
secondary: 'text.note',
|
||||
}[type];
|
||||
|
||||
let labelNode = (
|
||||
<Box
|
||||
as="span"
|
||||
display="inline-block"
|
||||
color={labelColor}
|
||||
color="text2"
|
||||
className={help ? 'hasHelp' : ''}
|
||||
css={labelStyle}
|
||||
title={isString(label) ? label : undefined}
|
||||
@@ -238,7 +218,7 @@ function FormLabel({
|
||||
<Box fontSize="14px" {...rest}>
|
||||
{labelNode}
|
||||
{note && (
|
||||
<Box as="i" display="inline-block" fontSize="12px" color="text.note" ml="m">
|
||||
<Box as="i" display="inline-block" fontSize="12px" color="text3" ml="m">
|
||||
{note}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -2,8 +2,8 @@ import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import { Box, css } from 'coral-system';
|
||||
import { Empty } from 'antd';
|
||||
import {
|
||||
ComponentPrototypeType,
|
||||
ComponentPropType,
|
||||
IComponentPrototype,
|
||||
IComponentProp,
|
||||
noop,
|
||||
filterTreeData,
|
||||
mapTreeData,
|
||||
@@ -20,20 +20,24 @@ import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
registerBuiltinSetters();
|
||||
|
||||
function normalizeComponentProps(props: ComponentPrototypeType['props']) {
|
||||
const groups: Record<string, ComponentPropType[]> = {};
|
||||
function normalizeComponentProps(
|
||||
props: IComponentPrototype['props'],
|
||||
groupOptions: IFormTabsGroupOption[],
|
||||
) {
|
||||
const groups: Record<string, IComponentProp[]> = groupOptions.reduce((prev, cur) => {
|
||||
prev[cur.value] = [];
|
||||
return prev;
|
||||
}, {});
|
||||
|
||||
props.forEach((prop) => {
|
||||
const group = prop.group || 'basic';
|
||||
if (!groups[group]) {
|
||||
groups[group] = [];
|
||||
}
|
||||
const group = groups[prop.group] ? prop.group : 'basic';
|
||||
groups[group].push(prop);
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
function filterComponentProps(props: ComponentPrototypeType['props'], keyword: string) {
|
||||
function filterComponentProps(props: IComponentPrototype['props'], keyword: string) {
|
||||
if (!keyword) {
|
||||
return props;
|
||||
}
|
||||
@@ -47,7 +51,9 @@ function filterComponentProps(props: ComponentPrototypeType['props'], keyword: s
|
||||
|
||||
const formStyle = css`
|
||||
position: relative;
|
||||
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
> .SettingFormMain > :first-child:is(.FormControl) {
|
||||
margin-top: var(--tango-space-m);
|
||||
}
|
||||
@@ -69,8 +75,8 @@ interface IFormTabsGroupOption {
|
||||
}
|
||||
|
||||
const internalGroups: IFormTabsGroupOption[] = [
|
||||
{ label: '常用', value: 'basic' },
|
||||
{ label: '事件', value: 'event' },
|
||||
{ label: '基本', value: 'basic' },
|
||||
// { label: '事件', value: 'event' },
|
||||
{ label: '样式', value: 'style' },
|
||||
{ label: '高级', value: 'advanced' },
|
||||
];
|
||||
@@ -91,7 +97,7 @@ export interface SettingFormProps {
|
||||
/**
|
||||
* 组件的可配置描述
|
||||
*/
|
||||
prototype?: ComponentPrototypeType;
|
||||
prototype?: IComponentPrototype;
|
||||
/**
|
||||
* 是否显示组件标识
|
||||
*/
|
||||
@@ -108,6 +114,10 @@ export interface SettingFormProps {
|
||||
* 是否显示分组导航
|
||||
*/
|
||||
showGroups?: boolean;
|
||||
/**
|
||||
* 选项分组信息,用于对表单项进行分组展示,如未提供,则使用内置的分组信息
|
||||
*/
|
||||
groupOptions?: IFormTabsGroupOption[];
|
||||
/**
|
||||
* 是否显示表单项的副标题
|
||||
*/
|
||||
@@ -115,11 +125,7 @@ export interface SettingFormProps {
|
||||
/**
|
||||
* 自定义渲染表单项的额外内容(标签右侧)
|
||||
*/
|
||||
renderItemExtra?: (props: ComponentPropType) => React.ReactNode;
|
||||
/**
|
||||
* 选项分组信息,用于对表单项进行分组展示,如未提供,则使用内置的分组信息
|
||||
*/
|
||||
groupOptions?: IFormTabsGroupOption[];
|
||||
renderItemExtra?: (props: IComponentProp) => React.ReactNode;
|
||||
/**
|
||||
* 是否允许表单项切换到表达式设置器
|
||||
*/
|
||||
@@ -144,8 +150,8 @@ export function SettingForm({
|
||||
const [model, setModel] = useState(modelProp ?? new FormModel(defaultValue, { onChange }));
|
||||
const { props: componentProps = [] } = prototype;
|
||||
const componentPropGroups = useMemo(() => {
|
||||
return normalizeComponentProps(componentProps);
|
||||
}, [componentProps]);
|
||||
return normalizeComponentProps(componentProps, groupOptions);
|
||||
}, [componentProps, groupOptions]);
|
||||
|
||||
const filterProps = useMemo(() => {
|
||||
const computedProps = mapTreeData(componentProps, (node) => {
|
||||
@@ -159,7 +165,15 @@ export function SettingForm({
|
||||
// 没有进行搜索的情况
|
||||
if (!keyword) {
|
||||
if (showGroups) {
|
||||
return componentPropGroups[tabKey] || [];
|
||||
return (
|
||||
componentPropGroups[tabKey].sort((a, b) => {
|
||||
// TIP: 将废弃属性往后移
|
||||
if (!a.deprecated && b.deprecated) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}) || []
|
||||
);
|
||||
} else {
|
||||
return computedProps;
|
||||
}
|
||||
@@ -170,7 +184,7 @@ export function SettingForm({
|
||||
}, [keyword, componentProps, tabKey, model, showGroups, componentPropGroups]);
|
||||
|
||||
const renderProps = useCallback(
|
||||
(list: ComponentPropType[]) =>
|
||||
(list: IComponentProp[]) =>
|
||||
list.map((item) => {
|
||||
const { getProp, ...rest } = item;
|
||||
const childProp = {
|
||||
@@ -199,7 +213,7 @@ export function SettingForm({
|
||||
<FormVariableProvider value={{ disableSwitchExpressionSetter, showItemSubtitle }}>
|
||||
<FormModelProvider value={model}>
|
||||
<Box className="SettingForm" mb="xl" css={formStyle}>
|
||||
<Box className="SettingFormHeader" position="sticky" top="0" bg="white" zIndex="2">
|
||||
<Box className="SettingFormHeader" bg="white">
|
||||
<Box px="l" py="m">
|
||||
{showIdentifier && (
|
||||
<FormHeader
|
||||
@@ -240,14 +254,21 @@ export function SettingForm({
|
||||
<Tabs.TabPane
|
||||
key={group.value}
|
||||
tab={group.label}
|
||||
disabled={!componentPropGroups[group.value]?.length}
|
||||
disabled={!componentPropGroups[group.value].length}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<Box className="SettingFormMain" display="flex" flexDirection="column" rowGap="m">
|
||||
<Box
|
||||
className="SettingFormMain"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
paddingBottom="m"
|
||||
rowGap="m"
|
||||
overflow="auto"
|
||||
>
|
||||
{renderProps(filterProps)}
|
||||
{filterProps.length === 0 && (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="未找到配置项" />
|
||||
|
||||
@@ -76,6 +76,7 @@ export function registerBuiltinSetters() {
|
||||
register({
|
||||
name: 'expressionSetter',
|
||||
component: ExpressionSetter,
|
||||
disableVariableSetter: true,
|
||||
});
|
||||
|
||||
register({
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ISetterOnChangeCallbackDetail {
|
||||
relatedImports?: string[];
|
||||
isRawCode?: boolean;
|
||||
}
|
||||
@@ -3,6 +3,32 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.2](https://github.com/netease/tango/compare/@music163/tango-ui@1.0.1...@music163/tango-ui@1.0.2) (2024-04-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- allow reload state tree & display pageStore in variable tree ([#135](https://github.com/netease/tango/issues/135)) ([2664613](https://github.com/netease/tango/commit/2664613ab263aead2a5239fa012454fc7fd5ff99))
|
||||
|
||||
## [1.0.1](https://github.com/netease/tango/compare/@music163/tango-ui@1.0.0...@music163/tango-ui@1.0.1) (2024-04-16)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-ui
|
||||
|
||||
# [1.0.0-alpha.12](https://github.com/netease/tango/compare/@music163/tango-ui@1.0.0-alpha.11...@music163/tango-ui@1.0.0-alpha.12) (2024-04-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- update setters and use tabOptions to filter props ([#129](https://github.com/netease/tango/issues/129)) ([93608d1](https://github.com/netease/tango/commit/93608d1037327afa4f755976b86427b6128ae3d0))
|
||||
|
||||
# [1.0.0-alpha.11](https://github.com/netease/tango/compare/@music163/tango-ui@1.0.0-alpha.10...@music163/tango-ui@1.0.0-alpha.11) (2024-03-26)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-ui
|
||||
|
||||
# [1.0.0-alpha.10](https://github.com/netease/tango/compare/@music163/tango-ui@1.0.0-alpha.9...@music163/tango-ui@1.0.0-alpha.10) (2024-03-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- select outline node & render dependency list ([#118](https://github.com/netease/tango/issues/118)) ([07febb3](https://github.com/netease/tango/commit/07febb385f710f9a0bc43639ce22787bb05e97ed))
|
||||
|
||||
# [1.0.0-alpha.9](https://github.com/netease/tango/compare/@music163/tango-ui@1.0.0-alpha.8...@music163/tango-ui@1.0.0-alpha.9) (2024-03-18)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-ui
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-ui",
|
||||
"version": "1.0.0-alpha.9",
|
||||
"version": "1.0.3",
|
||||
"description": "ui widgets of tango",
|
||||
"keywords": [
|
||||
"react",
|
||||
@@ -38,14 +38,14 @@
|
||||
"@codemirror/lang-javascript": "^6.2.1",
|
||||
"@codemirror/lint": "^6.4.2",
|
||||
"@codemirror/search": "^6.5.5",
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.4",
|
||||
"@music163/tango-helpers": "^1.0.0",
|
||||
"@uiw/react-codemirror": "^4.21.21",
|
||||
"antd": "^4.24.2",
|
||||
"classnames": "^2.5.1",
|
||||
"coral-system": "^1.0.5",
|
||||
"eslint-linter-browserify": "^8.51.0",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-monaco-editor-lite": "^1.2.4"
|
||||
"react-monaco-editor-lite": "^1.3.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -50,6 +50,11 @@ const actionInputStyle = css`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--tango-colors-text3);
|
||||
|
||||
&:hover {
|
||||
color: var(--tango-colors-text2);
|
||||
}
|
||||
}
|
||||
|
||||
.anticon-down {
|
||||
|
||||
@@ -68,6 +68,7 @@ export interface ActionProps extends HTMLCoralProps<any> {
|
||||
*/
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
onClick?: React.MouseEventHandler;
|
||||
}
|
||||
|
||||
export function Action({
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Tag, TagProps, Tooltip } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
interface ColorTagProps extends TagProps {
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
export function ColorTag({ tooltip, children, ...rest }: ColorTagProps) {
|
||||
const tag = <Tag {...rest}>{children}</Tag>;
|
||||
if (tooltip) {
|
||||
return <Tooltip title={tooltip}>{tag}</Tooltip>;
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { createIcon } from './create-icon';
|
||||
|
||||
const DualOutlinedSvg = () => (
|
||||
<svg
|
||||
viewBox="0 0 1025 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M912 64H112c-26.5 0-48 21.5-48 48v800c0 26.5 21.5 48 48 48h800c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zM136 193h340v695H136V193z m412 695V193h340v695H548z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const DualOutlined = createIcon(DualOutlinedSvg, 'DualOutlined');
|
||||
@@ -8,3 +8,4 @@ export * from './open-panel-left-outlined';
|
||||
export * from './open-panel-right-outlined';
|
||||
export * from './undo-outlined';
|
||||
export * from './redo-outlined';
|
||||
export * from './dual-outlined';
|
||||
|
||||
@@ -7,6 +7,7 @@ export * from './chat-input';
|
||||
export * from './error-boundary';
|
||||
export * from './file-explorer';
|
||||
export * from './collapse-panel';
|
||||
export * from './color-tag';
|
||||
export * from './iconfont';
|
||||
export * from './menu';
|
||||
export * from './panel';
|
||||
|
||||
@@ -162,7 +162,12 @@ export function InputCode({
|
||||
return (
|
||||
<Box className="InputCode" display="flex" alignItems="center" overflow="hidden" {...rootStyle}>
|
||||
<Box flex="1" overflow="auto">
|
||||
<CodeMirror extensions={extensions} basicSetup={codeSetup} {...rest} />
|
||||
<CodeMirror
|
||||
extensions={extensions}
|
||||
basicSetup={codeSetup}
|
||||
placeholder="//输入 javascript 代码"
|
||||
{...rest}
|
||||
/>
|
||||
</Box>
|
||||
{suffix}
|
||||
</Box>
|
||||
@@ -184,6 +189,7 @@ function useInputCode({
|
||||
border: 'solid',
|
||||
borderColor: status === 'error' ? 'error.60' : 'line.normal',
|
||||
borderRadius: 's',
|
||||
minHeight: 32,
|
||||
};
|
||||
lineNumbers = false;
|
||||
foldGutter = false;
|
||||
|
||||
@@ -21,7 +21,7 @@ export function SelectAction({ tooltip, children, ...rest }: SelectActionProps)
|
||||
bg="brand"
|
||||
color="white"
|
||||
px="s"
|
||||
fontSize="body"
|
||||
fontSize="subtitle"
|
||||
css={selectionActionStyle}
|
||||
{...rest}
|
||||
>
|
||||
|
||||
@@ -39,10 +39,11 @@ const buttonStyle = css`
|
||||
}
|
||||
|
||||
&.shape-text {
|
||||
padding: 0px 8px;
|
||||
padding: 0px 4px;
|
||||
height: auto;
|
||||
border: 0;
|
||||
border-radius: var(--tango-radii-s);
|
||||
color: var(--tango-colors-gray-40);
|
||||
color: var(--tango-colors-text2);
|
||||
background: transparent;
|
||||
|
||||
&.selected.type-normal {
|
||||
@@ -50,7 +51,8 @@ const buttonStyle = css`
|
||||
}
|
||||
|
||||
&.selected.type-primary {
|
||||
color: var(--tango-colors-brand);
|
||||
color: var(--tango-colors-white);
|
||||
background-color: var(--tango-colors-brand);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -87,7 +89,7 @@ const buttonStyle = css`
|
||||
|
||||
&.shape-ghost {
|
||||
background-color: var(--tango-colors-custom-toolbarButtonBg, rgba(223, 223, 223, 0.08));
|
||||
color: var(--tango-colors-custom-toolbarButtonTextColor, #fff);
|
||||
color: var(--tango-colors-custom-toolbarButtonTextColor, #999);
|
||||
border: 0;
|
||||
border-radius: var(--tango-radii-m);
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://typedoc.org/schema.json",
|
||||
"name": "Tango LowCode Builder",
|
||||
"entryPoints": ["packages/core", "packages/designer", "packages/helpers"],
|
||||
"entryPoints": ["packages/core", "packages/designer","packages/setting-form", "packages/helpers"],
|
||||
"entryPointStrategy": "packages",
|
||||
"exclude": ["packages/**/tests/**/*"],
|
||||
"out": "docs",
|
||||
|
||||
Reference in New Issue
Block a user