mirror of
https://github.com/NetEase/tango.git
synced 2026-09-06 16:12:11 +08:00
Compare commits
15 Commits
@music163/
...
@music163/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c240c1824b | ||
|
|
d63517ecb9 | ||
|
|
1a17ee1e94 | ||
|
|
e1bab66819 | ||
|
|
89bcd45728 | ||
|
|
e86a6d7cd9 | ||
|
|
46b54459ce | ||
|
|
85898dee19 | ||
|
|
e429610606 | ||
|
|
b6814d6484 | ||
|
|
3d7b58f19a | ||
|
|
b6a47acdde | ||
|
|
226d71c704 | ||
|
|
1c72ee6ac2 | ||
|
|
09a1e2135a |
4
.husky/commit-msg
Executable file
4
.husky/commit-msg
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
yarn commitlint --edit "$1"
|
||||||
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
yarn lint-staged --allow-empty
|
||||||
@@ -14,7 +14,7 @@ export default {
|
|||||||
],
|
],
|
||||||
devServer: {
|
devServer: {
|
||||||
host: 'local.netease.com',
|
host: 'local.netease.com',
|
||||||
port: 7007,
|
port: 8008,
|
||||||
https: {
|
https: {
|
||||||
key: path.resolve(__dirname, 'local.netease.com-key.pem'),
|
key: path.resolve(__dirname, 'local.netease.com-key.pem'),
|
||||||
cert: path.resolve(__dirname, 'local.netease.com.pem'),
|
cert: path.resolve(__dirname, 'local.netease.com.pem'),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "COMPRESS=none umi build",
|
"build": "COMPRESS=none umi build",
|
||||||
"start": "HTTPS=1 umi dev"
|
"start": "umi dev"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.8.0",
|
"@ant-design/icons": "^4.8.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Row, Switch } from 'antd';
|
import { Row, Switch } from 'antd';
|
||||||
import { observer } from '@music163/tango-designer';
|
import { observer } from '@music163/tango-context';
|
||||||
|
|
||||||
const OtherPanel = observer(({ autoRemove, setAutoRemove }: any) => {
|
const OtherPanel = observer(({ autoRemove, setAutoRemove }: any) => {
|
||||||
const onChange = (checked: boolean) => {
|
const onChange = (checked: boolean) => {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const tangoConfigJson = {
|
|||||||
resources: ['https://unpkg.com/moment@{{version}}/moment.js'],
|
resources: ['https://unpkg.com/moment@{{version}}/moment.js'],
|
||||||
},
|
},
|
||||||
'@music163/tango-boot': {
|
'@music163/tango-boot': {
|
||||||
version: '0.2.0',
|
version: '0.2.1',
|
||||||
library: 'TangoBoot',
|
library: 'TangoBoot',
|
||||||
type: 'baseDependency',
|
type: 'baseDependency',
|
||||||
resources: ['https://unpkg.com/@music163/tango-boot@{{version}}/dist/boot.js'],
|
resources: ['https://unpkg.com/@music163/tango-boot@{{version}}/dist/boot.js'],
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
@import '~antd/dist/antd.less'; // 引入官方提供的 less 样式入口文件
|
@import '~antd/dist/antd.less'; // 引入官方提供的 less 样式入口文件
|
||||||
|
|
||||||
@primary-color: #2f54eb;
|
@primary-color: #2f54eb;
|
||||||
@border-radius-base: 2px;
|
@border-radius-base: 2px;
|
||||||
@@ -9,15 +9,21 @@ import {
|
|||||||
Toolbar,
|
Toolbar,
|
||||||
WorkspacePanel,
|
WorkspacePanel,
|
||||||
WorkspaceView,
|
WorkspaceView,
|
||||||
ComponentsPanel,
|
|
||||||
CodeEditor,
|
CodeEditor,
|
||||||
Sandbox,
|
Sandbox,
|
||||||
DndQuery,
|
DndQuery,
|
||||||
} from '@music163/tango-designer';
|
} from '@music163/tango-designer';
|
||||||
import { createEngine, Workspace } from '@music163/tango-core';
|
import { createEngine, Workspace } from '@music163/tango-core';
|
||||||
import { Logo, ProjectDetail } from './share';
|
import { Logo, ProjectDetail, bootHelperVariables } from './share';
|
||||||
import { sampleFiles } from '../mock/project';
|
import { sampleFiles } from '../mock/project';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
import {
|
||||||
|
ApiOutlined,
|
||||||
|
AppstoreAddOutlined,
|
||||||
|
BuildOutlined,
|
||||||
|
ClusterOutlined,
|
||||||
|
FunctionOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
// 1. 实例化工作区
|
// 1. 实例化工作区
|
||||||
const workspace = new Workspace({
|
const workspace = new Workspace({
|
||||||
@@ -44,7 +50,14 @@ export default function App() {
|
|||||||
const [menuLoading, setMenuLoading] = useState(true);
|
const [menuLoading, setMenuLoading] = useState(true);
|
||||||
const [menuData, setMenuData] = useState(false);
|
const [menuData, setMenuData] = useState(false);
|
||||||
return (
|
return (
|
||||||
<Designer engine={engine} sandboxQuery={sandboxQuery}>
|
<Designer
|
||||||
|
engine={engine}
|
||||||
|
config={{
|
||||||
|
customActionVariables: bootHelperVariables,
|
||||||
|
customExpressionVariables: bootHelperVariables,
|
||||||
|
}}
|
||||||
|
sandboxQuery={sandboxQuery}
|
||||||
|
>
|
||||||
<DesignerPanel
|
<DesignerPanel
|
||||||
logo={<Logo />}
|
logo={<Logo />}
|
||||||
description={<ProjectDetail />}
|
description={<ProjectDetail />}
|
||||||
@@ -52,10 +65,11 @@ export default function App() {
|
|||||||
<Box px="l">
|
<Box px="l">
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Toolbar.Item key="routeSwitch" placement="left" />
|
<Toolbar.Item key="routeSwitch" placement="left" />
|
||||||
|
<Toolbar.Item key="preview" placement="left" />
|
||||||
<Toolbar.Item key="modeSwitch" placement="right" />
|
<Toolbar.Item key="modeSwitch" placement="right" />
|
||||||
<Toolbar.Item key="togglePanel" placement="right" />
|
<Toolbar.Item key="togglePanel" placement="right" />
|
||||||
<Toolbar.Separator />
|
<Toolbar.Separator />
|
||||||
<Toolbar.Item key="extra" placement="right">
|
<Toolbar.Item placement="right">
|
||||||
<Space>
|
<Space>
|
||||||
<Button type="primary">发布</Button>
|
<Button type="primary">发布</Button>
|
||||||
</Space>
|
</Space>
|
||||||
@@ -65,13 +79,31 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Sidebar>
|
<Sidebar>
|
||||||
<Sidebar.Item key="outline" />
|
<Sidebar.Item key="outline" label="结构" icon={<BuildOutlined />} />
|
||||||
<Sidebar.Item key="components">
|
<Sidebar.Item
|
||||||
<ComponentsPanel menuData={menuData as any} loading={menuLoading} />
|
key="components"
|
||||||
</Sidebar.Item>
|
label="组件"
|
||||||
<Sidebar.Item key="variables" isFloat width={800} />
|
icon={<AppstoreAddOutlined />}
|
||||||
<Sidebar.Item key="dataSource" isFloat width={800} />
|
widgetProps={{
|
||||||
<Sidebar.Item key="dependency" isFloat width={800} />
|
menuData: menuData as any,
|
||||||
|
loading: menuLoading,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Sidebar.Item
|
||||||
|
key="variables"
|
||||||
|
label="变量"
|
||||||
|
icon={<FunctionOutlined />}
|
||||||
|
isFloat
|
||||||
|
width={800}
|
||||||
|
/>
|
||||||
|
<Sidebar.Item key="dataSource" label="接口" icon={<ApiOutlined />} isFloat width={800} />
|
||||||
|
<Sidebar.Item
|
||||||
|
key="dependency"
|
||||||
|
label="依赖"
|
||||||
|
icon={<ClusterOutlined />}
|
||||||
|
isFloat
|
||||||
|
width={800}
|
||||||
|
/>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<WorkspacePanel>
|
<WorkspacePanel>
|
||||||
<WorkspaceView mode="design">
|
<WorkspaceView mode="design">
|
||||||
|
|||||||
@@ -6,6 +6,36 @@ import { registerSetter } from '@music163/tango-designer';
|
|||||||
import type { ComponentPrototypeType } from '@music163/tango-helpers';
|
import type { ComponentPrototypeType } from '@music163/tango-helpers';
|
||||||
import { FooSetter } from '../components';
|
import { FooSetter } from '../components';
|
||||||
|
|
||||||
|
export const bootHelperVariables = [
|
||||||
|
{
|
||||||
|
key: '$helpers',
|
||||||
|
title: '工具函数',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
// folder-name
|
// folder-name
|
||||||
// 物料列表定义
|
// 物料列表定义
|
||||||
const bizToggleButtonPrototype: ComponentPrototypeType = {
|
const bizToggleButtonPrototype: ComponentPrototypeType = {
|
||||||
@@ -133,7 +163,7 @@ export function ProjectDetail() {
|
|||||||
return (
|
return (
|
||||||
<Box display="flex" alignItems="center" columnGap="l">
|
<Box display="flex" alignItems="center" columnGap="l">
|
||||||
<Box className="ProjectName" fontSize="18px" fontWeight="bold">
|
<Box className="ProjectName" fontSize="18px" fontWeight="bold">
|
||||||
lc-online-test
|
community-test
|
||||||
</Box>
|
</Box>
|
||||||
<Box className="BranchName" as="code" fontSize="14px">
|
<Box className="BranchName" as="code" fontSize="14px">
|
||||||
<BranchesOutlined /> feature/list
|
<BranchesOutlined /> feature/list
|
||||||
|
|||||||
@@ -10,11 +10,20 @@
|
|||||||
|
|
||||||
项目使用的 npm 包依赖信息,用于定义依赖的类型以及外置依赖资源。该配置项为一个 map,key 为对应的 npm 包名,value 为该依赖的配置信息。
|
项目使用的 npm 包依赖信息,用于定义依赖的类型以及外置依赖资源。该配置项为一个 map,key 为对应的 npm 包名,value 为该依赖的配置信息。
|
||||||
|
|
||||||
|
- version 包的版本号,必选
|
||||||
|
- library UMD 包中的全局变量名,必选
|
||||||
|
- type 依赖的类型
|
||||||
|
- baseDependency 基础依赖包
|
||||||
|
- bizDependency 业务组件依赖包
|
||||||
|
- dependency 其他依赖包
|
||||||
|
- resources 外置依赖资源列表
|
||||||
|
- designerResources 在设计器视图载入的外置资源列表,用来需要区别加载时使用
|
||||||
|
- description 描述信息
|
||||||
|
|
||||||
依赖的类型 `type` 可以是 `baseDependency` 即项目的基础依赖,用于获取项目基础物料列表的信息;也可以是 `bizDependency` 即业务组件依赖,用于获取合并到物料面板业务组件菜单中的业务组件列表;也可以是 `depenency` 即项目的其他依赖,只用于提供 external 信息与版本信息等,不会和物料面板产生关系。
|
依赖的类型 `type` 可以是 `baseDependency` 即项目的基础依赖,用于获取项目基础物料列表的信息;也可以是 `bizDependency` 即业务组件依赖,用于获取合并到物料面板业务组件菜单中的业务组件列表;也可以是 `depenency` 即项目的其他依赖,只用于提供 external 信息与版本信息等,不会和物料面板产生关系。
|
||||||
|
|
||||||
当配置了外置依赖资源 `resources` 与其对应的全局变量 `library` 时,当这些依赖被 import 时,相关资源不会被打包到 bundle 中,而是在运行时再去外部获取这些扩展依赖,其等效于之前的 `sandbox.externalResources` 与 `sandbox.externals` 配置项。此外,可以将 `resource` 中的版本号替换为 `{{version}}`,其会在平台上构建时替换为版本号。特殊的,可以定义 `designerResources` 用于适配平台的设计器视图。
|
当配置了外置依赖资源 `resources` 与其对应的全局变量 `library` 时,当这些依赖被 import 时,相关资源不会被打包到 bundle 中,而是在运行时再去外部获取这些扩展依赖,其等效于之前的 `sandbox.externalResources` 与 `sandbox.externals` 配置项。此外,可以将 `resource` 中的版本号替换为 `{{version}}`,其会在平台上构建时替换为版本号。特殊的,可以定义 `designerResources` 用于适配平台的设计器视图。
|
||||||
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"react": {
|
"react": {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Tango 是一个用于快速构建低代码平台的低代码设计器框架,
|
|||||||
|
|
||||||
## 引擎架构概览
|
## 引擎架构概览
|
||||||
|
|
||||||
Tango 低代码引擎在实现上进行了分层解藕,使得上层的低代码平台与底层的低代码引擎可以独立开发和维护,快速集成部署。另一方面,Tango 低代码引擎定义了一套开放的物料生态体系,开发者可以自由的贡献扩展组件配置能力的属性设置器,以及扩展低代码物料的二方三方业务组件。
|
Tango 低代码引擎在实现上进行了分层解耦,使得上层的低代码平台与底层的低代码引擎可以独立开发和维护,快速集成部署。另一方面,Tango 低代码引擎定义了一套开放的物料生态体系,开发者可以自由的贡献扩展组件配置能力的属性设置器,以及扩展低代码物料的二方三方业务组件。
|
||||||
|
|
||||||
如下图所示:
|
如下图所示:
|
||||||
|
|
||||||
|
|||||||
7
commitlint.config.js
Normal file
7
commitlint.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['@commitlint/config-conventional'],
|
||||||
|
rules: {
|
||||||
|
'scope-case': [0],
|
||||||
|
'subject-case': [0],
|
||||||
|
},
|
||||||
|
};
|
||||||
13
package.json
13
package.json
@@ -21,7 +21,8 @@
|
|||||||
"ver": "lerna version --no-private --conventional-commits",
|
"ver": "lerna version --no-private --conventional-commits",
|
||||||
"release": "yarn eslint && yarn build && yarn run ver && lerna publish from-git",
|
"release": "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",
|
"release:beta": "yarn eslint && yarn build && yarn run ver && lerna publish from-package --dist-tag beta",
|
||||||
"up": "yarn upgrade-interactive --latest"
|
"up": "yarn upgrade-interactive --latest",
|
||||||
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-env": "^7.19.3",
|
"@babel/preset-env": "^7.19.3",
|
||||||
@@ -49,11 +50,11 @@
|
|||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"http-server": "^14.1.0",
|
"http-server": "^14.1.0",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.3",
|
||||||
"jest": "^29.1.1",
|
"jest": "^29.1.1",
|
||||||
"jest-environment-jsdom": "^29.1.1",
|
"jest-environment-jsdom": "^29.1.1",
|
||||||
"lerna": "^6.6.1",
|
"lerna": "^6.6.1",
|
||||||
"lint-staged": "^13.1.0",
|
"lint-staged": "^14.0.1",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.7",
|
||||||
"react": "^17.0.0",
|
"react": "^17.0.0",
|
||||||
"react-dom": "^17.0.0",
|
"react-dom": "^17.0.0",
|
||||||
@@ -63,5 +64,11 @@
|
|||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@yarnpkg/parsers": "3.0.0-rc.48.1"
|
"@yarnpkg/parsers": "3.0.0-rc.48.1"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,jsx,ts,tsx}": [
|
||||||
|
"eslint --fix",
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,16 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
# [0.2.0](https://github.com/netease/tango/compare/@music163/tango-context@0.1.4...@music163/tango-context@0.2.0) (2023-09-21)
|
# [0.2.0](https://github.com/netease/tango/compare/@music163/tango-context@0.1.4...@music163/tango-context@0.2.0) (2023-09-21)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-context",
|
"name": "@music163/tango-context",
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"description": "react context for tango-apps",
|
"description": "react context for tango-apps",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"react": ">= 16.8"
|
"react": ">= 16.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@music163/tango-core": "^0.2.0",
|
"@music163/tango-core": "^0.2.1",
|
||||||
"@music163/tango-helpers": "^0.1.3",
|
"@music163/tango-helpers": "^0.1.3",
|
||||||
"mobx-react-lite": "4.0.2"
|
"mobx-react-lite": "4.0.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
import type { Engine } from '@music163/tango-core';
|
import type { Engine } from '@music163/tango-core';
|
||||||
import { createContext } from '@music163/tango-helpers';
|
import { createContext } from '@music163/tango-helpers';
|
||||||
|
|
||||||
|
interface CustomVariableData {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
children?: CustomVariableData[];
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITangoEngineContext {
|
export interface ITangoEngineContext {
|
||||||
/**
|
/**
|
||||||
* 低代码引擎
|
* 低代码引擎
|
||||||
*/
|
*/
|
||||||
engine: Engine;
|
engine: Engine;
|
||||||
|
/**
|
||||||
|
* 自定义配置数据
|
||||||
|
*/
|
||||||
|
config: {
|
||||||
|
customActionVariables: CustomVariableData[];
|
||||||
|
customExpressionVariables: CustomVariableData[];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const [TangoEngineProvider, useTangoEngine] = createContext<ITangoEngineContext>({
|
const [TangoEngineProvider, useTangoEngine] = createContext<ITangoEngineContext>({
|
||||||
@@ -22,31 +36,8 @@ export const useDesigner = () => {
|
|||||||
return useTangoEngine()?.engine.designer;
|
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 = () => {
|
export const useWorkspaceData = () => {
|
||||||
|
const ctx = useTangoEngine();
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const modelVariables: any[] = []; // 绑定变量列表
|
const modelVariables: any[] = []; // 绑定变量列表
|
||||||
const storeActionVariables: any[] = []; // 模型中的所有 actions
|
const storeActionVariables: any[] = []; // 模型中的所有 actions
|
||||||
@@ -111,20 +102,29 @@ export const useWorkspaceData = () => {
|
|||||||
value: item.path,
|
value: item.path,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
let actionVariables: CustomVariableData[] = [
|
||||||
|
buildVariableOptions('数据模型', '$stores', storeActionVariables),
|
||||||
|
buildVariableOptions('服务函数', '$services', serviceVariables),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (ctx.config?.customActionVariables) {
|
||||||
|
actionVariables = actionVariables.concat(ctx.config?.customActionVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
let expressionVariables: CustomVariableData[] = [
|
||||||
|
buildVariableOptions('数据模型', '$stores', storeVariables),
|
||||||
|
buildVariableOptions('服务函数', '$services', serviceVariables),
|
||||||
|
];
|
||||||
|
if (ctx.config?.customExpressionVariables) {
|
||||||
|
expressionVariables = expressionVariables.concat(ctx.config?.customExpressionVariables);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modelVariables: [buildVariableOptions('数据模型', 'stores', modelVariables)],
|
modelVariables: [buildVariableOptions('数据模型', 'stores', modelVariables)],
|
||||||
actionVariables: [
|
actionVariables,
|
||||||
buildVariableOptions('数据模型', 'stores', storeActionVariables),
|
|
||||||
buildVariableOptions('服务函数', 'services', serviceVariables),
|
|
||||||
buildVariableOptions('工具函数', 'helpers', builtinHelpers),
|
|
||||||
],
|
|
||||||
storeVariables,
|
storeVariables,
|
||||||
serviceVariables,
|
serviceVariables,
|
||||||
expressionVariables: [
|
expressionVariables,
|
||||||
buildVariableOptions('数据模型', 'stores', storeVariables),
|
|
||||||
buildVariableOptions('服务函数', 'services', serviceVariables),
|
|
||||||
buildVariableOptions('工具函数', 'helpers', builtinHelpers),
|
|
||||||
],
|
|
||||||
routeOptions,
|
routeOptions,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
- add listServiceFunctions ([09a1e21](https://github.com/netease/tango/commit/09a1e2135a1f51b0a5f6c0a507ba42d2e6355c24))
|
||||||
|
|
||||||
# [0.2.0](https://github.com/netease/tango/compare/@music163/tango-core@0.1.4...@music163/tango-core@0.2.0) (2023-09-21)
|
# [0.2.0](https://github.com/netease/tango/compare/@music163/tango-core@0.1.4...@music163/tango-core@0.2.0) (2023-09-21)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-core",
|
"name": "@music163/tango-core",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"description": "tango core",
|
"description": "tango core",
|
||||||
"author": "wwsun <ww.sun@outlook.com>",
|
"author": "wwsun <ww.sun@outlook.com>",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ export interface IWorkspace {
|
|||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
getServiceFunction?: (serviceKey: string) => object;
|
getServiceFunction?: (serviceKey: string) => object;
|
||||||
|
listServiceFunctions?: () => Record<string, object>;
|
||||||
removeServiceFunction?: (serviceName: string, modName?: string) => void;
|
removeServiceFunction?: (serviceName: string, modName?: string) => void;
|
||||||
addServiceFunction?: (
|
addServiceFunction?: (
|
||||||
payload: IServiceFunctionPayload | IServiceFunctionPayload[],
|
payload: IServiceFunctionPayload | IServiceFunctionPayload[],
|
||||||
|
|||||||
@@ -16,12 +16,19 @@ import { TangoModule } from './module';
|
|||||||
*/
|
*/
|
||||||
export class TangoServiceModule extends TangoModule {
|
export class TangoServiceModule extends TangoModule {
|
||||||
/**
|
/**
|
||||||
* 模块名
|
* 服务函数的模块名,默认为 index
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原始注册的服务函数
|
||||||
|
* @example { get: {}, list: {} }
|
||||||
|
*/
|
||||||
_serviceFunctions: Dict;
|
_serviceFunctions: Dict;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原始的基础配置
|
||||||
|
*/
|
||||||
_baseConfig: Dict;
|
_baseConfig: Dict;
|
||||||
|
|
||||||
get serviceFunctions() {
|
get serviceFunctions() {
|
||||||
|
|||||||
@@ -647,6 +647,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务函数的详情
|
* 获取服务函数的详情
|
||||||
|
* TODO: 不要 services 前缀
|
||||||
* @param serviceKey `services.list` 或 `services.sub.list`
|
* @param serviceKey `services.list` 或 `services.sub.list`
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
@@ -663,6 +664,23 @@ export class Workspace extends EventTarget implements IWorkspace {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务函数的列表
|
||||||
|
*/
|
||||||
|
listServiceFunctions() {
|
||||||
|
const ret: Record<string, object> = {};
|
||||||
|
Object.values(this.serviceModules).forEach((module) => {
|
||||||
|
Object.keys(module.serviceFunctions).forEach((name) => {
|
||||||
|
const serviceKey =
|
||||||
|
module.baseConfig.namespace !== 'index'
|
||||||
|
? name
|
||||||
|
: [module.baseConfig.namespace, name].join('.');
|
||||||
|
ret[serviceKey] = module.serviceFunctions[name];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新服务函数
|
* 更新服务函数
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,33 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [0.5.0](https://github.com/netease/tango/compare/@music163/tango-designer@0.4.1...@music163/tango-designer@0.5.0) (2023-10-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- refactor expSetter ([#41](https://github.com/netease/tango/issues/41)) ([d63517e](https://github.com/netease/tango/commit/d63517ecb936e4227e70c33e610664316625f4f4))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- add umd resources config when modifying dependency ([#36](https://github.com/netease/tango/issues/36)) ([e1bab66](https://github.com/netease/tango/commit/e1bab66819cf9a0bd7b87b72b2962498dfd51b2c))
|
||||||
|
|
||||||
|
## [0.4.1](https://github.com/netease/tango/compare/@music163/tango-designer@0.4.0...@music163/tango-designer@0.4.1) (2023-10-09)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- always switch to design mode after preview is on ([85898de](https://github.com/netease/tango/commit/85898dee19b6031016679e33a4ce9edbd1089713))
|
||||||
|
- update export entry ([e429610](https://github.com/netease/tango/commit/e4296106066ad290ac838135fe6a57bc50ad2680))
|
||||||
|
|
||||||
|
# [0.4.0](https://github.com/netease/tango/compare/@music163/tango-designer@0.3.0...@music163/tango-designer@0.4.0) (2023-09-22)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- remove useless api ([226d71c](https://github.com/netease/tango/commit/226d71c704f41a756ee777d6de4627a7eb7b4259))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- add global widget map ([1c72ee6](https://github.com/netease/tango/commit/1c72ee6ac287c1b6fb5eb3806a68b877f3749405))
|
||||||
|
|
||||||
# [0.3.0](https://github.com/netease/tango/compare/@music163/tango-designer@0.2.1...@music163/tango-designer@0.3.0) (2023-09-21)
|
# [0.3.0](https://github.com/netease/tango/compare/@music163/tango-designer@0.2.1...@music163/tango-designer@0.3.0) (2023-09-21)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-designer",
|
"name": "@music163/tango-designer",
|
||||||
"version": "0.3.0",
|
"version": "0.5.0",
|
||||||
"description": "lowcode designer",
|
"description": "lowcode designer",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react"
|
"react"
|
||||||
@@ -33,12 +33,12 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.8.0",
|
"@ant-design/icons": "^4.8.0",
|
||||||
"@music163/request": "^0.1.0",
|
"@music163/request": "^0.1.0",
|
||||||
"@music163/tango-context": "^0.2.0",
|
"@music163/tango-context": "^0.2.2",
|
||||||
"@music163/tango-core": "^0.2.0",
|
"@music163/tango-core": "^0.2.1",
|
||||||
"@music163/tango-helpers": "^0.1.3",
|
"@music163/tango-helpers": "^0.1.3",
|
||||||
"@music163/tango-sandbox": "^0.1.5",
|
"@music163/tango-sandbox": "^0.1.6",
|
||||||
"@music163/tango-setting-form": "^0.2.2",
|
"@music163/tango-setting-form": "^0.2.5",
|
||||||
"@music163/tango-ui": "^0.1.4",
|
"@music163/tango-ui": "^0.1.6",
|
||||||
"antd": "^4.24.2",
|
"antd": "^4.24.2",
|
||||||
"cash-dom": "^8.1.2",
|
"cash-dom": "^8.1.2",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
|
|||||||
@@ -11,10 +11,15 @@ import {
|
|||||||
Empty,
|
Empty,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Alert,
|
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { Box, Text, css, Link } from 'coral-system';
|
import { Box, Text, css } from 'coral-system';
|
||||||
import { PlusOutlined, FunctionOutlined, DeleteOutlined } from '@ant-design/icons';
|
import {
|
||||||
|
PlusOutlined,
|
||||||
|
FunctionOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
filterTreeData,
|
filterTreeData,
|
||||||
isFunction,
|
isFunction,
|
||||||
@@ -96,10 +101,6 @@ export interface EditableVariableTreeProps {
|
|||||||
* 选择列表项时的回调
|
* 选择列表项时的回调
|
||||||
*/
|
*/
|
||||||
onSelect?: (data: IVariableTreeNode) => void;
|
onSelect?: (data: IVariableTreeNode) => void;
|
||||||
/**
|
|
||||||
* 点击使用此变量按钮时的回调
|
|
||||||
*/
|
|
||||||
onUse?: (data: IVariableTreeNode) => void;
|
|
||||||
/**
|
/**
|
||||||
* 保存结点定义的回调
|
* 保存结点定义的回调
|
||||||
*/
|
*/
|
||||||
@@ -124,10 +125,6 @@ export interface EditableVariableTreeProps {
|
|||||||
* 搜索框的后置结点
|
* 搜索框的后置结点
|
||||||
*/
|
*/
|
||||||
searchAddonAfter?: React.ReactNode;
|
searchAddonAfter?: React.ReactNode;
|
||||||
/**
|
|
||||||
* 是否展示预览值的提示信息
|
|
||||||
*/
|
|
||||||
showPreviewAlert?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewOptions = [
|
const previewOptions = [
|
||||||
@@ -145,11 +142,9 @@ export function EditableVariableTree({
|
|||||||
onDeleteStore = noop,
|
onDeleteStore = noop,
|
||||||
getPreviewValue = noop,
|
getPreviewValue = noop,
|
||||||
onSelect = noop,
|
onSelect = noop,
|
||||||
onUse = noop,
|
|
||||||
onSave = noop,
|
onSave = noop,
|
||||||
modes = ['add', 'preview', 'define'],
|
modes = ['add', 'preview', 'define'],
|
||||||
defaultMode = 'preview',
|
defaultMode = 'preview',
|
||||||
showPreviewAlert = true,
|
|
||||||
}: EditableVariableTreeProps) {
|
}: EditableVariableTreeProps) {
|
||||||
const [keyword, setKeyword] = useState('');
|
const [keyword, setKeyword] = useState('');
|
||||||
const [node, setNode] = useState<IVariableTreeNode>();
|
const [node, setNode] = useState<IVariableTreeNode>();
|
||||||
@@ -184,11 +179,16 @@ export function EditableVariableTree({
|
|||||||
</Box>
|
</Box>
|
||||||
<VariableTree
|
<VariableTree
|
||||||
dataSource={treeData}
|
dataSource={treeData}
|
||||||
|
showViewIcon
|
||||||
onSelect={(item) => {
|
onSelect={(item) => {
|
||||||
setNode(item);
|
setNode(item);
|
||||||
mode === 'add' && setMode(defaultMode);
|
mode === 'add' && setMode(defaultMode);
|
||||||
onSelect(item);
|
onSelect(item);
|
||||||
}}
|
}}
|
||||||
|
onView={(item) => {
|
||||||
|
setNode(item);
|
||||||
|
mode === 'add' && setMode(defaultMode);
|
||||||
|
}}
|
||||||
onAdd={(item) => {
|
onAdd={(item) => {
|
||||||
setNode(item);
|
setNode(item);
|
||||||
setMode('add');
|
setMode('add');
|
||||||
@@ -225,33 +225,15 @@ export function EditableVariableTree({
|
|||||||
borderRadius="m"
|
borderRadius="m"
|
||||||
position="sticky"
|
position="sticky"
|
||||||
top="0"
|
top="0"
|
||||||
bodyProps={{ px: 'l' }}
|
bodyProps={{ px: 'm' }}
|
||||||
>
|
>
|
||||||
{mode === 'preview' && (
|
{mode === 'preview' && (
|
||||||
<>
|
<ValuePreview
|
||||||
<Box mb="m" display={showPreviewAlert ? 'block' : 'none'}>
|
value={getPreviewValue(node)}
|
||||||
<Alert
|
onSelect={(valuePath) => {
|
||||||
message={
|
return ['tango', node.key.replaceAll('.', '?.'), valuePath].join('.');
|
||||||
<Space>
|
}}
|
||||||
<Text>
|
/>
|
||||||
<Text as="strong">变量路径:</Text>tango.{node.key}
|
|
||||||
</Text>
|
|
||||||
<Link onClick={() => onUse(node)}>使用此变量</Link>
|
|
||||||
<CopyClipboard text={`tango.${node.key.replaceAll('.', '?.')}`}>
|
|
||||||
{(copied) => <Link>{copied ? '已复制' : '复制变量路径'}</Link>}
|
|
||||||
</CopyClipboard>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
type="info"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<ValuePreview
|
|
||||||
value={getPreviewValue(node)}
|
|
||||||
onSelect={(valuePath) => {
|
|
||||||
return ['tango', node.key.replaceAll('.', '?.'), valuePath].join('.');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
{mode === 'define' && (
|
{mode === 'define' && (
|
||||||
<NodeDefineForm
|
<NodeDefineForm
|
||||||
@@ -308,7 +290,7 @@ export function EditableVariableTreeModal({
|
|||||||
{React.cloneElement(trigger, { onClick: on })}
|
{React.cloneElement(trigger, { onClick: on })}
|
||||||
<Modal
|
<Modal
|
||||||
title={title}
|
title={title}
|
||||||
visible={visible}
|
open={visible}
|
||||||
onCancel={off}
|
onCancel={off}
|
||||||
okButtonProps={{
|
okButtonProps={{
|
||||||
disabled: !node,
|
disabled: !node,
|
||||||
@@ -345,6 +327,11 @@ const varTreeStyle = css`
|
|||||||
.ant-tree-indent-unit {
|
.ant-tree-indent-unit {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.anticon-function {
|
||||||
|
margin-left: 4px;
|
||||||
|
color: var(--tango-colors-text2);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface VariableTreeProps {
|
interface VariableTreeProps {
|
||||||
@@ -352,7 +339,10 @@ interface VariableTreeProps {
|
|||||||
onSelect?: (data: IVariableTreeNode) => void;
|
onSelect?: (data: IVariableTreeNode) => void;
|
||||||
onAdd?: (data: IVariableTreeNode) => void;
|
onAdd?: (data: IVariableTreeNode) => void;
|
||||||
onRemove?: (data: IVariableTreeNode) => void;
|
onRemove?: (data: IVariableTreeNode) => void;
|
||||||
deletable?: boolean;
|
onCopy?: (data: IVariableTreeNode) => void;
|
||||||
|
onView?: (data: IVariableTreeNode) => void;
|
||||||
|
showDeleteIcon?: boolean;
|
||||||
|
showViewIcon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VariableTree({
|
export function VariableTree({
|
||||||
@@ -360,11 +350,15 @@ export function VariableTree({
|
|||||||
onSelect = noop,
|
onSelect = noop,
|
||||||
onAdd = noop,
|
onAdd = noop,
|
||||||
onRemove = noop,
|
onRemove = noop,
|
||||||
deletable = false,
|
onCopy = noop,
|
||||||
|
onView = noop,
|
||||||
|
showDeleteIcon = false,
|
||||||
|
showViewIcon = false,
|
||||||
}: VariableTreeProps) {
|
}: VariableTreeProps) {
|
||||||
return (
|
return (
|
||||||
<Box className="VariableTree" css={varTreeStyle}>
|
<Box className="VariableTree" css={varTreeStyle}>
|
||||||
<Tree.DirectoryTree
|
<Tree
|
||||||
|
blockNode
|
||||||
showIcon={false}
|
showIcon={false}
|
||||||
defaultExpandAll
|
defaultExpandAll
|
||||||
treeData={dataSource}
|
treeData={dataSource}
|
||||||
@@ -372,28 +366,18 @@ export function VariableTree({
|
|||||||
onSelect(detail.node);
|
onSelect(detail.node);
|
||||||
}}
|
}}
|
||||||
titleRender={(node) => {
|
titleRender={(node) => {
|
||||||
// 只有叶子结点支持删除
|
const isLeaf = !node.children;
|
||||||
const showRemoveIcon = (!node.children && node.showDeleteIcon) ?? deletable;
|
|
||||||
const showAddIcon = node.showAddChildIcon ?? false;
|
if (isLeaf) {
|
||||||
if (showRemoveIcon || showAddIcon) {
|
const isDeletable = node.showDeleteIcon ?? showDeleteIcon;
|
||||||
return (
|
return (
|
||||||
<Box display="flex" justifyContent="space-between" alignItems="center" pr="l">
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||||
<Text>{node.title}</Text>
|
<Text>
|
||||||
|
{node.title}
|
||||||
|
{node.type === 'function' && <FunctionOutlined />}
|
||||||
|
</Text>
|
||||||
<Box>
|
<Box>
|
||||||
{showAddIcon && (
|
{isDeletable && (
|
||||||
<Tooltip title={`向 ${node.title} 中添加变量`}>
|
|
||||||
<Button
|
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
onAdd(node);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
{showRemoveIcon && (
|
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确认删除吗?该操作会导致引用此模型的代码报错,请谨慎操作!"
|
title="确认删除吗?该操作会导致引用此模型的代码报错,请谨慎操作!"
|
||||||
onConfirm={() => onRemove(node)}
|
onConfirm={() => onRemove(node)}
|
||||||
@@ -408,14 +392,61 @@ export function VariableTree({
|
|||||||
/>
|
/>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
)}
|
)}
|
||||||
|
<CopyClipboard text={`tango.${node.key.replaceAll('.', '?.')}`}>
|
||||||
|
{({ copied, onClick }) => {
|
||||||
|
const label = copied ? '已复制' : '复制变量路径';
|
||||||
|
return (
|
||||||
|
<Tooltip title={label}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
icon={<CopyOutlined />}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onClick();
|
||||||
|
onCopy(node);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</CopyClipboard>
|
||||||
|
{showViewIcon && (
|
||||||
|
<Tooltip title="查看变量详情">
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
icon={<EyeOutlined />}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onView(node);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||||
<Text mr="m">{node.title}</Text>
|
<Text mr="m">{node.title}</Text>
|
||||||
{node.type === 'function' && <FunctionOutlined />}
|
<Box>
|
||||||
|
{node.showAddChildIcon && (
|
||||||
|
<Tooltip title={`向 ${node.title} 中添加变量`}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onAdd(node);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export interface DesignerProps extends IDesignerContext, ITangoEngineContext {
|
|||||||
export function Designer(props: DesignerProps) {
|
export function Designer(props: DesignerProps) {
|
||||||
const {
|
const {
|
||||||
engine,
|
engine,
|
||||||
|
config,
|
||||||
theme: themeProp,
|
theme: themeProp,
|
||||||
sandboxQuery,
|
sandboxQuery,
|
||||||
remoteServices = {},
|
remoteServices = {},
|
||||||
@@ -42,7 +43,7 @@ export function Designer(props: DesignerProps) {
|
|||||||
return (
|
return (
|
||||||
<SystemProvider theme={theme} prefix="--tango">
|
<SystemProvider theme={theme} prefix="--tango">
|
||||||
<ConfigProvider locale={zhCN}>
|
<ConfigProvider locale={zhCN}>
|
||||||
<TangoEngineProvider value={{ engine }}>
|
<TangoEngineProvider value={{ engine, config }}>
|
||||||
<DesignerProvider value={{ sandboxQuery, remoteServices }}>{children}</DesignerProvider>
|
<DesignerProvider value={{ sandboxQuery, remoteServices }}>{children}</DesignerProvider>
|
||||||
</TangoEngineProvider>
|
</TangoEngineProvider>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export * from './sandbox';
|
|||||||
export * from './sidebar';
|
export * from './sidebar';
|
||||||
export * from './toolbar';
|
export * from './toolbar';
|
||||||
export * from './selection-menu';
|
export * from './selection-menu';
|
||||||
|
export * from './widgets';
|
||||||
|
|
||||||
export { register as registerSetter } from '@music163/tango-setting-form';
|
export { register as registerSetter } from '@music163/tango-setting-form';
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ interface ISandboxEventHandlerConfig {
|
|||||||
export type SandboxProps = Omit<CodeSandboxProps, 'files' | 'eventHandlers' | 'onMessage'> & {
|
export type SandboxProps = Omit<CodeSandboxProps, 'files' | 'eventHandlers' | 'onMessage'> & {
|
||||||
isPreview?: boolean;
|
isPreview?: boolean;
|
||||||
selectionTools?: SelectionToolsProps['actions'];
|
selectionTools?: SelectionToolsProps['actions'];
|
||||||
builtinSelectionMenuMap?: SelectionToolsProps['builtinActionMap'];
|
|
||||||
/**
|
/**
|
||||||
* tangoConfigJson 处理器
|
* tangoConfigJson 处理器
|
||||||
*/
|
*/
|
||||||
@@ -32,10 +31,10 @@ export type SandboxProps = Omit<CodeSandboxProps, 'files' | 'eventHandlers' | 'o
|
|||||||
onLoad?: (config?: ISandboxEventHandlerConfig) => void;
|
onLoad?: (config?: ISandboxEventHandlerConfig) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CombinedSandboxRef = {
|
export interface CombinedSandboxRef {
|
||||||
designSandbox: CodeSandbox;
|
designSandbox: CodeSandbox;
|
||||||
previewSandbox: CodeSandbox;
|
previewSandbox: CodeSandbox;
|
||||||
};
|
}
|
||||||
|
|
||||||
const LANDING_PAGE_PATH = '/__background_landing_page__';
|
const LANDING_PAGE_PATH = '/__background_landing_page__';
|
||||||
|
|
||||||
@@ -281,7 +280,6 @@ export const Sandbox = observer(
|
|||||||
({
|
({
|
||||||
isPreview: isPreviewProp,
|
isPreview: isPreviewProp,
|
||||||
selectionTools,
|
selectionTools,
|
||||||
builtinSelectionMenuMap,
|
|
||||||
bundlerURL,
|
bundlerURL,
|
||||||
mode = 'combined',
|
mode = 'combined',
|
||||||
...props
|
...props
|
||||||
@@ -352,7 +350,7 @@ export const Sandbox = observer(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Simulator>
|
<Simulator>
|
||||||
<Viewport selectionTools={selectionTools} builtinActionMap={builtinSelectionMenuMap}>
|
<Viewport selectionTools={selectionTools}>
|
||||||
{mode === 'single' && (
|
{mode === 'single' && (
|
||||||
<DesignSandbox
|
<DesignSandbox
|
||||||
ref={sandboxRef}
|
ref={sandboxRef}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { Box, css } from 'coral-system';
|
import { Box, css } from 'coral-system';
|
||||||
import { Alert, Modal, Tabs } from 'antd';
|
import { Modal } from 'antd';
|
||||||
import { value2code, isValidExpressionCode } from '@music163/tango-core';
|
import { value2code, isValidExpressionCode } from '@music163/tango-core';
|
||||||
import {
|
import {
|
||||||
isVariableString,
|
isVariableString,
|
||||||
@@ -9,12 +9,12 @@ import {
|
|||||||
useBoolean,
|
useBoolean,
|
||||||
getValue,
|
getValue,
|
||||||
} from '@music163/tango-helpers';
|
} from '@music163/tango-helpers';
|
||||||
import { CloseCircleFilled, ExpandAltOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
import { CloseCircleFilled, ExpandAltOutlined } from '@ant-design/icons';
|
||||||
import { IconButton, Panel, InputCode, ChatInput } from '@music163/tango-ui';
|
import { IconButton, Panel, InputCode } from '@music163/tango-ui';
|
||||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||||
import { EditableVariableTree, IVariableTreeNode } from '../components';
|
import { EditableVariableTree, IVariableTreeNode } from '../components';
|
||||||
import { useRemoteServices, useSandboxQuery } from '../context';
|
import { useSandboxQuery } from '../context';
|
||||||
|
|
||||||
export const expressionValueValidate = (value: string) => {
|
export const expressionValueValidate = (value: string) => {
|
||||||
if (isVariableString(value)) {
|
if (isVariableString(value)) {
|
||||||
@@ -190,38 +190,24 @@ export function ExpressionModal({
|
|||||||
[workspace],
|
[workspace],
|
||||||
);
|
);
|
||||||
const sandbox = useSandboxQuery();
|
const sandbox = useSandboxQuery();
|
||||||
const remoteServices = useRemoteServices();
|
|
||||||
const { expressionVariables } = useWorkspaceData();
|
const { expressionVariables } = useWorkspaceData();
|
||||||
const evaluateContext = sandbox.window;
|
const evaluateContext = sandbox.window;
|
||||||
const gptService = remoteServices?.GptService;
|
|
||||||
|
|
||||||
const handleExpInputChange = (val: string) => {
|
const handleExpInputChange = (val: string) => {
|
||||||
setExp(val?.trim());
|
setExp(val?.trim());
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={title}
|
closable={false}
|
||||||
|
destroyOnClose
|
||||||
width="70%"
|
width="70%"
|
||||||
open={visible}
|
open={visible}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
onOk(exp);
|
onOk(exp);
|
||||||
}}
|
}}
|
||||||
destroyOnClose
|
|
||||||
>
|
>
|
||||||
<Panel
|
<Panel title={`将 ${title} 设置为引用变量或自定义表达式`} subTitle={subTitle} shape="solid">
|
||||||
title="绑定的表达式"
|
|
||||||
subTitle={subTitle}
|
|
||||||
extra={
|
|
||||||
<IconButton
|
|
||||||
icon={<QuestionCircleOutlined />}
|
|
||||||
tooltip="你可以直接编辑下面的表达式,表达式需要使用 { } 进行包裹,点击此图标可以查看详细文档。"
|
|
||||||
href="https://music-doc.st.netease.com/st/tango-docs/docs/guide/javascript/overview"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
shape="solid"
|
|
||||||
borderRadius="m"
|
|
||||||
>
|
|
||||||
<InputCode
|
<InputCode
|
||||||
shape="inset"
|
shape="inset"
|
||||||
minHeight="56px"
|
minHeight="56px"
|
||||||
@@ -233,91 +219,49 @@ export function ExpressionModal({
|
|||||||
autoCompleteOptions={autoCompleteOptions}
|
autoCompleteOptions={autoCompleteOptions}
|
||||||
/>
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
<Box mt="m" maxHeight={400} overflow="auto">
|
<Panel title="从变量列表中选中" shape="solid" borderTop="0" maxHeight={400} overflow="auto">
|
||||||
<Tabs type="card" size="small">
|
<EditableVariableTree
|
||||||
<Tabs.TabPane key="list" tab="从变量列表中选择">
|
dataSource={dataSource || expressionVariables}
|
||||||
<Box my="m">
|
onSelect={(node) => {
|
||||||
<Alert
|
if (!node.key) {
|
||||||
type="info"
|
return;
|
||||||
message="你可以直接从下方的列表中选择已定义的变量"
|
}
|
||||||
banner
|
if (node.key.split('.').length < 2) {
|
||||||
closable
|
return;
|
||||||
/>
|
}
|
||||||
</Box>
|
let str;
|
||||||
<EditableVariableTree
|
if (/^(stores|services)\./.test(node.key)) {
|
||||||
// height={400}
|
str = `{tango.${node.key.replaceAll('.', '?.')}}`;
|
||||||
dataSource={dataSource || expressionVariables}
|
} else {
|
||||||
onUse={(node) => {
|
str = `{${node.key}}`;
|
||||||
let str;
|
}
|
||||||
if (/^(stores|services)\./.test(node.key)) {
|
setExp(str);
|
||||||
str = `{tango.${node.key.replaceAll('.', '?.')}}`;
|
}}
|
||||||
} else {
|
onAddVariable={(storeName, data) => {
|
||||||
str = `{${node.key}}`;
|
onAction('addStoreState', [storeName, data.name, data.initialValue]);
|
||||||
}
|
}}
|
||||||
setExp(str);
|
onDeleteVariable={(storeName, stateName) => {
|
||||||
}}
|
onAction('removeStoreState', [storeName, stateName]);
|
||||||
onAddVariable={(storeName, data) => {
|
}}
|
||||||
onAction('addStoreState', [storeName, data.name, data.initialValue]);
|
onDeleteStore={(storeName) => {
|
||||||
}}
|
onAction('removeStoreModule', [storeName]);
|
||||||
onDeleteVariable={(storeName, stateName) => {
|
}}
|
||||||
onAction('removeStoreState', [storeName, stateName]);
|
onSave={(code, node) => {
|
||||||
}}
|
onAction('updateModuleCodeByVariablePath', [node.key, code]);
|
||||||
onDeleteStore={(storeName) => {
|
}}
|
||||||
onAction('removeStoreModule', [storeName]);
|
getPreviewValue={(node) => {
|
||||||
}}
|
if (!node || !node.key) {
|
||||||
onSave={(code, node) => {
|
return;
|
||||||
onAction('updateModuleCodeByVariablePath', [node.key, code]);
|
}
|
||||||
}}
|
|
||||||
getPreviewValue={(node) => {
|
|
||||||
if (!node || !node.key) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.type === 'function') {
|
if (node.type === 'function') {
|
||||||
return node.raw;
|
return node.raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getValue(evaluateContext['tango'], node.key);
|
return getValue(evaluateContext['tango'], node.key);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs.TabPane>
|
</Panel>
|
||||||
<Tabs.TabPane key="ai" tab="使用 AI 助手生成" disabled={!gptService?.textCompletion}>
|
|
||||||
<ChatInput
|
|
||||||
placeholder="例如:实现一个函数,从给定的数组中过滤出name为bob的数据"
|
|
||||||
onGenerate={(text) =>
|
|
||||||
gptService
|
|
||||||
.textCompletion({
|
|
||||||
prompt: getExpressionCode(text),
|
|
||||||
model: 'gpt-3.5-turbo',
|
|
||||||
temperature: 0.5,
|
|
||||||
topP: 1,
|
|
||||||
presencePenalty: 0,
|
|
||||||
frequencyPenalty: 0,
|
|
||||||
mode: 'chat',
|
|
||||||
})
|
|
||||||
.then((res: any) => {
|
|
||||||
return res?.detail?.choices?.[0]?.text;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
onApply={(preview) => {
|
|
||||||
setExp(`{${preview}}`);
|
|
||||||
}}
|
|
||||||
showCloseIcon={false}
|
|
||||||
/>
|
|
||||||
</Tabs.TabPane>
|
|
||||||
</Tabs>
|
|
||||||
</Box>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExpressionCode(userInput: string) {
|
|
||||||
return `你是一个代码生成助手。你使用的编程语言是 JavaScript。不要解释代码,只返回代码块本身,代码必须包裹在 {} 内部。
|
|
||||||
标准的输出格式如下:
|
|
||||||
1. {this.a}
|
|
||||||
2. {() => {}}
|
|
||||||
3. {tango.stores.user.name}
|
|
||||||
|
|
||||||
${userInput}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ export function ModelSetter({ value, onChange }: FormItemComponentProps) {
|
|||||||
onBlur={onInputBlur}
|
onBlur={onInputBlur}
|
||||||
suffix={
|
suffix={
|
||||||
<EditableVariableTreeModal
|
<EditableVariableTreeModal
|
||||||
showPreviewAlert={false}
|
|
||||||
title="同步到的变量"
|
title="同步到的变量"
|
||||||
trigger={
|
trigger={
|
||||||
<Tooltip title="从模型列表选择" placement="topRight">
|
<Tooltip title="从模型列表选择" placement="topRight">
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const DataSourceView = observer(({ onAdd, onUpdate, onDelete }: DataServiceViewP
|
|||||||
</Box>
|
</Box>
|
||||||
<VariableTree
|
<VariableTree
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
deletable
|
showDeleteIcon
|
||||||
onSelect={(item) => {
|
onSelect={(item) => {
|
||||||
setServiceData({ key: item.key, ...workspace.getServiceFunction(item.key) });
|
setServiceData({ key: item.key, ...workspace.getServiceFunction(item.key) });
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { observer, useWorkspace } from '@music163/tango-context';
|
import { observer, useWorkspace } from '@music163/tango-context';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -9,15 +9,17 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
Radio,
|
Radio,
|
||||||
Select,
|
|
||||||
message,
|
message,
|
||||||
Tag,
|
Tag,
|
||||||
|
Checkbox,
|
||||||
|
ModalProps,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
|
import { FormListProps } from 'antd/lib/form';
|
||||||
import { Box, Text } from 'coral-system';
|
import { Box, Text } from 'coral-system';
|
||||||
import semverLt from 'semver/functions/lt';
|
import semverLt from 'semver/functions/lt';
|
||||||
import semverValid from 'semver/functions/valid';
|
import semverValid from 'semver/functions/valid';
|
||||||
import { ConfigGroup, ConfigItem } from '@music163/tango-ui';
|
import { ConfigGroup, ConfigItem } from '@music163/tango-ui';
|
||||||
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
import { MinusCircleOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
import { useBoolean } from '@music163/tango-helpers';
|
import { useBoolean } from '@music163/tango-helpers';
|
||||||
import { isUndefined } from 'lodash-es';
|
import { isUndefined } from 'lodash-es';
|
||||||
import { Workspace } from '@music163/tango-core';
|
import { Workspace } from '@music163/tango-core';
|
||||||
@@ -177,18 +179,9 @@ function RenderItem({
|
|||||||
onUpgrade,
|
onUpgrade,
|
||||||
showTags = true,
|
showTags = true,
|
||||||
}: RenderItemProps) {
|
}: RenderItemProps) {
|
||||||
const [version, setVersion] = useState<string>();
|
|
||||||
const [versionList, setVersionList] = useState<Array<{ label: string; value: string }>>([]);
|
|
||||||
const [open, { on, off }] = useBoolean(false);
|
const [open, { on, off }] = useBoolean(false);
|
||||||
|
|
||||||
const workspace = useWorkspace() as Workspace;
|
const workspace = useWorkspace() as Workspace;
|
||||||
// const remoteServices = useRemoteServices();
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// remoteServices?.DependencyService?.listPackageVersions({ packageName: record.name }).then(
|
|
||||||
// (data: any) => setVersionList((data || []).map((v: string) => ({ label: v, value: v }))),
|
|
||||||
// );
|
|
||||||
// }, [remoteServices, record]);
|
|
||||||
|
|
||||||
const basePackage = useMemo(() => {
|
const basePackage = useMemo(() => {
|
||||||
if (type !== DependencyItemType.基础包 || !templateBaseDependencies) return undefined;
|
if (type !== DependencyItemType.基础包 || !templateBaseDependencies) return undefined;
|
||||||
@@ -198,9 +191,29 @@ function RenderItem({
|
|||||||
/**
|
/**
|
||||||
* 处理依赖升级的逻辑
|
* 处理依赖升级的逻辑
|
||||||
*/
|
*/
|
||||||
function onDepUpgrade() {
|
function onDepUpgrade(values: Record<string, any>) {
|
||||||
// 根据是否有 packages 字段,判断是否已升级为新的规范
|
// 根据是否有 packages 字段,判断是否已升级为新的规范
|
||||||
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
||||||
|
const { version, umd, library, resources, designerResources } = values || {};
|
||||||
|
|
||||||
|
let packageConfig = record?.package;
|
||||||
|
if (umd === false && packageConfig) {
|
||||||
|
packageConfig = {
|
||||||
|
...packageConfig,
|
||||||
|
version,
|
||||||
|
library: undefined,
|
||||||
|
resources: undefined,
|
||||||
|
designerResources: undefined,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
packageConfig = {
|
||||||
|
...packageConfig,
|
||||||
|
version,
|
||||||
|
library,
|
||||||
|
resources,
|
||||||
|
designerResources,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DependencyItemType.业务组件:
|
case DependencyItemType.业务组件:
|
||||||
@@ -210,7 +223,7 @@ function RenderItem({
|
|||||||
version,
|
version,
|
||||||
hasUpgraded
|
hasUpgraded
|
||||||
? {
|
? {
|
||||||
package: record?.package,
|
package: packageConfig,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
);
|
);
|
||||||
@@ -225,7 +238,7 @@ function RenderItem({
|
|||||||
basePackage?.version || 'latest',
|
basePackage?.version || 'latest',
|
||||||
hasUpgraded
|
hasUpgraded
|
||||||
? {
|
? {
|
||||||
package: record?.package,
|
package: packageConfig,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
);
|
);
|
||||||
@@ -295,7 +308,6 @@ function RenderItem({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onCloseModal() {
|
function onCloseModal() {
|
||||||
setVersion(undefined);
|
|
||||||
off();
|
off();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,10 +329,8 @@ function RenderItem({
|
|||||||
const { resources } = packageInfo || {};
|
const { resources } = packageInfo || {};
|
||||||
const dependencies = workspace.packageJson?.getValue('dependencies');
|
const dependencies = workspace.packageJson?.getValue('dependencies');
|
||||||
const hasUmd = !!resources?.length;
|
const hasUmd = !!resources?.length;
|
||||||
const unsafeUmd =
|
const packageJsonIncluded = !!dependencies?.[name];
|
||||||
hasUmd && resources.some((e: string) => /\/\/unpkg\.f[nt]\.netease\.com\//.test(e));
|
const tangoConfigJsonIncluded = !!packageInfo;
|
||||||
const packageJsonOnly = dependencies?.[name] && !packageInfo;
|
|
||||||
const tangoConfigJsonOnly = !dependencies?.[name] && packageInfo;
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
hasUmd && (
|
hasUmd && (
|
||||||
@@ -330,21 +340,14 @@ function RenderItem({
|
|||||||
</Tag>
|
</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
),
|
),
|
||||||
unsafeUmd && (
|
packageJsonIncluded && (
|
||||||
<Tooltip title="该依赖的 UMD 资源来自自建 unpkg 服务,稳定性不可靠,建议修改 tango.config.json 更换为正式 cdn 资源地址">
|
<Tooltip title="此依赖在 package.json 中存在">
|
||||||
<Tag key="unsafeUmd" color="orange">
|
<Tag key="packageJson">package</Tag>
|
||||||
非 CDN
|
|
||||||
</Tag>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
),
|
),
|
||||||
packageJsonOnly && (
|
tangoConfigJsonIncluded && (
|
||||||
<Tooltip title="此依赖只在 package.json 中存在">
|
<Tooltip title="此依赖在 tango.config.json 中存在">
|
||||||
<Tag key="packageJsonOnly">package.json</Tag>
|
<Tag key="tangoConfigJson">tango.config</Tag>
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
tangoConfigJsonOnly && (
|
|
||||||
<Tooltip title="此依赖只在 tango.config.json 中存在">
|
|
||||||
<Tag key="packageJsonOnly">tango.config.json</Tag>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
),
|
),
|
||||||
].filter((e) => e);
|
].filter((e) => e);
|
||||||
@@ -356,7 +359,7 @@ function RenderItem({
|
|||||||
type !== DependencyItemType.基础包
|
type !== DependencyItemType.基础包
|
||||||
? [
|
? [
|
||||||
<a key="upgrade" onClick={on}>
|
<a key="upgrade" onClick={on}>
|
||||||
升级
|
修改
|
||||||
</a>,
|
</a>,
|
||||||
<Popconfirm onConfirm={onDepDelete} title={`确认删除依赖 ${record.name}?`}>
|
<Popconfirm onConfirm={onDepDelete} title={`确认删除依赖 ${record.name}?`}>
|
||||||
<a key="delete">删除</a>
|
<a key="delete">删除</a>
|
||||||
@@ -366,7 +369,7 @@ function RenderItem({
|
|||||||
? [
|
? [
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={`确认升级 ${record.name} 到 ${basePackage.version} 吗?`}
|
title={`确认升级 ${record.name} 到 ${basePackage.version} 吗?`}
|
||||||
onConfirm={onDepUpgrade}
|
onConfirm={() => onDepUpgrade({ version: basePackage.version })}
|
||||||
>
|
>
|
||||||
<a key="upgrade">升级</a>
|
<a key="upgrade">升级</a>
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
@@ -394,28 +397,12 @@ function RenderItem({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Modal
|
<DependencyConfigModal
|
||||||
open={open}
|
open={open}
|
||||||
title={`升级依赖:${record.name}`}
|
record={record}
|
||||||
onCancel={onCloseModal}
|
onCancel={onCloseModal}
|
||||||
onOk={onDepUpgrade}
|
onOk={(event, { values }) => onDepUpgrade(values)}
|
||||||
>
|
/>
|
||||||
<Text mr="6px">
|
|
||||||
选择依赖版本号
|
|
||||||
<Tooltip title="只提供正式版本列表,Beta版本依赖需要手动升级">
|
|
||||||
<QuestionCircleOutlined />
|
|
||||||
</Tooltip>
|
|
||||||
</Text>
|
|
||||||
<Select
|
|
||||||
style={{ width: 160 }}
|
|
||||||
showSearch
|
|
||||||
defaultValue={record.version}
|
|
||||||
options={versionList || []}
|
|
||||||
onChange={(value: string) => setVersion(value)}
|
|
||||||
placeholder="请选择要升级的版本号"
|
|
||||||
optionFilterProp="value"
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
</List.Item>
|
</List.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -447,111 +434,114 @@ interface DependencyModalProps {
|
|||||||
templateBaseDependencies: Record<string, any>;
|
templateBaseDependencies: Record<string, any>;
|
||||||
onBizDependencyAdd: Function;
|
onBizDependencyAdd: Function;
|
||||||
onBaseDependencyAdd: Function;
|
onBaseDependencyAdd: Function;
|
||||||
|
record?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddDependencyModal({
|
function AddDependencyModal({
|
||||||
templateBaseDependencies,
|
templateBaseDependencies,
|
||||||
onBizDependencyAdd,
|
onBizDependencyAdd,
|
||||||
onBaseDependencyAdd,
|
onBaseDependencyAdd,
|
||||||
}: DependencyModalProps) {
|
record,
|
||||||
|
...rest
|
||||||
|
}: DependencyModalProps & ModalProps) {
|
||||||
// 依赖类型
|
// 依赖类型
|
||||||
const [type, setType] = useState<DependencyItemType>(DependencyItemType.其他依赖);
|
|
||||||
const [open, { on, off }] = useBoolean();
|
const [open, { on, off }] = useBoolean();
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const sandbox = useSandboxQuery();
|
const sandbox = useSandboxQuery();
|
||||||
|
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
||||||
|
|
||||||
const onFinishCallback = (name?: string) => {
|
const onFinishCallback = (name?: string) => {
|
||||||
name && message.success(`${name} 添加成功`);
|
name && message.success(`${name} 添加成功`);
|
||||||
// FIXME: 修复沙箱添加组件后 HMR 失效的 bug,添加完刷新
|
// FIXME: 修复沙箱添加组件后 HMR 失效的 bug,添加完刷新
|
||||||
sandbox.reload();
|
sandbox.reload();
|
||||||
off();
|
off();
|
||||||
form.resetFields();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOk = () => {
|
const onOk: DependencyConfigModalProps['onOk'] = (event, { values }) => {
|
||||||
form.validateFields().then((values) => {
|
const { type, name, version, umd, library, resources, designerResources } = values;
|
||||||
const { name, version } = values;
|
|
||||||
// 根据是否有 packages 字段,判断是否已升级为新的规范
|
|
||||||
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DependencyItemType.其他依赖: {
|
case DependencyItemType.其他依赖: {
|
||||||
workspace.updateDependency(name, version);
|
workspace.updateDependency(
|
||||||
break;
|
name,
|
||||||
}
|
version,
|
||||||
// case DependencyItemType.业务组件: {
|
hasUpgraded && umd
|
||||||
// const targetBizInfo = (bizList as any)?.find(
|
? {
|
||||||
// (biz: { packageName: string }) => biz.packageName === name,
|
package: {
|
||||||
// );
|
type: 'dependency',
|
||||||
// onBizDependencyAdd &&
|
...record?.package,
|
||||||
// onBizDependencyAdd({
|
version,
|
||||||
// ...values,
|
library,
|
||||||
// detail: targetBizInfo,
|
resources,
|
||||||
// });
|
designerResources,
|
||||||
// break;
|
},
|
||||||
// }
|
}
|
||||||
case DependencyItemType.基础包: {
|
: {},
|
||||||
const basePackage = templateBaseDependencies[name];
|
);
|
||||||
// 添加 package.json
|
break;
|
||||||
workspace.updateDependency(
|
|
||||||
name,
|
|
||||||
basePackage.version || 'latest',
|
|
||||||
hasUpgraded
|
|
||||||
? {
|
|
||||||
package: basePackage.package,
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
// 配置已经合并为新版,不需要走旧的逻辑
|
|
||||||
if (!hasUpgraded) {
|
|
||||||
// 更新 tango.config.json baseDependencies
|
|
||||||
workspace.tangoConfigJson.setValue('baseDependencies', (targetValue) => {
|
|
||||||
const bases = targetValue || [];
|
|
||||||
(bases as string[]).push(name);
|
|
||||||
return bases;
|
|
||||||
});
|
|
||||||
// 更新 tango.config.json externals
|
|
||||||
workspace.tangoConfigJson.setValue('sandbox.externals', (targetValue) => {
|
|
||||||
targetValue[name] = basePackage?.global;
|
|
||||||
return targetValue;
|
|
||||||
});
|
|
||||||
// 更新 tango.config.json externalResources
|
|
||||||
workspace.tangoConfigJson
|
|
||||||
.setValue('sandbox.externalResources', (targetValue) => {
|
|
||||||
basePackage.resources.forEach((resource: string) => {
|
|
||||||
(targetValue as string[]).push(
|
|
||||||
resource.replace('{latest_version}', basePackage.version),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return targetValue;
|
|
||||||
})
|
|
||||||
.update();
|
|
||||||
}
|
|
||||||
onBaseDependencyAdd && onBaseDependencyAdd(basePackage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
onFinishCallback(values?.name);
|
// case DependencyItemType.业务组件: {
|
||||||
});
|
// const targetBizInfo = (bizList as any)?.find(
|
||||||
};
|
// (biz: { packageName: string }) => biz.packageName === name,
|
||||||
|
// );
|
||||||
|
// onBizDependencyAdd &&
|
||||||
|
// onBizDependencyAdd({
|
||||||
|
// ...values,
|
||||||
|
// detail: targetBizInfo,
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
case DependencyItemType.基础包: {
|
||||||
|
const basePackage = templateBaseDependencies[name];
|
||||||
|
// 添加 package.json
|
||||||
|
workspace.updateDependency(
|
||||||
|
name,
|
||||||
|
basePackage.version || 'latest',
|
||||||
|
hasUpgraded
|
||||||
|
? {
|
||||||
|
package: basePackage.package,
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
);
|
||||||
|
|
||||||
const onValuesChange = ({ type: depType }: { type: DependencyItemType; name: string }) => {
|
// 配置已经合并为新版,不需要走旧的逻辑
|
||||||
if (depType) {
|
if (!hasUpgraded) {
|
||||||
// type 变更触发
|
// 更新 tango.config.json baseDependencies
|
||||||
form.resetFields(['name', 'version']);
|
workspace.tangoConfigJson.setValue('baseDependencies', (targetValue) => {
|
||||||
setType(depType);
|
const bases = targetValue || [];
|
||||||
|
(bases as string[]).push(name);
|
||||||
|
return bases;
|
||||||
|
});
|
||||||
|
// 更新 tango.config.json externals
|
||||||
|
workspace.tangoConfigJson.setValue('sandbox.externals', (targetValue) => {
|
||||||
|
targetValue[name] = basePackage?.global;
|
||||||
|
return targetValue;
|
||||||
|
});
|
||||||
|
// 更新 tango.config.json externalResources
|
||||||
|
workspace.tangoConfigJson
|
||||||
|
.setValue('sandbox.externalResources', (targetValue) => {
|
||||||
|
basePackage.resources.forEach((resource: string) => {
|
||||||
|
(targetValue as string[]).push(
|
||||||
|
resource.replace('{latest_version}', basePackage.version),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return targetValue;
|
||||||
|
})
|
||||||
|
.update();
|
||||||
|
}
|
||||||
|
onBaseDependencyAdd && onBaseDependencyAdd(basePackage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
onFinishCallback(values?.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCloseModal = () => {
|
function onCloseModal() {
|
||||||
off();
|
off();
|
||||||
form.resetFields(['name', 'version']);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px="l" py="m">
|
<Box px="l" py="m">
|
||||||
@@ -559,31 +549,155 @@ function AddDependencyModal({
|
|||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
添加依赖
|
添加依赖
|
||||||
</Button>
|
</Button>
|
||||||
<Modal
|
<DependencyConfigModal
|
||||||
width={560}
|
|
||||||
open={open}
|
open={open}
|
||||||
title="添加依赖"
|
title={record ? `修改依赖:${record.name}` : '添加依赖'}
|
||||||
onOk={onOk}
|
onOk={onOk}
|
||||||
onCancel={onCloseModal}
|
onCancel={onCloseModal}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type DependencyConfigModalProps = Omit<ModalProps, 'onOk'> & {
|
||||||
|
record?: Record<string, any>;
|
||||||
|
onOk: (
|
||||||
|
event: React.MouseEvent<HTMLElement, MouseEvent>,
|
||||||
|
data: {
|
||||||
|
values: Record<string, any>;
|
||||||
|
hasUpgraded: boolean;
|
||||||
|
},
|
||||||
|
) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ResourceFormList(props: Omit<FormListProps, 'children'>) {
|
||||||
|
return (
|
||||||
|
<Form.List {...props}>
|
||||||
|
{(fields, { add, remove }, { errors }) => (
|
||||||
|
<>
|
||||||
|
{fields.map((field) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<Box display="flex" alignItems="center">
|
||||||
|
<Form.Item
|
||||||
|
label="资源地址"
|
||||||
|
{...field}
|
||||||
|
rules={[{ required: true, whitespace: true, type: 'url' }]}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="资源地址中的版本号可用 {{version}} 替代"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Box width="20px" fontSize="18px" ml="6px" textAlign="center" flex="none">
|
||||||
|
<a onClick={() => remove(field.name)}>
|
||||||
|
<MinusCircleOutlined />
|
||||||
|
</a>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Button type="dashed" onClick={() => add()} block>
|
||||||
|
添加
|
||||||
|
</Button>
|
||||||
|
<Form.ErrorList errors={errors} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Form.List>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DependencyConfigModal({
|
||||||
|
record,
|
||||||
|
open,
|
||||||
|
onOk,
|
||||||
|
onCancel,
|
||||||
|
...rest
|
||||||
|
}: DependencyConfigModalProps) {
|
||||||
|
// 依赖类型
|
||||||
|
const [type, setType] = useState<DependencyItemType>(DependencyItemType.其他依赖);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const workspace = useWorkspace();
|
||||||
|
// 根据是否有 packages 字段,判断是否已升级为新的规范
|
||||||
|
const hasUpgraded = !!workspace.tangoConfigJson?.getValue('packages');
|
||||||
|
// const [versionList, setVersionList] = useState<Array<{ label: string; value: string }>>([]);
|
||||||
|
|
||||||
|
// const remoteServices = useRemoteServices();
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// remoteServices?.DependencyService?.listPackageVersions({ packageName: record.name }).then(
|
||||||
|
// (data: any) => setVersionList((data || []).map((v: string) => ({ label: v, value: v }))),
|
||||||
|
// );
|
||||||
|
// }, [remoteServices, record]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && form) {
|
||||||
|
form.resetFields();
|
||||||
|
if (record) {
|
||||||
|
setType(record?.type);
|
||||||
|
form.setFieldsValue({
|
||||||
|
...record,
|
||||||
|
...record?.package,
|
||||||
|
umd: !!record?.package?.resources?.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [record, open, form]);
|
||||||
|
|
||||||
|
const onSubmit = (event: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||||
|
form.validateFields().then((values) => {
|
||||||
|
if (onOk) {
|
||||||
|
onOk(event, {
|
||||||
|
values,
|
||||||
|
hasUpgraded,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onValuesChange = ({ type: depType }: { type: DependencyItemType; name: string }) => {
|
||||||
|
if (depType) {
|
||||||
|
// type 变更触发
|
||||||
|
form.resetFields();
|
||||||
|
form.setFieldValue('type', depType);
|
||||||
|
setType(depType);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
width={560}
|
||||||
|
open={open}
|
||||||
|
title={record ? `修改依赖:${record.name}` : '添加依赖'}
|
||||||
|
onOk={onSubmit}
|
||||||
|
onCancel={onCancel}
|
||||||
|
destroyOnClose
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
initialValues={{ type: DependencyItemType.业务组件 }}
|
initialValues={{ type }}
|
||||||
onValuesChange={onValuesChange}
|
onValuesChange={onValuesChange}
|
||||||
>
|
>
|
||||||
<Form.Item label="依赖类型" name="type">
|
{!record && (
|
||||||
<Radio.Group value={type}>
|
<>
|
||||||
{/* <Radio.Button value={DependencyItemType.业务组件}>业务组件</Radio.Button> */}
|
<Form.Item label="依赖类型" name="type" rules={[{ required: true }]}>
|
||||||
<Radio.Button value={DependencyItemType.基础包}>基础包</Radio.Button>
|
<Radio.Group>
|
||||||
<Radio.Button value={DependencyItemType.其他依赖}>其他依赖</Radio.Button>
|
{/* <Radio.Button value={DependencyItemType.业务组件}>业务组件</Radio.Button> */}
|
||||||
</Radio.Group>
|
<Radio.Button value={DependencyItemType.基础包}>基础包</Radio.Button>
|
||||||
</Form.Item>
|
<Radio.Button value={DependencyItemType.其他依赖}>其他依赖</Radio.Button>
|
||||||
<Form.Item label="npm包名" name="name" rules={[{ required: true }]}>
|
</Radio.Group>
|
||||||
<Input placeholder="输入依赖名" />
|
</Form.Item>
|
||||||
</Form.Item>
|
<Form.Item label="npm 包名" name="name" rules={[{ required: true }]}>
|
||||||
|
<Input placeholder="输入依赖名" />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="版本号"
|
label="版本号"
|
||||||
name="version"
|
name="version"
|
||||||
@@ -591,8 +705,43 @@ function AddDependencyModal({
|
|||||||
>
|
>
|
||||||
<Input placeholder="输入版本号" />
|
<Input placeholder="输入版本号" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
{hasUpgraded && (
|
||||||
|
<Form.Item name="umd" valuePropName="checked">
|
||||||
|
<Checkbox>使用预构建 UMD 资源</Checkbox>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
<Form.Item noStyle shouldUpdate>
|
||||||
|
{() =>
|
||||||
|
form.getFieldValue('umd') && (
|
||||||
|
<>
|
||||||
|
<Form.Item label="全局变量名" name="library">
|
||||||
|
<Input placeholder="输入 UMD 资源挂载在全局变量下的变量名" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="资源地址" required>
|
||||||
|
<ResourceFormList
|
||||||
|
name="resources"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (!value?.length) {
|
||||||
|
throw new Error('资源地址不能为空');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
{form.getFieldValue('type') === DependencyItemType.基础包 && (
|
||||||
|
<Form.Item label="设计器资源地址">
|
||||||
|
<ResourceFormList name="designerResources" />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
</Box>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,11 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { Box, Text, css, HTMLCoralProps } from 'coral-system';
|
import { Box, Text, css, HTMLCoralProps } from 'coral-system';
|
||||||
import {
|
|
||||||
ClusterOutlined,
|
|
||||||
FunctionOutlined,
|
|
||||||
ApiOutlined,
|
|
||||||
AppstoreOutlined,
|
|
||||||
BuildOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { Badge } from 'antd';
|
import { Badge } from 'antd';
|
||||||
import { ReactComponentProps } from '@music163/tango-helpers';
|
import { ReactComponentProps } from '@music163/tango-helpers';
|
||||||
import { observer, useDesigner } from '@music163/tango-context';
|
import { observer, useDesigner } from '@music163/tango-context';
|
||||||
import { ResizableBox } from './resizable-box';
|
import { ResizableBox } from './resizable-box';
|
||||||
import { OutlinePanel } from './outline-panel';
|
import { getWidget } from '../widgets';
|
||||||
import { DependencyPanel } from './dependency-panel';
|
|
||||||
import { VariablePanel } from './variable-panel';
|
|
||||||
import { DataSourcePanel } from './datasource-panel';
|
|
||||||
|
|
||||||
const sidebarStyle = css`
|
const sidebarStyle = css`
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -51,7 +41,6 @@ export interface SidebarProps extends ReactComponentProps {
|
|||||||
* 底部附加内容
|
* 底部附加内容
|
||||||
*/
|
*/
|
||||||
footer?: React.ReactNode;
|
footer?: React.ReactNode;
|
||||||
builtinPanelMap?: Record<string, SidebarPanelItemProps>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SidebarPanelItemProps
|
export interface SidebarPanelItemProps
|
||||||
@@ -79,54 +68,21 @@ export interface SidebarPanelItemProps
|
|||||||
* 面板宽度
|
* 面板宽度
|
||||||
*/
|
*/
|
||||||
width?: number;
|
width?: number;
|
||||||
|
/**
|
||||||
|
* 如果 key 匹配到内置组件的话,传递给子节点的属性
|
||||||
|
*/
|
||||||
|
widgetProps?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const INTERNAL_SIDEBAR_PANEL_MAP: Record<string, SidebarPanelItemProps> = {
|
function BaseSidebarPanel({ panelWidth: defaultPanelWidth = 280, footer, children }: SidebarProps) {
|
||||||
components: {
|
|
||||||
label: '组件',
|
|
||||||
title: '组件列表',
|
|
||||||
icon: <AppstoreOutlined />,
|
|
||||||
},
|
|
||||||
outline: {
|
|
||||||
label: '结构',
|
|
||||||
title: '结构',
|
|
||||||
icon: <BuildOutlined />,
|
|
||||||
children: <OutlinePanel />,
|
|
||||||
},
|
|
||||||
dependency: {
|
|
||||||
label: '依赖',
|
|
||||||
title: '项目依赖',
|
|
||||||
icon: <ClusterOutlined />,
|
|
||||||
children: <DependencyPanel />,
|
|
||||||
},
|
|
||||||
variables: {
|
|
||||||
label: '变量',
|
|
||||||
title: '变量管理',
|
|
||||||
icon: <FunctionOutlined />,
|
|
||||||
children: <VariablePanel />,
|
|
||||||
width: 440,
|
|
||||||
},
|
|
||||||
dataSource: {
|
|
||||||
label: '接口',
|
|
||||||
title: '数据源与接口',
|
|
||||||
icon: <ApiOutlined />,
|
|
||||||
children: <DataSourcePanel />,
|
|
||||||
width: 600,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function BaseSidebarPanel({
|
|
||||||
panelWidth: defaultPanelWidth = 280,
|
|
||||||
builtinPanelMap = INTERNAL_SIDEBAR_PANEL_MAP,
|
|
||||||
footer,
|
|
||||||
children,
|
|
||||||
}: SidebarProps) {
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const ret: Record<React.Key, SidebarPanelItemProps> = {};
|
const ret: Record<React.Key, SidebarPanelItemProps> = {};
|
||||||
React.Children.forEach(children, (child) => {
|
React.Children.forEach(children, (child) => {
|
||||||
if (child && React.isValidElement(child)) {
|
if (child && React.isValidElement(child)) {
|
||||||
|
const widget = getWidget(['sidebar', child.key].join('.'));
|
||||||
|
const fallbackNode = widget ? React.createElement(widget, child.props.widgetProps) : null;
|
||||||
ret[child.key] = {
|
ret[child.key] = {
|
||||||
...builtinPanelMap[child.key],
|
children: fallbackNode,
|
||||||
...child.props,
|
...child.props,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -203,7 +159,14 @@ function BaseSidebarPanel({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseSidebarPanel.Item = function ({ key, title, doc, isFloat, width }: SidebarPanelItemProps) {
|
BaseSidebarPanel.Item = function ({
|
||||||
|
key,
|
||||||
|
title,
|
||||||
|
doc,
|
||||||
|
isFloat,
|
||||||
|
width,
|
||||||
|
widgetProps,
|
||||||
|
}: SidebarPanelItemProps) {
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ISelectedItemData, isString, noop } from '@music163/tango-helpers';
|
|||||||
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||||
import { IconButton } from '@music163/tango-ui';
|
import { IconButton } from '@music163/tango-ui';
|
||||||
import { getDragGhostElement } from '../helpers';
|
import { getDragGhostElement } from '../helpers';
|
||||||
import { CopyNodeAction, DeleteNodeAction, ViewSourceAction } from '../selection-menu';
|
import { getWidget } from '../widgets';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择辅助工具的对齐方式
|
* 选择辅助工具的对齐方式
|
||||||
@@ -15,34 +15,21 @@ type SelectionHelperAlignType = 'top-right' | 'top-left' | 'inner-top-right';
|
|||||||
|
|
||||||
const boundingOffset = 100; // 检测选中模块的工具栏是否溢出的偏移值
|
const boundingOffset = 100; // 检测选中模块的工具栏是否溢出的偏移值
|
||||||
|
|
||||||
const internalSelectionToolsMap = {
|
|
||||||
source: <ViewSourceAction />,
|
|
||||||
copy: <CopyNodeAction />,
|
|
||||||
delete: <DeleteNodeAction />,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const INTERNAL_SELECTION_TOOLS = ['source', 'create', 'copy', 'delete'];
|
|
||||||
|
|
||||||
export interface SelectionToolsProps {
|
export interface SelectionToolsProps {
|
||||||
/**
|
/**
|
||||||
* 动作列表,内置的动作列表有 source-定位到源码, block-创建区块, copy-复制节点, delete-删除节点
|
* 动作列表,内置的动作列表有 source-定位到源码, block-创建区块, copy-复制节点, delete-删除节点
|
||||||
*/
|
*/
|
||||||
actions?: Array<string | React.ReactElement>;
|
actions?: Array<string | React.ReactElement>;
|
||||||
builtinActionMap?: Record<string, React.ReactElement>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectionTools = observer(
|
export const SelectionTools = observer(
|
||||||
({
|
({ actions: actionsProp = ['viewSource', 'copyNode', 'deleteNode'] }: SelectionToolsProps) => {
|
||||||
actions: actionsProp = INTERNAL_SELECTION_TOOLS,
|
|
||||||
builtinActionMap = internalSelectionToolsMap,
|
|
||||||
}: SelectionToolsProps) => {
|
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const selectSource = workspace.selectSource;
|
const selectSource = workspace.selectSource;
|
||||||
const actions = actionsProp.map((item) => {
|
const actions = actionsProp.map((item) => {
|
||||||
if (isString(item)) {
|
if (isString(item)) {
|
||||||
return builtinActionMap[item]
|
const widget = getWidget(['selectionMenu', item].join('.'));
|
||||||
? React.cloneElement(builtinActionMap[item], { key: item })
|
return widget ? React.createElement(widget, { key: item }) : null;
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,21 +10,14 @@ import { DraggingMask } from './mask';
|
|||||||
|
|
||||||
export interface ViewportProps extends HTMLCoralProps<'div'> {
|
export interface ViewportProps extends HTMLCoralProps<'div'> {
|
||||||
selectionTools?: SelectionToolsProps['actions'];
|
selectionTools?: SelectionToolsProps['actions'];
|
||||||
builtinActionMap?: SelectionToolsProps['builtinActionMap'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Viewport({
|
export function Viewport({ selectionTools, children, className, ...rest }: ViewportProps) {
|
||||||
selectionTools,
|
|
||||||
builtinActionMap,
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
...rest
|
|
||||||
}: ViewportProps) {
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" height="100%" className={cx('Viewport', className)} {...rest}>
|
<Box position="relative" height="100%" className={cx('Viewport', className)} {...rest}>
|
||||||
<ViewportBody>{children}</ViewportBody>
|
<ViewportBody>{children}</ViewportBody>
|
||||||
<div className="AuxTools">
|
<div className="AuxTools">
|
||||||
<SelectionTools actions={selectionTools} builtinActionMap={builtinActionMap} />
|
<SelectionTools actions={selectionTools} />
|
||||||
<InsertionPrompt />
|
<InsertionPrompt />
|
||||||
<DraggingMask />
|
<DraggingMask />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ export const PreviewTool = observer(() => {
|
|||||||
shape="ghost"
|
shape="ghost"
|
||||||
selected={designer.isPreview}
|
selected={designer.isPreview}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
designer.toggleIsPreview(!designer.isPreview);
|
const nextIsPreview = !designer.isPreview;
|
||||||
|
designer.toggleIsPreview(nextIsPreview);
|
||||||
|
if (nextIsPreview) {
|
||||||
|
designer.setActiveView('design');
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
tip="预览"
|
tip="预览"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,26 +1,13 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Box, Group } from 'coral-system';
|
import { Box, Group } from 'coral-system';
|
||||||
import { ReactComponentProps } from '@music163/tango-helpers';
|
import { ReactComponentProps } from '@music163/tango-helpers';
|
||||||
import { HistoryTool } from './history';
|
import { getWidget } from '../widgets';
|
||||||
import { ModeSwitchTool } from './mode-switch';
|
|
||||||
import { PreviewTool } from './preview';
|
|
||||||
import { RouteSwitchTool } from './route-switch';
|
|
||||||
import { TogglePanelTool } from './toggle-panel';
|
|
||||||
|
|
||||||
export interface ToolbarProps {
|
export interface ToolbarProps {
|
||||||
children?: React.ReactElement | React.ReactElement[];
|
children?: React.ReactElement | React.ReactElement[];
|
||||||
builtinToolMap?: Record<string, React.ReactNode>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const builtinToolsMap = {
|
export function Toolbar({ children }: ToolbarProps) {
|
||||||
history: <HistoryTool />,
|
|
||||||
modeSwitch: <ModeSwitchTool />,
|
|
||||||
preview: <PreviewTool />,
|
|
||||||
routeSwitch: <RouteSwitchTool />,
|
|
||||||
togglePanel: <TogglePanelTool />,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function Toolbar({ children, builtinToolMap = builtinToolsMap }: ToolbarProps) {
|
|
||||||
const [leftTools, centerTools, rightTools] = useMemo(() => {
|
const [leftTools, centerTools, rightTools] = useMemo(() => {
|
||||||
const left: React.ReactNode[] = [];
|
const left: React.ReactNode[] = [];
|
||||||
const center: React.ReactNode[] = [];
|
const center: React.ReactNode[] = [];
|
||||||
@@ -28,7 +15,15 @@ export function Toolbar({ children, builtinToolMap = builtinToolsMap }: ToolbarP
|
|||||||
|
|
||||||
let prevPlacement: string;
|
let prevPlacement: string;
|
||||||
React.Children.forEach(children, (child: React.ReactElement, index) => {
|
React.Children.forEach(children, (child: React.ReactElement, index) => {
|
||||||
let node = child.props.children || builtinToolMap[child.key];
|
let fallbackNode;
|
||||||
|
if (child.key) {
|
||||||
|
const Widget = getWidget(['toolbar', child.key].join('.'));
|
||||||
|
if (Widget) {
|
||||||
|
fallbackNode = React.createElement(Widget, child.props.widgetProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = child.props.children ?? fallbackNode ?? null;
|
||||||
if (!node) {
|
if (!node) {
|
||||||
node = child; // separator
|
node = child; // separator
|
||||||
}
|
}
|
||||||
@@ -75,9 +70,13 @@ export function Toolbar({ children, builtinToolMap = builtinToolsMap }: ToolbarP
|
|||||||
export interface ToolbarItemProps extends ReactComponentProps {
|
export interface ToolbarItemProps extends ReactComponentProps {
|
||||||
placement?: 'left' | 'center' | 'right';
|
placement?: 'left' | 'center' | 'right';
|
||||||
children?: React.ReactElement;
|
children?: React.ReactElement;
|
||||||
|
/**
|
||||||
|
* 如果 key 匹配到内置组件的话,传递给子节点的属性
|
||||||
|
*/
|
||||||
|
widgetProps?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ToolbarItem({ placement, children }: ToolbarItemProps) {
|
function ToolbarItem({ placement, widgetProps, children }: ToolbarItemProps) {
|
||||||
return <div>{children}</div>;
|
return <div>{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
44
packages/designer/src/widgets.ts
Normal file
44
packages/designer/src/widgets.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { getValue, setValue } from '@music163/tango-helpers';
|
||||||
|
import { ModeSwitchTool, PreviewTool, RouteSwitchTool, TogglePanelTool } from './toolbar';
|
||||||
|
import {
|
||||||
|
ComponentsPanel,
|
||||||
|
DataSourcePanel,
|
||||||
|
DependencyPanel,
|
||||||
|
OutlinePanel,
|
||||||
|
VariablePanel,
|
||||||
|
} from './sidebar';
|
||||||
|
import { CopyNodeAction, DeleteNodeAction, ViewSourceAction } from './selection-menu';
|
||||||
|
|
||||||
|
const widgets = {};
|
||||||
|
|
||||||
|
export function registerWidget(key: string, component: React.ComponentType<any>) {
|
||||||
|
if (!/^(toolbar|sidebar|selectionMenu)\.[a-zA-z]+$/.test(key)) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid widget key(${key}), should start with toolbar, sidebar, or selection-menu`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setValue(widgets, key, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWidget(key: string) {
|
||||||
|
if (!key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getValue(widgets, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerWidget('toolbar.modeSwitch', ModeSwitchTool);
|
||||||
|
registerWidget('toolbar.preview', PreviewTool);
|
||||||
|
registerWidget('toolbar.routeSwitch', RouteSwitchTool);
|
||||||
|
registerWidget('toolbar.togglePanel', TogglePanelTool);
|
||||||
|
|
||||||
|
registerWidget('sidebar.components', ComponentsPanel);
|
||||||
|
registerWidget('sidebar.outline', OutlinePanel);
|
||||||
|
registerWidget('sidebar.dependency', DependencyPanel);
|
||||||
|
registerWidget('sidebar.variables', VariablePanel);
|
||||||
|
registerWidget('sidebar.dataSource', DataSourcePanel);
|
||||||
|
|
||||||
|
registerWidget('selectionMenu.copyNode', CopyNodeAction);
|
||||||
|
registerWidget('selectionMenu.deleteNode', DeleteNodeAction);
|
||||||
|
registerWidget('selectionMenu.viewSource', ViewSourceAction);
|
||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.1.6](https://github.com/netease/tango/compare/@music163/tango-sandbox@0.1.5...@music163/tango-sandbox@0.1.6) (2023-09-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|
||||||
## [0.1.5](https://github.com/netease/tango/compare/@music163/tango-sandbox@0.1.4...@music163/tango-sandbox@0.1.5) (2023-09-21)
|
## [0.1.5](https://github.com/netease/tango/compare/@music163/tango-sandbox@0.1.4...@music163/tango-sandbox@0.1.5) (2023-09-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @music163/tango-sandbox
|
**Note:** Version bump only for package @music163/tango-sandbox
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-sandbox",
|
"name": "@music163/tango-sandbox",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"description": "sandbox of tango apps",
|
"description": "sandbox of tango apps",
|
||||||
"author": "wwsun <ww.sun@outlook.com>",
|
"author": "wwsun <ww.sun@outlook.com>",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.8.0",
|
"@ant-design/icons": "^4.8.0",
|
||||||
"@music163/tango-core": "^0.2.0",
|
"@music163/tango-core": "^0.2.1",
|
||||||
"@music163/tango-helpers": "^0.1.3",
|
"@music163/tango-helpers": "^0.1.3",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"lodash.isequal": "4.5.0",
|
"lodash.isequal": "4.5.0",
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.2.5](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.2.4...@music163/tango-setting-form@0.2.5) (2023-10-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-setting-form
|
||||||
|
|
||||||
|
## [0.2.4](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.2.3...@music163/tango-setting-form@0.2.4) (2023-10-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-setting-form
|
||||||
|
|
||||||
|
## [0.2.3](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.2.2...@music163/tango-setting-form@0.2.3) (2023-09-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @music163/tango-setting-form
|
||||||
|
|
||||||
## [0.2.2](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.2.1...@music163/tango-setting-form@0.2.2) (2023-09-21)
|
## [0.2.2](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.2.1...@music163/tango-setting-form@0.2.2) (2023-09-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @music163/tango-setting-form
|
**Note:** Version bump only for package @music163/tango-setting-form
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-setting-form",
|
"name": "@music163/tango-setting-form",
|
||||||
"version": "0.2.2",
|
"version": "0.2.5",
|
||||||
"description": "setting form of tango-apps",
|
"description": "setting form of tango-apps",
|
||||||
"author": "wwsun <ww.sun@outlook.com>",
|
"author": "wwsun <ww.sun@outlook.com>",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
@@ -28,9 +28,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.8.0",
|
"@ant-design/icons": "^4.8.0",
|
||||||
"@music163/tango-core": "^0.2.0",
|
"@music163/tango-core": "^0.2.1",
|
||||||
"@music163/tango-helpers": "^0.1.3",
|
"@music163/tango-helpers": "^0.1.3",
|
||||||
"@music163/tango-ui": "^0.1.4",
|
"@music163/tango-ui": "^0.1.6",
|
||||||
"antd": "^4.24.2",
|
"antd": "^4.24.2",
|
||||||
"coral-system": "^1.0.5",
|
"coral-system": "^1.0.5",
|
||||||
"mobx": "6.9.0",
|
"mobx": "6.9.0",
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.1.6](https://github.com/netease/tango/compare/@music163/tango-ui@0.1.5...@music163/tango-ui@0.1.6) (2023-10-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- refactor expSetter ([#41](https://github.com/netease/tango/issues/41)) ([d63517e](https://github.com/netease/tango/commit/d63517ecb936e4227e70c33e610664316625f4f4))
|
||||||
|
|
||||||
|
## [0.1.5](https://github.com/netease/tango/compare/@music163/tango-ui@0.1.4...@music163/tango-ui@0.1.5) (2023-10-09)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- update tabs style ([46b5445](https://github.com/netease/tango/commit/46b54459ceb23f9df52b9eb40dc935bc872ba1b4))
|
||||||
|
|
||||||
## [0.1.4](https://github.com/netease/tango/compare/@music163/tango-ui@0.1.3...@music163/tango-ui@0.1.4) (2023-09-18)
|
## [0.1.4](https://github.com/netease/tango/compare/@music163/tango-ui@0.1.3...@music163/tango-ui@0.1.4) (2023-09-18)
|
||||||
|
|
||||||
**Note:** Version bump only for package @music163/tango-ui
|
**Note:** Version bump only for package @music163/tango-ui
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@music163/tango-ui",
|
"name": "@music163/tango-ui",
|
||||||
"version": "0.1.4",
|
"version": "0.1.6",
|
||||||
"description": "ui widgets of tango",
|
"description": "ui widgets of tango",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ export interface CopyClipboardProps {
|
|||||||
* 拷贝成功后到回调
|
* 拷贝成功后到回调
|
||||||
*/
|
*/
|
||||||
onCopy?: () => void;
|
onCopy?: () => void;
|
||||||
children?: React.ReactNode | ((copied: boolean) => React.ReactElement);
|
children?:
|
||||||
|
| React.ReactNode
|
||||||
|
| ((data: { copied: boolean; onClick: Function }) => React.ReactElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CopyClipboard({ onCopy, text, children }: CopyClipboardProps) {
|
export function CopyClipboard({ onCopy, text, children }: CopyClipboardProps) {
|
||||||
@@ -37,7 +39,7 @@ export function CopyClipboard({ onCopy, text, children }: CopyClipboardProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (typeof children === 'function') {
|
if (typeof children === 'function') {
|
||||||
return React.cloneElement(children(copied), {
|
return React.cloneElement(children({ copied, onClick }), {
|
||||||
onClick,
|
onClick,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function Tabs({
|
|||||||
const tabBarStyle = useMemo(() => {
|
const tabBarStyle = useMemo(() => {
|
||||||
let ret = centered ? centerTabBarStyle : defaultTabBarStyle;
|
let ret = centered ? centerTabBarStyle : defaultTabBarStyle;
|
||||||
if (isTabBarSticky) {
|
if (isTabBarSticky) {
|
||||||
ret = { ...ret, position: 'sticky', top: tabBarStickyOffset, zIndex: 1, background: '#fff' };
|
ret = { ...ret, position: 'sticky', top: tabBarStickyOffset, zIndex: 2, background: '#fff' };
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}, [centered, isTabBarSticky, tabBarStickyOffset]);
|
}, [centered, isTabBarSticky, tabBarStickyOffset]);
|
||||||
|
|||||||
12
yarn.lock
12
yarn.lock
@@ -11185,9 +11185,9 @@ humanize-ms@^1.2.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.0.0"
|
ms "^2.0.0"
|
||||||
|
|
||||||
husky@^8.0.1:
|
husky@^8.0.3:
|
||||||
version "8.0.3"
|
version "8.0.3"
|
||||||
resolved "https://registry.npmmirror.com/husky/-/husky-8.0.3.tgz"
|
resolved "https://registry.npmmirror.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
|
||||||
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
|
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
|
||||||
|
|
||||||
iconv-lite@0.4.24, iconv-lite@^0.4.24:
|
iconv-lite@0.4.24, iconv-lite@^0.4.24:
|
||||||
@@ -13043,10 +13043,10 @@ lines-and-columns@~2.0.3:
|
|||||||
resolved "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz"
|
resolved "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz"
|
||||||
integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==
|
integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==
|
||||||
|
|
||||||
lint-staged@^13.1.0:
|
lint-staged@^14.0.1:
|
||||||
version "13.3.0"
|
version "14.0.1"
|
||||||
resolved "https://registry.npmmirror.com/lint-staged/-/lint-staged-13.3.0.tgz"
|
resolved "https://registry.npmmirror.com/lint-staged/-/lint-staged-14.0.1.tgz#57dfa3013a3d60762d9af5d9c83bdb51291a6232"
|
||||||
integrity sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ==
|
integrity sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "5.3.0"
|
chalk "5.3.0"
|
||||||
commander "11.0.0"
|
commander "11.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user