Compare commits

..

19 Commits

Author SHA1 Message Date
Wells 43b99de01c chore(release): publish
- @music163/tango-context@1.0.0
 - @music163/tango-core@1.0.0
 - @music163/tango-designer@1.0.0
 - @music163/tango-sandbox@1.0.0
 - @music163/tango-setting-form@1.0.0
 - @music163/tango-ui@1.0.0
2024-04-15 14:41:25 +08:00
BoBoooooo 1c1ad9e5eb feat: support traverse native dom (#132)
* Create sync.yml

* Update sync.yml

* feat: support nativeDom

* ci: remove sync.yml

* fix: update test case

* fix: update isValidComponentName logic
2024-04-15 14:22:34 +08:00
BoBoooooo 776837079a feat: update ui & add dual screen mode (#131)
* fix: change view source icon

* fix: setting-form collapsed panel default collapsed

* fix: selectAction font size larger

* feat: add dual screen mode

* fix: trigger bug
2024-04-11 10:20:07 +08:00
Wells 14375284e4 chore(release): publish
- @music163/tango-context@1.0.0-alpha.10
 - @music163/tango-core@1.0.0-alpha.10
 - @music163/tango-designer@1.0.0-alpha.18
 - @music163/tango-helpers@1.0.0-alpha.6
 - @music163/tango-sandbox@1.0.0-alpha.10
 - @music163/tango-setting-form@1.0.0-alpha.14
 - @music163/tango-ui@1.0.0-alpha.12
2024-04-09 14:37:56 +08:00
Wells cb6aabf6ce fix: check is in design mode 2024-04-08 18:02:43 +08:00
BoBoooooo 3f42516816 fix: update SettingForm style (#125)
* fix: auto focus expression panel

* fix: form control group level 1 sticky head

* fix: expression-setter add showOptionsDropDown

* fix: update icon

* fix: revert toggleIsVariable

* fix: revert comment
2024-04-03 12:41:10 +08:00
Wells 93608d1037 fix: update setters and use tabOptions to filter props (#129)
* docs: update

* fix: update actionSelect

* fix: update ToggleButton text shape style

* fix: update InputCode minHeight and placeholder

* fix: show value in renderSetter

* docs: update

* fix: update groupOptions

* fix: update types

* chore: update
2024-04-02 20:08:54 +08:00
Wells 41a61cce4e fix: render invalid icon in inserted dropdown (#128) 2024-04-02 14:01:36 +08:00
Wells 47364e4219 chore(release): publish
- @music163/tango-context@1.0.0-alpha.9
 - @music163/tango-core@1.0.0-alpha.9
 - @music163/tango-designer@1.0.0-alpha.17
 - @music163/tango-helpers@1.0.0-alpha.5
 - @music163/tango-sandbox@1.0.0-alpha.9
 - @music163/tango-setting-form@1.0.0-alpha.13
 - @music163/tango-ui@1.0.0-alpha.11
2024-03-26 19:43:53 +08:00
Wells 9ed6a7d1a4 feat: quick add sibling nodes (#127)
* docs: add mail example

* fix: update SettingForm style

* fix: add tooltip to quick add action

* feat: quick add sibling nodes

* fix: update view
2024-03-26 19:36:28 +08:00
Wells 6f2c8f239a chore(release): publish
- @music163/tango-designer@1.0.0-alpha.16
2024-03-21 16:06:40 +08:00
Wells 6215ed96f2 fix: copy node path in variable tree 2024-03-21 15:33:23 +08:00
Wells 190aa1d8ee chore(release): publish
- @music163/tango-designer@1.0.0-alpha.15
2024-03-20 17:42:17 +08:00
Wells bceb7a5c9c fix: update selection helper style 2024-03-20 17:40:12 +08:00
Wells d64042922b fix: quick insert child 2024-03-20 17:10:52 +08:00
Wells 9ada3fecb3 chore(release): publish
- @music163/tango-designer@1.0.0-alpha.14
 - @music163/tango-setting-form@1.0.0-alpha.12
 - @music163/tango-ui@1.0.0-alpha.10
2024-03-19 19:19:42 +08:00
Wells 07febb385f fix: select outline node & render dependency list (#118)
* fix: select outline tree

* fix: update selection tip

* fix: refactor ColorTag
2024-03-19 19:16:05 +08:00
Wells ddbda0e6fb chore(release): publish
- @music163/tango-context@1.0.0-alpha.8
 - @music163/tango-core@1.0.0-alpha.8
 - @music163/tango-designer@1.0.0-alpha.13
 - @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
2024-03-18 16:54:59 +08:00
Wells 9f7441c134 fix: add deprecated and data group to ComponentPropType (#116)
* fix: add deprecated and data group to ComponentPropType

* fix: render choiceSetter with icon
2024-03-18 16:49:41 +08:00
66 changed files with 2011 additions and 581 deletions
+1
View File
@@ -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',
+1 -5
View File
@@ -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"
}
}
+1 -108
View File
@@ -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',
+180
View File
@@ -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 },
];
+82 -40
View File
@@ -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="section3">
<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');
`;
+215
View File
@@ -0,0 +1,215 @@
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'],
},
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;
+44 -15
View File
@@ -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,42 @@ const sandboxQuery = new DndQuery({
// 4. 图标库初始化(物料面板和组件树使用了 iconfont 里的图标)
createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_2891794_lzc7rtwuzf.js',
scriptUrl: '//at.alicdn.com/t/c/font_2891794_6d4hj5u0bjx.js',
});
const menuData = {
common: [
{
title: '常用',
items: [
'Button',
'Section',
'Columns',
'Column',
'Box',
'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 +123,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 +149,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>}
+134
View File
@@ -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>
);
}
+71 -17
View File
@@ -1,23 +1,51 @@
import React from 'react';
import { FormModel, SettingForm, register } from '@music163/tango-setting-form';
import { ComponentPrototypeType } from '@music163/tango-helpers';
import { BorderSetter } from '@music163/tango-designer/src/setters/style-setter';
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 { Box } from 'coral-system';
import { JsonView } from '@music163/tango-ui';
import { toJS } from 'mobx';
import { observer } from 'mobx-react-lite';
import { Card } from 'antd';
import { createFromIconfontCN } from '@ant-design/icons';
// 这里按需注入,因为部分 setter 依赖 Designer 的上下文
register({
name: 'borderSetter',
component: BorderSetter,
});
register({
name: 'displaySetter',
component: DisplaySetter,
});
register({
name: 'jsxSetter',
component: JsxSetter,
});
register({
name: 'renderSetter',
component: RenderSetter,
});
register({
name: 'tableCellSetter',
component: TableCellSetter,
});
createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_2891794_cou9i7556tl.js',
});
export default {
title: 'SettingForm',
};
const prototype: ComponentPrototypeType = {
const prototype: IComponentPrototype = {
name: 'Test',
exportType: 'namedExport',
title: '测试',
@@ -31,12 +59,42 @@ const prototype: ComponentPrototypeType = {
name: 'text',
title: 'textSetter',
setter: 'textSetter',
tip: '这是一个文本属性',
docs: 'https://music-one.fn.netease.com/docs/button',
deprecated: '使用 text2 替代',
},
{
name: 'display',
title: 'displaySetter',
setter: 'displaySetter',
},
{
name: 'border',
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',
@@ -108,13 +166,7 @@ const prototype: ComponentPrototypeType = {
title: 'iconTypeSetter',
setter: 'iconTypeSetter',
},
{
name: 'onClick',
title: 'eventSetter',
tip: '当点击按钮时',
setter: 'eventSetter',
group: 'event',
},
{
name: 'onClick2',
title: 'actionSetter',
@@ -249,13 +301,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} />
+3 -3
View File
@@ -51,9 +51,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",
+12
View File
@@ -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.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.0.0-alpha.7](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0-alpha.6...@music163/tango-context@1.0.0-alpha.7) (2024-03-18)
**Note:** Version bump only for package @music163/tango-context
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-context",
"version": "1.0.0-alpha.7",
"version": "1.0.0",
"description": "react context for tango-apps",
"keywords": [
"react",
@@ -31,8 +31,8 @@
"react": ">= 16.8"
},
"dependencies": {
"@music163/tango-core": "^1.0.0-alpha.7",
"@music163/tango-helpers": "^1.0.0-alpha.3",
"@music163/tango-core": "^1.0.0",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"mobx-react-lite": "4.0.5"
},
"publishConfig": {
+16
View File
@@ -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.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.0.0-alpha.7](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0-alpha.6...@music163/tango-core@1.0.0-alpha.7) (2024-03-18)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-core",
"version": "1.0.0-alpha.7",
"version": "1.0.0",
"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.3",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"@types/babel__generator": "^7.6.7",
"@types/babel__traverse": "^7.20.4",
"mobx": "6.12.0",
+8 -8
View File
@@ -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,14 +141,14 @@ 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],
@@ -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;
}
+7 -2
View File
@@ -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();
}
+1 -1
View File
@@ -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;
+2
View File
@@ -11,6 +11,8 @@ export enum HistoryMessage {
ReplaceNode = 'replaceNode',
CloneNode = 'cloneNode',
InsertNode = 'insertNode',
InsertBeforeNode = 'insertBeforeNode',
InsertAfterNode = 'insertAfterNode',
DropNode = 'dropNode',
UpdateAttribute = 'updateAttribute',
UpdateCode = 'updateCode',
+9 -13
View File
@@ -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[],
+49 -9
View File
@@ -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>;
/**
* 入口文件
@@ -249,11 +249,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 +299,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 +954,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 +978,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 +1000,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 +1022,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[] = [],
+4
View File
@@ -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', () => {
+39
View File
@@ -3,6 +3,45 @@
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.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
- add deprecated and data group to ComponentPropType ([#116](https://github.com/netease/tango/issues/116)) ([9f7441c](https://github.com/netease/tango/commit/9f7441c13400dba55c58b7dd6ce9429013edbc45))
# [1.0.0-alpha.12](https://github.com/netease/tango/compare/@music163/tango-designer@1.0.0-alpha.11...@music163/tango-designer@1.0.0-alpha.12) (2024-03-18)
### Bug Fixes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-designer",
"version": "1.0.0-alpha.12",
"version": "1.0.0",
"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.7",
"@music163/tango-core": "^1.0.0-alpha.7",
"@music163/tango-helpers": "^1.0.0-alpha.3",
"@music163/tango-sandbox": "^1.0.0-alpha.7",
"@music163/tango-setting-form": "^1.0.0-alpha.10",
"@music163/tango-ui": "^1.0.0-alpha.8",
"@music163/tango-context": "^1.0.0",
"@music163/tango-core": "^1.0.0",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"@music163/tango-sandbox": "^1.0.0",
"@music163/tango-setting-form": "^1.0.0",
"@music163/tango-ui": "^1.0.0",
"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>
@@ -216,7 +222,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>
);
});
@@ -1,10 +1,10 @@
import React from 'react';
import React, { useCallback } from 'react';
import { Radio, RadioProps, Tooltip } from 'antd';
import type { OptionType } from '@music163/tango-helpers';
import { FormItemComponentProps } from '@music163/tango-setting-form';
import { IconFont } from '@music163/tango-ui';
interface ChoiceSetterProps {
mode?: 'text' | 'icon';
options?: OptionType[];
}
@@ -13,6 +13,18 @@ export function ChoiceSetter({
onChange,
...props
}: FormItemComponentProps<string> & ChoiceSetterProps) {
const renderIcon = useCallback((icon: string) => {
if (!icon) {
return null;
}
if (icon.startsWith('icon-')) {
return <IconFont type={icon} />;
}
return null;
}, []);
return (
<Radio.Group
optionType="button"
@@ -24,6 +36,7 @@ export function ChoiceSetter({
>
{options.map((item) => (
<RadioButton tip={item.tip} key={item.value} value={item.value}>
{renderIcon(item.icon)}
{item.label}
</RadioButton>
))}
@@ -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 />}
+1 -1
View File
@@ -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,
+29 -24
View File
@@ -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>
);
}
@@ -1,6 +1,7 @@
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';
interface IRenderOption {
label: string;
@@ -19,11 +20,18 @@ export interface RenderSetterProps {
* Render Props Setters
*/
export function RenderSetter({
value,
onChange,
text = '自定义渲染为',
options = [],
fallbackOption,
}: FormItemComponentProps & RenderSetterProps) {
const [inputValue, setInputValue] = useState(value);
useEffect(() => {
setInputValue(value);
}, [value]);
const optionsMap = useMemo(() => {
return options.reduce((prev, cur) => {
prev[cur.value] = cur;
@@ -42,7 +50,18 @@ export function RenderSetter({
},
[optionsMap, fallbackOption, onChange],
);
return <ActionSelect text={text} options={options} onSelect={onSelect} />;
return (
<Box>
<ActionSelect text={text} options={options} onSelect={onSelect} />
{value && (
<InputCode
value={value}
onChange={(val) => setInputValue(val)}
onBlur={() => onChange(inputValue)}
/>
)}
</Box>
);
}
const getRender = (content: string, type?: 'tableCell' | 'tableExpandable') => {
+3
View File
@@ -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={
<>
&nbsp;
<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);
+109 -28
View File
@@ -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>
+27 -10
View File
@@ -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>
);
});
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+19
View File
@@ -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.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
- add deprecated and data group to ComponentPropType ([#116](https://github.com/netease/tango/issues/116)) ([9f7441c](https://github.com/netease/tango/commit/9f7441c13400dba55c58b7dd6ce9429013edbc45))
# [1.0.0-alpha.3](https://github.com/netease/tango/compare/@music163/tango-helpers@1.0.0-alpha.2...@music163/tango-helpers@1.0.0-alpha.3) (2024-03-18)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-helpers",
"version": "1.0.0-alpha.3",
"version": "1.0.0-alpha.6",
"description": "Shared types, helpers, and hooks of tango-apps",
"keywords": [
"shared",
+1 -1
View File
@@ -66,5 +66,5 @@ export function isServiceVariablePath(key: string) {
* 是否在 Tango 设计模式
*/
export function isInTangoDesignMode() {
return '__TANGO_DESIGNER__' in window;
return !!(window as any).__TANGO_DESIGNER__;
}
-5
View File
@@ -132,8 +132,3 @@ export interface ISelectedItemData {
element?: HTMLElement;
parents?: ISelectedItemData[];
}
export type SetterOnChangeDetailType = {
relatedImports?: string[];
isRawCode?: boolean;
};
+36 -12
View File
@@ -4,10 +4,25 @@
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 ComponentPropType<T = any> = {
export interface IComponentProp<T = any> {
/**
* 属性名
*/
@@ -23,11 +38,12 @@ export type ComponentPropType<T = any> = {
/**
* 属性的分组
* - basic 常用属性
* - event 事件属性
* - style 样式属性
* - advanced 高级属性
* - event 事件属性
* - data 数据属性
*/
group?: 'basic' | 'event' | 'style' | 'advanced';
group?: 'basic' | 'event' | 'style' | 'data' | 'advanced';
/**
* 帮助文档链接地址
* @example https://foo.bar/help
@@ -49,7 +65,7 @@ export type ComponentPropType<T = any> = {
/**
* 如果是对象属性,这里声明子属性列表
*/
props?: ComponentPropType[];
props?: IComponentProp[];
/**
* 输入提示
*/
@@ -78,7 +94,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 的简写
*/
@@ -91,12 +107,16 @@ export type ComponentPropType<T = any> = {
* 动态设置表单项是否展示
*/
getVisible?: (form: any) => boolean;
};
/**
* 标记属性是否已废弃
*/
deprecated?: boolean | string;
}
/**
* 组件拖拽规则类型
*/
export type ComponentDndRulesType = {
export interface IComponentDndRules {
/**
* 当前节点是否可以被拖拽
*/
@@ -117,12 +137,12 @@ export type ComponentDndRulesType = {
* 子节点的容器选择器,用于快速定位子节点容器,适合组件存在多个可搭建区域时使用
*/
childrenContainerSelector?: string;
};
}
/**
* 组件原型类型
*/
export type ComponentPrototypeType = {
export interface IComponentPrototype {
/**
* 组件名
* @example Button
@@ -228,20 +248,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 类型配置
+12
View File
@@ -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.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.0.0-alpha.7](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.0-alpha.6...@music163/tango-sandbox@1.0.0-alpha.7) (2024-03-18)
**Note:** Version bump only for package @music163/tango-sandbox
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-sandbox",
"version": "1.0.0-alpha.7",
"version": "1.0.0",
"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.7",
"@music163/tango-helpers": "^1.0.0-alpha.3",
"@music163/tango-core": "^1.0.0",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"crypto-js": "^4.1.1",
"lodash.isequal": "4.5.0",
"react-frame-component": "^5.2.4"
+23
View File
@@ -3,6 +3,29 @@
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.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
- add deprecated and data group to ComponentPropType ([#116](https://github.com/netease/tango/issues/116)) ([9f7441c](https://github.com/netease/tango/commit/9f7441c13400dba55c58b7dd6ce9429013edbc45))
# [1.0.0-alpha.10](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.0.0-alpha.9...@music163/tango-setting-form@1.0.0-alpha.10) (2024-03-18)
### Bug Fixes
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-setting-form",
"version": "1.0.0-alpha.10",
"version": "1.0.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.7",
"@music163/tango-helpers": "^1.0.0-alpha.3",
"@music163/tango-ui": "^1.0.0-alpha.8",
"@music163/tango-core": "^1.0.0",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"@music163/tango-ui": "^1.0.0",
"antd": "^4.24.2",
"coral-system": "^1.0.5",
"mobx": "6.12.0",
+8 -10
View File
@@ -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, 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'];
@@ -86,6 +82,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
disableVariableSetter: disableVariableSetterProp = options.disableVariableSetter,
getVisible: getVisibleProp,
getSetterProps: getSetterPropsProp,
deprecated,
extra,
footer,
noStyle,
@@ -94,7 +91,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
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),
);
@@ -160,6 +157,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
note={showItemSubtitle ? name : null}
tip={tip}
docs={docs}
deprecated={deprecated}
error={field.error}
extra={
<Box>
@@ -168,7 +166,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
<ToggleButton
borderRadius="s"
size="s"
shape="outline"
shape="text"
type="primary"
tooltip={isVariable ? '关闭 JS 表达式' : '使用 JS 表达式'}
tooltipPlacement="left"
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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;
+50 -23
View File
@@ -1,8 +1,9 @@
import React, { useState } from 'react';
import { css, Box, HTMLCoralProps, Text } from 'coral-system';
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 { WarningOutlined } from '@ant-design/icons';
export interface FormControlProps extends Omit<FormLabelProps, 'type'> {
visible: boolean;
@@ -19,6 +20,7 @@ export function FormControl({
tip,
docs,
extra,
deprecated,
footer,
error,
children,
@@ -27,7 +29,7 @@ export function FormControl({
return (
<Box className="FormControl" display={visible ? 'block' : 'none'} {...rest}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb="s">
<FormLabel label={label} note={note} tip={tip} docs={docs} />
<FormLabel label={label} note={note} tip={tip} docs={docs} deprecated={deprecated} />
{extra}
</Box>
<Box>{children}</Box>
@@ -60,10 +62,6 @@ export interface FormControlGroupProps extends FormLabelProps {
* 是否勾选
*/
checked?: boolean;
/**
* checkbox 默认是否勾选
*/
defaultChecked?: boolean;
}
export function FormControlGroup({
@@ -76,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
@@ -107,6 +106,7 @@ export function FormControlGroup({
borderColor="line.normal"
headerProps={{
bg: 'fill1',
zIndex: 2,
}}
>
<Box
@@ -138,12 +138,12 @@ const labelStyle = css`
`;
const tipStyle = css`
a:link {
color: #fff;
a {
text-decoration: underline;
}
a:hover {
color: var(--tango-colors-brand);
a:link {
color: #fff;
}
`;
@@ -169,19 +169,45 @@ interface FormLabelProps extends HTMLCoralProps<'div'> {
* 文档地址
*/
docs?: string;
/**
* 是否显示废弃标记
*/
// eslint-disable-next-line react/no-unused-prop-types
deprecated?: boolean | string;
}
function FormLabel({ type = 'normal', label, note, tip, docs, ...rest }: FormLabelProps) {
const help = docs ? (
<Box css={tipStyle}>
<Text mr="m">{tip}</Text>
<a href={docs} target="_blank" rel="noreferrer">
</a>
</Box>
) : (
tip
);
function FormLabel({
type = 'normal',
label,
note,
tip,
docs,
deprecated,
...rest
}: FormLabelProps) {
let help: React.ReactNode;
if (deprecated || docs) {
help = (
<Box>
<Box css={tipStyle}>
{tip}
{docs ? (
<Link href={docs} isExternal ml="m">
</Link>
) : null}
</Box>
{deprecated ? (
<Box color="#faad14">
<WarningOutlined />
{isString(deprecated) ? deprecated : '该属性已废弃,请谨慎使用。'}
</Box>
) : null}
</Box>
);
} else {
help = tip;
}
const labelColor = {
normal: 'text.body',
@@ -197,6 +223,7 @@ function FormLabel({ type = 'normal', label, note, tip, docs, ...rest }: FormLab
css={labelStyle}
title={isString(label) ? label : undefined}
>
{deprecated ? <WarningOutlined style={{ color: '#faad14', marginRight: 4 }} /> : null}
{label}
</Box>
);
+37 -24
View File
@@ -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) => {
@@ -170,7 +176,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 +205,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 +246,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="未找到配置项" />
+4
View File
@@ -0,0 +1,4 @@
export interface ISetterOnChangeCallbackDetail {
relatedImports?: string[];
isRawCode?: boolean;
}
+20
View File
@@ -3,6 +3,26 @@
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.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.0.0-alpha.8](https://github.com/netease/tango/compare/@music163/tango-ui@1.0.0-alpha.7...@music163/tango-ui@1.0.0-alpha.8) (2024-03-18)
### Bug Fixes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-ui",
"version": "1.0.0-alpha.8",
"version": "1.0.0",
"description": "ui widgets of tango",
"keywords": [
"react",
@@ -38,7 +38,7 @@
"@codemirror/lang-javascript": "^6.2.1",
"@codemirror/lint": "^6.4.2",
"@codemirror/search": "^6.5.5",
"@music163/tango-helpers": "^1.0.0-alpha.3",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"@uiw/react-codemirror": "^4.21.21",
"antd": "^4.24.2",
"classnames": "^2.5.1",
+5
View File
@@ -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 {
+14
View File
@@ -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;
}
+17
View File
@@ -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');
+1
View File
@@ -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';
+1
View File
@@ -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';
+7 -1
View File
@@ -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;
+1 -1
View File
@@ -21,7 +21,7 @@ export function SelectAction({ tooltip, children, ...rest }: SelectActionProps)
bg="brand"
color="white"
px="s"
fontSize="body"
fontSize="subtitle"
css={selectionActionStyle}
{...rest}
>
+6 -4
View File
@@ -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);
+567 -142
View File
File diff suppressed because it is too large Load Diff