Compare commits
106 Commits
@music163/
...
chore/type
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e273ae06e | ||
|
|
a6758c905f | ||
|
|
704672399d | ||
|
|
439e0f0299 | ||
|
|
7f95dfe4ab | ||
|
|
63dca2a9b7 | ||
|
|
eaff3604f3 | ||
|
|
5b121939e6 | ||
|
|
62b39daf7e | ||
|
|
32962d47fb | ||
|
|
d5e42c93bb | ||
|
|
fc93f4424a | ||
|
|
d6a08eecd2 | ||
|
|
181bfee2d2 | ||
|
|
8fd085d4fd | ||
|
|
35e258b32a | ||
|
|
62d403f80a | ||
|
|
73aded3672 | ||
|
|
8c07821d93 | ||
|
|
765ea07cd5 | ||
|
|
a27c241d9c | ||
|
|
4d6b67eecd | ||
|
|
6fc889a161 | ||
|
|
c07b554360 | ||
|
|
149002dddd | ||
|
|
45b0d856c1 | ||
|
|
6fc0498901 | ||
|
|
f2919c1aa0 | ||
|
|
5dd5ab62a8 | ||
|
|
5e7e285452 | ||
|
|
b3a458a914 | ||
|
|
1acfa6864b | ||
|
|
baf071f130 | ||
|
|
6b1d55a11f | ||
|
|
a8357f45b8 | ||
|
|
9db6358588 | ||
|
|
5bc2fb2d5a | ||
|
|
05a0f8f490 | ||
|
|
c884712331 | ||
|
|
e35ff792a0 | ||
|
|
138cbe9b20 | ||
|
|
979aa34259 | ||
|
|
76a5a2c659 | ||
|
|
53ebd89c9a | ||
|
|
3980810502 | ||
|
|
accd826376 | ||
|
|
5ca1118f88 | ||
|
|
14cd4c54a1 | ||
|
|
7f46137c56 | ||
|
|
1db52948db | ||
|
|
2550411c1c | ||
|
|
640859d36e | ||
|
|
d91763379c | ||
|
|
588861f649 | ||
|
|
91b53da38e | ||
|
|
9b0ada94a0 | ||
|
|
f604996e56 | ||
|
|
fb26540f27 | ||
|
|
558f2cd0a6 | ||
|
|
8a1234e565 | ||
|
|
630c486eb8 | ||
|
|
324f450851 | ||
|
|
d6ffb50e85 | ||
|
|
2906efc8fc | ||
|
|
2259fe77fc | ||
|
|
dbbd1dddc7 | ||
|
|
14e17ebbed | ||
|
|
4a06eb20cb | ||
|
|
089a482f75 | ||
|
|
afb94d6a30 | ||
|
|
b9403cfec6 | ||
|
|
6cf673e7f6 | ||
|
|
653386dcc0 | ||
|
|
208e275bb1 | ||
|
|
77f4145adf | ||
|
|
11f166a2a6 | ||
|
|
0452fdb7f1 | ||
|
|
0068d3426e | ||
|
|
c5840f29b7 | ||
|
|
963bfe1ab8 | ||
|
|
ae806aee35 | ||
|
|
b29e23599b | ||
|
|
f53ce94c6e | ||
|
|
d775a5003a | ||
|
|
46d5f59b4c | ||
|
|
0c0d95fe58 | ||
|
|
9dd11f7a6d | ||
|
|
fd2fa943b0 | ||
|
|
70fc6f8030 | ||
|
|
4f7233bd6c | ||
|
|
564d50b168 | ||
|
|
0b3e632745 | ||
|
|
3f073eeb43 | ||
|
|
6bbbe634ad | ||
|
|
8fefa6344f | ||
|
|
5ebbb428fb | ||
|
|
c240c1824b | ||
|
|
d63517ecb9 | ||
|
|
1a17ee1e94 | ||
|
|
e1bab66819 | ||
|
|
89bcd45728 | ||
|
|
e86a6d7cd9 | ||
|
|
46b54459ce | ||
|
|
85898dee19 | ||
|
|
e429610606 | ||
|
|
b6814d6484 |
@@ -2,11 +2,12 @@
|
||||
"extends": ["eslint-config-ali/typescript/react", "prettier"],
|
||||
"ignorePatterns": ["**/dist/**/*", "**/lib/**/*", "**/node_modules/**/*", "scripts/**/*"],
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-type-definitions": "off",
|
||||
"@typescript-eslint/dot-notation": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
"import/no-cycle": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"no-useless-return":"off",
|
||||
"no-useless-return": "off",
|
||||
"no-param-reassign": "off",
|
||||
"prefer-destructuring": "off"
|
||||
}
|
||||
|
||||
49
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: CI
|
||||
# Event设置为main分支的pull request事件,
|
||||
# 这里的main分支相当于master分支,github项目新建是把main设置为默认分支,我懒得改了所以就保持这样吧
|
||||
on:
|
||||
pull_request:
|
||||
branches: main
|
||||
jobs:
|
||||
# 只需要定义一个job并命名为CI
|
||||
CI:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# 拉取项目代码
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
# 给当前环境下载node
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.x'
|
||||
# 检查缓存
|
||||
# 如果key命中缓存则直接将缓存的文件还原到 path 目录,从而减少流水线运行时间
|
||||
# 若 key 没命中缓存时,在当前Job成功完成时将自动创建一个新缓存
|
||||
- name: Cache
|
||||
# 缓存命中结果会存储在steps.[id].outputs.cache-hit里,该变量在继后的step中可读
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
# 缓存文件目录的路径
|
||||
path: |
|
||||
**/node_modules
|
||||
# key中定义缓存标志位的生成方式。runner.OS指当前环境的系统。外加对yarn.lock内容生成哈希码作为key值,如果yarn.lock改变则代表依赖有变化。
|
||||
# 这里用yarn.lock而不是package.json是因为package.json中还有version和description之类的描述项目但和依赖无关的属性
|
||||
key: ${{runner.OS}}-${{hashFiles('**/yarn.lock')}}
|
||||
# 安装依赖
|
||||
- name: Installing Dependencies
|
||||
# 如果缓存标志位没命中,则执行该step。否则就跳过该step
|
||||
if: steps.cache-dependencies.outputs.cache-hit != 'true'
|
||||
run: yarn install
|
||||
# 预构建
|
||||
- name: Prebuild
|
||||
run: yarn build
|
||||
# 运行代码扫描
|
||||
- name: Running Lint
|
||||
# 通过前面章节定义的命令行执行代码扫描
|
||||
run: yarn eslint
|
||||
# 运行自动化测试
|
||||
- name: Running Test
|
||||
# 通过前面章节定义的命令行执行自动化测试
|
||||
run: yarn test
|
||||
53
.github/workflows/typedoc.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Simple workflow for deploying static content to GitHub Pages
|
||||
name: Deploy static content to Pages
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ['main', 'chore/typedoc']
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow one concurrent deployment
|
||||
concurrency:
|
||||
group: 'pages'
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Single deploy job since we're just deploying
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Build
|
||||
run: yarn build
|
||||
- name: Build docs
|
||||
run: yarn typedoc
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v3
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
with:
|
||||
# Upload dist repository
|
||||
path: './docs'
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
||||
46
README.md
@@ -5,7 +5,7 @@
|
||||
<h1 align="center">Tango LowCode Builder</h1>
|
||||
<div align="center">
|
||||
|
||||
A source code based low-code builder, developed by NetEase Cloud Music Develop Team.
|
||||
A source code based low-code builder.
|
||||
|
||||
[](https://github.com/NetEase/tango/blob/main/LICENSE)
|
||||
[](http://npmjs.org/package/@music163/tango-designer)
|
||||
@@ -31,32 +31,34 @@ 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
|
||||
- Developed using TypeScript, providing complete type definition files
|
||||
|
||||
## 🌐 Compatibility
|
||||
|
||||
- Modern browsers(Chrome >= 80, Edge >= 80, last 2 safari versions, last 2 firefox versions)
|
||||
|
||||
## 🎯 Milestone
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Tango Release Milestone
|
||||
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
|
||||
Alpha version :active, des1, 2023-08-10, 2023-08-30
|
||||
Beta version : des2, 2023-09-01, 2023-09-30
|
||||
1.0 RC : des3, after des2, 40d
|
||||
1.0 version : des4, after des3, 21d
|
||||
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
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> 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
|
||||
|
||||
- Modern browsers(Chrome >= 80, Edge >= 80, last 2 safari versions, last 2 firefox versions)
|
||||
|
||||
## 💻 Development
|
||||
|
||||
### Recommended Development Environment
|
||||
### Environment
|
||||
|
||||
- Node.js >= 16.0.0
|
||||
- Yarn >= 1.22.0
|
||||
- Node `>= 18`
|
||||
- Yarn `>= 1.22 && < 2`
|
||||
|
||||
### Development Quick Start
|
||||
|
||||
@@ -79,6 +81,7 @@ yarn start
|
||||
Join NetEase Tango Community to share your ideas, suggestions, or questions and connect with other users and contributors.
|
||||
|
||||
- Discord: <https://discord.gg/B6hkGTe4Rz>
|
||||
- [Usage Trends](https://npm-compare.com/@music163/tango-helpers,@music163/tango-context,@music163/tango-core,@music163/tango-setting-form,@music163/tango-sandbox,@music163/tango-ui,@music163/tango-designer)
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
@@ -89,16 +92,23 @@ Please read the [github contribution guide](https://docs.github.com/en/get-start
|
||||
- Commit and push your code
|
||||
- Open a Pull Request
|
||||
|
||||
## 📈 Usage Trends
|
||||
|
||||
[Usage Trends of Tango Packages](https://npm-compare.com/@music163/tango-helpers,@music163/tango-context,@music163/tango-core,@music163/tango-setting-form,@music163/tango-sandbox,@music163/tango-ui,@music163/tango-designer)
|
||||
|
||||
## 💗 Acknowledgments
|
||||
|
||||
Thanks to the NetEase Cloud Music Front-end team, Public Technology team, Live Broadcasting Technology team, and all the colleagues who participated in the Tango project.
|
||||
Thanks to the NetEase Cloud Music Front-end team, Public Technology team, Live Broadcasting Technology team, and all the developers who participated in the Tango project.
|
||||
|
||||
Thank you to CodeSandbox for providing the [Sandpack](https://sandpack.codesandbox.io/) project, which provides powerful online code execution capabilities for Tango.
|
||||
|
||||
## 📣 Product Promotion
|
||||
|
||||

|
||||
|
||||
Don't waste your time restoring the UI, try the NetEase Cloud Music "Seal D2C" development tool! Easily turn your design into code, support React, RN, Vue, WeChat apps and other multi-end scenarios, what you see is what you get!
|
||||
|
||||
Experience "Seal D2C" now:
|
||||
|
||||
- I'm a Figma user: <https://www.figma.com/community/plugin/1174548852019950797/seal-figma-to-code-d2c/>
|
||||
- I'm a MasterGo user: <https://mastergo.com/community/plugin/98956774428196/>
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the terms of the [MIT license](./LICENSE)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<h1 align="center">Tango 低代码设计器</h1>
|
||||
<div align="center">
|
||||
|
||||
一个源码驱动的低代码设计器,来自网易云音乐技术团队
|
||||
一个源码驱动的低代码设计器框架
|
||||
|
||||
[](https://github.com/NetEase/tango/blob/main/LICENSE)
|
||||
[](http://npmjs.org/package/@music163/tango-designer)
|
||||
@@ -31,32 +31,34 @@
|
||||
- 开箱即用的前端低代码设计器,提供灵活易用的设计器 React 组件
|
||||
- 使用 TypeScript 开发,提供完整的类型定义文件
|
||||
|
||||
## 🌐 兼容环境
|
||||
|
||||
- 现代浏览器(Chrome >= 80, Edge >= 80, last 2 safari versions, last 2 firefox versions)
|
||||
|
||||
## 🎯 里程碑
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Tango Release Milestone
|
||||
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
|
||||
Alpha version :active, des1, 2023-08-10, 2023-08-30
|
||||
Beta version : des2, 2023-09-01, 2023-09-30
|
||||
1.0 RC : des3, after des2, 40d
|
||||
1.0 version : des4, after des3, 21d
|
||||
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
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Alpha 版本会持续对一些实现协议进行优化,并持续融合网易内网版本中的新增特性;RC 版本将会提供基本稳定的解析协议和实现;1.0 正式版本计划在 2024 年Q2发布。1.0 正式版发布后我们将会持续进行版本迭代,并逐步融入内网版本中的流程设计、组件库、AIGC 等核心基础能力。
|
||||
|
||||
## 🌐 兼容环境
|
||||
|
||||
- 现代浏览器(Chrome >= 80, Edge >= 80, last 2 safari versions, last 2 firefox versions)
|
||||
|
||||
## 💻 开发
|
||||
|
||||
### 推荐开发环境
|
||||
|
||||
- Node.js >= 16.0.0
|
||||
- Yarn >= 1.22.0
|
||||
- Node `>= 18`
|
||||
- Yarn `>= 1.22 && < 2`
|
||||
|
||||
### 本地开发调试方法
|
||||
|
||||
@@ -79,6 +81,7 @@ yarn start
|
||||
参与 NetEase Tango 的社区,以分享您的想法、建议或问题,并与其他用户和贡献者建立联系。
|
||||
|
||||
- Discord: <https://discord.gg/B6hkGTe4Rz>
|
||||
- [使用趋势](https://npm-compare.com/@music163/tango-helpers,@music163/tango-context,@music163/tango-core,@music163/tango-setting-form,@music163/tango-sandbox,@music163/tango-ui,@music163/tango-designer)
|
||||
|
||||
## 🤝 参与共建
|
||||
|
||||
@@ -90,16 +93,23 @@ yarn start
|
||||
- 合并修改 git rebase master
|
||||
- 发起 Pull Request
|
||||
|
||||
## 📈 使用量趋势
|
||||
|
||||
[Tango 使用量趋势](https://npm-compare.com/@music163/tango-helpers,@music163/tango-context,@music163/tango-core,@music163/tango-setting-form,@music163/tango-sandbox,@music163/tango-ui,@music163/tango-designer)
|
||||
|
||||
## 💗 致谢
|
||||
|
||||
感谢网易云音乐公共技术团队,大前端团队,直播技术团队,以及所有参与过 Tango 项目的同学们。
|
||||
感谢网易云音乐公共技术团队,大前端团队,直播技术团队,以及所有参与过 Tango 项目的开发者。
|
||||
|
||||
感谢 CodeSandbox 提供的 [Sandpack](https://sandpack.codesandbox.io/) 项目,为 Tango 提供了强大的基于浏览器的代码构建与执行能力。
|
||||
|
||||
## 📣 产品推广
|
||||
|
||||

|
||||
|
||||
不要再浪费时间还原 UI 啦,快来试试网易云音乐「海豹 D2C」研发工具吧!轻松将设计稿转为代码,支持 React、RN、Vue、微信小程序等多端场景,所见即所得!
|
||||
|
||||
立即体验「海豹 D2C」:
|
||||
|
||||
- 我是 Figma 用户:<https://www.figma.com/community/plugin/1174548852019950797/seal-figma-to-code-d2c/>
|
||||
- 我是 MasterGo 用户:<https://mastergo.com/community/plugin/98956774428196/>
|
||||
|
||||
## 📄 开源协议
|
||||
|
||||
此项目遵循 [MIT 开源协议](./LICENSE)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
PORT=6006
|
||||
HOST=local.netease.com
|
||||
9
apps/playground/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/node_modules
|
||||
/.env.local
|
||||
/.umirc.local.ts
|
||||
/config/config.local.ts
|
||||
/src/.umi
|
||||
/src/.umi-production
|
||||
/src/.umi-test
|
||||
/dist
|
||||
.swc
|
||||
@@ -1,33 +1,17 @@
|
||||
import path from 'path';
|
||||
import { defineConfig } from 'umi';
|
||||
|
||||
const resolvePackageIndex = (relativeEntry: string) =>
|
||||
path.resolve(__dirname, '../../../packages/', relativeEntry);
|
||||
path.resolve(__dirname, '../../packages/', relativeEntry);
|
||||
|
||||
export default {
|
||||
export default defineConfig({
|
||||
routes: [
|
||||
{
|
||||
exact: true,
|
||||
path: '/',
|
||||
component: 'index',
|
||||
name: '首页',
|
||||
},
|
||||
{ path: '/', component: 'index' },
|
||||
{ path: '/docs', component: 'docs' },
|
||||
],
|
||||
devServer: {
|
||||
host: 'local.netease.com',
|
||||
port: 7007,
|
||||
https: {
|
||||
key: path.resolve(__dirname, 'local.netease.com-key.pem'),
|
||||
cert: path.resolve(__dirname, 'local.netease.com.pem'),
|
||||
},
|
||||
headers: { 'Origin-Agent-Cluster': '?0' },
|
||||
},
|
||||
targets: {
|
||||
chrome: 79,
|
||||
firefox: false,
|
||||
safari: false,
|
||||
edge: false,
|
||||
ios: false,
|
||||
},
|
||||
npmClient: 'yarn',
|
||||
mfsu: false,
|
||||
codeSplitting: false,
|
||||
alias: {
|
||||
'@music163/tango-helpers': resolvePackageIndex('helpers/src/index.ts'),
|
||||
'@music163/tango-core': resolvePackageIndex('core/src/index.ts'),
|
||||
@@ -44,6 +28,21 @@ export default {
|
||||
moment: 'moment',
|
||||
antd: 'antd',
|
||||
},
|
||||
headScripts: [
|
||||
'https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.development.js',
|
||||
'https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.development.js',
|
||||
'https://cdn.jsdelivr.net/npm/react-is@18.2.0/umd/react-is.production.min.js',
|
||||
'https://cdn.jsdelivr.net/npm/moment/min/moment-with-locales.js',
|
||||
'https://cdn.jsdelivr.net/npm/styled-components@5.3.11/dist/styled-components.js',
|
||||
'https://cdn.jsdelivr.net/npm/antd@4.24.13/dist/antd-with-locales.min.js',
|
||||
'https://cdn.jsdelivr.net/npm/prettier@2.6.0/standalone.js',
|
||||
'https://cdn.jsdelivr.net/npm/prettier@2.6.0/parser-babel.js',
|
||||
],
|
||||
https: {
|
||||
key: path.resolve(__dirname, 'local.netease.com-key.pem'),
|
||||
cert: path.resolve(__dirname, 'local.netease.com.pem'),
|
||||
http2: false,
|
||||
},
|
||||
chainWebpack: (config: any) => {
|
||||
// @see https://github.com/graphql/graphql-js/issues/1272#issuecomment-393903706
|
||||
config.module
|
||||
@@ -61,4 +60,5 @@ export default {
|
||||
config.module.rule('js').include.add(resolvePackageIndex('ui/src'));
|
||||
return config;
|
||||
},
|
||||
};
|
||||
monorepoRedirect: {},
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
# `playground`
|
||||
|
||||
搭建平台演示示例
|
||||
|
||||
## 说明
|
||||
@@ -2,24 +2,23 @@
|
||||
"name": "playground",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "playground of tango-apps",
|
||||
"license": "MIT",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"author": "Wells <ww.sun@outlook.com>",
|
||||
"scripts": {
|
||||
"build": "COMPRESS=none umi build",
|
||||
"start": "HTTPS=1 umi dev"
|
||||
"dev": "HOST=local.netease.com PORT=8001 umi dev",
|
||||
"build": "umi build",
|
||||
"postinstall": "umi setup",
|
||||
"setup": "umi setup",
|
||||
"start": "npm run dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/tango-designer": "*",
|
||||
"@music163/tango-helpers": "*",
|
||||
"antd": "^4.24.2",
|
||||
"coral-system": "^1.0.5"
|
||||
"coral-system": "^1.0.5",
|
||||
"umi": "^4.0.89"
|
||||
},
|
||||
"devDependencies": {
|
||||
"umi": "^3.5.24"
|
||||
"@types/react": "^18.0.33",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"typescript": "^5.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
10
apps/playground/plugin.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { IApi } from 'umi';
|
||||
|
||||
export default (api: IApi) => {
|
||||
api.addMiddlewares(() => {
|
||||
return (req, res, next) => {
|
||||
res.setHeader('Origin-Agent-Cluster', '?0');
|
||||
next();
|
||||
};
|
||||
});
|
||||
};
|
||||
BIN
apps/playground/src/assets/yay.jpg
Normal file
|
After Width: | Height: | Size: 177 KiB |
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Row, Switch } from 'antd';
|
||||
import { observer } from '@music163/tango-designer';
|
||||
import { observer } from '@music163/tango-context';
|
||||
|
||||
const OtherPanel = observer(({ autoRemove, setAutoRemove }: any) => {
|
||||
const onChange = (checked: boolean) => {
|
||||
|
||||
@@ -3,9 +3,40 @@ import { Box, Group } from 'coral-system';
|
||||
import { Avatar, Space, Switch } from 'antd';
|
||||
import { BranchesOutlined, MenuOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { registerSetter } from '@music163/tango-designer';
|
||||
import type { ComponentPrototypeType } from '@music163/tango-helpers';
|
||||
import type { ComponentPrototypeType, IVariableTreeNode } from '@music163/tango-helpers';
|
||||
import { FooSetter } from '../components';
|
||||
|
||||
export * from './mock-files';
|
||||
|
||||
export const bootHelperVariables: IVariableTreeNode[] = [
|
||||
{
|
||||
key: '$helpers',
|
||||
title: '工具函数',
|
||||
children: [
|
||||
{
|
||||
title: '设置变量值',
|
||||
key: '() => tango.setStoreValue("variableName", "variableValue")',
|
||||
type: 'function',
|
||||
help: '设置状态值,tango.setStoreValue("variableName", "variableValue")',
|
||||
},
|
||||
{
|
||||
title: '获取变量值',
|
||||
key: '() => tango.getStoreValue("variableName")',
|
||||
type: 'function',
|
||||
help: '获取状态值',
|
||||
},
|
||||
{ title: '打开弹窗', key: '() => tango.openModal("")', type: 'function' },
|
||||
{ title: '关闭弹窗', key: '() => tango.closeModal("")', type: 'function' },
|
||||
{ title: '切换路由', key: '() => tango.navigateTo("/")', type: 'function' },
|
||||
{
|
||||
title: '拷贝到剪贴板',
|
||||
key: '() => tango.copyToClipboard("hello")',
|
||||
type: 'function',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// folder-name
|
||||
// 物料列表定义
|
||||
const bizToggleButtonPrototype: ComponentPrototypeType = {
|
||||
@@ -133,7 +164,7 @@ export function ProjectDetail() {
|
||||
return (
|
||||
<Box display="flex" alignItems="center" columnGap="l">
|
||||
<Box className="ProjectName" fontSize="18px" fontWeight="bold">
|
||||
lc-online-test
|
||||
community-test
|
||||
</Box>
|
||||
<Box className="BranchName" as="code" fontSize="14px">
|
||||
<BranchesOutlined /> feature/list
|
||||
@@ -17,37 +17,43 @@ const tangoConfigJson = {
|
||||
version: '17.0.2',
|
||||
library: 'React',
|
||||
type: 'dependency',
|
||||
resources: ['https://unpkg.com/react@{{version}}/umd/react.development.js'],
|
||||
resources: ['https://cdn.jsdelivr.net/npm/react@{{version}}/umd/react.development.js'],
|
||||
},
|
||||
'react-dom': {
|
||||
version: '17.0.2',
|
||||
library: 'ReactDOM',
|
||||
type: 'dependency',
|
||||
resources: ['https://unpkg.com/react-dom@{{version}}/umd/react-dom.development.js'],
|
||||
resources: [
|
||||
'https://cdn.jsdelivr.net/npm/react-dom@{{version}}/umd/react-dom.development.js',
|
||||
],
|
||||
},
|
||||
'react-is': {
|
||||
version: '16.13.1',
|
||||
library: 'ReactIs',
|
||||
type: 'dependency',
|
||||
resources: ['https://unpkg.com/react-is@{{version}}/umd/react-is.production.min.js'],
|
||||
resources: [
|
||||
'https://cdn.jsdelivr.net/npm/react-is@{{version}}/umd/react-is.production.min.js',
|
||||
],
|
||||
},
|
||||
'styled-components': {
|
||||
version: '5.3.5',
|
||||
library: 'styled',
|
||||
type: 'dependency',
|
||||
resources: ['https://unpkg.com/styled-components@{{version}}/dist/styled-components.min.js'],
|
||||
resources: [
|
||||
'https://cdn.jsdelivr.net/npm/styled-components@{{version}}/dist/styled-components.min.js',
|
||||
],
|
||||
},
|
||||
moment: {
|
||||
version: '2.29.4',
|
||||
library: 'moment',
|
||||
type: 'dependency',
|
||||
resources: ['https://unpkg.com/moment@{{version}}/moment.js'],
|
||||
resources: ['https://cdn.jsdelivr.net/npm/moment@{{version}}/moment.js'],
|
||||
},
|
||||
'@music163/tango-boot': {
|
||||
version: '0.2.0',
|
||||
version: '0.2.1',
|
||||
library: 'TangoBoot',
|
||||
type: 'baseDependency',
|
||||
resources: ['https://unpkg.com/@music163/tango-boot@{{version}}/dist/boot.js'],
|
||||
resources: ['https://cdn.jsdelivr.net/npm/@music163/tango-boot@{{version}}/dist/boot.js'],
|
||||
// resources: ['http://localhost:9001/boot.js'],
|
||||
description: '云音乐低代码运行时框架',
|
||||
},
|
||||
@@ -56,27 +62,30 @@ const tangoConfigJson = {
|
||||
library: 'TangoAntd',
|
||||
type: 'baseDependency',
|
||||
resources: [
|
||||
'https://unpkg.com/@music163/antd@{{version}}/dist/index.js',
|
||||
'https://unpkg.com/antd@4.24.13/dist/antd.css',
|
||||
'https://cdn.jsdelivr.net/npm/@music163/antd@{{version}}/dist/index.js',
|
||||
'https://cdn.jsdelivr.net/npm/antd@4.24.13/dist/antd.css',
|
||||
],
|
||||
description: '云音乐低代码中后台应用基础物料',
|
||||
designerResources: [
|
||||
'https://unpkg.com/@music163/antd@{{version}}/dist/designer.js',
|
||||
'https://cdn.jsdelivr.net/npm/@music163/antd@{{version}}/dist/designer.js',
|
||||
// 'http://localhost:9002/designer.js',
|
||||
'https://unpkg.com/antd@4.24.13/dist/antd.css',
|
||||
'https://cdn.jsdelivr.net/npm/antd@4.24.13/dist/antd.css',
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const appJson: any = {
|
||||
pages: [
|
||||
{
|
||||
path: '/',
|
||||
name: '首页',
|
||||
},
|
||||
],
|
||||
};
|
||||
const helperCode = `
|
||||
export function registerComponentPrototype(proto) {
|
||||
if (!proto) return;
|
||||
if (!window.localTangoComponentPrototypes) {
|
||||
window.localTangoComponentPrototypes = {};
|
||||
}
|
||||
if (proto.name) {
|
||||
window.localTangoComponentPrototypes[proto.name] = proto;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const routesCode = `
|
||||
import Index from "./pages/list";
|
||||
@@ -145,23 +154,73 @@ import {
|
||||
Input,
|
||||
FormilyForm,
|
||||
} from "@music163/antd";
|
||||
import { Space } from '@music163/antd';
|
||||
import { LocalButton } from '../components';
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Page title={tango.stores.app.title}>
|
||||
<Section>
|
||||
<Section title="Section Title">
|
||||
</Section>
|
||||
<Section>
|
||||
<Space>
|
||||
<LocalButton />
|
||||
<Button>button</Button>
|
||||
<Input />
|
||||
</Space>
|
||||
</Section>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default definePage(App);
|
||||
`;
|
||||
|
||||
const componentsButtonCode = `
|
||||
import React from 'react';
|
||||
import { registerComponentPrototype } from '../utils';
|
||||
|
||||
export default function MyButton(props) {
|
||||
return <button {...props}>my button</button>
|
||||
}
|
||||
|
||||
registerComponentPrototype({
|
||||
name: 'LocalButton',
|
||||
title: 'Local Button',
|
||||
exportType: 'namedExport',
|
||||
package: '/src/components',
|
||||
props: [
|
||||
{ name: 'background', title: '背景色', setter: 'colorSetter' },
|
||||
],
|
||||
});
|
||||
`;
|
||||
|
||||
const componentsInputCode = `
|
||||
import React from 'react';
|
||||
import { registerComponentPrototype } from '../utils';
|
||||
|
||||
export default function MyInput(props) {
|
||||
return <input {...props} />;
|
||||
}
|
||||
|
||||
registerComponentPrototype({
|
||||
name: 'LocalInput',
|
||||
title: 'Local Input',
|
||||
exportType: 'namedExport',
|
||||
package: '/src/components',
|
||||
props: [
|
||||
{ name: 'color', title: '文本色', setter: 'colorSetter' },
|
||||
],
|
||||
});
|
||||
`;
|
||||
|
||||
const componentsEntryCode = `
|
||||
export { default as LocalButton } from './button';
|
||||
export { default as LocalInput } from './input';
|
||||
`;
|
||||
|
||||
const storeApp = `
|
||||
import { defineStore } from '@music163/tango-boot';
|
||||
|
||||
@@ -170,6 +229,35 @@ export default defineStore({
|
||||
title: 'Page Title',
|
||||
|
||||
array: [1, 2, 3],
|
||||
|
||||
test: function() {
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
console.log('test');
|
||||
}
|
||||
}, 'app');
|
||||
`;
|
||||
|
||||
@@ -178,6 +266,13 @@ import { defineServices } from '@music163/tango-boot';
|
||||
import './sub';
|
||||
|
||||
export default defineServices({
|
||||
longLongLongLongLongLongLongLongGet: {
|
||||
url: 'https://nei.hz.netease.com/api/apimock-v2/cc974ffbaa7a85c77f30e4ce67deb67f/api/getUserProfile',
|
||||
formatter: res => res.data,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
get: {
|
||||
url: 'https://nei.hz.netease.com/api/apimock-v2/cc974ffbaa7a85c77f30e4ce67deb67f/api/getUserProfile',
|
||||
formatter: res => res.data,
|
||||
@@ -233,20 +328,22 @@ p {
|
||||
|
||||
export const sampleFiles = [
|
||||
{ filename: '/package.json', code: JSON.stringify(packageJson) },
|
||||
{ filename: '/appJson.json', code: JSON.stringify(appJson) },
|
||||
{ filename: '/tango.config.json', code: JSON.stringify(tangoConfigJson) },
|
||||
{ filename: '/README.md', code: '# readme' },
|
||||
{ filename: '/src/index.less', code: lessCode },
|
||||
{ filename: '/src/style.css', code: cssCode },
|
||||
{ filename: '/src/index.js', code: entryCode },
|
||||
{ filename: '/src/pages/list.js', code: viewHomeCode },
|
||||
{ filename: '/src/components/button.js', code: componentsButtonCode },
|
||||
{ filename: '/src/components/input.js', code: componentsInputCode },
|
||||
{ filename: '/src/components/index.js', code: componentsEntryCode },
|
||||
{ filename: '/src/routes.js', code: routesCode },
|
||||
{ filename: '/src/stores/index.js', code: storeIndexCode },
|
||||
{ filename: '/src/stores/app.js', code: storeApp },
|
||||
{ filename: '/src/stores/counter.js', code: storeCounter },
|
||||
{ filename: '/src/services/index.js', code: serviceCode },
|
||||
{ filename: '/src/services/sub.js', code: subServiceCode },
|
||||
{ filename: '/src/utils/index.js', code: `export function foo() {}` },
|
||||
{ filename: '/src/utils/index.js', code: helperCode },
|
||||
];
|
||||
|
||||
export const genDefaultPage = (index: number) => ({
|
||||
@@ -3,3 +3,4 @@
|
||||
|
||||
@primary-color: #2f54eb;
|
||||
@border-radius-base: 2px;
|
||||
|
||||
11
apps/playground/src/layouts/index.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Outlet } from 'umi';
|
||||
import './index.less';
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
apps/playground/src/pages/docs.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
const DocsPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>This is umi docs.</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DocsPage;
|
||||
@@ -1,21 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Tango Playground</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="https://unpkg.com/react@16.14.0/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.14.0/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/react-is@16.13.1/umd/react-is.production.min.js"></script>
|
||||
<script src="https://unpkg.com/moment/min/moment-with-locales.js"></script>
|
||||
<script src="https://unpkg.com/styled-components@5.3.11/dist/styled-components.js"></script>
|
||||
<script src="https://unpkg.com/@babel/standalone@7.15.6/babel.js"></script>
|
||||
<script src="https://unpkg.com/prettier@2.6.0/standalone.js"></script>
|
||||
<script src="https://unpkg.com/prettier@2.6.0/parser-babel.js"></script>
|
||||
<script src="https://unpkg.com/antd@4.24.13/dist/antd-with-locales.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,17 +12,17 @@ import {
|
||||
CodeEditor,
|
||||
Sandbox,
|
||||
DndQuery,
|
||||
themeLight,
|
||||
} from '@music163/tango-designer';
|
||||
import { createEngine, Workspace } from '@music163/tango-core';
|
||||
import { Logo, ProjectDetail } from './share';
|
||||
import { sampleFiles } from '../mock/project';
|
||||
import './index.less';
|
||||
import { Logo, ProjectDetail, bootHelperVariables, sampleFiles } from '../helpers';
|
||||
import {
|
||||
ApiOutlined,
|
||||
AppstoreAddOutlined,
|
||||
BuildOutlined,
|
||||
ClusterOutlined,
|
||||
FunctionOutlined,
|
||||
createFromIconfontCN,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
// 1. 实例化工作区
|
||||
@@ -39,18 +39,32 @@ const engine = createEngine({
|
||||
// @ts-ignore
|
||||
window.__workspace__ = workspace;
|
||||
|
||||
// 3. 沙箱初始化
|
||||
const sandboxQuery = new DndQuery({
|
||||
context: 'iframe',
|
||||
});
|
||||
|
||||
// 4. 图标库初始化(物料面板和组件树使用了 iconfont 里的图标)
|
||||
createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_2891794_lzc7rtwuzf.js',
|
||||
});
|
||||
|
||||
/**
|
||||
* 3. 平台初始化,访问 https://local.netease.com:6006/
|
||||
* 5. 平台初始化,访问 https://local.netease.com:6006/
|
||||
*/
|
||||
export default function App() {
|
||||
const [menuLoading, setMenuLoading] = useState(true);
|
||||
const [menuData, setMenuData] = useState(false);
|
||||
return (
|
||||
<Designer engine={engine} sandboxQuery={sandboxQuery}>
|
||||
<Designer
|
||||
theme={themeLight}
|
||||
engine={engine}
|
||||
config={{
|
||||
customActionVariables: bootHelperVariables,
|
||||
customExpressionVariables: bootHelperVariables,
|
||||
}}
|
||||
sandboxQuery={sandboxQuery}
|
||||
>
|
||||
<DesignerPanel
|
||||
logo={<Logo />}
|
||||
description={<ProjectDetail />}
|
||||
@@ -58,6 +72,8 @@ export default function App() {
|
||||
<Box px="l">
|
||||
<Toolbar>
|
||||
<Toolbar.Item key="routeSwitch" placement="left" />
|
||||
<Toolbar.Item key="history" placement="left" />
|
||||
<Toolbar.Item key="preview" placement="left" />
|
||||
<Toolbar.Item key="modeSwitch" placement="right" />
|
||||
<Toolbar.Item key="togglePanel" placement="right" />
|
||||
<Toolbar.Separator />
|
||||
@@ -71,7 +87,6 @@ export default function App() {
|
||||
}
|
||||
>
|
||||
<Sidebar>
|
||||
<Sidebar.Item key="outline" label="结构" icon={<BuildOutlined />} />
|
||||
<Sidebar.Item
|
||||
key="components"
|
||||
label="组件"
|
||||
@@ -81,6 +96,7 @@ export default function App() {
|
||||
loading: menuLoading,
|
||||
}}
|
||||
/>
|
||||
<Sidebar.Item key="outline" label="结构" icon={<BuildOutlined />} />
|
||||
<Sidebar.Item
|
||||
key="variables"
|
||||
label="变量"
|
||||
@@ -111,9 +127,13 @@ export default function App() {
|
||||
workspace.setComponentPrototypes(sandboxWindow.TangoAntd.prototypes);
|
||||
}
|
||||
}
|
||||
if (sandboxWindow.localTangoComponentPrototypes) {
|
||||
workspace.setComponentPrototypes(sandboxWindow.localTangoComponentPrototypes);
|
||||
}
|
||||
setMenuLoading(false);
|
||||
}
|
||||
}}
|
||||
navigatorExtra={<Button size="small">hello world</Button>}
|
||||
/>
|
||||
</WorkspaceView>
|
||||
<WorkspaceView mode="code">
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
"extends": "./src/.umi/tsconfig.json"
|
||||
}
|
||||
|
||||
1
apps/playground/typings.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import 'umi/typings';
|
||||
@@ -16,8 +16,8 @@
|
||||
"dependencies": {
|
||||
"@music163/tango-setting-form": "*",
|
||||
"@music163/tango-ui": "*",
|
||||
"mobx": "6.9.0",
|
||||
"mobx-react-lite": "4.0.2"
|
||||
"mobx": "6.12.0",
|
||||
"mobx-react-lite": "4.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^6.5.14",
|
||||
|
||||
70
apps/storybook/src/sandbox.stories.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { CodeSandbox } from '@music163/tango-sandbox';
|
||||
import { JsonView } from '@music163/tango-ui';
|
||||
|
||||
export default {
|
||||
title: 'CodeSandbox',
|
||||
};
|
||||
|
||||
const entryFilename = '/index.js';
|
||||
|
||||
const packageJson = {
|
||||
name: 'demo',
|
||||
private: true,
|
||||
main: entryFilename,
|
||||
dependencies: {
|
||||
react: '^17.0.1',
|
||||
'react-dom': '^17.0.1',
|
||||
},
|
||||
};
|
||||
|
||||
const entryFile = `
|
||||
import React from 'react';
|
||||
import ReactDOM from "react-dom";
|
||||
import App from './src';
|
||||
|
||||
const rootElement = document.getElementById("root");
|
||||
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
rootElement
|
||||
);
|
||||
`;
|
||||
|
||||
const srcAppFile = `
|
||||
import React from 'react';
|
||||
|
||||
export default function App() {
|
||||
return <div style={{ background: 'yellow', color: 'red' }}>hello world</div>;
|
||||
}
|
||||
`;
|
||||
|
||||
const files = {
|
||||
'/package.json': {
|
||||
code: JSON.stringify(packageJson),
|
||||
},
|
||||
[entryFilename]: {
|
||||
code: entryFile,
|
||||
},
|
||||
'/src/index.js': { code: srcAppFile },
|
||||
};
|
||||
|
||||
export function Basic() {
|
||||
return (
|
||||
<Box display="flex" height="100vh">
|
||||
<CodeSandbox
|
||||
template="create-react-app"
|
||||
bundlerURL="https://codesandbox.fn.netease.com"
|
||||
files={files}
|
||||
entry={entryFilename}
|
||||
style={{
|
||||
flex: 1,
|
||||
}}
|
||||
/>
|
||||
<Box width="30%">
|
||||
<JsonView src={files} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,58 +1,21 @@
|
||||
import React, { useState } from 'react';
|
||||
import { EditableVariableTree, FormModel, SettingForm } from '@music163/tango-setting-form';
|
||||
import { ComponentPrototypeType, getValue } from '@music163/tango-helpers';
|
||||
import { createServices } from '@music163/request';
|
||||
import React from 'react';
|
||||
import { FormModel, SettingForm, register } from '@music163/tango-setting-form';
|
||||
import { ComponentPrototypeType } from '@music163/tango-helpers';
|
||||
import { BorderSetter } from '@music163/tango-designer/src/setters/style-setter';
|
||||
import { Box } from 'coral-system';
|
||||
import { JsonView } from '@music163/tango-ui';
|
||||
import { toJS } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Card } from 'antd';
|
||||
|
||||
export default {
|
||||
title: 'SettingForm/ Setters',
|
||||
};
|
||||
register({
|
||||
name: 'borderSetter',
|
||||
component: BorderSetter,
|
||||
});
|
||||
|
||||
const modelVariables = [
|
||||
{
|
||||
title: 'stores',
|
||||
key: 'stores',
|
||||
selectable: false,
|
||||
children: [
|
||||
{
|
||||
title: 'app',
|
||||
key: 'stores.app',
|
||||
selectable: false,
|
||||
children: [
|
||||
{ title: 'app.title', key: 'stores.app.title', raw: '"hello"' },
|
||||
{ title: 'app.age', key: 'stores.app.age', raw: '20' },
|
||||
{ title: 'app.detail', key: 'stores.app.detail', raw: '{ foo: "foo" }' },
|
||||
{ title: 'app.list', key: 'stores.app.list', type: 'function', raw: '() => {}' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'user',
|
||||
key: 'stores.user',
|
||||
selectable: false,
|
||||
children: [
|
||||
{ title: 'name', key: 'stores.user.name' },
|
||||
{ title: 'age', key: 'stores.user.age' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'services',
|
||||
key: 'services',
|
||||
selectable: false,
|
||||
children: [
|
||||
{
|
||||
title: 'listUsers',
|
||||
key: 'services.listUsers',
|
||||
type: 'function',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
export default {
|
||||
title: 'SettingForm',
|
||||
};
|
||||
|
||||
const prototype: ComponentPrototypeType = {
|
||||
name: 'Test',
|
||||
@@ -69,6 +32,11 @@ const prototype: ComponentPrototypeType = {
|
||||
title: 'textSetter',
|
||||
setter: 'textSetter',
|
||||
},
|
||||
{
|
||||
name: 'border',
|
||||
title: 'borderSetter',
|
||||
setter: 'borderSetter',
|
||||
},
|
||||
{
|
||||
name: 'router',
|
||||
title: 'routerSetter',
|
||||
@@ -257,47 +225,6 @@ const prototype: ComponentPrototypeType = {
|
||||
],
|
||||
};
|
||||
|
||||
const deerService = createServices(
|
||||
{
|
||||
listMy: {
|
||||
url: '/my/upload/list',
|
||||
},
|
||||
listFav: {
|
||||
url: '/my/star/list',
|
||||
},
|
||||
listPub: {
|
||||
url: '/list',
|
||||
},
|
||||
},
|
||||
{
|
||||
baseURL: 'https://febase-openapi.fn.netease.com/deer/api/deer/pic',
|
||||
withCredentials: false, // 解决跨域时必须非*问题
|
||||
headers: {
|
||||
'Febase-Auth': 'dskPVkIRnQ2dEn1DbyxURmUj4rl4BsCh53xFAsJnvVs=',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const context = {
|
||||
stores: {
|
||||
app: {
|
||||
title: 'Sample App',
|
||||
age: 20,
|
||||
detail: {
|
||||
foo: 'foo',
|
||||
bar: 'bar',
|
||||
biz: {
|
||||
x: 'xxx',
|
||||
},
|
||||
},
|
||||
newKey: 'xxx',
|
||||
},
|
||||
foo: {
|
||||
test: 'test string',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单值预览
|
||||
*/
|
||||
@@ -310,9 +237,7 @@ export function Basic() {
|
||||
const model = new FormModel(
|
||||
{
|
||||
router: 'www.163.com',
|
||||
expression: {
|
||||
foo: 'foo',
|
||||
},
|
||||
expression: `{ foo: 'foo' }`,
|
||||
object: {
|
||||
name: 'Alice',
|
||||
},
|
||||
@@ -324,19 +249,7 @@ export function Basic() {
|
||||
|
||||
return (
|
||||
<Box display="flex">
|
||||
<SettingForm
|
||||
model={model}
|
||||
remoteServices={{
|
||||
ImageService: deerService as any,
|
||||
}}
|
||||
prototype={prototype}
|
||||
evaluateContext={{
|
||||
tango: context,
|
||||
__UNSAFE_TANGO_CURRENT_PAGE_RENDER_RUNTIME__: { routeData: { params: {}, query: {} } },
|
||||
}}
|
||||
modelVariables={modelVariables}
|
||||
expressionVariables={modelVariables}
|
||||
/>
|
||||
<SettingForm model={model} prototype={prototype} />
|
||||
<Box position="relative">
|
||||
<Card title="表单状态预览" style={{ position: 'sticky', top: 0 }}>
|
||||
<FormValuePreview model={model} />
|
||||
@@ -346,6 +259,20 @@ export function Basic() {
|
||||
);
|
||||
}
|
||||
|
||||
export function Lite() {
|
||||
return (
|
||||
<Box width={320} border="solid">
|
||||
<SettingForm
|
||||
showSearch={false}
|
||||
showGroups={false}
|
||||
showItemSubtitle={false}
|
||||
prototype={prototype}
|
||||
disableSwitchExpressionSetter
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export function NoExpressionSwitch() {
|
||||
const model = new FormModel({});
|
||||
return (
|
||||
@@ -354,30 +281,3 @@ export function NoExpressionSwitch() {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export function Binding() {
|
||||
const [data, setData] = useState<any>({});
|
||||
return (
|
||||
<Box>
|
||||
<Box as="code" bg="highlight" color="white" fontSize="24px">
|
||||
selected: {data?.key}
|
||||
</Box>
|
||||
<EditableVariableTree
|
||||
dataSource={modelVariables as any}
|
||||
getPreviewValue={(node) => {
|
||||
if (!node || !node.key) {
|
||||
return;
|
||||
}
|
||||
if (node.type === 'function') {
|
||||
return node.raw;
|
||||
}
|
||||
const keyPath = node.key.replaceAll('?', '');
|
||||
return getValue(context, keyPath);
|
||||
}}
|
||||
onSelect={setData}
|
||||
onAddVariable={console.log}
|
||||
onSave={console.log}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,41 @@ export function Basic() {
|
||||
);
|
||||
}
|
||||
|
||||
const code = `
|
||||
function foo() {
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
}
|
||||
`;
|
||||
|
||||
export function Readonly() {
|
||||
return <InputCode shape="inset" readOnly value={code} />;
|
||||
}
|
||||
|
||||
export function Inset() {
|
||||
return <InputCode shape="inset" />;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { ToggleButton, IconFont } from '@music163/tango-ui';
|
||||
import { Box, Flex } from 'coral-system';
|
||||
import { ToggleButton, UndoOutlined, RedoOutlined } from '@music163/tango-ui';
|
||||
import { Box, Group, Flex } from 'coral-system';
|
||||
|
||||
export default {
|
||||
title: 'UI/ToggleButton',
|
||||
@@ -9,28 +9,73 @@ export default {
|
||||
export function Basic() {
|
||||
return (
|
||||
<Box>
|
||||
<Flex gap="l" bg="#efefef" p="l">
|
||||
<Box display="flex" columnGap="l" p="l">
|
||||
<ToggleButton>
|
||||
<IconFont type="icon-undo" />
|
||||
<UndoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton selected>
|
||||
<IconFont type="icon-redo" />
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton disabled>
|
||||
<IconFont type="icon-redo" />
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
</Box>
|
||||
<Box display="flex" columnGap="l" p="l">
|
||||
<ToggleButton type="primary">
|
||||
<UndoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton type="primary" selected>
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton type="primary" disabled>
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export function DarkMode() {
|
||||
return (
|
||||
<Box>
|
||||
<Flex gap="l" bg="#222" p="l">
|
||||
<ToggleButton shape="ghost">
|
||||
<UndoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton shape="ghost" selected>
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton shape="ghost" disabled>
|
||||
<UndoOutlined />
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
<Flex gap="l" bg="#222" p="l">
|
||||
<ToggleButton shape="ghost">
|
||||
<IconFont type="icon-undo" />
|
||||
<ToggleButton shape="text">
|
||||
<UndoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton shape="ghost" selected>
|
||||
<IconFont type="icon-redo" />
|
||||
<ToggleButton shape="text" selected>
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton shape="ghost" disabled>
|
||||
<IconFont type="icon-undo" />
|
||||
<ToggleButton shape="text" disabled>
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export function ButtonGroup() {
|
||||
return (
|
||||
<Group attached>
|
||||
<ToggleButton>
|
||||
<UndoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton selected>
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
<ToggleButton disabled>
|
||||
<RedoOutlined />
|
||||
</ToggleButton>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
72
apps/storybook/src/ui/var-tree.stories.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { VariableTree } from '@music163/tango-designer/src/components';
|
||||
import { Box } from 'coral-system';
|
||||
|
||||
export default {
|
||||
title: 'Designer/VariableTree',
|
||||
};
|
||||
|
||||
const data = [
|
||||
{
|
||||
title: 'Stores',
|
||||
key: 'stores',
|
||||
selectable: false,
|
||||
children: [
|
||||
{
|
||||
title: 'user',
|
||||
key: 'stores.user',
|
||||
children: [
|
||||
{
|
||||
title: 'name',
|
||||
key: 'stores.user.name',
|
||||
raw: '"Tom"',
|
||||
},
|
||||
{
|
||||
title: 'age',
|
||||
key: 'stores.user.age',
|
||||
raw: '18',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'book',
|
||||
key: 'stores.book',
|
||||
children: [
|
||||
{
|
||||
title: 'name',
|
||||
key: 'stores.book.name',
|
||||
raw: '"JavaScript 高级程序设计"',
|
||||
},
|
||||
{
|
||||
title: 'price',
|
||||
key: 'stores.book.price',
|
||||
raw: '99.99',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Services',
|
||||
key: 'services',
|
||||
selectable: false,
|
||||
children: [
|
||||
{
|
||||
title: 'add',
|
||||
key: 'services.add',
|
||||
},
|
||||
{
|
||||
title: 'multiply',
|
||||
key: 'services.multiply',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export function Basic() {
|
||||
return (
|
||||
<Box width={600} border="solid">
|
||||
<VariableTree dataSource={data} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -10,11 +10,20 @@
|
||||
|
||||
项目使用的 npm 包依赖信息,用于定义依赖的类型以及外置依赖资源。该配置项为一个 map,key 为对应的 npm 包名,value 为该依赖的配置信息。
|
||||
|
||||
- version 包的版本号,必选
|
||||
- library UMD 包中的全局变量名,必选
|
||||
- type 依赖的类型
|
||||
- baseDependency 基础依赖包
|
||||
- bizDependency 业务组件依赖包
|
||||
- dependency 其他依赖包
|
||||
- resources 外置依赖资源列表
|
||||
- designerResources 在设计器视图载入的外置资源列表,用来需要区别加载时使用
|
||||
- description 描述信息
|
||||
|
||||
依赖的类型 `type` 可以是 `baseDependency` 即项目的基础依赖,用于获取项目基础物料列表的信息;也可以是 `bizDependency` 即业务组件依赖,用于获取合并到物料面板业务组件菜单中的业务组件列表;也可以是 `depenency` 即项目的其他依赖,只用于提供 external 信息与版本信息等,不会和物料面板产生关系。
|
||||
|
||||
当配置了外置依赖资源 `resources` 与其对应的全局变量 `library` 时,当这些依赖被 import 时,相关资源不会被打包到 bundle 中,而是在运行时再去外部获取这些扩展依赖,其等效于之前的 `sandbox.externalResources` 与 `sandbox.externals` 配置项。此外,可以将 `resource` 中的版本号替换为 `{{version}}`,其会在平台上构建时替换为版本号。特殊的,可以定义 `designerResources` 用于适配平台的设计器视图。
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"react": {
|
||||
|
||||
135
apps/website/docs/designer/customize/components.mdx
Normal file
@@ -0,0 +1,135 @@
|
||||
# 组件库自定义
|
||||
|
||||
Tango 提供了基于源码的低代码开发能力,默认不提供私有的内置组件库,仅提供了一个简单的示例用于说明如何将只有组件库接入到 Tango 中。
|
||||
|
||||
:::tip
|
||||
**组件库接入**意味着开发者可以将自己的组件库接入到 Tango 中,以便于在设计器中使用自己的组件,包括拖拽、配置、生成等行为。
|
||||
:::
|
||||
|
||||
## 基本的目录结构
|
||||
|
||||
基本的组件库目录结构如下:
|
||||
|
||||
```text
|
||||
+ src
|
||||
+ button
|
||||
- view.tsx // 默认视图文件
|
||||
- index.ts // 渲染视图入口文件
|
||||
- designer.ts // 设计器视图入口文件
|
||||
- prototype.ts // 【新增】组件描述文件
|
||||
+ date-picker
|
||||
- index.ts // 组件包默认入口文件
|
||||
- designer.ts // 【新增】组件包设计器视图入口文件
|
||||
```
|
||||
|
||||
可以发现,相比正常的组件代码,Tango 对于接入的组件库要求提供 2 个全新的文件 `designer.ts` 和 `prototype.ts`。其中 `designer.ts` 为设计视图文件,用来实现在 Tango 设计器中的辅助搭建行为,如果无需定制,可以直接保持 `designer.ts` 文件和 `index.ts` 文件一致。而 `prototype.ts` 文件则是用来描述组件的属性和行为的,用于在设计器中渲染组件的配置项,和控制组件的拖拽行为等。
|
||||
|
||||
一个可供参考的示例代码是 <https://github.com/NetEase/tango-components/tree/main/packages/antd/src>
|
||||
|
||||
### designer.ts
|
||||
|
||||
在 `designer.ts` 文件中,相比原有的组件库入口文件,还需要导出 `menuData` 和 `prototypes` 两个模块。
|
||||
|
||||
```jsx
|
||||
export * from './button';
|
||||
export * from './card';
|
||||
//...
|
||||
|
||||
// 组件的配置描述列表
|
||||
export const prototypes = [
|
||||
{
|
||||
title: '按钮',
|
||||
name: 'Button',
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
setter: 'textSetter',
|
||||
},
|
||||
//...
|
||||
],
|
||||
},
|
||||
//...
|
||||
];
|
||||
|
||||
export const menuData = {
|
||||
// 常用组件
|
||||
common: [
|
||||
{
|
||||
title: '基本',
|
||||
items: ['Button'],
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
其中 `menuData` 的 `key` 可选列表如下:
|
||||
|
||||
| key | 说明 |
|
||||
| ------- | -------- |
|
||||
| common | 常用组件 |
|
||||
| atom | 原子组件 |
|
||||
| snippet | 代码片段 |
|
||||
|
||||
### prototype.ts
|
||||
|
||||
组件的配置描述文件。
|
||||
|
||||
## 组件配置描述
|
||||
|
||||
### ComponentPrototypeType
|
||||
|
||||
组件的配置描述。
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| -------------- | ----------------------------------------------- | ---------------------------------------------------------------------------- | ------ |
|
||||
| childrenName | 子组件的名称 | `string` | - |
|
||||
| docs | 组件的文档地址 | `string` | - |
|
||||
| exportType | 组件的导出类型 `defaultExport` \| `namedExport` | `string` | - |
|
||||
| hasChildren | 是否有子组件 | `boolean` | - |
|
||||
| help | 组件的帮助文档 | `string` | - |
|
||||
| icon | 组件的图标,图片地址或 iconfont 图标名 | string | - |
|
||||
| name | 组件的名称 | `string` | - |
|
||||
| package | 组件的包名,或引入路径 | `string` | - |
|
||||
| props | 组件的属性描述 | `ComponentPropType[]` | - |
|
||||
| relatedImports | 组件的相关引入 | `string[]` | - |
|
||||
| rules | 组件的规则 | `ComponentDndRulesType` | - |
|
||||
| title | 组件的标题 | `string` | - |
|
||||
| type | 组件的类型 | "page" \| "container" \| "placeholder" \| "element" \| "snippet" \| "block"` | - |
|
||||
| usage | 组件的使用说明 | `string` | - |
|
||||
|
||||
### ComponentPropType
|
||||
|
||||
组件的属性描述。
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| --------------------- | -------------------------------------------- | ------------------------------------------- | ------ |
|
||||
| autoCompleteOptions | 自动补全的提示值,仅对 ExpressionSetter 有效 | `string[]` | - |
|
||||
| autoInitValue | 如果没提供 initValue, 是否自动初始化值 | `boolean` | - |
|
||||
| defaultValue | 组件的内置默认值 | `any` | - |
|
||||
| disableVariableSetter | 是否禁用变量设置器 | `boolean` | - |
|
||||
| docs | 属性的文档地址 | `string` | - |
|
||||
| getProp | 动态设置属性,覆盖已有的 prop 对象 | `(form) => any` | - |
|
||||
| getSetterProps | 动态设置属性,覆盖已有的 setterProps 对象 | `(form) => any` | - |
|
||||
| getVisible | 动态设置表单项是否展示 | `(form) => boolean` | - |
|
||||
| group | 属性的分组 | `basic` \| `event` \| `style` \| `advanced` | - |
|
||||
| initValue | 首次拖拽后用来初始化组件的属性值 | `any` | - |
|
||||
| name | 属性的名称 | `string` | - |
|
||||
| options | 属性的选项 | `any[]` | - |
|
||||
| placeholder | 属性的占位符 | `string` | - |
|
||||
| props | 如果是对象属性,这里声明子属性列表 | `ComponentPropType[]` | - |
|
||||
| setter | 属性的设置器 | `string` | - |
|
||||
| setterProps | 设置器的属性设置 | `any` | - |
|
||||
| tips | 属性的提示 | `string` | - |
|
||||
| title | 属性的标题 | `string` | - |
|
||||
|
||||
### ComponentDndRulesType
|
||||
|
||||
组件拖拽规则类型
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| ------------------------- | ---------------------------------------------------------------------------- | --------------------------- | ------ |
|
||||
| canDrag | 当前组件是否可以被拖拽 | `() => boolean` | - |
|
||||
| canDrop | 当前节点是否可以拖拽到目标节点中 | `(targetName) => boolean` | - |
|
||||
| canMoveIn | 进来的节点是否可以落进来,仅适用于容器节点 | `(incomingName) => boolean` | - |
|
||||
| canMoveOut | 被拖拽的节点是否可以被拖离当前节点,仅适用于容器节点 | `(outgoingName) => boolean` | - |
|
||||
| childrenContainerSelector | 子节点的容器选择器,用于快速定位子节点容器,适合组件存在多个可搭建区域时使用 | `string` | - |
|
||||
5
apps/website/docs/designer/customize/editor.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
# 编辑器自定义
|
||||
|
||||
:::tip
|
||||
正在编写中,敬请期待。
|
||||
:::
|
||||
72
apps/website/docs/designer/customize/panels.mdx
Normal file
@@ -0,0 +1,72 @@
|
||||
# 面板自定义
|
||||
|
||||
Tango 设计器由多个可自定义的面板组成,默认提供了一个标准的低代码设计器布局,可以根据实际需求进行自定义。如下图所示,可以对设计器的多个区域进行自定义,包括工具栏、侧边栏、配置面板、选择工具栏等等。
|
||||
|
||||

|
||||
|
||||
- 工具栏(Toolbar):设计器的顶部区域,通常用来放置一些全局信息和操作按钮,例如项目信息,顶部工具栏、发布按钮等等。
|
||||
- 侧边栏(Sidebar):设计器的左侧区域,通常用来放置一些核心功能的入口,例如k组件库、页面列表、变量配置、数据源配置等。
|
||||
- 配置面板(Properties):设计器的右侧区域,通常用来放置一些配置项,例如组件属性配置、页面属性配置等。
|
||||
- 选择工具栏(SelectionMenu):设计器的底部区域,通常用来放置一些选择工具,例如复制、删除、定位等。
|
||||
|
||||
除了使用预定义的设计器布局组件,Tango 支持完全使用开发者自行开发的组件进行替换。
|
||||
|
||||
## 内置设计器布局组件介绍
|
||||
|
||||
内置的设计器布局组件包括:
|
||||
|
||||
- Designer:设计器的状态容器,用来提供设计器核心状态的上下文容器,包括主题、引擎状态、沙箱状态等。
|
||||
- DesignerPanel:设计器的主框架,提供了核心的布局容器,便于开发者进行后续的自定义。
|
||||
- Toolbar:工具栏容器组件,提供了默认的工具栏的布局设置,可以通过 `Toolbar.Item` 进行快捷的定义工具栏的子项。
|
||||
- Sidebar:侧边栏容器组件,提供了默认的侧边栏的布局设置,可以通过 `Sidebar.Item` 进行快捷的定义侧边栏的子项。
|
||||
- WorkspacePanel:工作区容器组件,设计器中央的区域,包括画布和编辑等用户核心工作区,提供了默认的工作区的布局配置。
|
||||
- WorkspaceView:工作区的视图组件,用于快捷定制工作区的多种视图模式,可以实例化多个工作区视图,但同一时间只能由一个工作区视图处于激活状态。
|
||||
- SettingPanel:配置面板组件,提供了默认的属性配置能力。
|
||||
|
||||
## 基本的设计器布局
|
||||
|
||||
一个基本的的设计器布局示例如下图所示:
|
||||
|
||||
```jsx
|
||||
export default function App() {
|
||||
return (
|
||||
<Designer theme={themeLight} engine={engine} sandboxQuery={sandboxQuery}>
|
||||
<DesignerPanel
|
||||
logo={<Logo />}
|
||||
description={<ProjectDetail />}
|
||||
actions={
|
||||
<Box px="l">
|
||||
<Toolbar>
|
||||
<Toolbar.Item key="routeSwitch" placement="left" />
|
||||
<Toolbar.Item key="history" placement="left" />
|
||||
<Toolbar.Item key="preview" placement="left" />
|
||||
<Toolbar.Item key="modeSwitch" placement="right" />
|
||||
<Toolbar.Item key="togglePanel" placement="right" />
|
||||
<Toolbar.Separator />
|
||||
<Toolbar.Item placement="right">
|
||||
<Space>
|
||||
<Button type="primary">发布</Button>
|
||||
</Space>
|
||||
</Toolbar.Item>
|
||||
</Toolbar>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Sidebar>
|
||||
<Sidebar.Item key="components" label="组件" icon={<AppstoreAddOutlined />} />
|
||||
<Sidebar.Item key="outline" label="结构" icon={<BuildOutlined />} />
|
||||
</Sidebar>
|
||||
<WorkspacePanel>
|
||||
<WorkspaceView mode="design">
|
||||
<CustomDesignView />
|
||||
</WorkspaceView>
|
||||
<WorkspaceView mode="code">
|
||||
<CustomSourceCodeView />
|
||||
</WorkspaceView>
|
||||
</WorkspacePanel>
|
||||
<SettingPanel />
|
||||
</DesignerPanel>
|
||||
</Designer>
|
||||
);
|
||||
}
|
||||
```
|
||||
4
apps/website/docs/designer/customize/selections.mdx
Normal file
@@ -0,0 +1,4 @@
|
||||
# 选择器自定义
|
||||
|
||||
提供设计器视图中用户选中某个区域后展示的快捷工具。
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
# 属性设置器
|
||||
# 设置器自定义
|
||||
|
||||
属性设置器用于在配置面板中展示特定配置项的配置逻辑。Tango 内置了多种标准的属性设置器,对于一些特殊场景,内置的属性设置器可能无法满足你的需要,此时开发者可以扩展自己的属性设置。
|
||||
|
||||
## 设置器组件
|
||||
|
||||
### SettingPanel
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| ---------------- | -------------------------------------- | ---------------------------- | ------ |
|
||||
| title | 面板标题 | string | - |
|
||||
| defaultValue | 默认值 | object | - |
|
||||
| groupOptions | 分组选项 | object | - |
|
||||
| model | 表单状态管理实例 | FormModel | - |
|
||||
| onChange | 值变化回调 | (name, value, field) => void | - |
|
||||
| prototype | 组件的可配置描述 | ComponentPrototype | - |
|
||||
| renderItemExtra | 自定义渲染表单项的额外内容(标签右侧) | (props) => ReactNode | - |
|
||||
| showGroups | 是否展示分组 | boolean | - |
|
||||
| showItemSubtitle | 是否展示表单项的副标题 | boolean | - |
|
||||
| showSearch | 是否展示搜索框 | boolean | - |
|
||||
|
||||
## 内置属性设置器
|
||||
|
||||
| 设置器名 | 接收值类型 | 设置器说明 | 可配置项 |
|
||||
@@ -19,8 +36,6 @@
|
||||
| expressionSetter | expression | 表达式设置器 | |
|
||||
| jsonSetter | json expression | JSON 表达式设置器 | |
|
||||
| jsxSetter | jsx expression | JSX 设置器 | |
|
||||
| iconSetter | string | Icon 组件设置器 | |
|
||||
| iconTypeSetter | string | Icon 组件类型设置器 | |
|
||||
| numberSetter | number | 数字类型设置器 | |
|
||||
| textSetter | string | 文本设置器 | |
|
||||
| textAreaSetter | string | 文本域设置器 | |
|
||||
@@ -29,7 +44,6 @@
|
||||
| sliderSetter | number | 滑块设置器 | |
|
||||
| listSetter | `object[]` | 列表值设置器 | |
|
||||
| renderPropsSetter | Function | render props 设置器 | |
|
||||
| imageSetter | string | 云鹿图片设置器 | |
|
||||
|
||||
## 注册自定义属性设置器
|
||||
|
||||
53
apps/website/docs/designer/customize/sidebar.mdx
Normal file
@@ -0,0 +1,53 @@
|
||||
# 侧边栏自定义
|
||||
|
||||
提供了默认的设计器左侧边栏的布局设置。
|
||||
|
||||
## 侧边栏组件
|
||||
|
||||
### Sidebar
|
||||
|
||||
侧边栏容器组件。
|
||||
|
||||
### Sidebar.Item
|
||||
|
||||
侧边栏子项
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| ----------- | ------------------------------------------------ | ------------------------------------------------- | ------ |
|
||||
| key | 子项的唯一标识 | `string` | - |
|
||||
| label | 子项的描述文本,推荐不超过2个字 | `string` | - |
|
||||
| icon | 图标 | `ReactNode` | - |
|
||||
| showBadge | 是否显示角标 | `boolean` \| `{ count?: number; dot?: boolean; }` | - |
|
||||
| title | 展开面板的标题 | `string` | - |
|
||||
| width | 展开面板的宽度 | `number` | - |
|
||||
| isFloat | 展开面板是否为浮动面板,浮动面板不压缩工作区宽度 | `boolean` | - |
|
||||
| widgetProps | 子项的属性 | `object` | - |
|
||||
| children | 子项的展开面板内容 | `ReactNode` | - |
|
||||
|
||||
## 内置的工具栏组件
|
||||
|
||||
设计器内置了一些基本的侧边栏组件,当子项的 `key` 使用了特定的值时,会自动的进行渲染。
|
||||
|
||||
| key | 组件 |
|
||||
| ---------- | ---------- |
|
||||
| components | 组件列表 |
|
||||
| outline | 结构 |
|
||||
| dependency | 依赖管理 |
|
||||
| variables | 变量管理 |
|
||||
| dataSource | 数据源管理 |
|
||||
|
||||
例如,下面的代码会自动渲染一个结构面板:
|
||||
|
||||
```jsx
|
||||
<Sidebar.Item key="outline" label="结构" icon={<BuildOutlined />} />
|
||||
```
|
||||
|
||||
## 自定义侧边栏子项
|
||||
|
||||
可以直接在 `Sidebar.Item` 的子节点传入自定义的内容来渲染需要的结果,例如:
|
||||
|
||||
```jsx
|
||||
<Sidebar.Item key="custom" label="自定义" icon={<SmileOutlined />}>
|
||||
<div>展开的内容部分</div>
|
||||
</Sidebar.Item>
|
||||
```
|
||||
53
apps/website/docs/designer/customize/tools.mdx
Normal file
@@ -0,0 +1,53 @@
|
||||
# 工具栏自定义
|
||||
|
||||
提供了默认的设计器工具栏的布局设置。
|
||||
|
||||
## 工具栏组件
|
||||
|
||||
### Toolbar
|
||||
|
||||
工具栏列表容器
|
||||
|
||||
### Toolbar.Item
|
||||
|
||||
工具栏子项
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
| ----------- | -------------- | --------------------------- | ------- |
|
||||
| key | 子项的唯一标识 | `string` | - |
|
||||
| placement | 放置的位置 | `left` \| `right` \| `left` | `right` |
|
||||
| widgetProps | 子项的属性 | `object` | - |
|
||||
|
||||
### Toolbar.Separator
|
||||
|
||||
工具栏分隔线,用来对工具栏子项进行分组展示。
|
||||
|
||||
## 内置的工具栏组件
|
||||
|
||||
设计器内置了一些基本的工具栏组件,当工具栏子项使用了特定的 `key` 值时,会自动的进行渲染。
|
||||
|
||||
| key | 组件 |
|
||||
| ----------- | ------------------------------------ |
|
||||
| routeSwitch | 路由切换 |
|
||||
| history | 历史记录 |
|
||||
| preview | 沙箱预览 |
|
||||
| modeSwitch | 工作区模式切换,在源码和设计模式切换 |
|
||||
| togglePanel | 切换布局面板的显示和隐藏 |
|
||||
|
||||
例如,下面的代码会自动渲染为一个路由切换的工具项。
|
||||
|
||||
```jsx
|
||||
<Toolbar.Item key="routeSwitch" />
|
||||
```
|
||||
|
||||
## 自定义工具栏项
|
||||
|
||||
可以直接在 `Toolbar.Item` 的子节点传入自定义的工具栏项,例如:
|
||||
|
||||
```jsx
|
||||
<Toolbar.Item placement="right">
|
||||
<Space>
|
||||
<Button type="primary">发布</Button>
|
||||
</Space>
|
||||
</Toolbar.Item>
|
||||
```
|
||||
18
apps/website/docs/designer/deploy/designer.mdx
Normal file
@@ -0,0 +1,18 @@
|
||||
# 设计器接入
|
||||
|
||||
设计器为用户提供应用搭建的可视化界面。有两种方式初始化低代码设计器:
|
||||
|
||||
1. clone 官方示例代码,按照文档说明直接启动项目。
|
||||
2. 手工引入设计器的 npm 包,自定义配置、启动、运行。
|
||||
|
||||
## 方法1: 通过示例代码启动设计器
|
||||
|
||||
WIP
|
||||
|
||||
:::tip
|
||||
官方示例是一个包含了低代码设计器前后端低完整项目,可以直接启动。对于后端部分,作为示例而言,仅提供了最基本的逻辑,用户需要按照需求自行扩展。
|
||||
:::
|
||||
|
||||
## 方法2: 手工引入设计器的 npm 包
|
||||
|
||||
WIP
|
||||
19
apps/website/docs/designer/deploy/sandbox.mdx
Normal file
@@ -0,0 +1,19 @@
|
||||
# 低代码沙箱接入
|
||||
|
||||
沙箱是搭建产物(对于 Tango 主要是源码)的运行环境,它是一个独立的环境,可以在其中运行搭建产物,以便于开发者可以在不影响生产环境的情况下进行调试和测试。
|
||||
|
||||
Tango 沙箱由三个部分构成,包括低代码沙箱前端组件、在线打包器、沙箱后端服务,如下图所示。
|
||||
|
||||

|
||||
|
||||
- 沙箱前端组件:一个开箱即用的沙箱组件,只需要传入代码和配置就可以完成应用的渲染。
|
||||
- 在线打包器:提供搭建产物的浏览器端构建能力,类似于一个浏览器版本的 webpack,此部分逻辑主要来自于 [sandpack](https://sandpack.codesandbox.io/) 项目。
|
||||
- 沙箱后端服务:对依赖的资源进行预构建,以及提供资源合并等服务,用来加速沙箱内部的构建打包过程。
|
||||
|
||||
## 沙箱的前端组件接入
|
||||
|
||||
WIP
|
||||
|
||||
## 沙箱的后端服务接入
|
||||
|
||||
WIP
|
||||
7
apps/website/docs/designer/deploy/server.mdx
Normal file
@@ -0,0 +1,7 @@
|
||||
# 服务端接入
|
||||
|
||||
介绍一个基本的服务端实现,以及如何接入。
|
||||
|
||||
:::tip
|
||||
正在编写中,敬请期待。
|
||||
:::
|
||||
5
apps/website/docs/designer/design/filesystem.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
# 文件系统
|
||||
|
||||
:::tip
|
||||
正在编写中,敬请期待。
|
||||
:::
|
||||
112
apps/website/docs/designer/design/overview.mdx
Normal file
@@ -0,0 +1,112 @@
|
||||
# 技术架构概览
|
||||
|
||||
我们在 2023年8月底[正式开源了 Tango 低代码引擎](https://juejin.cn/post/7273051203562749971)。Tango 是一个基于源码的低代码设计器框架,支持直接基于项目源码提供低代码可视化开发能力,可以无缝的与既有的本地开发工作流进行集成,从而提供渐进式的低代码开发能力。
|
||||
|
||||

|
||||
|
||||
按照计划,我们在 2023年9月底[发布了 1.0 alpha 版本](https://github.com/NetEase/tango/releases),在此版本中我们遵循 **“最小内核”** 的原则对 Tango 的核心实现进行了大幅的重构,剥离了大量冗余的代码实现。
|
||||
|
||||
为了帮助大家更近一步的了解 Tango 开源版本的核心构成与代码实现,本文将会详细揭秘 Tango 低代码引擎的设计思考与实现过程。
|
||||
|
||||
- Github 仓库:<https://github.com/NetEase/tango>
|
||||
- 发行历史:<https://github.com/NetEase/tango/releases>
|
||||
- 文档站点:<https://netease.github.io/tango/>
|
||||
|
||||
## 低代码可视化搭建之殇
|
||||
|
||||
从实现上看,低代码搭建能力的核心是 UI 可视化编程。借助 UI 可视化编程,可以大大的弱化使用者对于代码编程的感知,但在真实的业务需求场景中,我们面临着大量的复杂的应用逻辑,使用者很难借助 UI 操作表达功能逻辑。例如下图中的合同管理,资金结算等页面。如果借助于传统的低代码方案,通常会发现,很容易一条路走到黑,没有回头路。所以,经常会有开发者抱怨,稍微复杂的场景下,低代码的效率甚至不如写代码。
|
||||
|
||||

|
||||
|
||||
## 传统低代码方案的问题
|
||||
|
||||
我们不妨先简单分析一下传统的低代码方案的问题。传统的低代码搭建方案往往采用定义私有 Schema 协议来可视化表达视图逻辑,也就是将代码逻辑转换为私有的描述,大致的原理可以参考下面这张图。
|
||||
|
||||

|
||||
|
||||
这类方案很容易面临不断膨胀的私有 JSON 协议。并且,私有协议扩展性和灵活性差,难以达到图灵完备状态。例如在我们的实际开发过程中,传统的低代码方案会面临各种各样的扩展性卡点。此外,开发能力往往受限于内置的组件和模板。且难以复用现有的前端资产,例如组件和代码等等。对于开发者而言,私有协议也导致问题定位难,调试难。
|
||||
|
||||
借助于私有协议的搭建方案通常适合于轻业务逻辑的简单类表单,营销类的活动页面等等,很难用于复杂的业务逻辑搭建场景,因为私有协议难以有效的应对这类场景的复杂性和灵活性需求。虽然,有些方案提供了协议转代码的能力,但通常只实现了单向转码,可视化开发和代码开发是两条完全割裂的路径。
|
||||
|
||||
**在此基础上,我们就需要重新思考低代码搭建协议的设计问题。**
|
||||
|
||||
## 从私有搭建协议到公有协议
|
||||
|
||||
那么,我们能否不使用私有协议,而是采用公有协议?
|
||||
|
||||
答案是,可以的 规范作为主流的处理 JavaScript 源代码的标准社区协议,被广泛用于浏览器 JavaScript Parser 的实现。借助于 ESTree 协议,可以完美的实现对源码逻辑的描述,并且社区有大量的工具可以帮助我们完成这个过程。
|
||||
|
||||

|
||||
|
||||
因此,我们尝试使用 ESTree 规范来实现低代码搭建过程。借助于 ESTree 规范,我们无需定义私有的渲染描述协议,并且可以低成本的实现代码到协议,协议到代码到互转。借助于双向转码的能力,我们获得全新的低代码开发体验。
|
||||
|
||||
## Tango 低代码引擎实现原理
|
||||
|
||||
基于这个思路,我们设计了基于 ESTree 规范的低代码引擎方案 -- Tango。可以通过下面这张图来简单的描述下实现逻辑:
|
||||
|
||||

|
||||
|
||||
首先将源代码解析为 AST。用户的拖拉拽等操作则映射为对 AST 的遍历和修改。最后将新的 AST 重新生成代码,交给设计器沙箱去渲染执行。而对 AST 的解析、遍历、修改、生成,则可以借助大量的社区工具,这里我们选择的是 babel!
|
||||
|
||||
> AST 的全称是抽象语法树,是一种分层的程序表达,根据编程语言的语法呈现源代码的结构。
|
||||
|
||||

|
||||
|
||||
其实,数量众多的前端工具库都是基于 AST 操纵实现的。我们可以发现,在任意的前端项目中的 package.json 里的 devDependencies 里的很多工具包是基于 AST 解析操纵实现的,例如 JS 的转译,代码压缩,ESLint 等等,我们可以阅读这些工具的源码来进一步的学习。
|
||||
|
||||

|
||||
|
||||
如图所示,将源代码转为 AST 描述的基本过程包括词法分析和句法分析两个阶段:
|
||||
|
||||
- 词法分析:借助词法分析器将代码字符串分割为标记列表。
|
||||
- 句法分析:借助句法分析器将标记数据转为 AST 描述。
|
||||
|
||||
最后,我们可以获得源代码的结构化描述树。有很多工具可以帮我们来实现这个过程,例如 babel -- 它可以帮助我们轻松的实现代码到 ast,ast 遍历修改,ast 到代码的过程。
|
||||
|
||||
## 基于 AST 实现搭建的基本过程
|
||||
|
||||
我们来看一下使用 ast 实现搭建逻辑的基本过程。
|
||||
|
||||
看一个具体的例子:通过修改 AST,在 Page 中插入一个 Section 节点。
|
||||
|
||||

|
||||
|
||||
中间这段代码,展示了核心的逻辑,通过遍历整个 AST 中的所有 JSXElement 节点,找到第一个 Page 元素,然后在 Page 元素的 children 里插入新的 Section 节点。这只是一段演示代码,具体的过程比这个要复杂的多,因为有很多的边际逻辑要处理。最后,我们可以将 ast 重新生成为代码,得到我们想要的结果。
|
||||
|
||||
## Tango 的数据变更流程设计
|
||||
|
||||
了解了基本的实现原理后,我们来看一下低代码引擎的数据变更流程设计。
|
||||
|
||||

|
||||
|
||||
首先是引擎初始化。源码文件会被引擎内核解析进行状态初始化。接下来,对于用户的操作,会触发浏览器事件,引擎接收到相应的事件,触发内核中的状态变更,更新 AST。
|
||||
|
||||
然后,内核会基于新的 AST 的同步生成代码,由引擎将代码同步给渲染沙箱。渲染沙箱感知到代码变化后,会触发页面重新渲染,也就是沙箱的 HMR 过程。
|
||||
|
||||
## 基于源码的在线渲染沙箱设计
|
||||
|
||||
接下来,我们需要考虑的是如何在浏览器中执行 JavaScript 源码工程?有很多方案可以选择,我们选择的方案是 [sandpack](https://sandpack.codesandbox.io/),它是由 CodeSandbox 开源的可以在浏览器中实时运行 JavaScript 项目的的工具库。在具体实现上,[我们对 sandpack 进行了一系列的改造](https://juejin.cn/post/7102243774985666596),以满足低代码生产环境的需要。
|
||||
|
||||
基于 sandpack 的在线渲染沙箱方案如下图图所示。
|
||||
|
||||

|
||||
|
||||
在实现上,主要包括 3 个部分,分别是:
|
||||
|
||||
- 低代码沙箱:它是一个开箱即用的前端组件,只需要传入源代码和构建配置信息即可完成前端项目的构建和执行。
|
||||
- 在线 Bundler:是低代码沙箱的核心,用来在浏览器上构建和执行源代码,本质上是一个在浏览器端运行的简化版 webpack。
|
||||
- 打包服务:是一个 node 服务,用来对 npm 包执行预构建和资源合并。
|
||||
|
||||
从沙箱执行流程来看,首先 Sandbox 组件将项目的源代码和 compile 指令使用 postMessage 传递给在线 Bundler,在线 Bundler 在接收到 compile 指令后,bundler 会从 packager 打包服务加载项目的 npm 依赖,然后编译和执行代码,最后发送 success 消息给低代码沙箱。
|
||||
|
||||
## Tango 低代码引擎的构成
|
||||
|
||||
结合上面的介绍,在构成上,Tango 低代码引擎主要包括 3 个核心组成部分,分别是:
|
||||
|
||||
- 引擎内核:扶额建立文件,节点模型,提供输入输出能力。
|
||||
- 拖拽引擎和可视化面板:提供可视化开发能力
|
||||
- 渲染沙箱:提供源码在浏览器上的编译执行能力。
|
||||
|
||||

|
||||
|
||||
借助于 Tango 低代码引擎,我们可以为开发者提供全新的在线开发体验,支持源码级的自定义能力。对可视化开发而言,可视化配置会触发 AST 的修改,进而会重新生成对应的源码。而对源码开发而言,修改源码后会同步更新 AST。
|
||||
5
apps/website/docs/designer/design/sandbox.mdx
Normal file
@@ -0,0 +1,5 @@
|
||||
# 沙箱实现
|
||||
|
||||
:::tip
|
||||
正在编写中,敬请期待。
|
||||
:::
|
||||
@@ -1,58 +0,0 @@
|
||||
# 设计器扩展概览
|
||||
|
||||
主要介绍如何扩展 tango 设计器。tango 设计器提供了三个部位的自定义扩展能力,分别是标题栏,工具栏,和侧边栏。可以通过下面这张图进行简要的了解:
|
||||
|
||||
<img src="https://p5.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/13359732115/c5ca/c5ae/bbd4/852daf849ac39162baf3bc3a985de6de.png" />
|
||||
|
||||
## 标题栏扩展
|
||||
|
||||
`DesignerPanel` 提供了 logo, description, actions 三个扩展点,分别对应于 平台标识、应用描述、行动点区域,开发者可以按照需求进行扩展。例如:
|
||||
|
||||
```jsx
|
||||
<DesignerPanel logo={<Logo />} description={<ProjectDetail />} actions={<Box>新版沙箱</Box>}>
|
||||
</SidebarPanel>
|
||||
```
|
||||
|
||||
## 工具栏扩展
|
||||
|
||||
工具栏面板支持任意组合和设置渲染的位置,开发者可以通过 `ToolbarPanel.Item` 添加自定义的工具栏选项,例如下面的代码扩展了一个 newPage 按钮,用于支持在设计器中创建新页面的需求。
|
||||
|
||||
```jsx
|
||||
<ToolbarPanel>
|
||||
<ToolbarPanel.Item key="history" />
|
||||
<ToolbarPanel.Item key="viewportRefresh" />
|
||||
<ToolbarPanel.Item key="preview" />
|
||||
<ToolbarPanel.Item key="routeSwitch" />
|
||||
<ToolbarPanel.Item key="newPage">
|
||||
<Button
|
||||
onClick={() => {
|
||||
const { name, code } = genDefaultPage(index);
|
||||
engine.workspace.addViewPage(`${name}${index++}`, code);
|
||||
message.success('页面新建成功');
|
||||
}}
|
||||
>
|
||||
添加新页面
|
||||
</Button>
|
||||
</ToolbarPanel.Item>
|
||||
<ToolbarPanel.Item key="modeSwitch" placement="center" />
|
||||
<ToolbarPanel.Item key="viewportSwitch" placement="right" />
|
||||
</ToolbarPanel>
|
||||
```
|
||||
|
||||
## 侧边栏扩展
|
||||
|
||||
侧边栏面板支持任意组合和调换顺序,用户可以通过 `SidebarPanel.Item` 组件进行自定义扩展,默认情况下根据一些内置的 key 标识,设计器会自动渲染对应的面板,用户也可以根据自己的需求采用自定义渲染子节点的方案进行扩展。
|
||||
例如,下面的示例代码中扩展了一个 “自定义面板”。
|
||||
|
||||
```jsx
|
||||
<SidebarPanel>
|
||||
<SidebarPanel.Item key="components" />
|
||||
<SidebarPanel.Item key="outline" />
|
||||
<SidebarPanel.Item key="dataSource" />
|
||||
<SidebarPanel.Item key="dependency" />
|
||||
<SidebarPanel.Item key="history" />
|
||||
<SidebarPanel.Item key="custom" title="自定义面板" icon={<BulbOutlined />}>
|
||||
<Box p="m">这里是一个自定义的面板,你可以任意添加</Box>
|
||||
</SidebarPanel.Item>
|
||||
</SidebarPanel>
|
||||
```
|
||||
@@ -1,67 +0,0 @@
|
||||
# 外部数据源
|
||||
|
||||
如果您的扩展部件需要使用外部数据源,可以参考本文的做法。
|
||||
|
||||
## 存在部件共享
|
||||
|
||||
如果数据源需要在多个扩展部件中共享,我们推荐您借助 tango 的 `remoteServices` 规范进行接口的实现。
|
||||
|
||||
### 外部数据源定义
|
||||
|
||||
例如,我们需要调用一组图片素材的接口,此时我们可以定义一个 `ImageService`,如下:
|
||||
|
||||
```tsx
|
||||
import { createServices } from '@music/request';
|
||||
|
||||
export const remoteServices = {
|
||||
ImageService: createServices(
|
||||
{
|
||||
listMy: {
|
||||
url: '/my/upload/list',
|
||||
},
|
||||
listFav: {
|
||||
url: '/my/star/list',
|
||||
},
|
||||
listPub: {
|
||||
url: '/list',
|
||||
},
|
||||
},
|
||||
{
|
||||
baseURL: 'https://febase-openapi.fn.netease.com/deer/api/deer/pic',
|
||||
withCredentials: false, // 解决跨域时必须非*问题
|
||||
}
|
||||
),
|
||||
};
|
||||
```
|
||||
|
||||
### 外部数据源引入
|
||||
|
||||
我们在设计器初始化的时候可以通过 `remoteServices` 属性进行外部数据源的传入。
|
||||
|
||||
```tsx
|
||||
<Designer remoteServices={remoteServices}></Designer>
|
||||
```
|
||||
|
||||
### 在设计器扩展中使用定义的外部数据源
|
||||
|
||||
```tsx
|
||||
import { useRemoteServices } from '@music163/tango-designer';
|
||||
|
||||
export function CustomWidget() {
|
||||
const remoteServices = useRemoteServices();
|
||||
}
|
||||
```
|
||||
|
||||
## 仅在单个组件
|
||||
|
||||
如果您的外部数据服务没有额外的共享需求,您也可以直接在组件内部进行数据服务的发起。
|
||||
|
||||
```tsx
|
||||
import request from '@music/request';
|
||||
|
||||
export function CustomWidget() {
|
||||
useEffect(() => {
|
||||
request('//some.domain/get');
|
||||
}, []);
|
||||
}
|
||||
```
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# 物料接入
|
||||
|
||||
Tango 提供了低成本的物料接入方式,支持直接复用现有的组件体系。只需要在现有组件包基础上简单的提供配置说明问题,即可轻松接入到 Tango 低代码生态体系中。
|
||||
|
||||
import Link from '@docusaurus/Link';
|
||||
|
||||
## 预览视图和设计器视图
|
||||
|
||||
- 预览视图:是组件默认的渲染模式,通常我们只需要关注此视图行为。
|
||||
- 设计器视图:为低代码平台定制的渲染视图,当我们需要自定义组件在 Tango 设计器中的部分渲染行为时,可以通过修改设计器视图的渲染逻辑实现。
|
||||
|
||||
### 设计器视图和 `withDnd`
|
||||
|
||||
通常情况下我们不需要太关注于设计器视图,只需通过 `withDnd` 简单的进行包装即可。例如:
|
||||
|
||||
```jsx
|
||||
// 将 antd Button 包装一层提供设计器视图
|
||||
export Button = withDnd({
|
||||
name: 'Button', // 必须,组件的名字,用来正确设置组件的 displayName
|
||||
isFunctionComponent: true, // 可选,如果包裹的组件为函数组件,可以在此设置
|
||||
overrideProps: {}, // 可选,覆盖掉包裹组件的默认属性值
|
||||
})(AntButton);
|
||||
```
|
||||
|
||||
默认情况下 withDnd 会在组件外层包裹一层 dnd 容器,以便于组件能够在设计器中被拖拽:
|
||||
|
||||
- draggable 属性表示该区域可以被拖拽
|
||||
- data-dnd 用来追踪渲染的 dom 元素
|
||||
|
||||
```jsx
|
||||
<div className="dnd-wrapper" draggable data-dnd="button:123">
|
||||
<button>hello</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 自定义拖拽容器
|
||||
|
||||
由于默认情况下拖拽容器是一个 `div` 节点,为[块级元素](https://www.w3schools.com/cssref/pr_class_display.asp)。部分情况下,你可能期望它渲染为一个[行内容器](https://www.w3schools.com/cssref/pr_class_display.asp),此时可以通过如下方式配置:
|
||||
|
||||
```jsx
|
||||
export Button = withDnd({
|
||||
name: 'Button',
|
||||
display: 'inline-block', // 将 dnd 容器渲染为行内元素
|
||||
wrapperStyle: {}, // 传入 dnd 容器的自定义样式
|
||||
})(AntButton);
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 禁用拖拽容器
|
||||
|
||||
某些情况下,你的组件可能会检查子节点的有效性,此时你可能不期望组件被包装额外的容器节点,此时你可以关闭拖拽容器,但你需要保证你的组件能够接收父级元素传入的属性信息并附加到组件的跟节点上,否则将无法正确的设置组件的 dnd 信息。
|
||||
|
||||
```jsx
|
||||
export Button = withDnd({
|
||||
name: 'Button',
|
||||
hasWrapper: false, // 禁用拖拽容器
|
||||
})(MyButton);
|
||||
|
||||
const MyButton = (props) => {
|
||||
const { children, ...rest } = props;
|
||||
// 此时,需要保证你的组件能够正确的将多余的属性透传到组件的根节点上
|
||||
return <button {...rest}>{children}</button>
|
||||
}
|
||||
```
|
||||
|
||||
## 组件包接入
|
||||
|
||||
组件包一般包含多个组件导出,例如 `@music163/antd` 就是典型的组件包,它面向中后台场景提供统一的物料层解决方案。组件包接入 Tango 体系,无需修改现有的代码实现,只需要在此基础上提供额外的配置文件即可。如下所示:
|
||||
|
||||
```
|
||||
- src
|
||||
- button
|
||||
- designer.tsx # 设计器视图,可以不提供
|
||||
- index.ts # 默认视图出口
|
||||
- prototype.ts # 组件可配置描述协议
|
||||
- index.ts
|
||||
- prototypes.ts
|
||||
- designer.ts
|
||||
```
|
||||
|
||||
参考示例:https://g.hz.netease.com/NeteaseMusicUI/music-one/-/tree/master/packages/components/src/action
|
||||
|
||||
### 设计器视图的实现
|
||||
|
||||
对大部分组件而言,都不需要提供特定的设计器视图,只需要使用 tango 提供的 dnd hoc 进行简单的包裹导出即可。
|
||||
|
||||
```tsx
|
||||
import { Button as Base, ButtonProps } from 'antd';
|
||||
import { withDnd } from '@music/tango-apps-shared';
|
||||
|
||||
export const Button = withDnd<HTMLDivElement, ButtonProps<any>>({
|
||||
name: 'Button',
|
||||
})(Base);
|
||||
```
|
||||
|
||||
### 编写 prototypes 文件
|
||||
|
||||
可以参考 <Link to="/docs/protocol/material-protocol">物料协议</Link> 编写该文档
|
||||
|
||||
### 属性设置器选择
|
||||
|
||||
参考属性设置器文档进行选择。
|
||||
|
||||
### 可配置项的验证
|
||||
|
||||
TangoApps 提供了 SettingFormPlayground 组件用来测试组件的可配置能力,可以直接在组件文档中加入可配置测试示例,例如在 storybook 中进行测试。
|
||||
|
||||
```jsx
|
||||
import React from 'react';
|
||||
import { SettingFormPlayground } from '@music/tango-apps-setting-form';
|
||||
import { Input, prototypes } from '@music163/antd';
|
||||
|
||||
export default {
|
||||
title: 'Prototype/Input',
|
||||
};
|
||||
|
||||
export function Basic() {
|
||||
return (
|
||||
<SettingFormPlayground prototype={prototypes.Input}>
|
||||
<Input />
|
||||
</SettingFormPlayground>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
SettingFormPlayground 组件支持实时预览配置结果,预览效果如下:
|
||||
|
||||
<img src="https://p6.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/13140796879/edbb/5dad/a330/013a98940e487956b605e1e533f54371.png" />
|
||||
|
||||
## 单个业务组件接入
|
||||
|
||||
单个组件接入可以参考组件包的接入方案。
|
||||
|
||||
:::info
|
||||
考虑到单组件形态的特点,在云音乐业务场景中,TangoStudio 将会与红石物料中心对接,提供业务组件的自动化接入能力,目前该能力正在开发过程中。
|
||||
:::
|
||||
@@ -1,9 +0,0 @@
|
||||
# DesignerPanel 设计器布局容器
|
||||
|
||||
设计器的主框架提供了一个基本的低代码设计器的布局容器,你可以很轻松的通过此布局容器来实现自己的设计器布局。
|
||||
|
||||
## 属性列表
|
||||
|
||||
import TypesTable from '@site/src/components/TypesTable';
|
||||
|
||||
<TypesTable name="DesignerPanelProps" />
|
||||
@@ -1,56 +0,0 @@
|
||||
# Designer 设计器容器
|
||||
|
||||
设计器根节点,注入全局状态。
|
||||
|
||||
## 属性列表
|
||||
|
||||
import TypesTable from '@site/src/components/TypesTable';
|
||||
|
||||
<TypesTable name="DesignerProps" />
|
||||
|
||||
## 设计器初始化
|
||||
|
||||
```jsx
|
||||
<Designer engine={engine} sandboxQuery={sandboxQuery} remoteServices={remoteServices}></Designer>
|
||||
```
|
||||
|
||||
## engine
|
||||
|
||||
设计器引擎实例,用于管理设计器的核心状态。
|
||||
|
||||
### 基本的初始化方式
|
||||
|
||||
```js
|
||||
const engine = createEngine({
|
||||
entry: '/src/index.js',
|
||||
files: sampleFiles,
|
||||
componentPrototypes: prototypes as any,
|
||||
});
|
||||
```
|
||||
|
||||
### 自定义 workspace 的初始化方式
|
||||
|
||||
默认情况下,引擎采用的是源码解析模式,即将源码解析为 ast 树,后续的搭建逻辑转为对 ast 树的操作。如果你想自定义搭建逻辑,可以通过自定义 workspace 的方式来实现。
|
||||
|
||||
引擎在 2.0 本本中提供了新的 JsonWorkspace 来支持自定义搭建逻辑,JsonWorkspace 采用的是 json 格式的数据结构,你可以通过自定义 json 来实现自定义搭建逻辑。
|
||||
|
||||
```js
|
||||
const engine = createEngine({
|
||||
workspace: new JsonWorkspace({
|
||||
prototypes: prototypes as any,
|
||||
files: schemaFiles,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
:::tip
|
||||
按照这种方式,你可以自定义自己的 Workspace 实现。具体可以参考 Workspace 的实现标准。
|
||||
:::
|
||||
|
||||
## sandboxQuery
|
||||
|
||||
沙箱的查询实例,用于向沙箱注册 dom 查询能力。
|
||||
|
||||
## remoteServices
|
||||
|
||||
远程服务实例,用于注册全局共享的数据服务实例。
|
||||
@@ -1,27 +0,0 @@
|
||||
# Hooks 钩子方法
|
||||
|
||||
设计器提供了一组 Hooks 用于快速获取设计器的各种状态。
|
||||
|
||||
import TypesTable from '@site/src/components/TypesTable';
|
||||
|
||||
## useWorkspace
|
||||
|
||||
获取工作区状态。
|
||||
|
||||
```js
|
||||
// App 需要放置在 Designer 容器中
|
||||
function App() {
|
||||
const workspace = useWorkspace();
|
||||
// do what you want
|
||||
}
|
||||
```
|
||||
|
||||
## useDesigner
|
||||
|
||||
```js
|
||||
// App 需要放置在 Designer 容器中
|
||||
function App() {
|
||||
const designer = useDesigner();
|
||||
// do what you want
|
||||
}
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
# Sandbox 沙箱
|
||||
|
||||
设计器的运行时沙箱,用来执行代码,渲染页面。
|
||||
|
||||
import TypesTable from '@site/src/components/TypesTable';
|
||||
|
||||
默认沙箱是一个基于 CodeSandbox 的沙箱实例,可以直接在浏览器端执行代码。
|
||||
|
||||
<TypesTable name="SandboxProps" />
|
||||
@@ -1,9 +0,0 @@
|
||||
# SettingPanel 属性设置面板
|
||||
|
||||
属性设置器用于在配置面板中展示特定配置项的配置逻辑。
|
||||
|
||||
## 属性列表
|
||||
|
||||
import TypesTable from '@site/src/components/TypesTable';
|
||||
|
||||
<TypesTable name="SettingPanelProps" />
|
||||
@@ -1,38 +0,0 @@
|
||||
# SidebarPanel 侧边栏面板
|
||||
|
||||
侧边栏面板提供了一个简单易用的主操作面板,可以将一些高频操作和核心部件放置到侧边栏面板中,方便用户快速操作。
|
||||
|
||||
## 属性列表
|
||||
|
||||
import TypesTable from '@site/src/components/TypesTable';
|
||||
|
||||
<TypesTable name="SidebarPanelProps" />
|
||||
|
||||
## 创建浮动面板
|
||||
|
||||
借助 `isFloat` 和 `width` 属性可以创建浮动自定义宽度面板,脱离框架对侧边栏的宽度限制。
|
||||
|
||||
```jsx
|
||||
<SidebarPanel.Item key="history" isFloat width="40vw" />
|
||||
```
|
||||
|
||||
## 徽标提示
|
||||
|
||||
`showBadge` 属性可以设置工具栏是否显示徽标提示,用于某些面板需要对外展现需要被用户注意的时机。
|
||||
|
||||
<img
|
||||
alt="img"
|
||||
src="https://p6.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/18224398190/2847/b86d/df55/c817e86a0b1b3a6b08f7cc98362caec5.png"
|
||||
width="300px"
|
||||
/>
|
||||
|
||||
```jsx
|
||||
<SidebarPanel.Item
|
||||
key="info"
|
||||
label="消息"
|
||||
title="消息列表"
|
||||
icon={<NotificationOutlined />}
|
||||
showBadge={{ count: 2 }}>
|
||||
<Box>info panel</Box>
|
||||
</SidebarPanel.Item>
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
# ViewPanel 主视图面板
|
||||
|
||||
设计器的主视图面板,用来放置设计器的主视图,包括:沙箱,编辑器,预览等。主视图可以放置多个,但只有一个会在激活状态。
|
||||
|
||||
## 属性列表
|
||||
|
||||
import TypesTable from '@site/src/components/TypesTable';
|
||||
|
||||
<TypesTable name="ViewPanelProps" />
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# WorkspacePanel 工作区面板
|
||||
|
||||
主工作区的布局容器,用于放置工作区的多重视图。
|
||||
@@ -2,10 +2,10 @@ import Link from '@docusaurus/Link';
|
||||
|
||||
# 快速开始
|
||||
|
||||
Tango 是一个用于快速构建低代码工具的前端框架。借助 Tango 只需要少量代码就可以快速的搭建一个基本的低代码设计器。本文将会帮助你借助 Tango 实现一个基本的低代码可视化设计器。
|
||||
Tango 是一个用于快速构建低代码平台的低代码设计器框架,借助 Tango 只需要数行代码就可以完成一个基本的低代码平台前端系统的搭建。
|
||||
|
||||
:::warning 测试版免责声明
|
||||
基于 Tango 低代码引擎的低代码平台已经在网易云音乐内部服务了内部众多业务开发场景。由于开源工作涉及到大量的代码分离工作,当前开源版本为低代码引擎的 alpha 演示版本,API 可能不稳定,不建议在生产环境中使用。我们预计在 2023 年 Q4 发布可以运行在生产环境的正式版。
|
||||
当前开源版本为低代码引擎的 beta 演示版本,API 可能不稳定,不建议在生产环境中使用。为了保障社区版本能够有效与内部版本解藕,并且项目开源涉及到大量的额外的代码分离工作,我们预计在 2023 年 Q4 发布可以运行在生产环境的正式版。
|
||||
:::
|
||||
|
||||
## 环境要求
|
||||
@@ -34,6 +34,10 @@ Tango 是一个用于快速构建低代码工具的前端框架。借助 Tango
|
||||
|
||||
本地开发除了需要启动 Tango 的 playground 外,还需要一个可用的沙箱环境来承载设计器。
|
||||
|
||||
:::tip
|
||||
沙箱部分来源于 codesandbox,不属于 tango 的核心代码库,我们在原有的 codesandbox 基础上进行了一系列的改造
|
||||
:::
|
||||
|
||||
如果没有可用的在线沙箱,可以通过本地启动一个沙箱来完成开发;如果已有一个在线沙箱,且沙箱与本地的 playground 共用一级域名,请直接跳至第 7 步修改 `bundlerURL` 为在线沙箱的地址。
|
||||
|
||||
1. 克隆仓库至本地
|
||||
|
||||
@@ -2,36 +2,30 @@ import Link from '@docusaurus/Link';
|
||||
|
||||
# 简介
|
||||
|
||||
Tango 是一个用于快速构建低代码平台的低代码设计器框架,借助 Tango 只需要数行代码就可以完成一个基本的低代码平台前端系统的搭建。Tango 低代码设计器**直接读取前端项目的源代码,并以源代码为中心,执行和渲染前端视图,并为用户提供低代码可视化搭建能力,用户的搭建操作会转为对代码的修改**。借助 Tango 可以实现 **源码进,源码出**的效果。
|
||||
Tango 是一个用于快速构建低代码平台的低代码设计器框架,借助 Tango 只需要数行代码就可以完成一个基本的低代码平台前端系统的搭建。Tango 低代码设计器直接读取前端项目的源代码,并以源代码为中心,执行和渲染前端视图,并为用户提供低代码可视化搭建能力,用户的搭建操作会转为对源代码的修改。借助于 Tango 构建的低代码工具或平台,可以实现 源码进,源码出的效果,无缝与企业内部现有的研发体系进行集成。
|
||||
|
||||
## 引擎架构概览
|
||||
<img src="https://p6.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/30108735057/7ba9/dced/9ac3/420f6e04b371dd47de06e7d71142560d.gif" alt="preview" />
|
||||
|
||||
Tango 低代码引擎在实现上进行了分层解藕,使得上层的低代码平台与底层的低代码引擎可以独立开发和维护,快速集成部署。另一方面,Tango 低代码引擎定义了一套开放的物料生态体系,开发者可以自由的贡献扩展组件配置能力的属性设置器,以及扩展低代码物料的二方三方业务组件。
|
||||
## 核心特性
|
||||
|
||||
如下图所示:
|
||||
- 经历网易云音乐内网生产环境的实际检验,可灵活集成应用于低代码平台,本地开发工具等
|
||||
- 基于源码 AST 驱动,无私有 DSL 和协议
|
||||
- 提供实时出码能力,支持源码进,源码出
|
||||
- 开箱即用的前端低代码设计器,提供灵活易用的设计器 React 组件
|
||||
- 使用 TypeScript 开发,提供完整的类型定义文件
|
||||
|
||||
<img src="https://p6.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/18236990116/aabf/8a16/28ee/f0fd29a84c7aa40bb26383cdef12b88c.png" />
|
||||
## 实现原理
|
||||
|
||||
## 引擎设计器构成概览
|
||||
Tango 低代码引擎不依赖私有搭建协议和 DSL,而是直接使用源代码驱动,引擎内部将源码转为 AST,用户的所有的搭建操作转为对 AST 的遍历和修改,进而将 AST 重新生成为代码,将代码同步给在线沙箱执行。具体的过程如下图所示,首先将源代码解析为 AST。用户的拖拉拽等操作则映射为对 AST 的遍历和修改。最后将新的 AST 重新生成代码,交给设计器沙箱去渲染执行。而对 AST 的解析、遍历、修改、生成,则可以借助大量的社区工具,这里我们选择的是 babel!
|
||||
|
||||
Tango 低代码引擎设计器用于低成本初始化一个基本的低代码平台,其前端部分主要包括如下几个部分:
|
||||

|
||||
|
||||
- 设计器主框架:进行外框架初始化,状态管理,拖拽引擎绑定等核心逻辑。
|
||||
- 侧边栏面板:提供可扩展的侧边面板,除了内置通用的面板,用户还可以任意添加自定义的面板。
|
||||
- 属性设置面板:属性设置器面板,支持用户进行属性配置。
|
||||
- 运行时沙箱:用于设计器运行时视图的渲染。
|
||||
- Web IDE:用于进行在线源代码编辑。
|
||||
## 技术架构
|
||||
|
||||
<img src="https://p6.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/30108642346/b8cf/e86d/ef5a/514d90b722b5d8dc0e18516ed594a07b.png" />
|
||||
Tango 低代码引擎主要包括 3 个核心组成部分,分别是:
|
||||
|
||||
## 基于源代码的引擎内核
|
||||
- 引擎内核:扶额建立文件,节点模型,提供输入输出能力。
|
||||
- 拖拽引擎和可视化面板:提供可视化开发能力
|
||||
- 渲染沙箱:提供源码在浏览器上的编译执行能力。
|
||||
|
||||
Tango 低代码引擎不依赖私有搭建协议和 DSL,而是直接使用源代码驱动,引擎内部将源码转为 AST,用户的所有的搭建操作转为对 AST 的遍历和修改,进而将 AST 重新生成为代码,将代码同步给在线沙箱执行。与传统的借助 [Schema 驱动的低代码方案](https://mp.weixin.qq.com/s/yqYey76qLGYPfDtpGkVFfA) 相比,不受到私有 DSL 和协议的限制,能够完美的实现低代码搭建与源码开发的无缝集成。
|
||||
|
||||
<img src="https://p5.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/13140534982/ee2e/f42c/cc9a/184e2918a011b57d46e6c64a2722fa44.png" />
|
||||
|
||||
## 源码进,源码出
|
||||
|
||||
由于引擎内核完全基于源代码驱动实现,Tango 低代码引擎能够实现源代码进,源代码出的可视化搭建能力,不提供任何私有的中间产物。从而使得,Tango 构建的线上研发能力可以与团队现有的研发服务(代码托管、构建、部署、CDN)无缝衔接与集成。
|
||||
|
||||
<img src="https://p6.music.126.net/obj/wonDlsKUwrLClGjCm8Kx/13208022400/b809/b82e/77b0/5e4fe78a8f11c8ed89c9ec9ced43e845.png" />
|
||||

|
||||
|
||||
@@ -64,7 +64,7 @@ const config = {
|
||||
image: 'img/social-card.png',
|
||||
announcementBar: {
|
||||
id: 'notion_alert',
|
||||
content: '🏗 当前版本为测试版,请暂时不要用于生产环境,正式版将于2023年Q4发布!',
|
||||
content: '🏗 当前版本为 alpha 版本,相关文档正在编写之中,敬请期待!',
|
||||
backgroundColor: 'var(--ifm-color-primary-contrast-background)',
|
||||
textColor: 'var(--ifm-color-primary-contrast-foreground)',
|
||||
isCloseable: false,
|
||||
@@ -80,7 +80,7 @@ const config = {
|
||||
type: 'docSidebar',
|
||||
sidebarId: 'designer',
|
||||
position: 'left',
|
||||
label: '引擎文档',
|
||||
label: '文档',
|
||||
},
|
||||
{
|
||||
type: 'docSidebar',
|
||||
@@ -88,12 +88,6 @@ const config = {
|
||||
position: 'left',
|
||||
label: '应用框架',
|
||||
},
|
||||
{
|
||||
type: 'docSidebar',
|
||||
sidebarId: 'protocol',
|
||||
position: 'left',
|
||||
label: '协议',
|
||||
},
|
||||
{ to: '/blog', label: '博客', position: 'left' },
|
||||
{
|
||||
type: 'html',
|
||||
@@ -127,8 +121,8 @@ const config = {
|
||||
title: '社区',
|
||||
items: [
|
||||
{
|
||||
label: 'Stack Overflow',
|
||||
href: 'https://stackoverflow.com/questions/tagged/netease-tango',
|
||||
label: 'Discussions',
|
||||
href: 'https://github.com/NetEase/tango/discussions',
|
||||
},
|
||||
{
|
||||
label: 'Discord',
|
||||
@@ -137,7 +131,7 @@ const config = {
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '更多',
|
||||
title: '相关资源',
|
||||
items: [
|
||||
{
|
||||
label: '博客',
|
||||
@@ -149,6 +143,19 @@ const config = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '更多产品',
|
||||
items: [
|
||||
{
|
||||
label: '海豹 D2C - Figma 插件',
|
||||
to: 'https://www.figma.com/community/plugin/1174548852019950797/seal-figma-to-code-d2c',
|
||||
},
|
||||
{
|
||||
label: '海豹 D2C - MasterGo 插件',
|
||||
to: 'https://mastergo.com/community/plugin/98956774428196/',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
logo: {
|
||||
alt: 'NetEase Cloud Music',
|
||||
|
||||
@@ -20,28 +20,31 @@ const sidebars = {
|
||||
designer: [
|
||||
'intro',
|
||||
'designer/quick-start',
|
||||
// {
|
||||
// type: 'category',
|
||||
// label: '模块',
|
||||
// items: [
|
||||
// 'designer/modules/designer',
|
||||
// 'designer/modules/designer-panel',
|
||||
// 'designer/modules/sidebar-panel',
|
||||
// 'designer/modules/setting-panel',
|
||||
// 'designer/modules/workspace-panel',
|
||||
// 'designer/modules/view-panel',
|
||||
// 'designer/modules/sandbox',
|
||||
// 'designer/modules/hooks',
|
||||
// ],
|
||||
// collapsible: false,
|
||||
// },
|
||||
// {
|
||||
// type: 'category',
|
||||
// label: '扩展',
|
||||
// items: ['designer/extend/overview', 'designer/extend/remote-service'],
|
||||
// collapsible: false,
|
||||
// },
|
||||
// 'designer/setters',
|
||||
{
|
||||
type: 'category',
|
||||
label: '接入指南',
|
||||
items: ['designer/deploy/designer', 'designer/deploy/sandbox', 'designer/deploy/server'],
|
||||
collapsed: false,
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: '设计器自定义',
|
||||
items: [
|
||||
'designer/customize/panels',
|
||||
'designer/customize/tools',
|
||||
'designer/customize/sidebar',
|
||||
'designer/customize/setters',
|
||||
'designer/customize/editor',
|
||||
'designer/customize/components',
|
||||
],
|
||||
collapsed: false,
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: '设计原理',
|
||||
items: ['designer/design/overview', 'designer/design/filesystem', 'designer/design/sandbox'],
|
||||
collapsed: false,
|
||||
},
|
||||
],
|
||||
|
||||
boot: [
|
||||
@@ -59,7 +62,7 @@ const sidebars = {
|
||||
// 'boot/i18n',
|
||||
],
|
||||
|
||||
protocol: ['protocol/material-protocol', 'protocol/material-package-spec'],
|
||||
// protocol: ['protocol/material-protocol', 'protocol/material-package-spec'],
|
||||
};
|
||||
|
||||
module.exports = sidebars;
|
||||
|
||||
@@ -3,37 +3,37 @@ import clsx from 'clsx';
|
||||
import styles from './styles.module.css';
|
||||
import Translate, { translate } from '@docusaurus/Translate';
|
||||
|
||||
type FeatureItem = {
|
||||
interface FeatureItem {
|
||||
title: string;
|
||||
Svg: React.ComponentType<React.ComponentProps<'svg'>>;
|
||||
description: JSX.Element;
|
||||
};
|
||||
}
|
||||
|
||||
const FeatureList: FeatureItem[] = [
|
||||
{
|
||||
title: translate({ id: 'homepage.features.low-code', message: '可视化搭建' }),
|
||||
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
|
||||
title: translate({ id: 'homepage.features.code-driven', message: '源码驱动' }),
|
||||
Svg: require('@site/static/img/html-d.svg').default,
|
||||
description: (
|
||||
<Translate id="homepage.features.low-code-content">
|
||||
提供通用低代码引擎,支持通过拖拽和配置的方式快捷创建和修改应用,轻松完成模式化应用开发。
|
||||
<Translate id="homepage.features.code-driven-content">
|
||||
基于项目源码提供低代码能力,提供源码级的自定义扩展能力
|
||||
</Translate>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: translate({ id: 'homepage.features.code-driven', message: '源码驱动' }),
|
||||
Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
|
||||
title: translate({ id: 'homepage.features.low-code', message: '实时出码' }),
|
||||
Svg: require('@site/static/img/digital-content-4.svg').default,
|
||||
description: (
|
||||
<Translate id="homepage.features.code-driven-content">
|
||||
低代码引擎底层使用源代码驱动,无私有搭建协议和DSL,支持在线低代码和源码双模式同步开发。
|
||||
<Translate id="homepage.features.low-code-content">
|
||||
源码进,源码出,可视化和源码自由切换
|
||||
</Translate>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: translate({ id: 'homepage.features.outbox', message: '开箱即用' }),
|
||||
Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
|
||||
Svg: require('@site/static/img/cpu-f.svg').default,
|
||||
description: (
|
||||
<Translate id="homepage.features.outbox-content">
|
||||
提供开箱即用的低代码设计器组件,支持轻松网关,快捷构建基于低代码引擎的低代码生产力工具。
|
||||
提供灵活易用的设计器框架,支持开发者自由扩展封装
|
||||
</Translate>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -6,37 +6,28 @@ const timelines = [
|
||||
{
|
||||
icon: null,
|
||||
title: 'Alpha',
|
||||
date: '2023.08.30',
|
||||
date: '2024.01.31',
|
||||
description: translate({
|
||||
id: 'homepage.timeline.alpha',
|
||||
message: '开源仓库和文档站点上线,发布 alpha 演示版本。',
|
||||
}),
|
||||
},
|
||||
{
|
||||
icon: null,
|
||||
title: 'Beta',
|
||||
date: '2023.09.30',
|
||||
description: translate({
|
||||
id: 'homepage.timeline.beta',
|
||||
message: '核心 API 面向社区场景重构和优化,发布 Beta 测试版本。',
|
||||
message: '核心 API 重构完成,文档内容优化',
|
||||
}),
|
||||
},
|
||||
{
|
||||
icon: null,
|
||||
title: '1.0 RC',
|
||||
date: '2023.10.30',
|
||||
date: '2024.04.30',
|
||||
description: translate({
|
||||
id: 'homepage.timeline.rc',
|
||||
message: '核心 API 基本稳定,不再发生 BR,发布 1.0 RC 版本。',
|
||||
message: '核心 API 基本稳定,能力完善。',
|
||||
}),
|
||||
},
|
||||
{
|
||||
icon: null,
|
||||
title: '1.0',
|
||||
date: 'Before 2023.12.25',
|
||||
date: 'Before 2024.12.31',
|
||||
description: translate({
|
||||
id: 'homepage.timeline.stable',
|
||||
message: 'API 完全稳定,提供良好的社区支持,可用于生产环境。',
|
||||
message: '1.0 正式版常规迭代',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -24,30 +24,18 @@ function HomepageHeader() {
|
||||
{translate({ id: 'homepage.hero.tagline', message: siteConfig.tagline })}
|
||||
</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link className="button button--primary button--lg" to="/docs/intro">
|
||||
{translate({
|
||||
id: 'homepage.hero.button.document',
|
||||
message: '快速开始',
|
||||
})}
|
||||
</Link>
|
||||
<Link
|
||||
className="button button--primary button--lg"
|
||||
className="button button--secondary button--lg"
|
||||
to="https://tango-demo.musicfe.com/designer/"
|
||||
>
|
||||
{translate({ id: 'homepage.hero.button.playground', message: '演示应用' })}
|
||||
</Link>
|
||||
<Link className="button button--secondary button--lg" to="/docs/intro">
|
||||
{translate({
|
||||
id: 'homepage.hero.button.document',
|
||||
message: '使用文档',
|
||||
})}
|
||||
</Link>
|
||||
<a
|
||||
href="https://www.producthunt.com/posts/tango-b8474917-fbce-4180-9fac-4ac7d7a6fa7d?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-tango-b8474917-fbce-4180-9fac-4ac7d7a6fa7d"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<img
|
||||
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=412854&theme=light"
|
||||
alt="Tango - A source code based low-code builder | Product Hunt"
|
||||
width="250px"
|
||||
height="54px"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div className={styles.heroImageBox}>
|
||||
<img
|
||||
|
||||
1
apps/website/static/img/cpu-f.svg
Normal file
|
After Width: | Height: | Size: 19 KiB |
1
apps/website/static/img/digital-content-4.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300" data-imageid="digital-content-4" imageName="Coding" class="illustrations_image" style="width: 261px;"><g id="_87_coding_outline" data-name="#87_coding_outline"><path d="M289.32,90.26V223.54a3.65,3.65,0,0,1-3.65,3.65H130.85a3.65,3.65,0,0,1-3.65-3.65V90.26a3.65,3.65,0,0,1,3.65-3.66H285.67A3.65,3.65,0,0,1,289.32,90.26Z" fill="#2e8555" class="target-color"/><path d="M285.67,228.19H130.85a4.66,4.66,0,0,1-4.65-4.65V90.26a4.66,4.66,0,0,1,4.65-4.66H285.67a4.66,4.66,0,0,1,4.65,4.66V223.54A4.65,4.65,0,0,1,285.67,228.19ZM130.85,87.6a2.66,2.66,0,0,0-2.65,2.66V223.54a2.65,2.65,0,0,0,2.65,2.65H285.67a2.65,2.65,0,0,0,2.65-2.65V90.26a2.66,2.66,0,0,0-2.65-2.66Z" fill="#093f68"/><path d="M272.8,76.47V209.75a3.65,3.65,0,0,1-3.65,3.65H114.33a3.65,3.65,0,0,1-3.65-3.65V76.47a3.65,3.65,0,0,1,3.65-3.66H269.15A3.65,3.65,0,0,1,272.8,76.47Z" fill="#fff"/><path d="M269.15,214.4H114.33a4.65,4.65,0,0,1-4.65-4.65V76.47a4.66,4.66,0,0,1,4.65-4.66H269.15a4.66,4.66,0,0,1,4.65,4.66V209.75A4.65,4.65,0,0,1,269.15,214.4ZM114.33,73.81a2.66,2.66,0,0,0-2.65,2.66V209.75a2.65,2.65,0,0,0,2.65,2.65H269.15a2.65,2.65,0,0,0,2.65-2.65V76.47a2.66,2.66,0,0,0-2.65-2.66Z" fill="#093f68"/><path d="M272.8,76.47V209.75a3.65,3.65,0,0,1-3.65,3.65h-103V72.81h103A3.65,3.65,0,0,1,272.8,76.47Z" fill="#093f68"/><path d="M269.15,214.4h-103a1,1,0,0,1-1-1V72.81a1,1,0,0,1,1-1h103a4.66,4.66,0,0,1,4.65,4.66V209.75A4.65,4.65,0,0,1,269.15,214.4Zm-102-2h102a2.65,2.65,0,0,0,2.65-2.65V76.47a2.66,2.66,0,0,0-2.65-2.66h-102Z" fill="#093f68"/><path d="M272.8,76.47v15H110.68v-15a3.65,3.65,0,0,1,3.65-3.66H269.15A3.65,3.65,0,0,1,272.8,76.47Z" fill="#fff"/><path d="M272.8,92.48H110.68a1,1,0,0,1-1-1v-15a4.66,4.66,0,0,1,4.65-4.66H269.15a4.66,4.66,0,0,1,4.65,4.66v15A1,1,0,0,1,272.8,92.48Zm-161.12-2H271.8v-14a2.66,2.66,0,0,0-2.65-2.66H114.33a2.66,2.66,0,0,0-2.65,2.66Z" fill="#093f68"/><circle cx="123.19" cy="81.63" r="3.38" fill="#f56132"/><path d="M123.19,86a4.38,4.38,0,1,1,4.38-4.38A4.39,4.39,0,0,1,123.19,86Zm0-6.76a2.38,2.38,0,1,0,2.38,2.38A2.39,2.39,0,0,0,123.19,79.25Z" fill="#093f68"/><circle cx="136.84" cy="81.63" r="3.38" fill="#ffbc0e"/><path d="M136.84,86a4.38,4.38,0,1,1,4.37-4.38A4.39,4.39,0,0,1,136.84,86Zm0-6.76a2.38,2.38,0,1,0,2.37,2.38A2.38,2.38,0,0,0,136.84,79.25Z" fill="#093f68"/><circle cx="150.48" cy="81.63" r="3.38" fill="#70cc40"/><path d="M150.48,86a4.38,4.38,0,1,1,4.38-4.38A4.39,4.39,0,0,1,150.48,86Zm0-6.76a2.38,2.38,0,1,0,2.38,2.38A2.39,2.39,0,0,0,150.48,79.25Z" fill="#093f68"/><path d="M148.3,104.19H120.73a1,1,0,0,1,0-2H148.3a1,1,0,0,1,0,2Z" fill="#dfeaef"/><path d="M153,116.59H129.79a1,1,0,0,1,0-2H153a1,1,0,0,1,0,2Z" fill="#dfeaef"/><path d="M156.18,129h-19.3a1,1,0,0,1,0-2h19.3a1,1,0,0,1,0,2Z" fill="#dfeaef"/><path d="M149.88,141.41H125.46a1,1,0,0,1,0-2h24.42a1,1,0,0,1,0,2Z" fill="#dfeaef"/><path d="M143.31,153.81H120.73a1,1,0,0,1,0-2h22.58a1,1,0,0,1,0,2Z" fill="#dfeaef"/><path d="M148.3,166.22h-20a1,1,0,0,1,0-2h20a1,1,0,0,1,0,2Z" fill="#dfeaef"/><path d="M141.41,178.62H120.73a1,1,0,0,1,0-2h20.68a1,1,0,0,1,0,2Z" fill="#dfeaef"/><path d="M148.3,191h-20a1,1,0,0,1,0-2h20a1,1,0,0,1,0,2Z" fill="#dfeaef"/><path d="M193.62,104.19h-16.8a1,1,0,0,1,0-2h16.8a1,1,0,0,1,0,2Z" fill="#fff"/><path d="M216.82,116.59H182.34a1,1,0,0,1,0-2h34.48a1,1,0,0,1,0,2Z" fill="#ffbc0e"/><path d="M208.26,129H182.34a1,1,0,0,1,0-2h25.92a1,1,0,0,1,0,2Z" fill="#ffbc0e"/><path d="M194.58,141.41H179.7a1,1,0,0,1,0-2h14.88a1,1,0,0,1,0,2Z" fill="#fff"/><path d="M200.5,153.81H183.7a1,1,0,0,1,0-2h16.8a1,1,0,0,1,0,2Z" fill="#f56132"/><path d="M209.06,166.22H183.12a1,1,0,0,1,0-2h25.94a1,1,0,0,1,0,2Z" fill="#fff"/><path d="M189.42,178.62h-12.6a1,1,0,0,1,0-2h12.6a1,1,0,0,1,0,2Z" fill="#fff"/><path d="M209.06,191h-27.6a1,1,0,0,1,0-2h27.6a1,1,0,0,1,0,2Z" fill="#70cc40"/><path d="M235.5,191H217.38a1,1,0,0,1,0-2H235.5a1,1,0,0,1,0,2Z" fill="#ffbc0e"/><path d="M229.62,129h-12.8a1,1,0,0,1,0-2h12.8a1,1,0,0,1,0,2Z" fill="#fff"/><path d="M247.87,116.59H223.22a1,1,0,0,1,0-2h24.65a1,1,0,0,1,0,2Z" fill="#2e8555" class="target-color"/><path d="M243.79,129h-7.21a1,1,0,0,1,0-2h7.21a1,1,0,0,1,0,2Z" fill="#f56132"/><path d="M212.66,153.81h-7.2a1,1,0,1,1,0-2h7.2a1,1,0,1,1,0,2Z" fill="#f56132"/><path d="M247.87,153.81H218.58a1,1,0,1,1,0-2h29.29a1,1,0,0,1,0,2Z" fill="#ffbc0e"/><path d="M223.22,166.22h-6.4a1,1,0,0,1,0-2h6.4a1,1,0,0,1,0,2Z" fill="#2e8555" class="target-color"/><path d="M247.87,191h-4.08a1,1,0,0,1,0-2h4.08a1,1,0,0,1,0,2Z" fill="#fff"/></g></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
1
apps/website/static/img/html-d.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300" data-imageid="html-d" imageName="Javascript" class="illustrations_image" style="width: 261px;"><g id="_181_javascript_outline" data-name="#181_javascript_outline"><rect x="144.72" y="69.25" width="130.1" height="153.64" rx="10.96" fill="#2e8555" class="target-color"/><path d="M263.86,223.89H155.67a12,12,0,0,1-11.95-11.95V80.21a12,12,0,0,1,11.95-12H263.86a12,12,0,0,1,12,12V211.94A12,12,0,0,1,263.86,223.89ZM155.67,70.25a10,10,0,0,0-9.95,10V211.94a10,10,0,0,0,9.95,9.95H263.86a10,10,0,0,0,10-9.95V80.21a10,10,0,0,0-10-10Z" fill="#093f68"/><path d="M257.86,230.75H141.93a7.08,7.08,0,0,1-7.08-7.08V84.75a7.08,7.08,0,0,1,7.08-7.08h93.81l29.2,27.19V223.67A7.08,7.08,0,0,1,257.86,230.75Z" fill="#fff"/><path d="M257.86,231.75H141.93a8.09,8.09,0,0,1-8.08-8.08V84.75a8.09,8.09,0,0,1,8.08-8.08h93.81a1,1,0,0,1,.68.27l29.21,27.19a1,1,0,0,1,.31.73V223.67A8.09,8.09,0,0,1,257.86,231.75ZM141.93,78.67a6.09,6.09,0,0,0-6.08,6.08V223.67a6.09,6.09,0,0,0,6.08,6.08H257.86a6.08,6.08,0,0,0,6.08-6.08V105.3l-28.6-26.63Z" fill="#093f68"/><path d="M235.74,77.67V99.06a5.46,5.46,0,0,0,5.39,5.47l23.81.33Z" fill="#2e8555" class="target-color"/><path d="M264.94,105.86h0l-23.81-.33a6.49,6.49,0,0,1-6.38-6.47V77.67a1,1,0,0,1,1.68-.73l29.21,27.19a1,1,0,0,1,.24,1.1A1,1,0,0,1,264.94,105.86ZM236.74,80V99.06a4.47,4.47,0,0,0,4.4,4.47l21.22.29Z" fill="#093f68"/><path d="M182.76,164.08,164,172.64v.14l18.78,8.56v9.39l-30-13.67v-8.7l30-13.68Z" fill="#093f68"/><path d="M182.76,191.73a.85.85,0,0,1-.41-.09l-30-13.67a1,1,0,0,1-.58-.91v-8.7a1,1,0,0,1,.58-.91l30-13.68a1,1,0,0,1,1.41.91v9.4a1,1,0,0,1-.58.91l-17,7.72,17,7.72a1,1,0,0,1,.58.91v9.39a1,1,0,0,1-.46.85A1,1,0,0,1,182.76,191.73Zm-29-15.31,28,12.76V182l-18.2-8.29a1,1,0,0,1-.58-.91v-.14a1,1,0,0,1,.58-.91l18.2-8.3v-7.19l-28,12.76Zm29-12.34h0Z" fill="#093f68"/><path d="M205,141.08l6.76,2.41L194,197.3l-6.7-2.42Z" fill="#093f68"/><path d="M194,198.3a1,1,0,0,1-.34-.06l-6.7-2.42a1,1,0,0,1-.61-1.25l17.72-53.81a1,1,0,0,1,.51-.58,1,1,0,0,1,.78,0l6.75,2.41a1,1,0,0,1,.62,1.26L195,197.61a1,1,0,0,1-.51.59A1.13,1.13,0,0,1,194,198.3Zm-5.44-4,4.81,1.74,17.15-51.9-4.87-1.75Z" fill="#093f68"/><path d="M216.36,181.34l18.78-8.56v-.14l-18.78-8.56v-9.4l30,13.68v8.7l-30,13.67Z" fill="#093f68"/><path d="M216.36,191.73a1,1,0,0,1-.54-.15,1,1,0,0,1-.46-.85v-9.39a1,1,0,0,1,.58-.91l16.94-7.72L215.94,165a1,1,0,0,1-.58-.91v-9.4a1,1,0,0,1,1.41-.91l30,13.68a1,1,0,0,1,.58.91v8.7a1,1,0,0,1-.58.91l-30,13.67A.85.85,0,0,1,216.36,191.73Zm1-9.75v7.2l28-12.76V169l-28-12.76v7.19l18.2,8.3a1,1,0,0,1,.58.91v.14a1,1,0,0,1-.58.91Z" fill="#093f68"/><rect x="120.98" y="92.53" width="71.35" height="44.25" rx="2.13" fill="#2e8555" class="target-color"/><path d="M190.2,137.78H123.11a3.13,3.13,0,0,1-3.13-3.12v-40a3.14,3.14,0,0,1,3.13-3.13H190.2a3.13,3.13,0,0,1,3.13,3.13v40A3.13,3.13,0,0,1,190.2,137.78ZM123.11,93.53A1.14,1.14,0,0,0,122,94.66v40a1.13,1.13,0,0,0,1.13,1.12H190.2a1.12,1.12,0,0,0,1.13-1.12v-40a1.13,1.13,0,0,0-1.13-1.13Z" fill="#093f68"/><path d="M151.53,122.88a7.29,7.29,0,0,1-1.07,2.59,6,6,0,0,1-2.18,2,7.46,7.46,0,0,1-3.61.77,7.3,7.3,0,0,1-4.6-1.43,6.78,6.78,0,0,1-2.46-3.67l4.64-1.56a2.7,2.7,0,0,0,.78,1.38,2,2,0,0,0,1.4.52,1.69,1.69,0,0,0,1.7-.89,5.49,5.49,0,0,0,.42-2.33V105.87h5.28v14.3A13.07,13.07,0,0,1,151.53,122.88Z" fill="#093f68"/><path d="M167.67,111.52a4.69,4.69,0,0,0-1.77-1.37,5,5,0,0,0-2.1-.51,6.44,6.44,0,0,0-1.06.1,2.81,2.81,0,0,0-1,.35,2.31,2.31,0,0,0-.72.67,1.84,1.84,0,0,0-.29,1.06,1.78,1.78,0,0,0,.23.93,2.06,2.06,0,0,0,.67.64,7,7,0,0,0,1.06.51c.41.15.87.31,1.39.47.73.25,1.5.52,2.3.82a8.49,8.49,0,0,1,2.18,1.18,6.3,6.3,0,0,1,1.62,1.82,5.29,5.29,0,0,1,.65,2.72,7.1,7.1,0,0,1-.69,3.24,6.51,6.51,0,0,1-1.86,2.26,8.14,8.14,0,0,1-2.67,1.32,11.49,11.49,0,0,1-3.1.43,12.59,12.59,0,0,1-4.51-.82A9.59,9.59,0,0,1,154.4,125l3.44-3.5a6.32,6.32,0,0,0,2.11,1.64,5.66,5.66,0,0,0,2.59.66,5.38,5.38,0,0,0,1.14-.12,3.08,3.08,0,0,0,1-.4,2,2,0,0,0,.66-.74,2.28,2.28,0,0,0,.25-1.11,1.71,1.71,0,0,0-.31-1,2.9,2.9,0,0,0-.87-.78A7,7,0,0,0,163,119l-1.92-.63a19.51,19.51,0,0,1-2-.79,7,7,0,0,1-1.78-1.19,5.49,5.49,0,0,1-1.26-1.75,6,6,0,0,1-.47-2.5,6.16,6.16,0,0,1,.73-3.1,6.57,6.57,0,0,1,1.94-2.12,8.49,8.49,0,0,1,2.7-1.21,12.06,12.06,0,0,1,3-.38,11.27,11.27,0,0,1,3.76.67,9.66,9.66,0,0,1,3.36,2Z" fill="#093f68"/><path d="M191.53,101.63H121a1,1,0,0,1,0-2h70.55a1,1,0,0,1,0,2Z" fill="#093f68"/><path d="M131.06,98.11h-5.12a1,1,0,0,1,0-2h5.12a1,1,0,0,1,0,2Z" fill="#093f68"/><path d="M257.68,212.94h-113a1,1,0,1,1,0-2h113a1,1,0,0,1,0,2Z" fill="#dfeaef"/></g></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -1,171 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1088" height="687.962" viewBox="0 0 1088 687.962">
|
||||
<title>Easy to Use</title>
|
||||
<g id="Group_12" data-name="Group 12" transform="translate(-57 -56)">
|
||||
<g id="Group_11" data-name="Group 11" transform="translate(57 56)">
|
||||
<path id="Path_83" data-name="Path 83" d="M1017.81,560.461c-5.27,45.15-16.22,81.4-31.25,110.31-20,38.52-54.21,54.04-84.77,70.28a193.275,193.275,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.282,657.282,0,0,0-104.09-13.16q-14.97-.675-29.97-.67c-15.42.02-293.07,5.29-360.67-131.57-16.69-33.76-28.13-75-32.24-125.27-11.63-142.12,52.29-235.46,134.74-296.47,155.97-115.41,369.76-110.57,523.43,7.88C941.15,276.621,1036.99,396.031,1017.81,560.461Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_84" data-name="Path 84" d="M986.56,670.771c-20,38.52-47.21,64.04-77.77,80.28a193.272,193.272,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.3,657.3,0,0,0-104.09-13.16q-14.97-.675-29.97-.67-23.13.03-46.25,1.72c-100.17,7.36-253.82-6.43-321.42-143.29L382,283.981,444.95,445.6l20.09,51.59,55.37-75.98L549,381.981l130.2,149.27,36.8-81.27L970.78,657.9l14.21,11.59Z" transform="translate(-56 -106.019)" fill="#f2f2f2"/>
|
||||
<path id="Path_85" data-name="Path 85" d="M302,282.962l26-57,36,83-31-60Z" opacity="0.1"/>
|
||||
<path id="Path_86" data-name="Path 86" d="M610.5,753.821q-14.97-.675-29.97-.67L465.04,497.191Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<path id="Path_87" data-name="Path 87" d="M464.411,315.191,493,292.962l130,150-132-128Z" opacity="0.1"/>
|
||||
<path id="Path_88" data-name="Path 88" d="M908.79,751.051a193.265,193.265,0,0,1-27.46,11.94L679.2,531.251Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<circle id="Ellipse_11" data-name="Ellipse 11" cx="3" cy="3" r="3" transform="translate(479 98.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_12" data-name="Ellipse 12" cx="3" cy="3" r="3" transform="translate(396 201.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_13" data-name="Ellipse 13" cx="2" cy="2" r="2" transform="translate(600 220.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_14" data-name="Ellipse 14" cx="2" cy="2" r="2" transform="translate(180 265.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_15" data-name="Ellipse 15" cx="2" cy="2" r="2" transform="translate(612 96.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_16" data-name="Ellipse 16" cx="2" cy="2" r="2" transform="translate(736 192.962)" fill="#f2f2f2"/>
|
||||
<circle id="Ellipse_17" data-name="Ellipse 17" cx="2" cy="2" r="2" transform="translate(858 344.962)" fill="#f2f2f2"/>
|
||||
<path id="Path_89" data-name="Path 89" d="M306,121.222h-2.76v-2.76h-1.48v2.76H299V122.7h2.76v2.759h1.48V122.7H306Z" fill="#f2f2f2"/>
|
||||
<path id="Path_90" data-name="Path 90" d="M848,424.222h-2.76v-2.76h-1.48v2.76H841V425.7h2.76v2.759h1.48V425.7H848Z" fill="#f2f2f2"/>
|
||||
<path id="Path_91" data-name="Path 91" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_92" data-name="Path 92" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<ellipse id="Ellipse_18" data-name="Ellipse 18" cx="544" cy="30" rx="544" ry="30" transform="translate(0 583.962)" fill="#3f3d56"/>
|
||||
<path id="Path_93" data-name="Path 93" d="M624,677.981c0,33.137-14.775,24-33,24s-33,9.137-33-24,33-96,33-96S624,644.844,624,677.981Z" transform="translate(-56 -106.019)" fill="#ff6584"/>
|
||||
<path id="Path_94" data-name="Path 94" d="M606,690.66c0,15.062-6.716,10.909-15,10.909s-15,4.153-15-10.909,15-43.636,15-43.636S606,675.6,606,690.66Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<rect id="Rectangle_97" data-name="Rectangle 97" width="92" height="18" rx="9" transform="translate(489 604.962)" fill="#2f2e41"/>
|
||||
<rect id="Rectangle_98" data-name="Rectangle 98" width="92" height="18" rx="9" transform="translate(489 586.962)" fill="#2f2e41"/>
|
||||
<path id="Path_95" data-name="Path 95" d="M193,596.547c0,55.343,34.719,100.126,77.626,100.126" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_96" data-name="Path 96" d="M270.626,696.673c0-55.965,38.745-101.251,86.626-101.251" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_97" data-name="Path 97" d="M221.125,601.564c0,52.57,22.14,95.109,49.5,95.109" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_98" data-name="Path 98" d="M270.626,696.673c0-71.511,44.783-129.377,100.126-129.377" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_99" data-name="Path 99" d="M254.3,697.379s11.009-.339,14.326-2.7,16.934-5.183,17.757-1.395,16.544,18.844,4.115,18.945-28.879-1.936-32.19-3.953S254.3,697.379,254.3,697.379Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_100" data-name="Path 100" d="M290.716,710.909c-12.429.1-28.879-1.936-32.19-3.953-2.522-1.536-3.527-7.048-3.863-9.591l-.368.014s.7,8.879,4.009,10.9,19.761,4.053,32.19,3.953c3.588-.029,4.827-1.305,4.759-3.2C294.755,710.174,293.386,710.887,290.716,710.909Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_101" data-name="Path 101" d="M777.429,633.081c0,38.029,23.857,68.8,53.341,68.8" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_102" data-name="Path 102" d="M830.769,701.882c0-38.456,26.623-69.575,59.525-69.575" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_103" data-name="Path 103" d="M796.755,636.528c0,36.124,15.213,65.354,34.014,65.354" transform="translate(-56 -106.019)" fill="#6c63ff"/>
|
||||
<path id="Path_104" data-name="Path 104" d="M830.769,701.882c0-49.139,30.773-88.9,68.8-88.9" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
<path id="Path_105" data-name="Path 105" d="M819.548,702.367s7.565-.233,9.844-1.856,11.636-3.562,12.2-.958,11.368,12.949,2.828,13.018-19.844-1.33-22.119-2.716S819.548,702.367,819.548,702.367Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_106" data-name="Path 106" d="M844.574,711.664c-8.54.069-19.844-1.33-22.119-2.716-1.733-1.056-2.423-4.843-2.654-6.59l-.253.01s.479,6.1,2.755,7.487,13.579,2.785,22.119,2.716c2.465-.02,3.317-.9,3.27-2.2C847.349,711.159,846.409,711.649,844.574,711.664Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_107" data-name="Path 107" d="M949.813,724.718s11.36-1.729,14.5-4.591,16.89-7.488,18.217-3.667,19.494,17.447,6.633,19.107-30.153,1.609-33.835-.065S949.813,724.718,949.813,724.718Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_108" data-name="Path 108" d="M989.228,734.173c-12.86,1.659-30.153,1.609-33.835-.065-2.8-1.275-4.535-6.858-5.2-9.45l-.379.061s1.833,9.109,5.516,10.783,20.975,1.725,33.835.065c3.712-.479,4.836-1.956,4.529-3.906C993.319,732.907,991.991,733.817,989.228,734.173Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_109" data-name="Path 109" d="M670.26,723.9s9.587-1.459,12.237-3.875,14.255-6.32,15.374-3.095,16.452,14.725,5.6,16.125-25.448,1.358-28.555-.055S670.26,723.9,670.26,723.9Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_110" data-name="Path 110" d="M703.524,731.875c-10.853,1.4-25.448,1.358-28.555-.055-2.367-1.076-3.827-5.788-4.39-7.976l-.32.051s1.547,7.687,4.655,9.1,17.7,1.456,28.555.055c3.133-.4,4.081-1.651,3.822-3.3C706.977,730.807,705.856,731.575,703.524,731.875Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_111" data-name="Path 111" d="M178.389,719.109s7.463-1.136,9.527-3.016,11.1-4.92,11.969-2.409,12.808,11.463,4.358,12.553-19.811,1.057-22.23-.043S178.389,719.109,178.389,719.109Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
|
||||
<path id="Path_112" data-name="Path 112" d="M204.285,725.321c-8.449,1.09-19.811,1.057-22.23-.043-1.842-.838-2.979-4.506-3.417-6.209l-.249.04s1.2,5.984,3.624,7.085,13.781,1.133,22.23.043c2.439-.315,3.177-1.285,2.976-2.566C206.973,724.489,206.1,725.087,204.285,725.321Z" transform="translate(-56 -106.019)" opacity="0.2"/>
|
||||
<path id="Path_113" data-name="Path 113" d="M439.7,707.337c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873,42.118-36.793,93.694-36.793S439.7,677.117,439.7,707.337Z" transform="translate(-56 -106.019)" opacity="0.1"/>
|
||||
<path id="Path_114" data-name="Path 114" d="M439.7,699.9c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873S295.04,663.1,346.616,663.1,439.7,669.676,439.7,699.9Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
|
||||
</g>
|
||||
<g id="docusaurus_keytar" transform="translate(312.271 493.733)">
|
||||
<path id="Path_40" data-name="Path 40" d="M99,52h91.791V89.153H99Z" transform="translate(5.904 -14.001)" fill="#fff" fill-rule="evenodd"/>
|
||||
<path id="Path_41" data-name="Path 41" d="M24.855,163.927A21.828,21.828,0,0,1,5.947,153a21.829,21.829,0,0,0,18.908,32.782H46.71V163.927Z" transform="translate(-3 -4.634)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_42" data-name="Path 42" d="M121.861,61.1l76.514-4.782V45.39A21.854,21.854,0,0,0,176.52,23.535H78.173L75.441,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L64.513,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L53.586,18.8a3.154,3.154,0,0,0-5.464,0L45.39,23.535c-.024,0-.046,0-.071,0l-4.526-4.525a3.153,3.153,0,0,0-5.276,1.414l-1.5,5.577-5.674-1.521a3.154,3.154,0,0,0-3.863,3.864L26,34.023l-5.575,1.494a3.155,3.155,0,0,0-1.416,5.278l4.526,4.526c0,.023,0,.046,0,.07L18.8,48.122a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,59.05a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,69.977a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,80.9a3.154,3.154,0,0,0,0,5.464L23.535,89.1,18.8,91.832a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,102.76a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,113.687a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,124.615a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,135.542a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,146.469a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,157.4a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,168.324a3.154,3.154,0,0,0,0,5.464l4.732,2.732A21.854,21.854,0,0,0,45.39,198.375H176.52a21.854,21.854,0,0,0,21.855-21.855V89.1l-76.514-4.782a11.632,11.632,0,0,1,0-23.219" transform="translate(-1.681 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_43" data-name="Path 43" d="M143,186.71h32.782V143H143Z" transform="translate(9.984 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_44" data-name="Path 44" d="M196.71,159.855a5.438,5.438,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(10.912 -6.025)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_45" data-name="Path 45" d="M153,124.855h32.782V103H153Z" transform="translate(10.912 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_46" data-name="Path 46" d="M194.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.814,2.814,0,0,0,.349.035" transform="translate(12.767 -9.377)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_47" data-name="Path 47" d="M65.087,56.891a2.732,2.732,0,0,1-2.732-2.732,8.2,8.2,0,0,0-16.391,0,2.732,2.732,0,0,1-5.464,0,13.659,13.659,0,0,1,27.319,0,2.732,2.732,0,0,1-2.732,2.732" transform="translate(0.478 -15.068)" fill-rule="evenodd"/>
|
||||
<path id="Path_48" data-name="Path 48" d="M103,191.347h65.565a21.854,21.854,0,0,0,21.855-21.855V93H124.855A21.854,21.854,0,0,0,103,114.855Z" transform="translate(6.275 -10.199)" fill="#ffff50" fill-rule="evenodd"/>
|
||||
<path id="Path_49" data-name="Path 49" d="M173.216,129.787H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0-54.434H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.652H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186M189.585,61.611c-.013,0-.024-.007-.037-.005-3.377.115-4.974,3.492-6.384,6.472-1.471,3.114-2.608,5.139-4.473,5.078-2.064-.074-3.244-2.406-4.494-4.874-1.436-2.835-3.075-6.049-6.516-5.929-3.329.114-4.932,3.053-6.346,5.646-1.5,2.762-2.529,4.442-4.5,4.364-2.106-.076-3.225-1.972-4.52-4.167-1.444-2.443-3.112-5.191-6.487-5.1-3.272.113-4.879,2.606-6.3,4.808-1.5,2.328-2.552,3.746-4.551,3.662-2.156-.076-3.27-1.65-4.558-3.472-1.447-2.047-3.077-4.363-6.442-4.251-3.2.109-4.807,2.153-6.224,3.954-1.346,1.709-2.4,3.062-4.621,2.977a1.093,1.093,0,0,0-.079,2.186c3.3.11,4.967-1.967,6.417-3.81,1.286-1.635,2.4-3.045,4.582-3.12,2.1-.09,3.091,1.218,4.584,3.327,1.417,2,3.026,4.277,6.263,4.394,3.391.114,5.022-2.42,6.467-4.663,1.292-2,2.406-3.734,4.535-3.807,1.959-.073,3.026,1.475,4.529,4.022,1.417,2.4,3.023,5.121,6.324,5.241,3.415.118,5.064-2.863,6.5-5.5,1.245-2.282,2.419-4.437,4.5-4.509,1.959-.046,2.981,1.743,4.492,4.732,1.412,2.79,3.013,5.95,6.365,6.071l.185,0c3.348,0,4.937-3.36,6.343-6.331,1.245-2.634,2.423-5.114,4.444-5.216Z" transform="translate(7.109 -13.11)" fill-rule="evenodd"/>
|
||||
<path id="Path_50" data-name="Path 50" d="M83,186.71h43.71V143H83Z" transform="translate(4.42 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 109.327, 91.085)">
|
||||
<rect id="Rectangle_3" data-name="Rectangle 3" width="92.361" height="36.462" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
|
||||
<g id="Group_2" data-name="Group 2" transform="translate(1.531 23.03)">
|
||||
<rect id="Rectangle_4" data-name="Rectangle 4" width="5.336" height="5.336" rx="1" transform="translate(16.797 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_5" data-name="Rectangle 5" width="5.336" height="5.336" rx="1" transform="translate(23.12 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_6" data-name="Rectangle 6" width="5.336" height="5.336" rx="1" transform="translate(29.444 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_7" data-name="Rectangle 7" width="5.336" height="5.336" rx="1" transform="translate(35.768 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_8" data-name="Rectangle 8" width="5.336" height="5.336" rx="1" transform="translate(42.091 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_9" data-name="Rectangle 9" width="5.336" height="5.336" rx="1" transform="translate(48.415 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_10" data-name="Rectangle 10" width="5.336" height="5.336" rx="1" transform="translate(54.739 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_11" data-name="Rectangle 11" width="5.336" height="5.336" rx="1" transform="translate(61.063 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_12" data-name="Rectangle 12" width="5.336" height="5.336" rx="1" transform="translate(67.386 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_51" data-name="Path 51" d="M1.093,0H14.518a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0ZM75,0H88.426a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H75a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,75,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_3" data-name="Group 3" transform="translate(1.531 10.261)">
|
||||
<path id="Path_52" data-name="Path 52" d="M1.093,0H6.218A1.093,1.093,0,0,1,7.31,1.093V4.242A1.093,1.093,0,0,1,6.218,5.335H1.093A1.093,1.093,0,0,1,0,4.242V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_13" data-name="Rectangle 13" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_14" data-name="Rectangle 14" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_15" data-name="Rectangle 15" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_16" data-name="Rectangle 16" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_17" data-name="Rectangle 17" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_18" data-name="Rectangle 18" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_19" data-name="Rectangle 19" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_20" data-name="Rectangle 20" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_21" data-name="Rectangle 21" width="5.336" height="5.336" rx="1" transform="translate(58.888 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_22" data-name="Rectangle 22" width="5.336" height="5.336" rx="1" transform="translate(65.212 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_23" data-name="Rectangle 23" width="5.336" height="5.336" rx="1" transform="translate(71.536 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_24" data-name="Rectangle 24" width="5.336" height="5.336" rx="1" transform="translate(77.859 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_25" data-name="Rectangle 25" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_4" data-name="Group 4" transform="translate(91.05 9.546) rotate(180)">
|
||||
<path id="Path_53" data-name="Path 53" d="M1.093,0H6.219A1.093,1.093,0,0,1,7.312,1.093v3.15A1.093,1.093,0,0,1,6.219,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_26" data-name="Rectangle 26" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_27" data-name="Rectangle 27" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_28" data-name="Rectangle 28" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_29" data-name="Rectangle 29" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_30" data-name="Rectangle 30" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_31" data-name="Rectangle 31" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_32" data-name="Rectangle 32" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_33" data-name="Rectangle 33" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_34" data-name="Rectangle 34" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_35" data-name="Rectangle 35" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_36" data-name="Rectangle 36" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_37" data-name="Rectangle 37" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_38" data-name="Rectangle 38" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_39" data-name="Rectangle 39" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_40" data-name="Rectangle 40" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_41" data-name="Rectangle 41" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_42" data-name="Rectangle 42" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_43" data-name="Rectangle 43" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_44" data-name="Rectangle 44" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_45" data-name="Rectangle 45" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_46" data-name="Rectangle 46" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_47" data-name="Rectangle 47" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_48" data-name="Rectangle 48" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_49" data-name="Rectangle 49" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_50" data-name="Rectangle 50" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_51" data-name="Rectangle 51" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_6" data-name="Group 6" transform="translate(1.531 16.584)">
|
||||
<path id="Path_54" data-name="Path 54" d="M1.093,0h7.3A1.093,1.093,0,0,1,9.485,1.093v3.15A1.093,1.093,0,0,1,8.392,5.336h-7.3A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<g id="Group_5" data-name="Group 5" transform="translate(10.671 0)">
|
||||
<rect id="Rectangle_52" data-name="Rectangle 52" width="5.336" height="5.336" rx="1" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_53" data-name="Rectangle 53" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_54" data-name="Rectangle 54" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_55" data-name="Rectangle 55" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_56" data-name="Rectangle 56" width="5.336" height="5.336" rx="1" transform="translate(25.295 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_57" data-name="Rectangle 57" width="5.336" height="5.336" rx="1" transform="translate(31.619 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_58" data-name="Rectangle 58" width="5.336" height="5.336" rx="1" transform="translate(37.942 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_59" data-name="Rectangle 59" width="5.336" height="5.336" rx="1" transform="translate(44.265 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_60" data-name="Rectangle 60" width="5.336" height="5.336" rx="1" transform="translate(50.589 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_61" data-name="Rectangle 61" width="5.336" height="5.336" rx="1" transform="translate(56.912 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_62" data-name="Rectangle 62" width="5.336" height="5.336" rx="1" transform="translate(63.236 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<path id="Path_55" data-name="Path 55" d="M1.094,0H8A1.093,1.093,0,0,1,9.091,1.093v3.15A1.093,1.093,0,0,1,8,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(80.428 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_7" data-name="Group 7" transform="translate(1.531 29.627)">
|
||||
<rect id="Rectangle_63" data-name="Rectangle 63" width="5.336" height="5.336" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_64" data-name="Rectangle 64" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_65" data-name="Rectangle 65" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_66" data-name="Rectangle 66" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_56" data-name="Path 56" d="M1.093,0H31.515a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.244V1.093A1.093,1.093,0,0,1,1.093,0ZM34.687,0h3.942a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H34.687a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,34.687,0Z" transform="translate(25.294 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_67" data-name="Rectangle 67" width="5.336" height="5.336" rx="1" transform="translate(66.003 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_68" data-name="Rectangle 68" width="5.336" height="5.336" rx="1" transform="translate(72.327 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_69" data-name="Rectangle 69" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_57" data-name="Path 57" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(83.59 2.273) rotate(180)" fill="#4a4a4a"/>
|
||||
<path id="Path_58" data-name="Path 58" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(78.255 3.063)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<rect id="Rectangle_70" data-name="Rectangle 70" width="88.927" height="2.371" rx="1.085" transform="translate(1.925 1.17)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_71" data-name="Rectangle 71" width="4.986" height="1.581" rx="0.723" transform="translate(4.1 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_72" data-name="Rectangle 72" width="4.986" height="1.581" rx="0.723" transform="translate(10.923 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="4.986" height="1.581" rx="0.723" transform="translate(16.173 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_74" data-name="Rectangle 74" width="4.986" height="1.581" rx="0.723" transform="translate(21.421 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_75" data-name="Rectangle 75" width="4.986" height="1.581" rx="0.723" transform="translate(26.671 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_76" data-name="Rectangle 76" width="4.986" height="1.581" rx="0.723" transform="translate(33.232 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_77" data-name="Rectangle 77" width="4.986" height="1.581" rx="0.723" transform="translate(38.48 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_78" data-name="Rectangle 78" width="4.986" height="1.581" rx="0.723" transform="translate(43.73 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_79" data-name="Rectangle 79" width="4.986" height="1.581" rx="0.723" transform="translate(48.978 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_80" data-name="Rectangle 80" width="4.986" height="1.581" rx="0.723" transform="translate(55.54 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_81" data-name="Rectangle 81" width="4.986" height="1.581" rx="0.723" transform="translate(60.788 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_82" data-name="Rectangle 82" width="4.986" height="1.581" rx="0.723" transform="translate(66.038 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_83" data-name="Rectangle 83" width="4.986" height="1.581" rx="0.723" transform="translate(72.599 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_84" data-name="Rectangle 84" width="4.986" height="1.581" rx="0.723" transform="translate(77.847 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_85" data-name="Rectangle 85" width="4.986" height="1.581" rx="0.723" transform="translate(83.097 1.566)" fill="#d8d8d8" opacity="0.136"/>
|
||||
</g>
|
||||
<path id="Path_59" data-name="Path 59" d="M146.71,159.855a5.439,5.439,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(6.275 -6.025)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_60" data-name="Path 60" d="M83,124.855h43.71V103H83Z" transform="translate(4.42 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_61" data-name="Path 61" d="M134.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.811,2.811,0,0,0,.349.035" transform="translate(7.202 -9.377)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_62" data-name="Path 62" d="M143.232,42.33a2.967,2.967,0,0,1-.535-.055,2.754,2.754,0,0,1-.514-.153,2.838,2.838,0,0,1-.471-.251,4.139,4.139,0,0,1-.415-.339,3.2,3.2,0,0,1-.338-.415A2.7,2.7,0,0,1,140.5,39.6a2.968,2.968,0,0,1,.055-.535,3.152,3.152,0,0,1,.152-.514,2.874,2.874,0,0,1,.252-.47,2.633,2.633,0,0,1,.753-.754,2.837,2.837,0,0,1,.471-.251,2.753,2.753,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,4.019,4.019,0,0,1,.339.415,2.786,2.786,0,0,1,.251.47,2.864,2.864,0,0,1,.208,1.049,2.77,2.77,0,0,1-.8,1.934,4.139,4.139,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459m21.855-1.366a2.789,2.789,0,0,1-1.935-.8,4.162,4.162,0,0,1-.338-.415,2.7,2.7,0,0,1-.459-1.519,2.789,2.789,0,0,1,.8-1.934,4.139,4.139,0,0,1,.415-.339,2.838,2.838,0,0,1,.471-.251,2.752,2.752,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,2.79,2.79,0,0,1,.8,1.934,3.069,3.069,0,0,1-.055.535,2.779,2.779,0,0,1-.153.514,3.885,3.885,0,0,1-.251.47,4.02,4.02,0,0,1-.339.415,4.138,4.138,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459" transform="translate(9.753 -15.532)" fill-rule="evenodd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,170 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1041.277" height="554.141" viewBox="0 0 1041.277 554.141">
|
||||
<title>Powered by React</title>
|
||||
<g id="Group_24" data-name="Group 24" transform="translate(-440 -263)">
|
||||
<g id="Group_23" data-name="Group 23" transform="translate(439.989 262.965)">
|
||||
<path id="Path_299" data-name="Path 299" d="M1040.82,611.12q-1.74,3.75-3.47,7.4-2.7,5.67-5.33,11.12c-.78,1.61-1.56,3.19-2.32,4.77-8.6,17.57-16.63,33.11-23.45,45.89A73.21,73.21,0,0,1,942.44,719l-151.65,1.65h-1.6l-13,.14-11.12.12-34.1.37h-1.38l-17.36.19h-.53l-107,1.16-95.51,1-11.11.12-69,.75H429l-44.75.48h-.48l-141.5,1.53-42.33.46a87.991,87.991,0,0,1-10.79-.54h0c-1.22-.14-2.44-.3-3.65-.49a87.38,87.38,0,0,1-51.29-27.54C116,678.37,102.75,655,93.85,629.64q-1.93-5.49-3.6-11.12C59.44,514.37,97,380,164.6,290.08q4.25-5.64,8.64-11l.07-.08c20.79-25.52,44.1-46.84,68.93-62,44-26.91,92.75-34.49,140.7-11.9,40.57,19.12,78.45,28.11,115.17,30.55,3.71.24,7.42.42,11.11.53,84.23,2.65,163.17-27.7,255.87-47.29,3.69-.78,7.39-1.55,11.12-2.28,66.13-13.16,139.49-20.1,226.73-5.51a189.089,189.089,0,0,1,26.76,6.4q5.77,1.86,11.12,4c41.64,16.94,64.35,48.24,74,87.46q1.37,5.46,2.37,11.11C1134.3,384.41,1084.19,518.23,1040.82,611.12Z" transform="translate(-79.34 -172.91)" fill="#f2f2f2"/>
|
||||
<path id="Path_300" data-name="Path 300" d="M576.36,618.52a95.21,95.21,0,0,1-1.87,11.12h93.7V618.52Zm-78.25,62.81,11.11-.09V653.77c-3.81-.17-7.52-.34-11.11-.52ZM265.19,618.52v11.12h198.5V618.52ZM1114.87,279h-74V191.51q-5.35-2.17-11.12-4V279H776.21V186.58c-3.73.73-7.43,1.5-11.12,2.28V279H509.22V236.15c-3.69-.11-7.4-.29-11.11-.53V279H242.24V217c-24.83,15.16-48.14,36.48-68.93,62h-.07v.08q-4.4,5.4-8.64,11h8.64V618.52h-83q1.66,5.63,3.6,11.12h79.39v93.62a87,87,0,0,0,12.2,2.79c1.21.19,2.43.35,3.65.49h0a87.991,87.991,0,0,0,10.79.54l42.33-.46v-97H498.11v94.21l11.11-.12V629.64H765.09V721l11.12-.12V629.64H1029.7v4.77c.76-1.58,1.54-3.16,2.32-4.77q2.63-5.45,5.33-11.12,1.73-3.64,3.47-7.4v-321h76.42Q1116.23,284.43,1114.87,279ZM242.24,618.52V290.08H498.11V618.52Zm267,0V290.08H765.09V618.52Zm520.48,0H776.21V290.08H1029.7Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_301" data-name="Path 301" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" fill="#65617d"/>
|
||||
<path id="Path_302" data-name="Path 302" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" opacity="0.2"/>
|
||||
<path id="Path_303" data-name="Path 303" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<path id="Path_304" data-name="Path 304" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_305" data-name="Path 305" d="M377.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_137" data-name="Rectangle 137" width="47.17" height="31.5" transform="translate(680.92 483.65)" fill="#3f3d56"/>
|
||||
<rect id="Rectangle_138" data-name="Rectangle 138" width="47.17" height="31.5" transform="translate(680.92 483.65)" opacity="0.1"/>
|
||||
<rect id="Rectangle_139" data-name="Rectangle 139" width="47.17" height="31.5" transform="translate(678.92 483.65)" fill="#3f3d56"/>
|
||||
<path id="Path_306" data-name="Path 306" d="M298.09,483.65v4.97l-47.17,1.26v-6.23Z" opacity="0.1"/>
|
||||
<path id="Path_307" data-name="Path 307" d="M460.69,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6a4,4,0,0,1,3.95,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_308" data-name="Path 308" d="M265.19,481.32v181.2h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_309" data-name="Path 309" d="M194.59,319.15h177.5V467.4l-177.5,4Z" fill="#39374d"/>
|
||||
<path id="Path_310" data-name="Path 310" d="M726.09,483.65v6.41l-47.17-1.26v-5.15Z" opacity="0.1"/>
|
||||
<path id="Path_311" data-name="Path 311" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0L672,657.42a4,4,0,0,1-3.85-3.95V485.27a4,4,0,0,1,3.95-3.95H863.7a4,4,0,0,1,3.99,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_312" data-name="Path 312" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0V481.32h0a4,4,0,0,1,4,3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_313" data-name="Path 313" d="M775.59,319.15H598.09V467.4l177.5,4Z" fill="#39374d"/>
|
||||
<path id="Path_314" data-name="Path 314" d="M663.19,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h0a4,4,0,0,1-4-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6A4,4,0,0,1,663.19,485.27Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
|
||||
<path id="Path_315" data-name="Path 315" d="M397.09,319.15h177.5V467.4l-177.5,4Z" fill="#4267b2"/>
|
||||
<path id="Path_316" data-name="Path 316" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l202.51-1.33h.48l40.99-.28h.19l283.08-1.87h.29l.17-.01h.47l4.79-.03h1.46l74.49-.5,4.4-.02.98-.01Z" opacity="0.1"/>
|
||||
<circle id="Ellipse_111" data-name="Ellipse 111" cx="51.33" cy="51.33" r="51.33" transform="translate(435.93 246.82)" fill="#fbbebe"/>
|
||||
<path id="Path_317" data-name="Path 317" d="M617.94,550.07s-99.5,12-90,0c3.44-4.34,4.39-17.2,4.2-31.85-.06-4.45-.22-9.06-.45-13.65-1.1-22-3.75-43.5-3.75-43.5s87-41,77-8.5c-4,13.13-2.69,31.57.35,48.88.89,5.05,1.92,10,3,14.7a344.66,344.66,0,0,0,9.65,33.92Z" transform="translate(-79.34 -172.91)" fill="#fbbebe"/>
|
||||
<path id="Path_318" data-name="Path 318" d="M585.47,546c11.51-2.13,23.7-6,34.53-1.54,2.85,1.17,5.47,2.88,8.39,3.86s6.12,1.22,9.16,1.91c10.68,2.42,19.34,10.55,24.9,20s8.44,20.14,11.26,30.72l6.9,25.83c6,22.45,12,45.09,13.39,68.3a2437.506,2437.506,0,0,1-250.84,1.43c5.44-10.34,11-21.31,10.54-33s-7.19-23.22-4.76-34.74c1.55-7.34,6.57-13.39,9.64-20.22,8.75-19.52,1.94-45.79,17.32-60.65,6.92-6.68,17-9.21,26.63-8.89,12.28.41,24.85,4.24,37,6.11C555.09,547.48,569.79,548.88,585.47,546Z" transform="translate(-79.34 -172.91)" fill="#ff6584"/>
|
||||
<path id="Path_319" data-name="Path 319" d="M716.37,657.17l-.1,1.43v.1l-.17,2.3-1.33,18.51-1.61,22.3-.46,6.28-1,13.44v.17l-107,1-175.59,1.9v.84h-.14v-1.12l.45-14.36.86-28.06.74-23.79.07-2.37a10.53,10.53,0,0,1,11.42-10.17c4.72.4,10.85.89,18.18,1.41l3,.22c42.33,2.94,120.56,6.74,199.5,2,1.66-.09,3.33-.19,5-.31,12.24-.77,24.47-1.76,36.58-3a10.53,10.53,0,0,1,11.6,11.23Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_320" data-name="Path 320" d="M429.08,725.44v-.84l175.62-1.91,107-1h.3v-.17l1-13.44.43-6,1.64-22.61,1.29-17.9v-.44a10.617,10.617,0,0,0-.11-2.47.3.3,0,0,0,0-.1,10.391,10.391,0,0,0-2-4.64,10.54,10.54,0,0,0-9.42-4c-12.11,1.24-24.34,2.23-36.58,3-1.67.12-3.34.22-5,.31-78.94,4.69-157.17.89-199.5-2l-3-.22c-7.33-.52-13.46-1-18.18-1.41a10.54,10.54,0,0,0-11.24,8.53,11,11,0,0,0-.18,1.64l-.68,22.16L429.54,710l-.44,14.36v1.12Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
<path id="Path_321" data-name="Path 321" d="M716.67,664.18l-1.23,15.33-1.83,22.85-.46,5.72-1,12.81-.06.64v.17h0l-.15,1.48.11-1.48h-.29l-107,1-175.65,1.9v-.28l.49-14.36,1-28.06.64-18.65A6.36,6.36,0,0,1,434.3,658a6.25,6.25,0,0,1,3.78-.9c2.1.17,4.68.37,7.69.59,4.89.36,10.92.78,17.94,1.22,13,.82,29.31,1.7,48,2.42,52,2,122.2,2.67,188.88-3.17,3-.26,6.1-.55,9.13-.84a6.26,6.26,0,0,1,3.48.66,5.159,5.159,0,0,1,.86.54,6.14,6.14,0,0,1,2,2.46,3.564,3.564,0,0,1,.25.61A6.279,6.279,0,0,1,716.67,664.18Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_322" data-name="Path 322" d="M377.44,677.87v3.19a6.13,6.13,0,0,1-3.5,5.54l-40.1.77a6.12,6.12,0,0,1-3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_323" data-name="Path 323" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_324" data-name="Path 324" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" opacity="0.1"/>
|
||||
<path id="Path_325" data-name="Path 325" d="M300.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_326" data-name="Path 326" d="M758.56,679.87v3.19a6.13,6.13,0,0,0,3.5,5.54l40.1.77a6.12,6.12,0,0,0,3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
|
||||
<path id="Path_327" data-name="Path 327" d="M678.72,517.57l52.25,1V509.9l-52.25-1Z" opacity="0.1"/>
|
||||
<path id="Path_328" data-name="Path 328" d="M676.72,517.57l52.25,1V509.9l-52.25-1Z" fill="#3f3d56"/>
|
||||
<path id="Path_329" data-name="Path 329" d="M534.13,486.79c.08,7-3.16,13.6-5.91,20.07a163.491,163.491,0,0,0-12.66,74.71c.73,11,2.58,22,.73,32.9s-8.43,21.77-19,24.9c17.53,10.45,41.26,9.35,57.76-2.66,8.79-6.4,15.34-15.33,21.75-24.11a97.86,97.86,0,0,1-13.31,44.75A103.43,103.43,0,0,0,637,616.53c4.31-5.81,8.06-12.19,9.72-19.23,3.09-13-1.22-26.51-4.51-39.5a266.055,266.055,0,0,1-6.17-33c-.43-3.56-.78-7.22.1-10.7,1-4.07,3.67-7.51,5.64-11.22,5.6-10.54,5.73-23.3,2.86-34.88s-8.49-22.26-14.06-32.81c-4.46-8.46-9.3-17.31-17.46-22.28-5.1-3.1-11-4.39-16.88-5.64l-25.37-5.43c-5.55-1.19-11.26-2.38-16.87-1.51-9.47,1.48-16.14,8.32-22,15.34-4.59,5.46-15.81,15.71-16.6,22.86-.72,6.59,5.1,17.63,6.09,24.58,1.3,9,2.22,6,7.3,11.52C532,478.05,534.07,482,534.13,486.79Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
|
||||
</g>
|
||||
<g id="docusaurus_keytar" transform="translate(670.271 615.768)">
|
||||
<path id="Path_40" data-name="Path 40" d="M99,52h43.635V69.662H99Z" transform="translate(-49.132 -33.936)" fill="#fff" fill-rule="evenodd"/>
|
||||
<path id="Path_41" data-name="Path 41" d="M13.389,158.195A10.377,10.377,0,0,1,4.4,153a10.377,10.377,0,0,0,8.988,15.584H23.779V158.195Z" transform="translate(-3 -82.47)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_42" data-name="Path 42" d="M66.967,38.083l36.373-2.273V30.615A10.389,10.389,0,0,0,92.95,20.226H46.2l-1.3-2.249a1.5,1.5,0,0,0-2.6,0L41,20.226l-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-.034,0-2.152-2.151a1.5,1.5,0,0,0-2.508.672L25.21,21.4l-2.7-.723a1.5,1.5,0,0,0-1.836,1.837l.722,2.7-2.65.71a1.5,1.5,0,0,0-.673,2.509l2.152,2.152c0,.011,0,.022,0,.033l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6L20.226,41l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3A10.389,10.389,0,0,0,30.615,103.34H92.95A10.389,10.389,0,0,0,103.34,92.95V51.393L66.967,49.12a5.53,5.53,0,0,1,0-11.038" transform="translate(-9.836 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_43" data-name="Path 43" d="M143,163.779h15.584V143H143Z" transform="translate(-70.275 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_44" data-name="Path 44" d="M173.779,148.389a2.582,2.582,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-75.08 -75.262)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_45" data-name="Path 45" d="M153,113.389h15.584V103H153Z" transform="translate(-75.08 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_46" data-name="Path 46" d="M183.389,108.944a1.3,1.3,0,1,0,0-2.6,1.336,1.336,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.337,1.337,0,0,0,.166.017" transform="translate(-84.691 -57.894)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_47" data-name="Path 47" d="M52.188,48.292a1.3,1.3,0,0,1-1.3-1.3,3.9,3.9,0,0,0-7.792,0,1.3,1.3,0,1,1-2.6,0,6.493,6.493,0,0,1,12.987,0,1.3,1.3,0,0,1-1.3,1.3" transform="translate(-21.02 -28.41)" fill-rule="evenodd"/>
|
||||
<path id="Path_48" data-name="Path 48" d="M103,139.752h31.168a10.389,10.389,0,0,0,10.389-10.389V93H113.389A10.389,10.389,0,0,0,103,103.389Z" transform="translate(-51.054 -53.638)" fill="#ffff50" fill-rule="evenodd"/>
|
||||
<path id="Path_49" data-name="Path 49" d="M141.1,94.017H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0-25.877H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.293H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m7.782-47.993c-.006,0-.011,0-.018,0-1.605.055-2.365,1.66-3.035,3.077-.7,1.48-1.24,2.443-2.126,2.414-.981-.035-1.542-1.144-2.137-2.317-.683-1.347-1.462-2.876-3.1-2.819-1.582.054-2.344,1.451-3.017,2.684-.715,1.313-1.2,2.112-2.141,2.075-1-.036-1.533-.938-2.149-1.981-.686-1.162-1.479-2.467-3.084-2.423-1.555.053-2.319,1.239-2.994,2.286-.713,1.106-1.213,1.781-2.164,1.741-1.025-.036-1.554-.784-2.167-1.65-.688-.973-1.463-2.074-3.062-2.021a3.815,3.815,0,0,0-2.959,1.879c-.64.812-1.14,1.456-2.2,1.415a.52.52,0,0,0-.037,1.039,3.588,3.588,0,0,0,3.05-1.811c.611-.777,1.139-1.448,2.178-1.483,1-.043,1.47.579,2.179,1.582.674.953,1.438,2.033,2.977,2.089,1.612.054,2.387-1.151,3.074-2.217.614-.953,1.144-1.775,2.156-1.81.931-.035,1.438.7,2.153,1.912.674,1.141,1.437,2.434,3.006,2.491,1.623.056,2.407-1.361,3.09-2.616.592-1.085,1.15-2.109,2.14-2.143.931-.022,1.417.829,2.135,2.249.671,1.326,1.432,2.828,3.026,2.886l.088,0c1.592,0,2.347-1.6,3.015-3.01.592-1.252,1.152-2.431,2.113-2.479Z" transform="translate(-55.378 -38.552)" fill-rule="evenodd"/>
|
||||
<path id="Path_50" data-name="Path 50" d="M83,163.779h20.779V143H83Z" transform="translate(-41.443 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 51.971, 43.3)">
|
||||
<rect id="Rectangle_3" data-name="Rectangle 3" width="43.906" height="17.333" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
|
||||
<g id="Group_2" data-name="Group 2" transform="translate(0.728 10.948)">
|
||||
<rect id="Rectangle_4" data-name="Rectangle 4" width="2.537" height="2.537" rx="1" transform="translate(7.985 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_5" data-name="Rectangle 5" width="2.537" height="2.537" rx="1" transform="translate(10.991 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_6" data-name="Rectangle 6" width="2.537" height="2.537" rx="1" transform="translate(13.997 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_7" data-name="Rectangle 7" width="2.537" height="2.537" rx="1" transform="translate(17.003 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_8" data-name="Rectangle 8" width="2.537" height="2.537" rx="1" transform="translate(20.009 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_9" data-name="Rectangle 9" width="2.537" height="2.537" rx="1" transform="translate(23.015 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_10" data-name="Rectangle 10" width="2.537" height="2.537" rx="1" transform="translate(26.021 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_11" data-name="Rectangle 11" width="2.537" height="2.537" rx="1" transform="translate(29.028 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_12" data-name="Rectangle 12" width="2.537" height="2.537" rx="1" transform="translate(32.034 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_51" data-name="Path 51" d="M.519,0H6.9A.519.519,0,0,1,7.421.52v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0ZM35.653,0h6.383a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H35.652a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,35.652,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_3" data-name="Group 3" transform="translate(0.728 4.878)">
|
||||
<path id="Path_52" data-name="Path 52" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_13" data-name="Rectangle 13" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_14" data-name="Rectangle 14" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_15" data-name="Rectangle 15" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_16" data-name="Rectangle 16" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_17" data-name="Rectangle 17" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_18" data-name="Rectangle 18" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_19" data-name="Rectangle 19" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_20" data-name="Rectangle 20" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_21" data-name="Rectangle 21" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_22" data-name="Rectangle 22" width="2.537" height="2.537" rx="1" transform="translate(31 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_23" data-name="Rectangle 23" width="2.537" height="2.537" rx="1" transform="translate(34.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_24" data-name="Rectangle 24" width="2.537" height="2.537" rx="1" transform="translate(37.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_25" data-name="Rectangle 25" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_4" data-name="Group 4" transform="translate(43.283 4.538) rotate(180)">
|
||||
<path id="Path_53" data-name="Path 53" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_26" data-name="Rectangle 26" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_27" data-name="Rectangle 27" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_28" data-name="Rectangle 28" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_29" data-name="Rectangle 29" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_30" data-name="Rectangle 30" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_31" data-name="Rectangle 31" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_32" data-name="Rectangle 32" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_33" data-name="Rectangle 33" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_34" data-name="Rectangle 34" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_35" data-name="Rectangle 35" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_36" data-name="Rectangle 36" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_37" data-name="Rectangle 37" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_38" data-name="Rectangle 38" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_39" data-name="Rectangle 39" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_40" data-name="Rectangle 40" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_41" data-name="Rectangle 41" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_42" data-name="Rectangle 42" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_43" data-name="Rectangle 43" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_44" data-name="Rectangle 44" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_45" data-name="Rectangle 45" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_46" data-name="Rectangle 46" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_47" data-name="Rectangle 47" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_48" data-name="Rectangle 48" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_49" data-name="Rectangle 49" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_50" data-name="Rectangle 50" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_51" data-name="Rectangle 51" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<g id="Group_6" data-name="Group 6" transform="translate(0.728 7.883)">
|
||||
<path id="Path_54" data-name="Path 54" d="M.519,0h3.47a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<g id="Group_5" data-name="Group 5" transform="translate(5.073 0)">
|
||||
<rect id="Rectangle_52" data-name="Rectangle 52" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_53" data-name="Rectangle 53" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_54" data-name="Rectangle 54" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_55" data-name="Rectangle 55" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_56" data-name="Rectangle 56" width="2.537" height="2.537" rx="1" transform="translate(12.025 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_57" data-name="Rectangle 57" width="2.537" height="2.537" rx="1" transform="translate(15.031 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_58" data-name="Rectangle 58" width="2.537" height="2.537" rx="1" transform="translate(18.037 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_59" data-name="Rectangle 59" width="2.537" height="2.537" rx="1" transform="translate(21.042 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_60" data-name="Rectangle 60" width="2.537" height="2.537" rx="1" transform="translate(24.049 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_61" data-name="Rectangle 61" width="2.537" height="2.537" rx="1" transform="translate(27.055 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_62" data-name="Rectangle 62" width="2.537" height="2.537" rx="1" transform="translate(30.061 0)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<path id="Path_55" data-name="Path 55" d="M.52,0H3.8a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(38.234 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="Group_7" data-name="Group 7" transform="translate(0.728 14.084)">
|
||||
<rect id="Rectangle_63" data-name="Rectangle 63" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_64" data-name="Rectangle 64" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_65" data-name="Rectangle 65" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_66" data-name="Rectangle 66" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_56" data-name="Path 56" d="M.519,0H14.981A.519.519,0,0,1,15.5.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.018V.519A.519.519,0,0,1,.519,0Zm15.97,0h1.874a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H16.489a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,16.489,0Z" transform="translate(12.024 0)" fill="#4a4a4a" fill-rule="evenodd"/>
|
||||
<rect id="Rectangle_67" data-name="Rectangle 67" width="2.537" height="2.537" rx="1" transform="translate(31.376 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_68" data-name="Rectangle 68" width="2.537" height="2.537" rx="1" transform="translate(34.382 0)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_69" data-name="Rectangle 69" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
|
||||
<path id="Path_57" data-name="Path 57" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(39.736 1.08) rotate(180)" fill="#4a4a4a"/>
|
||||
<path id="Path_58" data-name="Path 58" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(37.2 1.456)" fill="#4a4a4a"/>
|
||||
</g>
|
||||
<rect id="Rectangle_70" data-name="Rectangle 70" width="42.273" height="1.127" rx="0.564" transform="translate(0.915 0.556)" fill="#4a4a4a"/>
|
||||
<rect id="Rectangle_71" data-name="Rectangle 71" width="2.37" height="0.752" rx="0.376" transform="translate(1.949 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_72" data-name="Rectangle 72" width="2.37" height="0.752" rx="0.376" transform="translate(5.193 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="2.37" height="0.752" rx="0.376" transform="translate(7.688 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_74" data-name="Rectangle 74" width="2.37" height="0.752" rx="0.376" transform="translate(10.183 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_75" data-name="Rectangle 75" width="2.37" height="0.752" rx="0.376" transform="translate(12.679 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_76" data-name="Rectangle 76" width="2.37" height="0.752" rx="0.376" transform="translate(15.797 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_77" data-name="Rectangle 77" width="2.37" height="0.752" rx="0.376" transform="translate(18.292 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_78" data-name="Rectangle 78" width="2.37" height="0.752" rx="0.376" transform="translate(20.788 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_79" data-name="Rectangle 79" width="2.37" height="0.752" rx="0.376" transform="translate(23.283 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_80" data-name="Rectangle 80" width="2.37" height="0.752" rx="0.376" transform="translate(26.402 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_81" data-name="Rectangle 81" width="2.37" height="0.752" rx="0.376" transform="translate(28.897 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_82" data-name="Rectangle 82" width="2.37" height="0.752" rx="0.376" transform="translate(31.393 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_83" data-name="Rectangle 83" width="2.37" height="0.752" rx="0.376" transform="translate(34.512 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_84" data-name="Rectangle 84" width="2.37" height="0.752" rx="0.376" transform="translate(37.007 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
<rect id="Rectangle_85" data-name="Rectangle 85" width="2.37" height="0.752" rx="0.376" transform="translate(39.502 0.744)" fill="#d8d8d8" opacity="0.136"/>
|
||||
</g>
|
||||
<path id="Path_59" data-name="Path 59" d="M123.779,148.389a2.583,2.583,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-51.054 -75.262)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_60" data-name="Path 60" d="M83,113.389h20.779V103H83Z" transform="translate(-41.443 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
|
||||
<path id="Path_61" data-name="Path 61" d="M123.389,108.944a1.3,1.3,0,1,0,0-2.6,1.338,1.338,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.335,1.335,0,0,0,.166.017" transform="translate(-55.859 -57.894)" fill="#44d860" fill-rule="evenodd"/>
|
||||
<path id="Path_62" data-name="Path 62" d="M141.8,38.745a1.41,1.41,0,0,1-.255-.026,1.309,1.309,0,0,1-.244-.073,1.349,1.349,0,0,1-.224-.119,1.967,1.967,0,0,1-.2-.161,1.52,1.52,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.41,1.41,0,0,1,.026-.255,1.5,1.5,0,0,1,.072-.244,1.364,1.364,0,0,1,.12-.223,1.252,1.252,0,0,1,.358-.358,1.349,1.349,0,0,1,.224-.119,1.309,1.309,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.968,1.968,0,0,1,.2.161,1.908,1.908,0,0,1,.161.2,1.322,1.322,0,0,1,.12.223,1.361,1.361,0,0,1,.1.5,1.317,1.317,0,0,1-.379.919,1.968,1.968,0,0,1-.2.161,1.346,1.346,0,0,1-.223.119,1.332,1.332,0,0,1-.5.1m10.389-.649a1.326,1.326,0,0,1-.92-.379,1.979,1.979,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.326,1.326,0,0,1,.379-.919,1.967,1.967,0,0,1,.2-.161,1.351,1.351,0,0,1,.224-.119,1.308,1.308,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.967,1.967,0,0,1,.2.161,1.326,1.326,0,0,1,.379.919,1.461,1.461,0,0,1-.026.255,1.323,1.323,0,0,1-.073.244,1.847,1.847,0,0,1-.119.223,1.911,1.911,0,0,1-.161.2,1.967,1.967,0,0,1-.2.161,1.294,1.294,0,0,1-.722.218" transform="translate(-69.074 -26.006)" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g id="React-icon" transform="translate(906.3 541.56)">
|
||||
<path id="Path_330" data-name="Path 330" d="M263.668,117.179c0-5.827-7.3-11.35-18.487-14.775,2.582-11.4,1.434-20.477-3.622-23.382a7.861,7.861,0,0,0-4.016-1v4a4.152,4.152,0,0,1,2.044.466c2.439,1.4,3.5,6.724,2.672,13.574-.2,1.685-.52,3.461-.914,5.272a86.9,86.9,0,0,0-11.386-1.954,87.469,87.469,0,0,0-7.459-8.965c5.845-5.433,11.332-8.41,15.062-8.41V78h0c-4.931,0-11.386,3.514-17.913,9.611-6.527-6.061-12.982-9.539-17.913-9.539v4c3.712,0,9.216,2.959,15.062,8.356a84.687,84.687,0,0,0-7.405,8.947,83.732,83.732,0,0,0-11.4,1.972c-.412-1.793-.717-3.532-.932-5.2-.843-6.85.2-12.175,2.618-13.592a3.991,3.991,0,0,1,2.062-.466v-4h0a8,8,0,0,0-4.052,1c-5.039,2.9-6.168,11.96-3.568,23.328-11.153,3.443-18.415,8.947-18.415,14.757,0,5.828,7.3,11.35,18.487,14.775-2.582,11.4-1.434,20.477,3.622,23.382a7.882,7.882,0,0,0,4.034,1c4.931,0,11.386-3.514,17.913-9.611,6.527,6.061,12.982,9.539,17.913,9.539a8,8,0,0,0,4.052-1c5.039-2.9,6.168-11.96,3.568-23.328C256.406,128.511,263.668,122.988,263.668,117.179Zm-23.346-11.96c-.663,2.313-1.488,4.7-2.421,7.083-.735-1.434-1.506-2.869-2.349-4.3-.825-1.434-1.7-2.833-2.582-4.2C235.517,104.179,237.974,104.645,240.323,105.219Zm-8.212,19.1c-1.4,2.421-2.833,4.716-4.321,6.85-2.672.233-5.379.359-8.1.359-2.708,0-5.415-.126-8.069-.341q-2.232-3.2-4.339-6.814-2.044-3.523-3.73-7.136c1.112-2.4,2.367-4.805,3.712-7.154,1.4-2.421,2.833-4.716,4.321-6.85,2.672-.233,5.379-.359,8.1-.359,2.708,0,5.415.126,8.069.341q2.232,3.2,4.339,6.814,2.044,3.523,3.73,7.136C234.692,119.564,233.455,121.966,232.11,124.315Zm5.792-2.331c.968,2.4,1.793,4.805,2.474,7.136-2.349.574-4.823,1.058-7.387,1.434.879-1.381,1.757-2.8,2.582-4.25C236.4,124.871,237.167,123.419,237.9,121.984ZM219.72,141.116a73.921,73.921,0,0,1-4.985-5.738c1.614.072,3.263.126,4.931.126,1.685,0,3.353-.036,4.985-.126A69.993,69.993,0,0,1,219.72,141.116ZM206.38,130.555c-2.546-.377-5-.843-7.352-1.417.663-2.313,1.488-4.7,2.421-7.083.735,1.434,1.506,2.869,2.349,4.3S205.5,129.192,206.38,130.555ZM219.63,93.241a73.924,73.924,0,0,1,4.985,5.738c-1.614-.072-3.263-.126-4.931-.126-1.686,0-3.353.036-4.985.126A69.993,69.993,0,0,1,219.63,93.241ZM206.362,103.8c-.879,1.381-1.757,2.8-2.582,4.25-.825,1.434-1.6,2.869-2.331,4.3-.968-2.4-1.793-4.805-2.474-7.136C201.323,104.663,203.8,104.179,206.362,103.8Zm-16.227,22.449c-6.348-2.708-10.454-6.258-10.454-9.073s4.106-6.383,10.454-9.073c1.542-.663,3.228-1.255,4.967-1.811a86.122,86.122,0,0,0,4.034,10.92,84.9,84.9,0,0,0-3.981,10.866C193.38,127.525,191.694,126.915,190.134,126.252Zm9.647,25.623c-2.439-1.4-3.5-6.724-2.672-13.574.2-1.686.52-3.461.914-5.272a86.9,86.9,0,0,0,11.386,1.954,87.465,87.465,0,0,0,7.459,8.965c-5.845,5.433-11.332,8.41-15.062,8.41A4.279,4.279,0,0,1,199.781,151.875Zm42.532-13.663c.843,6.85-.2,12.175-2.618,13.592a3.99,3.99,0,0,1-2.062.466c-3.712,0-9.216-2.959-15.062-8.356a84.689,84.689,0,0,0,7.405-8.947,83.731,83.731,0,0,0,11.4-1.972A50.194,50.194,0,0,1,242.313,138.212Zm6.9-11.96c-1.542.663-3.228,1.255-4.967,1.811a86.12,86.12,0,0,0-4.034-10.92,84.9,84.9,0,0,0,3.981-10.866c1.775.556,3.461,1.165,5.039,1.829,6.348,2.708,10.454,6.258,10.454,9.073C259.67,119.994,255.564,123.562,249.216,126.252Z" fill="#61dafb"/>
|
||||
<path id="Path_331" data-name="Path 331" d="M320.8,78.4Z" transform="translate(-119.082 -0.328)" fill="#61dafb"/>
|
||||
<circle id="Ellipse_112" data-name="Ellipse 112" cx="8.194" cy="8.194" r="8.194" transform="translate(211.472 108.984)" fill="#61dafb"/>
|
||||
<path id="Path_332" data-name="Path 332" d="M520.5,78.1Z" transform="translate(-282.975 -0.082)" fill="#61dafb"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 35 KiB |
@@ -1,40 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1129" height="663" viewBox="0 0 1129 663">
|
||||
<title>Focus on What Matters</title>
|
||||
<circle cx="321" cy="321" r="321" fill="#f2f2f2" />
|
||||
<ellipse cx="559" cy="635.49998" rx="514" ry="27.50002" fill="#3f3d56" />
|
||||
<ellipse cx="558" cy="627" rx="460" ry="22" opacity="0.2" />
|
||||
<rect x="131" y="152.5" width="840" height="50" fill="#3f3d56" />
|
||||
<path d="M166.5,727.3299A21.67009,21.67009,0,0,0,188.1701,749H984.8299A21.67009,21.67009,0,0,0,1006.5,727.3299V296h-840Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" opacity="0.2" />
|
||||
<circle cx="181" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<circle cx="217" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<circle cx="253" cy="147.5" r="13" fill="#3f3d56" />
|
||||
<rect x="168" y="213.5" width="337" height="386" rx="5.33505" fill="#606060" />
|
||||
<rect x="603" y="272.5" width="284" height="22" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="352.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="396.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="440.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="537" y="484.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
|
||||
<rect x="865" y="552.5" width="88" height="26" rx="7.02756" fill="#3ecc5f" />
|
||||
<path d="M1088.60287,624.61594a30.11371,30.11371,0,0,0,3.98291-15.266c0-13.79652-8.54358-24.98081-19.08256-24.98081s-19.08256,11.18429-19.08256,24.98081a30.11411,30.11411,0,0,0,3.98291,15.266,31.248,31.248,0,0,0,0,30.53213,31.248,31.248,0,0,0,0,30.53208,31.248,31.248,0,0,0,0,30.53208,30.11408,30.11408,0,0,0-3.98291,15.266c0,13.79652,8.54353,24.98081,19.08256,24.98081s19.08256-11.18429,19.08256-24.98081a30.11368,30.11368,0,0,0-3.98291-15.266,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53213Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
|
||||
<ellipse cx="1038.00321" cy="460.31783" rx="19.08256" ry="24.9808" fill="#3f3d56" />
|
||||
<ellipse cx="1038.00321" cy="429.78574" rx="19.08256" ry="24.9808" fill="#3f3d56" />
|
||||
<path d="M1144.93871,339.34489a91.61081,91.61081,0,0,0,7.10658-10.46092l-50.141-8.23491,54.22885.4033a91.566,91.566,0,0,0,1.74556-72.42605l-72.75449,37.74139,67.09658-49.32086a91.41255,91.41255,0,1,0-150.971,102.29805,91.45842,91.45842,0,0,0-10.42451,16.66946l65.0866,33.81447-69.40046-23.292a91.46011,91.46011,0,0,0,14.73837,85.83669,91.40575,91.40575,0,1,0,143.68892,0,91.41808,91.41808,0,0,0,0-113.02862Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M981.6885,395.8592a91.01343,91.01343,0,0,0,19.56129,56.51431,91.40575,91.40575,0,1,0,143.68892,0C1157.18982,436.82067,981.6885,385.60008,981.6885,395.8592Z" transform="translate(-35.5 -118.5)" opacity="0.1" />
|
||||
<path d="M365.62,461.43628H477.094v45.12043H365.62Z" transform="translate(-35.5 -118.5)" fill="#fff" fill-rule="evenodd" />
|
||||
<path d="M264.76252,608.74122a26.50931,26.50931,0,0,1-22.96231-13.27072,26.50976,26.50976,0,0,0,22.96231,39.81215H291.304V608.74122Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M384.17242,468.57061l92.92155-5.80726V449.49263a26.54091,26.54091,0,0,0-26.54143-26.54143H331.1161l-3.31768-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622-3.31767-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622L301.257,417.205a3.83043,3.83043,0,0,0-6.63536,0L291.304,422.9512c-.02919,0-.05573.004-.08625.004l-5.49674-5.49541a3.8293,3.8293,0,0,0-6.4071,1.71723l-1.81676,6.77338L270.607,424.1031a3.82993,3.82993,0,0,0-4.6912,4.69253l1.84463,6.89148-6.77072,1.81411a3.8315,3.8315,0,0,0-1.71988,6.40975l5.49673,5.49673c0,.02787-.004.05574-.004.08493l-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74621,3.31768L259.0163,466.081a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768L259.0163,558.976a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768A26.54091,26.54091,0,0,0,291.304,635.28265H450.55254A26.5409,26.5409,0,0,0,477.094,608.74122V502.5755l-92.92155-5.80727a14.12639,14.12639,0,0,1,0-28.19762" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M424.01111,635.28265h39.81214V582.19979H424.01111Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M490.36468,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15393-.59852A6.62668,6.62668,0,1,0,482.80568,590.21q-.2203-.22491-.44457-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39414-.10218-.59056-.15262a6.63957,6.63957,0,1,0-13.10086,0c-.1964.05042-.39414.09687-.59056.15262a6.62767,6.62767,0,1,0-11.39688,6.56369,26.52754,26.52754,0,1,0,44.23127,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M437.28182,555.65836H477.094V529.11693H437.28182Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M490.36468,545.70532a3.31768,3.31768,0,0,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M317.84538,466.081a3.31768,3.31768,0,0,1-3.31767-3.31768,9.953,9.953,0,1,0-19.90608,0,3.31768,3.31768,0,1,1-6.63535,0,16.58839,16.58839,0,1,1,33.17678,0,3.31768,3.31768,0,0,1-3.31768,3.31768" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
<path d="M370.92825,635.28265h79.62429A26.5409,26.5409,0,0,0,477.094,608.74122v-92.895H397.46968a26.54091,26.54091,0,0,0-26.54143,26.54143Z" transform="translate(-35.5 -118.5)" fill="#ffff50" fill-rule="evenodd" />
|
||||
<path d="M457.21444,556.98543H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0-66.10674H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.29459H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414M477.094,474.19076c-.01592,0-.0292-.008-.04512-.00663-4.10064.13934-6.04083,4.24132-7.75274,7.86024-1.78623,3.78215-3.16771,6.24122-5.43171,6.16691-2.50685-.09024-3.94007-2.92222-5.45825-5.91874-1.74377-3.44243-3.73438-7.34667-7.91333-7.20069-4.04227.138-5.98907,3.70784-7.70631,6.857-1.82738,3.35484-3.07084,5.39455-5.46887,5.30033-2.55727-.09289-3.91619-2.39536-5.48877-5.06013-1.75306-2.96733-3.77951-6.30359-7.8775-6.18946-3.97326.13669-5.92537,3.16507-7.64791,5.83912-1.82207,2.82666-3.09872,4.5492-5.52725,4.447-2.61832-.09289-3.9706-2.00388-5.53522-4.21611-1.757-2.4856-3.737-5.299-7.82308-5.16231-3.88567.13271-5.83779,2.61434-7.559,4.80135-1.635,2.07555-2.9116,3.71846-5.61218,3.615a1.32793,1.32793,0,1,0-.09555,2.65414c4.00377.134,6.03154-2.38873,7.79257-4.6275,1.562-1.9853,2.91027-3.69855,5.56441-3.78879,2.55594-.10882,3.75429,1.47968,5.56707,4.04093,1.7212,2.43385,3.67465,5.19416,7.60545,5.33616,4.11789.138,6.09921-2.93946,7.8536-5.66261,1.56861-2.43385,2.92221-4.53461,5.50734-4.62352,2.37944-.08892,3.67466,1.79154,5.50072,4.885,1.72121,2.91557,3.67069,6.21865,7.67977,6.36463,4.14709.14332,6.14965-3.47693,7.89475-6.68181,1.51155-2.77092,2.93814-5.38791,5.46621-5.4755,2.37944-.05573,3.62025,2.11668,5.45558,5.74622,1.71459,3.388,3.65875,7.22591,7.73019,7.37321l.22429.004c4.06614,0,5.99571-4.08074,7.70364-7.68905,1.51154-3.19825,2.94211-6.21069,5.3972-6.33411Z" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
<path d="M344.38682,635.28265h53.08286V582.19979H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M424.01111,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15394-.59852A6.62667,6.62667,0,1,0,416.45211,590.21q-.2203-.22491-.44458-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39413-.10218-.59054-.15262a6.63957,6.63957,0,1,0-13.10084,0c-.19641.05042-.39414.09687-.59055.15262a6.62767,6.62767,0,1,0-11.39689,6.56369,26.52755,26.52755,0,1,0,44.2313,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M344.38682,555.65836h53.08286V529.11693H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
|
||||
<path d="M410.74039,545.70532a3.31768,3.31768,0,1,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
|
||||
<path d="M424.01111,447.8338a3.60349,3.60349,0,0,1-.65028-.06636,3.34415,3.34415,0,0,1-.62372-.18579,3.44679,3.44679,0,0,1-.572-.30522,5.02708,5.02708,0,0,1-.50429-.4114,3.88726,3.88726,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.60248,3.60248,0,0,1,.06636-.65027,3.82638,3.82638,0,0,1,.18447-.62373,3.48858,3.48858,0,0,1,.30656-.57064,3.197,3.197,0,0,1,.91436-.91568,3.44685,3.44685,0,0,1,.572-.30523,3.344,3.344,0,0,1,.62372-.18578,3.06907,3.06907,0,0,1,1.30053,0,3.22332,3.22332,0,0,1,1.19436.491,5.02835,5.02835,0,0,1,.50429.41139,4.8801,4.8801,0,0,1,.41139.50429,3.38246,3.38246,0,0,1,.30522.57064,3.47806,3.47806,0,0,1,.25215,1.274A3.36394,3.36394,0,0,1,426.36,446.865a5.02708,5.02708,0,0,1-.50429.4114,3.3057,3.3057,0,0,1-1.84463.55737m26.54143-1.65884a3.38754,3.38754,0,0,1-2.35024-.96877,5.04185,5.04185,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.38659,3.38659,0,0,1,.96744-2.34892,5.02559,5.02559,0,0,1,.50429-.41139,3.44685,3.44685,0,0,1,.572-.30523,3.3432,3.3432,0,0,1,.62373-.18579,3.06952,3.06952,0,0,1,1.30052,0,3.22356,3.22356,0,0,1,1.19436.491,5.02559,5.02559,0,0,1,.50429.41139,3.38792,3.38792,0,0,1,.96876,2.34892,3.72635,3.72635,0,0,1-.06636.65026,3.37387,3.37387,0,0,1-.18579.62373,4.71469,4.71469,0,0,1-.30522.57064,4.8801,4.8801,0,0,1-.41139.50429,5.02559,5.02559,0,0,1-.50429.41139,3.30547,3.30547,0,0,1-1.84463.55737" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
@@ -154,7 +154,7 @@ const config = {
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'],
|
||||
testMatch: ['**/tests/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"useWorkspaces": true,
|
||||
"version": "independent",
|
||||
"packages": ["packages/*"],
|
||||
"npmClient": "yarn",
|
||||
|
||||
71
package.json
@@ -6,64 +6,65 @@
|
||||
"apps/*"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "yarn workspace playground start",
|
||||
"start:story": "yarn workspace storybook storybook",
|
||||
"start": "yarn workspace playground dev",
|
||||
"start:docs": "yarn workspace storybook storybook",
|
||||
"start:site": "yarn workspace website start",
|
||||
"deploy:site": "yarn workspace website deploy",
|
||||
"build": "lerna run build",
|
||||
"build:site": "yarn workspace website build",
|
||||
"typedoc": "typedoc --options typedoc.json",
|
||||
"typedoc": "typedoc",
|
||||
"docs": "yarn typedoc && open docs/index.html",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"eslint": "eslint packages/**/src/*.{ts,tsx} --cache",
|
||||
"publish": "lerna publish",
|
||||
"ver": "lerna version --no-private --conventional-commits",
|
||||
"release": "yarn eslint && yarn build && yarn run ver && lerna publish from-git",
|
||||
"ver": "lerna version --no-private",
|
||||
"release": "yarn eslint && yarn build && lerna version --no-private --conventional-commits && lerna publish from-git",
|
||||
"release:npm": "yarn eslint && yarn build && yarn run ver && lerna publish from-git",
|
||||
"release:beta": "yarn eslint && yarn build && yarn run ver && lerna publish from-package --dist-tag beta",
|
||||
"up": "yarn upgrade-interactive --latest",
|
||||
"postinstall": "husky install"
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.19.3",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@commitlint/cli": "^17.5.0",
|
||||
"@commitlint/config-conventional": "^17.3.0",
|
||||
"@types/jest": "^29.2.4",
|
||||
"@types/lodash-es": "^4.17.8",
|
||||
"@types/lodash.get": "^4.4.7",
|
||||
"@types/lodash.isequal": "^4.5.6",
|
||||
"@types/lodash.set": "^4.3.7",
|
||||
"@types/react": "^18.2.7",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"@typescript-eslint/eslint-plugin": "^6.4.1",
|
||||
"@typescript-eslint/parser": "^6.4.1",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"@babel/preset-env": "^7.23.6",
|
||||
"@babel/preset-react": "^7.23.3",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@commitlint/cli": "^18.4.3",
|
||||
"@commitlint/config-conventional": "^18.4.3",
|
||||
"@types/jest": "^29.5.7",
|
||||
"@types/lodash-es": "^4.17.10",
|
||||
"@types/lodash.get": "^4.4.8",
|
||||
"@types/lodash.isequal": "^4.5.7",
|
||||
"@types/lodash.set": "^4.3.8",
|
||||
"@types/react": "^18.2.34",
|
||||
"@types/react-dom": "^18.2.14",
|
||||
"@types/styled-components": "^5.1.29",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||
"@typescript-eslint/parser": "^6.13.2",
|
||||
"conventional-changelog-cli": "^4.1.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-config-ali": "^14.0.2",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"http-server": "^14.1.0",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.1.1",
|
||||
"jest-environment-jsdom": "^29.1.1",
|
||||
"lerna": "^6.6.1",
|
||||
"lint-staged": "^14.0.1",
|
||||
"prettier": "^2.8.7",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"lerna": "^8.0.0",
|
||||
"lint-staged": "^15.2.0",
|
||||
"prettier": "^3.1.0",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"styled-components": "^5.3.6",
|
||||
"typedoc": "^0.25.0",
|
||||
"typescript": "^5.0.4"
|
||||
"typedoc": "^0.25.4",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"@yarnpkg/parsers": "3.0.0-rc.48.1"
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
|
||||
@@ -3,6 +3,76 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.0.0-alpha.6](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0-alpha.5...@music163/tango-context@1.0.0-alpha.6) (2024-01-16)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
# [1.0.0-alpha.5](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0-alpha.4...@music163/tango-context@1.0.0-alpha.5) (2023-12-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- refactor VariableTree & Workspace ([#83](https://github.com/netease/tango/issues/83)) ([8c07821](https://github.com/netease/tango/commit/8c07821d93cea4dfc43f81ca948b845176821184))
|
||||
|
||||
# [1.0.0-alpha.4](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0-alpha.3...@music163/tango-context@1.0.0-alpha.4) (2023-12-26)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
# [1.0.0-alpha.3](https://github.com/netease/tango/compare/@music163/tango-context@1.0.0-alpha.2...@music163/tango-context@1.0.0-alpha.3) (2023-12-25)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
# [1.0.0-alpha.1](https://github.com/netease/tango/compare/@music163/tango-context@0.2.12...@music163/tango-context@1.0.0-alpha.1) (2023-12-12)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.12](https://github.com/netease/tango/compare/@music163/tango-context@0.2.11...@music163/tango-context@0.2.12) (2023-12-04)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.11](https://github.com/netease/tango/compare/@music163/tango-context@0.2.10...@music163/tango-context@0.2.11) (2023-12-04)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.10](https://github.com/netease/tango/compare/@music163/tango-context@0.2.9...@music163/tango-context@0.2.10) (2023-11-30)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.9](https://github.com/netease/tango/compare/@music163/tango-context@0.2.8...@music163/tango-context@0.2.9) (2023-11-29)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.8](https://github.com/netease/tango/compare/@music163/tango-context@0.2.7...@music163/tango-context@0.2.8) (2023-11-27)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.7](https://github.com/netease/tango/compare/@music163/tango-context@0.2.6...@music163/tango-context@0.2.7) (2023-11-23)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.6](https://github.com/netease/tango/compare/@music163/tango-context@0.2.5...@music163/tango-context@0.2.6) (2023-11-20)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.5](https://github.com/netease/tango/compare/@music163/tango-context@0.2.4...@music163/tango-context@0.2.5) (2023-11-16)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.4](https://github.com/netease/tango/compare/@music163/tango-context@0.2.3...@music163/tango-context@0.2.4) (2023-11-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- optional context config ([70fc6f8](https://github.com/netease/tango/commit/70fc6f8030202f5d340281c0424752044ed0fb83))
|
||||
|
||||
## [0.2.3](https://github.com/netease/tango/compare/@music163/tango-context@0.2.2...@music163/tango-context@0.2.3) (2023-10-23)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## [0.2.2](https://github.com/netease/tango/compare/@music163/tango-context@0.2.1...@music163/tango-context@0.2.2) (2023-10-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- refactor expSetter ([#41](https://github.com/netease/tango/issues/41)) ([d63517e](https://github.com/netease/tango/commit/d63517ecb936e4227e70c33e610664316625f4f4))
|
||||
|
||||
## [0.2.1](https://github.com/netease/tango/compare/@music163/tango-context@0.2.0...@music163/tango-context@0.2.1) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-context",
|
||||
"version": "0.2.1",
|
||||
"version": "1.0.0-alpha.6",
|
||||
"description": "react context for tango-apps",
|
||||
"keywords": [
|
||||
"react",
|
||||
@@ -31,9 +31,9 @@
|
||||
"react": ">= 16.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@music163/tango-core": "^0.2.1",
|
||||
"@music163/tango-helpers": "^0.1.3",
|
||||
"mobx-react-lite": "4.0.2"
|
||||
"@music163/tango-core": "^1.0.0-alpha.6",
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.2",
|
||||
"mobx-react-lite": "4.0.5"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import type { Engine } from '@music163/tango-core';
|
||||
import { createContext } from '@music163/tango-helpers';
|
||||
import { IVariableTreeNode, createContext } from '@music163/tango-helpers';
|
||||
|
||||
export interface ITangoEngineContext {
|
||||
/**
|
||||
* 低代码引擎
|
||||
*/
|
||||
engine: Engine;
|
||||
/**
|
||||
* 自定义配置数据
|
||||
*/
|
||||
config?: {
|
||||
customActionVariables?: IVariableTreeNode[];
|
||||
customExpressionVariables?: IVariableTreeNode[];
|
||||
};
|
||||
}
|
||||
|
||||
const [TangoEngineProvider, useTangoEngine] = createContext<ITangoEngineContext>({
|
||||
@@ -22,49 +29,28 @@ export const useDesigner = () => {
|
||||
return useTangoEngine()?.engine.designer;
|
||||
};
|
||||
|
||||
const builtinHelpers = [
|
||||
{
|
||||
title: 'setStoreValue',
|
||||
key: '() => tango.setStoreValue("variableName", "variableValue")',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
title: 'getStoreValue',
|
||||
key: '() => tango.getStoreValue("variableName")',
|
||||
type: 'function',
|
||||
},
|
||||
{ title: 'openModal', key: '() => tango.openModal("")', type: 'function' },
|
||||
{ title: 'closeModal', key: '() => tango.closeModal("")', type: 'function' },
|
||||
{ title: 'navigateTo', key: '() => tango.navigateTo("/")', type: 'function' },
|
||||
{ title: 'showToast', key: '() => tango.showToast("hello")', type: 'function' },
|
||||
{ title: 'formatDate', key: '() => tango.formatDate("2022-12-12")', type: 'function' },
|
||||
{ title: 'formatNumber', key: '() => tango.formatDate(9999)', type: 'function' },
|
||||
{
|
||||
title: 'copyToClipboard',
|
||||
key: '() => tango.copyToClipboard("hello")',
|
||||
type: 'function',
|
||||
},
|
||||
];
|
||||
|
||||
export const useWorkspaceData = () => {
|
||||
const ctx = useTangoEngine();
|
||||
const workspace = useWorkspace();
|
||||
const modelVariables: any[] = []; // 绑定变量列表
|
||||
const storeActionVariables: any[] = []; // 模型中的所有 actions
|
||||
const storeVariables: any[] = []; // 模型中的所有变量
|
||||
const serviceVariables: any[] = []; // 服务中的所有变量
|
||||
const modelVariables: IVariableTreeNode[] = []; // 绑定变量列表
|
||||
const storeActionVariables: IVariableTreeNode[] = []; // 模型中的所有 actions
|
||||
const storeVariables: IVariableTreeNode[] = []; // 模型中的所有变量
|
||||
const serviceVariables: IVariableTreeNode[] = []; // 服务中的所有变量
|
||||
|
||||
Object.values(workspace.storeModules).forEach((file) => {
|
||||
const prefix = `stores.${file.name}`;
|
||||
const states = file.states.map((item) => ({
|
||||
const states: IVariableTreeNode[] = file.states.map((item) => ({
|
||||
title: item.name,
|
||||
key: `${prefix}.${item.name}`,
|
||||
raw: item.code,
|
||||
showRemoveButton: true,
|
||||
}));
|
||||
const actions = file.actions.map((item) => ({
|
||||
const actions: IVariableTreeNode[] = file.actions.map((item) => ({
|
||||
title: item.name,
|
||||
key: `${prefix}.${item.name}`,
|
||||
type: 'function',
|
||||
raw: item.code,
|
||||
showRemoveButton: true,
|
||||
}));
|
||||
|
||||
modelVariables.push({
|
||||
@@ -72,6 +58,7 @@ export const useWorkspaceData = () => {
|
||||
key: prefix,
|
||||
selectable: false,
|
||||
children: states,
|
||||
showAddButton: true,
|
||||
});
|
||||
|
||||
storeActionVariables.push({
|
||||
@@ -86,8 +73,7 @@ export const useWorkspaceData = () => {
|
||||
key: prefix,
|
||||
selectable: false,
|
||||
children: [...states, ...actions],
|
||||
showAddChildIcon: true,
|
||||
showRemoveIcon: true,
|
||||
showAddButton: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,10 +83,12 @@ export const useWorkspaceData = () => {
|
||||
title: file.name,
|
||||
key: prefix,
|
||||
selectable: false,
|
||||
showAddButton: true,
|
||||
children: Object.keys(file.serviceFunctions || {}).map((key) => ({
|
||||
title: key,
|
||||
key: [prefix, key].join('.'),
|
||||
type: 'function',
|
||||
showRemoveButton: true,
|
||||
})),
|
||||
});
|
||||
});
|
||||
@@ -111,25 +99,34 @@ export const useWorkspaceData = () => {
|
||||
value: item.path,
|
||||
}));
|
||||
|
||||
let actionVariables: IVariableTreeNode[] = [
|
||||
buildVariableOptions('数据模型', '$stores', storeActionVariables),
|
||||
buildVariableOptions('服务函数', '$services', serviceVariables),
|
||||
];
|
||||
|
||||
if (ctx.config?.customActionVariables) {
|
||||
actionVariables = actionVariables.concat(ctx.config?.customActionVariables);
|
||||
}
|
||||
|
||||
let expressionVariables: IVariableTreeNode[] = [
|
||||
buildVariableOptions('数据模型', '$stores', storeVariables),
|
||||
buildVariableOptions('服务函数', '$services', serviceVariables),
|
||||
];
|
||||
if (ctx.config?.customExpressionVariables) {
|
||||
expressionVariables = expressionVariables.concat(ctx.config?.customExpressionVariables);
|
||||
}
|
||||
|
||||
return {
|
||||
modelVariables: [buildVariableOptions('数据模型', 'stores', modelVariables)],
|
||||
actionVariables: [
|
||||
buildVariableOptions('数据模型', 'stores', storeActionVariables),
|
||||
buildVariableOptions('服务函数', 'services', serviceVariables),
|
||||
buildVariableOptions('工具函数', 'helpers', builtinHelpers),
|
||||
],
|
||||
actionVariables,
|
||||
storeVariables,
|
||||
serviceVariables,
|
||||
expressionVariables: [
|
||||
buildVariableOptions('数据模型', 'stores', storeVariables),
|
||||
buildVariableOptions('服务函数', 'services', serviceVariables),
|
||||
buildVariableOptions('工具函数', 'helpers', builtinHelpers),
|
||||
],
|
||||
expressionVariables,
|
||||
routeOptions,
|
||||
};
|
||||
};
|
||||
|
||||
function buildVariableOptions(title: string, key: string, children: any[]) {
|
||||
function buildVariableOptions(title: string, key: string, children: IVariableTreeNode[]) {
|
||||
return {
|
||||
key,
|
||||
title,
|
||||
|
||||
@@ -3,6 +3,107 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.0.0-alpha.6](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0-alpha.5...@music163/tango-core@1.0.0-alpha.6) (2024-01-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- 优化变量树实现 ([#90](https://github.com/netease/tango/issues/90)) ([62d403f](https://github.com/netease/tango/commit/62d403f80a5ad08c216bc0c035ffc12c2cf329d2))
|
||||
|
||||
# [1.0.0-alpha.5](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0-alpha.4...@music163/tango-core@1.0.0-alpha.5) (2023-12-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- check if store value is valid ([765ea07](https://github.com/netease/tango/commit/765ea07cd5be6528e3b326e4ecb163647774d9e5))
|
||||
- refactor VariableTree & Workspace ([#83](https://github.com/netease/tango/issues/83)) ([8c07821](https://github.com/netease/tango/commit/8c07821d93cea4dfc43f81ca948b845176821184))
|
||||
|
||||
# [1.0.0-alpha.4](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0-alpha.3...@music163/tango-core@1.0.0-alpha.4) (2023-12-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- list overflow in expSetter ([4d6b67e](https://github.com/netease/tango/commit/4d6b67eecd02b31f01d1bc896c6eeb83f6b34b35))
|
||||
|
||||
# [1.0.0-alpha.3](https://github.com/netease/tango/compare/@music163/tango-core@1.0.0-alpha.2...@music163/tango-core@1.0.0-alpha.3) (2023-12-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- export componentEntry module ([149002d](https://github.com/netease/tango/commit/149002ddddaee859333469f65c054d811bde2f7f))
|
||||
|
||||
# [1.0.0-alpha.1](https://github.com/netease/tango/compare/@music163/tango-core@0.2.11...@music163/tango-core@1.0.0-alpha.1) (2023-12-12)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-core
|
||||
|
||||
## [0.2.11](https://github.com/netease/tango/compare/@music163/tango-core@0.2.10...@music163/tango-core@0.2.11) (2023-12-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- update FileType ([c884712](https://github.com/netease/tango/commit/c8847123312bc47ea06575f4e714dfd596893e39))
|
||||
|
||||
## [0.2.10](https://github.com/netease/tango/compare/@music163/tango-core@0.2.9...@music163/tango-core@0.2.10) (2023-12-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- add filesFormatter to sandbox & fix update props with imports ([#71](https://github.com/netease/tango/issues/71)) ([138cbe9](https://github.com/netease/tango/commit/138cbe9b203b370aff42c1ae8086d69edacf35e9))
|
||||
|
||||
## [0.2.9](https://github.com/netease/tango/compare/@music163/tango-core@0.2.8...@music163/tango-core@0.2.9) (2023-11-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fix render components tree ([76a5a2c](https://github.com/netease/tango/commit/76a5a2c65920bc42b019cd1f32a3cacd0d888638))
|
||||
|
||||
## [0.2.8](https://github.com/netease/tango/compare/@music163/tango-core@0.2.7...@music163/tango-core@0.2.8) (2023-11-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- workspace onFilesChange ([#70](https://github.com/netease/tango/issues/70)) ([accd826](https://github.com/netease/tango/commit/accd8263764c811ea8175a9cd341fc6fa6c75967))
|
||||
|
||||
## [0.2.7](https://github.com/netease/tango/compare/@music163/tango-core@0.2.6...@music163/tango-core@0.2.7) (2023-11-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- list service functions ([91b53da](https://github.com/netease/tango/commit/91b53da38e362ae320457429037dc347adf90bd3))
|
||||
- parse tango variables in view file ([d917633](https://github.com/netease/tango/commit/d91763379c4ccf9d826b717aa80a07abdaf2e3a2))
|
||||
|
||||
## [0.2.6](https://github.com/netease/tango/compare/@music163/tango-core@0.2.5...@music163/tango-core@0.2.6) (2023-11-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- prototype to import declaration ([#67](https://github.com/netease/tango/issues/67)) ([8a1234e](https://github.com/netease/tango/commit/8a1234e565489c92f041356b7a06339eaeee48df))
|
||||
- refactor update import specifiers ([#68](https://github.com/netease/tango/issues/68)) ([558f2cd](https://github.com/netease/tango/commit/558f2cd0a692c6bbc866d08250d25e2619af183f))
|
||||
|
||||
## [0.2.5](https://github.com/netease/tango/compare/@music163/tango-core@0.2.4...@music163/tango-core@0.2.5) (2023-11-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- refactor parse expression ([#61](https://github.com/netease/tango/issues/61)) ([dbbd1dd](https://github.com/netease/tango/commit/dbbd1dddc75c532b7c9710ab0941c8680100f093))
|
||||
|
||||
## [0.2.4](https://github.com/netease/tango/compare/@music163/tango-core@0.2.3...@music163/tango-core@0.2.4) (2023-11-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- allow override existing file via addFile ([6cf673e](https://github.com/netease/tango/commit/6cf673e7f6f330613e1d6331256a59900e8cbc68))
|
||||
- export isJSXElementById from traverse ([#54](https://github.com/netease/tango/issues/54)) ([208e275](https://github.com/netease/tango/commit/208e275bb1cd98ed21d816c26179f9ebc11df223))
|
||||
- parse variables from view ([089a482](https://github.com/netease/tango/commit/089a482f750e9d9a7743a6641d6e39989347d318))
|
||||
- update types ([653386d](https://github.com/netease/tango/commit/653386dcc0b064b41915548129952eeabe53019f))
|
||||
|
||||
## [0.2.3](https://github.com/netease/tango/compare/@music163/tango-core@0.2.2...@music163/tango-core@0.2.3) (2023-11-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- add onFilesChange to workspace ([#50](https://github.com/netease/tango/issues/50)) ([d775a50](https://github.com/netease/tango/commit/d775a5003ab1a14f801d3b38f7187ea15ef7d74d))
|
||||
- **core:** create store without entry file ([ae806ae](https://github.com/netease/tango/commit/ae806aee3583ef5f957836e15d715035c4556867))
|
||||
- **core:** logging drop action ([963bfe1](https://github.com/netease/tango/commit/963bfe1ab80fa4114637963534b2891a27538120))
|
||||
- designer model add defaultActiveSidebarPanel ([#51](https://github.com/netease/tango/issues/51)) ([9dd11f7](https://github.com/netease/tango/commit/9dd11f7a6d5807c80a63c84c1ad0df5bc1ce9558))
|
||||
- refactor workspace onFilesChange ([f53ce94](https://github.com/netease/tango/commit/f53ce94c6ed777d45ba6137188bfbc4566e03942))
|
||||
- update types ([46d5f59](https://github.com/netease/tango/commit/46d5f59b4cd4fa71ed247f5e470e66c8c9f6d4a0))
|
||||
|
||||
## [0.2.2](https://github.com/netease/tango/compare/@music163/tango-core@0.2.1...@music163/tango-core@0.2.2) (2023-10-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- add listImportSources to viewModule ([6bbbe63](https://github.com/netease/tango/commit/6bbbe634ad52acaf06a0d776aeff450993f7b69a))
|
||||
- enhance expSetter ([#43](https://github.com/netease/tango/issues/43)) ([5ebbb42](https://github.com/netease/tango/commit/5ebbb428fb3fb786d330ab01959028443338d315))
|
||||
- update interface ([0b3e632](https://github.com/netease/tango/commit/0b3e632745e30d578268458329d5702dbd729010))
|
||||
|
||||
## [0.2.1](https://github.com/netease/tango/compare/@music163/tango-core@0.2.0...@music163/tango-core@0.2.1) (2023-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-core",
|
||||
"version": "0.2.1",
|
||||
"version": "1.0.0-alpha.6",
|
||||
"description": "tango core",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"homepage": "",
|
||||
@@ -24,14 +24,14 @@
|
||||
"prepublishOnly": "yarn build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/generator": "^7.22.15",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/traverse": "^7.22.15",
|
||||
"@babel/types": "^7.22.15",
|
||||
"@music163/tango-helpers": "^0.1.3",
|
||||
"@types/babel__generator": "^7.6.4",
|
||||
"@types/babel__traverse": "^7.18.3",
|
||||
"mobx": "6.9.0",
|
||||
"@babel/generator": "^7.23.5",
|
||||
"@babel/parser": "^7.23.5",
|
||||
"@babel/traverse": "^7.23.5",
|
||||
"@babel/types": "^7.23.5",
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.2",
|
||||
"@types/babel__generator": "^7.6.7",
|
||||
"@types/babel__traverse": "^7.20.4",
|
||||
"mobx": "6.12.0",
|
||||
"path-browserify": "^1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -10,6 +10,10 @@ interface ICreateEngineOptions {
|
||||
* 默认的模拟器模式
|
||||
*/
|
||||
defaultSimulatorMode?: SimulatorNameType;
|
||||
/**
|
||||
* 默认激活的侧边栏
|
||||
*/
|
||||
defaultActiveSidebarPanel?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,12 +24,14 @@ interface ICreateEngineOptions {
|
||||
export function createEngine({
|
||||
workspace,
|
||||
defaultSimulatorMode = 'desktop',
|
||||
defaultActiveSidebarPanel = '',
|
||||
}: ICreateEngineOptions) {
|
||||
const engine = new Engine({
|
||||
workspace,
|
||||
designer: new Designer({
|
||||
workspace,
|
||||
simulator: defaultSimulatorMode,
|
||||
activeSidebarPanel: defaultActiveSidebarPanel,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isValidExpressionCode } from './ast';
|
||||
|
||||
const defineServiceHandlerNames = ['defineServices', 'createServices'];
|
||||
const sfHandlerPattern = new RegExp(`^(${defineServiceHandlerNames.join('|')})$`);
|
||||
|
||||
@@ -20,3 +22,28 @@ const defineStoreHandlerName = 'defineStore';
|
||||
export function isDefineStore(name: string) {
|
||||
return defineStoreHandlerName === name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是 tango 的变量引用
|
||||
* @example tango.stores.app.name
|
||||
* @example tango.stores?.app?.name
|
||||
*
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
export function isTangoVariable(name: string) {
|
||||
return /^tango\??\.(stores|services)\??\./.test(name) && name.split('.').length > 2;
|
||||
}
|
||||
|
||||
const templatePattern = /^{(.+)}$/s;
|
||||
|
||||
/**
|
||||
* 判断给定字符串是否被表达式容器`{expCode}`包裹
|
||||
* @param code
|
||||
*/
|
||||
export function isWrappedByExpressionContainer(code: string, isStrict = true) {
|
||||
if (isStrict && isValidExpressionCode(code)) {
|
||||
return false;
|
||||
}
|
||||
return templatePattern.test(code);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import * as t from '@babel/types';
|
||||
import {
|
||||
logger,
|
||||
isValidObjectString,
|
||||
isVariableString,
|
||||
getVariableContent,
|
||||
isPlainObject,
|
||||
} from '@music163/tango-helpers';
|
||||
import { isWrappedByExpressionContainer } from '../assert';
|
||||
|
||||
// @see https://babeljs.io/docs/en/babel-parser#pluginss
|
||||
const babelParserConfig: ParserOptions = {
|
||||
@@ -22,7 +22,6 @@ const babelParserConfig: ParserOptions = {
|
||||
'classProperties',
|
||||
'asyncGenerators',
|
||||
'functionBind',
|
||||
'functionSent',
|
||||
'dynamicImport',
|
||||
'optionalChaining',
|
||||
],
|
||||
@@ -44,6 +43,12 @@ export function isValidCode(code: string) {
|
||||
|
||||
/**
|
||||
* 检测代码是否是合法的表达式代码
|
||||
* 表达式是一组代码的集合,它返回一个值;每一个合法的表达式都能计算成某个值
|
||||
* 只要你输入这段代码,可以形成一个值,就算是表达式
|
||||
* @example x = 1; // 1
|
||||
* @example 1=1
|
||||
* @example 'hello'
|
||||
* @example { foo: 'bar' }
|
||||
* @param code
|
||||
* @returns
|
||||
*/
|
||||
@@ -91,8 +96,8 @@ export function code2expression(code: string) {
|
||||
try {
|
||||
expNode = t.cloneNode(parseExpression(code, babelParserConfig), false, true);
|
||||
} catch (err) {
|
||||
console.error('invalid code', err);
|
||||
expNode = t.identifier('undefined');
|
||||
logger.error('invalid code', err);
|
||||
// expNode = t.identifier('undefined');
|
||||
}
|
||||
return expNode;
|
||||
}
|
||||
@@ -103,7 +108,7 @@ export function code2expression(code: string) {
|
||||
* @returns File
|
||||
*/
|
||||
export function expressionCode2ast(code: string) {
|
||||
if (isVariableString(code)) {
|
||||
if (isWrappedByExpressionContainer(code)) {
|
||||
code = getVariableContent(code);
|
||||
}
|
||||
const node = code2expression(code);
|
||||
@@ -130,7 +135,7 @@ export function value2node(
|
||||
ret = t.numericLiteral(value);
|
||||
break;
|
||||
case 'string':
|
||||
if (isVariableString(value)) {
|
||||
if (isWrappedByExpressionContainer(value)) {
|
||||
// 再检查是否是表达式容器,例如 {this.foo}, {1}
|
||||
const innerString = getVariableContent(value);
|
||||
ret = code2expression(innerString);
|
||||
@@ -201,7 +206,7 @@ export function value2jsxAttributeValueNode(value: any) {
|
||||
if (isValidObjectString(value)) {
|
||||
// 先检查是否是对象字符串
|
||||
ret = t.jsxExpressionContainer(code2expression(value));
|
||||
} else if (isVariableString(value)) {
|
||||
} else if (isWrappedByExpressionContainer(value)) {
|
||||
// 再检查是否是表达式容器,例如 {this.foo}, {1}
|
||||
const innerString = getVariableContent(value);
|
||||
ret = t.jsxExpressionContainer(code2expression(innerString));
|
||||
@@ -221,7 +226,7 @@ export function value2jsxChildrenValueNode(value: any) {
|
||||
let ret: t.JSXElement | t.JSXFragment | t.JSXExpressionContainer | t.JSXSpreadChild | t.JSXText;
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
if (isVariableString(value)) {
|
||||
if (isWrappedByExpressionContainer(value)) {
|
||||
const innerString = getVariableContent(value);
|
||||
ret = t.jsxExpressionContainer(code2expression(innerString));
|
||||
} else {
|
||||
|
||||
@@ -22,16 +22,16 @@ import {
|
||||
code2expression,
|
||||
object2node,
|
||||
} from './parse';
|
||||
import { isValidComponentName } from '../string';
|
||||
import { isDefineService, isDefineStore } from '../assert';
|
||||
import { getFullPath, isValidComponentName } from '../string';
|
||||
import { isDefineService, isDefineStore, isTangoVariable } from '../assert';
|
||||
import type {
|
||||
IRouteData,
|
||||
IStorePropertyData,
|
||||
IClassPropertyNodeData,
|
||||
ITangoViewNodeData,
|
||||
IImportDeclarationPayload,
|
||||
IServiceFunctionPayload,
|
||||
InsertChildPositionType,
|
||||
IImportSpecifierData,
|
||||
IExportSpecifierData,
|
||||
} from '../../types';
|
||||
import { IdGenerator } from '../id-generator';
|
||||
|
||||
@@ -194,7 +194,7 @@ export function queryXFormItemFields(ast: t.File | t.JSXElement) {
|
||||
* @param node
|
||||
* @param id
|
||||
*/
|
||||
function isJSXElementById(node: t.JSXElement, jsxElementNodeId: string) {
|
||||
export function isJSXElementById(node: t.JSXElement, jsxElementNodeId: string) {
|
||||
let isTargetJSXElement = false;
|
||||
visitJSXElementAttributes(node, (name, value) => {
|
||||
if (name === SLOT.dnd && value === jsxElementNodeId) {
|
||||
@@ -448,11 +448,12 @@ function getLastExportDeclarationIndex(node: t.Program) {
|
||||
|
||||
/**
|
||||
* 生成新的 ImportDeclaration
|
||||
* @deprecated 使用 makeImportDeclaration2 代替
|
||||
* @param importedModule
|
||||
* @param importedSourcePath
|
||||
* @returns
|
||||
*/
|
||||
export function makeImportDeclaration(importedModule: IImportDeclarationPayload) {
|
||||
export function makeImportDeclarationLegacy(importedModule: IImportDeclarationPayload) {
|
||||
const specifierNodes =
|
||||
importedModule.specifiers?.map((localName) =>
|
||||
t.importSpecifier(t.identifier(localName), t.identifier(localName)),
|
||||
@@ -467,6 +468,36 @@ export function makeImportDeclaration(importedModule: IImportDeclarationPayload)
|
||||
return t.importDeclaration(newSpecifierNodes, t.stringLiteral(importedModule.sourcePath));
|
||||
}
|
||||
|
||||
function specifierData2node(data: IImportSpecifierData) {
|
||||
switch (data.type) {
|
||||
case 'ImportDefaultSpecifier':
|
||||
return t.importDefaultSpecifier(t.identifier(data.localName));
|
||||
case 'ImportSpecifier':
|
||||
return t.importSpecifier(
|
||||
t.identifier(data.importedName || data.localName),
|
||||
t.identifier(data.localName),
|
||||
);
|
||||
case 'ImportNamespaceSpecifier':
|
||||
return t.importNamespaceSpecifier(t.identifier(data.localName));
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function specifierDataList2nodes(specifiers: IImportSpecifierData[]) {
|
||||
return specifiers.map((item) => specifierData2node(item)).filter((item) => !!item);
|
||||
}
|
||||
|
||||
export function makeImportDeclaration(source: string, specifiers: IImportSpecifierData[]) {
|
||||
const specifierNodes = specifierDataList2nodes(specifiers);
|
||||
return t.importDeclaration(specifierNodes, t.stringLiteral(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 使用 parseImportDeclaration 代替
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
function getImportDeclarationData(node: t.ImportDeclaration): IImportDeclarationPayload {
|
||||
const sourcePath = node2value(node.source);
|
||||
let defaultSpecifier;
|
||||
@@ -487,16 +518,33 @@ function getImportDeclarationData(node: t.ImportDeclaration): IImportDeclaration
|
||||
|
||||
/**
|
||||
* 添加新的导入语句
|
||||
* @deprecated 使用 addImportDeclaration2 代替
|
||||
* @param ast
|
||||
* @param importedModule
|
||||
* @param importedSourcePath
|
||||
* @returns
|
||||
*/
|
||||
export function addImportDeclaration(ast: t.File, importedModule: IImportDeclarationPayload) {
|
||||
export function addImportDeclarationLegacy(ast: t.File, importedModule: IImportDeclarationPayload) {
|
||||
traverse(ast, {
|
||||
Program(path) {
|
||||
const lastIndex = getLastImportDeclarationIndex(path.node);
|
||||
const newImportDeclaration = makeImportDeclaration(importedModule);
|
||||
const newImportDeclaration = makeImportDeclarationLegacy(importedModule);
|
||||
path.node.body.splice(lastIndex, 0, newImportDeclaration);
|
||||
path.stop();
|
||||
},
|
||||
});
|
||||
return ast;
|
||||
}
|
||||
|
||||
export function addImportDeclaration(
|
||||
ast: t.File,
|
||||
source: string,
|
||||
specifiers: IImportSpecifierData[],
|
||||
) {
|
||||
traverse(ast, {
|
||||
Program(path) {
|
||||
const lastIndex = getLastImportDeclarationIndex(path.node);
|
||||
const newImportDeclaration = makeImportDeclaration(source, specifiers);
|
||||
path.node.body.splice(lastIndex, 0, newImportDeclaration);
|
||||
path.stop();
|
||||
},
|
||||
@@ -506,17 +554,21 @@ export function addImportDeclaration(ast: t.File, importedModule: IImportDeclara
|
||||
|
||||
/**
|
||||
* 更新已有的导入语句
|
||||
* @deprecated 使用 updateImportDeclaration2 代替
|
||||
* @param ast
|
||||
* @param importedModule
|
||||
* @param importedSourcePath
|
||||
* @returns
|
||||
*/
|
||||
export function updateImportDeclaration(ast: t.File, importedModule: IImportDeclarationPayload) {
|
||||
export function updateImportDeclarationLegacy(
|
||||
ast: t.File,
|
||||
importedModule: IImportDeclarationPayload,
|
||||
) {
|
||||
traverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
const currentSourcePath = node2value(path.node.source);
|
||||
if (currentSourcePath === importedModule.sourcePath) {
|
||||
const newImportDeclaration = makeImportDeclaration(importedModule);
|
||||
const newImportDeclaration = makeImportDeclarationLegacy(importedModule);
|
||||
path.replaceWith(newImportDeclaration);
|
||||
path.stop(); // 只修改匹配到的第一条
|
||||
}
|
||||
@@ -525,6 +577,50 @@ export function updateImportDeclaration(ast: t.File, importedModule: IImportDecl
|
||||
return ast;
|
||||
}
|
||||
|
||||
export function updateImportDeclaration(
|
||||
ast: t.File,
|
||||
source: string,
|
||||
specifiers: IImportSpecifierData[],
|
||||
) {
|
||||
traverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
const currentSourcePath = node2value(path.node.source);
|
||||
if (currentSourcePath === source) {
|
||||
const newImportDeclaration = makeImportDeclaration(source, specifiers);
|
||||
path.replaceWith(newImportDeclaration);
|
||||
path.stop(); // 只修改匹配到的第一条
|
||||
}
|
||||
},
|
||||
});
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* 再已有的导入语句中添加新的导入符号
|
||||
* @param ast
|
||||
* @param source
|
||||
* @param newSpecifiers
|
||||
* @returns
|
||||
*/
|
||||
export function insertImportSpecifiers(
|
||||
ast: t.File,
|
||||
source: string,
|
||||
newSpecifiers: IImportSpecifierData[],
|
||||
) {
|
||||
traverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
const currentSourcePath = node2value(path.node.source);
|
||||
if (currentSourcePath === source) {
|
||||
const nodes = specifierDataList2nodes(newSpecifiers);
|
||||
path.node.specifiers.push(...nodes);
|
||||
// 只在匹配到的第一个导入声明语句添加即可,不再重复执行
|
||||
path.stop();
|
||||
}
|
||||
},
|
||||
});
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增类属性
|
||||
* @param ast
|
||||
@@ -592,7 +688,6 @@ export function addRouteToRouteFile(ast: t.File, routePath: string, importFilePa
|
||||
const newNode = t.objectExpression([
|
||||
t.objectProperty(t.identifier('path'), t.stringLiteral(routePath)),
|
||||
t.objectProperty(t.identifier('component'), t.identifier(component)),
|
||||
// FIXME: tango/issues#144 https://g.hz.netease.com/tango/issues/-/issues/144
|
||||
t.objectProperty(t.identifier('exact'), t.booleanLiteral(true)),
|
||||
]);
|
||||
path.node.elements.push(newNode);
|
||||
@@ -902,7 +997,7 @@ export function deleteServiceConfigFromServiceFile(ast: t.File, serviceFunctionN
|
||||
* @param payload
|
||||
* @returns
|
||||
*/
|
||||
export function serviceConfig2Node(payload: IServiceFunctionPayload) {
|
||||
export function serviceConfig2Node(payload: object) {
|
||||
return object2node(payload, (value, key) => {
|
||||
if (key === 'formatter' && value) {
|
||||
return code2expression(value);
|
||||
@@ -911,10 +1006,7 @@ export function serviceConfig2Node(payload: IServiceFunctionPayload) {
|
||||
});
|
||||
}
|
||||
|
||||
export function updateServiceConfigToServiceFile(
|
||||
ast: t.File,
|
||||
config: { [key: string]: IServiceFunctionPayload },
|
||||
) {
|
||||
export function updateServiceConfigToServiceFile(ast: t.File, config: Dict<object>) {
|
||||
traverse(ast, {
|
||||
CallExpression(path) {
|
||||
const calleeName = keyNode2value(path.node.callee) as string;
|
||||
@@ -986,7 +1078,12 @@ export function traverseServiceFile(ast: t.File) {
|
||||
const baseConfig: any = {
|
||||
encryptFetch: false,
|
||||
};
|
||||
const imports: Record<string, IImportSpecifierData[]> = {};
|
||||
traverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
const { source, specifiers } = parseImportDeclaration(path.node);
|
||||
imports[source] = specifiers;
|
||||
},
|
||||
CallExpression(path) {
|
||||
const calleeName = keyNode2value(path.node.callee) as string;
|
||||
if (isDefineService(calleeName)) {
|
||||
@@ -1003,6 +1100,7 @@ export function traverseServiceFile(ast: t.File) {
|
||||
},
|
||||
});
|
||||
return {
|
||||
imports,
|
||||
services,
|
||||
baseConfig,
|
||||
};
|
||||
@@ -1107,6 +1205,7 @@ export function cloneJSXElementWithoutTrackingData(node: t.JSXElement) {
|
||||
}
|
||||
|
||||
export function traverseViewFile(ast: t.File, idGenerator: IdGenerator) {
|
||||
const imports: Record<string, IImportSpecifierData[]> = {};
|
||||
const importedModules: Dict<IImportDeclarationPayload | IImportDeclarationPayload[]> = {};
|
||||
const nodes: Array<ITangoViewNodeData<t.JSXElement>> = [];
|
||||
const cloneAst = t.cloneNode(ast, true, true);
|
||||
@@ -1115,12 +1214,43 @@ export function traverseViewFile(ast: t.File, idGenerator: IdGenerator) {
|
||||
|
||||
traverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
const declarationData = getImportDeclarationData(path.node);
|
||||
const exist = importedModules[declarationData.sourcePath];
|
||||
if (!exist) {
|
||||
importedModules[declarationData.sourcePath] = declarationData;
|
||||
const { source, specifiers } = parseImportDeclaration(path.node);
|
||||
if (imports[source]) {
|
||||
// 存在重复的导入语句,合并导入符号列表
|
||||
imports[source] = imports[source].concat(specifiers);
|
||||
} else {
|
||||
importedModules[declarationData.sourcePath] = Array.isArray(exist)
|
||||
imports[source] = specifiers;
|
||||
}
|
||||
|
||||
// FIXME: 下面的逻辑兼容旧的逻辑,后续需要移除掉
|
||||
const declarationData = specifiers.reduce(
|
||||
(prev, cur) => {
|
||||
switch (cur.type) {
|
||||
case 'ImportDefaultSpecifier':
|
||||
prev.defaultSpecifier = cur.localName;
|
||||
break;
|
||||
case 'ImportSpecifier':
|
||||
prev.specifiers.push(cur.localName);
|
||||
break;
|
||||
case 'ImportNamespaceSpecifier':
|
||||
prev.specifiers.push(cur.localName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return prev;
|
||||
},
|
||||
{
|
||||
defaultSpecifier: undefined,
|
||||
specifiers: [],
|
||||
sourcePath: source,
|
||||
},
|
||||
);
|
||||
const exist = importedModules[source];
|
||||
if (!exist) {
|
||||
importedModules[source] = declarationData;
|
||||
} else {
|
||||
importedModules[source] = Array.isArray(exist)
|
||||
? exist.concat([declarationData])
|
||||
: [exist, declarationData];
|
||||
}
|
||||
@@ -1128,15 +1258,23 @@ export function traverseViewFile(ast: t.File, idGenerator: IdGenerator) {
|
||||
|
||||
MemberExpression(path) {
|
||||
const variable = node2value(path.node, false);
|
||||
const parentNode = path.parentPath.node;
|
||||
if (
|
||||
/^tango\.(stores|services)\./.test(variable) &&
|
||||
variable.split('.').length === 3 &&
|
||||
!t.isMemberExpression(parentNode) &&
|
||||
isTangoVariable(variable) &&
|
||||
!variables.includes(variable)
|
||||
) {
|
||||
variables.push(variable);
|
||||
}
|
||||
},
|
||||
|
||||
OptionalMemberExpression(path) {
|
||||
const variable = node2value(path.node, false);
|
||||
if (isTangoVariable(variable) && !variables.includes(variable)) {
|
||||
variables.push(variable);
|
||||
}
|
||||
},
|
||||
|
||||
JSXElement(path) {
|
||||
const attributes = getJSXElementAttributes(path.node);
|
||||
|
||||
@@ -1183,7 +1321,77 @@ export function traverseViewFile(ast: t.File, idGenerator: IdGenerator) {
|
||||
ast,
|
||||
cleanAst,
|
||||
nodes,
|
||||
imports,
|
||||
importedModules,
|
||||
variables,
|
||||
};
|
||||
}
|
||||
|
||||
export function traverseComponentsEntryFile(ast: t.File, baseDir?: string) {
|
||||
const exportMap: Record<string, IExportSpecifierData> = {};
|
||||
traverse(ast, {
|
||||
ExportNamedDeclaration(path) {
|
||||
const node = path.node;
|
||||
let source = node2value(node.source);
|
||||
if (baseDir) {
|
||||
// fix relative source path
|
||||
source = getFullPath(baseDir, source);
|
||||
}
|
||||
node.specifiers.forEach((specifier) => {
|
||||
if (t.isExportSpecifier(specifier)) {
|
||||
const name = keyNode2value(specifier.exported) as string;
|
||||
if (name) {
|
||||
exportMap[name] = {
|
||||
source,
|
||||
exportedName: name,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return { ast, exportMap };
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析导入语句
|
||||
*/
|
||||
function parseImportDeclaration(node: t.ImportDeclaration) {
|
||||
const source = node2value(node.source) as string;
|
||||
const specifiers: IImportSpecifierData[] = [];
|
||||
node.specifiers.forEach((specifierNode) => {
|
||||
const data: IImportSpecifierData = {
|
||||
localName: keyNode2value(specifierNode.local) as string,
|
||||
type: specifierNode.type,
|
||||
};
|
||||
if (specifierNode.type === 'ImportSpecifier') {
|
||||
data.importedName = keyNode2value(specifierNode.imported) as string;
|
||||
}
|
||||
specifiers.push(data);
|
||||
});
|
||||
|
||||
return {
|
||||
source,
|
||||
specifiers,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 基本的文件解析过程
|
||||
* @param ast
|
||||
*/
|
||||
export function traverseFile(ast: t.File) {
|
||||
const imports: Record<string, IImportSpecifierData[]> = {};
|
||||
|
||||
traverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
const { source, specifiers } = parseImportDeclaration(path.node);
|
||||
imports[source] = specifiers;
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
imports,
|
||||
};
|
||||
}
|
||||
|
||||
63
packages/core/src/helpers/code-helpers.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { getVariableContent } from '@music163/tango-helpers';
|
||||
import { value2node, expression2code, isValidExpressionCode } from './ast';
|
||||
import { isWrappedByExpressionContainer } from './assert';
|
||||
|
||||
/**
|
||||
* 将 js value 转换为代码字符串
|
||||
*/
|
||||
export function value2code(value: any) {
|
||||
const node = value2node(value);
|
||||
const code = expression2code(node);
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是字符串代码
|
||||
* @param code
|
||||
* @returns
|
||||
*/
|
||||
function isStringCode(code: string) {
|
||||
return /^".*"$/.test(code?.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* js value 转为表达式代码
|
||||
* @example 1 => 1
|
||||
* @example hello => "hello"
|
||||
* @example { foo: bar } => {{ foo: bar }}
|
||||
* @example [1,2,3] => {[1,2,3]}
|
||||
*
|
||||
* @param val js value
|
||||
* @returns 表达式代码
|
||||
*/
|
||||
export function value2expressionCode(val: any) {
|
||||
if (!val) return '';
|
||||
|
||||
let ret;
|
||||
|
||||
switch (typeof val) {
|
||||
case 'string': {
|
||||
if (isValidExpressionCode(val)) {
|
||||
ret = val;
|
||||
} else if (isWrappedByExpressionContainer(val, false)) {
|
||||
ret = getVariableContent(val);
|
||||
} else if (isStringCode(val)) {
|
||||
ret = val;
|
||||
} else {
|
||||
ret = `"${val}"`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'number':
|
||||
ret = String(val);
|
||||
break;
|
||||
case 'object':
|
||||
ret = value2code(val);
|
||||
break;
|
||||
default:
|
||||
ret = '';
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './ast';
|
||||
export * from './assert';
|
||||
export * from './code-helpers';
|
||||
export * from './string';
|
||||
export * from './object';
|
||||
export * from './prototype';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IImportDeclarationPayload } from '../types';
|
||||
import { Dict } from '@music163/tango-helpers';
|
||||
import { IImportDeclarationPayload, IImportSpecifierSourceData } from '../types';
|
||||
|
||||
/**
|
||||
* 导入列表解析为导入声明对象
|
||||
@@ -8,13 +9,13 @@ import { IImportDeclarationPayload } from '../types';
|
||||
*/
|
||||
export function namesToImportDeclarations(
|
||||
names: string[],
|
||||
nameMap: Record<string, { package: string; isDefault?: boolean }>,
|
||||
nameMap: Dict<IImportSpecifierSourceData>,
|
||||
) {
|
||||
const map = {};
|
||||
names.forEach((name) => {
|
||||
const mod = nameMap[name];
|
||||
if (mod) {
|
||||
updateMod(map, mod.package, name, mod.isDefault, !map[mod.package]);
|
||||
updateMod(map, mod.source, name, mod.isDefault, !map[mod.source]);
|
||||
}
|
||||
});
|
||||
return Object.keys(map).map((sourcePath) => ({
|
||||
|
||||
@@ -3,16 +3,51 @@ import {
|
||||
ComponentPropType,
|
||||
ComponentPrototypeType,
|
||||
isNil,
|
||||
isVariableString,
|
||||
logger,
|
||||
uuid,
|
||||
} from '@music163/tango-helpers';
|
||||
import { getRelativePath, isFilepath } from './string';
|
||||
import type { IImportDeclarationPayload } from '../types';
|
||||
import type { IImportDeclarationPayload, IImportSpecifierData } from '../types';
|
||||
import { code2expression } from './ast';
|
||||
import { isWrappedByExpressionContainer } from './assert';
|
||||
|
||||
export function prototype2importDeclarationData(
|
||||
prototype: ComponentPrototypeType,
|
||||
relativeFilepath?: string,
|
||||
): { source: string; specifiers: IImportSpecifierData[] } {
|
||||
let source = prototype.package;
|
||||
if (relativeFilepath && isFilepath(source)) {
|
||||
source = getRelativePath(relativeFilepath, source);
|
||||
}
|
||||
if (source.endsWith('.js')) {
|
||||
source = source.slice(0, -3);
|
||||
}
|
||||
|
||||
const specifiers: IImportSpecifierData[] = [];
|
||||
|
||||
if (prototype.exportType === 'defaultExport') {
|
||||
specifiers.push({
|
||||
localName: prototype.name,
|
||||
type: 'ImportDefaultSpecifier',
|
||||
});
|
||||
} else {
|
||||
[prototype.name, ...(prototype.relatedImports || [])].forEach((item) => {
|
||||
specifiers.push({
|
||||
localName: item,
|
||||
type: 'ImportSpecifier',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
source,
|
||||
specifiers,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据组件的 prototype 生成 ImportDeclarationPayload
|
||||
* @deprecated
|
||||
*/
|
||||
export function getImportDeclarationPayloadByPrototype(
|
||||
prototype: ComponentPrototypeType,
|
||||
@@ -35,6 +70,9 @@ export function getImportDeclarationPayloadByPrototype(
|
||||
if (relativeFilepath && isFilepath(sourcePath)) {
|
||||
sourcePath = getRelativePath(relativeFilepath, sourcePath);
|
||||
}
|
||||
if (sourcePath.endsWith('.js')) {
|
||||
sourcePath = sourcePath.slice(0, -3);
|
||||
}
|
||||
|
||||
return {
|
||||
defaultSpecifier,
|
||||
@@ -82,7 +120,7 @@ function getPropKeyValuePair(item: ComponentPropType, generateValue: (...args: a
|
||||
break;
|
||||
}
|
||||
case 'string': {
|
||||
if (!isVariableString(value)) {
|
||||
if (!isWrappedByExpressionContainer(value)) {
|
||||
// 不是变量字符串
|
||||
value = `"${value}"`;
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import path from 'path';
|
||||
import { camelCase, upperCamelCase } from '@music163/tango-helpers';
|
||||
import { value2node, expression2code } from './ast';
|
||||
import { FileType } from './../types';
|
||||
|
||||
/**
|
||||
@@ -34,7 +33,10 @@ export function inferFileType(filename: string): FileType {
|
||||
return FileType.JsonViewModule;
|
||||
}
|
||||
|
||||
// FIXME: 暂时写死,后续要支持多 services 文件解析
|
||||
if (/\/(blocks|components)\/index\.js/.test(filename)) {
|
||||
return FileType.ComponentsEntryModule;
|
||||
}
|
||||
|
||||
if (/\/services\/.+\.js$/.test(filename)) {
|
||||
return FileType.ServiceModule;
|
||||
}
|
||||
@@ -51,10 +53,6 @@ export function inferFileType(filename: string): FileType {
|
||||
return FileType.StoreModule;
|
||||
}
|
||||
|
||||
if (/\/blocks\/[\w-]+\/index\.js/.test(filename)) {
|
||||
return FileType.BlockEntryModule;
|
||||
}
|
||||
|
||||
if (/\.jsx?$/.test(filename)) {
|
||||
return FileType.Module;
|
||||
}
|
||||
@@ -141,15 +139,6 @@ export function formatCode(code: string, parser = 'babel') {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 js value 转换为代码字符串
|
||||
*/
|
||||
export function value2code(value: any) {
|
||||
const node = value2node(value);
|
||||
const code = expression2code(node);
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否匹配路由
|
||||
* @example isPathnameMatchRoute('/user/123', '/users/:id') -> true
|
||||
@@ -181,24 +170,35 @@ export function getBlockNameByFilename(filename: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: 有问题,需要优化下
|
||||
* 基于 from 文件的地址计算 to 文件的相对引用路径
|
||||
* @param from
|
||||
* @param to
|
||||
* 合并两个路径
|
||||
* @param root
|
||||
* @param filename
|
||||
* @returns
|
||||
*/
|
||||
export function getRelativePath(from: string, to: string) {
|
||||
const fromFolder = path.dirname(from);
|
||||
return path.relative(fromFolder, to);
|
||||
export function getFullPath(root: string, filename: string) {
|
||||
return path.join(root, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 targetFile 在 sourceFile 中的相对引用路径
|
||||
* @param sourceFile
|
||||
* @param targetFile
|
||||
* @returns
|
||||
*/
|
||||
export function getRelativePath(sourceFile: string, targetFile: string) {
|
||||
sourceFile = path.dirname(sourceFile);
|
||||
return path.relative(sourceFile, targetFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断给定字符串是否是文件路径
|
||||
* @example ./pages/index.js -- yes
|
||||
* @example ../pages/index.js -- yes
|
||||
* @example ../components -- yes
|
||||
* @example /src/pages/index.js -- yes
|
||||
* @example @music163/tango-designer -- no
|
||||
* @param str
|
||||
*/
|
||||
export function isFilepath(str: string) {
|
||||
return /^(\.\.?\/|\/).*\.[a-z]+$/.test(str);
|
||||
return /^(\.\.?\/|\/).*(\.[a-z]+)?$/.test(str);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
export * from './models';
|
||||
export * from './factory';
|
||||
export * from './helpers';
|
||||
export * from './types';
|
||||
export * from './types';
|
||||
|
||||
export { default as generator } from '@babel/generator';
|
||||
export { default as traverse } from '@babel/traverse';
|
||||
export * from '@babel/parser';
|
||||
|
||||
40
packages/core/src/models/component-module.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import path from 'path';
|
||||
import { action, computed, makeObservable, observable } from 'mobx';
|
||||
import { TangoModule } from './module';
|
||||
import { IWorkspace } from './interfaces';
|
||||
import { IExportSpecifierData, IFileConfig } from '../types';
|
||||
import { traverseComponentsEntryFile } from '../helpers';
|
||||
|
||||
/**
|
||||
* 本地组件目录的入口文件,例如 '/components/index.js' 或 `/blocks/index.js`
|
||||
*/
|
||||
export class TangoComponentsEntryModule extends TangoModule {
|
||||
exportList: Record<string, IExportSpecifierData>;
|
||||
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this.update(props.code, false, false);
|
||||
makeObservable(this, {
|
||||
_code: observable,
|
||||
_cleanCode: observable,
|
||||
exportList: observable,
|
||||
code: computed,
|
||||
cleanCode: computed,
|
||||
update: action,
|
||||
});
|
||||
}
|
||||
|
||||
_analysisAst() {
|
||||
const baseDir = path.dirname(this.filename);
|
||||
const { exportMap } = traverseComponentsEntryFile(this.ast, baseDir);
|
||||
this.exportList = exportMap;
|
||||
Object.keys(this.exportList).forEach((key) => {
|
||||
this.workspace.componentPrototypes.set(key, {
|
||||
name: key,
|
||||
exportType: 'namedExport',
|
||||
package: baseDir,
|
||||
type: 'element',
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,26 @@ import { IWorkspace } from './interfaces';
|
||||
|
||||
export type SimulatorNameType = 'desktop' | 'phone';
|
||||
|
||||
type SimulatorType = {
|
||||
export type DesignerViewType = 'design' | 'code';
|
||||
|
||||
interface ISimulatorType {
|
||||
name: SimulatorNameType;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
type ViewportBoundingType = {
|
||||
interface IViewportBounding {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
export type DesignerViewType = 'design' | 'code';
|
||||
interface IDesignerOptions {
|
||||
workspace: IWorkspace;
|
||||
simulator?: SimulatorNameType | ISimulatorType;
|
||||
activeSidebarPanel?: string;
|
||||
}
|
||||
|
||||
type DesignerOptionsType = { workspace: IWorkspace; simulator?: SimulatorNameType | SimulatorType };
|
||||
|
||||
const simulatorTypes: Record<string, SimulatorType> = {
|
||||
const ISimulatorTypes: Record<string, ISimulatorType> = {
|
||||
desktop: {
|
||||
name: 'desktop',
|
||||
width: 1366,
|
||||
@@ -35,12 +39,12 @@ export class Designer {
|
||||
/**
|
||||
* 当前的沙箱模拟器类型
|
||||
*/
|
||||
_simulator: SimulatorType = simulatorTypes.desktop;
|
||||
_simulator: ISimulatorType = ISimulatorTypes.desktop;
|
||||
|
||||
/**
|
||||
* 当前的视图尺寸
|
||||
*/
|
||||
_viewport: ViewportBoundingType = {
|
||||
_viewport: IViewportBounding = {
|
||||
width: 1366,
|
||||
height: 800,
|
||||
};
|
||||
@@ -70,9 +74,14 @@ export class Designer {
|
||||
*/
|
||||
_isPreview = false;
|
||||
|
||||
/**
|
||||
* 默认展开的侧边栏
|
||||
*/
|
||||
defaultActiveSidebarPanel?: string;
|
||||
|
||||
private readonly workspace: IWorkspace;
|
||||
|
||||
get simulator(): SimulatorType {
|
||||
get simulator(): ISimulatorType {
|
||||
return toJS(this._simulator);
|
||||
}
|
||||
|
||||
@@ -100,11 +109,21 @@ export class Designer {
|
||||
return this._showRightPanel;
|
||||
}
|
||||
|
||||
constructor(options: DesignerOptionsType) {
|
||||
constructor(options: IDesignerOptions) {
|
||||
this.workspace = options.workspace;
|
||||
if (options.simulator) {
|
||||
this.setSimulator(options.simulator);
|
||||
|
||||
const { simulator, activeSidebarPanel: defaultActiveSidebarPanel } = options;
|
||||
|
||||
// 默认设计器模式
|
||||
if (simulator) {
|
||||
this.setSimulator(simulator);
|
||||
}
|
||||
|
||||
// 默认展开的侧边栏
|
||||
if (defaultActiveSidebarPanel) {
|
||||
this.setActiveSidebarPanel(defaultActiveSidebarPanel);
|
||||
}
|
||||
|
||||
makeObservable(this, {
|
||||
_simulator: observable,
|
||||
_viewport: observable,
|
||||
@@ -131,15 +150,15 @@ export class Designer {
|
||||
});
|
||||
}
|
||||
|
||||
setSimulator(value: SimulatorType | SimulatorNameType) {
|
||||
setSimulator(value: ISimulatorType | SimulatorNameType) {
|
||||
if (typeof value === 'string') {
|
||||
this._simulator = simulatorTypes[value];
|
||||
this._simulator = ISimulatorTypes[value];
|
||||
} else {
|
||||
this._simulator = value;
|
||||
}
|
||||
}
|
||||
|
||||
setViewport(value: ViewportBoundingType) {
|
||||
setViewport(value: IViewportBounding) {
|
||||
this._viewport = value;
|
||||
}
|
||||
|
||||
@@ -154,7 +173,6 @@ export class Designer {
|
||||
this._activeSidebarPanel = '';
|
||||
}
|
||||
}
|
||||
|
||||
closeSidebarPanel() {
|
||||
this._activeSidebarPanel = '';
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ export class TangoFile {
|
||||
this._code = code;
|
||||
this._cleanCode = code;
|
||||
}
|
||||
this.workspace.onFilesChange([this.filename]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +135,7 @@ export class TangoJsonFile extends TangoFile {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
this.workspace.onFilesChange([this.filename]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,5 +11,6 @@ export * from './route-module';
|
||||
export * from './service-module';
|
||||
export * from './store-module';
|
||||
export * from './view-module';
|
||||
export * from './component-module';
|
||||
export * from './node';
|
||||
export * from './interfaces';
|
||||
|
||||
@@ -5,11 +5,11 @@ import { DragSource } from './drag-source';
|
||||
import {
|
||||
IFileConfig,
|
||||
FileType,
|
||||
IImportDeclarationPayload,
|
||||
InsertChildPositionType,
|
||||
ITangoConfigPackages,
|
||||
IPageConfigData,
|
||||
IServiceFunctionPayload,
|
||||
IImportSpecifierSourceData,
|
||||
IImportSpecifierData,
|
||||
} from '../types';
|
||||
import { TangoFile, TangoJsonFile } from './file';
|
||||
import { TangoRouteModule } from './route-module';
|
||||
@@ -24,13 +24,22 @@ export interface IViewFile {
|
||||
/**
|
||||
* 通过导入组件名查找组件来自的包
|
||||
*/
|
||||
importMap?: Dict<{ package: string; isDefault?: boolean }>;
|
||||
importMap?: Dict<IImportSpecifierSourceData>;
|
||||
|
||||
listImportSources?: () => string[];
|
||||
listModals?: () => Array<{ label: string; value: string }>;
|
||||
listForms?: () => Record<string, string[]>;
|
||||
|
||||
update: (code?: string, isFormatCode?: boolean, refreshWorkspace?: boolean) => void;
|
||||
|
||||
/**
|
||||
* 添加新的导入符号
|
||||
* @param source 导入来源
|
||||
* @param newSpecifiers 新导入符号列表
|
||||
* @returns this
|
||||
*/
|
||||
addImportSpecifiers: (source: string, newSpecifiers: IImportSpecifierData[]) => IViewFile;
|
||||
|
||||
getNode: (targetNodeId: string) => IViewNode;
|
||||
|
||||
removeNode: (targetNodeId: string) => IViewFile;
|
||||
@@ -39,31 +48,15 @@ export interface IViewFile {
|
||||
targetNodeId: string,
|
||||
newNode: any,
|
||||
position?: InsertChildPositionType,
|
||||
sourcePrototype?: string | ComponentPrototypeType,
|
||||
) => IViewFile;
|
||||
|
||||
insertAfter: (
|
||||
targetNodeId: string,
|
||||
newNode: any,
|
||||
sourcePrototype?: string | ComponentPrototypeType,
|
||||
) => IViewFile;
|
||||
insertAfter: (targetNodeId: string, newNode: any) => IViewFile;
|
||||
|
||||
insertBefore: (
|
||||
targetNodeId: string,
|
||||
newNode: any,
|
||||
sourcePrototype?: string | ComponentPrototypeType,
|
||||
) => IViewFile;
|
||||
insertBefore: (targetNodeId: string, newNode: any) => IViewFile;
|
||||
|
||||
replaceNode: (
|
||||
targetNodeId: string,
|
||||
newNode: any,
|
||||
sourcePrototype?: string | ComponentPrototypeType,
|
||||
) => IViewFile;
|
||||
replaceNode: (targetNodeId: string, newNode: any) => IViewFile;
|
||||
|
||||
replaceViewChildren: (
|
||||
rawNodes: any[],
|
||||
importDeclarations?: IImportDeclarationPayload[],
|
||||
) => IViewFile;
|
||||
replaceViewChildren: (rawNodes: any[]) => IViewFile;
|
||||
|
||||
updateNodeAttribute: (
|
||||
nodeId: string,
|
||||
@@ -146,8 +139,8 @@ export interface IWorkspace {
|
||||
storeModules?: Record<string, TangoStoreModule>;
|
||||
serviceModules?: Record<string, TangoServiceModule>;
|
||||
|
||||
refresh: (names: string[]) => void;
|
||||
ready: () => void;
|
||||
refresh: (names: string[]) => void;
|
||||
|
||||
setActiveRoute: (path: string) => void;
|
||||
setActiveFile: (filename: string) => void;
|
||||
@@ -155,23 +148,32 @@ export interface IWorkspace {
|
||||
setComponentPrototypes: (prototypes: Record<string, ComponentPrototypeType>) => void;
|
||||
getPrototype: (name: string | ComponentPrototypeType) => ComponentPrototypeType;
|
||||
|
||||
/**
|
||||
* 查询节点
|
||||
* @param id 节点 ID
|
||||
* @param module 节点所在的模块名
|
||||
* @returns 返回节点对象
|
||||
*/
|
||||
getNode: (id: string, module?: string) => IViewNode;
|
||||
|
||||
getFile: (filename: string) => TangoFile;
|
||||
listFiles: () => Record<string, string>;
|
||||
addFile: (filename: string, code: string, fileType?: FileType) => void;
|
||||
// ----------------- 文件操作 -----------------
|
||||
addFiles: (files: IFileConfig[]) => void;
|
||||
updateFile: (filename: string, code: string, shouldFormatCode?: boolean) => void;
|
||||
addFile: (filename: string, code: string, fileType?: FileType) => void;
|
||||
|
||||
addServiceFile: (serviceName: string, code: string) => void;
|
||||
addStoreFile: (storeName: string, code: string) => void;
|
||||
addViewFile: (viewName: string, code: string) => void;
|
||||
|
||||
removeFile: (filename: string) => void;
|
||||
|
||||
renameFile: (oldFilename: string, newFilename: string) => void;
|
||||
renameFolder: (oldFoldername: string, newFoldername: string) => void;
|
||||
|
||||
updateFile: (filename: string, code: string, shouldFormatCode?: boolean) => void;
|
||||
|
||||
listFiles: () => Record<string, string>;
|
||||
getFile: (filename: string) => TangoFile;
|
||||
|
||||
/**
|
||||
* 文件变化回调
|
||||
* @param filenames 文件名列表
|
||||
*/
|
||||
onFilesChange: (filenames: string[]) => void;
|
||||
|
||||
// ----------------- 节点操作 -----------------
|
||||
|
||||
removeSelectedNode: () => void;
|
||||
cloneSelectedNode: () => void;
|
||||
copySelectedNode: () => void;
|
||||
@@ -191,31 +193,48 @@ export interface IWorkspace {
|
||||
relatedImports?: string[],
|
||||
) => void;
|
||||
|
||||
getServiceFunction?: (serviceKey: string) => object;
|
||||
listServiceFunctions?: () => Record<string, object>;
|
||||
removeServiceFunction?: (serviceName: string, modName?: string) => void;
|
||||
addServiceFunction?: (
|
||||
payload: IServiceFunctionPayload | IServiceFunctionPayload[],
|
||||
modName?: string,
|
||||
) => void;
|
||||
updateServiceFunction?: (payload: IServiceFunctionPayload, modName?: string) => void;
|
||||
updateServiceBaseConfig?: (IServiceFunctionPayload: object, modName?: string) => void;
|
||||
/**
|
||||
* 查询节点
|
||||
* @param id 节点 ID
|
||||
* @param module 节点所在的模块名
|
||||
* @returns 返回节点对象
|
||||
*/
|
||||
getNode: (id: string, module?: string) => IViewNode;
|
||||
|
||||
addStoreModule?: (storeName: string, code: string) => void;
|
||||
removeStoreModule?: (storeName: string) => void;
|
||||
// ----------------- 服务函数文件操作 -----------------
|
||||
|
||||
getServiceFunction?: (serviceKey: string) => {
|
||||
name: string;
|
||||
moduleName: string;
|
||||
config: Dict<object>;
|
||||
};
|
||||
listServiceFunctions?: () => Dict<object>;
|
||||
removeServiceFunction?: (serviceKey: string) => void;
|
||||
addServiceFunction?: (serviceName: string, config: Dict, modName?: string) => void;
|
||||
addServiceFunctions?: (configs: Dict<object>, modName?: string) => void;
|
||||
updateServiceFunction?: (serviceName: string, payload: Dict, modName?: string) => void;
|
||||
updateServiceBaseConfig?: (config: Dict, modName?: string) => void;
|
||||
|
||||
// ----------------- 状态管理文件操作 -----------------
|
||||
addStoreState?: (storeName: string, stateName: string, initValue: string) => void;
|
||||
removeStoreState?: (storeName: string, stateName: string) => void;
|
||||
removeStoreModule?: (storeName: string) => void;
|
||||
removeStoreVariable?: (variablePath: string) => void;
|
||||
updateStoreVariable?: (variablePath: string, code: string) => void;
|
||||
|
||||
updateModuleCodeByVariablePath?: (variablePath: string, code: string) => void;
|
||||
// ----------------- 视图文件操作 -----------------
|
||||
|
||||
// TODO: 是否需要,和 addFile 有啥区别 ???
|
||||
addViewPage: (name: string, code: string) => void;
|
||||
removeViewModule: (routePath: string) => void;
|
||||
copyViewPage: (sourceRoutePath: string, targetPageData: IPageConfigData) => void;
|
||||
|
||||
// ----------------- 路由文件操作 -----------------
|
||||
|
||||
updateRoute: (sourceRoutePath: string, targetPageData: IPageConfigData) => void;
|
||||
|
||||
// ----------------- 依赖包操作 -----------------
|
||||
|
||||
addDependency?: (data: any) => void;
|
||||
listDependencies?: () => any;
|
||||
getDependency?: (pkgName: string) => object;
|
||||
|
||||
updateDependency?: (
|
||||
name: string,
|
||||
@@ -239,9 +258,11 @@ export interface IWorkspace {
|
||||
|
||||
removeBizComp?: (name: string) => void;
|
||||
|
||||
// ----------------- getter -----------------
|
||||
|
||||
get activeViewModule(): IViewFile;
|
||||
get pages(): any[];
|
||||
get bizComps(): string[];
|
||||
get baseComps(): string[];
|
||||
get blocks(): any[];
|
||||
get localComps(): string[];
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import * as t from '@babel/types';
|
||||
import { action, computed, makeObservable, observable } from 'mobx';
|
||||
import { isNil } from '@music163/tango-helpers';
|
||||
import { code2ast, ast2code, formatCode } from '../helpers';
|
||||
import {
|
||||
code2ast,
|
||||
ast2code,
|
||||
formatCode,
|
||||
traverseFile,
|
||||
addImportDeclaration,
|
||||
updateImportDeclaration,
|
||||
} from '../helpers';
|
||||
import { TangoFile } from './file';
|
||||
import { IFileConfig } from '../types';
|
||||
import { IFileConfig, IImportSpecifierData, ImportDeclarationDataType } from '../types';
|
||||
import { IWorkspace } from './interfaces';
|
||||
|
||||
/**
|
||||
@@ -14,6 +21,11 @@ import { IWorkspace } from './interfaces';
|
||||
export class TangoModule extends TangoFile {
|
||||
ast: t.File;
|
||||
|
||||
/**
|
||||
* 导入的依赖列表
|
||||
*/
|
||||
importList: ImportDeclarationDataType;
|
||||
|
||||
constructor(workspace: IWorkspace, props: IFileConfig, isSyncCode = true) {
|
||||
super(workspace, props, isSyncCode);
|
||||
}
|
||||
@@ -34,11 +46,23 @@ export class TangoModule extends TangoFile {
|
||||
|
||||
this._analysisAst();
|
||||
|
||||
this.workspace.onFilesChange([this.filename]);
|
||||
|
||||
if (refreshWorkspace) {
|
||||
this.workspace.refresh([this.filename]);
|
||||
}
|
||||
}
|
||||
|
||||
addImportDeclaration(source: string, specifiers: IImportSpecifierData[]) {
|
||||
this.ast = addImportDeclaration(this.ast, source, specifiers);
|
||||
return this;
|
||||
}
|
||||
|
||||
updateImportDeclaration(source: string, specifiers: IImportSpecifierData[]) {
|
||||
this.ast = updateImportDeclaration(this.ast, source, specifiers);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于最新的 ast 进行源码同步
|
||||
*/
|
||||
@@ -69,7 +93,10 @@ export class TangoModule extends TangoFile {
|
||||
this.ast = code2ast(code);
|
||||
}
|
||||
|
||||
_analysisAst() {}
|
||||
_analysisAst() {
|
||||
const { imports } = traverseFile(this.ast);
|
||||
this.importList = imports;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
deleteServiceConfigFromServiceFile,
|
||||
updateBaseConfigToServiceFile,
|
||||
} from '../helpers';
|
||||
import { IFileConfig, IServiceFunctionPayload } from '../types';
|
||||
import { IFileConfig } from '../types';
|
||||
import { IWorkspace } from './interfaces';
|
||||
import { TangoModule } from './module';
|
||||
|
||||
@@ -58,7 +58,8 @@ export class TangoServiceModule extends TangoModule {
|
||||
}
|
||||
|
||||
_analysisAst() {
|
||||
const { services, baseConfig } = traverseServiceFile(this.ast);
|
||||
const { imports, services, baseConfig } = traverseServiceFile(this.ast);
|
||||
this.importList = imports;
|
||||
this._serviceFunctions = services;
|
||||
this._baseConfig = baseConfig;
|
||||
if (baseConfig.namespace) {
|
||||
@@ -66,25 +67,23 @@ export class TangoServiceModule extends TangoModule {
|
||||
}
|
||||
}
|
||||
|
||||
addServiceFunction(payload: IServiceFunctionPayload) {
|
||||
const { name, ...rest } = payload;
|
||||
this.ast = updateServiceConfigToServiceFile(this.ast, { [name]: clone(rest, false) });
|
||||
addServiceFunction(name: string, config: object) {
|
||||
this.ast = updateServiceConfigToServiceFile(this.ast, { [name]: clone(config, false) });
|
||||
return this;
|
||||
}
|
||||
|
||||
addServiceFunctions(payloads: IServiceFunctionPayload[]) {
|
||||
const config = payloads.reduce((acc, cur) => {
|
||||
const { name, ...rest } = cur;
|
||||
acc[name] = clone(rest, false);
|
||||
return acc;
|
||||
}, {});
|
||||
this.ast = updateServiceConfigToServiceFile(this.ast, config);
|
||||
/**
|
||||
* 批量添加服务函数
|
||||
* @param configs { [name: string]: object }
|
||||
* @returns
|
||||
*/
|
||||
addServiceFunctions(configs: Dict<object>) {
|
||||
this.ast = updateServiceConfigToServiceFile(this.ast, configs);
|
||||
return this;
|
||||
}
|
||||
|
||||
updateServiceFunction(payload: IServiceFunctionPayload) {
|
||||
const { name, ...rest } = payload;
|
||||
this.ast = updateServiceConfigToServiceFile(this.ast, { [name]: clone(rest, false) });
|
||||
updateServiceFunction(name: string, payload: object) {
|
||||
this.ast = updateServiceConfigToServiceFile(this.ast, { [name]: clone(payload, false) });
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as t from '@babel/types';
|
||||
import { action, computed, makeObservable, observable, toJS } from 'mobx';
|
||||
import { ComponentPrototypeType, Dict } from '@music163/tango-helpers';
|
||||
import { Dict } from '@music163/tango-helpers';
|
||||
import {
|
||||
ast2code,
|
||||
traverseViewFile,
|
||||
@@ -8,15 +8,17 @@ import {
|
||||
insertSiblingAfterJSXElement,
|
||||
appendChildToJSXElement,
|
||||
addImportDeclaration,
|
||||
updateImportDeclaration,
|
||||
replaceJSXElement,
|
||||
getImportDeclarationPayloadByPrototype,
|
||||
removeUnusedImportSpecifiers,
|
||||
insertSiblingBeforeJSXElement,
|
||||
replaceRootJSXElementChildren,
|
||||
IdGenerator,
|
||||
updateJSXAttributes,
|
||||
queryXFormItemFields,
|
||||
prototype2importDeclarationData,
|
||||
insertImportSpecifiers,
|
||||
addImportDeclarationLegacy,
|
||||
updateImportDeclarationLegacy,
|
||||
} from '../helpers';
|
||||
import { TangoNode } from './node';
|
||||
import {
|
||||
@@ -24,10 +26,59 @@ import {
|
||||
ITangoViewNodeData,
|
||||
IImportDeclarationPayload,
|
||||
InsertChildPositionType,
|
||||
IImportSpecifierSourceData,
|
||||
ImportDeclarationDataType,
|
||||
IImportSpecifierData,
|
||||
} from '../types';
|
||||
import { IViewFile, IWorkspace } from './interfaces';
|
||||
import { TangoModule } from './module';
|
||||
|
||||
/**
|
||||
* 导入信息转为 变量名->来源 的 map 结构
|
||||
* @param importedModules
|
||||
* @returns
|
||||
*/
|
||||
function buildImportMap(importedModules: ImportDeclarationDataType) {
|
||||
const map: Dict<IImportSpecifierSourceData> = {};
|
||||
Object.keys(importedModules).forEach((source) => {
|
||||
const specifiers = importedModules[source];
|
||||
specifiers?.forEach((specifier) => {
|
||||
map[specifier.localName] = {
|
||||
source,
|
||||
isDefault: specifier.type === 'ImportDefaultSpecifier',
|
||||
};
|
||||
});
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将节点列表转换为 tree data 嵌套数组
|
||||
* @param list
|
||||
*/
|
||||
function nodeListToTreeData(list: ITangoViewNodeData[]) {
|
||||
const map: Record<string, ITangoViewNodeData> = {};
|
||||
|
||||
list.forEach((item) => {
|
||||
// 如果不存在,则初始化
|
||||
if (!map[item.id]) {
|
||||
map[item.id] = {
|
||||
...item,
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
|
||||
// 是否找到父节点,找到则塞进去
|
||||
if (item.parentId && map[item.parentId]) {
|
||||
map[item.parentId].children.push(map[item.id]);
|
||||
}
|
||||
});
|
||||
|
||||
// 保留根节点
|
||||
const ret = Object.values(map).filter((item) => !item.parentId);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图模块
|
||||
*/
|
||||
@@ -37,7 +88,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
/**
|
||||
* 通过导入组件名查找组件来自的包
|
||||
*/
|
||||
importMap: Dict<{ package: string; isDefault?: boolean }>;
|
||||
importMap: Dict<IImportSpecifierSourceData>;
|
||||
|
||||
/**
|
||||
* 视图中依赖的 tango 变量,仅 stores 和 services
|
||||
@@ -50,6 +101,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
private _nodes: Map<string, TangoNode>;
|
||||
/**
|
||||
* 导入的模块
|
||||
* @deprecated
|
||||
*/
|
||||
private _importedModules: Dict<IImportDeclarationPayload | IImportDeclarationPayload[]>;
|
||||
|
||||
@@ -97,6 +149,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
ast: newAst,
|
||||
cleanAst,
|
||||
nodes,
|
||||
imports,
|
||||
importedModules,
|
||||
variables,
|
||||
} = traverseViewFile(this.ast, this._idGenerator);
|
||||
@@ -106,7 +159,8 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
this._cleanCode = ast2code(cleanAst);
|
||||
|
||||
this._importedModules = importedModules;
|
||||
this.importMap = this.buildImportMap(importedModules);
|
||||
this.importList = imports;
|
||||
this.importMap = buildImportMap(imports);
|
||||
this.variables = variables;
|
||||
|
||||
this._nodes.clear();
|
||||
@@ -122,6 +176,13 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
this._nodesTree = nodeListToTreeData(nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 依赖列表
|
||||
*/
|
||||
listImportSources() {
|
||||
return Object.keys(this.importList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹窗列表
|
||||
*/
|
||||
@@ -160,23 +221,30 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于组件的 prototype 信息更新导入信息
|
||||
* @param prototype
|
||||
* @param shouldUpdateCode
|
||||
* 添加导入符号
|
||||
* @param source
|
||||
* @param newSpecifiers
|
||||
* @returns
|
||||
*/
|
||||
updateImportSpecifiersByPrototype(sourcePrototype: string | ComponentPrototypeType) {
|
||||
const prototype = this.workspace.getPrototype(sourcePrototype);
|
||||
if (prototype) {
|
||||
const importDeclaration = getImportDeclarationPayloadByPrototype(prototype, this.filename);
|
||||
this.updateImportSpecifiers(importDeclaration);
|
||||
addImportSpecifiers(source: string, newSpecifiers: IImportSpecifierData[]) {
|
||||
const existSpecifiers = this.importList[source];
|
||||
if (existSpecifiers) {
|
||||
const insertedSpecifiers = newSpecifiers.filter((item) => {
|
||||
return !existSpecifiers.find((existItem) => existItem.localName === item.localName);
|
||||
});
|
||||
this.ast = insertImportSpecifiers(this.ast, source, insertedSpecifiers);
|
||||
} else {
|
||||
// 不存在导入来源,直接添加新的导入语句
|
||||
this.ast = addImportDeclaration(this.ast, source, newSpecifiers);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新导入的变量(新版)
|
||||
* 更新导入的变量
|
||||
* @deprecated 使用 updateImportDeclaration 代替
|
||||
*/
|
||||
updateImportSpecifiers(importDeclaration: IImportDeclarationPayload) {
|
||||
updateImportSpecifiersLegacy(importDeclaration: IImportDeclarationPayload) {
|
||||
const mods = this._importedModules[importDeclaration.sourcePath];
|
||||
let ast;
|
||||
// 如果模块已存在,需要去重
|
||||
@@ -191,12 +259,12 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
(name) => !specifiers.includes(name),
|
||||
);
|
||||
|
||||
ast = updateImportDeclaration(this.ast, {
|
||||
ast = updateImportDeclarationLegacy(this.ast, {
|
||||
...importDeclaration,
|
||||
specifiers: newSpecifiers.concat(targetMod.specifiers),
|
||||
});
|
||||
} else {
|
||||
ast = addImportDeclaration(this.ast, importDeclaration);
|
||||
ast = addImportDeclarationLegacy(this.ast, importDeclaration);
|
||||
}
|
||||
this.ast = ast;
|
||||
return this;
|
||||
@@ -239,9 +307,24 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
updateNodeAttributes(nodeId: string, config: Record<string, any>, relatedImports?: string[]) {
|
||||
if (relatedImports && relatedImports.length) {
|
||||
// 导入依赖的组件
|
||||
relatedImports.forEach((name: string) => {
|
||||
const newImportData = relatedImports.reduce((prev, name) => {
|
||||
const proto = this.workspace.getPrototype(name);
|
||||
this.updateImportSpecifiersByPrototype(proto);
|
||||
const { source, specifiers } = prototype2importDeclarationData(proto, this.filename);
|
||||
const existSpecifiers: IImportSpecifierData[] = prev[source];
|
||||
if (existSpecifiers) {
|
||||
// merge specifiers
|
||||
specifiers.forEach((item) => {
|
||||
if (!existSpecifiers.find((existItem) => existItem.localName === item.localName)) {
|
||||
existSpecifiers.push(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
prev[source] = specifiers;
|
||||
}
|
||||
return prev;
|
||||
}, {});
|
||||
Object.keys(newImportData).forEach((source) => {
|
||||
this.addImportSpecifiers(source, newImportData[source]);
|
||||
});
|
||||
}
|
||||
this.ast = updateJSXAttributes(this.ast, nodeId, config);
|
||||
@@ -252,43 +335,25 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
* 插入子节点的最后面
|
||||
* @param targetNodeId
|
||||
* @param newNode
|
||||
* @param sourceName
|
||||
* @param position
|
||||
* @returns
|
||||
*/
|
||||
insertChild(
|
||||
targetNodeId: string,
|
||||
newNode: t.JSXElement,
|
||||
position: InsertChildPositionType = 'last',
|
||||
sourceName: string | ComponentPrototypeType,
|
||||
) {
|
||||
this.ast = appendChildToJSXElement(this.ast, targetNodeId, newNode, position);
|
||||
if (sourceName) {
|
||||
this.updateImportSpecifiersByPrototype(sourceName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
insertAfter(
|
||||
targetNodeId: string,
|
||||
newNode: t.JSXElement,
|
||||
sourceName?: string | ComponentPrototypeType,
|
||||
) {
|
||||
insertAfter(targetNodeId: string, newNode: t.JSXElement) {
|
||||
this.ast = insertSiblingAfterJSXElement(this.ast, targetNodeId, newNode);
|
||||
if (sourceName) {
|
||||
this.updateImportSpecifiersByPrototype(sourceName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
insertBefore(
|
||||
targetNodeId: string,
|
||||
newNode: t.JSXElement,
|
||||
sourceName?: string | ComponentPrototypeType,
|
||||
) {
|
||||
insertBefore(targetNodeId: string, newNode: t.JSXElement) {
|
||||
this.ast = insertSiblingBeforeJSXElement(this.ast, targetNodeId, newNode);
|
||||
if (sourceName) {
|
||||
this.updateImportSpecifiersByPrototype(sourceName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -296,22 +361,15 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
* 替换目标节点为新节点
|
||||
* @param targetNodeId
|
||||
* @param newNode
|
||||
* @param sourcePrototype
|
||||
*/
|
||||
replaceNode(
|
||||
targetNodeId: string,
|
||||
newNode: t.JSXElement,
|
||||
sourceName?: string | ComponentPrototypeType,
|
||||
) {
|
||||
replaceNode(targetNodeId: string, newNode: t.JSXElement) {
|
||||
this.ast = replaceJSXElement(this.ast, targetNodeId, newNode);
|
||||
if (sourceName) {
|
||||
this.updateImportSpecifiersByPrototype(sourceName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换 jsx 跟结点的子元素
|
||||
* @deprecated 不推荐使用
|
||||
*/
|
||||
replaceViewChildren(
|
||||
childrenNodes: t.JSXElement[],
|
||||
@@ -323,60 +381,10 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
|
||||
if (importDeclarations?.length) {
|
||||
importDeclarations.forEach((item) => {
|
||||
this.updateImportSpecifiers(item);
|
||||
this.updateImportSpecifiersLegacy(item);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private buildImportMap(
|
||||
importedModules: Dict<IImportDeclarationPayload | IImportDeclarationPayload[]>,
|
||||
) {
|
||||
const map = {};
|
||||
Object.keys(importedModules).forEach((modName) => {
|
||||
const mod = importedModules[modName];
|
||||
(Array.isArray(mod) ? mod : [mod]).forEach((item) => {
|
||||
if (item.defaultSpecifier) {
|
||||
map[item.defaultSpecifier] = {
|
||||
package: modName,
|
||||
isDefault: true,
|
||||
};
|
||||
}
|
||||
if (item.specifiers.length) {
|
||||
item.specifiers.forEach((spe) => {
|
||||
map[spe] = { package: modName };
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将节点列表转换为 tree data 嵌套数组
|
||||
* @param list
|
||||
*/
|
||||
function nodeListToTreeData(list: ITangoViewNodeData[]) {
|
||||
const map: Record<string, ITangoViewNodeData> = {};
|
||||
|
||||
list.forEach((item) => {
|
||||
// 如果不存在,则初始化
|
||||
if (!map[item.id]) {
|
||||
map[item.id] = {
|
||||
...item,
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
|
||||
// 是否找到父节点,找到则塞进去
|
||||
if (item.parentId && map[item.parentId]) {
|
||||
map[item.parentId].children.push(map[item.id]);
|
||||
}
|
||||
});
|
||||
|
||||
// 保留根节点
|
||||
const ret = Object.values(map).filter((item) => !item.parentId);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { action, computed, makeObservable, observable } from 'mobx';
|
||||
import { JSXElement } from '@babel/types';
|
||||
import {
|
||||
array2object,
|
||||
ComponentPrototypeType,
|
||||
Dict,
|
||||
hasFileExtension,
|
||||
isStoreVariablePath,
|
||||
isString,
|
||||
logger,
|
||||
parseServiceVariablePath,
|
||||
parseStoreVariablePath,
|
||||
uniq,
|
||||
} from '@music163/tango-helpers';
|
||||
import {
|
||||
@@ -15,8 +18,7 @@ import {
|
||||
isPathnameMatchRoute,
|
||||
getJSXElementChildrenNames,
|
||||
namesToImportDeclarations,
|
||||
getBlockNameByFilename,
|
||||
getPrivilegeCode,
|
||||
prototype2importDeclarationData,
|
||||
} from '../helpers';
|
||||
import { DropMethod } from './drop-target';
|
||||
import { HistoryMessage, TangoHistory } from './history';
|
||||
@@ -24,19 +26,14 @@ import { TangoNode } from './node';
|
||||
import { TangoJsModule } from './module';
|
||||
import { TangoFile, TangoJsonFile, TangoLessFile } from './file';
|
||||
import { IWorkspace } from './interfaces';
|
||||
import {
|
||||
IFileConfig,
|
||||
FileType,
|
||||
ITangoConfigPackages,
|
||||
IPageConfigData,
|
||||
IServiceFunctionPayload,
|
||||
} from '../types';
|
||||
import { IFileConfig, FileType, ITangoConfigPackages, IPageConfigData } from '../types';
|
||||
import { SelectSource } from './select-source';
|
||||
import { DragSource } from './drag-source';
|
||||
import { TangoRouteModule } from './route-module';
|
||||
import { TangoStoreEntryModule, TangoStoreModule } from './store-module';
|
||||
import { TangoServiceModule } from './service-module';
|
||||
import { TangoViewModule } from './view-module';
|
||||
import { TangoComponentsEntryModule } from './component-module';
|
||||
|
||||
export interface IWorkspaceOptions {
|
||||
/**
|
||||
@@ -55,6 +52,10 @@ export interface IWorkspaceOptions {
|
||||
* 组件描述列表
|
||||
*/
|
||||
prototypes?: Record<string, ComponentPrototypeType>;
|
||||
/**
|
||||
* 工作区文件变更事件
|
||||
*/
|
||||
onFilesChange?: IWorkspace['onFilesChange'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +71,14 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
*/
|
||||
files: Map<string, TangoFile>;
|
||||
|
||||
/**
|
||||
* 组件配置
|
||||
*/
|
||||
componentPrototypes: Map<string, ComponentPrototypeType>;
|
||||
|
||||
/**
|
||||
* 入口文件
|
||||
*/
|
||||
entry: string;
|
||||
|
||||
/**
|
||||
@@ -89,12 +96,6 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
*/
|
||||
activeViewFile: string;
|
||||
|
||||
/**
|
||||
* 本地区块 { [blockName]: filePath }
|
||||
* FIXME: 修正类型 Record<string, TangoBlockModule>
|
||||
*/
|
||||
localBlocks: Record<string, string>;
|
||||
|
||||
/**
|
||||
* 路由配置模块
|
||||
*/
|
||||
@@ -109,6 +110,8 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
|
||||
serviceModules: Record<string, TangoServiceModule> = {};
|
||||
|
||||
componentsEntryModule: TangoComponentsEntryModule;
|
||||
|
||||
/**
|
||||
* package.json 文件
|
||||
*/
|
||||
@@ -119,12 +122,6 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
*/
|
||||
tangoConfigJson: TangoJsonFile;
|
||||
|
||||
/**
|
||||
* appJson.json 文件
|
||||
* FIXME: 是否保留 ???
|
||||
*/
|
||||
appJson: TangoJsonFile;
|
||||
|
||||
/**
|
||||
* 绑定事件
|
||||
* TODO: 是否需要自己来管理 listeners,并及时进行 gc
|
||||
@@ -160,15 +157,12 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* 获取页面列表
|
||||
*/
|
||||
get pages() {
|
||||
const appJsonPages = this.appJson?.getValue('pages');
|
||||
const pagesMap = array2object(appJsonPages || [], (item) => item.path);
|
||||
const ret: IPageConfigData[] = [];
|
||||
this.routeModule?.routes.forEach((item) => {
|
||||
const data = pagesMap[item.path];
|
||||
if (item.path !== '*') {
|
||||
ret.push({
|
||||
...data,
|
||||
path: item.path,
|
||||
name: item.component,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -203,8 +197,8 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
return list;
|
||||
}
|
||||
|
||||
get blocks() {
|
||||
return Object.keys(this.localBlocks);
|
||||
get localComps(): string[] {
|
||||
return Object.keys(this.componentsEntryModule?.exportList || {});
|
||||
}
|
||||
|
||||
constructor(options?: IWorkspaceOptions) {
|
||||
@@ -213,7 +207,6 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
this.selectSource = new SelectSource(this);
|
||||
this.dragSource = new DragSource(this);
|
||||
this.componentPrototypes = new Map();
|
||||
this.localBlocks = {};
|
||||
this.entry = options?.entry;
|
||||
this.activeRoute = options?.defaultActiveRoute || '/';
|
||||
this.activeFile = options?.entry;
|
||||
@@ -221,6 +214,11 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
this.files = new Map();
|
||||
this.isReady = false;
|
||||
|
||||
if (options?.onFilesChange) {
|
||||
// 使用用户提供的 onFilesChange
|
||||
this.onFilesChange = options.onFilesChange;
|
||||
}
|
||||
|
||||
if (options?.files) {
|
||||
this.addFiles(options.files);
|
||||
}
|
||||
@@ -234,8 +232,6 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
activeRoute: observable,
|
||||
activeFile: observable,
|
||||
activeViewFile: observable,
|
||||
localBlocks: observable,
|
||||
blocks: computed,
|
||||
pages: computed,
|
||||
bizComps: computed,
|
||||
setActiveRoute: action,
|
||||
@@ -302,7 +298,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一组文件到工作区
|
||||
* 添加一组文件到工作区,如果文件同名,后面的文件会覆盖前面的文件
|
||||
* @param files
|
||||
*/
|
||||
addFiles(files: IFileConfig[] = []) {
|
||||
@@ -318,81 +314,100 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* @param fileType 模块类型
|
||||
*/
|
||||
addFile(filename: string, code: string, fileType?: FileType) {
|
||||
if (!this.files.has(filename)) {
|
||||
const moduleType = fileType || inferFileType(filename);
|
||||
const props = {
|
||||
filename,
|
||||
code,
|
||||
type: moduleType,
|
||||
};
|
||||
const moduleType = fileType || inferFileType(filename);
|
||||
const props = {
|
||||
filename,
|
||||
code,
|
||||
type: moduleType,
|
||||
};
|
||||
|
||||
let module;
|
||||
switch (moduleType) {
|
||||
case FileType.StoreEntryModule:
|
||||
module = new TangoStoreEntryModule(this, props);
|
||||
this.storeEntryModule = module;
|
||||
break;
|
||||
case FileType.RouteModule: {
|
||||
module = new TangoRouteModule(this, props);
|
||||
this.routeModule = module;
|
||||
// check if activeRoute exists
|
||||
const route = module.routes.find((item) => item.path === this.activeRoute);
|
||||
if (!route) {
|
||||
this.setActiveRoute(module.routes[0]?.path);
|
||||
}
|
||||
break;
|
||||
let module;
|
||||
switch (moduleType) {
|
||||
case FileType.StoreEntryModule:
|
||||
module = new TangoStoreEntryModule(this, props);
|
||||
this.storeEntryModule = module;
|
||||
break;
|
||||
case FileType.ComponentsEntryModule:
|
||||
module = new TangoComponentsEntryModule(this, props);
|
||||
this.componentsEntryModule = module;
|
||||
break;
|
||||
case FileType.RouteModule: {
|
||||
module = new TangoRouteModule(this, props);
|
||||
this.routeModule = module;
|
||||
// check if activeRoute exists
|
||||
const route = module.routes.find((item) => item.path === this.activeRoute);
|
||||
if (!route) {
|
||||
this.setActiveRoute(module.routes[0]?.path);
|
||||
}
|
||||
case FileType.JsxViewModule:
|
||||
module = new TangoViewModule(this, props);
|
||||
break;
|
||||
case FileType.ServiceModule:
|
||||
module = new TangoServiceModule(this, props);
|
||||
this.serviceModules[module.name] = module;
|
||||
break;
|
||||
case FileType.StoreModule:
|
||||
module = new TangoStoreModule(this, props);
|
||||
this.storeModules[module.name] = module;
|
||||
break;
|
||||
case FileType.BlockEntryModule: {
|
||||
const blockName = getBlockNameByFilename(props.filename);
|
||||
const prototype: ComponentPrototypeType = {
|
||||
name: blockName,
|
||||
exportType: 'defaultExport',
|
||||
package: props.filename,
|
||||
type: 'block',
|
||||
};
|
||||
this.localBlocks[blockName] = props.filename;
|
||||
this.componentPrototypes.set(blockName, prototype);
|
||||
module = new TangoViewModule(this, props);
|
||||
break;
|
||||
}
|
||||
case FileType.Module:
|
||||
module = new TangoJsModule(this, props);
|
||||
break;
|
||||
case FileType.Less:
|
||||
module = new TangoLessFile(this, props);
|
||||
break;
|
||||
case FileType.PackageJson:
|
||||
module = new TangoJsonFile(this, props);
|
||||
this.packageJson = module;
|
||||
break;
|
||||
case FileType.TangoConfigJson:
|
||||
module = new TangoJsonFile(this, props);
|
||||
this.tangoConfigJson = module;
|
||||
break;
|
||||
case FileType.AppJson:
|
||||
module = new TangoJsonFile(this, props);
|
||||
this.appJson = module;
|
||||
break;
|
||||
case FileType.Json:
|
||||
module = new TangoJsonFile(this, props);
|
||||
break;
|
||||
default:
|
||||
module = new TangoFile(this, props);
|
||||
break;
|
||||
}
|
||||
|
||||
this.files.set(filename, module);
|
||||
case FileType.JsxViewModule:
|
||||
module = new TangoViewModule(this, props);
|
||||
break;
|
||||
case FileType.ServiceModule:
|
||||
module = new TangoServiceModule(this, props);
|
||||
this.serviceModules[module.name] = module;
|
||||
break;
|
||||
case FileType.StoreModule:
|
||||
module = new TangoStoreModule(this, props);
|
||||
this.storeModules[module.name] = module;
|
||||
break;
|
||||
case FileType.Module:
|
||||
module = new TangoJsModule(this, props);
|
||||
break;
|
||||
case FileType.Less:
|
||||
module = new TangoLessFile(this, props);
|
||||
break;
|
||||
case FileType.PackageJson:
|
||||
module = new TangoJsonFile(this, props);
|
||||
this.packageJson = module;
|
||||
break;
|
||||
case FileType.TangoConfigJson:
|
||||
module = new TangoJsonFile(this, props);
|
||||
this.tangoConfigJson = module;
|
||||
break;
|
||||
case FileType.Json:
|
||||
module = new TangoJsonFile(this, props);
|
||||
break;
|
||||
default:
|
||||
module = new TangoFile(this, props);
|
||||
}
|
||||
|
||||
this.files.set(filename, module);
|
||||
}
|
||||
|
||||
addServiceFile(serviceName: string, code: string) {
|
||||
const filename = `/src/services/${serviceName}.js`;
|
||||
this.addFile(filename, code, FileType.ServiceModule);
|
||||
const indexServiceModule = this.serviceModules.index;
|
||||
indexServiceModule?.addImportDeclaration(`./${serviceName}`, []).update();
|
||||
}
|
||||
|
||||
addStoreFile(storeName: string, code: string) {
|
||||
const filename = `/src/stores/${storeName}.js`;
|
||||
this.addFile(filename, code);
|
||||
if (!this.storeEntryModule) {
|
||||
this.addFile('/src/stores/index.js', '');
|
||||
}
|
||||
this.storeEntryModule.addStore(storeName).update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加视图文件
|
||||
* @param viewName 文件名
|
||||
* @param code 代码
|
||||
*/
|
||||
addViewFile(viewName: string, code: string) {
|
||||
const viewRoute = viewName.startsWith('/') ? viewName : `/${viewName}`;
|
||||
const filename = `/src/pages/${viewName}.js`;
|
||||
this.addFile(filename, code);
|
||||
this.addRoute(
|
||||
{
|
||||
name: viewName,
|
||||
path: viewRoute,
|
||||
},
|
||||
filename,
|
||||
);
|
||||
}
|
||||
|
||||
updateFile(filename: string, code: string, shouldFormatCode = false) {
|
||||
@@ -488,50 +503,14 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
this.routeModule.removeRoute(routePath).update();
|
||||
this.setActiveRoute(this.routeModule.routes[0]?.path || '/');
|
||||
}
|
||||
// remove appJson page
|
||||
this.appJson
|
||||
?.setValue('pages', (pages) => {
|
||||
return (pages as IPageConfigData[]).filter((page) => page.path !== routePath);
|
||||
})
|
||||
.update();
|
||||
this.removeFile(filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加新的视图文件
|
||||
* @deprecated
|
||||
* @param route 视图名
|
||||
* @param code 视图代码
|
||||
*/
|
||||
addViewPage(routeConfig: string | IPageConfigData, code: string) {
|
||||
const config =
|
||||
typeof routeConfig === 'string'
|
||||
? {
|
||||
name: routeConfig,
|
||||
path: routeConfig.startsWith('/') ? routeConfig : `/${routeConfig}`,
|
||||
}
|
||||
: routeConfig;
|
||||
|
||||
const filename = getFilepath(config.path, '/src/pages', '.js');
|
||||
this.addFile(filename, code);
|
||||
this.addRoute(config, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加新的路由
|
||||
*/
|
||||
addRoute(routeData: IPageConfigData, importFilePath: string) {
|
||||
this.routeModule?.addRoute(routeData.path, importFilePath).update();
|
||||
this.appJson
|
||||
?.setValue('pages', (pages) => {
|
||||
(pages as IPageConfigData[]).push({
|
||||
name: routeData.name || routeData.path,
|
||||
path: routeData.path,
|
||||
privilegeCode: getPrivilegeCode(this.packageJson?.getValue('name'), routeData.path),
|
||||
});
|
||||
return pages;
|
||||
})
|
||||
.update();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -543,18 +522,6 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
if (sourceRoutePath !== targetPageData.path) {
|
||||
this.routeModule?.updateRoute(sourceRoutePath, targetPageData.path).update();
|
||||
}
|
||||
this.appJson
|
||||
?.setValue('pages', (pages) => {
|
||||
for (const page of pages as IPageConfigData[]) {
|
||||
if (page.path === sourceRoutePath) {
|
||||
page.path = targetPageData.path;
|
||||
page.name = targetPageData.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pages;
|
||||
})
|
||||
.update();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -594,13 +561,10 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
|
||||
/**
|
||||
* 添加新的模型文件
|
||||
* @param name 模型名
|
||||
* @param code 模型代码
|
||||
* @deprecated 使用 addStoreFile 代替
|
||||
*/
|
||||
addStoreModule(name: string, code: string) {
|
||||
const filename = getFilepath(name, '/src/stores', '.js');
|
||||
this.addFile(filename, code);
|
||||
this.storeEntryModule?.addStore(name).update();
|
||||
this.addStoreFile(name, code);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -633,15 +597,23 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据变量路径更新模块内容
|
||||
* TODO: 改名,不直观
|
||||
* 根据变量路径删除状态变量
|
||||
* @param variablePath
|
||||
*/
|
||||
removeStoreVariable(variablePath: string) {
|
||||
const { storeName, variableName } = parseStoreVariablePath(variablePath);
|
||||
this.removeStoreState(storeName, variableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据变量路径更新状态变量的值
|
||||
* @param variablePath 变量路径
|
||||
* @param code 变量代码
|
||||
*/
|
||||
updateModuleCodeByVariablePath(variablePath: string, code: string) {
|
||||
if (/^stores\.\w+\.\w+$/.test(variablePath)) {
|
||||
const [, storeName, stateName] = variablePath.split('.');
|
||||
this.storeModules[storeName]?.updateState(stateName, code).update();
|
||||
updateStoreVariable(variablePath: string, code: string) {
|
||||
if (isStoreVariablePath(variablePath)) {
|
||||
const { storeName, variableName } = parseStoreVariablePath(variablePath);
|
||||
this.storeModules[storeName]?.updateState(variableName, code).update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,7 +624,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* @returns
|
||||
*/
|
||||
getServiceFunction(serviceKey: string) {
|
||||
const { name, moduleName } = this.parseServiceKey(serviceKey);
|
||||
const { name, moduleName } = parseServiceVariablePath(serviceKey);
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
@@ -666,15 +638,14 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
|
||||
/**
|
||||
* 获取服务函数的列表
|
||||
* @returns 返回服务函数的列表 { [serviceKey: string]: Dict }
|
||||
*/
|
||||
listServiceFunctions() {
|
||||
const ret: Record<string, object> = {};
|
||||
Object.values(this.serviceModules).forEach((module) => {
|
||||
const ret: Record<string, Dict> = {};
|
||||
Object.keys(this.serviceModules).forEach((moduleName) => {
|
||||
const module = this.serviceModules[moduleName];
|
||||
Object.keys(module.serviceFunctions).forEach((name) => {
|
||||
const serviceKey =
|
||||
module.baseConfig.namespace !== 'index'
|
||||
? name
|
||||
: [module.baseConfig.namespace, name].join('.');
|
||||
const serviceKey = moduleName === 'index' ? name : [moduleName, name].join('.');
|
||||
ret[serviceKey] = module.serviceFunctions[name];
|
||||
});
|
||||
});
|
||||
@@ -684,47 +655,60 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
/**
|
||||
* 更新服务函数
|
||||
*/
|
||||
updateServiceFunction(payload: IServiceFunctionPayload, moduleName = 'index') {
|
||||
this.serviceModules[moduleName].updateServiceFunction(payload).update();
|
||||
updateServiceFunction(serviceName: string, payload: Dict, moduleName = 'index') {
|
||||
this.serviceModules[moduleName].updateServiceFunction(serviceName, payload).update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增服务函数,支持批量添加
|
||||
*/
|
||||
addServiceFunction(
|
||||
payload: IServiceFunctionPayload | IServiceFunctionPayload[],
|
||||
moduleName = 'index',
|
||||
) {
|
||||
if (Array.isArray(payload)) {
|
||||
this.serviceModules[moduleName]?.addServiceFunctions(payload).update();
|
||||
} else {
|
||||
this.serviceModules[moduleName]?.addServiceFunction(payload).update();
|
||||
}
|
||||
addServiceFunction(name: string, config: Dict, moduleName = 'index') {
|
||||
this.serviceModules[moduleName]?.addServiceFunction(name, config).update();
|
||||
}
|
||||
|
||||
addServiceFunctions(configs: Dict<Dict>, modName = 'index') {
|
||||
this.serviceModules[modName]?.addServiceFunctions(configs).update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除服务函数
|
||||
* @param name
|
||||
*/
|
||||
removeServiceFunction(name: string, moduleName = 'index') {
|
||||
removeServiceFunction(serviceKey: string) {
|
||||
const { moduleName, name } = parseServiceVariablePath(serviceKey);
|
||||
this.serviceModules[moduleName]?.deleteServiceFunction(name).update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新服务的基础配置
|
||||
*/
|
||||
updateServiceBaseConfig(config: object, moduleName = 'index') {
|
||||
updateServiceBaseConfig(config: Dict, moduleName = 'index') {
|
||||
this.serviceModules[moduleName]?.updateBaseConfig(config).update();
|
||||
}
|
||||
|
||||
addDependency(data: any) {
|
||||
// TODO: implement it to replace addBizComp & addServiceComp
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 package.json 中的依赖列表
|
||||
* @returns
|
||||
* TODO: fix this logic to merge dependencies from package.json and tango.config.json
|
||||
*/
|
||||
listDependencies() {
|
||||
return this.packageJson?.getValue('dependencies');
|
||||
}
|
||||
|
||||
getDependency(pkgName: string) {
|
||||
const packages = this.tangoConfigJson?.getValue('packages');
|
||||
const dependencies = this.packageJson?.getValue('dependencies'); // 兼容老版本
|
||||
const detail = {
|
||||
version: dependencies?.[pkgName],
|
||||
...(packages?.[pkgName] || {}),
|
||||
};
|
||||
return detail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新依赖,没有就添加
|
||||
* @param name
|
||||
@@ -909,7 +893,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
);
|
||||
|
||||
importDeclarations.forEach((importDeclaration) => {
|
||||
this.activeViewModule.updateImportSpecifiers(importDeclaration);
|
||||
this.activeViewModule.updateImportSpecifiersLegacy(importDeclaration);
|
||||
});
|
||||
|
||||
this.copyTempNodes.forEach((node) => {
|
||||
@@ -960,7 +944,11 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
const sourcePrototype = this.getPrototype(sourceName);
|
||||
const newNode = prototype2jsxElement(sourcePrototype);
|
||||
const file = this.getNode(targetNodeId).file;
|
||||
file.insertChild(targetNodeId, newNode, 'last', sourcePrototype).update();
|
||||
const { source, specifiers } = prototype2importDeclarationData(sourcePrototype, file.filename);
|
||||
file
|
||||
.insertChild(targetNodeId, newNode, 'last')
|
||||
.addImportSpecifiers(source, specifiers)
|
||||
.update();
|
||||
this.history.push({
|
||||
message: HistoryMessage.InsertNode,
|
||||
data: {
|
||||
@@ -980,7 +968,8 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
const sourcePrototype = this.getPrototype(sourceName);
|
||||
const newNode = prototype2jsxElement(sourcePrototype);
|
||||
const file = this.getNode(targetNodeId).file;
|
||||
file.replaceNode(targetNodeId, newNode, sourcePrototype).update();
|
||||
const { source, specifiers } = prototype2importDeclarationData(sourcePrototype, file.filename);
|
||||
file.replaceNode(targetNodeId, newNode).addImportSpecifiers(source, specifiers).update();
|
||||
this.history.push({
|
||||
message: HistoryMessage.ReplaceNode,
|
||||
data: {
|
||||
@@ -998,7 +987,14 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
if (insertedPrototype) {
|
||||
const newNode = prototype2jsxElement(insertedPrototype);
|
||||
const file = this.selectSource.file;
|
||||
file.insertChild(this.selectSource.first.id, newNode, 'last', insertedPrototype).update();
|
||||
const { source, specifiers } = prototype2importDeclarationData(
|
||||
insertedPrototype,
|
||||
file.filename,
|
||||
);
|
||||
file
|
||||
.insertChild(this.selectSource.first.id, newNode, 'last')
|
||||
.addImportSpecifiers(source, specifiers)
|
||||
.update();
|
||||
this.history.push({
|
||||
message: HistoryMessage.InsertNode,
|
||||
data: {
|
||||
@@ -1054,30 +1050,40 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
const targetFile = dropTarget.node?.file;
|
||||
const sourceFile = dragSource.node?.file;
|
||||
|
||||
// dragSourcePrototype to importDeclarations
|
||||
const { source, specifiers } = prototype2importDeclarationData(
|
||||
dragSourcePrototype,
|
||||
targetFile.filename,
|
||||
);
|
||||
|
||||
let isValidOperation = true;
|
||||
switch (dropTarget.method) {
|
||||
// 直接往目标节点的 children 里添加一个节点
|
||||
case DropMethod.InsertChild: {
|
||||
targetFile.insertChild(dropTarget.id, newNode, 'last', dragSourcePrototype);
|
||||
targetFile
|
||||
.insertChild(dropTarget.id, newNode, 'last')
|
||||
.addImportSpecifiers(source, specifiers);
|
||||
break;
|
||||
}
|
||||
case DropMethod.InsertFirstChild: {
|
||||
targetFile.insertChild(dropTarget.id, newNode, 'first', dragSourcePrototype);
|
||||
targetFile
|
||||
.insertChild(dropTarget.id, newNode, 'first')
|
||||
.addImportSpecifiers(source, specifiers);
|
||||
break;
|
||||
}
|
||||
// 往目标节点的后边插入一个节点
|
||||
case DropMethod.InsertAfter: {
|
||||
targetFile.insertAfter(dropTarget.id, newNode, dragSourcePrototype);
|
||||
targetFile.insertAfter(dropTarget.id, newNode).addImportSpecifiers(source, specifiers);
|
||||
break;
|
||||
}
|
||||
// 往目标节点的前方插入一个节点
|
||||
case DropMethod.InsertBefore: {
|
||||
targetFile.insertBefore(dropTarget.id, newNode, dragSourcePrototype);
|
||||
targetFile.insertBefore(dropTarget.id, newNode).addImportSpecifiers(source, specifiers);
|
||||
break;
|
||||
}
|
||||
// 替换目标节点
|
||||
case DropMethod.ReplaceNode: {
|
||||
targetFile.replaceNode(dropTarget.id, newNode, dragSourcePrototype);
|
||||
targetFile.replaceNode(dropTarget.id, newNode).addImportSpecifiers(source, specifiers);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1094,13 +1100,6 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
}
|
||||
|
||||
this.selectSource.clear();
|
||||
|
||||
this.history.push({
|
||||
message: HistoryMessage.DropNode,
|
||||
data: {
|
||||
[targetFile.filename]: targetFile.code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
targetFile.update();
|
||||
@@ -1109,6 +1108,19 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
}
|
||||
|
||||
dragSource.clear();
|
||||
|
||||
if (isValidOperation) {
|
||||
this.history.push({
|
||||
message: HistoryMessage.DropNode,
|
||||
data: {
|
||||
[targetFile.filename]: targetFile.code,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onFilesChange(filenams: string[]) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1185,11 +1197,11 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
if (this.files.has(sourceFilePath)) {
|
||||
// 来源是文件
|
||||
const file = this.files.get(sourceFilePath);
|
||||
this.addFile(targetFilePath + '.js', file.cleanCode, file.type);
|
||||
} else if (this.files.has(sourceFilePath + '/index.js')) {
|
||||
this.addFile(`${targetFilePath}.js`, file.cleanCode, file.type);
|
||||
} else if (this.files.has(`${sourceFilePath}/index.js`)) {
|
||||
// 来源是目录
|
||||
Array.from(this.files.keys()).forEach((key) => {
|
||||
if (key.startsWith(sourceFilePath + '/')) {
|
||||
if (key.startsWith(`${sourceFilePath}/`)) {
|
||||
const sourceFile = this.getFile(key);
|
||||
this.addFile(
|
||||
targetFilePath + key.slice(sourceFilePath.length),
|
||||
@@ -1202,40 +1214,4 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
logger.error('copyFiles failed, source: %s, target: %s', sourceFilePath, targetFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 serviceKey
|
||||
* @param serviceKey
|
||||
* @returns
|
||||
*
|
||||
* @example services.list => { moduleName: 'index', name: 'list' }
|
||||
* @example services.sub.list => { moduleName: 'sub', name: 'list' }
|
||||
* @example foo => undefined
|
||||
*/
|
||||
private parseServiceKey(serviceKey: string) {
|
||||
const parts = serviceKey.split('.');
|
||||
if (parts[0] !== 'services') {
|
||||
return {};
|
||||
}
|
||||
|
||||
let moduleName = 'index';
|
||||
let name = '';
|
||||
switch (parts.length) {
|
||||
case 2: {
|
||||
name = parts[1];
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
moduleName = parts[1];
|
||||
name = parts[2];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {
|
||||
moduleName,
|
||||
name,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,25 @@ export enum FileType {
|
||||
Module = 'module',
|
||||
StoreEntryModule = 'storeEntryModule',
|
||||
RouteModule = 'routeModule',
|
||||
BlockEntryModule = 'blockEntryModule',
|
||||
ServiceModule = 'serviceModule',
|
||||
StoreModule = 'storeModule',
|
||||
|
||||
// 组件配置文件
|
||||
ComponentPrototypeModule = 'componentPrototypeModule',
|
||||
// 组件运行调试入口文件,一般为 `/app.js`
|
||||
ComponentDemoEntryModule = 'componentDemoEntryModule',
|
||||
/**
|
||||
* 本地组件目录的入口文件
|
||||
*/
|
||||
ComponentsEntryModule = 'componentsEntryModule',
|
||||
/**
|
||||
* @deprecated 已废弃
|
||||
*/
|
||||
BlockEntryModule = 'blockEntryModule',
|
||||
|
||||
// jsx 类型视图文件
|
||||
JsxViewModule = 'jsxViewModule',
|
||||
// json 类型视图文件
|
||||
JsonViewModule = 'jsonViewModule',
|
||||
|
||||
// 非 js 文件
|
||||
@@ -72,8 +86,58 @@ export interface ITangoViewNodeData<T = JSXElement> {
|
||||
children?: Array<ITangoViewNodeData<T>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出变量数据
|
||||
*/
|
||||
export interface IExportSpecifierData {
|
||||
/**
|
||||
* 来源
|
||||
*/
|
||||
source: string;
|
||||
/**
|
||||
* exported name
|
||||
*/
|
||||
exportedName: string;
|
||||
/**
|
||||
* local name
|
||||
*/
|
||||
localName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入变量的来源
|
||||
*/
|
||||
export interface IImportSpecifierSourceData {
|
||||
source: string;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析的导入变量数据
|
||||
*/
|
||||
export interface IImportSpecifierData {
|
||||
/**
|
||||
* 导入名
|
||||
*/
|
||||
importedName?: string;
|
||||
/**
|
||||
* 本地名
|
||||
*/
|
||||
localName: string;
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
type: 'ImportDefaultSpecifier' | 'ImportSpecifier' | 'ImportNamespaceSpecifier';
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析的导入语句数据
|
||||
*/
|
||||
export type ImportDeclarationDataType = Record<string, IImportSpecifierData[]>;
|
||||
|
||||
/**
|
||||
* 模块导入的参数类型
|
||||
* @deprecated 使用 IImportSpecifierData 代替
|
||||
*/
|
||||
export interface IImportDeclarationPayload {
|
||||
defaultSpecifier?: string;
|
||||
@@ -81,23 +145,6 @@ export interface IImportDeclarationPayload {
|
||||
sourcePath: string;
|
||||
}
|
||||
|
||||
export interface IClassPropertyNodeData {
|
||||
reference: string;
|
||||
propertyName: string;
|
||||
propertyBody: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务函数参数类型
|
||||
*/
|
||||
export interface IServiceFunctionPayload {
|
||||
[key: string]: any;
|
||||
/**
|
||||
* 服务函数名
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store 属性类型
|
||||
*/
|
||||
@@ -166,7 +213,7 @@ export interface IPageConfigData {
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* PMS 权限码
|
||||
* 权限码
|
||||
*/
|
||||
privilegeCode?: string;
|
||||
}
|
||||
|
||||
25
packages/core/tests/assert.test.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { isTangoVariable, isWrappedByExpressionContainer } from '../src/helpers';
|
||||
|
||||
describe('assert', () => {
|
||||
it('isTangoVariable', () => {
|
||||
expect(isTangoVariable('tango.stores.app.name')).toBeTruthy();
|
||||
expect(isTangoVariable('tango.stores?.app?.name')).toBeTruthy();
|
||||
expect(isTangoVariable('tango.stores.app?.name')).toBeTruthy();
|
||||
// expect(isTangoVariable('tango.copyToClipboard')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isWrappedByExpressionContainer', () => {
|
||||
expect(isWrappedByExpressionContainer('{this.foo}')).toBeTruthy();
|
||||
expect(isWrappedByExpressionContainer('{!false}')).toBeTruthy();
|
||||
expect(isWrappedByExpressionContainer('{[]}')).toBeTruthy();
|
||||
expect(isWrappedByExpressionContainer('{{ foo: "bar" }}')).toBeTruthy();
|
||||
expect(isWrappedByExpressionContainer('{[{ foo: "bar" }]}')).toBeTruthy();
|
||||
expect(isWrappedByExpressionContainer('{123}')).toBeTruthy();
|
||||
expect(isWrappedByExpressionContainer('{"hello"}')).toBeTruthy();
|
||||
expect(isWrappedByExpressionContainer('{ foo: "bar" }')).toBeFalsy();
|
||||
expect(isWrappedByExpressionContainer('{ type: tango.stores?.homePage?.tabKey }')).toBeFalsy();
|
||||
expect(isWrappedByExpressionContainer('{ type: tango.stores.homePage.tabKey }')).toBeFalsy();
|
||||
expect(isWrappedByExpressionContainer('{ foo: "bar" }')).toBeFalsy();
|
||||
expect(isWrappedByExpressionContainer('{ color: tango.stores.app.color }')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -3,26 +3,37 @@ import {
|
||||
serviceConfig2Node,
|
||||
isValidCode,
|
||||
isValidExpressionCode,
|
||||
code2expression,
|
||||
} from '../src/helpers';
|
||||
|
||||
describe('helpers', () => {
|
||||
test('isValidCode', () => {
|
||||
describe('ast helpers', () => {
|
||||
it('isValidCode', () => {
|
||||
expect(isValidCode('() => { hello world }')).toBeFalsy();
|
||||
expect(isValidCode('function() {}')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('isValidExpression', () => {
|
||||
it('isValidExpression', () => {
|
||||
expect(isValidExpressionCode('() => { }')).toBeTruthy();
|
||||
expect(isValidExpressionCode('1')).toBeTruthy();
|
||||
expect(isValidExpressionCode('a = 1')).toBeTruthy();
|
||||
expect(isValidExpressionCode('1 + 1')).toBeTruthy();
|
||||
expect(isValidExpressionCode('"hello"')).toBeTruthy();
|
||||
expect(isValidExpressionCode('false')).toBeTruthy();
|
||||
expect(isValidExpressionCode('{ bizId: "vip", type: "category" }')).toBeTruthy();
|
||||
expect(isValidExpressionCode('[1,2,3]')).toBeTruthy();
|
||||
expect(isValidExpressionCode('<div>hello</div>')).toBeTruthy();
|
||||
expect(isValidExpressionCode('<div>hello</div>')).toBeTruthy();
|
||||
|
||||
expect(isValidExpressionCode('{1}')).toBeFalsy();
|
||||
expect(isValidExpressionCode('{"1"}')).toBeFalsy();
|
||||
expect(isValidExpressionCode('{ 1+1 }')).toBeFalsy();
|
||||
expect(isValidExpressionCode('{<div>aaa</div>}')).toBeFalsy();
|
||||
expect(isValidExpressionCode('{() => {}}')).toBeFalsy();
|
||||
expect(isValidExpressionCode('{[1,2,3]}')).toBeFalsy();
|
||||
expect(isValidExpressionCode('{tango.stores.app.title}')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('object2node', () => {
|
||||
it('object2node', () => {
|
||||
const node = object2node({
|
||||
url: '/api/backend/clientversion/appmarket/list',
|
||||
method: 'POST',
|
||||
@@ -40,4 +51,22 @@ describe('helpers', () => {
|
||||
} as any);
|
||||
expect(node.type).toEqual('ObjectExpression');
|
||||
});
|
||||
|
||||
it('code2expression', () => {
|
||||
expect(code2expression('')).toBeUndefined();
|
||||
expect(code2expression('{tango.stores.app}')).toBeUndefined();
|
||||
|
||||
expect(code2expression('{ type: window.bar }').type).toEqual('ObjectExpression');
|
||||
expect(code2expression('() => {};').type).toEqual('ArrowFunctionExpression');
|
||||
expect(code2expression('<Button>hello</Button>').type).toBe('JSXElement');
|
||||
expect(code2expression('<BreadcrumbItem children="节点名称" />').type).toBe('JSXElement');
|
||||
|
||||
const arrayCode = `
|
||||
[
|
||||
{ label: 'foo', value: 'foo' },
|
||||
{ label: 'bar', value: 'bar' },
|
||||
]
|
||||
`;
|
||||
expect(code2expression(arrayCode).type).toEqual('ArrayExpression');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,8 +27,6 @@ describe('engine', () => {
|
||||
}),
|
||||
});
|
||||
expect(engine.workspace.activeViewModule).toBeUndefined();
|
||||
expect(engine.workspace.serviceModule).toBeUndefined();
|
||||
expect(engine.workspace.routeModule).toBeUndefined();
|
||||
expect(engine.workspace?.storeModules.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||