mirror of
https://github.com/NetEase/tango.git
synced 2026-09-01 15:03:03 +08:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a86e0e049c | |||
| c4e1ed67f5 | |||
| 179de0cc52 | |||
| 6de54884b5 | |||
| 55bf417ddf | |||
| ca56edd9ae | |||
| 65149f1326 | |||
| 939466648d | |||
| 8fe57333a0 | |||
| 269e304d68 | |||
| 8c80255f81 | |||
| 359aa0b148 | |||
| 51ca4a72ce | |||
| b83d965955 | |||
| a1eea02f94 | |||
| 0d0479e3e5 | |||
| 67d602a3ec | |||
| 8dd289cd7d | |||
| 82bc5db4d8 | |||
| 85c053b3db | |||
| 32f5667cae | |||
| 1d0fb4f274 | |||
| 0011cdca0f | |||
| 0f3f0afdfa | |||
| b53a9f7eef | |||
| 5336c2ec28 | |||
| cdbd2351a2 | |||
| 1f03977f7d | |||
| a2cc418a73 |
@@ -9,3 +9,4 @@ coverage/
|
|||||||
.umi-production/
|
.umi-production/
|
||||||
.log
|
.log
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
*.pem
|
||||||
|
|||||||
@@ -31,23 +31,17 @@ You can view the detailed usage guide through the following links:
|
|||||||
- Out-of-the-box front-end low-code designer, providing flexible and easy-to-use designer React components
|
- Out-of-the-box front-end low-code designer, providing flexible and easy-to-use designer React components
|
||||||
- Developed using TypeScript, providing complete type definition files
|
- Developed using TypeScript, providing complete type definition files
|
||||||
|
|
||||||
## 🎯 Milestone
|
## 💡 Examples
|
||||||
|
|
||||||
```mermaid
|
You can use tango to build lowcode builders, for example:
|
||||||
gantt
|
|
||||||
dateFormat YYYY-MM-DD
|
|
||||||
title Tango 1.0 Milestones
|
|
||||||
excludes weekends
|
|
||||||
%% (`excludes` accepts specific dates in YYYY-MM-DD format, days of the week ("sunday") or "weekends", but not the word "weekdays".)
|
|
||||||
|
|
||||||
section Builder Release
|
| Preview | Description |
|
||||||
1.0 Alpha : m1, 2023-10-01, 2024-01-31
|
| ---------------------------------------------------------------------------------------------- | ----------------------- |
|
||||||
1.0 RC : m2, 2024-02-01, 2024-04-30
|
|  | Internal App Builder |
|
||||||
1.0 Stable : m3, 2024-04-30, 2024-12-31
|
|  | Dashboard App Builder |
|
||||||
```
|
|  | Mobile App Builder |
|
||||||
|
|  | ReactNative App Builder |
|
||||||
> [!NOTE]
|
|  | Email Builder |
|
||||||
> Alpha versions will continue to optimize some implementation protocols and continuously integrate new features from the internal version of NetEase; RC versions will provide a stable parsing protocol and implementation; The 1.0 official version is planned to be released in Q2 2024. After the release of the 1.0 official version, we will continue to iterate on versions and gradually incorporate core capabilities such as process design, component library, and AIGC from the internal version.
|
|
||||||
|
|
||||||
## 🌐 Compatibility
|
## 🌐 Compatibility
|
||||||
|
|
||||||
@@ -76,6 +70,23 @@ yarn
|
|||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Local Https certificate
|
||||||
|
|
||||||
|
If you need to use https in the local development environment, you can use the following command to generate a certificate:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install mkcert
|
||||||
|
|
||||||
|
# install the local certificate
|
||||||
|
mkcert -install
|
||||||
|
|
||||||
|
# enter the playground app directory
|
||||||
|
cd apps/playground
|
||||||
|
|
||||||
|
# generate a certificate
|
||||||
|
mkcert local.netease.com # or change to your own domain name
|
||||||
|
```
|
||||||
|
|
||||||
## 💬 Community
|
## 💬 Community
|
||||||
|
|
||||||
Join NetEase Tango Community to share your ideas, suggestions, or questions and connect with other users and contributors.
|
Join NetEase Tango Community to share your ideas, suggestions, or questions and connect with other users and contributors.
|
||||||
|
|||||||
+30
-19
@@ -26,28 +26,22 @@
|
|||||||
## ✨ 特性
|
## ✨ 特性
|
||||||
|
|
||||||
- 经历网易云音乐内网生产环境的实际检验,可灵活集成应用于低代码平台,本地开发工具等
|
- 经历网易云音乐内网生产环境的实际检验,可灵活集成应用于低代码平台,本地开发工具等
|
||||||
- 基于源码 AST 驱动,无私有 DSL 和协议
|
- 提供基于源码 AST 驱动的低代码引擎,无私有 DSL 和协议
|
||||||
- 提供实时出码能力,支持源码进,源码出
|
- 提供实时出码能力,支持源码进,源码出,适配多种源码开发场景
|
||||||
- 开箱即用的前端低代码设计器,提供灵活易用的设计器 React 组件
|
- 提供开箱即用的前端低代码设计器,提供灵活易用的设计器 React 组件
|
||||||
- 使用 TypeScript 开发,提供完整的类型定义文件
|
- 使用 TypeScript 开发,提供完整的类型定义文件和完善的文档支持
|
||||||
|
|
||||||
## 🎯 里程碑
|
## 💡 例子
|
||||||
|
|
||||||
```mermaid
|
你可以使用 Tango 快速构建多种类型的基于源码的低代码搭建工具,例如:
|
||||||
gantt
|
|
||||||
dateFormat YYYY-MM-DD
|
|
||||||
title Tango 1.0 Milestones
|
|
||||||
excludes weekends
|
|
||||||
%% (`excludes` accepts specific dates in YYYY-MM-DD format, days of the week ("sunday") or "weekends", but not the word "weekdays".)
|
|
||||||
|
|
||||||
section Builder Release
|
| 预览图 | 说明 |
|
||||||
1.0 Alpha : m1, 2023-10-01, 2024-01-31
|
| ---------------------------------------------------------------------------------------------- | -------------------- |
|
||||||
1.0 RC : m2, 2024-02-01, 2024-04-30
|
|  | 中后台系统搭建 |
|
||||||
1.0 Stable : m3, 2024-04-30, 2024-12-31
|
|  | 仪表盘应用搭建 |
|
||||||
```
|
|  | H5活动页面搭建 |
|
||||||
|
|  | ReactNative 应用搭建 |
|
||||||
> [!NOTE]
|
|  | 营销类邮件搭建 |
|
||||||
> Alpha 版本会持续对一些实现协议进行优化,并持续融合网易内网版本中的新增特性;RC 版本将会提供基本稳定的解析协议和实现;1.0 正式版本计划在 2024 年Q2发布。1.0 正式版发布后我们将会持续进行版本迭代,并逐步融入内网版本中的流程设计、组件库、AIGC 等核心基础能力。
|
|
||||||
|
|
||||||
## 🌐 兼容环境
|
## 🌐 兼容环境
|
||||||
|
|
||||||
@@ -76,6 +70,23 @@ yarn
|
|||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 本地 https 证书
|
||||||
|
|
||||||
|
如果需要在本地开发环境中使用 https,可以使用以下命令生成证书:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install mkcert
|
||||||
|
|
||||||
|
# 将 mkcert 添加到本地根 CA,仅在本地生效
|
||||||
|
mkcert -install
|
||||||
|
|
||||||
|
# 进入 playground 应用目录
|
||||||
|
cd apps/playground
|
||||||
|
|
||||||
|
# 为网站生成一个由 mkcert 签名的证书
|
||||||
|
mkcert local.netease.com
|
||||||
|
```
|
||||||
|
|
||||||
## 💬 社区讨论
|
## 💬 社区讨论
|
||||||
|
|
||||||
参与 NetEase Tango 的社区,以分享您的想法、建议或问题,并与其他用户和贡献者建立联系。
|
参与 NetEase Tango 的社区,以分享您的想法、建议或问题,并与其他用户和贡献者建立联系。
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4sAqDE3nhtSWD
|
|
||||||
YqV4mlglnzIs2AioZr2uxfj/OKrpE1ZML/dflJyc5NGUpOZWV+W1vRc70wWVNFxB
|
|
||||||
Si8nrYrrseoID7Ehxxe6+J1m/JsEbhZWHwV3M1iGW7JD7VhujaDpSLg67wMTMpSW
|
|
||||||
3BUS+0NT+MNfCisp+u/ls4fXPCTgXiWcjMULm95oLlTeYwurPHfxwn9f9hh85I5I
|
|
||||||
K3dI7w8MsQN7EFNrJz3UfrNLpmARMtSUBMwBYqrCitoUxRT0q7KYCru7kHxGPB6A
|
|
||||||
SQrFpSpOWT79vp8GBSp9g3axhRsK+aEgzQ0QGrFPHBwBP/kWgmdB1URc/K7STSC9
|
|
||||||
xyMNZOM7AgMBAAECggEATbyDYDKVbaRymr+tyHqmuYctdmSkGEXOdz8LFFoEzj/+
|
|
||||||
ZekWpVuHJB7H/FoAPb2XiYyCuAKVafz1C+IuwPOx8o0bZbM9KmuDmIZGsm+GzDGO
|
|
||||||
I0fBZC/vhfYYrbC8NSRV43thWCT8VVtH4cvW7vtllnWxvlIoYu4lhbiwZ68AEpO1
|
|
||||||
8PlJsOhnagfkFpG8LehSDs9if5L+Ig0geQEcUY3pGUM9LKYsRoxFv6Vh8wQku1XD
|
|
||||||
YOxLvLWDPqJ75m816t50RARe3e6eDJ+ukzDCoDjmLP8uhf1sk1FcL5aOgUNU3VOi
|
|
||||||
P6sIH5uzuD0MA3FXx2mItYLOc44r4uaFJZiRUMEXGQKBgQDCjUYblE4rh0nKFkqx
|
|
||||||
AhgfcRgpgLbP42OwFtD0u20RXdZAEJDRDQ43NdOgjFp2B50k75LBPIoFXYUZdzKj
|
|
||||||
l7S2FH5O9Cc6hEgZGI4hCiYd+nQVI1mxv820YnzZJN4oooxyL+B5RlnW5cDT7LJN
|
|
||||||
11Q5PRewkM+XpfbE3lKUjTNcDwKBgQDzBS+j8T9GJSzV2xgA+mpGHe1RjDq6yQtk
|
|
||||||
Rl5GWpflW4Nr3/58BbmCWNA8WP0NOWutJhrkltwLCqJC8OZX/WHnmcmq2k+UAfQQ
|
|
||||||
z98p+xcMWOTcl5XIbveuhYoZb2udt9WVJLcpRVxVhEWcBrtHsrpScZXsKYf/TaZ1
|
|
||||||
JtCjPU5KFQKBgGXj/UqmYkYzxXZ8NEP998pHvcLGsXew/F2VpXv2yQlmXrYQtvd1
|
|
||||||
YfOSUjJsL1hPZoKYogBoB1UGBJYwsimxYyhVFU2eWwkvjF7wWEd/fDUJHVCQwgHw
|
|
||||||
lPga+I303BDmCK4o2uRI7vY4P8P92+gelPKhR03mmYIvnky+rvsz9CkLAoGAVerl
|
|
||||||
du6Z0jDecxUsnVvrKrL17jjHorXlYyRFvFXXEe2SvxbIIAzeEylXJZ7IiF5epS6t
|
|
||||||
n1d+oCC4UTZeGYmpjXofhFn+fHNcWH1FhtAQy/q5nwuwltY0luz7cBamU3Jk/n+m
|
|
||||||
id7N0CYdfhYbLDQSD4e822ureyV2zkBUzwGjpikCgYEAoCVbhkxVrQHRmxLC2jDx
|
|
||||||
pM94W1ij9TqblfGg3OMpPXjpTdlknatWjBw7JkoyxYiYJq5Gi2p4QV6HHUvS1osj
|
|
||||||
BB+4A99/PD9A0ICoJglz8SXlGe/895zwHwpKT9f0zjizQrEd2KCPf75URa+t2FGY
|
|
||||||
B2GkXsbChRWVrey5+uMzUD8=
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEQjCCAqqgAwIBAgIRAIeLJ2jwt4MSKycHFmFXL6YwDQYJKoZIhvcNAQELBQAw
|
|
||||||
fTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSkwJwYDVQQLDCB3d3N1
|
|
||||||
bkBzdW4tbWJwLmxvY2FsIChTVU4gV2Vpd2VpKTEwMC4GA1UEAwwnbWtjZXJ0IHd3
|
|
||||||
c3VuQHN1bi1tYnAubG9jYWwgKFNVTiBXZWl3ZWkpMB4XDTIyMDcyODAxNTIxMFoX
|
|
||||||
DTI0MTAyODAxNTIxMFowVDEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNl
|
|
||||||
cnRpZmljYXRlMSkwJwYDVQQLDCB3d3N1bkBzdW4tbWJwLmxvY2FsIChTVU4gV2Vp
|
|
||||||
d2VpKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiwCoMTeeG1JYNi
|
|
||||||
pXiaWCWfMizYCKhmva7F+P84qukTVkwv91+UnJzk0ZSk5lZX5bW9FzvTBZU0XEFK
|
|
||||||
Lyetiuux6ggPsSHHF7r4nWb8mwRuFlYfBXczWIZbskPtWG6NoOlIuDrvAxMylJbc
|
|
||||||
FRL7Q1P4w18KKyn67+Wzh9c8JOBeJZyMxQub3mguVN5jC6s8d/HCf1/2GHzkjkgr
|
|
||||||
d0jvDwyxA3sQU2snPdR+s0umYBEy1JQEzAFiqsKK2hTFFPSrspgKu7uQfEY8HoBJ
|
|
||||||
CsWlKk5ZPv2+nwYFKn2DdrGFGwr5oSDNDRAasU8cHAE/+RaCZ0HVRFz8rtJNIL3H
|
|
||||||
Iw1k4zsCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF
|
|
||||||
BwMBMB8GA1UdIwQYMBaAFDILZR+SW32Ug0HkAKrH1KT2a1ttMBwGA1UdEQQVMBOC
|
|
||||||
EWxvY2FsLm5ldGVhc2UuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQCEdeV2Likl4MUt
|
|
||||||
wWeN/X3tAR3L7mkl7e4f0CQ4v2lOs8cekOP7p3ZFk5nSyVbxda3usPB0OtbVTibw
|
|
||||||
qZ1n/TMRb5epHroXIMleqngP82zFZNg+WVyHm/ZAml8gVD0VZjYM1Tb+U9+SrgGw
|
|
||||||
lvZndbuUXa3h/d0irPH90KsdEeAR1SqwzAzvLO2JgvBuimHRceQWcTiiGWDZOyii
|
|
||||||
tux4U+pEzh0Dia40uFozFAoYZPDUpZ+gsiQ3MR3HOrAu2OR0a+CfelDXZNLR3C4z
|
|
||||||
cBKr53ajAbHstnpxKJh9IfeKph44PrbDo6HMe1XPprl7idoEesX3Ma3SBgm4lmId
|
|
||||||
cfEOUWNIYOgtOT5bflXNNyzXOgYVjpLnjwg4EOASO/XIjhvqdjIyCu7HJMIcZ9oo
|
|
||||||
Aob2exeluv5LVwic1UJQDl2McDBIgJ8R32oEN4skCKIBSbWlxWyjnxL5I5WkJF4y
|
|
||||||
vlSx8bKAjVr8map0C3rWISuZdgheJkF96qjwhVSxOv++p6srq6M=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Group } from 'coral-system';
|
import { Box, Group, Text } from 'coral-system';
|
||||||
import { Avatar, Space, Switch } from 'antd';
|
import { Avatar, Space, Switch } from 'antd';
|
||||||
import { BranchesOutlined, MenuOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
import { BranchesOutlined, MenuOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
import { registerSetter } from '@music163/tango-designer';
|
import { registerSetter } from '@music163/tango-designer';
|
||||||
import type { IVariableTreeNode } from '@music163/tango-helpers';
|
import type { IVariableTreeNode } from '@music163/tango-helpers';
|
||||||
import { FooSetter } from '../components';
|
import { FooSetter } from '../components';
|
||||||
|
import { ToggleButton } from '@music163/tango-ui';
|
||||||
|
|
||||||
export * from './mock-files';
|
export * from './mock-files';
|
||||||
|
|
||||||
@@ -46,8 +47,17 @@ registerSetter({
|
|||||||
// 平台 Logo
|
// 平台 Logo
|
||||||
export function Logo() {
|
export function Logo() {
|
||||||
return (
|
return (
|
||||||
<Box width="50px" display="flex" alignItems="center" justifyContent="center" fontSize="20px">
|
<Box width="50px" display="flex" alignItems="center" justifyContent="center" fontSize="14px">
|
||||||
|
<ToggleButton
|
||||||
|
shape="text"
|
||||||
|
items={[
|
||||||
|
{ label: '回到首页', key: 'home' },
|
||||||
|
{ label: '文档', key: 'doc' },
|
||||||
|
{ label: '设置', key: 'setting' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
<MenuOutlined />
|
<MenuOutlined />
|
||||||
|
</ToggleButton>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -56,12 +66,12 @@ export function Logo() {
|
|||||||
export function ProjectDetail() {
|
export function ProjectDetail() {
|
||||||
return (
|
return (
|
||||||
<Box display="flex" alignItems="center" columnGap="l">
|
<Box display="flex" alignItems="center" columnGap="l">
|
||||||
<Box className="ProjectName" fontSize="18px" fontWeight="bold">
|
<Text className="ProjectName" fontSize="18px" fontWeight="bold" truncated>
|
||||||
community-test
|
community-test
|
||||||
</Box>
|
</Text>
|
||||||
<Box className="BranchName" as="code" fontSize="14px">
|
<Text className="BranchName" as="code" fontSize="14px" truncated>
|
||||||
<BranchesOutlined /> feature/list
|
<BranchesOutlined /> feature/list
|
||||||
</Box>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const packageJson = {
|
|||||||
private: true,
|
private: true,
|
||||||
dependencies: {
|
dependencies: {
|
||||||
'@music163/antd': '0.2.5',
|
'@music163/antd': '0.2.5',
|
||||||
'@music163/tango-boot': '0.2.5',
|
'@music163/tango-boot': '0.3.5',
|
||||||
react: '17.0.2',
|
react: '17.0.2',
|
||||||
'react-dom': '17.0.2',
|
'react-dom': '17.0.2',
|
||||||
'prop-types': '15.7.2',
|
'prop-types': '15.7.2',
|
||||||
@@ -48,7 +48,7 @@ const tangoConfigJson = {
|
|||||||
},
|
},
|
||||||
'@music163/tango-boot': {
|
'@music163/tango-boot': {
|
||||||
description: '云音乐低代码运行时框架',
|
description: '云音乐低代码运行时框架',
|
||||||
version: '0.2.5',
|
version: '0.3.5',
|
||||||
library: 'TangoBoot',
|
library: 'TangoBoot',
|
||||||
type: 'baseDependency',
|
type: 'baseDependency',
|
||||||
resources: ['https://unpkg.com/@music163/tango-boot@{{version}}/dist/boot.js'],
|
resources: ['https://unpkg.com/@music163/tango-boot@{{version}}/dist/boot.js'],
|
||||||
@@ -153,6 +153,7 @@ import { definePage } from "@music163/tango-boot";
|
|||||||
import {
|
import {
|
||||||
Page,
|
Page,
|
||||||
Section,
|
Section,
|
||||||
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Input,
|
Input,
|
||||||
FormilyForm,
|
FormilyForm,
|
||||||
@@ -166,8 +167,10 @@ import { OutButton } from "../components";
|
|||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Page title={tango.stores.app.title} subTitle={111}>
|
<Page title={tango.stores.app.title} subTitle={<><Button>hello</Button></>}>
|
||||||
<Section tid="section0" />
|
<Section tid="section0">
|
||||||
|
<Box></Box>
|
||||||
|
</Section>
|
||||||
<Section tid="section1" title="Section Title">
|
<Section tid="section1" title="Section Title">
|
||||||
your input: <Input tid="input1" defaultValue="hello" />
|
your input: <Input tid="input1" defaultValue="hello" />
|
||||||
copy input: <Input value={tango.page.input1?.value} />
|
copy input: <Input value={tango.page.input1?.value} />
|
||||||
|
|||||||
@@ -132,6 +132,9 @@ export const nativeDomPrototypes = () => {
|
|||||||
title: '样式',
|
title: '样式',
|
||||||
group: 'style',
|
group: 'style',
|
||||||
setter: 'expressionSetter',
|
setter: 'expressionSetter',
|
||||||
|
setterProps: {
|
||||||
|
expressionType: 'cssObject',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'className',
|
name: 'className',
|
||||||
@@ -197,6 +200,13 @@ const prototypes: Dict<IComponentPrototype> = {
|
|||||||
title: 'd',
|
title: 'd',
|
||||||
setter: 'textSetter',
|
setter: 'textSetter',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'onClick',
|
||||||
|
title: '点击事件',
|
||||||
|
setter: 'eventSetter',
|
||||||
|
template: '(e) => {\n {{content}}\n}',
|
||||||
|
tip: '回调参数说明:e 为事件对象',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
Columns: {
|
Columns: {
|
||||||
|
|||||||
@@ -20,12 +20,11 @@ import {
|
|||||||
ApiOutlined,
|
ApiOutlined,
|
||||||
AppstoreAddOutlined,
|
AppstoreAddOutlined,
|
||||||
BuildOutlined,
|
BuildOutlined,
|
||||||
ClusterOutlined,
|
|
||||||
FunctionOutlined,
|
FunctionOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
createFromIconfontCN,
|
createFromIconfontCN,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Action } from '@music163/tango-ui';
|
import { Action, PackageOutlined } from '@music163/tango-ui';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
const menuData = {
|
const menuData = {
|
||||||
@@ -114,8 +113,8 @@ export default function App() {
|
|||||||
actions={
|
actions={
|
||||||
<Box px="l">
|
<Box px="l">
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Toolbar.Item key="routeSwitch" placement="left" />
|
<Toolbar.Item key="routeSwitch" placement="left" activeViews={['design']} />
|
||||||
<Toolbar.Item key="addPage" placement="left">
|
<Toolbar.Item key="addPage" placement="left" activeViews={['design']}>
|
||||||
<Action
|
<Action
|
||||||
tooltip="添加页面"
|
tooltip="添加页面"
|
||||||
shape="outline"
|
shape="outline"
|
||||||
@@ -123,10 +122,10 @@ export default function App() {
|
|||||||
onClick={() => setShowNewPageModal(true)}
|
onClick={() => setShowNewPageModal(true)}
|
||||||
/>
|
/>
|
||||||
</Toolbar.Item>
|
</Toolbar.Item>
|
||||||
<Toolbar.Item key="history" placement="left" />
|
<Toolbar.Item key="history" placement="left" activeViews={['design']} />
|
||||||
<Toolbar.Item key="preview" placement="left" />
|
<Toolbar.Item key="preview" placement="left" activeViews={['design']} />
|
||||||
|
<Toolbar.Item key="togglePanel" placement="right" activeViews={['design']} />
|
||||||
<Toolbar.Item key="modeSwitch" placement="right" />
|
<Toolbar.Item key="modeSwitch" placement="right" />
|
||||||
<Toolbar.Item key="togglePanel" placement="right" />
|
|
||||||
<Toolbar.Separator />
|
<Toolbar.Separator />
|
||||||
<Toolbar.Item placement="right">
|
<Toolbar.Item placement="right">
|
||||||
<Space>
|
<Space>
|
||||||
@@ -182,12 +181,15 @@ export default function App() {
|
|||||||
<Sidebar.Item
|
<Sidebar.Item
|
||||||
key="dependency"
|
key="dependency"
|
||||||
label="依赖"
|
label="依赖"
|
||||||
icon={<ClusterOutlined />}
|
icon={<PackageOutlined />}
|
||||||
isFloat
|
isFloat
|
||||||
width={800}
|
width={800}
|
||||||
/>
|
/>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<WorkspacePanel>
|
<WorkspacePanel>
|
||||||
|
<WorkspaceView mode="code">
|
||||||
|
<CodeEditor />
|
||||||
|
</WorkspaceView>
|
||||||
<WorkspaceView mode="design">
|
<WorkspaceView mode="design">
|
||||||
<Sandbox
|
<Sandbox
|
||||||
onMessage={(e) => {
|
onMessage={(e) => {
|
||||||
@@ -209,9 +211,6 @@ export default function App() {
|
|||||||
navigatorExtra={<Button size="small">hello world</Button>}
|
navigatorExtra={<Button size="small">hello world</Button>}
|
||||||
/>
|
/>
|
||||||
</WorkspaceView>
|
</WorkspaceView>
|
||||||
<WorkspaceView mode="code">
|
|
||||||
<CodeEditor />
|
|
||||||
</WorkspaceView>
|
|
||||||
</WorkspacePanel>
|
</WorkspacePanel>
|
||||||
<SettingPanel />
|
<SettingPanel />
|
||||||
</DesignerPanel>
|
</DesignerPanel>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { SystemProvider } from 'coral-system';
|
|||||||
import 'antd/dist/antd.css';
|
import 'antd/dist/antd.css';
|
||||||
|
|
||||||
export const parameters = {
|
export const parameters = {
|
||||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
actions: { argTypesRegex: '^on.*' },
|
||||||
controls: {
|
controls: {
|
||||||
matchers: {
|
matchers: {
|
||||||
color: /(background|color)$/i,
|
color: /(background|color)$/i,
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ const prototypeHasBasicProps: IComponentPrototype = {
|
|||||||
title: 'codeSetter',
|
title: 'codeSetter',
|
||||||
setter: 'codeSetter',
|
setter: 'codeSetter',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'style',
|
||||||
|
title: 'codeSetter(cssObject)',
|
||||||
|
setter: 'codeSetter',
|
||||||
|
setterProps: {
|
||||||
|
expressionType: 'cssObject',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'text',
|
name: 'text',
|
||||||
title: 'textSetter',
|
title: 'textSetter',
|
||||||
@@ -79,6 +87,11 @@ const prototypeHasBasicProps: IComponentPrototype = {
|
|||||||
title: 'textAreaSetter',
|
title: 'textAreaSetter',
|
||||||
setter: 'textAreaSetter',
|
setter: 'textAreaSetter',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'src',
|
||||||
|
title: 'imageSetter',
|
||||||
|
setter: 'imageSetter',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'number',
|
name: 'number',
|
||||||
title: 'numberSetter',
|
title: 'numberSetter',
|
||||||
@@ -263,6 +276,9 @@ export function InitValues() {
|
|||||||
name: 'style',
|
name: 'style',
|
||||||
title: 'codeSetter',
|
title: 'codeSetter',
|
||||||
setter: 'codeSetter',
|
setter: 'codeSetter',
|
||||||
|
setterProps: {
|
||||||
|
expressionType: 'cssObject',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'object',
|
name: 'object',
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ActionSelect } from '@music163/tango-ui';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'UI/ActionSelect',
|
||||||
|
component: ActionSelect,
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{ label: 'action1', value: 'action1' },
|
||||||
|
{ label: 'action2', value: 'action2' },
|
||||||
|
{ label: 'action3', value: 'action3' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Basic = {
|
||||||
|
args: {
|
||||||
|
defaultText: '选择动作',
|
||||||
|
options,
|
||||||
|
onSelect: console.log,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const showInput = {
|
||||||
|
args: {
|
||||||
|
text: '选择动作',
|
||||||
|
options,
|
||||||
|
showInput: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@ export default {
|
|||||||
|
|
||||||
export function Basic() {
|
export function Basic() {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<DragPanel
|
<DragPanel
|
||||||
title="弹层标题"
|
title="弹层标题"
|
||||||
extra="右上角内容"
|
extra="右上角内容"
|
||||||
@@ -18,6 +19,24 @@ export function Basic() {
|
|||||||
>
|
>
|
||||||
<Button>点击唤起浮层</Button>
|
<Button>点击唤起浮层</Button>
|
||||||
</DragPanel>
|
</DragPanel>
|
||||||
|
<DragPanel
|
||||||
|
title="弹层标题"
|
||||||
|
extra="右上角内容"
|
||||||
|
width={350}
|
||||||
|
body={<Box p="m">内容</Box>}
|
||||||
|
footer={<Text>底部信息</Text>}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 20,
|
||||||
|
right: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
底部浮层自动修正弹出区域
|
||||||
|
</Button>
|
||||||
|
</DragPanel>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,3 +60,23 @@ export function FooterCustom() {
|
|||||||
</DragPanel>
|
</DragPanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ResizeablePanel() {
|
||||||
|
return (
|
||||||
|
<DragPanel
|
||||||
|
title="弹层标题"
|
||||||
|
resizeable
|
||||||
|
extra="右上角内容"
|
||||||
|
width={350}
|
||||||
|
height={400}
|
||||||
|
body={
|
||||||
|
<Box p="m" textAlign="center" background="#fa8484">
|
||||||
|
内容
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
footer={<Text>底部信息</Text>}
|
||||||
|
>
|
||||||
|
<Button>点击唤起浮层</Button>
|
||||||
|
</DragPanel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Action, InputCode } from '@music163/tango-ui';
|
import { Action, InputCode, InputStyleCode } from '@music163/tango-ui';
|
||||||
import { BlockOutlined } from '@ant-design/icons';
|
import { BlockOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -28,6 +28,10 @@ export function Basic() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function CSS() {
|
||||||
|
return <InputStyleCode enableESLint suffix={<Action icon={<BlockOutlined />} size="small" />} />;
|
||||||
|
}
|
||||||
|
|
||||||
const code = `
|
const code = `
|
||||||
function foo() {
|
function foo() {
|
||||||
console.log("test");
|
console.log("test");
|
||||||
|
|||||||
+4
-4
@@ -15,10 +15,10 @@
|
|||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"eslint": "eslint packages/**/src/*.{ts,tsx} --cache",
|
"eslint": "eslint packages/**/src/*.{ts,tsx} --cache",
|
||||||
"publish": "lerna publish",
|
"publish": "lerna publish",
|
||||||
"ver": "lerna version --no-private",
|
"ver": "lerna version --no-private --conventional-commits",
|
||||||
"release": "yarn eslint && yarn build && lerna version --no-private --conventional-commits && lerna publish from-git",
|
"release": "yarn eslint && yarn build && yarn ver && lerna publish from-git",
|
||||||
"release:npm": "yarn eslint && yarn build && yarn run ver && lerna publish from-git",
|
"release:npm": "yarn eslint && yarn build && yarn ver && lerna publish from-package",
|
||||||
"release:beta": "yarn eslint && yarn build && yarn run ver && lerna publish from-package --dist-tag beta",
|
"release:beta": "yarn eslint && yarn build && yarn ver && lerna publish from-package --dist-tag beta",
|
||||||
"up": "yarn upgrade-interactive --latest",
|
"up": "yarn upgrade-interactive --latest",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,30 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.1.9](https://github.com/netease/tango/compare/@music163/tango-context@1.1.8...@music163/tango-context@1.1.9) (2024-09-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-context
|
||||||
|
|
||||||
|
## [1.1.8](https://github.com/netease/tango/compare/@music163/tango-context@1.1.7...@music163/tango-context@1.1.8) (2024-08-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-context
|
||||||
|
|
||||||
|
## [1.1.7](https://github.com/netease/tango/compare/@music163/tango-context@1.1.6...@music163/tango-context@1.1.7) (2024-08-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-context
|
||||||
|
|
||||||
|
## [1.1.6](https://github.com/netease/tango/compare/@music163/tango-context@1.1.5...@music163/tango-context@1.1.6) (2024-08-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-context
|
||||||
|
|
||||||
|
## [1.1.5](https://github.com/netease/tango/compare/@music163/tango-context@1.1.4...@music163/tango-context@1.1.5) (2024-07-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-context
|
||||||
|
|
||||||
|
## [1.1.4](https://github.com/netease/tango/compare/@music163/tango-context@1.1.3...@music163/tango-context@1.1.4) (2024-06-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-context
|
||||||
|
|
||||||
## [1.1.3](https://github.com/netease/tango/compare/@music163/tango-context@1.1.2...@music163/tango-context@1.1.3) (2024-06-05)
|
## [1.1.3](https://github.com/netease/tango/compare/@music163/tango-context@1.1.2...@music163/tango-context@1.1.3) (2024-06-05)
|
||||||
|
|
||||||
**Note:** Version bump only for package @music163/tango-context
|
**Note:** Version bump only for package @music163/tango-context
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-context",
|
"name": "@music163/tango-context",
|
||||||
"version": "1.1.3",
|
"version": "1.1.9",
|
||||||
"description": "react context for tango-apps",
|
"description": "react context for tango-apps",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
@@ -31,8 +31,8 @@
|
|||||||
"react": ">= 16.8"
|
"react": ">= 16.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@music163/tango-core": "^1.3.1",
|
"@music163/tango-core": "^1.4.3",
|
||||||
"@music163/tango-helpers": "^1.2.0",
|
"@music163/tango-helpers": "^1.2.3",
|
||||||
"mobx-react-lite": "4.0.7"
|
"mobx-react-lite": "4.0.7"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@@ -3,6 +3,42 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.4.3](https://github.com/netease/tango/compare/@music163/tango-core@1.4.2...@music163/tango-core@1.4.3) (2024-09-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-core
|
||||||
|
|
||||||
|
## [1.4.2](https://github.com/netease/tango/compare/@music163/tango-core@1.4.1...@music163/tango-core@1.4.2) (2024-08-06)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- view module \_nodesTree set init value ([#194](https://github.com/netease/tango/issues/194)) ([269e304](https://github.com/netease/tango/commit/269e304d685c4fcc80635b9d04220cda333b80c4))
|
||||||
|
|
||||||
|
## [1.4.1](https://github.com/netease/tango/compare/@music163/tango-core@1.4.0...@music163/tango-core@1.4.1) (2024-08-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-core
|
||||||
|
|
||||||
|
# [1.4.0](https://github.com/netease/tango/compare/@music163/tango-core@1.3.3...@music163/tango-core@1.4.0) (2024-08-01)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update types ([67d602a](https://github.com/netease/tango/commit/67d602a3ec6b7f74156bb78fda6f3b4bc2676e55))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- add isError and errorMessage to File & add isAstSynced to Module ([#190](https://github.com/netease/tango/issues/190)) ([8dd289c](https://github.com/netease/tango/commit/8dd289cd7daba628e54dc6b8929f22a1e2245160))
|
||||||
|
|
||||||
|
## [1.3.3](https://github.com/netease/tango/compare/@music163/tango-core@1.3.2...@music163/tango-core@1.3.3) (2024-07-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update designer config ([#186](https://github.com/netease/tango/issues/186)) ([85c053b](https://github.com/netease/tango/commit/85c053b3db6d652b41c9873dba1366315174833f))
|
||||||
|
|
||||||
|
## [1.3.2](https://github.com/netease/tango/compare/@music163/tango-core@1.3.1...@music163/tango-core@1.3.2) (2024-06-20)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update designer style ([#176](https://github.com/netease/tango/issues/176)) ([0f3f0af](https://github.com/netease/tango/commit/0f3f0afdfa8aee2532a97c5c2e92ef4230397d86))
|
||||||
|
|
||||||
## [1.3.1](https://github.com/netease/tango/compare/@music163/tango-core@1.3.0...@music163/tango-core@1.3.1) (2024-06-05)
|
## [1.3.1](https://github.com/netease/tango/compare/@music163/tango-core@1.3.0...@music163/tango-core@1.3.1) (2024-06-05)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-core",
|
"name": "@music163/tango-core",
|
||||||
"version": "1.3.1",
|
"version": "1.4.3",
|
||||||
"description": "tango core",
|
"description": "tango core",
|
||||||
"author": "wwsun <ww.sun@outlook.com>",
|
"author": "wwsun <ww.sun@outlook.com>",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"@babel/parser": "^7.23.5",
|
"@babel/parser": "^7.23.5",
|
||||||
"@babel/traverse": "^7.23.5",
|
"@babel/traverse": "^7.23.5",
|
||||||
"@babel/types": "^7.23.5",
|
"@babel/types": "^7.23.5",
|
||||||
"@music163/tango-helpers": "^1.2.0",
|
"@music163/tango-helpers": "^1.2.3",
|
||||||
"@types/babel__generator": "^7.6.7",
|
"@types/babel__generator": "^7.6.7",
|
||||||
"@types/babel__traverse": "^7.20.4",
|
"@types/babel__traverse": "^7.20.4",
|
||||||
"mobx": "6.12.3",
|
"mobx": "6.12.3",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function isTangoVariable(name: string) {
|
|||||||
return /^tango\??\.(stores|services)\??\./.test(name) && name.split('.').length > 2;
|
return /^tango\??\.(stores|services)\??\./.test(name) && name.split('.').length > 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const templatePattern = /^{(.+)}$/s;
|
const templatePattern = /^{(.+)}$/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断给定字符串是否被表达式容器`{expCode}`包裹
|
* 判断给定字符串是否被表达式容器`{expCode}`包裹
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import generator, { GeneratorOptions } from '@babel/generator';
|
import generator, { GeneratorOptions } from '@babel/generator';
|
||||||
import * as t from '@babel/types';
|
import * as t from '@babel/types';
|
||||||
import { logger, wrapCode } from '@music163/tango-helpers';
|
import { Dict, logger, wrapCode } from '@music163/tango-helpers';
|
||||||
import { formatCode } from '../string';
|
import { formatCode } from '../string';
|
||||||
|
|
||||||
const defaultGeneratorOptions: GeneratorOptions = {
|
const defaultGeneratorOptions: GeneratorOptions = {
|
||||||
@@ -25,19 +25,7 @@ export function ast2code(ast: t.Node, options: GeneratorOptions = defaultGenerat
|
|||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bracketPattern = /^\(.+\)$/s;
|
const bracketPattern = /^\(.+\)$/;
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否被 () 包裹
|
|
||||||
*
|
|
||||||
* @example ({ foo: 'foo' }) -> true
|
|
||||||
* @example { foo: 'foo' } -> false
|
|
||||||
*
|
|
||||||
* @param str 目标字符串
|
|
||||||
*/
|
|
||||||
function isWrappingWithBrackets(str: string) {
|
|
||||||
return bracketPattern.test(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将表达式生成为块级代码
|
* 将表达式生成为块级代码
|
||||||
@@ -54,7 +42,7 @@ export function expression2code(node: t.Expression) {
|
|||||||
|
|
||||||
const isWrappingExpression = t.isObjectExpression(node) || t.isFunctionExpression(node);
|
const isWrappingExpression = t.isObjectExpression(node) || t.isFunctionExpression(node);
|
||||||
|
|
||||||
if (isWrappingExpression && isWrappingWithBrackets(ret)) {
|
if (isWrappingExpression && bracketPattern.test(ret)) {
|
||||||
// 如果是对象,输出包含 ({}),则去掉首尾的括号
|
// 如果是对象,输出包含 ({}),则去掉首尾的括号
|
||||||
ret = ret.slice(1, -1);
|
ret = ret.slice(1, -1);
|
||||||
}
|
}
|
||||||
@@ -195,7 +183,7 @@ export function node2value(node: t.Node, isWrapCode = true): any {
|
|||||||
);
|
);
|
||||||
if (isSimpleObject) {
|
if (isSimpleObject) {
|
||||||
// simple object: { key1, key2, key3 }
|
// simple object: { key1, key2, key3 }
|
||||||
ret = node.properties.reduce((prev, propertyNode) => {
|
ret = node.properties.reduce<Dict>((prev, propertyNode) => {
|
||||||
if (propertyNode.type === 'ObjectProperty') {
|
if (propertyNode.type === 'ObjectProperty') {
|
||||||
const key = keyNode2value(propertyNode.key);
|
const key = keyNode2value(propertyNode.key);
|
||||||
const value = node2value(propertyNode.value, isWrapCode);
|
const value = node2value(propertyNode.value, isWrapCode);
|
||||||
|
|||||||
@@ -68,11 +68,7 @@ export function isValidExpressionCode(code: string) {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function code2ast(code: string): t.File {
|
export function code2ast(code: string): t.File {
|
||||||
try {
|
|
||||||
return parse(code, babelParserConfig);
|
return parse(code, babelParserConfig);
|
||||||
} catch (err) {
|
|
||||||
logger.error('[code2ast failed!]', err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,7 +171,7 @@ export function value2node(
|
|||||||
* 将 js 普通对象解析为 t.Node
|
* 将 js 普通对象解析为 t.Node
|
||||||
*/
|
*/
|
||||||
export function object2node(
|
export function object2node(
|
||||||
obj: object,
|
obj: Dict,
|
||||||
getValueNode: (value: any, key?: string) => t.Expression = value2node,
|
getValueNode: (value: any, key?: string) => t.Expression = value2node,
|
||||||
) {
|
) {
|
||||||
if (!isPlainObject(obj)) {
|
if (!isPlainObject(obj)) {
|
||||||
|
|||||||
@@ -1009,7 +1009,7 @@ export function serviceConfig2Node(payload: object) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateServiceConfigToServiceFile(ast: t.File, config: Dict<object>) {
|
export function updateServiceConfigToServiceFile(ast: t.File, config: Dict<Dict>) {
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
CallExpression(path) {
|
CallExpression(path) {
|
||||||
const calleeName = keyNode2value(path.node.callee) as string;
|
const calleeName = keyNode2value(path.node.callee) as string;
|
||||||
@@ -1017,7 +1017,7 @@ export function updateServiceConfigToServiceFile(ast: t.File, config: Dict<objec
|
|||||||
const configNode = path.node.arguments[0];
|
const configNode = path.node.arguments[0];
|
||||||
|
|
||||||
if (t.isObjectExpression(configNode)) {
|
if (t.isObjectExpression(configNode)) {
|
||||||
const newPropertiesNodeMap = Object.keys(config).reduce((properties, key) => {
|
const newPropertiesNodeMap = Object.keys(config).reduce<Dict>((properties, key) => {
|
||||||
const serviceConfig = config[key];
|
const serviceConfig = config[key];
|
||||||
const property = t.objectProperty(t.identifier(key), serviceConfig2Node(serviceConfig));
|
const property = t.objectProperty(t.identifier(key), serviceConfig2Node(serviceConfig));
|
||||||
properties[key] = property;
|
properties[key] = property;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function namesToImportDeclarations(
|
|||||||
names: string[],
|
names: string[],
|
||||||
nameMap: Dict<IImportSpecifierSourceData>,
|
nameMap: Dict<IImportSpecifierSourceData>,
|
||||||
) {
|
) {
|
||||||
const map = {};
|
const map: Dict = {};
|
||||||
names.forEach((name) => {
|
names.forEach((name) => {
|
||||||
const mod = nameMap[name];
|
const mod = nameMap[name];
|
||||||
if (mod) {
|
if (mod) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { parseDndId, uuid } from '@music163/tango-helpers';
|
import { Dict, parseDndId, uuid } from '@music163/tango-helpers';
|
||||||
|
|
||||||
// { id: 1, component, props: { id: 1 }, children: [ { id: 2, component } ] }
|
// { id: 1, component, props: { id: 1 }, children: [ { id: 2, component } ] }
|
||||||
export function deepCloneNode(obj: any, component?: string): any {
|
export function deepCloneNode(obj: any, component?: string): any {
|
||||||
function deepCloneObject() {
|
function deepCloneObject() {
|
||||||
const target = {};
|
const target: Dict = {};
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (Object.hasOwn(obj, key)) {
|
if (Object.hasOwn(obj, key)) {
|
||||||
target[key] = deepCloneNode(obj[key], component);
|
target[key] = deepCloneNode(obj[key], component);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class TangoComponentsEntryModule extends TangoModule {
|
|||||||
|
|
||||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||||
super(workspace, props, false);
|
super(workspace, props, false);
|
||||||
this.update(props.code, false, false);
|
this.update(props.code, true, false);
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
_code: observable,
|
_code: observable,
|
||||||
_cleanCode: observable,
|
_cleanCode: observable,
|
||||||
|
|||||||
@@ -239,6 +239,13 @@ export class Designer {
|
|||||||
|
|
||||||
setActiveView(view: DesignerViewType) {
|
setActiveView(view: DesignerViewType) {
|
||||||
this._activeView = view;
|
this._activeView = view;
|
||||||
|
if (view === 'dual') {
|
||||||
|
this.setActiveSidebarPanel('');
|
||||||
|
this.toggleRightPanel(false);
|
||||||
|
this.toggleIsPreview(true);
|
||||||
|
} else if (view === 'design') {
|
||||||
|
this.toggleIsPreview(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setActiveSidebarPanel(panel: string) {
|
setActiveSidebarPanel(panel: string) {
|
||||||
|
|||||||
@@ -24,6 +24,16 @@ export class TangoFile {
|
|||||||
*/
|
*/
|
||||||
lastModified: number;
|
lastModified: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件解析是否出错
|
||||||
|
*/
|
||||||
|
isError: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件解析错误消息
|
||||||
|
*/
|
||||||
|
errorMessage: string;
|
||||||
|
|
||||||
_code: string;
|
_code: string;
|
||||||
_cleanCode: string;
|
_cleanCode: string;
|
||||||
|
|
||||||
@@ -40,6 +50,7 @@ export class TangoFile {
|
|||||||
this.filename = props.filename;
|
this.filename = props.filename;
|
||||||
this.type = props.type;
|
this.type = props.type;
|
||||||
this.lastModified = Date.now();
|
this.lastModified = Date.now();
|
||||||
|
this.isError = false;
|
||||||
|
|
||||||
// 这里主要是为了解决 umi ts 编译错误的问题,@see https://github.com/umijs/umi/issues/7594
|
// 这里主要是为了解决 umi ts 编译错误的问题,@see https://github.com/umijs/umi/issues/7594
|
||||||
if (isSyncCode) {
|
if (isSyncCode) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
IPageConfigData,
|
IPageConfigData,
|
||||||
IImportSpecifierSourceData,
|
IImportSpecifierSourceData,
|
||||||
IImportSpecifierData,
|
IImportSpecifierData,
|
||||||
|
IFileError,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { TangoFile, TangoJsonFile } from './file';
|
import { TangoFile, TangoJsonFile } from './file';
|
||||||
import { TangoRouteModule } from './route-module';
|
import { TangoRouteModule } from './route-module';
|
||||||
@@ -192,7 +193,18 @@ export interface IWorkspace {
|
|||||||
renameFile: (oldFilename: string, newFilename: string) => void;
|
renameFile: (oldFilename: string, newFilename: string) => void;
|
||||||
renameFolder: (oldFoldername: string, newFoldername: string) => void;
|
renameFolder: (oldFoldername: string, newFoldername: string) => void;
|
||||||
|
|
||||||
updateFile: (filename: string, code: string, shouldFormatCode?: boolean) => void;
|
/**
|
||||||
|
* 更新文件
|
||||||
|
* @param filename 文件名
|
||||||
|
* @param code 代码
|
||||||
|
* @param isSyncAst 是否同步 ast
|
||||||
|
*/
|
||||||
|
updateFile: (filename: string, code: string, isSyncAst?: boolean) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查并同步文件的 ast
|
||||||
|
*/
|
||||||
|
syncFiles: () => void;
|
||||||
|
|
||||||
listFiles: () => Record<string, string>;
|
listFiles: () => Record<string, string>;
|
||||||
getFile: (filename: string) => TangoFile;
|
getFile: (filename: string) => TangoFile;
|
||||||
@@ -298,8 +310,12 @@ export interface IWorkspace {
|
|||||||
get bizComps(): string[];
|
get bizComps(): string[];
|
||||||
get baseComps(): string[];
|
get baseComps(): string[];
|
||||||
get localComps(): string[];
|
get localComps(): string[];
|
||||||
|
get fileErrors(): IFileError[];
|
||||||
/**
|
/**
|
||||||
* 是否是合法的项目
|
* 是否是有效的项目
|
||||||
|
* - 包含 tango.config.json
|
||||||
|
* - 包含视图模块
|
||||||
|
* - 没有文件错误
|
||||||
*/
|
*/
|
||||||
get isValid(): boolean;
|
get isValid(): boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ import { IWorkspace } from './interfaces';
|
|||||||
export class TangoModule extends TangoFile {
|
export class TangoModule extends TangoFile {
|
||||||
ast: t.File;
|
ast: t.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ast 是否与 code 保持同步
|
||||||
|
*/
|
||||||
|
isAstSynced: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入的依赖列表
|
* 导入的依赖列表
|
||||||
*/
|
*/
|
||||||
@@ -33,24 +38,54 @@ export class TangoModule extends TangoFile {
|
|||||||
/**
|
/**
|
||||||
* 基于最新的 ast 进行同步
|
* 基于最新的 ast 进行同步
|
||||||
* @param code 如果传入 code,则基于 code 进行同步
|
* @param code 如果传入 code,则基于 code 进行同步
|
||||||
* @param isFormatCode 是否格式化代码
|
* @param isSyncAst 是否同步 ast
|
||||||
* @param refreshWorkspace 是否刷新 workspace
|
* @param isRefreshWorkspace 是否刷新 workspace
|
||||||
*/
|
*/
|
||||||
update(code?: string, isFormatCode = true, refreshWorkspace = true) {
|
update(code?: string, isSyncAst = true, isRefreshWorkspace = true) {
|
||||||
this.lastModified = Date.now();
|
this.lastModified = Date.now();
|
||||||
|
|
||||||
|
try {
|
||||||
if (isNil(code)) {
|
if (isNil(code)) {
|
||||||
this._syncByAst();
|
this._syncByAst();
|
||||||
} else {
|
} else {
|
||||||
this._syncByCode(code, isFormatCode);
|
this._syncByCode(code, isSyncAst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isSyncAst) {
|
||||||
this._analysisAst();
|
this._analysisAst();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isAstSynced = isSyncAst;
|
||||||
|
this.isError = false;
|
||||||
|
this.errorMessage = undefined;
|
||||||
|
|
||||||
this.workspace.onFilesChange([this.filename]);
|
this.workspace.onFilesChange([this.filename]);
|
||||||
|
if (isRefreshWorkspace) {
|
||||||
if (refreshWorkspace) {
|
|
||||||
this.workspace.refresh([this.filename]);
|
this.workspace.refresh([this.filename]);
|
||||||
}
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
this.isError = true;
|
||||||
|
this.errorMessage = err.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于当前的代码重新生成 ast
|
||||||
|
*/
|
||||||
|
updateAst() {
|
||||||
|
if (!this.isAstSynced) {
|
||||||
|
try {
|
||||||
|
this.ast = code2ast(this._code);
|
||||||
|
this._analysisAst();
|
||||||
|
this.isAstSynced = true;
|
||||||
|
this.isError = false;
|
||||||
|
this.errorMessage = undefined;
|
||||||
|
} catch (err: any) {
|
||||||
|
this.isAstSynced = false;
|
||||||
|
this.isError = true;
|
||||||
|
this.errorMessage = err.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addImportDeclaration(source: string, specifiers: IImportSpecifierData[]) {
|
addImportDeclaration(source: string, specifiers: IImportSpecifierData[]) {
|
||||||
@@ -75,23 +110,27 @@ export class TangoModule extends TangoFile {
|
|||||||
/**
|
/**
|
||||||
* 基于输入的源码进行同步
|
* 基于输入的源码进行同步
|
||||||
* @param code 源码
|
* @param code 源码
|
||||||
* @param isFormatCode 是否格式化代码
|
* @param isSyncAst 是否同步 ast
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
_syncByCode(code: string, isFormatCode = true) {
|
_syncByCode(code: string, isSyncAst = true) {
|
||||||
if (code === this._code) {
|
if (code === this._code) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提前格式化代码
|
// 提前格式化代码
|
||||||
if (isFormatCode) {
|
try {
|
||||||
code = formatCode(code);
|
code = formatCode(code);
|
||||||
|
} catch (err) {
|
||||||
|
// err ignored, format code failed
|
||||||
}
|
}
|
||||||
|
|
||||||
this._code = code;
|
this._code = code;
|
||||||
this._cleanCode = code;
|
this._cleanCode = code;
|
||||||
|
if (isSyncAst) {
|
||||||
this.ast = code2ast(code);
|
this.ast = code2ast(code);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_analysisAst() {
|
_analysisAst() {
|
||||||
const { imports } = traverseFile(this.ast);
|
const { imports } = traverseFile(this.ast);
|
||||||
@@ -105,14 +144,17 @@ export class TangoModule extends TangoFile {
|
|||||||
export class TangoJsModule extends TangoModule {
|
export class TangoJsModule extends TangoModule {
|
||||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||||
super(workspace, props, false);
|
super(workspace, props, false);
|
||||||
this.update(props.code, false, false);
|
this.update(props.code, true, false);
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
_code: observable,
|
_code: observable,
|
||||||
_cleanCode: observable,
|
_cleanCode: observable,
|
||||||
|
isError: observable,
|
||||||
|
errorMessage: observable,
|
||||||
code: computed,
|
code: computed,
|
||||||
cleanCode: computed,
|
cleanCode: computed,
|
||||||
update: action,
|
update: action,
|
||||||
|
updateAst: action,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { action, computed, makeObservable, observable, toJS } from 'mobx';
|
import { action, computed, makeObservable, observable, toJS } from 'mobx';
|
||||||
|
import { logger } from '@music163/tango-helpers';
|
||||||
import {
|
import {
|
||||||
traverseRouteFile,
|
traverseRouteFile,
|
||||||
addRouteToRouteFile,
|
addRouteToRouteFile,
|
||||||
@@ -27,10 +28,13 @@ export class TangoRouteModule extends TangoModule {
|
|||||||
_routes: observable,
|
_routes: observable,
|
||||||
_code: observable,
|
_code: observable,
|
||||||
_cleanCode: observable,
|
_cleanCode: observable,
|
||||||
|
isError: observable,
|
||||||
|
errorMessage: observable,
|
||||||
routes: computed,
|
routes: computed,
|
||||||
code: computed,
|
code: computed,
|
||||||
cleanCode: computed,
|
cleanCode: computed,
|
||||||
update: action,
|
update: action,
|
||||||
|
updateAst: action,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +78,7 @@ export class TangoRouteModule extends TangoModule {
|
|||||||
*/
|
*/
|
||||||
removeRoute(route: string) {
|
removeRoute(route: string) {
|
||||||
if (route === '/') {
|
if (route === '/') {
|
||||||
console.warn('index route should not be removed!');
|
logger.warn('index route should not be removed!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const record = this.getRouteByRoutePath(route);
|
const record = this.getRouteByRoutePath(route);
|
||||||
|
|||||||
@@ -49,11 +49,14 @@ export class TangoServiceModule extends TangoModule {
|
|||||||
_baseConfig: observable,
|
_baseConfig: observable,
|
||||||
_code: observable,
|
_code: observable,
|
||||||
_cleanCode: observable,
|
_cleanCode: observable,
|
||||||
|
isError: observable,
|
||||||
|
errorMessage: observable,
|
||||||
serviceFunctions: computed,
|
serviceFunctions: computed,
|
||||||
baseConfig: computed,
|
baseConfig: computed,
|
||||||
cleanCode: computed,
|
cleanCode: computed,
|
||||||
code: computed,
|
code: computed,
|
||||||
update: action,
|
update: action,
|
||||||
|
updateAst: action,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,13 @@ export class TangoStoreEntryModule extends TangoModule {
|
|||||||
_stores: observable,
|
_stores: observable,
|
||||||
_code: observable,
|
_code: observable,
|
||||||
_cleanCode: observable,
|
_cleanCode: observable,
|
||||||
|
isError: observable,
|
||||||
|
errorMessage: observable,
|
||||||
stores: computed,
|
stores: computed,
|
||||||
code: computed,
|
code: computed,
|
||||||
cleanCode: computed,
|
cleanCode: computed,
|
||||||
update: action,
|
update: action,
|
||||||
|
updateAst: action,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ function nodeListToTreeData(list: ITangoViewNodeData[]) {
|
|||||||
*/
|
*/
|
||||||
export class TangoViewModule extends TangoModule implements IViewFile {
|
export class TangoViewModule extends TangoModule implements IViewFile {
|
||||||
// 解析为树结构的 jsxNodes 数组
|
// 解析为树结构的 jsxNodes 数组
|
||||||
_nodesTree: ITangoViewNodeData[];
|
_nodesTree: ITangoViewNodeData[] = [];
|
||||||
/**
|
/**
|
||||||
* 通过导入组件名查找组件来自的包
|
* 通过导入组件名查找组件来自的包
|
||||||
*/
|
*/
|
||||||
@@ -138,10 +138,14 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
|||||||
_code: observable,
|
_code: observable,
|
||||||
_cleanCode: observable,
|
_cleanCode: observable,
|
||||||
|
|
||||||
|
isError: observable,
|
||||||
|
errorMessage: observable,
|
||||||
|
|
||||||
code: computed,
|
code: computed,
|
||||||
cleanCode: computed,
|
cleanCode: computed,
|
||||||
|
|
||||||
update: action,
|
update: action,
|
||||||
|
updateAst: action,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +324,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
|||||||
updateNodeAttributes(nodeId: string, config: Record<string, any>, relatedImports?: string[]) {
|
updateNodeAttributes(nodeId: string, config: Record<string, any>, relatedImports?: string[]) {
|
||||||
if (relatedImports && relatedImports.length) {
|
if (relatedImports && relatedImports.length) {
|
||||||
// 导入依赖的组件
|
// 导入依赖的组件
|
||||||
const newImportData = relatedImports.reduce((prev, name) => {
|
const newImportData = relatedImports.reduce<Dict<IImportSpecifierData[]>>((prev, name) => {
|
||||||
const proto = this.workspace.getPrototype(name);
|
const proto = this.workspace.getPrototype(name);
|
||||||
const { source, specifiers } = prototype2importDeclarationData(proto, this.filename);
|
const { source, specifiers } = prototype2importDeclarationData(proto, this.filename);
|
||||||
const existSpecifiers: IImportSpecifierData[] = prev[source];
|
const existSpecifiers: IImportSpecifierData[] = prev[source];
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
parseServiceVariablePath,
|
parseServiceVariablePath,
|
||||||
parseStoreVariablePath,
|
parseStoreVariablePath,
|
||||||
uniq,
|
uniq,
|
||||||
|
setValue,
|
||||||
} from '@music163/tango-helpers';
|
} from '@music163/tango-helpers';
|
||||||
import {
|
import {
|
||||||
prototype2jsxElement,
|
prototype2jsxElement,
|
||||||
@@ -24,10 +25,10 @@ import {
|
|||||||
import { DropMethod } from './drop-target';
|
import { DropMethod } from './drop-target';
|
||||||
import { HistoryMessage, TangoHistory } from './history';
|
import { HistoryMessage, TangoHistory } from './history';
|
||||||
import { TangoNode } from './node';
|
import { TangoNode } from './node';
|
||||||
import { TangoJsModule } from './module';
|
import { TangoJsModule, TangoModule } from './module';
|
||||||
import { TangoFile, TangoJsonFile, TangoLessFile } from './file';
|
import { TangoFile, TangoJsonFile, TangoLessFile } from './file';
|
||||||
import { IWorkspace } from './interfaces';
|
import { IWorkspace } from './interfaces';
|
||||||
import { IFileConfig, FileType, ITangoConfigPackages, IPageConfigData } from '../types';
|
import { IFileConfig, FileType, ITangoConfigPackages, IPageConfigData, IFileError } from '../types';
|
||||||
import { SelectSource } from './select-source';
|
import { SelectSource } from './select-source';
|
||||||
import { DragSource } from './drag-source';
|
import { DragSource } from './drag-source';
|
||||||
import { TangoRouteModule } from './route-module';
|
import { TangoRouteModule } from './route-module';
|
||||||
@@ -150,11 +151,8 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
*/
|
*/
|
||||||
private copyTempNodes: TangoNode[];
|
private copyTempNodes: TangoNode[];
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否是合法的项目
|
|
||||||
*/
|
|
||||||
get isValid() {
|
get isValid() {
|
||||||
return !!this.tangoConfigJson && !!this.activeViewModule;
|
return !!this.tangoConfigJson && !!this.activeViewModule && this.fileErrors.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -222,6 +220,19 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
return Object.keys(this.componentsEntryModule?.exportList || {});
|
return Object.keys(this.componentsEntryModule?.exportList || {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get fileErrors() {
|
||||||
|
const errors: IFileError[] = [];
|
||||||
|
this.files.forEach((file) => {
|
||||||
|
if (file.isError) {
|
||||||
|
errors.push({
|
||||||
|
filename: file.filename,
|
||||||
|
message: file.errorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(options?: IWorkspaceOptions) {
|
constructor(options?: IWorkspaceOptions) {
|
||||||
super();
|
super();
|
||||||
this.history = new TangoHistory(this);
|
this.history = new TangoHistory(this);
|
||||||
@@ -255,6 +266,8 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
activeViewFile: observable,
|
activeViewFile: observable,
|
||||||
pages: computed,
|
pages: computed,
|
||||||
bizComps: computed,
|
bizComps: computed,
|
||||||
|
fileErrors: computed,
|
||||||
|
isValid: computed,
|
||||||
setActiveRoute: action,
|
setActiveRoute: action,
|
||||||
setActiveFile: action,
|
setActiveFile: action,
|
||||||
addFile: action,
|
addFile: action,
|
||||||
@@ -438,10 +451,16 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFile(filename: string, code: string, shouldFormatCode = false) {
|
updateFile(filename: string, code: string, isSyncAst = true) {
|
||||||
const file = this.getFile(filename);
|
const file = this.getFile(filename);
|
||||||
|
if (file instanceof TangoModule) {
|
||||||
|
file.update(code, isSyncAst);
|
||||||
|
} else {
|
||||||
file.update(code);
|
file.update(code);
|
||||||
if (shouldFormatCode && file instanceof TangoViewModule) {
|
}
|
||||||
|
|
||||||
|
const shouldFormat = this.projectConfig?.designerConfig?.autoFormatCode;
|
||||||
|
if (shouldFormat && file instanceof TangoViewModule) {
|
||||||
file.removeUnusedImportSpecifiers().update();
|
file.removeUnusedImportSpecifiers().update();
|
||||||
}
|
}
|
||||||
this.history.push({
|
this.history.push({
|
||||||
@@ -452,12 +471,15 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
syncFiles() {
|
||||||
* 删除工作区的文件
|
this.files.forEach((file) => {
|
||||||
* @param filename
|
if (file instanceof TangoModule) {
|
||||||
*/
|
file.updateAst();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
removeFile(filename: string) {
|
removeFile(filename: string) {
|
||||||
// TODO: refactor visitFile to share this logic
|
|
||||||
if (this.files.get(filename)) {
|
if (this.files.get(filename)) {
|
||||||
// 如果是文件,直接删除
|
// 如果是文件,直接删除
|
||||||
this.files.delete(filename);
|
this.files.delete(filename);
|
||||||
@@ -513,7 +535,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
* @returns { [filename]: fileCode }
|
* @returns { [filename]: fileCode }
|
||||||
*/
|
*/
|
||||||
listFiles() {
|
listFiles() {
|
||||||
const ret = {};
|
const ret: Dict<string> = {};
|
||||||
this.files.forEach((file) => {
|
this.files.forEach((file) => {
|
||||||
ret[file.filename] = file.cleanCode;
|
ret[file.filename] = file.cleanCode;
|
||||||
});
|
});
|
||||||
@@ -741,6 +763,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
* 更新依赖,没有就添加
|
* 更新依赖,没有就添加
|
||||||
* @param name
|
* @param name
|
||||||
* @param version
|
* @param version
|
||||||
|
* FIXME: 参数3的设置需要重新考虑下
|
||||||
*/
|
*/
|
||||||
updateDependency(
|
updateDependency(
|
||||||
name: string,
|
name: string,
|
||||||
@@ -763,15 +786,12 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
if (!packages) {
|
if (!packages) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (options?.package || packages[name]) {
|
|
||||||
packages[name] = {
|
setValue(packages, name, {
|
||||||
type: packages[name]?.type,
|
...packages[name], // 保留原有的配置
|
||||||
// 如果没有传入 package 信息,则沿用 tango.config.json 中已记录的数据
|
version, // 更新版本号
|
||||||
// 如果传入了,则全量使用传入的信息,适配可能在某个版本中如 resources 等字段被删除的情况
|
...options?.package, // 更新 package 配置
|
||||||
...(options?.package || packages[name]),
|
});
|
||||||
version,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return packages;
|
return packages;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ export interface IFileConfig {
|
|||||||
type?: FileType;
|
type?: FileType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IFileError {
|
||||||
|
filename: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 视图节点数据类型
|
* 视图节点数据类型
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,62 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.4.5](https://github.com/netease/tango/compare/@music163/tango-designer@1.4.4...@music163/tango-designer@1.4.5) (2024-09-02)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- add prop value template & fix components popover ([#201](https://github.com/netease/tango/issues/201)) ([c4e1ed6](https://github.com/netease/tango/commit/c4e1ed67f55deb17dae740559602cd0adcfc8eb4))
|
||||||
|
|
||||||
|
## [1.4.4](https://github.com/netease/tango/compare/@music163/tango-designer@1.4.3...@music163/tango-designer@1.4.4) (2024-08-16)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- CodeInput 支持 jsx css 代码补全 ([#198](https://github.com/netease/tango/issues/198)) ([6de5488](https://github.com/netease/tango/commit/6de54884b50d683c74d695a8c13184dc0ce8d3c4))
|
||||||
|
|
||||||
|
## [1.4.3](https://github.com/netease/tango/compare/@music163/tango-designer@1.4.2...@music163/tango-designer@1.4.3) (2024-08-09)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- file errors overlay support click to error file & optimize ui ([#197](https://github.com/netease/tango/issues/197)) ([ca56edd](https://github.com/netease/tango/commit/ca56edd9ae201dbeb84b03ad390a09085433a257))
|
||||||
|
- route matching without query string & toggle button wrapping ([#195](https://github.com/netease/tango/issues/195)) ([9394666](https://github.com/netease/tango/commit/939466648d40331800f15e886256a0789b7e7623))
|
||||||
|
- update mobile simulator ui ([#196](https://github.com/netease/tango/issues/196)) ([65149f1](https://github.com/netease/tango/commit/65149f1326eaf78e8a27222993c3cacc674cd378))
|
||||||
|
|
||||||
|
## [1.4.2](https://github.com/netease/tango/compare/@music163/tango-designer@1.4.1...@music163/tango-designer@1.4.2) (2024-08-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-designer
|
||||||
|
|
||||||
|
## [1.4.1](https://github.com/netease/tango/compare/@music163/tango-designer@1.4.0...@music163/tango-designer@1.4.1) (2024-08-05)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- parse template code & update maxRow of TextAreaSetter ([#192](https://github.com/netease/tango/issues/192)) ([a1eea02](https://github.com/netease/tango/commit/a1eea02f9409db73f042c74d7903ad84258f25c2))
|
||||||
|
- show right panel when design view select node & optimize editor css ([#193](https://github.com/netease/tango/issues/193)) ([b83d965](https://github.com/netease/tango/commit/b83d965955c8d33ed8d37063482c7abe079e62b1))
|
||||||
|
|
||||||
|
# [1.4.0](https://github.com/netease/tango/compare/@music163/tango-designer@1.3.3...@music163/tango-designer@1.4.0) (2024-08-01)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update types ([67d602a](https://github.com/netease/tango/commit/67d602a3ec6b7f74156bb78fda6f3b4bc2676e55))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- add isError and errorMessage to File & add isAstSynced to Module ([#190](https://github.com/netease/tango/issues/190)) ([8dd289c](https://github.com/netease/tango/commit/8dd289cd7daba628e54dc6b8929f22a1e2245160))
|
||||||
|
|
||||||
|
## [1.3.3](https://github.com/netease/tango/compare/@music163/tango-designer@1.3.2...@music163/tango-designer@1.3.3) (2024-07-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- selection bug without prototype & update ToggleButton style ([#180](https://github.com/netease/tango/issues/180)) ([1d0fb4f](https://github.com/netease/tango/commit/1d0fb4f274a5b767962c54583ab5863329d5bcc5))
|
||||||
|
- update designer config ([#186](https://github.com/netease/tango/issues/186)) ([85c053b](https://github.com/netease/tango/commit/85c053b3db6d652b41c9873dba1366315174833f))
|
||||||
|
|
||||||
|
## [1.3.2](https://github.com/netease/tango/compare/@music163/tango-designer@1.3.1...@music163/tango-designer@1.3.2) (2024-06-20)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- drag-panel add resizeable & height prop ([#175](https://github.com/netease/tango/issues/175)) ([b53a9f7](https://github.com/netease/tango/commit/b53a9f7eefc748ba8a7c94270c3a374b9d6ed211))
|
||||||
|
- fix global hotkey triggering on content editable elements ([#174](https://github.com/netease/tango/issues/174)) ([a2cc418](https://github.com/netease/tango/commit/a2cc418a73edfd9b7fe8aad456ae775a49df2166))
|
||||||
|
- update designer style ([#176](https://github.com/netease/tango/issues/176)) ([0f3f0af](https://github.com/netease/tango/commit/0f3f0afdfa8aee2532a97c5c2e92ef4230397d86))
|
||||||
|
|
||||||
## [1.3.1](https://github.com/netease/tango/compare/@music163/tango-designer@1.3.0...@music163/tango-designer@1.3.1) (2024-06-05)
|
## [1.3.1](https://github.com/netease/tango/compare/@music163/tango-designer@1.3.0...@music163/tango-designer@1.3.1) (2024-06-05)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-designer",
|
"name": "@music163/tango-designer",
|
||||||
"version": "1.3.1",
|
"version": "1.4.5",
|
||||||
"description": "lowcode designer",
|
"description": "lowcode designer",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react"
|
"react"
|
||||||
@@ -33,12 +33,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.8.0",
|
"@ant-design/icons": "^4.8.0",
|
||||||
"@music163/request": "^0.2.0",
|
"@music163/request": "^0.2.0",
|
||||||
"@music163/tango-context": "^1.1.3",
|
"@music163/tango-context": "^1.1.9",
|
||||||
"@music163/tango-core": "^1.3.1",
|
"@music163/tango-core": "^1.4.3",
|
||||||
"@music163/tango-helpers": "^1.2.0",
|
"@music163/tango-helpers": "^1.2.3",
|
||||||
"@music163/tango-sandbox": "^1.0.6",
|
"@music163/tango-sandbox": "^1.0.12",
|
||||||
"@music163/tango-setting-form": "^1.2.6",
|
"@music163/tango-setting-form": "^1.2.14",
|
||||||
"@music163/tango-ui": "^1.3.1",
|
"@music163/tango-ui": "^1.4.4",
|
||||||
"antd": "^4.24.2",
|
"antd": "^4.24.2",
|
||||||
"cash-dom": "^8.1.2",
|
"cash-dom": "^8.1.2",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|||||||
@@ -34,17 +34,17 @@ export const ComponentsPopover = observer(
|
|||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
workspace.componentPrototypes.get(selectedNode?.name) ?? ({} as IComponentPrototype);
|
workspace.componentPrototypes.get(selectedNode?.name) ?? ({} as IComponentPrototype);
|
||||||
|
|
||||||
// 推荐使用的子组件
|
const recommendedList = useMemo(() => {
|
||||||
const insertedList = useMemo(
|
if (type === 'inner') {
|
||||||
() =>
|
return prototype?.childrenName
|
||||||
Array.isArray(prototype?.childrenName)
|
? Array.isArray(prototype?.childrenName)
|
||||||
? prototype?.childrenName
|
? prototype.childrenName
|
||||||
: [prototype?.childrenName].filter(Boolean),
|
: [prototype.childrenName]
|
||||||
[prototype?.childrenName],
|
: [];
|
||||||
);
|
}
|
||||||
|
// 默认推荐使用相同类型的组件作为兄弟节点
|
||||||
// 推荐使用的代码片段
|
return prototype.siblingNames || [prototype.name];
|
||||||
const siblingList = useMemo(() => prototype?.siblingNames ?? [], [prototype.siblingNames]);
|
}, [prototype.childrenName, prototype.siblingNames, prototype.name, type]);
|
||||||
|
|
||||||
const tipsTextMap = useMemo(
|
const tipsTextMap = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -82,22 +82,15 @@ export const ComponentsPopover = observer(
|
|||||||
const menuList = JSON.parse(JSON.stringify(designer.menuData));
|
const menuList = JSON.parse(JSON.stringify(designer.menuData));
|
||||||
|
|
||||||
const commonList = menuList['common'] ?? [];
|
const commonList = menuList['common'] ?? [];
|
||||||
if (commonList?.length && siblingList?.length) {
|
if (commonList?.length && recommendedList.length) {
|
||||||
commonList.unshift({
|
|
||||||
title: '代码片段',
|
|
||||||
items: siblingList,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commonList?.length && insertedList?.length) {
|
|
||||||
commonList.unshift({
|
commonList.unshift({
|
||||||
title: '推荐使用',
|
title: '推荐使用',
|
||||||
items: insertedList,
|
items: recommendedList,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return menuList;
|
return menuList;
|
||||||
}, [insertedList, siblingList, designer.menuData]);
|
}, [recommendedList, designer.menuData]);
|
||||||
|
|
||||||
const innerTypeProps =
|
const innerTypeProps =
|
||||||
// 手动触发 适用于 点击添加组件
|
// 手动触发 适用于 点击添加组件
|
||||||
@@ -125,6 +118,8 @@ export const ComponentsPopover = observer(
|
|||||||
}
|
}
|
||||||
footer={tipsTextMap[type]}
|
footer={tipsTextMap[type]}
|
||||||
width="330px"
|
width="330px"
|
||||||
|
height="450px"
|
||||||
|
resizeable
|
||||||
maskClosable
|
maskClosable
|
||||||
body={
|
body={
|
||||||
<ComponentsPanel
|
<ComponentsPanel
|
||||||
@@ -133,10 +128,6 @@ export const ComponentsPopover = observer(
|
|||||||
menuData={menuData}
|
menuData={menuData}
|
||||||
layout={layout}
|
layout={layout}
|
||||||
onItemSelect={handleSelect}
|
onItemSelect={handleSelect}
|
||||||
style={{
|
|
||||||
maxHeight: '400px',
|
|
||||||
overflow: 'auto',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{...popoverProps}
|
{...popoverProps}
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Box, css, Text } from 'coral-system';
|
||||||
|
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||||
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
const errorMessageStyle = css`
|
||||||
|
padding: 35px;
|
||||||
|
position: relative;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
color: #f45755;
|
||||||
|
font-weight: bold;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #171717;
|
||||||
|
border-radius: 8px;
|
||||||
|
.top-bar {
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 10px;
|
||||||
|
background-color: #f45755;
|
||||||
|
}
|
||||||
|
.WorkspaceErrorOverlayItem {
|
||||||
|
font-size: 18px;
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
.WorkspaceErrorOverlayItemFilename {
|
||||||
|
color: #48cacb;
|
||||||
|
text-decoration: underline;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
&:hover {
|
||||||
|
color: #51b1ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件错误提示浮层
|
||||||
|
*/
|
||||||
|
export const FileErrorsOverlay = observer(() => {
|
||||||
|
const [isVisible, setIsVisible] = useState<boolean>(true);
|
||||||
|
const workspace = useWorkspace();
|
||||||
|
const designer = useDesigner();
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setIsVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenErrorFile = (fileName: string) => {
|
||||||
|
workspace.setActiveFile(fileName);
|
||||||
|
designer.setActiveView('code');
|
||||||
|
};
|
||||||
|
|
||||||
|
if (workspace.fileErrors.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
className="WorkspaceErrorOverlay"
|
||||||
|
position="absolute"
|
||||||
|
top="0"
|
||||||
|
left="0"
|
||||||
|
right="0"
|
||||||
|
height="100%"
|
||||||
|
bg="rgb(84, 84, 84)"
|
||||||
|
padding="30px"
|
||||||
|
color="red"
|
||||||
|
overflow="auto"
|
||||||
|
zIndex={1999}
|
||||||
|
>
|
||||||
|
<Box position="relative" p="4" boxShadow="0 0 10px rgba(0, 0, 0, 0.5)">
|
||||||
|
<CloseOutlined
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 24,
|
||||||
|
right: 12,
|
||||||
|
color: '#fff',
|
||||||
|
zIndex: 2,
|
||||||
|
}}
|
||||||
|
onClick={handleClose}
|
||||||
|
/>
|
||||||
|
<Box css={errorMessageStyle}>
|
||||||
|
<Box className="top-bar" />
|
||||||
|
<Box fontSize="18px" mb="l">
|
||||||
|
<Text letterSpacing=".5px" color="#c29ef5">
|
||||||
|
[plugin: @babel/parser]
|
||||||
|
</Text>
|
||||||
|
<Text marginLeft={'8px'} color="#f45755">
|
||||||
|
SyntaxError in {workspace.fileErrors.length} files,Click file to modify the error
|
||||||
|
code
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
{workspace.fileErrors.map((fileError, index) => (
|
||||||
|
<Box
|
||||||
|
className="WorkspaceErrorOverlayItem"
|
||||||
|
key={fileError.filename}
|
||||||
|
borderBottom={index === workspace.fileErrors.length - 1 ? 'none' : 'dashed'}
|
||||||
|
borderBottomColor="line2"
|
||||||
|
py="l"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
className="WorkspaceErrorOverlayItemFilename"
|
||||||
|
fontWeight="bold"
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => handleOpenErrorFile(fileError.filename)}
|
||||||
|
>
|
||||||
|
{fileError.filename}
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
as="code"
|
||||||
|
display="block"
|
||||||
|
color="#daa15e"
|
||||||
|
fontSize="16px"
|
||||||
|
fontWeight={'normal'}
|
||||||
|
>
|
||||||
|
{fileError.message}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -4,3 +4,4 @@ export * from './input-kv';
|
|||||||
export * from './variable-tree';
|
export * from './variable-tree';
|
||||||
export * from './variable-tree-modal';
|
export * from './variable-tree-modal';
|
||||||
export * from './components-popover';
|
export * from './components-popover';
|
||||||
|
export * from './file-errors-overlay';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { isValidFunctionCode, url2serviceName } from '@music163/tango-helpers';
|
import { Dict, isValidFunctionCode, url2serviceName } from '@music163/tango-helpers';
|
||||||
import { InputCode } from '@music163/tango-ui';
|
import { InputCode } from '@music163/tango-ui';
|
||||||
import { Button, Dropdown, Form, FormProps, Input, Select, Space } from 'antd';
|
import { Button, Dropdown, Form, FormProps, Input, Select, Space } from 'antd';
|
||||||
import { Box } from 'coral-system';
|
import { Box } from 'coral-system';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
interface AddServiceFormProps extends FormProps {
|
interface AddServiceFormProps extends FormProps {
|
||||||
initialValues?: object;
|
initialValues?: Dict;
|
||||||
onSubmit: (values: object) => void;
|
onSubmit: (values: object) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
serviceModules: object[];
|
serviceModules: object[];
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import { Dict } from '@music163/tango-helpers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 快捷键
|
* 快捷键
|
||||||
*/
|
*/
|
||||||
export class Hotkey {
|
export class Hotkey {
|
||||||
private readonly hotkeyMap = {};
|
private readonly hotkeyMap: Dict = {};
|
||||||
|
|
||||||
constructor(hotkeys: Record<string, Function>) {
|
constructor(hotkeys: Record<string, Function>) {
|
||||||
Object.keys(hotkeys).forEach((hotkey) => {
|
Object.keys(hotkeys).forEach((hotkey) => {
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ export function useDnd({
|
|||||||
const data = sandboxQuery.getDraggableParentsData(e.target as HTMLElement, true);
|
const data = sandboxQuery.getDraggableParentsData(e.target as HTMLElement, true);
|
||||||
if (data && data.id) {
|
if (data && data.id) {
|
||||||
selectSource.select(data);
|
selectSource.select(data);
|
||||||
|
// 画布选中节点时,自动唤起 right-panel
|
||||||
|
designer.toggleRightPanel(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -386,7 +388,8 @@ export function useDnd({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onKeyDown = (e: React.KeyboardEvent) => {
|
const onKeyDown = (e: React.KeyboardEvent) => {
|
||||||
const isInputElement = isInputElements((e.target as HTMLElement).nodeName);
|
const target = e.target as HTMLElement;
|
||||||
|
const isInputElement = target.isContentEditable || isInputElements(target.nodeName);
|
||||||
// 禁用输入事件的触发的快捷键逻辑
|
// 禁用输入事件的触发的快捷键逻辑
|
||||||
if (!isInputElement) {
|
if (!isInputElement) {
|
||||||
const key = getHotkey(e);
|
const key = getHotkey(e);
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import React, { useRef, useEffect, useCallback } from 'react';
|
|||||||
import { Box } from 'coral-system';
|
import { Box } from 'coral-system';
|
||||||
import { MultiEditor, MultiEditorProps } from '@music163/tango-ui';
|
import { MultiEditor, MultiEditorProps } from '@music163/tango-ui';
|
||||||
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||||
import { isValidCode } from '@music163/tango-core';
|
|
||||||
import { Modal } from 'antd';
|
|
||||||
|
|
||||||
const ideConfig = {
|
const ideConfig = {
|
||||||
// disableFileOps: {
|
// disableFileOps: {
|
||||||
@@ -21,15 +19,9 @@ const ideConfig = {
|
|||||||
// disableSetting: true,
|
// disableSetting: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface CodeEditorProps extends Partial<MultiEditorProps> {
|
export type CodeEditorProps = Partial<MultiEditorProps>;
|
||||||
/**
|
|
||||||
* 是否自动清楚未使用的导入
|
|
||||||
*/
|
|
||||||
autoRemoveUnusedImports?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CodeEditor = observer(
|
export const CodeEditor = observer((props: CodeEditorProps) => {
|
||||||
({ autoRemoveUnusedImports = true, ...rest }: CodeEditorProps) => {
|
|
||||||
const editorRef = useRef(null);
|
const editorRef = useRef(null);
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const designer = useDesigner();
|
const designer = useDesigner();
|
||||||
@@ -48,26 +40,9 @@ export const CodeEditor = observer(
|
|||||||
|
|
||||||
const fileSave = useCallback(
|
const fileSave = useCallback(
|
||||||
(path: string, value: string) => {
|
(path: string, value: string) => {
|
||||||
if (!isJsFile(path)) {
|
workspace.updateFile(path, value, false);
|
||||||
// 非 js 文件直接保存
|
|
||||||
workspace.updateFile(path, value, autoRemoveUnusedImports);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// js 文件需要先检查语法,只有语法正确才会保存
|
|
||||||
if (isValidCode(value)) {
|
|
||||||
workspace.updateFile(path, value, autoRemoveUnusedImports);
|
|
||||||
} else {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '检测到代码中存在语法错误,暂时无法将代码同步给设计器,是否回退到安全代码?',
|
|
||||||
onOk: () => {
|
|
||||||
editorRef.current?.refresh(files, activeFile);
|
|
||||||
},
|
},
|
||||||
onCancel: () => {},
|
[workspace],
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[workspace, autoRemoveUnusedImports, activeFile, files],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFileChange = useCallback(
|
const handleFileChange = useCallback(
|
||||||
@@ -106,6 +81,7 @@ export const CodeEditor = observer(
|
|||||||
designer.activeView === 'dual'
|
designer.activeView === 'dual'
|
||||||
? {
|
? {
|
||||||
borderLeft: 'solid 1px var(--tango-colors-line2)',
|
borderLeft: 'solid 1px var(--tango-colors-line2)',
|
||||||
|
borderRight: 'solid 1px var(--tango-colors-line2)',
|
||||||
}
|
}
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
@@ -124,13 +100,8 @@ export const CodeEditor = observer(
|
|||||||
defaultPath={activeFile}
|
defaultPath={activeFile}
|
||||||
defaultTheme="GithubLightDefault"
|
defaultTheme="GithubLightDefault"
|
||||||
defaultFiles={files}
|
defaultFiles={files}
|
||||||
{...rest}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
function isJsFile(path: string) {
|
|
||||||
return /.jsx?$/.test(path);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useImperativeHandle, ForwardedRef, useRef, useState, useEffect } from 'react';
|
import React, { useImperativeHandle, ForwardedRef, useRef, useState, useEffect } from 'react';
|
||||||
import { Box } from 'coral-system';
|
import { Box } from 'coral-system';
|
||||||
import { CodeSandbox, CodeSandboxProps } from '@music163/tango-sandbox';
|
import { CodeSandbox, CodeSandboxProps } from '@music163/tango-sandbox';
|
||||||
import { getValue, isFunction, logger, pick, setValue } from '@music163/tango-helpers';
|
import { Dict, getValue, isFunction, logger, pick, setValue } from '@music163/tango-helpers';
|
||||||
import { observer, useWorkspace, useDesigner } from '@music163/tango-context';
|
import { observer, useWorkspace, useDesigner } from '@music163/tango-context';
|
||||||
import { Simulator, Viewport } from '../simulator';
|
import { Simulator, Viewport } from '../simulator';
|
||||||
import { useSandboxQuery } from '../context';
|
import { useSandboxQuery } from '../context';
|
||||||
@@ -88,7 +88,7 @@ function useSandbox({
|
|||||||
onViewChange: (data) => onViewChange && onViewChange(data, getSandboxConfig()),
|
onViewChange: (data) => onViewChange && onViewChange(data, getSandboxConfig()),
|
||||||
});
|
});
|
||||||
|
|
||||||
let files = Array.from(workspace.files.keys()).reduce((prev, filename) => {
|
let files = Array.from(workspace.files.keys()).reduce<Dict>((prev, filename) => {
|
||||||
let code = workspace.getFile(filename).code;
|
let code = workspace.getFile(filename).code;
|
||||||
if (filename === '/tango.config.json') {
|
if (filename === '/tango.config.json') {
|
||||||
code = mergeTangoConfigJson(code, { isPreview, injectScript, formatter: configFormatter });
|
code = mergeTangoConfigJson(code, { isPreview, injectScript, formatter: configFormatter });
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { css, Box, Text } from 'coral-system';
|
import { css, Box, Text } from 'coral-system';
|
||||||
import { AutoComplete } from 'antd';
|
import { AutoComplete, Input } from 'antd';
|
||||||
import { ActionSelect } from '@music163/tango-ui';
|
import { ActionSelect } from '@music163/tango-ui';
|
||||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||||
import { wrapCode } from '@music163/tango-helpers';
|
import { Dict, wrapCode } from '@music163/tango-helpers';
|
||||||
import { ExpressionPopover } from './expression-setter';
|
import { ExpressionPopover, getCallbackValue } from './expression-setter';
|
||||||
import { value2code } from '@music163/tango-core';
|
import { value2code } from '@music163/tango-core';
|
||||||
|
|
||||||
enum EventAction {
|
enum EventAction {
|
||||||
@@ -31,7 +31,7 @@ export type EventSetterProps = FormItemComponentProps<string>;
|
|||||||
* 事件监听函数绑定器
|
* 事件监听函数绑定器
|
||||||
*/
|
*/
|
||||||
export function EventSetter(props: EventSetterProps) {
|
export function EventSetter(props: EventSetterProps) {
|
||||||
const { value, onChange, modalTitle } = props;
|
const { value, onChange, modalTitle, modalTip, template } = props;
|
||||||
const [type, setType] = useState<EventAction>(); // 事件类型
|
const [type, setType] = useState<EventAction>(); // 事件类型
|
||||||
const [temp, setTemp] = useState(''); // 二级暂存值
|
const [temp, setTemp] = useState(''); // 二级暂存值
|
||||||
const { actionVariables, routeOptions } = useWorkspaceData();
|
const { actionVariables, routeOptions } = useWorkspaceData();
|
||||||
@@ -59,7 +59,9 @@ export function EventSetter(props: EventSetterProps) {
|
|||||||
label: (
|
label: (
|
||||||
<ExpressionPopover
|
<ExpressionPopover
|
||||||
title={modalTitle}
|
title={modalTitle}
|
||||||
|
subTitle={modalTip}
|
||||||
value={value}
|
value={value}
|
||||||
|
template={template}
|
||||||
onOk={(nextValue) => {
|
onOk={(nextValue) => {
|
||||||
handleChange(nextValue);
|
handleChange(nextValue);
|
||||||
}}
|
}}
|
||||||
@@ -74,30 +76,34 @@ export function EventSetter(props: EventSetterProps) {
|
|||||||
{ label: '打开弹窗', value: EventAction.OpenModal },
|
{ label: '打开弹窗', value: EventAction.OpenModal },
|
||||||
{ label: '关闭弹窗', value: EventAction.CloseModal },
|
{ label: '关闭弹窗', value: EventAction.CloseModal },
|
||||||
],
|
],
|
||||||
[modalTitle, value, actionVariables, handleChange],
|
[modalTitle, value, actionVariables, template, handleChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onAction = (key: string) => {
|
const onAction = (key: string) => {
|
||||||
setType(key as EventAction); // 记录事件类型
|
setType(key as EventAction); // 记录事件类型
|
||||||
setTemp(''); // 重置二级选项值
|
setTemp(''); // 重置二级选项值
|
||||||
|
|
||||||
switch (key) {
|
if (key === EventAction.NoAction) {
|
||||||
case EventAction.ConsoleLog:
|
|
||||||
handleChange('(...args) => console.log(...args)');
|
|
||||||
break;
|
|
||||||
case EventAction.NoAction:
|
|
||||||
handleChange(undefined);
|
handleChange(undefined);
|
||||||
break;
|
return;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const actionText = getActionText(type, temp, code);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box css={wrapperStyle}>
|
<Box css={wrapperStyle}>
|
||||||
<ActionSelect options={options} onSelect={onAction} text={actionText} />
|
<ActionSelect options={options} onSelect={onAction} defaultText="请选择动作类型" />
|
||||||
|
{type === EventAction.ConsoleLog && (
|
||||||
|
<Input
|
||||||
|
placeholder="输入 Console.log 日志内容"
|
||||||
|
value={temp}
|
||||||
|
onChange={(e) => setTemp(e.target.value)}
|
||||||
|
onBlur={() => {
|
||||||
|
if (temp) {
|
||||||
|
handleChange(getExpressionValue(type, temp));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{type === EventAction.NavigateTo && (
|
{type === EventAction.NavigateTo && (
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
placeholder="选择或输入页面路由"
|
placeholder="选择或输入页面路由"
|
||||||
@@ -141,26 +147,16 @@ export function EventSetter(props: EventSetterProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlerMap = {
|
const handlerMap: Dict = {
|
||||||
[EventAction.OpenModal]: 'openModal',
|
[EventAction.ConsoleLog]: 'console.log',
|
||||||
[EventAction.CloseModal]: 'closeModal',
|
[EventAction.OpenModal]: 'tango.openModal',
|
||||||
[EventAction.NavigateTo]: 'navigateTo',
|
[EventAction.CloseModal]: 'tango.closeModal',
|
||||||
|
[EventAction.NavigateTo]: 'tango.navigateTo',
|
||||||
};
|
};
|
||||||
|
|
||||||
function getActionText(type: EventAction, temp: string, fallbackCode: string) {
|
|
||||||
let text;
|
|
||||||
if (handlerMap[type]) {
|
|
||||||
text = getExpressionValue(type, temp);
|
|
||||||
} else if (fallbackCode) {
|
|
||||||
text = fallbackCode;
|
|
||||||
}
|
|
||||||
text = text || '请选择';
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getExpressionValue(type: EventAction, value = '') {
|
function getExpressionValue(type: EventAction, value = '') {
|
||||||
const handler = handlerMap[type];
|
const handler = handlerMap[type];
|
||||||
if (handler) {
|
if (handler) {
|
||||||
return `() => tango.${handler}("${value}")`;
|
return getCallbackValue(`${handler}("${value}");`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,17 @@ import React, { useState, useEffect, useCallback } from 'react';
|
|||||||
import { Box, Text, css } from 'coral-system';
|
import { Box, Text, css } from 'coral-system';
|
||||||
import { Dropdown, Button } from 'antd';
|
import { Dropdown, Button } from 'antd';
|
||||||
import { isValidExpressionCode } from '@music163/tango-core';
|
import { isValidExpressionCode } from '@music163/tango-core';
|
||||||
import { getValue, IVariableTreeNode, noop } from '@music163/tango-helpers';
|
import { getValue, interpolate, IVariableTreeNode, noop } from '@music163/tango-helpers';
|
||||||
import { CloseCircleFilled, ExpandAltOutlined, MenuOutlined } from '@ant-design/icons';
|
import { CloseCircleFilled, MenuOutlined } from '@ant-design/icons';
|
||||||
import { Panel, InputCode, Action, DragPanel } from '@music163/tango-ui';
|
import {
|
||||||
|
Panel,
|
||||||
|
InputCode,
|
||||||
|
Action,
|
||||||
|
DragPanel,
|
||||||
|
PopOutOutlined,
|
||||||
|
InputCodeProps,
|
||||||
|
InputStyleCode,
|
||||||
|
} from '@music163/tango-ui';
|
||||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||||
import { VariableTree } from '../components';
|
import { VariableTree } from '../components';
|
||||||
@@ -26,6 +34,19 @@ export const jsonValueValidate = (value: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼装回调函数
|
||||||
|
* @param value 回调函数体
|
||||||
|
* @param template 回调函数模板
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getCallbackValue(value: string, template?: string) {
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return template ? interpolate(template, { content: value }) : `() => {\n ${value}\n}`;
|
||||||
|
}
|
||||||
|
|
||||||
const suffixStyle = css`
|
const suffixStyle = css`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -43,6 +64,10 @@ export interface ExpressionSetterProps extends FormItemComponentProps<string> {
|
|||||||
autoCompleteOptions?: string[];
|
autoCompleteOptions?: string[];
|
||||||
allowClear?: boolean;
|
allowClear?: boolean;
|
||||||
showOptionsDropDown?: boolean;
|
showOptionsDropDown?: boolean;
|
||||||
|
/**
|
||||||
|
* 表达式类型指定为 css object
|
||||||
|
*/
|
||||||
|
expressionType?: 'cssObject';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ExpressionSetter(props: ExpressionSetterProps) {
|
export function ExpressionSetter(props: ExpressionSetterProps) {
|
||||||
@@ -51,12 +76,14 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
|||||||
modalTitle,
|
modalTitle,
|
||||||
modalTip,
|
modalTip,
|
||||||
autoCompleteOptions,
|
autoCompleteOptions,
|
||||||
placeholder = '在这里输入JS代码',
|
template,
|
||||||
|
placeholder = '在这里输入代码',
|
||||||
value: valueProp,
|
value: valueProp,
|
||||||
status,
|
status,
|
||||||
allowClear = true,
|
allowClear = true,
|
||||||
newStoreTemplate,
|
newStoreTemplate,
|
||||||
showOptionsDropDown = true,
|
showOptionsDropDown = true,
|
||||||
|
expressionType,
|
||||||
} = props;
|
} = props;
|
||||||
// const codeValue = getCodeOfWrappedCode(valueProp);
|
// const codeValue = getCodeOfWrappedCode(valueProp);
|
||||||
const [inputValue, setInputValue] = useState(valueProp);
|
const [inputValue, setInputValue] = useState(valueProp);
|
||||||
@@ -79,10 +106,12 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
|||||||
const sandbox = useSandboxQuery();
|
const sandbox = useSandboxQuery();
|
||||||
const evaluateContext = sandbox.window;
|
const evaluateContext = sandbox.window;
|
||||||
|
|
||||||
|
const InputCodeComponent = expressionType === 'cssObject' ? InputStyleCode : InputCode;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="ExpressionSetter">
|
<Box className="ExpressionSetter">
|
||||||
{/* 同时支持下拉框展示 */}
|
{/* 同时支持下拉框展示 */}
|
||||||
<InputCode
|
<InputCodeComponent
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
suffix={
|
suffix={
|
||||||
<Box css={suffixStyle}>
|
<Box css={suffixStyle}>
|
||||||
@@ -90,7 +119,7 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
|||||||
<CloseCircleFilled
|
<CloseCircleFilled
|
||||||
title="清空"
|
title="清空"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
change('');
|
change(undefined);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -119,8 +148,10 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
|||||||
subTitle={modalTip}
|
subTitle={modalTip}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoCompleteOptions={autoCompleteOptions}
|
autoCompleteOptions={autoCompleteOptions}
|
||||||
|
template={template}
|
||||||
newStoreTemplate={newStoreTemplate}
|
newStoreTemplate={newStoreTemplate}
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
|
expressionType={expressionType}
|
||||||
onOk={(value) => {
|
onOk={(value) => {
|
||||||
change(value);
|
change(value);
|
||||||
}}
|
}}
|
||||||
@@ -141,7 +172,7 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExpressionPopoverProps {
|
export interface ExpressionPopoverProps extends InputCodeProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
subTitle?: string;
|
subTitle?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@@ -150,10 +181,18 @@ export interface ExpressionPopoverProps {
|
|||||||
onOk?: (value: string) => void;
|
onOk?: (value: string) => void;
|
||||||
dataSource?: IVariableTreeNode[];
|
dataSource?: IVariableTreeNode[];
|
||||||
autoCompleteOptions?: string[];
|
autoCompleteOptions?: string[];
|
||||||
|
/**
|
||||||
|
* 值的模板,一般用于定义函数模板
|
||||||
|
*/
|
||||||
|
template?: string;
|
||||||
/**
|
/**
|
||||||
* 新建 store 的模板代码
|
* 新建 store 的模板代码
|
||||||
*/
|
*/
|
||||||
newStoreTemplate?: string;
|
newStoreTemplate?: string;
|
||||||
|
/**
|
||||||
|
* 表达式类型指定为 css object
|
||||||
|
*/
|
||||||
|
expressionType?: 'cssObject';
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,12 +205,15 @@ export function ExpressionPopover({
|
|||||||
value,
|
value,
|
||||||
dataSource,
|
dataSource,
|
||||||
autoCompleteOptions,
|
autoCompleteOptions,
|
||||||
|
template,
|
||||||
newStoreTemplate = CODE_TEMPLATES.newStoreTemplate,
|
newStoreTemplate = CODE_TEMPLATES.newStoreTemplate,
|
||||||
children,
|
children,
|
||||||
|
expressionType,
|
||||||
}: ExpressionPopoverProps) {
|
}: ExpressionPopoverProps) {
|
||||||
const [exp, setExp] = useState(value ?? defaultValue);
|
const [exp, setExp] = useState(value ?? defaultValue);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
|
const InputCodeComponent = expressionType === 'cssObject' ? InputStyleCode : InputCode;
|
||||||
|
|
||||||
const selectNodePath = workspace.selectSource.selected[0]?.codeId;
|
const selectNodePath = workspace.selectSource.selected[0]?.codeId;
|
||||||
|
|
||||||
@@ -182,7 +224,7 @@ export function ExpressionPopover({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const sandbox = useSandboxQuery();
|
const sandbox = useSandboxQuery();
|
||||||
const evaluateContext = sandbox.window;
|
const evaluateContext: any = sandbox.window;
|
||||||
|
|
||||||
const handleExpInputChange = (val: string) => {
|
const handleExpInputChange = (val: string) => {
|
||||||
setExp(val?.trim());
|
setExp(val?.trim());
|
||||||
@@ -195,56 +237,64 @@ export function ExpressionPopover({
|
|||||||
return (
|
return (
|
||||||
<DragPanel
|
<DragPanel
|
||||||
width={700}
|
width={700}
|
||||||
title={`将 ${selectNodePath}/${title} 设置为引用变量或自定义表达式`}
|
height={615}
|
||||||
extra={subTitle}
|
resizeable
|
||||||
|
title={`使用代码设置 ${selectNodePath} 组件的${title}`}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setExp(undefined);
|
setExp(undefined);
|
||||||
setError('');
|
setError('');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
popoverStyle={{
|
|
||||||
height: '615px',
|
|
||||||
}}
|
|
||||||
body={
|
body={
|
||||||
<>
|
<Box display="flex" flexDirection="column">
|
||||||
<Panel shape="solid">
|
<Box p="m">
|
||||||
<InputCode
|
<InputCodeComponent
|
||||||
shape="inset"
|
shape="inset"
|
||||||
minHeight="56px"
|
minHeight="56px"
|
||||||
maxHeight="200px"
|
maxHeight="160px"
|
||||||
value={exp}
|
value={exp}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onChange={handleExpInputChange}
|
onChange={handleExpInputChange}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
|
if (exp) {
|
||||||
setError(expressionValueValidate(exp));
|
setError(expressionValueValidate(exp));
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
autoCompleteContext={evaluateContext}
|
autoCompleteContext={evaluateContext}
|
||||||
autoCompleteOptions={autoCompleteOptions}
|
autoCompleteOptions={autoCompleteOptions}
|
||||||
/>
|
/>
|
||||||
{error ? (
|
{error ? (
|
||||||
<Text color="red" fontSize="12px">
|
<Text color="red" fontSize="12px" as="div">
|
||||||
出错了!输入的表达式存在语法错误,请修改后再提交!
|
出错了!输入的表达式存在语法错误,请修改后再提交!
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : null}
|
||||||
<Text fontSize="12px" color="text3">
|
<Box fontSize="12px" color="text2">
|
||||||
说明:你可以在上面的代码输入框里输入常规的 javascript 代码,还可以直接使用 jsx
|
<Text display="block">说明:</Text>
|
||||||
|
{subTitle && <Text display="block">{subTitle}</Text>}
|
||||||
|
<Text display="block">
|
||||||
|
你可以在上面的代码输入框里输入常规的 javascript 代码,还可以直接使用 jsx
|
||||||
代码,但需要符合该属性的接受值定义。
|
代码,但需要符合该属性的接受值定义。
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
</Box>
|
||||||
</Panel>
|
</Box>
|
||||||
<Panel
|
<Panel
|
||||||
title="从变量列表中选中"
|
title="从变量列表中选中"
|
||||||
shape="solid"
|
shape="solid"
|
||||||
borderTop="0"
|
borderLeft="0"
|
||||||
overflow="hidden"
|
borderRight="0"
|
||||||
bodyProps={{ overflow: 'hidden' }}
|
flex={1}
|
||||||
|
bodyProps={{
|
||||||
|
style: {
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<VariableTree
|
<VariableTree
|
||||||
height={380}
|
height="100%"
|
||||||
showViewButton
|
showViewButton
|
||||||
dataSource={dataSource || expressionVariables}
|
dataSource={dataSource || expressionVariables}
|
||||||
appContext={sandbox?.window['tango']}
|
appContext={evaluateContext['tango']}
|
||||||
getStoreNames={() => Object.keys(workspace.storeModules)}
|
getStoreNames={() => Object.keys(workspace.storeModules)}
|
||||||
serviceModules={serviceModules}
|
serviceModules={serviceModules}
|
||||||
getServiceData={(serviceKey) => {
|
getServiceData={(serviceKey) => {
|
||||||
@@ -265,7 +315,11 @@ export function ExpressionPopover({
|
|||||||
}
|
}
|
||||||
let str;
|
let str;
|
||||||
if (/^(stores|services)\./.test(node.key)) {
|
if (/^(stores|services)\./.test(node.key)) {
|
||||||
str = `tango.${node.key.replaceAll('.', '?.')}`;
|
// 从匹配到的第二个点开始替换为 ?.,因为第一个点是 stores 或 services
|
||||||
|
str = `tango.${node.key.replace(/(?<=\..*?)\./g, '?.')}`;
|
||||||
|
if (node.type === 'function') {
|
||||||
|
str = getCallbackValue(`${str}();`, template);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
str = `${node.key}`;
|
str = `${node.key}`;
|
||||||
}
|
}
|
||||||
@@ -307,10 +361,10 @@ export function ExpressionPopover({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
</>
|
</Box>
|
||||||
}
|
}
|
||||||
footer={(close) => (
|
footer={(close) => (
|
||||||
<Box display="flex" justifyContent="flex-end" gap="5px">
|
<Box display="flex" width="100%" justifyContent="flex-end" gap="5px">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -330,7 +384,12 @@ export function ExpressionPopover({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children || (
|
{children || (
|
||||||
<Action tooltip="打开表达式变量选择面板" icon={<ExpandAltOutlined />} size="small" />
|
<Action
|
||||||
|
tooltip="弹出代码编辑和变量选择面板"
|
||||||
|
tooltipProps={{ placement: 'topLeft' }}
|
||||||
|
icon={<PopOutOutlined />}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</DragPanel>
|
</DragPanel>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import { css, Box } from 'coral-system';
|
|||||||
import { Form, Input, Button, Popover } from 'antd';
|
import { Form, Input, Button, Popover } from 'antd';
|
||||||
import { DeleteOutlined, EditOutlined, HolderOutlined } from '@ant-design/icons';
|
import { DeleteOutlined, EditOutlined, HolderOutlined } from '@ant-design/icons';
|
||||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||||
|
import { Dict } from '@music163/tango-helpers';
|
||||||
import { DragBox } from '../components';
|
import { DragBox } from '../components';
|
||||||
|
|
||||||
function copyWithoutUndefined(source: Record<string, any>) {
|
function copyWithoutUndefined(source: Dict) {
|
||||||
const ret = {};
|
const ret: Dict = {};
|
||||||
for (const key in source) {
|
for (const key in source) {
|
||||||
if (source[key] !== undefined) {
|
if (source[key] !== undefined) {
|
||||||
ret[key] = source[key];
|
ret[key] = source[key];
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export function ModelSetter({
|
|||||||
}: FormItemComponentProps) {
|
}: FormItemComponentProps) {
|
||||||
const [inputValue, setInputValue] = useState(value);
|
const [inputValue, setInputValue] = useState(value);
|
||||||
const { modelVariables } = useWorkspaceData();
|
const { modelVariables } = useWorkspaceData();
|
||||||
const evaluateContext = useSandboxQuery().window || {};
|
const evaluateContext: any = useSandboxQuery().window || {};
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const definedVariables = useMemo(() => {
|
const definedVariables = useMemo(() => {
|
||||||
const list: string[] = [];
|
const list: string[] = [];
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import { ActionSelect, InputCode } from '@music163/tango-ui';
|
import { ActionSelect, InputCode } from '@music163/tango-ui';
|
||||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||||
import { Box } from 'coral-system';
|
import { Box } from 'coral-system';
|
||||||
|
import { Dict } from '@music163/tango-helpers';
|
||||||
|
|
||||||
interface IRenderOption {
|
interface IRenderOption {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -37,7 +38,7 @@ export function RenderSetter({
|
|||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
const optionsMap = useMemo(() => {
|
const optionsMap = useMemo(() => {
|
||||||
return options.reduce((prev, cur) => {
|
return options.reduce<Dict>((prev, cur) => {
|
||||||
prev[cur.value] = cur;
|
prev[cur.value] = cur;
|
||||||
return prev;
|
return prev;
|
||||||
}, {});
|
}, {});
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Box } from 'coral-system';
|
import { Box } from 'coral-system';
|
||||||
import { Input, Radio } from 'antd';
|
import { Input, Radio } from 'antd';
|
||||||
import { isValidUrl } from '@music163/tango-helpers';
|
import { Dict, isValidUrl } from '@music163/tango-helpers';
|
||||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||||
import { useWorkspaceData } from '@music163/tango-context';
|
import { useWorkspaceData } from '@music163/tango-context';
|
||||||
import { PickerSetter } from './picker-setter';
|
import { PickerSetter } from './picker-setter';
|
||||||
|
|
||||||
|
const routerModeMap: Dict = {
|
||||||
|
link: '链接',
|
||||||
|
router: '路由',
|
||||||
|
both: '',
|
||||||
|
};
|
||||||
|
|
||||||
export function RouterSetter(props: FormItemComponentProps) {
|
export function RouterSetter(props: FormItemComponentProps) {
|
||||||
const [type, setType] = useState<'input' | 'select'>(() => {
|
const [type, setType] = useState<'input' | 'select'>(() => {
|
||||||
return isValidUrl(props.value) ? 'input' : 'select';
|
return isValidUrl(props.value) ? 'input' : 'select';
|
||||||
@@ -37,7 +43,7 @@ export function RouterSetter(props: FormItemComponentProps) {
|
|||||||
)}
|
)}
|
||||||
{type === 'input' && (
|
{type === 'input' && (
|
||||||
<Input
|
<Input
|
||||||
placeholder={`请输入任意${{ link: '链接', router: '路由', both: '' }[displayType]}地址`}
|
placeholder={`请输入任意${routerModeMap[displayType]}地址`}
|
||||||
{...props}
|
{...props}
|
||||||
value={input}
|
value={input}
|
||||||
onChange={(e) => setInput(e.target.value)}
|
onChange={(e) => setInput(e.target.value)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { Box } from 'coral-system';
|
import { Box } from 'coral-system';
|
||||||
import { SettingForm, FormModel, SettingFormProps } from '@music163/tango-setting-form';
|
import { SettingForm, FormModel, SettingFormProps } from '@music163/tango-setting-form';
|
||||||
import { Panel } from '@music163/tango-ui';
|
import { Panel } from '@music163/tango-ui';
|
||||||
import { clone, parseDndId } from '@music163/tango-helpers';
|
import { clone, Dict, parseDndId } from '@music163/tango-helpers';
|
||||||
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||||
import { registerBuiltinSetters } from './setters';
|
import { registerBuiltinSetters } from './setters';
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ export const SettingPanel = observer((props: SettingPanelProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getSettingValue = (attributes: Record<string, string>) => {
|
const getSettingValue = (attributes: Record<string, string>) => {
|
||||||
const ret = {};
|
const ret: Dict = {};
|
||||||
const keys = Object.keys(attributes);
|
const keys = Object.keys(attributes);
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
if (/^data-/.test(key)) {
|
if (/^data-/.test(key)) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useMemo, useState } from 'react';
|
|||||||
import { Box, Grid, Text } from 'coral-system';
|
import { Box, Grid, Text } from 'coral-system';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import {
|
import {
|
||||||
|
Dict,
|
||||||
IComponentPrototype,
|
IComponentPrototype,
|
||||||
MenuDataType,
|
MenuDataType,
|
||||||
MenuValueType,
|
MenuValueType,
|
||||||
@@ -66,7 +67,7 @@ export interface ComponentsPanelProps {
|
|||||||
layout?: 'grid' | 'line';
|
layout?: 'grid' | 'line';
|
||||||
}
|
}
|
||||||
|
|
||||||
const localeMap = {
|
const localeMap: Dict = {
|
||||||
common: '基础组件',
|
common: '基础组件',
|
||||||
atom: '原子组件',
|
atom: '原子组件',
|
||||||
snippet: '组合',
|
snippet: '组合',
|
||||||
@@ -124,7 +125,7 @@ export const ComponentsPanel = observer(
|
|||||||
const tabs = Object.keys(menuData).map((key) => ({
|
const tabs = Object.keys(menuData).map((key) => ({
|
||||||
key,
|
key,
|
||||||
label: localeMap[key],
|
label: localeMap[key],
|
||||||
children: <MaterialList data={menuData[key]} />,
|
children: <MaterialList data={(menuData as any)[key]} />,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (showBizComps) {
|
if (showBizComps) {
|
||||||
@@ -364,7 +365,7 @@ function MaterialGrid({ data }: MaterialProps) {
|
|||||||
<IconFont className="material-icon" type={data.icon || 'icon-placeholder'} />
|
<IconFont className="material-icon" type={data.icon || 'icon-placeholder'} />
|
||||||
) : (
|
) : (
|
||||||
<Box className="material-icon">
|
<Box className="material-icon">
|
||||||
<img src={icon} alt={data.name} />
|
<img src={icon} alt={data.name} height="32" />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const DataSourceView = observer(() => {
|
|||||||
<Box className="ServiceFunctionList" p="m">
|
<Box className="ServiceFunctionList" p="m">
|
||||||
<VariableTree
|
<VariableTree
|
||||||
dataSource={serviceVariables}
|
dataSource={serviceVariables}
|
||||||
appContext={sandbox?.window['tango']}
|
appContext={(sandbox?.window as any)['tango']}
|
||||||
serviceModules={serviceModules}
|
serviceModules={serviceModules}
|
||||||
onRemoveService={(variableKey) => {
|
onRemoveService={(variableKey) => {
|
||||||
workspace.removeServiceFunction(variableKey);
|
workspace.removeServiceFunction(variableKey);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ConfigGroup, ConfigItem } from '@music163/tango-ui';
|
|||||||
import { Box, css } from 'coral-system';
|
import { Box, css } from 'coral-system';
|
||||||
import { Input, Switch, Form, Button, message, FormProps } from 'antd';
|
import { Input, Switch, Form, Button, message, FormProps } from 'antd';
|
||||||
import { CloseCircleOutlined, CloseOutlined } from '@ant-design/icons';
|
import { CloseCircleOutlined, CloseOutlined } from '@ant-design/icons';
|
||||||
|
import { Dict } from '@music163/tango-helpers';
|
||||||
|
|
||||||
export default observer(() => {
|
export default observer(() => {
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
@@ -63,7 +64,7 @@ function ProxyManage({
|
|||||||
initialValues: initialValuesProp = {},
|
initialValues: initialValuesProp = {},
|
||||||
onSave,
|
onSave,
|
||||||
}: {
|
}: {
|
||||||
initialValues?: object;
|
initialValues?: Dict;
|
||||||
onSave?: (values: any) => void;
|
onSave?: (values: any) => void;
|
||||||
}) {
|
}) {
|
||||||
const initialValues = useMemo(() => {
|
const initialValues = useMemo(() => {
|
||||||
@@ -84,7 +85,7 @@ function ProxyManage({
|
|||||||
{...proxyManageFormStyle}
|
{...proxyManageFormStyle}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onFinish={(values) => {
|
onFinish={(values) => {
|
||||||
const proxy = values.proxy.reduce((prev: object, { path, ...rest }: any) => {
|
const proxy = values.proxy.reduce((prev: any, { path, ...rest }: any) => {
|
||||||
prev[path] = rest;
|
prev[path] = rest;
|
||||||
return prev;
|
return prev;
|
||||||
}, {});
|
}, {});
|
||||||
|
|||||||
@@ -68,17 +68,6 @@ export const DependencyPanel = observer(
|
|||||||
[dependencies, baseDependencies, bizDependencies, packages, packageNames],
|
[dependencies, baseDependencies, bizDependencies, packages, packageNames],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 业务组件依赖
|
|
||||||
const bizDeps = useMemo(
|
|
||||||
() =>
|
|
||||||
bizDependencies?.map((name: string) => ({
|
|
||||||
name,
|
|
||||||
version: dependencies[name] ?? packages?.[name]?.version,
|
|
||||||
package: packages?.[name],
|
|
||||||
})) || [],
|
|
||||||
[bizDependencies, dependencies, packages],
|
|
||||||
);
|
|
||||||
|
|
||||||
// 基础包
|
// 基础包
|
||||||
const baseDeps = useMemo(
|
const baseDeps = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React, { useEffect, useReducer } from 'react';
|
import React, { useEffect, useReducer } from 'react';
|
||||||
import { observer } from '@music163/tango-context';
|
import { observer } from '@music163/tango-context';
|
||||||
import { isFunction, isObject, pick } from '@music163/tango-helpers';
|
import { Dict, isFunction, isObject, pick } from '@music163/tango-helpers';
|
||||||
import { CollapsePanel, IconButton, JsonView } from '@music163/tango-ui';
|
import { CollapsePanel, IconButton, JsonView } from '@music163/tango-ui';
|
||||||
import { ReloadOutlined } from '@ant-design/icons';
|
import { ReloadOutlined } from '@ant-design/icons';
|
||||||
import { useSandboxQuery } from '../../context';
|
import { useSandboxQuery } from '../../context';
|
||||||
|
|
||||||
function processTango(obj: any = {}, index = 0) {
|
function processTango(obj: Dict = {}, index = 0) {
|
||||||
const ret = {};
|
const ret: Dict = {};
|
||||||
Object.keys(obj).forEach((key) => {
|
Object.keys(obj).forEach((key) => {
|
||||||
const val = obj[key];
|
const val = obj[key];
|
||||||
if (index > 2) {
|
if (index > 2) {
|
||||||
@@ -25,11 +25,12 @@ function processTango(obj: any = {}, index = 0) {
|
|||||||
export const StateTree = observer(() => {
|
export const StateTree = observer(() => {
|
||||||
const sandboxQuery = useSandboxQuery();
|
const sandboxQuery = useSandboxQuery();
|
||||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||||
const tangoContext = pick(sandboxQuery.window['tango'] || {}, [
|
const tangoContext = pick((sandboxQuery.window as any)['tango'] || {}, [
|
||||||
'stores',
|
'stores',
|
||||||
'pageStore',
|
'pageStore',
|
||||||
'services',
|
'services',
|
||||||
'config',
|
'config',
|
||||||
|
'routeData',
|
||||||
]);
|
]);
|
||||||
const callback = () => {
|
const callback = () => {
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const resizeHandleStyle = (axis: ResizableBoxProps['axis'], barStyle: HTMLCoralP
|
|||||||
cursor: ${axis === 'x' ? 'col-resize' : 'row-resize'};
|
cursor: ${axis === 'x' ? 'col-resize' : 'row-resize'};
|
||||||
${barStyle &&
|
${barStyle &&
|
||||||
Object.keys(barStyle)
|
Object.keys(barStyle)
|
||||||
.map((key) => `${key}: ${barStyle[key]};`)
|
.map((key) => `${key}: ${(barStyle as any)[key]};`)
|
||||||
.join('')}
|
.join('')}
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active {
|
&:active {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import cx from 'classnames';
|
|||||||
import { Box, Text, css, HTMLCoralProps } from 'coral-system';
|
import { Box, Text, css, HTMLCoralProps } from 'coral-system';
|
||||||
import { Badge } from 'antd';
|
import { Badge } from 'antd';
|
||||||
import { ReactComponentProps } from '@music163/tango-helpers';
|
import { ReactComponentProps } from '@music163/tango-helpers';
|
||||||
import { observer, useDesigner } from '@music163/tango-context';
|
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||||
import { ResizableBox } from './resizable-box';
|
import { ResizableBox } from './resizable-box';
|
||||||
import { getWidget } from '../widgets';
|
import { getWidget } from '../widgets';
|
||||||
|
|
||||||
@@ -19,15 +19,22 @@ const sidebarStyle = css`
|
|||||||
list-style: none;
|
list-style: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.SidebarPanelBarListItem {
|
.SidebarPanelBarListItem {
|
||||||
|
color: var(--tango-colors-text2);
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--tango-colors-custom-sidebarItemActiveBg);
|
color: var(--tango-colors-brand);
|
||||||
|
background-color: var(--tango-colors-custom-sidebarItemActiveBg, --tango-colors-fill2);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--tango-colors-custom-sidebarItemHoverBg);
|
background-color: var(--tango-colors-custom-sidebarItemHoverBg, --tango-colors-fill2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -76,6 +83,7 @@ export interface SidebarPanelItemProps
|
|||||||
|
|
||||||
function BaseSidebarPanel({ panelWidth: defaultPanelWidth = 266, footer, children }: SidebarProps) {
|
function BaseSidebarPanel({ panelWidth: defaultPanelWidth = 266, footer, children }: SidebarProps) {
|
||||||
const designer = useDesigner();
|
const designer = useDesigner();
|
||||||
|
const workspace = useWorkspace();
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const ret: Record<string, SidebarPanelItemProps> = {};
|
const ret: Record<string, SidebarPanelItemProps> = {};
|
||||||
@@ -92,10 +100,23 @@ function BaseSidebarPanel({ panelWidth: defaultPanelWidth = 266, footer, childre
|
|||||||
return ret;
|
return ret;
|
||||||
}, [children]);
|
}, [children]);
|
||||||
|
|
||||||
|
if (designer.activeView !== 'design') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box display="flex" flexShrink={0} css={sidebarStyle} className="SidebarPanel">
|
<Box display="flex" flexShrink={0} css={sidebarStyle} className="SidebarPanel">
|
||||||
<Box className="SidebarPanelBar" display="flex" flexDirection="column" position="relative">
|
<Box className="SidebarPanelBar" display="flex" flexDirection="column" position="relative">
|
||||||
<Box as="ul" flex="0" p="0" m="0" textAlign="center" className="SidebarPanelBarList">
|
<Box
|
||||||
|
as="ul"
|
||||||
|
flex="0"
|
||||||
|
p="0"
|
||||||
|
m="0"
|
||||||
|
textAlign="center"
|
||||||
|
className={cx('SidebarPanelBarList', {
|
||||||
|
disabled: workspace.fileErrors.length > 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
{Object.keys(items).map((key) => {
|
{Object.keys(items).map((key) => {
|
||||||
const item = items[key];
|
const item = items[key];
|
||||||
const isActive = key === designer.activeSidebarPanel;
|
const isActive = key === designer.activeSidebarPanel;
|
||||||
@@ -112,7 +133,6 @@ function BaseSidebarPanel({ panelWidth: defaultPanelWidth = 266, footer, childre
|
|||||||
icon={item.icon}
|
icon={item.icon}
|
||||||
label={item.label}
|
label={item.label}
|
||||||
showBadge={item.showBadge}
|
showBadge={item.showBadge}
|
||||||
isActive={isActive}
|
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@@ -183,10 +203,6 @@ BaseSidebarPanel.Item = function ({
|
|||||||
export const Sidebar = observer(BaseSidebarPanel);
|
export const Sidebar = observer(BaseSidebarPanel);
|
||||||
|
|
||||||
interface SidebarPanelBarItemProps {
|
interface SidebarPanelBarItemProps {
|
||||||
/**
|
|
||||||
* 是否选中
|
|
||||||
*/
|
|
||||||
isActive?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* 侧边栏图标
|
* 侧边栏图标
|
||||||
*/
|
*/
|
||||||
@@ -212,15 +228,9 @@ interface SidebarPanelBarItemProps {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarPanelBarItem({
|
function SidebarPanelBarItem({ icon: iconProp, label, showBadge }: SidebarPanelBarItemProps) {
|
||||||
isActive,
|
|
||||||
icon: iconProp,
|
|
||||||
label,
|
|
||||||
showBadge,
|
|
||||||
}: SidebarPanelBarItemProps) {
|
|
||||||
const color = isActive ? 'brand' : 'text.body';
|
|
||||||
let icon = (
|
let icon = (
|
||||||
<Text fontSize="24px" lineHeight={1} color={color}>
|
<Text fontSize="24px" lineHeight={1}>
|
||||||
{iconProp}
|
{iconProp}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
@@ -241,7 +251,7 @@ function SidebarPanelBarItem({
|
|||||||
py="12px"
|
py="12px"
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
<Text fontSize="12px" mt="s" color={color}>
|
<Text fontSize="12px" mt="s">
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
|||||||
css={selectionBoxStyle}
|
css={selectionBoxStyle}
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
|
{!isPage ? (
|
||||||
<>
|
<>
|
||||||
<ComponentsPopover type="before">
|
<ComponentsPopover type="before">
|
||||||
<Tooltip title={`在 ${selectedNodeName} 的前方添加兄弟节点`}>
|
<Tooltip title={`在 ${selectedNodeName} 的前方添加兄弟节点`}>
|
||||||
@@ -167,6 +168,7 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ComponentsPopover>
|
</ComponentsPopover>
|
||||||
</>
|
</>
|
||||||
|
) : null}
|
||||||
{showActions && (
|
{showActions && (
|
||||||
<SelectionHelpers align={selectionHelpersAlign}>
|
<SelectionHelpers align={selectionHelpersAlign}>
|
||||||
<SelectionHelper
|
<SelectionHelper
|
||||||
@@ -204,7 +206,7 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SelectionToolSet>{!isPage && actions}</SelectionToolSet>
|
<SelectionToolSet>{!isPage && actions}</SelectionToolSet>
|
||||||
{isFromCurrentFile && prototype?.hasChildren !== false && (
|
{isFromCurrentFile && prototype?.hasChildren && (
|
||||||
<ComponentsPopover>
|
<ComponentsPopover>
|
||||||
<Tooltip title="快捷添加子元素">
|
<Tooltip title="快捷添加子元素">
|
||||||
<SelectionHelper icon={<PlusOutlined />} />
|
<SelectionHelper icon={<PlusOutlined />} />
|
||||||
|
|||||||
@@ -25,22 +25,45 @@ function DesktopSimulator({ children }: HTMLCoralProps<'div'>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mobileStyle = css`
|
const mobileStyle = css`
|
||||||
--device-viewport-width: 375px;
|
--device-viewport-width: 360px;
|
||||||
--device-viewport-height: 812px;
|
--device-viewport-height: 640px;
|
||||||
--device-frame-width: 13px;
|
position: relative;
|
||||||
background-image: url(https://p5.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/21897838692/b1c5/2665/56db/10558c1bb2030d55ceabbcc74431de99.png);
|
margin: auto;
|
||||||
background-repeat: no-repeat;
|
border: 16px black solid;
|
||||||
background-size: 100% 100%;
|
border-top-width: 60px;
|
||||||
box-shadow: 0 2px 8px rgb(2 8 20 / 10%), 0 8px 16px rgb(2 8 20 / 8%);
|
border-bottom-width: 60px;
|
||||||
border-radius: 62px;
|
border-radius: 36px;
|
||||||
overflow: hidden;
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 60px;
|
||||||
|
height: 5px;
|
||||||
|
position: absolute;
|
||||||
|
top: -30px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: #333;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: -65px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: #333;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.MobileSimulatorDeviceFrame {
|
.MobileSimulatorDeviceFrame {
|
||||||
border-radius: 50px;
|
|
||||||
width: var(--device-viewport-width);
|
width: var(--device-viewport-width);
|
||||||
height: var(--device-viewport-height);
|
height: var(--device-viewport-height);
|
||||||
margin: var(--device-frame-width);
|
background: white;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import { observer, useWorkspace } from '@music163/tango-context';
|
|||||||
|
|
||||||
export const HistoryTool = observer(() => {
|
export const HistoryTool = observer(() => {
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
|
const disabled = !workspace.isValid;
|
||||||
return (
|
return (
|
||||||
<Box display="flex" columnGap="m">
|
<Box display="flex" columnGap="m">
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
shape="ghost"
|
shape="ghost"
|
||||||
disabled={!workspace.history.couldBack || workspace.history.index === 0}
|
disabled={disabled || !workspace.history.couldBack || workspace.history.index === 0}
|
||||||
tooltip="撤销"
|
tooltip="撤销"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
workspace.history.back();
|
workspace.history.back();
|
||||||
@@ -20,7 +21,7 @@ export const HistoryTool = observer(() => {
|
|||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
shape="ghost"
|
shape="ghost"
|
||||||
disabled={!workspace.history.couldForward}
|
disabled={disabled || !workspace.history.couldForward}
|
||||||
tooltip="重做"
|
tooltip="重做"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
workspace.history.forward();
|
workspace.history.forward();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { Group } from 'coral-system';
|
import { Group } from 'coral-system';
|
||||||
import { Modal } from 'antd';
|
|
||||||
import { ToggleButton, CodeOutlined, DualOutlined } from '@music163/tango-ui';
|
import { ToggleButton, CodeOutlined, DualOutlined } from '@music163/tango-ui';
|
||||||
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||||
import { BorderOutlined } from '@ant-design/icons';
|
import { BorderOutlined } from '@ant-design/icons';
|
||||||
@@ -9,23 +8,13 @@ export const ModeSwitchTool = observer(() => {
|
|||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const designer = useDesigner();
|
const designer = useDesigner();
|
||||||
|
|
||||||
const activeFileCheck = useCallback(() => {
|
|
||||||
if (workspace.activeFile !== workspace.activeViewFile) {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '当前打开的文件与视图不匹配,是否切换到当前视图对应的文件?',
|
|
||||||
onOk: () => {
|
|
||||||
workspace.setActiveFile(workspace.activeViewFile);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [workspace]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group attached>
|
<Group attached whiteSpace="nowrap">
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
shape="ghost"
|
shape="ghost"
|
||||||
selected={designer.activeView === 'design'}
|
selected={designer.activeView === 'design'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
workspace.syncFiles(); // 保证 ast 与 code 是同步的
|
||||||
designer.setActiveView('design');
|
designer.setActiveView('design');
|
||||||
}}
|
}}
|
||||||
tooltip="设计视图"
|
tooltip="设计视图"
|
||||||
@@ -38,7 +27,6 @@ export const ModeSwitchTool = observer(() => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
designer.setActiveView('code'); // 切换到源码视图
|
designer.setActiveView('code'); // 切换到源码视图
|
||||||
designer.setActiveSidebarPanel(''); // 关闭左侧面板
|
designer.setActiveSidebarPanel(''); // 关闭左侧面板
|
||||||
activeFileCheck();
|
|
||||||
}}
|
}}
|
||||||
tooltip="源码视图"
|
tooltip="源码视图"
|
||||||
>
|
>
|
||||||
@@ -50,7 +38,6 @@ export const ModeSwitchTool = observer(() => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
designer.setActiveView('dual'); // 切换到双屏视图
|
designer.setActiveView('dual'); // 切换到双屏视图
|
||||||
designer.setActiveSidebarPanel(''); // 关闭左侧面板
|
designer.setActiveSidebarPanel(''); // 关闭左侧面板
|
||||||
activeFileCheck();
|
|
||||||
}}
|
}}
|
||||||
tooltip="双屏视图"
|
tooltip="双屏视图"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ToggleButton } from '@music163/tango-ui';
|
import { ToggleButton } from '@music163/tango-ui';
|
||||||
import { EyeOutlined } from '@ant-design/icons';
|
import { EyeOutlined } from '@ant-design/icons';
|
||||||
import { observer, useDesigner } from '@music163/tango-context';
|
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||||
|
|
||||||
export const PreviewTool = observer(() => {
|
export const PreviewTool = observer(() => {
|
||||||
const designer = useDesigner();
|
const designer = useDesigner();
|
||||||
|
const workspace = useWorkspace();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
|
disabled={!workspace.isValid}
|
||||||
shape="ghost"
|
shape="ghost"
|
||||||
selected={designer.isPreview}
|
selected={designer.isPreview}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -47,11 +47,14 @@ function PageSelect({ value, onSelect, onRemove, onUpdate, onCopy, options }: Pa
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const current = optionMap[value];
|
const pathname = value?.split?.('?')[0];
|
||||||
|
const current = optionMap[pathname];
|
||||||
return (
|
return (
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
shape="ghost"
|
shape="ghost"
|
||||||
minWidth={100}
|
minWidth={100}
|
||||||
|
maxWidth={200}
|
||||||
|
textAlign="left"
|
||||||
dropdownProps={{
|
dropdownProps={{
|
||||||
open: visible,
|
open: visible,
|
||||||
onOpenChange: setVisible,
|
onOpenChange: setVisible,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Box, Group } from 'coral-system';
|
import { Box, Group } from 'coral-system';
|
||||||
import { ReactComponentProps } from '@music163/tango-helpers';
|
import { ReactComponentProps } from '@music163/tango-helpers';
|
||||||
|
import { DesignerViewType } from '@music163/tango-core';
|
||||||
|
import { observer, useDesigner } from '@music163/tango-context';
|
||||||
import { getWidget } from '../widgets';
|
import { getWidget } from '../widgets';
|
||||||
|
|
||||||
export interface ToolbarProps {
|
export interface ToolbarProps {
|
||||||
@@ -15,25 +17,23 @@ export function Toolbar({ children }: ToolbarProps) {
|
|||||||
|
|
||||||
let prevPlacement: string;
|
let prevPlacement: string;
|
||||||
React.Children.forEach(children, (child: React.ReactElement, index) => {
|
React.Children.forEach(children, (child: React.ReactElement, index) => {
|
||||||
let fallbackNode;
|
const childProps = child.props;
|
||||||
|
|
||||||
|
let fallbackChildren;
|
||||||
if (child.key) {
|
if (child.key) {
|
||||||
const Widget = getWidget(['toolbar', child.key].join('.'));
|
const Widget = getWidget(['toolbar', child.key].join('.'));
|
||||||
if (Widget) {
|
if (Widget) {
|
||||||
fallbackNode = React.createElement(Widget, child.props.widgetProps);
|
fallbackChildren = <Widget {...childProps.widgetProps} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = child.props.children ?? fallbackNode ?? null;
|
|
||||||
if (!node) {
|
|
||||||
node = child; // separator
|
|
||||||
}
|
|
||||||
const key = child.key || index;
|
const key = child.key || index;
|
||||||
node = (
|
const node = React.cloneElement(child, {
|
||||||
<div key={key} className="ToolbarPanelItem" data-key={key}>
|
key,
|
||||||
{node}
|
'data-key': key,
|
||||||
</div>
|
children: childProps.children || fallbackChildren,
|
||||||
);
|
});
|
||||||
const placement = child.props.placement || prevPlacement || 'center';
|
|
||||||
|
const placement = childProps.placement || prevPlacement || 'center';
|
||||||
switch (placement) {
|
switch (placement) {
|
||||||
case 'left':
|
case 'left':
|
||||||
left.push(node);
|
left.push(node);
|
||||||
@@ -68,18 +68,48 @@ export function Toolbar({ children }: ToolbarProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolbarItemProps extends ReactComponentProps {
|
export interface ToolbarItemProps extends ReactComponentProps {
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
type?: 'divider';
|
||||||
placement?: 'left' | 'center' | 'right';
|
placement?: 'left' | 'center' | 'right';
|
||||||
children?: React.ReactElement;
|
children?: React.ReactElement;
|
||||||
/**
|
/**
|
||||||
* 如果 key 匹配到内置组件的话,传递给子节点的属性
|
* 如果 key 匹配到内置组件的话,传递给子节点的属性
|
||||||
*/
|
*/
|
||||||
widgetProps?: object;
|
widgetProps?: object;
|
||||||
|
/**
|
||||||
|
* 工具箱展示的视图
|
||||||
|
*/
|
||||||
|
activeViews?: DesignerViewType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolbarItem({ placement, widgetProps, children }: ToolbarItemProps) {
|
const ToolbarItem = observer(
|
||||||
return <div>{children}</div>;
|
({
|
||||||
|
activeViews = ['code', 'design', 'dual'],
|
||||||
|
type,
|
||||||
|
placement,
|
||||||
|
widgetProps,
|
||||||
|
children,
|
||||||
|
...rest
|
||||||
|
}: ToolbarItemProps) => {
|
||||||
|
const designer = useDesigner();
|
||||||
|
|
||||||
|
if (!activeViews.includes(designer.activeView)) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'divider') {
|
||||||
|
return <Separator />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="ToolbarPanelItem" {...rest}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
function Separator() {
|
function Separator() {
|
||||||
return <Box className="Separator" width={1} height={16} bg="colors.custom.toolbarDividerColor" />;
|
return <Box className="Separator" width={1} height={16} bg="colors.custom.toolbarDividerColor" />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
const widgets = {};
|
const widgets = {};
|
||||||
|
|
||||||
export function registerWidget(key: string, component: React.ComponentType<any>) {
|
export function registerWidget(key: string, component: React.ComponentType<any>) {
|
||||||
if (!/^(toolbar|sidebar|selectionMenu|contextMenu)\.[a-zA-z]+$/.test(key)) {
|
if (!/^(toolbar|sidebar|selectionMenu|contextMenu)\.[a-zA-Z]+$/.test(key)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid widget key(${key}), should start with toolbar, sidebar, contextMenu or selectionMenu`,
|
`Invalid widget key(${key}), should start with toolbar, sidebar, contextMenu or selectionMenu`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,35 +1,37 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box } from 'coral-system';
|
import { Box, HTMLCoralProps } from 'coral-system';
|
||||||
import { observer, useDesigner } from '@music163/tango-context';
|
import { observer, useDesigner } from '@music163/tango-context';
|
||||||
import { DesignerViewType } from '@music163/tango-core';
|
import { DesignerViewType } from '@music163/tango-core';
|
||||||
import { ComponentsPopover } from './components';
|
import cx from 'classnames';
|
||||||
|
import { ComponentsPopover, FileErrorsOverlay } from './components';
|
||||||
|
|
||||||
export interface WorkspaceViewProps {
|
export interface WorkspaceViewProps extends HTMLCoralProps<'div'> {
|
||||||
/**
|
/**
|
||||||
* 视图面板模式,对应 Workspace 的模式
|
* 视图面板模式,对应 Workspace 的模式
|
||||||
*/
|
*/
|
||||||
mode?: DesignerViewType;
|
mode?: DesignerViewType;
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkspaceView = observer((props: WorkspaceViewProps) => {
|
export const WorkspaceView = observer((props: WorkspaceViewProps) => {
|
||||||
const { mode = 'design', children } = props;
|
const { mode = 'design', children, className, ...rest } = props;
|
||||||
const designer = useDesigner();
|
const designer = useDesigner();
|
||||||
const display = mode !== designer.activeView ? 'none' : 'block';
|
const display = mode !== designer.activeView ? 'none' : 'block';
|
||||||
// 云音乐移动端模式小屏幕适配,可能会溢出屏幕
|
// 移动端模式小屏幕适配,可能会溢出屏幕
|
||||||
const overflow =
|
const overflow =
|
||||||
designer.simulator.name === 'phone' ? 'auto' : designer.isPreview ? 'auto' : 'hidden';
|
designer.simulator.name === 'phone' ? 'auto' : designer.isPreview ? 'auto' : 'hidden';
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className={`ViewPanel ${mode}`}
|
className={cx('ViewPanel', mode, className)}
|
||||||
display={designer.activeView === 'dual' ? 'block' : display}
|
display={designer.activeView === 'dual' ? 'block' : display}
|
||||||
flex="1"
|
flex="1"
|
||||||
overflow={overflow}
|
overflow={overflow}
|
||||||
position="relative"
|
position="relative"
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
{/* 添加组件弹层 */}
|
{/* 添加组件弹层 */}
|
||||||
{display === 'block' && <ComponentsPopover type="inner" isControlled />}
|
{display === 'block' && <ComponentsPopover type="inner" isControlled />}
|
||||||
|
{designer.activeView === 'design' && <FileErrorsOverlay />}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.2.3](https://github.com/netease/tango/compare/@music163/tango-helpers@1.2.2...@music163/tango-helpers@1.2.3) (2024-09-02)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- add prop value template & fix components popover ([#201](https://github.com/netease/tango/issues/201)) ([c4e1ed6](https://github.com/netease/tango/commit/c4e1ed67f55deb17dae740559602cd0adcfc8eb4))
|
||||||
|
|
||||||
|
## [1.2.2](https://github.com/netease/tango/compare/@music163/tango-helpers@1.2.1...@music163/tango-helpers@1.2.2) (2024-08-05)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- parse template code & update maxRow of TextAreaSetter ([#192](https://github.com/netease/tango/issues/192)) ([a1eea02](https://github.com/netease/tango/commit/a1eea02f9409db73f042c74d7903ad84258f25c2))
|
||||||
|
|
||||||
|
## [1.2.1](https://github.com/netease/tango/compare/@music163/tango-helpers@1.2.0...@music163/tango-helpers@1.2.1) (2024-08-01)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update types ([67d602a](https://github.com/netease/tango/commit/67d602a3ec6b7f74156bb78fda6f3b4bc2676e55))
|
||||||
|
|
||||||
# [1.2.0](https://github.com/netease/tango/compare/@music163/tango-helpers@1.1.1...@music163/tango-helpers@1.2.0) (2024-06-03)
|
# [1.2.0](https://github.com/netease/tango/compare/@music163/tango-helpers@1.1.1...@music163/tango-helpers@1.2.0) (2024-06-03)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-helpers",
|
"name": "@music163/tango-helpers",
|
||||||
"version": "1.2.0",
|
"version": "1.2.3",
|
||||||
"description": "Shared types, helpers, and hooks of tango-apps",
|
"description": "Shared types, helpers, and hooks of tango-apps",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"shared",
|
"shared",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function filterTreeData<T = unknown>(
|
|||||||
childrenProp = 'children',
|
childrenProp = 'children',
|
||||||
onlyLeaf = false,
|
onlyLeaf = false,
|
||||||
) {
|
) {
|
||||||
const reducer = (result: T[], current: T) => {
|
const reducer = (result: T[], current: any) => {
|
||||||
if (current[childrenProp]) {
|
if (current[childrenProp]) {
|
||||||
const newChildren = current[childrenProp].reduce(reducer, []);
|
const newChildren = current[childrenProp].reduce(reducer, []);
|
||||||
if (newChildren.length) {
|
if (newChildren.length) {
|
||||||
@@ -70,7 +70,7 @@ export function mapTreeData<T = unknown>(
|
|||||||
mapper: (node: T) => any,
|
mapper: (node: T) => any,
|
||||||
childrenProp = 'children',
|
childrenProp = 'children',
|
||||||
) {
|
) {
|
||||||
return treeData.map((node) => {
|
return treeData.map((node: any) => {
|
||||||
const newNode = mapper(node);
|
const newNode = mapper(node);
|
||||||
if (node[childrenProp]) {
|
if (node[childrenProp]) {
|
||||||
newNode[childrenProp] = mapTreeData(node[childrenProp], mapper, childrenProp);
|
newNode[childrenProp] = mapTreeData(node[childrenProp], mapper, childrenProp);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function isValidFunctionCode(str: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const templatePattern = /^{{(.+)}}$/s;
|
const templatePattern = /^{{([\s\S]+)}}$/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断给定代码是否被双花括号包裹
|
* 判断给定代码是否被双花括号包裹
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import get from 'lodash.get';
|
import get from 'lodash.get';
|
||||||
import set from 'lodash.set';
|
import set from 'lodash.set';
|
||||||
|
import { Dict } from '../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 合并 target 和 source 对象,source 对象的优先级更高,如存在重名,覆盖 target 中的 key
|
* 合并 target 和 source 对象,source 对象的优先级更高,如存在重名,覆盖 target 中的 key
|
||||||
@@ -45,7 +46,7 @@ export function clone(obj: any, withUndefined = true) {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = {};
|
const target: Dict = {};
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (withUndefined) {
|
if (withUndefined) {
|
||||||
target[key] = obj[key];
|
target[key] = obj[key];
|
||||||
@@ -65,7 +66,7 @@ export function omit(obj: any, keys: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function pick(obj: any, keys: string[]) {
|
export function pick(obj: any, keys: string[]) {
|
||||||
const target = {};
|
const target: Dict = {};
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
target[key] = obj[key];
|
target[key] = obj[key];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,3 +125,17 @@ export function parseDndId(str: string): DndIdParsedType {
|
|||||||
id: str,
|
id: str,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换模板中的变量
|
||||||
|
* @example interpolate('hello {{name}}', { name: 'world' }) -> 'hello world'
|
||||||
|
*
|
||||||
|
* @param template 带模板变量的字符串
|
||||||
|
* @param props 变量字典
|
||||||
|
* @returns 返回替换后的字符串
|
||||||
|
*/
|
||||||
|
export function interpolate(template: string, props: Record<string, any>) {
|
||||||
|
return template.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
||||||
|
return props[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function withDnd(options: IWithDndOptions<any>) {
|
|||||||
const override = options.overrideProps || {};
|
const override = options.overrideProps || {};
|
||||||
const renderFooter = options.renderFooter || renderEmpty;
|
const renderFooter = options.renderFooter || renderEmpty;
|
||||||
|
|
||||||
const Component = forwardRef<unknown, P & DraggableComponentProps>((props, refProp) => {
|
const Component = forwardRef<unknown, P & DraggableComponentProps>((props: any, refProp) => {
|
||||||
const dndProps = {
|
const dndProps = {
|
||||||
[SLOT.id]: props.tid, // id 作为唯一标记
|
[SLOT.id]: props.tid, // id 作为唯一标记
|
||||||
[SLOT.dnd]: props[SLOT.dnd], // dnd 作为追踪标记
|
[SLOT.dnd]: props[SLOT.dnd], // dnd 作为追踪标记
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { warning } from '../helpers';
|
import { warning } from '../helpers';
|
||||||
|
import { Dict } from '../types';
|
||||||
|
|
||||||
type ListStoreOptionsType<T = object> = {
|
type ListStoreOptionsType<T extends Dict = Dict> = {
|
||||||
data: T[];
|
data: T[];
|
||||||
keyProp?: string;
|
keyProp?: string;
|
||||||
labelProp?: string;
|
labelProp?: string;
|
||||||
childrenProp?: string;
|
childrenProp?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ListStore<T = object> {
|
export class ListStore<T extends Dict = Dict> {
|
||||||
private nodeMap: Map<string, T>;
|
private nodeMap: Map<string, T>;
|
||||||
private keyProp: string;
|
private keyProp: string;
|
||||||
private childrenProp: string;
|
private childrenProp: string;
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ export interface IComponentProp<T = any> {
|
|||||||
* 自动补全的提示值,仅对 ExpressionSetter 有效
|
* 自动补全的提示值,仅对 ExpressionSetter 有效
|
||||||
*/
|
*/
|
||||||
autoCompleteOptions?: string[];
|
autoCompleteOptions?: string[];
|
||||||
|
/**
|
||||||
|
* value 的模板,一般用于函数类型的属性,便于 setter 用来拼装返回值
|
||||||
|
* @example "(arg1, arg2, arg3) => { {{content}}}"
|
||||||
|
*/
|
||||||
|
template?: string;
|
||||||
/**
|
/**
|
||||||
* 设置器
|
* 设置器
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { isWrappedCode } from '../src/helpers';
|
||||||
|
|
||||||
|
describe('helpers/codeHelper', () => {
|
||||||
|
it('isWrappedCode', () => {
|
||||||
|
expect(isWrappedCode('{{[]}}')).toBeTruthy(); // true
|
||||||
|
expect(isWrappedCode('{{{}}}')).toBeTruthy(); // true
|
||||||
|
expect(isWrappedCode('{{this.foo}}')).toBeTruthy(); // true
|
||||||
|
expect(isWrappedCode('{{123}}')).toBeTruthy(); // true
|
||||||
|
expect(isWrappedCode('{{() => {}}}')).toBeTruthy(); // true
|
||||||
|
expect(isWrappedCode('{{<Button>button</Button>}}')).toBeTruthy(); // true
|
||||||
|
expect(isWrappedCode('{{<>\n<Button>hello</Button>\n</>}}')).toBeTruthy(); // true
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,6 +3,30 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.0.12](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.11...@music163/tango-sandbox@1.0.12) (2024-09-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|
||||||
|
## [1.0.11](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.10...@music163/tango-sandbox@1.0.11) (2024-08-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|
||||||
|
## [1.0.10](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.9...@music163/tango-sandbox@1.0.10) (2024-08-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|
||||||
|
## [1.0.9](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.8...@music163/tango-sandbox@1.0.9) (2024-08-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|
||||||
|
## [1.0.8](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.7...@music163/tango-sandbox@1.0.8) (2024-07-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|
||||||
|
## [1.0.7](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.6...@music163/tango-sandbox@1.0.7) (2024-06-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|
||||||
## [1.0.6](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.5...@music163/tango-sandbox@1.0.6) (2024-06-05)
|
## [1.0.6](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.5...@music163/tango-sandbox@1.0.6) (2024-06-05)
|
||||||
|
|
||||||
**Note:** Version bump only for package @music163/tango-sandbox
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-sandbox",
|
"name": "@music163/tango-sandbox",
|
||||||
"version": "1.0.6",
|
"version": "1.0.12",
|
||||||
"description": "sandbox of tango apps",
|
"description": "sandbox of tango apps",
|
||||||
"author": "wwsun <ww.sun@outlook.com>",
|
"author": "wwsun <ww.sun@outlook.com>",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.8.0",
|
"@ant-design/icons": "^4.8.0",
|
||||||
"@music163/tango-core": "^1.3.1",
|
"@music163/tango-core": "^1.4.3",
|
||||||
"@music163/tango-helpers": "^1.2.0",
|
"@music163/tango-helpers": "^1.2.3",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"lodash.isequal": "4.5.0",
|
"lodash.isequal": "4.5.0",
|
||||||
"react-frame-component": "^5.2.4"
|
"react-frame-component": "^5.2.4"
|
||||||
|
|||||||
@@ -3,6 +3,48 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.2.14](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.13...@music163/tango-setting-form@1.2.14) (2024-09-02)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- add prop value template & fix components popover ([#201](https://github.com/netease/tango/issues/201)) ([c4e1ed6](https://github.com/netease/tango/commit/c4e1ed67f55deb17dae740559602cd0adcfc8eb4))
|
||||||
|
|
||||||
|
## [1.2.13](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.12...@music163/tango-setting-form@1.2.13) (2024-08-16)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- CodeInput 支持 jsx css 代码补全 ([#198](https://github.com/netease/tango/issues/198)) ([6de5488](https://github.com/netease/tango/commit/6de54884b50d683c74d695a8c13184dc0ce8d3c4))
|
||||||
|
|
||||||
|
## [1.2.12](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.11...@music163/tango-setting-form@1.2.12) (2024-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-setting-form
|
||||||
|
|
||||||
|
## [1.2.11](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.10...@music163/tango-setting-form@1.2.11) (2024-08-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-setting-form
|
||||||
|
|
||||||
|
## [1.2.10](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.9...@music163/tango-setting-form@1.2.10) (2024-08-05)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- parse template code & update maxRow of TextAreaSetter ([#192](https://github.com/netease/tango/issues/192)) ([a1eea02](https://github.com/netease/tango/commit/a1eea02f9409db73f042c74d7903ad84258f25c2))
|
||||||
|
|
||||||
|
## [1.2.9](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.8...@music163/tango-setting-form@1.2.9) (2024-08-01)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update types ([67d602a](https://github.com/netease/tango/commit/67d602a3ec6b7f74156bb78fda6f3b4bc2676e55))
|
||||||
|
|
||||||
|
## [1.2.8](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.7...@music163/tango-setting-form@1.2.8) (2024-07-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-setting-form
|
||||||
|
|
||||||
|
## [1.2.7](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.6...@music163/tango-setting-form@1.2.7) (2024-06-20)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update designer style ([#176](https://github.com/netease/tango/issues/176)) ([0f3f0af](https://github.com/netease/tango/commit/0f3f0afdfa8aee2532a97c5c2e92ef4230397d86))
|
||||||
|
|
||||||
## [1.2.6](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.5...@music163/tango-setting-form@1.2.6) (2024-06-05)
|
## [1.2.6](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.5...@music163/tango-setting-form@1.2.6) (2024-06-05)
|
||||||
|
|
||||||
**Note:** Version bump only for package @music163/tango-setting-form
|
**Note:** Version bump only for package @music163/tango-setting-form
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-setting-form",
|
"name": "@music163/tango-setting-form",
|
||||||
"version": "1.2.6",
|
"version": "1.2.14",
|
||||||
"description": "setting form of tango-apps",
|
"description": "setting form of tango-apps",
|
||||||
"author": "wwsun <ww.sun@outlook.com>",
|
"author": "wwsun <ww.sun@outlook.com>",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
@@ -28,9 +28,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.8.0",
|
"@ant-design/icons": "^4.8.0",
|
||||||
"@music163/tango-core": "^1.3.1",
|
"@music163/tango-core": "^1.4.3",
|
||||||
"@music163/tango-helpers": "^1.2.0",
|
"@music163/tango-helpers": "^1.2.3",
|
||||||
"@music163/tango-ui": "^1.3.1",
|
"@music163/tango-ui": "^1.4.4",
|
||||||
"antd": "^4.24.2",
|
"antd": "^4.24.2",
|
||||||
"coral-system": "^1.0.5",
|
"coral-system": "^1.0.5",
|
||||||
"mobx": "6.12.3",
|
"mobx": "6.12.3",
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
|
|||||||
placeholder,
|
placeholder,
|
||||||
docs,
|
docs,
|
||||||
autoCompleteOptions,
|
autoCompleteOptions,
|
||||||
|
template,
|
||||||
setter: setterProp,
|
setter: setterProp,
|
||||||
setterProps,
|
setterProps,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
@@ -238,6 +239,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
|
|||||||
modalTitle: title,
|
modalTitle: title,
|
||||||
modalTip: tip,
|
modalTip: tip,
|
||||||
autoCompleteOptions,
|
autoCompleteOptions,
|
||||||
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +248,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
|
|||||||
const CodeSetter = REGISTERED_FORM_ITEM_MAP['codeSetter']?.config?.component;
|
const CodeSetter = REGISTERED_FORM_ITEM_MAP['codeSetter']?.config?.component;
|
||||||
|
|
||||||
const setterNode = isCodeSetter ? (
|
const setterNode = isCodeSetter ? (
|
||||||
<CodeSetter {...expProps} {...baseComponentProps} />
|
<CodeSetter {...expProps} {...baseComponentProps} {...setterProps} />
|
||||||
) : (
|
) : (
|
||||||
renderSetter({
|
renderSetter({
|
||||||
...expProps,
|
...expProps,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
noop,
|
noop,
|
||||||
filterTreeData,
|
filterTreeData,
|
||||||
mapTreeData,
|
mapTreeData,
|
||||||
|
Dict,
|
||||||
} from '@music163/tango-helpers';
|
} from '@music163/tango-helpers';
|
||||||
import { Action, Search, Tabs } from '@music163/tango-ui';
|
import { Action, Search, Tabs } from '@music163/tango-ui';
|
||||||
import { SettingFormItem, register } from './form-item';
|
import { SettingFormItem, register } from './form-item';
|
||||||
@@ -24,7 +25,7 @@ function normalizeComponentProps(
|
|||||||
props: IComponentPrototype['props'],
|
props: IComponentPrototype['props'],
|
||||||
groupOptions: IFormTabsGroupOption[],
|
groupOptions: IFormTabsGroupOption[],
|
||||||
) {
|
) {
|
||||||
const groups: Record<string, IComponentProp[]> = groupOptions.reduce((prev, cur) => {
|
const groups: Record<string, IComponentProp[]> = groupOptions.reduce<Dict>((prev, cur) => {
|
||||||
prev[cur.value] = [];
|
prev[cur.value] = [];
|
||||||
return prev;
|
return prev;
|
||||||
}, {});
|
}, {});
|
||||||
|
|||||||
+5
@@ -1,3 +1,4 @@
|
|||||||
|
import React from 'react';
|
||||||
import { CodeSetter } from './code-setter';
|
import { CodeSetter } from './code-setter';
|
||||||
import { BoolSetter } from './bool-setter';
|
import { BoolSetter } from './bool-setter';
|
||||||
import { IdSetter } from './id-setter';
|
import { IdSetter } from './id-setter';
|
||||||
@@ -36,6 +37,10 @@ const BASIC_SETTERS: IFormItemCreateOptions[] = [
|
|||||||
name: 'idSetter',
|
name: 'idSetter',
|
||||||
component: IdSetter,
|
component: IdSetter,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'imageSetter',
|
||||||
|
render: (props) => <TextSetter placeholder="请提供图片地址" {...props} />,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let registered = false;
|
let registered = false;
|
||||||
@@ -34,7 +34,7 @@ export function TextSetter({
|
|||||||
|
|
||||||
const autoSize = {
|
const autoSize = {
|
||||||
minRows: 2,
|
minRows: 2,
|
||||||
maxRows: 5,
|
maxRows: 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function TextAreaSetter({
|
export function TextAreaSetter({
|
||||||
|
|||||||
@@ -3,6 +3,52 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.4.4](https://github.com/netease/tango/compare/@music163/tango-ui@1.4.3...@music163/tango-ui@1.4.4) (2024-09-02)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- add prop value template & fix components popover ([#201](https://github.com/netease/tango/issues/201)) ([c4e1ed6](https://github.com/netease/tango/commit/c4e1ed67f55deb17dae740559602cd0adcfc8eb4))
|
||||||
|
|
||||||
|
## [1.4.3](https://github.com/netease/tango/compare/@music163/tango-ui@1.4.2...@music163/tango-ui@1.4.3) (2024-08-16)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- CodeInput 支持 jsx css 代码补全 ([#198](https://github.com/netease/tango/issues/198)) ([6de5488](https://github.com/netease/tango/commit/6de54884b50d683c74d695a8c13184dc0ce8d3c4))
|
||||||
|
|
||||||
|
## [1.4.2](https://github.com/netease/tango/compare/@music163/tango-ui@1.4.1...@music163/tango-ui@1.4.2) (2024-08-09)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- route matching without query string & toggle button wrapping ([#195](https://github.com/netease/tango/issues/195)) ([9394666](https://github.com/netease/tango/commit/939466648d40331800f15e886256a0789b7e7623))
|
||||||
|
- update mobile simulator ui ([#196](https://github.com/netease/tango/issues/196)) ([65149f1](https://github.com/netease/tango/commit/65149f1326eaf78e8a27222993c3cacc674cd378))
|
||||||
|
|
||||||
|
## [1.4.1](https://github.com/netease/tango/compare/@music163/tango-ui@1.4.0...@music163/tango-ui@1.4.1) (2024-08-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-ui
|
||||||
|
|
||||||
|
# [1.4.0](https://github.com/netease/tango/compare/@music163/tango-ui@1.3.3...@music163/tango-ui@1.4.0) (2024-08-01)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update types ([67d602a](https://github.com/netease/tango/commit/67d602a3ec6b7f74156bb78fda6f3b4bc2676e55))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- add isError and errorMessage to File & add isAstSynced to Module ([#190](https://github.com/netease/tango/issues/190)) ([8dd289c](https://github.com/netease/tango/commit/8dd289cd7daba628e54dc6b8929f22a1e2245160))
|
||||||
|
|
||||||
|
## [1.3.3](https://github.com/netease/tango/compare/@music163/tango-ui@1.3.2...@music163/tango-ui@1.3.3) (2024-07-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- selection bug without prototype & update ToggleButton style ([#180](https://github.com/netease/tango/issues/180)) ([1d0fb4f](https://github.com/netease/tango/commit/1d0fb4f274a5b767962c54583ab5863329d5bcc5))
|
||||||
|
|
||||||
|
## [1.3.2](https://github.com/netease/tango/compare/@music163/tango-ui@1.3.1...@music163/tango-ui@1.3.2) (2024-06-20)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- drag-panel add resizeable & height prop ([#175](https://github.com/netease/tango/issues/175)) ([b53a9f7](https://github.com/netease/tango/commit/b53a9f7eefc748ba8a7c94270c3a374b9d6ed211))
|
||||||
|
- update designer style ([#176](https://github.com/netease/tango/issues/176)) ([0f3f0af](https://github.com/netease/tango/commit/0f3f0afdfa8aee2532a97c5c2e92ef4230397d86))
|
||||||
|
|
||||||
## [1.3.1](https://github.com/netease/tango/compare/@music163/tango-ui@1.3.0...@music163/tango-ui@1.3.1) (2024-06-05)
|
## [1.3.1](https://github.com/netease/tango/compare/@music163/tango-ui@1.3.0...@music163/tango-ui@1.3.1) (2024-06-05)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-ui",
|
"name": "@music163/tango-ui",
|
||||||
"version": "1.3.1",
|
"version": "1.4.4",
|
||||||
"description": "ui widgets of tango",
|
"description": "ui widgets of tango",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@codemirror/lint": "^6.7.1",
|
"@codemirror/lint": "^6.7.1",
|
||||||
"@codemirror/search": "^6.5.6",
|
"@codemirror/search": "^6.5.6",
|
||||||
"@music163/tango-helpers": "^1.2.0",
|
"@music163/tango-helpers": "^1.2.3",
|
||||||
"@uiw/react-codemirror": "^4.22.0",
|
"@uiw/react-codemirror": "^4.22.0",
|
||||||
"antd": "^4.24.2",
|
"antd": "^4.24.2",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Box, css, Text } from 'coral-system';
|
import { Box, css, Text } from 'coral-system';
|
||||||
import { Button, Dropdown, Input, Menu } from 'antd';
|
import { Button, Dropdown, Input, Menu } from 'antd';
|
||||||
import { DownOutlined, PlusSquareOutlined } from '@ant-design/icons';
|
import { DownOutlined, PlusSquareOutlined } from '@ant-design/icons';
|
||||||
@@ -23,9 +23,13 @@ interface ActionSelectProps {
|
|||||||
*/
|
*/
|
||||||
onInputChange?: (value: string) => void;
|
onInputChange?: (value: string) => void;
|
||||||
/**
|
/**
|
||||||
* 提示文本
|
* 受控的提示文本
|
||||||
*/
|
*/
|
||||||
text?: string;
|
text?: string;
|
||||||
|
/**
|
||||||
|
* 默认的提示文本
|
||||||
|
*/
|
||||||
|
defaultText?: string;
|
||||||
/**
|
/**
|
||||||
* 选择菜单时的回调
|
* 选择菜单时的回调
|
||||||
*/
|
*/
|
||||||
@@ -50,11 +54,6 @@ const actionInputStyle = css`
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
color: var(--tango-colors-text3);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--tango-colors-text2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.anticon-down {
|
.anticon-down {
|
||||||
@@ -71,15 +70,25 @@ const inputStyle = { width: 'calc(100% - 82px)' };
|
|||||||
export function ActionSelect({
|
export function ActionSelect({
|
||||||
showInput = false,
|
showInput = false,
|
||||||
defaultInputValue,
|
defaultInputValue,
|
||||||
text,
|
text: textProp,
|
||||||
|
defaultText,
|
||||||
options = [],
|
options = [],
|
||||||
onSelect,
|
onSelect,
|
||||||
onInputChange,
|
onInputChange,
|
||||||
}: ActionSelectProps) {
|
}: ActionSelectProps) {
|
||||||
|
const [text, setText] = useState<string>(defaultText);
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu onClick={({ key }) => onSelect(key)}>
|
<Menu>
|
||||||
{options.map((item) => (
|
{options.map((item) => (
|
||||||
<Menu.Item key={item.value}>
|
<Menu.Item
|
||||||
|
key={item.value}
|
||||||
|
onClick={() => {
|
||||||
|
onSelect?.(item.value);
|
||||||
|
if (!textProp) {
|
||||||
|
setText(item.label as string);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Box display="flex" alignItems="center" minWidth={200} columnGap="m">
|
<Box display="flex" alignItems="center" minWidth={200} columnGap="m">
|
||||||
<Box flex="1">{item.label}</Box>
|
<Box flex="1">{item.label}</Box>
|
||||||
<Text fontSize="12px" color="text.note">
|
<Text fontSize="12px" color="text.note">
|
||||||
@@ -122,8 +131,8 @@ export function ActionSelect({
|
|||||||
mb="m"
|
mb="m"
|
||||||
css={actionInputStyle}
|
css={actionInputStyle}
|
||||||
>
|
>
|
||||||
<Box className="action-input-text" color="text.body">
|
<Box className="action-input-text" color="text2">
|
||||||
{text}
|
{textProp ?? text}
|
||||||
</Box>
|
</Box>
|
||||||
<DownOutlined />
|
<DownOutlined />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Tooltip } from 'antd';
|
import { Tooltip, TooltipProps } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { css, Button, Link, HTMLCoralProps } from 'coral-system';
|
import { css, Button, Link, HTMLCoralProps } from 'coral-system';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
@@ -48,6 +48,10 @@ export interface ActionProps extends HTMLCoralProps<any> {
|
|||||||
* 提示文本
|
* 提示文本
|
||||||
*/
|
*/
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
|
/**
|
||||||
|
* Tooltip属性透传
|
||||||
|
*/
|
||||||
|
tooltipProps?: TooltipProps;
|
||||||
/**
|
/**
|
||||||
* 超链接
|
* 超链接
|
||||||
*/
|
*/
|
||||||
@@ -75,6 +79,7 @@ export interface ActionProps extends HTMLCoralProps<any> {
|
|||||||
export function Action({
|
export function Action({
|
||||||
icon,
|
icon,
|
||||||
tooltip,
|
tooltip,
|
||||||
|
tooltipProps,
|
||||||
href,
|
href,
|
||||||
size,
|
size,
|
||||||
shape,
|
shape,
|
||||||
@@ -110,7 +115,11 @@ export function Action({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tooltip) {
|
if (tooltip) {
|
||||||
return <Tooltip title={tooltip}>{ret}</Tooltip>;
|
return (
|
||||||
|
<Tooltip title={tooltip} {...tooltipProps}>
|
||||||
|
{ret}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, HTMLCoralProps, css } from 'coral-system';
|
import { Box, HTMLCoralProps, css } from 'coral-system';
|
||||||
import { UpOutlined } from '@ant-design/icons';
|
import { CaretRightOutlined } from '@ant-design/icons';
|
||||||
import { useControllableState } from '@music163/tango-helpers';
|
import { useControllableState } from '@music163/tango-helpers';
|
||||||
|
|
||||||
export interface CollapsePanelProps extends Omit<HTMLCoralProps<'div'>, 'title'> {
|
export interface CollapsePanelProps extends Omit<HTMLCoralProps<'div'>, 'title'> {
|
||||||
@@ -39,8 +39,10 @@ export interface CollapsePanelProps extends Omit<HTMLCoralProps<'div'>, 'title'>
|
|||||||
const headerStyle = css`
|
const headerStyle = css`
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
.anticon-up {
|
.anticon-caret-right {
|
||||||
margin-right: 8px;
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
margin-right: 4px;
|
||||||
color: var(--tango-colors-text2);
|
color: var(--tango-colors-text2);
|
||||||
transition: transform 0.2s ease 0s;
|
transition: transform 0.2s ease 0s;
|
||||||
}
|
}
|
||||||
@@ -67,7 +69,7 @@ export function CollapsePanel(props: CollapsePanelProps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const iconStyle = {
|
const iconStyle = {
|
||||||
transform: collapsed ? undefined : 'rotate(-180deg)',
|
transform: collapsed ? undefined : 'rotate(90deg)',
|
||||||
};
|
};
|
||||||
|
|
||||||
const stickHeaderProps: HTMLCoralProps<'div'> = stickyHeader
|
const stickHeaderProps: HTMLCoralProps<'div'> = stickyHeader
|
||||||
@@ -104,7 +106,7 @@ export function CollapsePanel(props: CollapsePanelProps) {
|
|||||||
css={headerStyle}
|
css={headerStyle}
|
||||||
>
|
>
|
||||||
<Box display="flex" alignItems="center" fontSize="14px" fontWeight="500">
|
<Box display="flex" alignItems="center" fontSize="14px" fontWeight="500">
|
||||||
<UpOutlined style={iconStyle} />
|
<CaretRightOutlined style={iconStyle} />
|
||||||
{title}
|
{title}
|
||||||
</Box>
|
</Box>
|
||||||
<Box>{extra}</Box>
|
<Box>{extra}</Box>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Menu, Space } from 'antd';
|
import { Menu, Space } from 'antd';
|
||||||
import { Box, css } from 'coral-system';
|
import { Box, css } from 'coral-system';
|
||||||
import { isApplePlatform } from '@music163/tango-helpers';
|
import { Dict, isApplePlatform } from '@music163/tango-helpers';
|
||||||
|
|
||||||
const contextActionStyle = css`
|
const contextActionStyle = css`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -46,7 +46,7 @@ export function ContextAction({
|
|||||||
if (!hotkey) {
|
if (!hotkey) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const keyMap = isApplePlatform()
|
const keyMap: Dict = isApplePlatform()
|
||||||
? {
|
? {
|
||||||
command: '⌘',
|
command: '⌘',
|
||||||
meta: '⌘',
|
meta: '⌘',
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Box, Text, styled } from 'coral-system';
|
import { Box, Text, styled } from 'coral-system';
|
||||||
import { Popover, PopoverProps, IconFont } from './';
|
|
||||||
import Draggable from 'react-draggable';
|
import Draggable from 'react-draggable';
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined, HolderOutlined } from '@ant-design/icons';
|
||||||
import { noop } from '@music163/tango-helpers';
|
import { noop } from '@music163/tango-helpers';
|
||||||
|
import { Popover, PopoverProps } from './popover';
|
||||||
|
|
||||||
// Dragging over an iframe stops dragging when moving the mouse too fast #613
|
// Dragging over an iframe stops dragging when moving the mouse too fast #613
|
||||||
// https://github.com/react-grid-layout/react-draggable/issues/613
|
// https://github.com/react-grid-layout/react-draggable/issues/613
|
||||||
@@ -35,6 +35,52 @@ const CloseIcon = styled(CloseOutlined)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const DragPanelContainer = styled(Box)`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
border: 1px solid var(--tango-colors-line2);
|
||||||
|
width: ${(props) => props.width}px;
|
||||||
|
height: ${(props) => props.height}px;
|
||||||
|
resize: ${(props) => (props.resizeable ? 'both' : 'none')};
|
||||||
|
overflow: ${(props) => (props.resizeable ? 'auto' : 'hidden')};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DragPanelHeader = styled(Box)`
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-bottom: 1px solid var(--tango-colors-line2);
|
||||||
|
cursor: move;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 36px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DragPanelBody = styled(Box)`
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
> * {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DragPanelFooter = styled(Box)`
|
||||||
|
padding: 8px 12px;
|
||||||
|
min-height: 36px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
background: var(--tango-colors-line1);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
border-top: 1px solid var(--tango-colors-line2);
|
||||||
|
`;
|
||||||
|
|
||||||
interface DragPanelProps extends Omit<PopoverProps, 'overlay' | 'open'> {
|
interface DragPanelProps extends Omit<PopoverProps, 'overlay' | 'open'> {
|
||||||
// 标题
|
// 标题
|
||||||
title?: React.ReactNode | string;
|
title?: React.ReactNode | string;
|
||||||
@@ -44,8 +90,12 @@ interface DragPanelProps extends Omit<PopoverProps, 'overlay' | 'open'> {
|
|||||||
footer?: ((close: () => void) => React.ReactNode) | React.ReactNode | string;
|
footer?: ((close: () => void) => React.ReactNode) | React.ReactNode | string;
|
||||||
// 宽度
|
// 宽度
|
||||||
width?: number | string;
|
width?: number | string;
|
||||||
|
// 高度
|
||||||
|
height?: number | string;
|
||||||
// 右上角区域
|
// 右上角区域
|
||||||
extra?: React.ReactNode | string;
|
extra?: React.ReactNode | string;
|
||||||
|
// 是否可以缩放
|
||||||
|
resizeable?: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +105,10 @@ export function DragPanel({
|
|||||||
body,
|
body,
|
||||||
children,
|
children,
|
||||||
width = 330,
|
width = 330,
|
||||||
|
height = 330,
|
||||||
extra,
|
extra,
|
||||||
onOpenChange = noop,
|
onOpenChange = noop,
|
||||||
|
resizeable = false,
|
||||||
...props
|
...props
|
||||||
}: DragPanelProps) {
|
}: DragPanelProps) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -78,32 +130,20 @@ export function DragPanel({
|
|||||||
}}
|
}}
|
||||||
overlay={
|
overlay={
|
||||||
<Draggable
|
<Draggable
|
||||||
handle=".selection-drag-bar"
|
handle=".drag-panel-header"
|
||||||
onStart={() => {
|
onStart={() => {
|
||||||
injectStyleToBody();
|
injectStyleToBody();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<DragPanelContainer
|
||||||
bg="#FFF"
|
className="drag-panel"
|
||||||
borderRadius="m"
|
|
||||||
boxShadow="lowDown"
|
|
||||||
border="solid"
|
|
||||||
borderColor="line2"
|
|
||||||
overflow="hidden"
|
|
||||||
width={width}
|
width={width}
|
||||||
|
height={height}
|
||||||
|
resizeable={resizeable}
|
||||||
>
|
>
|
||||||
{/* 头部区域 */}
|
<DragPanelHeader className="drag-panel-header">
|
||||||
<Box
|
|
||||||
px="l"
|
|
||||||
py="m"
|
|
||||||
className="selection-drag-bar"
|
|
||||||
borderBottom="1px solid var(--tango-colors-line2)"
|
|
||||||
cursor="move"
|
|
||||||
display="flex"
|
|
||||||
justifyContent="space-between"
|
|
||||||
>
|
|
||||||
<Box fontSize="12px" color="text2">
|
<Box fontSize="12px" color="text2">
|
||||||
<IconFont type="icon-applications" />
|
<HolderOutlined />
|
||||||
<Text marginLeft={'5px'}>{title}</Text>
|
<Text marginLeft={'5px'}>{title}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box color="text2" fontSize="12px" display="flex" alignItems="center">
|
<Box color="text2" fontSize="12px" display="flex" alignItems="center">
|
||||||
@@ -115,26 +155,12 @@ export function DragPanel({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</DragPanelHeader>
|
||||||
{/* 主体区域 */}
|
<DragPanelBody className="drag-panel-body">{body}</DragPanelBody>
|
||||||
{body}
|
|
||||||
{/* 底部 */}
|
|
||||||
{footer && (
|
{footer && (
|
||||||
<Box
|
<DragPanelFooter className="drag-panel-footer">{footerNode}</DragPanelFooter>
|
||||||
px="l"
|
|
||||||
py="m"
|
|
||||||
whiteSpace="nowrap"
|
|
||||||
overflow="hidden"
|
|
||||||
textOverflow="ellipsis"
|
|
||||||
background="var(--tango-colors-line1)"
|
|
||||||
fontSize="12px"
|
|
||||||
fontWeight={400}
|
|
||||||
borderTop="1px solid var(--tango-colors-line2)"
|
|
||||||
>
|
|
||||||
{footerNode}
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</DragPanelContainer>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
}
|
}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createIcon } from './create-icon';
|
|||||||
|
|
||||||
const DualOutlinedSvg = () => (
|
const DualOutlinedSvg = () => (
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 1025 1024"
|
viewBox="0 0 1024 1024"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="1em"
|
width="1em"
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ export * from './create-icon';
|
|||||||
export * from './code-outlined';
|
export * from './code-outlined';
|
||||||
export * from './line-dashed-outlined';
|
export * from './line-dashed-outlined';
|
||||||
export * from './line-solid-outlined';
|
export * from './line-solid-outlined';
|
||||||
|
export * from './package-outlined';
|
||||||
export * from './open-panel-filled-left-outlined';
|
export * from './open-panel-filled-left-outlined';
|
||||||
export * from './open-panel-filled-right-outlined';
|
export * from './open-panel-filled-right-outlined';
|
||||||
export * from './open-panel-left-outlined';
|
export * from './open-panel-left-outlined';
|
||||||
export * from './open-panel-right-outlined';
|
export * from './open-panel-right-outlined';
|
||||||
export * from './undo-outlined';
|
export * from './undo-outlined';
|
||||||
|
export * from './pop-out-outlined';
|
||||||
export * from './redo-outlined';
|
export * from './redo-outlined';
|
||||||
export * from './dual-outlined';
|
export * from './dual-outlined';
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createIcon } from './create-icon';
|
||||||
|
|
||||||
|
const PackageOutlinedSvg = () => (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M915.2 284.8c0-3.2 0-3.2-3.2-6.4 0-3.2-3.2-3.2-6.4-6.4-9.6-12.8-22.4-25.6-38.4-35.2l-297.6-169.6c-35.2-22.4-80-22.4-118.4 0L313.6 147.2c-6.4 3.2-12.8 6.4-16 12.8L153.6 240c-12.8 9.6-25.6 22.4-38.4 35.2 0 3.2-3.2 3.2-3.2 6.4s0 3.2-3.2 6.4c-6.4 16-12.8 35.2-12.8 54.4v342.4c0 41.6 22.4 80 57.6 102.4l297.6 169.6c12.8 6.4 28.8 12.8 41.6 16 3.2 3.2 9.6 3.2 16 3.2s9.6-3.2 16-3.2c16-3.2 28.8-6.4 41.6-16l297.6-169.6c35.2-22.4 57.6-60.8 57.6-102.4V342.4c6.4-19.2 0-38.4-6.4-57.6z m-428.8-160c9.6-3.2 16-6.4 25.6-6.4 9.6 0 19.2 3.2 25.6 6.4L832 291.2l-128 73.6-316.8-182.4 99.2-57.6z m25.6 352L192 291.2l128-73.6 316.8 182.4-124.8 76.8zM185.6 729.6c-16-9.6-25.6-28.8-25.6-48V345.6l320 185.6V896l-294.4-166.4z m652.8 0L544 896V531.2l320-185.6v336c0 19.2-9.6 38.4-25.6 48z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const PackageOutlined = createIcon(PackageOutlinedSvg, 'PackageOutlined');
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createIcon } from './create-icon';
|
||||||
|
|
||||||
|
const Svg = () => (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M832 544a32 32 0 0 1 64 0V832a128 128 0 0 1-128 128H192a128 128 0 0 1-128-128V256a128 128 0 0 1 128-128h288a32 32 0 0 1 0 64H192a64 64 0 0 0-64 64v576a64 64 0 0 0 64 64h576a64 64 0 0 0 64-64V544z m-327.68 21.696a32 32 0 1 1-45.184-45.248L851.52 128H672a32 32 0 0 1 0-64H896a64 64 0 0 1 64 64v224a32 32 0 0 1-64 0V174.08L504.32 565.632z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const PopOutOutlined = createIcon(Svg, 'PopOutOutlined');
|
||||||
@@ -13,6 +13,7 @@ export * from './menu';
|
|||||||
export * from './panel';
|
export * from './panel';
|
||||||
export * from './toggle-button';
|
export * from './toggle-button';
|
||||||
export * from './input-code';
|
export * from './input-code';
|
||||||
|
export * from './input-style-code';
|
||||||
export * from './input-list';
|
export * from './input-list';
|
||||||
export * from './search';
|
export * from './search';
|
||||||
export * from './json-view';
|
export * from './json-view';
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ export function InputCode({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function useInputCode({
|
export function useInputCode({
|
||||||
shape,
|
shape,
|
||||||
status,
|
status,
|
||||||
showLineNumbers,
|
showLineNumbers,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user