mirror of
https://github.com/NetEase/tango.git
synced 2026-08-31 20:06:59 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 994db5db55 | |||
| 15542d9eb2 | |||
| 4d809e9afd | |||
| b786de2f1a | |||
| 8d396ff134 | |||
| 7e9ab12503 | |||
| 443ae589f6 | |||
| 12de10b0bc | |||
| 19d0a4523b | |||
| b80f5e4c10 | |||
| f9e6537dd6 | |||
| 5ecf444fc3 |
@@ -6,6 +6,8 @@
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
"import/no-cycle": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"no-useless-return":"off"
|
||||
"no-useless-return":"off",
|
||||
"no-param-reassign": "off",
|
||||
"prefer-destructuring": "off"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ const packageJson = {
|
||||
name: 'demo',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'@music163/antd': '0.1.5',
|
||||
'@music163/antd': '0.1.6',
|
||||
'@music163/tango-boot': '0.1.3',
|
||||
react: '17.0.2',
|
||||
'react-dom': '17.0.2',
|
||||
@@ -11,7 +11,7 @@ const packageJson = {
|
||||
},
|
||||
};
|
||||
|
||||
const tangoJson = {
|
||||
const tangoConfigJson = {
|
||||
packages: {
|
||||
react: {
|
||||
version: '17.0.2',
|
||||
@@ -52,7 +52,7 @@ const tangoJson = {
|
||||
description: '云音乐低代码运行时框架',
|
||||
},
|
||||
'@music163/antd': {
|
||||
version: '0.1.5',
|
||||
version: '0.1.7',
|
||||
library: 'TangoAntd',
|
||||
type: 'baseDependency',
|
||||
resources: [
|
||||
@@ -148,7 +148,7 @@ import {
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Page title="页面标题">
|
||||
<Page title={tango.stores.app.title}>
|
||||
<Section>
|
||||
</Section>
|
||||
<Section>
|
||||
@@ -167,7 +167,7 @@ import { defineStore } from '@music163/tango-boot';
|
||||
|
||||
export default defineStore({
|
||||
|
||||
title: 'hello world',
|
||||
title: 'Page Title',
|
||||
|
||||
array: [1, 2, 3],
|
||||
}, 'app');
|
||||
@@ -221,7 +221,7 @@ p {
|
||||
export const sampleFiles = [
|
||||
{ filename: '/package.json', code: JSON.stringify(packageJson) },
|
||||
{ filename: '/appJson.json', code: JSON.stringify(appJson) },
|
||||
{ filename: '/tango.config.json', code: JSON.stringify(tangoJson) },
|
||||
{ filename: '/tango.config.json', code: JSON.stringify(tangoConfigJson) },
|
||||
{ filename: '/README.md', code: '# readme' },
|
||||
{ filename: '/src/index.less', code: lessCode },
|
||||
{ filename: '/src/style.css', code: cssCode },
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { Button, Space } from 'antd';
|
||||
import {
|
||||
Designer,
|
||||
DesignerPanel,
|
||||
@@ -6,8 +8,8 @@ import {
|
||||
Sidebar,
|
||||
Toolbar,
|
||||
WorkspacePanel,
|
||||
WorkspaceView,
|
||||
ComponentsPanel,
|
||||
ViewPanel,
|
||||
CodeEditor,
|
||||
Sandbox,
|
||||
DndQuery,
|
||||
@@ -16,8 +18,6 @@ import { createEngine, Workspace } from '@music163/tango-core';
|
||||
import { Logo, ProjectDetail } from './share';
|
||||
import { sampleFiles } from '../mock/project';
|
||||
import './index.less';
|
||||
import { Box } from 'coral-system';
|
||||
import { Button, Space } from 'antd';
|
||||
|
||||
// 1. 实例化工作区
|
||||
const workspace = new Workspace({
|
||||
@@ -74,7 +74,7 @@ export default function App() {
|
||||
<Sidebar.Item key="dependency" isFloat width={800} />
|
||||
</Sidebar>
|
||||
<WorkspacePanel>
|
||||
<ViewPanel mode="design">
|
||||
<WorkspaceView mode="design">
|
||||
<Sandbox
|
||||
onMessage={(e) => {
|
||||
if (e.type === 'done') {
|
||||
@@ -91,10 +91,10 @@ export default function App() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ViewPanel>
|
||||
<ViewPanel mode="code">
|
||||
</WorkspaceView>
|
||||
<WorkspaceView mode="code">
|
||||
<CodeEditor />
|
||||
</ViewPanel>
|
||||
</WorkspaceView>
|
||||
</WorkspacePanel>
|
||||
<SettingPanel />
|
||||
</DesignerPanel>
|
||||
|
||||
+1
-7
@@ -53,19 +53,13 @@
|
||||
"jest": "^29.1.1",
|
||||
"jest-environment-jsdom": "^29.1.1",
|
||||
"lerna": "^6.6.1",
|
||||
"less": "^4.1.2",
|
||||
"less-loader": "^7.3.0",
|
||||
"lint-staged": "^13.1.0",
|
||||
"mini-css-extract-plugin": "^1.6.2",
|
||||
"prettier": "^2.8.7",
|
||||
"progress-bar-webpack-plugin": "^2.1.0",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"styled-components": "^5.3.6",
|
||||
"typedoc": "^0.25.0",
|
||||
"typescript": "^5.0.4",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"@yarnpkg/parsers": "3.0.0-rc.48.1"
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.1.3](https://github.com/netease/tango/compare/@music163/tango-context@0.1.2...@music163/tango-context@0.1.3) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
## 0.1.2 (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-context
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-context",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "react context for tango-apps",
|
||||
"keywords": [
|
||||
"react",
|
||||
@@ -31,7 +31,7 @@
|
||||
"react": ">= 16.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@music163/tango-core": "^0.1.2",
|
||||
"@music163/tango-core": "^0.1.3",
|
||||
"@music163/tango-helpers": "^0.1.2",
|
||||
"mobx-react-lite": "4.0.2"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.1.3](https://github.com/netease/tango/compare/@music163/tango-core@0.1.2...@music163/tango-core@0.1.3) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** optimize find jsxElement parent node way ([#22](https://github.com/netease/tango/issues/22)) ([19d0a45](https://github.com/netease/tango/commit/19d0a4523bb31ea9286714737a0f4f1883e1c801))
|
||||
- refactor setting formItem ([b786de2](https://github.com/netease/tango/commit/b786de2f1a0e4e9141eb09fce696e45df633b232))
|
||||
- refactor types ([15542d9](https://github.com/netease/tango/commit/15542d9eb2f8959597b81cae457091ee71710c83))
|
||||
- update sidebar ([4d809e9](https://github.com/netease/tango/commit/4d809e9afd0d6d525850708722736847b510638e))
|
||||
|
||||
## 0.1.2 (2023-09-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-core",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "tango core",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"homepage": "",
|
||||
|
||||
@@ -32,12 +32,12 @@ import {
|
||||
import { isValidComponentName } from '../string';
|
||||
import { isDefineService, isDefineStore } from '../assert';
|
||||
import type {
|
||||
RouteDataType,
|
||||
StorePropertyType,
|
||||
ClassPropertyNodeType,
|
||||
TangoViewNodeDataType,
|
||||
ImportDeclarationPayloadType,
|
||||
ServiceFunctionPayloadType,
|
||||
IRouteData,
|
||||
IStorePropertyData,
|
||||
IClassPropertyNodeData,
|
||||
ITangoViewNodeData,
|
||||
IImportDeclarationPayload,
|
||||
IServiceFunctionPayload,
|
||||
InsertChildPositionType,
|
||||
} from '../../types';
|
||||
import { IdGenerator } from '../id-generator';
|
||||
@@ -459,7 +459,7 @@ function getLastExportDeclarationIndex(node: t.Program) {
|
||||
* @param importedSourcePath
|
||||
* @returns
|
||||
*/
|
||||
export function makeImportDeclaration(importedModule: ImportDeclarationPayloadType) {
|
||||
export function makeImportDeclaration(importedModule: IImportDeclarationPayload) {
|
||||
const specifierNodes =
|
||||
importedModule.specifiers?.map((localName) =>
|
||||
t.importSpecifier(t.identifier(localName), t.identifier(localName)),
|
||||
@@ -474,7 +474,7 @@ export function makeImportDeclaration(importedModule: ImportDeclarationPayloadTy
|
||||
return t.importDeclaration(newSpecifierNodes, t.stringLiteral(importedModule.sourcePath));
|
||||
}
|
||||
|
||||
function getImportDeclarationData(node: t.ImportDeclaration): ImportDeclarationPayloadType {
|
||||
function getImportDeclarationData(node: t.ImportDeclaration): IImportDeclarationPayload {
|
||||
const sourcePath = node2value(node.source);
|
||||
let defaultSpecifier;
|
||||
const specifiers: string[] = [];
|
||||
@@ -499,7 +499,7 @@ function getImportDeclarationData(node: t.ImportDeclaration): ImportDeclarationP
|
||||
* @param importedSourcePath
|
||||
* @returns
|
||||
*/
|
||||
export function addImportDeclaration(ast: t.File, importedModule: ImportDeclarationPayloadType) {
|
||||
export function addImportDeclaration(ast: t.File, importedModule: IImportDeclarationPayload) {
|
||||
traverse(ast, {
|
||||
Program(path) {
|
||||
const lastIndex = getLastImportDeclarationIndex(path.node);
|
||||
@@ -518,7 +518,7 @@ export function addImportDeclaration(ast: t.File, importedModule: ImportDeclarat
|
||||
* @param importedSourcePath
|
||||
* @returns
|
||||
*/
|
||||
export function updateImportDeclaration(ast: t.File, importedModule: ImportDeclarationPayloadType) {
|
||||
export function updateImportDeclaration(ast: t.File, importedModule: IImportDeclarationPayload) {
|
||||
traverse(ast, {
|
||||
ImportDeclaration(path) {
|
||||
const currentSourcePath = node2value(path.node.source);
|
||||
@@ -656,7 +656,7 @@ export function removeRouteFromRouteFile(ast: t.File, routePath: string, importP
|
||||
}
|
||||
|
||||
export function traverseRouteFile(ast: t.File) {
|
||||
const routes: RouteDataType[] = [];
|
||||
const routes: IRouteData[] = [];
|
||||
const importMap: Dict<string> = {};
|
||||
|
||||
traverse(ast, {
|
||||
@@ -819,8 +819,8 @@ export function updateStoreState(ast: t.File, stateName: string, code: string) {
|
||||
|
||||
export function traverseStoreFile(ast: t.File) {
|
||||
let namespace: string;
|
||||
const actions: StorePropertyType[] = [];
|
||||
const states: StorePropertyType[] = [];
|
||||
const actions: IStorePropertyData[] = [];
|
||||
const states: IStorePropertyData[] = [];
|
||||
|
||||
traverse(ast, {
|
||||
CallExpression(path) {
|
||||
@@ -909,7 +909,7 @@ export function deleteServiceConfigFromServiceFile(ast: t.File, serviceFunctionN
|
||||
* @param payload
|
||||
* @returns
|
||||
*/
|
||||
export function serviceConfig2Node(payload: ServiceFunctionPayloadType) {
|
||||
export function serviceConfig2Node(payload: IServiceFunctionPayload) {
|
||||
return object2node(payload, (value, key) => {
|
||||
if (key === 'formatter' && value) {
|
||||
return code2expression(value);
|
||||
@@ -920,7 +920,7 @@ export function serviceConfig2Node(payload: ServiceFunctionPayloadType) {
|
||||
|
||||
export function updateServiceConfigToServiceFile(
|
||||
ast: t.File,
|
||||
config: { [key: string]: ServiceFunctionPayloadType },
|
||||
config: { [key: string]: IServiceFunctionPayload },
|
||||
) {
|
||||
traverse(ast, {
|
||||
CallExpression(path) {
|
||||
@@ -1133,11 +1133,11 @@ export function cloneJSXElementWithoutTrackingData(node: t.JSXElement) {
|
||||
}
|
||||
|
||||
export function traverseViewFile(ast: t.File, idGenerator: IdGenerator) {
|
||||
const importedModules: Dict<ImportDeclarationPayloadType | ImportDeclarationPayloadType[]> = {};
|
||||
const classProperties: Dict<ClassPropertyNodeType> = {};
|
||||
const importedModules: Dict<IImportDeclarationPayload | IImportDeclarationPayload[]> = {};
|
||||
const classProperties: Dict<IClassPropertyNodeData> = {};
|
||||
let stateProperties: string[] = [];
|
||||
let stateCode = '';
|
||||
const nodes: Array<TangoViewNodeDataType<t.JSXElement>> = [];
|
||||
const nodes: Array<ITangoViewNodeData<t.JSXElement>> = [];
|
||||
const cloneAst = t.cloneNode(ast, true, true);
|
||||
const cleanAst = removeTrackingDataFromViewAst(cloneAst);
|
||||
const variables: string[] = []; // 使用的 tango 变量
|
||||
@@ -1216,9 +1216,10 @@ export function traverseViewFile(ast: t.File, idGenerator: IdGenerator) {
|
||||
|
||||
// parentId 用于追溯上下游关系
|
||||
let parentId;
|
||||
const parentNode = path.parentPath.node;
|
||||
if (t.isJSXElement(parentNode)) {
|
||||
const parentAttributes = getJSXElementAttributes(parentNode);
|
||||
const parentNode = path.findParent((p) => p.isJSXElement());
|
||||
|
||||
if (t.isJSXElement(parentNode?.node)) {
|
||||
const parentAttributes = getJSXElementAttributes(parentNode.node);
|
||||
parentId = parentAttributes[SLOT.dnd];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ImportDeclarationPayloadType } from '../types';
|
||||
import { IImportDeclarationPayload } from '../types';
|
||||
|
||||
/**
|
||||
* 导入列表解析为导入声明对象
|
||||
@@ -20,7 +20,7 @@ export function namesToImportDeclarations(
|
||||
return Object.keys(map).map((sourcePath) => ({
|
||||
sourcePath,
|
||||
...map[sourcePath],
|
||||
})) as ImportDeclarationPayloadType[];
|
||||
})) as IImportDeclarationPayload[];
|
||||
}
|
||||
|
||||
function updateMod(
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
uuid,
|
||||
} from '@music163/tango-helpers';
|
||||
import { getRelativePath, isFilepath } from './string';
|
||||
import type { ImportDeclarationPayloadType } from '../types';
|
||||
import type { IImportDeclarationPayload } from '../types';
|
||||
import { code2expression } from './ast';
|
||||
|
||||
/**
|
||||
@@ -17,7 +17,7 @@ import { code2expression } from './ast';
|
||||
export function getImportDeclarationPayloadByPrototype(
|
||||
prototype: ComponentPrototypeType,
|
||||
relativeFilepath?: string,
|
||||
): ImportDeclarationPayloadType {
|
||||
): IImportDeclarationPayload {
|
||||
let defaultSpecifier;
|
||||
let specifiers;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from './models';
|
||||
export * from './factory';
|
||||
export * from './types';
|
||||
export * from './helpers';
|
||||
export * from './types';
|
||||
@@ -1,8 +1,8 @@
|
||||
import { action, computed, makeObservable, observable, toJS } from 'mobx';
|
||||
import { getValue, isNil, logger, setValue } from '@music163/tango-helpers';
|
||||
import type { FileType, ModulePropsType } from '../types';
|
||||
import { formatCode } from '../helpers';
|
||||
import type { FileType, IFileConfig } from '../types';
|
||||
import { IWorkspace } from './interfaces';
|
||||
import { formatCode } from '../helpers';
|
||||
|
||||
/**
|
||||
* 普通文件,不进行 AST 解析
|
||||
@@ -35,7 +35,7 @@ export class TangoFile {
|
||||
return this._cleanCode;
|
||||
}
|
||||
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType, isSyncCode = true) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig, isSyncCode = true) {
|
||||
this.workspace = workspace;
|
||||
this.filename = props.filename;
|
||||
this.type = props.type;
|
||||
@@ -60,7 +60,7 @@ export class TangoFile {
|
||||
}
|
||||
|
||||
export class TangoLessFile extends TangoFile {
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this.update(props.code);
|
||||
makeObservable(this, {
|
||||
@@ -87,7 +87,7 @@ export class TangoJsonFile extends TangoFile {
|
||||
return toJS(this._object);
|
||||
}
|
||||
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this.update(props.code);
|
||||
makeObservable(this, {
|
||||
|
||||
@@ -3,15 +3,15 @@ import { TangoHistory } from './history';
|
||||
import { SelectSource } from './select-source';
|
||||
import { DragSource } from './drag-source';
|
||||
import {
|
||||
FileItemType,
|
||||
IFileConfig,
|
||||
FileType,
|
||||
ImportDeclarationPayloadType,
|
||||
IImportDeclarationPayload,
|
||||
InsertChildPositionType,
|
||||
PackageConfigType,
|
||||
PageConfigType,
|
||||
ProjectDataType,
|
||||
ServiceFunctionPayloadType,
|
||||
} from '../types/types';
|
||||
ITangoConfigPackages,
|
||||
IPageConfigData,
|
||||
IProjectData,
|
||||
IServiceFunctionPayload,
|
||||
} from '../types';
|
||||
import { TangoFile, TangoJsonFile } from './file';
|
||||
import { TangoRouteModule, TangoServiceModule, TangoStoreModule } from './module';
|
||||
|
||||
@@ -38,17 +38,41 @@ export interface IViewFile {
|
||||
sourcePrototype?: string | ComponentPrototypeType,
|
||||
) => IViewFile;
|
||||
|
||||
insertAfter: (targetNodeId: string, newNode: any, sourcePrototype?: string | ComponentPrototypeType) => IViewFile;
|
||||
insertAfter: (
|
||||
targetNodeId: string,
|
||||
newNode: any,
|
||||
sourcePrototype?: string | ComponentPrototypeType,
|
||||
) => IViewFile;
|
||||
|
||||
insertBefore: (targetNodeId: string, newNode: any, sourcePrototype?: string | ComponentPrototypeType) => IViewFile;
|
||||
insertBefore: (
|
||||
targetNodeId: string,
|
||||
newNode: any,
|
||||
sourcePrototype?: string | ComponentPrototypeType,
|
||||
) => IViewFile;
|
||||
|
||||
replaceNode: (targetNodeId: string, newNode: any, sourcePrototype?: string | ComponentPrototypeType) => IViewFile;
|
||||
replaceNode: (
|
||||
targetNodeId: string,
|
||||
newNode: any,
|
||||
sourcePrototype?: string | ComponentPrototypeType,
|
||||
) => IViewFile;
|
||||
|
||||
replaceViewChildren: (rawNodes: any[], importDeclarations?: ImportDeclarationPayloadType[]) => IViewFile;
|
||||
replaceViewChildren: (
|
||||
rawNodes: any[],
|
||||
importDeclarations?: IImportDeclarationPayload[],
|
||||
) => IViewFile;
|
||||
|
||||
updateNodeAttribute: (nodeId: string, attrName: string, attrValue?: any, relatedImports?: string[]) => IViewFile;
|
||||
updateNodeAttribute: (
|
||||
nodeId: string,
|
||||
attrName: string,
|
||||
attrValue?: any,
|
||||
relatedImports?: string[],
|
||||
) => IViewFile;
|
||||
|
||||
updateNodeAttributes: (nodeId: string, config: Record<string, any>, relatedImports?: string[]) => IViewFile;
|
||||
updateNodeAttributes: (
|
||||
nodeId: string,
|
||||
config: Record<string, any>,
|
||||
relatedImports?: string[],
|
||||
) => IViewFile;
|
||||
|
||||
get code(): string;
|
||||
get nodes(): Map<string, IViewNode>;
|
||||
@@ -129,7 +153,7 @@ export interface IWorkspace {
|
||||
/**
|
||||
* 获取项目数据
|
||||
*/
|
||||
getProjectData?: () => ProjectDataType;
|
||||
getProjectData?: () => IProjectData;
|
||||
|
||||
/**
|
||||
* 查询节点
|
||||
@@ -142,7 +166,7 @@ export interface IWorkspace {
|
||||
listForms?: () => Record<string, string[]>;
|
||||
|
||||
addFile: (filename: string, code: string, fileType?: FileType) => void;
|
||||
addFiles: (files: FileItemType[]) => void;
|
||||
addFiles: (files: IFileConfig[]) => void;
|
||||
updateFile: (filename: string, code: string, shouldFormatCode?: boolean) => void;
|
||||
removeFile: (filename: string) => void;
|
||||
renameFile: (oldFilename: string, newFilename: string) => void;
|
||||
@@ -163,11 +187,20 @@ export interface IWorkspace {
|
||||
|
||||
dropNode: () => void;
|
||||
|
||||
insertToNode: (targetNodeId: string, sourceNameOrPrototype: string | ComponentPrototypeType) => void;
|
||||
insertToNode: (
|
||||
targetNodeId: string,
|
||||
sourceNameOrPrototype: string | ComponentPrototypeType,
|
||||
) => void;
|
||||
|
||||
replaceNode: (targetNodeId: string, sourceNameOrPrototype: string | ComponentPrototypeType) => void;
|
||||
replaceNode: (
|
||||
targetNodeId: string,
|
||||
sourceNameOrPrototype: string | ComponentPrototypeType,
|
||||
) => void;
|
||||
|
||||
updateSelectedNodeAttributes: (attributes: Record<string, any>, relatedImports?: string[]) => void;
|
||||
updateSelectedNodeAttributes: (
|
||||
attributes: Record<string, any>,
|
||||
relatedImports?: string[],
|
||||
) => void;
|
||||
|
||||
addBlock?: (files: object, name: string) => void;
|
||||
|
||||
@@ -175,7 +208,7 @@ export interface IWorkspace {
|
||||
|
||||
removeServiceFunction?: (serviceName: string) => void;
|
||||
|
||||
addServiceFunction?: (payload: ServiceFunctionPayloadType | ServiceFunctionPayloadType[]) => void;
|
||||
addServiceFunction?: (payload: IServiceFunctionPayload | IServiceFunctionPayload[]) => void;
|
||||
|
||||
updateServiceFunction?: (payload: any) => void;
|
||||
|
||||
@@ -195,9 +228,9 @@ export interface IWorkspace {
|
||||
|
||||
removeViewModule: (routePath: string) => void;
|
||||
|
||||
copyViewPage: (sourceRoutePath: string, targetPageData: PageConfigType) => void;
|
||||
copyViewPage: (sourceRoutePath: string, targetPageData: IPageConfigData) => void;
|
||||
|
||||
updateRoute: (sourceRoutePath: string, targetPageData: PageConfigType) => void;
|
||||
updateRoute: (sourceRoutePath: string, targetPageData: IPageConfigData) => void;
|
||||
|
||||
listDependencies?: () => any;
|
||||
|
||||
@@ -205,7 +238,7 @@ export interface IWorkspace {
|
||||
name: string,
|
||||
version: string,
|
||||
options?: {
|
||||
package?: PackageConfigType;
|
||||
package?: ITangoConfigPackages;
|
||||
[x: string]: any;
|
||||
},
|
||||
) => void;
|
||||
@@ -216,7 +249,7 @@ export interface IWorkspace {
|
||||
name: string,
|
||||
version: string,
|
||||
options?: {
|
||||
package?: PackageConfigType;
|
||||
package?: ITangoConfigPackages;
|
||||
[x: string]: any;
|
||||
},
|
||||
) => void;
|
||||
|
||||
@@ -38,13 +38,13 @@ import {
|
||||
import { TangoNode } from './node';
|
||||
import { TangoFile } from './file';
|
||||
import {
|
||||
RouteDataType,
|
||||
ModulePropsType,
|
||||
ClassPropertyNodeType,
|
||||
ServiceFunctionPayloadType,
|
||||
TangoViewNodeDataType,
|
||||
StorePropertyType,
|
||||
ImportDeclarationPayloadType,
|
||||
IRouteData,
|
||||
IFileConfig,
|
||||
IClassPropertyNodeData,
|
||||
IServiceFunctionPayload,
|
||||
ITangoViewNodeData,
|
||||
IStorePropertyData,
|
||||
IImportDeclarationPayload,
|
||||
InsertChildPositionType,
|
||||
} from '../types';
|
||||
import { IViewFile, IWorkspace } from './interfaces';
|
||||
@@ -57,7 +57,7 @@ import { IViewFile, IWorkspace } from './interfaces';
|
||||
class TangoModule extends TangoFile {
|
||||
ast: t.File;
|
||||
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType, isSyncCode = true) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig, isSyncCode = true) {
|
||||
super(workspace, props, isSyncCode);
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class TangoModule extends TangoFile {
|
||||
* 普通 JS 文件
|
||||
*/
|
||||
export class TangoJsModule extends TangoModule {
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this.update(props.code, false, false);
|
||||
|
||||
@@ -143,7 +143,7 @@ export class TangoStoreEntryModule extends TangoModule {
|
||||
return toJS(this._stores);
|
||||
}
|
||||
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this.update(props.code, true, false);
|
||||
|
||||
@@ -185,13 +185,13 @@ export class TangoStoreEntryModule extends TangoModule {
|
||||
* 路由配置模块
|
||||
*/
|
||||
export class TangoRouteModule extends TangoModule {
|
||||
_routes: RouteDataType[];
|
||||
_routes: IRouteData[];
|
||||
|
||||
get routes() {
|
||||
return toJS(this._routes);
|
||||
}
|
||||
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this.update(props.code, true, false);
|
||||
|
||||
@@ -264,7 +264,7 @@ export class TangoRouteModule extends TangoModule {
|
||||
*/
|
||||
export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
// 解析为树结构的 jsxNodes 数组
|
||||
_nodesTree: TangoViewNodeDataType[];
|
||||
_nodesTree: ITangoViewNodeData[];
|
||||
/**
|
||||
* 通过导入组件名查找组件来自的包
|
||||
*/
|
||||
@@ -282,12 +282,12 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
/**
|
||||
* 导入的模块
|
||||
*/
|
||||
private _importedModules: Dict<ImportDeclarationPayloadType | ImportDeclarationPayloadType[]>;
|
||||
private _importedModules: Dict<IImportDeclarationPayload | IImportDeclarationPayload[]>;
|
||||
/**
|
||||
* 类属性
|
||||
* @deprecated
|
||||
*/
|
||||
private _classProperties: Dict<ClassPropertyNodeType>;
|
||||
private _classProperties: Dict<IClassPropertyNodeData>;
|
||||
/**
|
||||
* 状态属性
|
||||
*/
|
||||
@@ -325,7 +325,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
return this.ast;
|
||||
}
|
||||
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this._nodes = new Map();
|
||||
this._idGenerator = new IdGenerator({ prefix: props.filename });
|
||||
@@ -401,7 +401,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
/**
|
||||
* 更新导入的变量(新版)
|
||||
*/
|
||||
updateImportSpecifiers(importDeclaration: ImportDeclarationPayloadType) {
|
||||
updateImportSpecifiers(importDeclaration: IImportDeclarationPayload) {
|
||||
const mods = this._importedModules[importDeclaration.sourcePath];
|
||||
let ast;
|
||||
// 如果模块已存在,需要去重
|
||||
@@ -540,7 +540,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
*/
|
||||
replaceViewChildren(
|
||||
childrenNodes: t.JSXElement[],
|
||||
importDeclarations?: ImportDeclarationPayloadType[],
|
||||
importDeclarations?: IImportDeclarationPayload[],
|
||||
) {
|
||||
if (childrenNodes.length) {
|
||||
this.ast = replaceRootJSXElementChildren(this.ast, childrenNodes);
|
||||
@@ -556,7 +556,7 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
}
|
||||
|
||||
private buildImportMap(
|
||||
importedModules: Dict<ImportDeclarationPayloadType | ImportDeclarationPayloadType[]>,
|
||||
importedModules: Dict<IImportDeclarationPayload | IImportDeclarationPayload[]>,
|
||||
) {
|
||||
const map = {};
|
||||
Object.keys(importedModules).forEach((modName) => {
|
||||
@@ -583,8 +583,8 @@ export class TangoViewModule extends TangoModule implements IViewFile {
|
||||
* 将节点列表转换为 tree data 嵌套数组
|
||||
* @param list
|
||||
*/
|
||||
export function nodeListToTreeData(list: TangoViewNodeDataType[]) {
|
||||
const map: Record<string, TangoViewNodeDataType> = {};
|
||||
export function nodeListToTreeData(list: ITangoViewNodeData[]) {
|
||||
const map: Record<string, ITangoViewNodeData> = {};
|
||||
|
||||
list.forEach((item) => {
|
||||
// 如果不存在,则初始化
|
||||
@@ -627,7 +627,7 @@ export class TangoServiceModule extends TangoModule {
|
||||
return toJS(this._baseConfig);
|
||||
}
|
||||
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this.name = getModuleNameByFilename(props.filename);
|
||||
this.update(props.code, true, false);
|
||||
@@ -651,13 +651,13 @@ export class TangoServiceModule extends TangoModule {
|
||||
this._baseConfig = baseConfig;
|
||||
}
|
||||
|
||||
addServiceFunction(payload: ServiceFunctionPayloadType) {
|
||||
addServiceFunction(payload: IServiceFunctionPayload) {
|
||||
const { name, ...rest } = payload;
|
||||
this.ast = updateServiceConfigToServiceFile(this.ast, { [name]: clone(rest, false) });
|
||||
return this;
|
||||
}
|
||||
|
||||
addServiceFunctions(payloads: ServiceFunctionPayloadType[]) {
|
||||
addServiceFunctions(payloads: IServiceFunctionPayload[]) {
|
||||
const config = payloads.reduce((acc, cur) => {
|
||||
const { name, ...rest } = cur;
|
||||
acc[name] = clone(rest, false);
|
||||
@@ -667,7 +667,7 @@ export class TangoServiceModule extends TangoModule {
|
||||
return this;
|
||||
}
|
||||
|
||||
updateServiceFunction(payload: ServiceFunctionPayloadType) {
|
||||
updateServiceFunction(payload: IServiceFunctionPayload) {
|
||||
const { name, ...rest } = payload;
|
||||
this.ast = updateServiceConfigToServiceFile(this.ast, { [name]: clone(rest, false) });
|
||||
return this;
|
||||
@@ -702,11 +702,11 @@ export class TangoStoreModule extends TangoModule {
|
||||
|
||||
namespace: string;
|
||||
|
||||
states: StorePropertyType[];
|
||||
states: IStorePropertyData[];
|
||||
|
||||
actions: StorePropertyType[];
|
||||
actions: IStorePropertyData[];
|
||||
|
||||
constructor(workspace: IWorkspace, props: ModulePropsType) {
|
||||
constructor(workspace: IWorkspace, props: IFileConfig) {
|
||||
super(workspace, props, false);
|
||||
this.name = getModuleNameByFilename(props.filename);
|
||||
this.update(props.code, true, false);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { JSXElement, SourceLocation } from '@babel/types';
|
||||
import { cloneJSXElementWithoutTrackingData, getJSXElementAttributes } from '../helpers';
|
||||
import { TangoViewNodeDataType } from '../types';
|
||||
import { ITangoViewNodeData } from '../types';
|
||||
import { TangoViewModule } from './module';
|
||||
import { IViewNode } from './interfaces';
|
||||
|
||||
type TangoNodeConstructorPropsType = TangoViewNodeDataType & {
|
||||
type TangoNodeConstructorPropsType = ITangoViewNodeData & {
|
||||
file: TangoViewModule;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,19 +31,19 @@ import {
|
||||
TangoJsModule,
|
||||
} from './module';
|
||||
import { TangoFile, TangoJsonFile, TangoLessFile } from './file';
|
||||
import {
|
||||
FileItemType,
|
||||
FileType,
|
||||
PackageConfigType,
|
||||
PageConfigType,
|
||||
ProjectDataType,
|
||||
ServiceFunctionPayloadType,
|
||||
} from '../types';
|
||||
import { IWorkspace } from './interfaces';
|
||||
import {
|
||||
IFileConfig,
|
||||
FileType,
|
||||
ITangoConfigPackages,
|
||||
IPageConfigData,
|
||||
IProjectData,
|
||||
IServiceFunctionPayload,
|
||||
} from '../types';
|
||||
import { SelectSource } from './select-source';
|
||||
import { DragSource } from './drag-source';
|
||||
|
||||
export type WorkspaceOptionsType = {
|
||||
export interface IWorkspaceOptions {
|
||||
/**
|
||||
* 入口文件
|
||||
*/
|
||||
@@ -51,7 +51,7 @@ export type WorkspaceOptionsType = {
|
||||
/**
|
||||
* 初始化文件列表
|
||||
*/
|
||||
files?: FileItemType[];
|
||||
files?: IFileConfig[];
|
||||
/**
|
||||
* 默认的激活的路由
|
||||
*/
|
||||
@@ -60,7 +60,7 @@ export type WorkspaceOptionsType = {
|
||||
* 组件描述列表
|
||||
*/
|
||||
prototypes?: Record<string, ComponentPrototypeType>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作区
|
||||
@@ -180,7 +180,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
get pages() {
|
||||
const appJsonPages = this.appJson?.getValue('pages');
|
||||
const pagesMap = array2object(appJsonPages || [], (item) => item.path);
|
||||
const ret: PageConfigType[] = [];
|
||||
const ret: IPageConfigData[] = [];
|
||||
this.routeModule?.routes.forEach((item) => {
|
||||
const data = pagesMap[item.path];
|
||||
if (item.path !== '*') {
|
||||
@@ -225,7 +225,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
return Object.keys(this.localBlocks);
|
||||
}
|
||||
|
||||
constructor(options?: WorkspaceOptionsType) {
|
||||
constructor(options?: IWorkspaceOptions) {
|
||||
super();
|
||||
this.history = new TangoHistory(this);
|
||||
this.selectSource = new SelectSource(this);
|
||||
@@ -292,7 +292,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
functions: Object.keys(this.serviceModule.serviceFunctions),
|
||||
},
|
||||
};
|
||||
const data: ProjectDataType = {
|
||||
const data: IProjectData = {
|
||||
pages,
|
||||
stores,
|
||||
services,
|
||||
@@ -353,7 +353,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* 添加一组文件到工作区
|
||||
* @param files
|
||||
*/
|
||||
addFiles(files: FileItemType[] = []) {
|
||||
addFiles(files: IFileConfig[] = []) {
|
||||
files.forEach((file) => {
|
||||
this.addFile(file.filename, file.code, file.type);
|
||||
});
|
||||
@@ -540,7 +540,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
// remove appJson page
|
||||
this.appJson
|
||||
?.setValue('pages', (pages) => {
|
||||
return (pages as PageConfigType[]).filter((page) => page.path !== routePath);
|
||||
return (pages as IPageConfigData[]).filter((page) => page.path !== routePath);
|
||||
})
|
||||
.update();
|
||||
this.removeFile(filename);
|
||||
@@ -552,7 +552,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* @param route 视图名
|
||||
* @param code 视图代码
|
||||
*/
|
||||
addViewPage(routeConfig: string | PageConfigType, code: string) {
|
||||
addViewPage(routeConfig: string | IPageConfigData, code: string) {
|
||||
const config =
|
||||
typeof routeConfig === 'string'
|
||||
? {
|
||||
@@ -569,11 +569,11 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
/**
|
||||
* 添加新的路由
|
||||
*/
|
||||
addRoute(routeData: PageConfigType, importFilePath: string) {
|
||||
addRoute(routeData: IPageConfigData, importFilePath: string) {
|
||||
this.routeModule?.addRoute(routeData.path, importFilePath).update();
|
||||
this.appJson
|
||||
?.setValue('pages', (pages) => {
|
||||
(pages as PageConfigType[]).push({
|
||||
(pages as IPageConfigData[]).push({
|
||||
name: routeData.name || routeData.path,
|
||||
path: routeData.path,
|
||||
privilegeCode: getPrivilegeCode(this.packageJson?.getValue('name'), routeData.path),
|
||||
@@ -588,13 +588,13 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* @param sourceRoutePath
|
||||
* @param targetPageData
|
||||
*/
|
||||
updateRoute(sourceRoutePath: string, targetPageData: PageConfigType) {
|
||||
updateRoute(sourceRoutePath: string, targetPageData: IPageConfigData) {
|
||||
if (sourceRoutePath !== targetPageData.path) {
|
||||
this.routeModule?.updateRoute(sourceRoutePath, targetPageData.path).update();
|
||||
}
|
||||
this.appJson
|
||||
?.setValue('pages', (pages) => {
|
||||
for (const page of pages as PageConfigType[]) {
|
||||
for (const page of pages as IPageConfigData[]) {
|
||||
if (page.path === sourceRoutePath) {
|
||||
page.path = targetPageData.path;
|
||||
page.name = targetPageData.name;
|
||||
@@ -611,7 +611,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
* @param sourceRoute
|
||||
* @param targetRouteConfig
|
||||
*/
|
||||
copyViewPage(sourceRoutePath: string, targetPageData: PageConfigType) {
|
||||
copyViewPage(sourceRoutePath: string, targetPageData: IPageConfigData) {
|
||||
const sourceFilePath = this.getRealViewFilePath(this.getFilenameByRoutePath(sourceRoutePath));
|
||||
const targetFilePath = getFilepath(targetPageData.path, '/src/pages');
|
||||
this.copyFiles(sourceFilePath, targetFilePath);
|
||||
@@ -771,7 +771,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
/**
|
||||
* 新增服务函数,支持批量添加
|
||||
*/
|
||||
addServiceFunction(payload: ServiceFunctionPayloadType | ServiceFunctionPayloadType[]) {
|
||||
addServiceFunction(payload: IServiceFunctionPayload | IServiceFunctionPayload[]) {
|
||||
if (Array.isArray(payload)) {
|
||||
this.serviceModule.addServiceFunctions(payload).update();
|
||||
} else {
|
||||
@@ -811,7 +811,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
name: string,
|
||||
version: string,
|
||||
options?: {
|
||||
package?: PackageConfigType;
|
||||
package?: ITangoConfigPackages;
|
||||
[x: string]: any;
|
||||
},
|
||||
) {
|
||||
@@ -906,7 +906,7 @@ export class Workspace extends EventTarget implements IWorkspace {
|
||||
name: string,
|
||||
version: string,
|
||||
options?: {
|
||||
package?: PackageConfigType;
|
||||
package?: ITangoConfigPackages;
|
||||
[x: string]: any;
|
||||
},
|
||||
) {
|
||||
|
||||
@@ -27,7 +27,7 @@ export enum FileType {
|
||||
Scss = 'scss',
|
||||
}
|
||||
|
||||
export type FileItemType = {
|
||||
export interface IFileConfig {
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
@@ -40,14 +40,12 @@ export type FileItemType = {
|
||||
* 文件类型
|
||||
*/
|
||||
type?: FileType;
|
||||
};
|
||||
|
||||
export type ModulePropsType = FileItemType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图节点数据类型
|
||||
*/
|
||||
export type TangoViewNodeDataType<T = JSXElement> = {
|
||||
export interface ITangoViewNodeData<T = JSXElement> {
|
||||
/**
|
||||
* 节点 ID
|
||||
*/
|
||||
@@ -71,39 +69,39 @@ export type TangoViewNodeDataType<T = JSXElement> = {
|
||||
/**
|
||||
* 子节点列表
|
||||
*/
|
||||
children?: Array<TangoViewNodeDataType<T>>;
|
||||
};
|
||||
children?: Array<ITangoViewNodeData<T>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块导入的参数类型
|
||||
*/
|
||||
export type ImportDeclarationPayloadType = {
|
||||
export interface IImportDeclarationPayload {
|
||||
defaultSpecifier?: string;
|
||||
specifiers?: string[];
|
||||
sourcePath: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type ClassPropertyNodeType = {
|
||||
export interface IClassPropertyNodeData {
|
||||
reference: string;
|
||||
propertyName: string;
|
||||
propertyBody: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务函数参数类型
|
||||
*/
|
||||
export type ServiceFunctionPayloadType = {
|
||||
export interface IServiceFunctionPayload {
|
||||
[key: string]: any;
|
||||
/**
|
||||
* 服务函数名
|
||||
*/
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Store 属性类型
|
||||
*/
|
||||
export type StorePropertyType = {
|
||||
export interface IStorePropertyData {
|
||||
/**
|
||||
* 属性名
|
||||
*/
|
||||
@@ -130,50 +128,12 @@ export type StorePropertyType = {
|
||||
| 'type'
|
||||
| 'variable'
|
||||
| 'object';
|
||||
};
|
||||
|
||||
/*
|
||||
* 服务函数的操作类型
|
||||
*/
|
||||
export enum ESFOperationType {
|
||||
ADD = 'add',
|
||||
UPDATE = 'update',
|
||||
DELETE = 'delete',
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务函数数据源类型
|
||||
*/
|
||||
export enum ESFDataSourceType {
|
||||
OvermindX = 'ox',
|
||||
Custom = 'custom',
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务函数 HTTP Type
|
||||
* 云音乐网关只支持 get 和 post
|
||||
*/
|
||||
export enum ESFHTTPMethodType {
|
||||
GET = 'GET',
|
||||
// PUT = 'PUT',
|
||||
POST = 'POST',
|
||||
// PATCH = 'PATCH',
|
||||
// DELETE = 'DELETE',
|
||||
}
|
||||
|
||||
/**
|
||||
* requestType
|
||||
* headers: Content-Type
|
||||
*/
|
||||
export enum ESFHTTRequestType {
|
||||
'application/json' = 'json',
|
||||
'application/x-www-form-urlencoded' = 'x-www-form-urlencoded',
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由解析数据
|
||||
*/
|
||||
export type RouteDataType = {
|
||||
export interface IRouteData {
|
||||
/**
|
||||
* 路由
|
||||
*/
|
||||
@@ -187,12 +147,12 @@ export type RouteDataType = {
|
||||
*/
|
||||
importPath?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面配置数据
|
||||
*/
|
||||
export type PageConfigType = {
|
||||
export interface IPageConfigData {
|
||||
/**
|
||||
* 路由
|
||||
*/
|
||||
@@ -209,12 +169,12 @@ export type PageConfigType = {
|
||||
* PMS 权限码
|
||||
*/
|
||||
privilegeCode?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目数据
|
||||
*/
|
||||
export type ProjectDataType = {
|
||||
export interface IProjectData {
|
||||
/**
|
||||
* 页面列表
|
||||
*/
|
||||
@@ -241,14 +201,14 @@ export type ProjectDataType = {
|
||||
functions: string[];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type InsertChildPositionType = 'first' | 'last';
|
||||
|
||||
/**
|
||||
* tango.config.json 中 packages 定义
|
||||
*/
|
||||
export type PackageConfigType = {
|
||||
export interface ITangoConfigPackages {
|
||||
/**
|
||||
* 依赖类型
|
||||
*/
|
||||
@@ -273,4 +233,4 @@ export type PackageConfigType = {
|
||||
* 设计态 umd 资源
|
||||
*/
|
||||
designerResources?: string[];
|
||||
};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './types';
|
||||
@@ -3,6 +3,27 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.2.0](https://github.com/netease/tango/compare/@music163/tango-designer@0.1.3...@music163/tango-designer@0.2.0) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- nodeTree defaultExpandAll rerender bug ([#24](https://github.com/netease/tango/issues/24)) ([12de10b](https://github.com/netease/tango/commit/12de10b0bc89a27d80b1b3bbb4d4b5aed4b73b35))
|
||||
- refactor setting formItem ([b786de2](https://github.com/netease/tango/commit/b786de2f1a0e4e9141eb09fce696e45df633b232))
|
||||
- refactor types ([15542d9](https://github.com/netease/tango/commit/15542d9eb2f8959597b81cae457091ee71710c83))
|
||||
- remove some useless setters ([7e9ab12](https://github.com/netease/tango/commit/7e9ab12503ed33c1e6acb8a1fa5fd89fc82d35fd))
|
||||
- update exports ([443ae58](https://github.com/netease/tango/commit/443ae589f67255f3815b587fd8f928c2b8742e9a))
|
||||
- update sidebar ([4d809e9](https://github.com/netease/tango/commit/4d809e9afd0d6d525850708722736847b510638e))
|
||||
|
||||
### Features
|
||||
|
||||
- refactor setting-form ([8d396ff](https://github.com/netease/tango/commit/8d396ff13459beeb57b6b3c48f7e8fe1765041ae))
|
||||
|
||||
## [0.1.3](https://github.com/netease/tango/compare/@music163/tango-designer@0.1.2...@music163/tango-designer@0.1.3) (2023-09-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- add WorkspaceView ([5ecf444](https://github.com/netease/tango/commit/5ecf444fc3563a1bf8e533136e93c1a1355d39dc))
|
||||
|
||||
## 0.1.2 (2023-09-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-designer",
|
||||
"version": "0.1.2",
|
||||
"version": "0.2.0",
|
||||
"description": "lowcode designer",
|
||||
"keywords": [
|
||||
"react"
|
||||
@@ -33,22 +33,28 @@
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/request": "^0.1.0",
|
||||
"@music163/tango-context": "^0.1.2",
|
||||
"@music163/tango-core": "^0.1.2",
|
||||
"@music163/tango-context": "^0.1.3",
|
||||
"@music163/tango-core": "^0.1.3",
|
||||
"@music163/tango-helpers": "^0.1.2",
|
||||
"@music163/tango-sandbox": "^0.1.2",
|
||||
"@music163/tango-setting-form": "^0.1.2",
|
||||
"@music163/tango-ui": "^0.1.2",
|
||||
"@music163/tango-sandbox": "^0.1.3",
|
||||
"@music163/tango-setting-form": "^0.2.0",
|
||||
"@music163/tango-ui": "^0.1.3",
|
||||
"antd": "^4.24.2",
|
||||
"cash-dom": "^8.1.2",
|
||||
"classnames": "^2.3.2",
|
||||
"color": "^4.2.3",
|
||||
"coral-system": "^1.0.5",
|
||||
"cssjson": "^2.1.3",
|
||||
"date-fns": "^2.29.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"react-color": "^2.19.3",
|
||||
"react-resizable": "^3.0.5",
|
||||
"semver": "^7.3.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/color": "^3.0.4",
|
||||
"@types/react-color": "^3.0.6",
|
||||
"@types/react-resizable": "^3.0.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
+59
-6
@@ -3,7 +3,6 @@ import { Input } from 'antd';
|
||||
import { css } from 'styled-components';
|
||||
import { Box } from 'coral-system';
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { formatValue, compareValue } from './util';
|
||||
|
||||
const rowCss = css`
|
||||
display: flex;
|
||||
@@ -99,25 +98,25 @@ const OneInputKV = (props: any) => {
|
||||
!newData.length && setValidate(false);
|
||||
};
|
||||
|
||||
const handleInput = (name: string, index: number, value: string) => {
|
||||
const handleInput = (name: string, index: number, val: string) => {
|
||||
const newData = [...data];
|
||||
|
||||
if (!newData[index]) return;
|
||||
|
||||
// 更新值
|
||||
newData[index][name] = value;
|
||||
newData[index][name] = val;
|
||||
|
||||
_updateData(newData);
|
||||
};
|
||||
|
||||
const _updateData = (values: Array<Record<string, string>>) => {
|
||||
const omit = ({ index, ...rest }: any) => rest;
|
||||
let value = formatValue({ data: values, format });
|
||||
let nextValue = formatValue({ data: values, format });
|
||||
|
||||
Array.isArray(value) && (value = value.map((item) => omit(item)));
|
||||
Array.isArray(nextValue) && (nextValue = nextValue.map((item) => omit(item)));
|
||||
|
||||
setData(values);
|
||||
onChange?.(value);
|
||||
onChange?.(nextValue);
|
||||
};
|
||||
|
||||
// render
|
||||
@@ -166,3 +165,57 @@ const OneInputKV = (props: any) => {
|
||||
};
|
||||
|
||||
export const InputKV = memo(OneInputKV, (prev, next) => prev.value === next.value);
|
||||
|
||||
interface IFormatValueConfig {
|
||||
format: 'simple' | 'original';
|
||||
data?: Record<string, string> | Array<Record<string, string>>;
|
||||
}
|
||||
|
||||
const formatData = (data?: Array<Record<string, string>>) => {
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
// 遍历数据
|
||||
data?.forEach((item) => {
|
||||
item.key && !(item.key in result) && (result[item.key] = item.value);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// 格式化value
|
||||
const formatValue = ({ format, data }: IFormatValueConfig) => {
|
||||
if (format === 'original') {
|
||||
if (!data) return [{ index: 0 }];
|
||||
|
||||
const list = Array.isArray(data)
|
||||
? data
|
||||
: Object.keys(data).map((key) => ({ key, value: data[key] }));
|
||||
|
||||
return list.map((item, idx) => ({ ...item, index: idx - list.length - 1 }));
|
||||
} else {
|
||||
if (!data) return {};
|
||||
|
||||
return Array.isArray(data) ? formatData(data) : data;
|
||||
}
|
||||
};
|
||||
|
||||
const compareValue = (origin: Array<any>, data: Array<any>) => {
|
||||
const resolve = (list: Array<any>) => {
|
||||
let keyStr = '';
|
||||
let valueStr = '';
|
||||
|
||||
list.forEach((item) => {
|
||||
const { key, value } = item;
|
||||
|
||||
keyStr += key || '$';
|
||||
valueStr += value || '$';
|
||||
});
|
||||
|
||||
return { keyStr, valueStr };
|
||||
};
|
||||
|
||||
const oldRes = resolve(origin);
|
||||
const newRes = resolve(data);
|
||||
|
||||
return oldRes.keyStr === newRes.keyStr && oldRes.valueStr === newRes.valueStr;
|
||||
};
|
||||
+26
-26
@@ -33,7 +33,7 @@ import {
|
||||
} from '@music163/tango-ui';
|
||||
import { isValidExpressionCode } from '@music163/tango-core';
|
||||
|
||||
export type VariableTreeNodeType = {
|
||||
export interface IVariableTreeNode {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
@@ -65,9 +65,9 @@ export type VariableTreeNodeType = {
|
||||
/**
|
||||
* 子结点
|
||||
*/
|
||||
children?: VariableTreeNodeType[];
|
||||
children?: IVariableTreeNode[];
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
type VariableModelType = 'preview' | 'define' | 'add';
|
||||
|
||||
@@ -91,15 +91,15 @@ export interface EditableVariableTreeProps {
|
||||
/**
|
||||
* 预览值取值函数
|
||||
*/
|
||||
getPreviewValue?: (node: VariableTreeNodeType) => unknown;
|
||||
getPreviewValue?: (node: IVariableTreeNode) => unknown;
|
||||
/**
|
||||
* 选择列表项时的回调
|
||||
*/
|
||||
onSelect?: (data: VariableTreeNodeType) => void;
|
||||
onSelect?: (data: IVariableTreeNode) => void;
|
||||
/**
|
||||
* 点击使用此变量按钮时的回调
|
||||
*/
|
||||
onUse?: (data: VariableTreeNodeType) => void;
|
||||
onUse?: (data: IVariableTreeNode) => void;
|
||||
/**
|
||||
* 保存结点定义的回调
|
||||
*/
|
||||
@@ -152,7 +152,7 @@ export function EditableVariableTree({
|
||||
showPreviewAlert = true,
|
||||
}: EditableVariableTreeProps) {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [node, setNode] = useState<VariableTreeNodeType>();
|
||||
const [node, setNode] = useState<IVariableTreeNode>();
|
||||
const [mode, setMode] = useState<EditableVariableTreeProps['defaultMode']>(defaultMode);
|
||||
const hasPanelSwitch = modes.includes('define') && modes.includes('preview');
|
||||
|
||||
@@ -184,17 +184,17 @@ export function EditableVariableTree({
|
||||
</Box>
|
||||
<VariableTree
|
||||
dataSource={treeData}
|
||||
onSelect={(node) => {
|
||||
setNode(node);
|
||||
onSelect={(item) => {
|
||||
setNode(item);
|
||||
mode === 'add' && setMode(defaultMode);
|
||||
onSelect(node);
|
||||
onSelect(item);
|
||||
}}
|
||||
onAdd={(node) => {
|
||||
setNode(node);
|
||||
onAdd={(item) => {
|
||||
setNode(item);
|
||||
setMode('add');
|
||||
}}
|
||||
onRemove={(node) => {
|
||||
const [type, storeName] = node.key.split('.');
|
||||
onRemove={(item) => {
|
||||
const [type, storeName] = item.key.split('.');
|
||||
setNode(null);
|
||||
onDeleteStore(storeName);
|
||||
}}
|
||||
@@ -257,8 +257,8 @@ export function EditableVariableTree({
|
||||
<NodeDefineForm
|
||||
node={node}
|
||||
onSave={onSave}
|
||||
onDelete={(node) => {
|
||||
const [type, storeName, stateName] = node.key.split('.');
|
||||
onDelete={(item) => {
|
||||
const [type, storeName, stateName] = item.key.split('.');
|
||||
onDeleteVariable(storeName, stateName);
|
||||
setNode(null);
|
||||
}}
|
||||
@@ -301,7 +301,7 @@ export function EditableVariableTreeModal({
|
||||
onSelect = noop,
|
||||
...rest
|
||||
}: EditableVariableTreeModalProps) {
|
||||
const [node, setNode] = useState<VariableTreeNodeType>();
|
||||
const [node, setNode] = useState<IVariableTreeNode>();
|
||||
const [visible, { on, off }] = useBoolean(false);
|
||||
return (
|
||||
<Box>
|
||||
@@ -348,10 +348,10 @@ const varTreeStyle = css`
|
||||
`;
|
||||
|
||||
interface VariableTreeProps {
|
||||
dataSource?: VariableTreeNodeType[];
|
||||
onSelect?: (data: VariableTreeNodeType) => void;
|
||||
onAdd?: (data: VariableTreeNodeType) => void;
|
||||
onRemove?: (data: VariableTreeNodeType) => void;
|
||||
dataSource?: IVariableTreeNode[];
|
||||
onSelect?: (data: IVariableTreeNode) => void;
|
||||
onAdd?: (data: IVariableTreeNode) => void;
|
||||
onRemove?: (data: IVariableTreeNode) => void;
|
||||
}
|
||||
|
||||
function VariableTree({
|
||||
@@ -450,9 +450,9 @@ export function ValuePreview({ value, onSelect }: NodePreviewProps) {
|
||||
}
|
||||
|
||||
interface NodeDefineProps {
|
||||
node: VariableTreeNodeType;
|
||||
onSave?: (code: string, node: VariableTreeNodeType) => void;
|
||||
onDelete?: (node: VariableTreeNodeType) => void;
|
||||
node: IVariableTreeNode;
|
||||
onSave?: (code: string, node: IVariableTreeNode) => void;
|
||||
onDelete?: (node: IVariableTreeNode) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -470,7 +470,7 @@ function NodeDefineForm({ node, onSave = noop, onDelete = noop }: NodeDefineProp
|
||||
<InputCode
|
||||
shape="inset"
|
||||
value={node.raw}
|
||||
onChange={(value) => setValue(value)}
|
||||
onChange={(nextValue) => setValue(nextValue)}
|
||||
editable={editable}
|
||||
onBlur={() => {
|
||||
if (!isValidExpressionCode(value)) {
|
||||
@@ -527,7 +527,7 @@ function NodeDefineForm({ node, onSave = noop, onDelete = noop }: NodeDefineProp
|
||||
}
|
||||
|
||||
interface AddNodeFormProps {
|
||||
parentNode: VariableTreeNodeType;
|
||||
parentNode: IVariableTreeNode;
|
||||
onCancel?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onSubmit?: (storeName: string, data: { name: string; initialValue: string }) => void;
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
import { createContext, TangoRemoteServicesType } from '@music163/tango-helpers';
|
||||
import { createContext } from '@music163/tango-helpers';
|
||||
import type { DndQuery } from './dnd';
|
||||
|
||||
/**
|
||||
* TODO: 是不是直接合并到 Context 中
|
||||
*/
|
||||
export interface IDesignerContext {
|
||||
export interface IDesignerContext<S = any> {
|
||||
/**
|
||||
* 沙箱查询实例
|
||||
*/
|
||||
@@ -12,7 +9,7 @@ export interface IDesignerContext {
|
||||
/**
|
||||
* 远程服务
|
||||
*/
|
||||
remoteServices?: TangoRemoteServicesType;
|
||||
remoteServices?: Record<string, S>;
|
||||
}
|
||||
|
||||
const [DesignerProvider, useDesigner] = createContext<IDesignerContext>({
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { SystemProvider } from 'coral-system';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { SystemProvider, extendTheme } from 'coral-system';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import zhCN from 'antd/lib/locale/zh_CN';
|
||||
import { TangoEngineProvider, ITangoEngineContext } from '@music163/tango-context';
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
import { DesignerProvider, IDesignerContext } from './context';
|
||||
import { theme as baseTheme } from './theme';
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
import zhCN from 'antd/lib/locale/zh_CN';
|
||||
|
||||
export interface DesignerProps extends IDesignerContext, ITangoEngineContext {
|
||||
/**
|
||||
@@ -33,7 +33,7 @@ export function Designer(props: DesignerProps) {
|
||||
iconfontScriptUrl = '//at.alicdn.com/t/c/font_2891794_lzc7rtwuzf.js',
|
||||
children,
|
||||
} = props;
|
||||
const theme = themeProp ?? baseTheme;
|
||||
const theme = useMemo(() => extendTheme(themeProp, baseTheme), [themeProp]);
|
||||
useEffect(() => {
|
||||
createFromIconfontCN({
|
||||
scriptUrl: iconfontScriptUrl,
|
||||
|
||||
@@ -3,14 +3,15 @@ export * from './designer';
|
||||
export * from './designer-panel';
|
||||
export * from './workspace-panel';
|
||||
export * from './setting-panel';
|
||||
export * from './view-panel';
|
||||
export * from './workspace-view';
|
||||
export * from './simulator';
|
||||
export * from './dnd';
|
||||
export * from './editor';
|
||||
export * from './sandbox';
|
||||
export * from './sidebar';
|
||||
export * from './toolbar';
|
||||
export * from './dnd';
|
||||
export * from './selection-menu';
|
||||
|
||||
export { INTERNAL_SELECTION_TOOLS } from './simulator/selection';
|
||||
|
||||
export { useWorkspace, useDesigner, observer } from '@music163/tango-context';
|
||||
export { register as registerSetter } from '@music163/tango-setting-form';
|
||||
|
||||
export type { FormItemComponentProps, IFormItemCreateOptions } from '@music163/tango-setting-form';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useImperativeHandle, ForwardedRef, useRef, useState, useEffect } from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { CodeSandbox, CodeSandboxProps } from '@music163/tango-sandbox';
|
||||
import { getValue, logger, pick, setValue } from '@music163/tango-helpers';
|
||||
import { getValue, isFunction, logger, pick, setValue } from '@music163/tango-helpers';
|
||||
import { observer, useWorkspace, useDesigner } from '@music163/tango-context';
|
||||
import { Simulator, Viewport } from '../simulator';
|
||||
import { useSandboxQuery } from '../context';
|
||||
@@ -9,22 +9,27 @@ import { DndQuery, useDnd } from '../dnd';
|
||||
import { Navigator } from './navigator';
|
||||
import { SelectionToolsProps } from '../simulator/selection';
|
||||
|
||||
type SandboxEventHandlerConfig = {
|
||||
interface ISandboxEventHandlerConfig {
|
||||
sandboxQuery?: DndQuery;
|
||||
sandboxType?: 'design' | 'preview';
|
||||
isActive: boolean;
|
||||
[x: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export type SandboxProps = Omit<CodeSandboxProps, 'files' | 'eventHandlers' | 'onMessage'> & {
|
||||
isPreview?: boolean;
|
||||
selectionTools?: SelectionToolsProps['actions'];
|
||||
builtinSelectionMenuMap?: SelectionToolsProps['builtinActionMap'];
|
||||
/**
|
||||
* tangoConfigJson 处理器
|
||||
*/
|
||||
configFormatter?: IMergeTangoConfigJsonConfig['formatter'];
|
||||
sandboxType?: 'design' | 'preview';
|
||||
mode?: 'single' | 'combined';
|
||||
injectScript?: string;
|
||||
onViewChange?: (data: any, config?: SandboxEventHandlerConfig) => void;
|
||||
onMessage?: (data: any, config?: SandboxEventHandlerConfig) => void;
|
||||
onLoad?: (config?: SandboxEventHandlerConfig) => void;
|
||||
onViewChange?: (data: any, config?: ISandboxEventHandlerConfig) => void;
|
||||
onMessage?: (data: any, config?: ISandboxEventHandlerConfig) => void;
|
||||
onLoad?: (config?: ISandboxEventHandlerConfig) => void;
|
||||
};
|
||||
|
||||
export type CombinedSandboxRef = {
|
||||
@@ -36,6 +41,7 @@ const LANDING_PAGE_PATH = '/__background_landing_page__';
|
||||
|
||||
function useSandbox({
|
||||
isPreview: isPreviewProp,
|
||||
configFormatter,
|
||||
onViewChange,
|
||||
onMessage: onMessageProp,
|
||||
onLoad: onLoadProp,
|
||||
@@ -69,12 +75,12 @@ function useSandbox({
|
||||
let files = Array.from(workspace.files.keys()).reduce((prev, filename) => {
|
||||
let code = workspace.getFile(filename).code;
|
||||
if (filename === '/tango.config.json') {
|
||||
code = mergeTangoConfigJson(code, isPreview, { injectScript });
|
||||
code = mergeTangoConfigJson(code, { isPreview, injectScript, formatter: configFormatter });
|
||||
}
|
||||
prev[filename] = { code };
|
||||
return prev;
|
||||
}, {});
|
||||
files = normalizeFiles(files, workspace.entry);
|
||||
files = fixSandboxFiles(files, workspace.entry);
|
||||
|
||||
const onMessage = (data: any) => onMessageProp && onMessageProp(data, getSandboxConfig());
|
||||
const onLoad = () => onLoadProp && onLoadProp(getSandboxConfig());
|
||||
@@ -187,7 +193,7 @@ export const CombinedSandbox = observer(
|
||||
const activeSandbox = useRef<string>();
|
||||
const [startRoute, setStartRoute] = useState(workspace.activeRoute);
|
||||
|
||||
const onViewChange = (data: any, config: SandboxEventHandlerConfig) => {
|
||||
const onViewChange = (data: any, config: ISandboxEventHandlerConfig) => {
|
||||
if (config.isActive) {
|
||||
const curPath = data?.pathname + data?.search;
|
||||
const isSandboxChanged = config.sandboxType !== activeSandbox.current;
|
||||
@@ -275,6 +281,7 @@ export const Sandbox = observer(
|
||||
({
|
||||
isPreview: isPreviewProp,
|
||||
selectionTools,
|
||||
builtinSelectionMenuMap,
|
||||
bundlerURL,
|
||||
mode = 'combined',
|
||||
...props
|
||||
@@ -294,7 +301,7 @@ export const Sandbox = observer(
|
||||
: combinedSandboxRef.current?.designSandbox;
|
||||
}
|
||||
|
||||
const onViewChange = (data: any, { isActive }: SandboxEventHandlerConfig) => {
|
||||
const onViewChange = (data: any, { isActive }: ISandboxEventHandlerConfig) => {
|
||||
if (isActive) {
|
||||
navigatorRef.current.changeRelativeUrl(data?.pathname + data?.search);
|
||||
}
|
||||
@@ -345,7 +352,7 @@ export const Sandbox = observer(
|
||||
}}
|
||||
/>
|
||||
<Simulator>
|
||||
<Viewport selectionTools={selectionTools}>
|
||||
<Viewport selectionTools={selectionTools} builtinActionMap={builtinSelectionMenuMap}>
|
||||
{mode === 'single' && (
|
||||
<DesignSandbox
|
||||
ref={sandboxRef}
|
||||
@@ -378,7 +385,7 @@ export const Sandbox = observer(
|
||||
);
|
||||
|
||||
// 兼容 tango.config.json,转成 sandbox.config.json
|
||||
function normalizeFiles(files: object, entry = '/src/index.js') {
|
||||
function fixSandboxFiles(files: Record<string, { code: string }>, entry = '/src/index.js') {
|
||||
if (files['/tango.config.json']) {
|
||||
const tangConfigJsonStr = files['/tango.config.json'].code;
|
||||
const tangConfigJson = JSON.parse(tangConfigJsonStr);
|
||||
@@ -386,6 +393,7 @@ function normalizeFiles(files: object, entry = '/src/index.js') {
|
||||
code: JSON.stringify(tangConfigJson.sandbox, null, 2),
|
||||
};
|
||||
}
|
||||
|
||||
if (!files['/index.html']) {
|
||||
files['/index.html'] = {
|
||||
code: `
|
||||
@@ -405,28 +413,30 @@ function normalizeFiles(files: object, entry = '/src/index.js') {
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
function mergeTangoConfigJson(code: string, isPreview: boolean, config?: { [x: string]: any }) {
|
||||
interface IMergeTangoConfigJsonConfig {
|
||||
isPreview?: boolean;
|
||||
injectScript?: string;
|
||||
formatter?: (json: object) => object;
|
||||
}
|
||||
|
||||
function mergeTangoConfigJson(
|
||||
code: string,
|
||||
{ isPreview, injectScript, formatter }: IMergeTangoConfigJsonConfig = {},
|
||||
) {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(code);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
logger.error('Json parse failed!', err);
|
||||
return code;
|
||||
}
|
||||
|
||||
const ox = getValue(json, 'dataSource.ox');
|
||||
const userJs = getValue(json, 'sandbox.evaluateJavaScript') || '';
|
||||
let mergedUserJs = userJs;
|
||||
const { injectScript } = config || {};
|
||||
|
||||
if (ox) {
|
||||
// TIP: 自动拼装 __tango_ox__ 注入到沙箱中
|
||||
mergedUserJs = `window.__tango_ox__=${JSON.stringify(ox)};${mergedUserJs}`;
|
||||
}
|
||||
if (injectScript) {
|
||||
mergedUserJs = `${mergedUserJs};${injectScript}`;
|
||||
}
|
||||
@@ -435,15 +445,6 @@ function mergeTangoConfigJson(code: string, isPreview: boolean, config?: { [x: s
|
||||
setValue(json, 'sandbox.evaluateJavaScript', mergedUserJs);
|
||||
}
|
||||
|
||||
const i18n = getValue(json, 'i18n');
|
||||
if (i18n) {
|
||||
// TIP: 合并 i18n 配置到沙箱配置中
|
||||
setValue(json, 'sandbox.i18n', {
|
||||
id: i18n.appId,
|
||||
preModule: i18n.preModule,
|
||||
});
|
||||
}
|
||||
|
||||
// 合并 packages 内的信息至 sandbox
|
||||
const packages = getValue(json, 'packages');
|
||||
const externals = getValue(json, 'sandbox.externals') || {};
|
||||
@@ -490,6 +491,10 @@ function mergeTangoConfigJson(code: string, isPreview: boolean, config?: { [x: s
|
||||
setValue(json, 'sandbox.externals', externals);
|
||||
setValue(json, 'sandbox.externalResources', [...new Set(externalResources)]);
|
||||
|
||||
if (isFunction(formatter)) {
|
||||
json = formatter?.(json);
|
||||
}
|
||||
|
||||
return JSON.stringify(json);
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Select } from 'antd';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { ListSetter, NewOptionFormFieldType } from './list-setter';
|
||||
|
||||
const defaultNewOptionValue = {
|
||||
@@ -28,7 +28,11 @@ const optionFormFields: NewOptionFormFieldType[] = [
|
||||
{ label: 'key', name: 'key', required: true },
|
||||
{ label: '文本', name: 'label', required: true },
|
||||
{ label: '外观', name: 'shape', component: <Select placeholder="请选择" options={shapes} /> },
|
||||
{ label: '按钮类型', name: 'buttonType', component: <Select placeholder="请选择" options={buttonTypes} /> },
|
||||
{
|
||||
label: '按钮类型',
|
||||
name: 'buttonType',
|
||||
component: <Select placeholder="请选择" options={buttonTypes} />,
|
||||
},
|
||||
{ label: '类型', name: 'actionType', component: <Select placeholder="请选择" options={types} /> },
|
||||
{ label: '确认气泡内容', name: 'confirmContent' },
|
||||
];
|
||||
+2
-2
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Switch } from 'antd';
|
||||
import { Box } from 'coral-system';
|
||||
import { FieldStringOutlined } from '@ant-design/icons';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { ListSetter, NewOptionFormFieldType } from './list-setter';
|
||||
|
||||
const defaultNewOptionValue = {
|
||||
@@ -37,7 +37,7 @@ const renderSetterItem = (item: any) => {
|
||||
export function ColumnSetter(props: FormItemComponentProps<any[]>) {
|
||||
return (
|
||||
<ListSetter
|
||||
getListItemKey={(item) => item.key || item.dataIndex}
|
||||
getListItemKey={(item: any) => item.key || item.dataIndex}
|
||||
addBtnText="添加新列"
|
||||
listItemFormFields={optionFormFields}
|
||||
newItemDefaultValues={defaultNewOptionValue}
|
||||
+1
-2
@@ -3,8 +3,7 @@ import { Box } from 'coral-system';
|
||||
// @ts-ignore
|
||||
import { toJSON } from 'cssjson';
|
||||
import { InputNumber, Space } from 'antd';
|
||||
import { SliderSetter } from './number-setter';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps, SliderSetter } from '@music163/tango-setting-form';
|
||||
import {
|
||||
BgSetter,
|
||||
BorderSetter,
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { DatePicker, TimePicker } from 'antd';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
|
||||
function toMoment(value: any, format: string) {
|
||||
let ret: moment.Moment;
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { InputKV } from '../components';
|
||||
|
||||
export function EnumSetter(props: FormItemComponentProps<string>) {
|
||||
+8
-7
@@ -1,11 +1,10 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { css } from 'styled-components';
|
||||
import { Box } from 'coral-system';
|
||||
import { css, Box } from 'coral-system';
|
||||
import { AutoComplete } from 'antd';
|
||||
import { ActionSelect } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||
import { ExpressionModal, getInputValue } from './expression-setter';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { useFormVariable } from '../context';
|
||||
|
||||
enum EventAction {
|
||||
NoAction = 'noAction',
|
||||
@@ -42,7 +41,9 @@ export function EventSetter(props: EventSetterProps) {
|
||||
const [temp, setTemp] = useState(''); // 二级暂存值
|
||||
const [expModalVisible, setExpModalVisible] = useState(false); // 弹窗是否显示
|
||||
|
||||
const { actionVariables, routeOptions, modalOptions } = useFormVariable();
|
||||
const { actionVariables, routeOptions } = useWorkspaceData();
|
||||
const workspace = useWorkspace();
|
||||
const modalOptions = workspace.listModals() || [];
|
||||
|
||||
const handleChange = useCallback<FormItemComponentProps['onChange']>(
|
||||
(nextValue: any, ...args) => {
|
||||
@@ -80,9 +81,9 @@ export function EventSetter(props: EventSetterProps) {
|
||||
defaultValue={inputValue}
|
||||
visible={expModalVisible}
|
||||
onCancel={() => setExpModalVisible(false)}
|
||||
onOk={(value) => {
|
||||
onOk={(nextValue) => {
|
||||
setExpModalVisible(false);
|
||||
handleChange(value);
|
||||
handleChange(nextValue);
|
||||
}}
|
||||
dataSource={actionVariables}
|
||||
/>
|
||||
+30
-13
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { Box, css } from 'coral-system';
|
||||
import { Alert, Modal, Tabs } from 'antd';
|
||||
import { value2code, isValidExpressionCode } from '@music163/tango-core';
|
||||
@@ -11,9 +11,10 @@ import {
|
||||
} from '@music163/tango-helpers';
|
||||
import { CloseCircleFilled, ExpandAltOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { IconButton, Panel, InputCode, ChatInput } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { EditableVariableTree, VariableTreeNodeType } from '../components';
|
||||
import { useFormVariable } from '../context';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||
import { EditableVariableTree, IVariableTreeNode } from '../components';
|
||||
import { useRemoteServices, useSandboxQuery } from '../context';
|
||||
|
||||
export const expressionValueValidate = (value: string) => {
|
||||
if (isVariableString(value)) {
|
||||
@@ -91,7 +92,8 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
||||
const [inputValue, setInputValue] = useState(() => {
|
||||
return getInputValue(valueProp);
|
||||
});
|
||||
const { evaluateContext } = useFormVariable();
|
||||
const sandbox = useSandboxQuery();
|
||||
const evaluateContext = sandbox.window;
|
||||
|
||||
// when receive new value, sync state
|
||||
useEffect(() => {
|
||||
@@ -122,7 +124,11 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<IconButton tooltip="打开表达式变量选择面板" icon={<ExpandAltOutlined />} onClick={on} />
|
||||
<IconButton
|
||||
tooltip="打开表达式变量选择面板"
|
||||
icon={<ExpandAltOutlined />}
|
||||
onClick={on}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
value={inputValue}
|
||||
@@ -160,7 +166,7 @@ export interface ExpressionModalProps {
|
||||
defaultValue?: string;
|
||||
onCancel?: () => void;
|
||||
onOk?: (value: string) => void;
|
||||
dataSource?: VariableTreeNodeType[];
|
||||
dataSource?: IVariableTreeNode[];
|
||||
autoCompleteOptions?: string[];
|
||||
}
|
||||
|
||||
@@ -176,8 +182,19 @@ export function ExpressionModal({
|
||||
autoCompleteOptions,
|
||||
}: ExpressionModalProps) {
|
||||
const [exp, setExp] = useState(defaultValue);
|
||||
const { expressionVariables, evaluateContext, onAction, remoteServices } = useFormVariable();
|
||||
const workspace = useWorkspace();
|
||||
const onAction = useCallback(
|
||||
(action: string, args: unknown[]) => {
|
||||
workspace[action]?.(...args);
|
||||
},
|
||||
[workspace],
|
||||
);
|
||||
const sandbox = useSandboxQuery();
|
||||
const remoteServices = useRemoteServices();
|
||||
const { expressionVariables } = useWorkspaceData();
|
||||
const evaluateContext = sandbox.window;
|
||||
const gptService = remoteServices?.GptService;
|
||||
|
||||
const handleExpInputChange = (val: string) => {
|
||||
setExp(val?.trim());
|
||||
};
|
||||
@@ -231,13 +248,13 @@ export function ExpressionModal({
|
||||
// height={400}
|
||||
dataSource={dataSource || expressionVariables}
|
||||
onUse={(node) => {
|
||||
let exp;
|
||||
let str;
|
||||
if (/^(stores|services)\./.test(node.key)) {
|
||||
exp = `{tango.${node.key.replaceAll('.', '?.')}}`;
|
||||
str = `{tango.${node.key.replaceAll('.', '?.')}}`;
|
||||
} else {
|
||||
exp = `{${node.key}}`;
|
||||
str = `{${node.key}}`;
|
||||
}
|
||||
setExp(exp);
|
||||
setExp(str);
|
||||
}}
|
||||
onAddVariable={(storeName, data) => {
|
||||
onAction('addStoreState', [storeName, data.name, data.initialValue]);
|
||||
@@ -278,7 +295,7 @@ export function ExpressionModal({
|
||||
frequencyPenalty: 0,
|
||||
mode: 'chat',
|
||||
})
|
||||
.then((res) => {
|
||||
.then((res: any) => {
|
||||
return res?.detail?.choices?.[0]?.text;
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import { register, IFormItemCreateOptions } from '@music163/tango-setting-form';
|
||||
import { ActionListSetter } from './action-list-setter';
|
||||
import { ColumnSetter } from './column-setter';
|
||||
import { CssSetter } from './css-setter';
|
||||
import { DateRangeSetter, DateSetter, TimeRangeSetter } from './date-setter';
|
||||
import { EnumSetter } from './enum-setter';
|
||||
import { EventSetter } from './event-setter';
|
||||
import { ExpressionSetter } from './expression-setter';
|
||||
import { JSONSetter } from './json-setter';
|
||||
import { JsxSetter } from './jsx-setter';
|
||||
import { ListSetter } from './list-setter';
|
||||
import { ModelSetter } from './model-setter';
|
||||
import { OptionSetter } from './option-setter';
|
||||
import { PickerSetter } from './picker-setter';
|
||||
import { RenderSetter, TableCellSetter, TableExpandableSetter } from './render-props-setter';
|
||||
import { RouterSetter } from './router-setter';
|
||||
import {
|
||||
SpacingSetter,
|
||||
ColorSetter,
|
||||
BgSetter,
|
||||
BorderSetter,
|
||||
FlexGapSetter,
|
||||
DisplaySetter,
|
||||
FlexJustifyContentSetter,
|
||||
FlexAlignItemsSetter,
|
||||
FlexDirectionSetter,
|
||||
} from './style-setter';
|
||||
|
||||
const setters: IFormItemCreateOptions[] = [
|
||||
{
|
||||
name: 'actionListSetter',
|
||||
component: ActionListSetter,
|
||||
},
|
||||
{
|
||||
name: 'columnSetter',
|
||||
component: ColumnSetter,
|
||||
},
|
||||
{
|
||||
name: 'cssSetter',
|
||||
component: CssSetter,
|
||||
},
|
||||
{
|
||||
name: 'dateSetter',
|
||||
component: DateSetter,
|
||||
},
|
||||
{
|
||||
name: 'dateRangeSetter',
|
||||
component: DateRangeSetter,
|
||||
},
|
||||
{
|
||||
name: 'timeSetter',
|
||||
component: DateRangeSetter,
|
||||
},
|
||||
{
|
||||
name: 'timeRangeSetter',
|
||||
component: TimeRangeSetter,
|
||||
},
|
||||
{
|
||||
name: 'enumSetter',
|
||||
component: EnumSetter,
|
||||
},
|
||||
{
|
||||
name: 'eventSetter',
|
||||
alias: ['actionSetter', 'functionSetter', 'callbackSetter'],
|
||||
component: EventSetter,
|
||||
},
|
||||
{
|
||||
name: 'expressionSetter',
|
||||
alias: ['expSetter'],
|
||||
component: ExpressionSetter,
|
||||
},
|
||||
{
|
||||
name: 'jsonSetter',
|
||||
component: JSONSetter,
|
||||
},
|
||||
{
|
||||
name: 'jsxSetter',
|
||||
component: JsxSetter,
|
||||
},
|
||||
{
|
||||
name: 'listSetter',
|
||||
component: ListSetter as any,
|
||||
},
|
||||
{
|
||||
name: 'modelSetter',
|
||||
component: ModelSetter,
|
||||
},
|
||||
{
|
||||
name: 'optionSetter',
|
||||
component: OptionSetter,
|
||||
},
|
||||
{
|
||||
name: 'pickerSetter',
|
||||
component: PickerSetter,
|
||||
},
|
||||
{
|
||||
name: 'renderPropsSetter',
|
||||
component: RenderSetter,
|
||||
},
|
||||
{
|
||||
name: 'tableCellSetter',
|
||||
component: TableCellSetter,
|
||||
},
|
||||
{
|
||||
name: 'tableExpandableSetter',
|
||||
component: TableExpandableSetter,
|
||||
},
|
||||
{
|
||||
name: 'routerSetter',
|
||||
component: RouterSetter,
|
||||
},
|
||||
{
|
||||
name: 'displaySetter',
|
||||
component: DisplaySetter,
|
||||
},
|
||||
{ name: 'flexDirectionSetter', component: FlexDirectionSetter },
|
||||
{
|
||||
name: 'flexGapSetter',
|
||||
component: FlexGapSetter,
|
||||
},
|
||||
{
|
||||
name: 'flexJustifyContentSetter',
|
||||
component: FlexJustifyContentSetter,
|
||||
},
|
||||
{
|
||||
name: 'flexAlignItemsSetter',
|
||||
component: FlexAlignItemsSetter,
|
||||
},
|
||||
{
|
||||
name: 'spacingSetter',
|
||||
component: SpacingSetter,
|
||||
},
|
||||
{
|
||||
name: 'colorSetter',
|
||||
component: ColorSetter,
|
||||
},
|
||||
{ name: 'bgSetter', component: BgSetter },
|
||||
{ name: 'borderSetter', component: BorderSetter },
|
||||
];
|
||||
|
||||
setters.forEach(register);
|
||||
+3
-4
@@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import { SingleMonacoEditor } from '@music163/tango-ui';
|
||||
import { Box } from 'coral-system';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { SingleMonacoEditor } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
|
||||
/**
|
||||
* 废弃掉,直接使用 ExpressionSetter 代替
|
||||
* @deprecated
|
||||
* JSON Setter
|
||||
*/
|
||||
export function JSONSetter({ value, onChange }: FormItemComponentProps) {
|
||||
return (
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { isPlainString } from '@music163/tango-helpers';
|
||||
import { ActionSelect } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
|
||||
const options = [
|
||||
{ label: '取消自定义', value: '' },
|
||||
+4
-5
@@ -1,10 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { css, Box } from 'coral-system';
|
||||
import { Form, Input, Button, Popover } from 'antd';
|
||||
import { DeleteOutlined, EditOutlined, HolderOutlined } from '@ant-design/icons';
|
||||
import { Box } from 'coral-system';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { css } from 'styled-components';
|
||||
import { DragBox } from '../components/index';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { DragBox } from '../components';
|
||||
|
||||
function copyWithoutUndefined(source: Record<string, any>) {
|
||||
const ret = {};
|
||||
@@ -254,7 +253,7 @@ function NewOptionForm({ fields = [], initialValues = {}, onSubmit }: NewOptionF
|
||||
);
|
||||
}
|
||||
|
||||
type ListSetterProps = FormItemComponentProps<any[]> & {
|
||||
interface ListSetterProps extends FormItemComponentProps<any[]> {
|
||||
listItemFormFields: ListSetterItemProps['formFields'];
|
||||
newItemDefaultValues?: AddListItemButtonProps['formInitialValues'];
|
||||
getNewItemDefaultValues?: AddListItemButtonProps['getFormInitialValues'];
|
||||
+13
-4
@@ -2,10 +2,11 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { Input, Tooltip } from 'antd';
|
||||
import { getValue, isFunction } from '@music163/tango-helpers';
|
||||
import { useFormVariable } from '../context';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { EditableVariableTreeModal } from '../components';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { MenuOutlined } from '@ant-design/icons';
|
||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||
import { EditableVariableTreeModal } from '../components';
|
||||
import { useSandboxQuery } from '../context';
|
||||
|
||||
function object2treeData(
|
||||
val: any,
|
||||
@@ -53,7 +54,15 @@ function traverseTreeData(val: any, callback: (val: any) => void) {
|
||||
|
||||
export function ModelSetter({ value, onChange }: FormItemComponentProps) {
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
const { onAction, modelVariables, evaluateContext = {} } = useFormVariable();
|
||||
const { modelVariables } = useWorkspaceData();
|
||||
const evaluateContext = useSandboxQuery().window || {};
|
||||
const workspace = useWorkspace();
|
||||
const onAction = useCallback(
|
||||
(action: string, args: unknown[]) => {
|
||||
workspace[action]?.(...args);
|
||||
},
|
||||
[workspace],
|
||||
);
|
||||
const definedVariables = useMemo(() => {
|
||||
const map = new Map();
|
||||
traverseTreeData(modelVariables, (node) => {
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { uuid } from '@music163/tango-helpers';
|
||||
import React from 'react';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { ListSetter, NewOptionFormFieldType } from './list-setter';
|
||||
|
||||
const optionFormFields: NewOptionFormFieldType[] = [
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Select } from 'antd';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { ListStore, OptionType } from '@music163/tango-helpers';
|
||||
|
||||
const style = {
|
||||
+5
-19
@@ -1,18 +1,18 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { ActionSelect } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
|
||||
type RenderOptionType = {
|
||||
interface IRenderOption {
|
||||
label: string;
|
||||
value: string;
|
||||
render?: string;
|
||||
relatedImports?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface RenderSetterProps {
|
||||
text?: string;
|
||||
options?: RenderOptionType[];
|
||||
fallbackOption?: RenderOptionType;
|
||||
options?: IRenderOption[];
|
||||
fallbackOption?: IRenderOption;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,16 +97,6 @@ const tableExpandableOptions: RenderSetterProps['options'] = [
|
||||
{ label: '取消可展开行', value: '' },
|
||||
];
|
||||
|
||||
const xtoolbarOptions: RenderSetterProps['options'] = [
|
||||
{
|
||||
label: '配置左侧区域',
|
||||
value: 'Box',
|
||||
render: getRender('<Box display="flex"></Box>'),
|
||||
relatedImports: ['Box'],
|
||||
},
|
||||
{ label: '取消左侧区域', value: undefined },
|
||||
];
|
||||
|
||||
export function TableCellSetter(props: FormItemComponentProps) {
|
||||
return <RenderSetter options={tableCellOptions} {...props} />;
|
||||
}
|
||||
@@ -114,7 +104,3 @@ export function TableCellSetter(props: FormItemComponentProps) {
|
||||
export function TableExpandableSetter(props: FormItemComponentProps) {
|
||||
return <RenderSetter options={tableExpandableOptions} text="配置表格可展开行" {...props} />;
|
||||
}
|
||||
|
||||
export function XToolbarSetter(props: FormItemComponentProps) {
|
||||
return <RenderSetter options={xtoolbarOptions} text="配置工具栏左侧区域" {...props} />;
|
||||
}
|
||||
+3
-3
@@ -2,8 +2,8 @@ import React, { useState } from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { Input, Radio } from 'antd';
|
||||
import { isValidUrl } from '@music163/tango-helpers';
|
||||
import { useFormVariable } from '../context';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { useWorkspaceData } from '@music163/tango-context';
|
||||
import { PickerSetter } from './picker-setter';
|
||||
|
||||
export function RouterSetter(props: FormItemComponentProps) {
|
||||
@@ -11,7 +11,7 @@ export function RouterSetter(props: FormItemComponentProps) {
|
||||
return isValidUrl(props.value) ? 'input' : 'select';
|
||||
});
|
||||
const [input, setInput] = useState<string>(props.value);
|
||||
const { routeOptions } = useFormVariable();
|
||||
const { routeOptions } = useWorkspaceData();
|
||||
return (
|
||||
<Box>
|
||||
<Box mb="m">
|
||||
+9
-10
@@ -5,9 +5,8 @@ import { SketchPicker } from 'react-color';
|
||||
import Color from 'color';
|
||||
import { BgColorsOutlined, FileImageOutlined } from '@ant-design/icons';
|
||||
import { SingleMonacoEditor, IconFont } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { ImageSetter } from './image-setter';
|
||||
import { ChoiceSetter } from './choice-setter';
|
||||
import { FormItemComponentProps, ChoiceSetter, TextSetter } from '@music163/tango-setting-form';
|
||||
// import { ImageSetter } from './image-setter';
|
||||
|
||||
function getRawCssValue(value: string) {
|
||||
if (value && value.startsWith('{css`')) {
|
||||
@@ -175,8 +174,8 @@ export function ColorSetter(props: FormItemComponentProps<string>) {
|
||||
const colorPanel = (
|
||||
<SketchPicker
|
||||
color={color}
|
||||
onChangeComplete={(color: any) => {
|
||||
const nextValue = color.rgb.a === 1 ? color.hex : rgbaObjectToString(color.rgb);
|
||||
onChangeComplete={(newColor: any) => {
|
||||
const nextValue = newColor.rgb.a === 1 ? newColor.hex : rgbaObjectToString(newColor.rgb);
|
||||
if (typeof onChange === 'function') {
|
||||
onChange(nextValue);
|
||||
}
|
||||
@@ -238,7 +237,7 @@ export function BgSetter({ value, onChange }: FormItemComponentProps<string>) {
|
||||
<Box mt="m">
|
||||
{mode === 'color' && <ColorSetter value={value} onChange={onChange} />}
|
||||
{mode === 'image' && (
|
||||
<ImageSetter
|
||||
<TextSetter
|
||||
value={getImageUrl(value)}
|
||||
onChange={(imgUrl) => {
|
||||
onChange(imgUrl ? `url(${imgUrl})` : undefined);
|
||||
@@ -353,19 +352,19 @@ export function BorderSetter({ value, onChange }: FormItemComponentProps<string>
|
||||
const [width, setWidth] = useState<number>(Number(getPxValue(valueParts[0])) || 0);
|
||||
const [color, setColor] = useState<string>(valueParts[2] || '#000');
|
||||
|
||||
const handleChange = (key: string, value: any) => {
|
||||
const handleChange = (key: string, val: any) => {
|
||||
let ret: any[];
|
||||
switch (key) {
|
||||
case 'style': {
|
||||
ret = [width, value, color];
|
||||
ret = [width, val, color];
|
||||
break;
|
||||
}
|
||||
case 'width': {
|
||||
ret = [`${value}px`, style, color];
|
||||
ret = [`${val}px`, style, color];
|
||||
break;
|
||||
}
|
||||
case 'color': {
|
||||
ret = [width, style, value];
|
||||
ret = [width, style, val];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { SettingForm, FormModel, SettingFormProps } from '@music163/tango-setting-form';
|
||||
import { Panel } from '@music163/tango-ui';
|
||||
import { clone } from '@music163/tango-helpers';
|
||||
import { observer, useDesigner, useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||
import { useSandboxQuery } from './context';
|
||||
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||
import './setters';
|
||||
|
||||
export interface SettingPanelProps extends SettingFormProps {
|
||||
title?: React.ReactNode;
|
||||
@@ -15,17 +15,8 @@ const headerProps = {
|
||||
};
|
||||
|
||||
export const SettingPanel = observer(({ title = '设置面板', ...props }: SettingPanelProps) => {
|
||||
const sandbox = useSandboxQuery();
|
||||
const workspace = useWorkspace();
|
||||
const designer = useDesigner();
|
||||
const { modelVariables, actionVariables, expressionVariables, routeOptions } = useWorkspaceData();
|
||||
|
||||
const onAction = useCallback(
|
||||
(action: string, args: unknown[]) => {
|
||||
workspace[action]?.(...args);
|
||||
},
|
||||
[workspace],
|
||||
);
|
||||
|
||||
if (!designer.showRightPanel) {
|
||||
return <Box className="SettingPanel" />;
|
||||
@@ -74,7 +65,10 @@ export const SettingPanel = observer(({ title = '设置面板', ...props }: Sett
|
||||
});
|
||||
} else if (!field.error) {
|
||||
// 针对 Form.Item 变化的情况
|
||||
workspace.updateSelectedNodeAttributes({ [firstName]: realValue }, field.detail?.relatedImports);
|
||||
workspace.updateSelectedNodeAttributes(
|
||||
{ [firstName]: realValue },
|
||||
field.detail?.relatedImports,
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -99,13 +93,6 @@ export const SettingPanel = observer(({ title = '设置面板', ...props }: Sett
|
||||
key={workspace.selectSource.first.id}
|
||||
model={formModel}
|
||||
prototype={prototype}
|
||||
modelVariables={modelVariables}
|
||||
actionVariables={actionVariables}
|
||||
expressionVariables={expressionVariables}
|
||||
modalOptions={workspace.listModals() || []}
|
||||
routeOptions={routeOptions}
|
||||
onAction={onAction}
|
||||
evaluateContext={sandbox.window}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { observer, useWorkspace } from '@music163/tango-context';
|
||||
import { Box, Link, css } from 'coral-system';
|
||||
import { Box, css } from 'coral-system';
|
||||
import { Button, FormProps, Empty, Space, Dropdown } from 'antd';
|
||||
import { PlayCircleOutlined } from '@ant-design/icons';
|
||||
import { Form, InputCode, Menu, Panel, JsonView, Search } from '@music163/tango-ui';
|
||||
@@ -12,14 +12,38 @@ import {
|
||||
logger,
|
||||
code2object,
|
||||
} from '@music163/tango-helpers';
|
||||
import {
|
||||
ESFDataSourceType,
|
||||
ESFOperationType,
|
||||
ESFHTTPMethodType,
|
||||
ESFHTTRequestType,
|
||||
} from '@music163/tango-core';
|
||||
import { useSandboxQuery } from '../../context';
|
||||
|
||||
/*
|
||||
* 服务函数的操作类型
|
||||
*/
|
||||
enum ServiceFunctionOperationModeType {
|
||||
ADD = 'add',
|
||||
UPDATE = 'update',
|
||||
DELETE = 'delete',
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务函数 HTTP Type
|
||||
* 云音乐网关只支持 get 和 post
|
||||
*/
|
||||
enum ServiceFunctionMethodType {
|
||||
GET = 'GET',
|
||||
// PUT = 'PUT',
|
||||
POST = 'POST',
|
||||
// PATCH = 'PATCH',
|
||||
// DELETE = 'DELETE',
|
||||
}
|
||||
|
||||
/**
|
||||
* requestType
|
||||
* headers: Content-Type
|
||||
*/
|
||||
enum ESFHTTRequestType {
|
||||
'application/json' = 'json',
|
||||
'application/x-www-form-urlencoded' = 'x-www-form-urlencoded',
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 api 路径转换为默认的驼峰方法名
|
||||
*/
|
||||
@@ -47,28 +71,8 @@ type DataServiceViewProps = {
|
||||
onDelete?: (values: Record<string, string>) => void;
|
||||
};
|
||||
|
||||
// 判断是不是一个完整的 url 请求
|
||||
const VALID_REQUEST_URL_REG = /((http|https):)?\/\/([\w.]+\/?)\S*/;
|
||||
|
||||
/**
|
||||
* 根据 url 类型推断方法类型
|
||||
*/
|
||||
export function inferServiceType(url?: string) {
|
||||
// 删除的时候,也会走一下这个逻辑,兼容
|
||||
if (!url) return ESFDataSourceType.Custom;
|
||||
// 如果是完整的,是 custom
|
||||
if (VALID_REQUEST_URL_REG.test(url)) return ESFDataSourceType.Custom;
|
||||
// 如果是 / 开头的,认为是 ox 的
|
||||
if (url.startsWith('/')) return ESFDataSourceType.OvermindX;
|
||||
/**
|
||||
* 这里预留出数据平台 fastX 的
|
||||
*/
|
||||
// 默认是 ox 的
|
||||
return ESFDataSourceType.OvermindX;
|
||||
}
|
||||
|
||||
// http 方法类型
|
||||
const httpMethods = Object.keys(ESFHTTPMethodType).map((key) => ({
|
||||
const httpMethods = Object.keys(ServiceFunctionMethodType).map((key) => ({
|
||||
label: key,
|
||||
value: key,
|
||||
}));
|
||||
@@ -113,13 +117,7 @@ const DataSourceView = observer(({ onAdd, onUpdate, onDelete }: DataServiceViewP
|
||||
const isAddMode = !serviceKey;
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="DataSourceView"
|
||||
display="flex"
|
||||
borderTopColor="line.normal"
|
||||
height="100%"
|
||||
overflow="hidden"
|
||||
>
|
||||
<Box className="ServiceFunctionList" display="flex" borderTopColor="line.normal">
|
||||
<Box width="35%" overflow="auto" borderRight="solid" borderColor="line.normal">
|
||||
<Panel
|
||||
height="100%"
|
||||
@@ -174,11 +172,10 @@ const DataSourceView = observer(({ onAdd, onUpdate, onDelete }: DataServiceViewP
|
||||
serviceKeys={Object.keys(serviceMap)}
|
||||
initialValues={
|
||||
isAddMode
|
||||
? { type: ESFDataSourceType.OvermindX }
|
||||
? {}
|
||||
: {
|
||||
name: serviceKey,
|
||||
method: 'get',
|
||||
type: inferServiceType(serviceData?.url),
|
||||
...serviceData,
|
||||
}
|
||||
}
|
||||
@@ -186,7 +183,7 @@ const DataSourceView = observer(({ onAdd, onUpdate, onDelete }: DataServiceViewP
|
||||
setServiceKey(undefined);
|
||||
setVisible(null);
|
||||
}}
|
||||
onSubmit={(values, mode: ESFOperationType) => {
|
||||
onSubmit={(values, mode: ServiceFunctionOperationModeType) => {
|
||||
function shapeServiceValues(val: any) {
|
||||
const shapeValues = { ...val };
|
||||
delete shapeValues.type;
|
||||
@@ -198,10 +195,10 @@ const DataSourceView = observer(({ onAdd, onUpdate, onDelete }: DataServiceViewP
|
||||
}
|
||||
// 移除掉不必要的属性
|
||||
const data = shapeServiceValues(values);
|
||||
if (mode === ESFOperationType.ADD) {
|
||||
if (mode === ServiceFunctionOperationModeType.ADD) {
|
||||
workspace.addServiceFunction(data);
|
||||
onAdd && onAdd(values);
|
||||
} else if (mode === ESFOperationType.UPDATE) {
|
||||
} else if (mode === ServiceFunctionOperationModeType.UPDATE) {
|
||||
workspace.updateServiceFunction(data);
|
||||
onUpdate && onUpdate(values);
|
||||
}
|
||||
@@ -231,7 +228,7 @@ const DataSourceView = observer(({ onAdd, onUpdate, onDelete }: DataServiceViewP
|
||||
interface ServiceDetailFormProps extends FormProps {
|
||||
serviceKeys?: string[];
|
||||
onCancel?: () => void;
|
||||
onSubmit?: (values: any, mode: ESFOperationType) => void;
|
||||
onSubmit?: (values: any, mode: ServiceFunctionOperationModeType) => void;
|
||||
}
|
||||
|
||||
function ServiceDetailForm({
|
||||
@@ -267,7 +264,12 @@ function ServiceDetailForm({
|
||||
layout="horizontal"
|
||||
initialValues={initialValues}
|
||||
onFinish={(values) => {
|
||||
onSubmit(values, isModifyMode ? ESFOperationType.UPDATE : ESFOperationType.ADD);
|
||||
onSubmit(
|
||||
values,
|
||||
isModifyMode
|
||||
? ServiceFunctionOperationModeType.UPDATE
|
||||
: ServiceFunctionOperationModeType.ADD,
|
||||
);
|
||||
setDisabled(true);
|
||||
}}
|
||||
{...formProps}
|
||||
@@ -345,18 +347,7 @@ function ServiceDetailForm({
|
||||
<Form.Item
|
||||
label="格式化响应"
|
||||
name="formatter"
|
||||
tooltip={
|
||||
<Box>
|
||||
提供格式化函数,对结果进行格式化。请参考
|
||||
<Link
|
||||
href="https://music-doc.st.netease.com/st/tango-docs/docs/guide/basic/services#%E6%8E%A5%E5%8F%A3%E8%BF%94%E5%9B%9E%E5%80%BC%E8%A7%84%E8%8C%83"
|
||||
target="_blank"
|
||||
>
|
||||
云音乐接口规范
|
||||
</Link>
|
||||
。
|
||||
</Box>
|
||||
}
|
||||
tooltip="提供格式化函数,对结果进行格式化"
|
||||
validateTrigger="onBlur"
|
||||
rules={[
|
||||
{
|
||||
|
||||
@@ -372,7 +372,7 @@ function RenderItem({
|
||||
</Popconfirm>,
|
||||
]
|
||||
: [
|
||||
<Text color={'rgba(0, 0, 0, 0.45)'}>
|
||||
<Text key="latest" color={'rgba(0, 0, 0, 0.45)'}>
|
||||
{!isUndefined(baseNeedUpgrade) && '已是最新'}
|
||||
</Text>,
|
||||
]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export * from './components-panel';
|
||||
export * from './datasource-panel';
|
||||
export * from './outline-panel';
|
||||
export * from './components-panel';
|
||||
export * from './dependency-panel';
|
||||
export * from './history-panel';
|
||||
export * from './variable-panel';
|
||||
export * from './sidebar';
|
||||
@@ -4,7 +4,7 @@ import { Box, css } from 'coral-system';
|
||||
import { IconFont } from '@music163/tango-ui';
|
||||
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
|
||||
import { observer, useWorkspace } from '@music163/tango-context';
|
||||
import { DropMethod, TangoViewNodeDataType } from '@music163/tango-core';
|
||||
import { DropMethod, ITangoViewNodeData } from '@music163/tango-core';
|
||||
import { parseDndId } from '@music163/tango-helpers';
|
||||
import { useSandboxQuery } from '../../context';
|
||||
import { buildQueryBySlotId } from '../../helpers';
|
||||
@@ -40,7 +40,18 @@ const filedNames = {
|
||||
children: 'children',
|
||||
};
|
||||
|
||||
const OutlineTreeNode: React.FC<any> = observer(({ node }: { node: TangoViewNodeDataType }) => {
|
||||
const getNodeKeys = (data: ITangoViewNodeData[]) => {
|
||||
const ids: string[] = [];
|
||||
data?.forEach((node) => {
|
||||
ids.push(node.id);
|
||||
if (node.children) {
|
||||
ids.push(...getNodeKeys(node.children));
|
||||
}
|
||||
});
|
||||
return ids;
|
||||
};
|
||||
|
||||
const OutlineTreeNode: React.FC<any> = observer(({ node }: { node: ITangoViewNodeData }) => {
|
||||
const workspace = useWorkspace();
|
||||
const sandboxQuery = useSandboxQuery();
|
||||
const [visible, setVisible] = useState(true);
|
||||
@@ -87,7 +98,7 @@ export const ComponentsTree = observer(() => {
|
||||
workspace.selectSource.selected.map((item) => item.id),
|
||||
);
|
||||
const file = workspace.activeViewModule;
|
||||
const nodesTree = file.nodesTree as TangoViewNodeDataType[];
|
||||
const nodesTree = file.nodesTree as ITangoViewNodeData[];
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedKeys(workspace.selectSource.selected.map((item) => item.id));
|
||||
@@ -124,7 +135,7 @@ export const ComponentsTree = observer(() => {
|
||||
}}
|
||||
blockNode
|
||||
autoExpandParent
|
||||
defaultExpandAll
|
||||
expandedKeys={getNodeKeys(nodesTree)}
|
||||
draggable
|
||||
onDragStart={(data) => {
|
||||
const prototype = workspace.componentPrototypes.get(data.node.component);
|
||||
|
||||
@@ -51,6 +51,7 @@ export interface SidebarProps extends ReactComponentProps {
|
||||
* 底部附加内容
|
||||
*/
|
||||
footer?: React.ReactNode;
|
||||
builtinPanelMap?: Record<string, SidebarPanelItemProps>;
|
||||
}
|
||||
|
||||
export interface SidebarPanelItemProps
|
||||
@@ -114,13 +115,18 @@ const INTERNAL_SIDEBAR_PANEL_MAP: Record<string, SidebarPanelItemProps> = {
|
||||
},
|
||||
};
|
||||
|
||||
function BaseSidebarPanel({ panelWidth: defaultPanelWidth = 280, footer, children }: SidebarProps) {
|
||||
function BaseSidebarPanel({
|
||||
panelWidth: defaultPanelWidth = 280,
|
||||
builtinPanelMap = INTERNAL_SIDEBAR_PANEL_MAP,
|
||||
footer,
|
||||
children,
|
||||
}: SidebarProps) {
|
||||
const items = useMemo(() => {
|
||||
const ret: Record<React.Key, SidebarPanelItemProps> = {};
|
||||
React.Children.forEach(children, (child) => {
|
||||
if (child && React.isValidElement(child)) {
|
||||
ret[child.key] = {
|
||||
...INTERNAL_SIDEBAR_PANEL_MAP[child.key],
|
||||
...builtinPanelMap[child.key],
|
||||
...child.props,
|
||||
};
|
||||
}
|
||||
@@ -272,11 +278,10 @@ function SidebarPanelBarItem({
|
||||
const expandPanelStyle = css`
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--tango-colors-white);
|
||||
border-left: 1px solid var(--tango-colors-line-normal);
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
|
||||
&.isFloat {
|
||||
position: absolute;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Button, Space } from 'antd';
|
||||
import { EditableVariableTree, EditableVariableTreeProps } from '@music163/tango-setting-form';
|
||||
import { useBoolean } from '@music163/tango-helpers';
|
||||
import { Panel, Form, IconButton } from '@music163/tango-ui';
|
||||
import { FileAddOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { observer, useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||
import { EditableVariableTree, EditableVariableTreeProps } from '../components';
|
||||
|
||||
export interface VariablePanelProps extends EditableVariableTreeProps {
|
||||
wrapperHeight?: number | string;
|
||||
|
||||
@@ -15,7 +15,7 @@ type SelectionHelperAlignType = 'top-right' | 'top-left' | 'inner-top-right';
|
||||
|
||||
const boundingOffset = 100; // 检测选中模块的工具栏是否溢出的偏移值
|
||||
|
||||
const internalSelectionTools = {
|
||||
const internalSelectionToolsMap = {
|
||||
source: <ViewSourceAction />,
|
||||
copy: <CopyNodeAction />,
|
||||
delete: <DeleteNodeAction />,
|
||||
@@ -28,16 +28,20 @@ export interface SelectionToolsProps {
|
||||
* 动作列表,内置的动作列表有 source-定位到源码, block-创建区块, copy-复制节点, delete-删除节点
|
||||
*/
|
||||
actions?: Array<string | React.ReactElement>;
|
||||
builtinActionMap?: Record<string, React.ReactElement>;
|
||||
}
|
||||
|
||||
export const SelectionTools = observer(
|
||||
({ actions: actionsProp = INTERNAL_SELECTION_TOOLS }: SelectionToolsProps) => {
|
||||
({
|
||||
actions: actionsProp = INTERNAL_SELECTION_TOOLS,
|
||||
builtinActionMap = internalSelectionToolsMap,
|
||||
}: SelectionToolsProps) => {
|
||||
const workspace = useWorkspace();
|
||||
const selectSource = workspace.selectSource;
|
||||
const actions = actionsProp.map((item) => {
|
||||
if (isString(item)) {
|
||||
return internalSelectionTools[item]
|
||||
? React.cloneElement(internalSelectionTools[item], { key: item })
|
||||
return builtinActionMap[item]
|
||||
? React.cloneElement(builtinActionMap[item], { key: item })
|
||||
: null;
|
||||
}
|
||||
return item;
|
||||
|
||||
@@ -10,14 +10,21 @@ import { DraggingMask } from './mask';
|
||||
|
||||
export interface ViewportProps extends HTMLCoralProps<'div'> {
|
||||
selectionTools?: SelectionToolsProps['actions'];
|
||||
builtinActionMap?: SelectionToolsProps['builtinActionMap'];
|
||||
}
|
||||
|
||||
export function Viewport({ selectionTools, children, className, ...rest }: ViewportProps) {
|
||||
export function Viewport({
|
||||
selectionTools,
|
||||
builtinActionMap,
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
}: ViewportProps) {
|
||||
return (
|
||||
<Box position="relative" height="100%" className={cx('Viewport', className)} {...rest}>
|
||||
<ViewportBody>{children}</ViewportBody>
|
||||
<div className="AuxTools">
|
||||
<SelectionTools actions={selectionTools} />
|
||||
<SelectionTools actions={selectionTools} builtinActionMap={builtinActionMap} />
|
||||
<InsertionPrompt />
|
||||
<DraggingMask />
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Button, Popconfirm, Modal, ModalProps } from 'antd';
|
||||
import { Box, css, Group, Text } from 'coral-system';
|
||||
import { FileOutlined, DeleteOutlined, CopyOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
import { array2object, callAll } from '@music163/tango-helpers';
|
||||
import { PageConfigType } from '@music163/tango-core';
|
||||
import { IPageConfigData } from '@music163/tango-core';
|
||||
import { Form, ToggleButton } from '@music163/tango-ui';
|
||||
import { observer, useWorkspace } from '@music163/tango-context';
|
||||
|
||||
@@ -108,12 +108,12 @@ const pageListStyle = css`
|
||||
|
||||
interface PageListProps {
|
||||
activeKey?: string;
|
||||
items?: PageConfigType[];
|
||||
items?: IPageConfigData[];
|
||||
onAction?: (key: 'delete' | 'copy' | 'update', e: React.MouseEvent) => void;
|
||||
onSelect?: (key: string) => void;
|
||||
onRemove?: (key: string) => void;
|
||||
onCopy?: (sourcePath: string, newPageData: PageConfigType) => void;
|
||||
onUpdate?: (sourcePath: string, newPageData: PageConfigType) => void;
|
||||
onCopy?: (sourcePath: string, newPageData: IPageConfigData) => void;
|
||||
onUpdate?: (sourcePath: string, newPageData: IPageConfigData) => void;
|
||||
}
|
||||
|
||||
// 表单编辑模式
|
||||
@@ -128,7 +128,7 @@ function PageList({
|
||||
onCopy,
|
||||
onAction,
|
||||
}: PageListProps) {
|
||||
const [current, setCurrent] = useState<PageConfigType>(null);
|
||||
const [current, setCurrent] = useState<IPageConfigData>(null);
|
||||
const [type, setType] = useState<FormEditType>();
|
||||
return (
|
||||
<Box as="ul" bg="white" boxShadow="lowDown" borderRadius="m" css={pageListStyle}>
|
||||
@@ -219,10 +219,10 @@ function PageList({
|
||||
interface CopyViewModalFormProps {
|
||||
title?: string;
|
||||
visible?: boolean;
|
||||
onOk?: (sourceRoute: string, targetRouteConfig: PageConfigType) => void;
|
||||
onOk?: (sourceRoute: string, targetRouteConfig: IPageConfigData) => void;
|
||||
onCancel?: ModalProps['onCancel'];
|
||||
current?: PageConfigType;
|
||||
items?: PageConfigType[];
|
||||
current?: IPageConfigData;
|
||||
items?: IPageConfigData[];
|
||||
editType?: FormEditType;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@ import { PreviewTool } from './preview';
|
||||
import { RouteSwitchTool } from './route-switch';
|
||||
import { TogglePanelTool } from './toggle-panel';
|
||||
|
||||
export interface ToolbarProps {
|
||||
children?: React.ReactElement | React.ReactElement[];
|
||||
builtinToolMap?: Record<string, React.ReactNode>;
|
||||
}
|
||||
|
||||
const builtinToolsMap = {
|
||||
history: <HistoryTool />,
|
||||
modeSwitch: <ModeSwitchTool />,
|
||||
@@ -15,11 +20,7 @@ const builtinToolsMap = {
|
||||
togglePanel: <TogglePanelTool />,
|
||||
};
|
||||
|
||||
interface ToolbarProps {
|
||||
children?: React.ReactElement | React.ReactElement[];
|
||||
}
|
||||
|
||||
export function Toolbar({ children }: ToolbarProps) {
|
||||
export function Toolbar({ children, builtinToolMap = builtinToolsMap }: ToolbarProps) {
|
||||
const [leftTools, centerTools, rightTools] = useMemo(() => {
|
||||
const left: React.ReactNode[] = [];
|
||||
const center: React.ReactNode[] = [];
|
||||
@@ -27,7 +28,7 @@ export function Toolbar({ children }: ToolbarProps) {
|
||||
|
||||
let prevPlacement: string;
|
||||
React.Children.forEach(children, (child: React.ReactElement, index) => {
|
||||
let node = child.props.children || builtinToolsMap[child.key];
|
||||
let node = child.props.children || builtinToolMap[child.key];
|
||||
if (!node) {
|
||||
node = child; // separator
|
||||
}
|
||||
@@ -71,7 +72,7 @@ export function Toolbar({ children }: ToolbarProps) {
|
||||
);
|
||||
}
|
||||
|
||||
interface ToolbarItemProps extends ReactComponentProps {
|
||||
export interface ToolbarItemProps extends ReactComponentProps {
|
||||
placement?: 'left' | 'center' | 'right';
|
||||
children?: React.ReactElement;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Box } from 'coral-system';
|
||||
import { observer, useDesigner } from '@music163/tango-context';
|
||||
import { DesignerViewType } from '@music163/tango-core';
|
||||
|
||||
export interface ViewPanelProps {
|
||||
export interface WorkspaceViewProps {
|
||||
/**
|
||||
* 视图面板模式,对应 Workspace 的模式
|
||||
*/
|
||||
@@ -11,7 +11,7 @@ export interface ViewPanelProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const ViewPanel = observer((props: ViewPanelProps) => {
|
||||
export const WorkspaceView = observer((props: WorkspaceViewProps) => {
|
||||
const { mode = 'design', children } = props;
|
||||
const designer = useDesigner();
|
||||
const display = mode !== designer.activeView ? 'none' : 'block';
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.1.3](https://github.com/netease/tango/compare/@music163/tango-sandbox@0.1.2...@music163/tango-sandbox@0.1.3) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-sandbox
|
||||
|
||||
## 0.1.2 (2023-09-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-sandbox",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "sandbox of tango apps",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"homepage": "",
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/tango-core": "^0.1.2",
|
||||
"@music163/tango-core": "^0.1.3",
|
||||
"@music163/tango-helpers": "^0.1.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"lodash.isequal": "4.5.0",
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.2.0](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.1.3...@music163/tango-setting-form@0.2.0) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- refactor setting formItem ([b786de2](https://github.com/netease/tango/commit/b786de2f1a0e4e9141eb09fce696e45df633b232))
|
||||
- remove some useless setters ([7e9ab12](https://github.com/netease/tango/commit/7e9ab12503ed33c1e6acb8a1fa5fd89fc82d35fd))
|
||||
|
||||
### Features
|
||||
|
||||
- refactor setting-form ([8d396ff](https://github.com/netease/tango/commit/8d396ff13459beeb57b6b3c48f7e8fe1765041ae))
|
||||
|
||||
## [0.1.3](https://github.com/netease/tango/compare/@music163/tango-setting-form@0.1.2...@music163/tango-setting-form@0.1.3) (2023-09-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fix lint errors ([f9e6537](https://github.com/netease/tango/commit/f9e6537dd6569f4196b2799d51dbaab9838fd7f2))
|
||||
|
||||
## 0.1.2 (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-setting-form
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-setting-form",
|
||||
"version": "0.1.2",
|
||||
"version": "0.2.0",
|
||||
"description": "setting form of tango-apps",
|
||||
"author": "wwsun <ww.sun@outlook.com>",
|
||||
"homepage": "",
|
||||
@@ -28,21 +28,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@music163/tango-core": "^0.1.2",
|
||||
"@music163/tango-core": "^0.1.3",
|
||||
"@music163/tango-helpers": "^0.1.2",
|
||||
"@music163/tango-ui": "^0.1.2",
|
||||
"@music163/tango-ui": "^0.1.3",
|
||||
"antd": "^4.24.2",
|
||||
"color": "^4.2.3",
|
||||
"coral-system": "^1.0.5",
|
||||
"cssjson": "^2.1.3",
|
||||
"mobx": "6.9.0",
|
||||
"mobx-react-lite": "4.0.2",
|
||||
"moment": "^2.29.3",
|
||||
"react-color": "^2.19.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/react-color": "^3.0.6"
|
||||
"mobx-react-lite": "4.0.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
interface FormatProps {
|
||||
format: 'simple' | 'original';
|
||||
data?: Record<string, string> | Array<Record<string, string>>;
|
||||
}
|
||||
|
||||
const formatData = (data?: Array<Record<string, string>>) => {
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
// 遍历数据
|
||||
data?.forEach((item) => {
|
||||
item.key && !(item.key in result) && (result[item.key] = item.value);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// 格式化value
|
||||
export const formatValue = ({ format, data }: FormatProps) => {
|
||||
if (format === 'original') {
|
||||
if (!data) return [{ index: 0 }];
|
||||
|
||||
const list = Array.isArray(data) ? data : Object.keys(data).map((key) => ({ key, value: data[key] }));
|
||||
|
||||
return list.map((item, idx) => ({ ...item, index: idx - list.length - 1 }));
|
||||
} else {
|
||||
if (!data) return {};
|
||||
|
||||
return Array.isArray(data) ? formatData(data) : data;
|
||||
}
|
||||
};
|
||||
|
||||
export const compareValue = (origin: Array<any>, data: Array<any>) => {
|
||||
const resolve = (list: Array<any>) => {
|
||||
let keyStr = '';
|
||||
let valueStr = '';
|
||||
|
||||
list.forEach((item) => {
|
||||
const { key, value } = item;
|
||||
|
||||
keyStr += key || '$';
|
||||
valueStr += value || '$';
|
||||
});
|
||||
|
||||
return { keyStr, valueStr };
|
||||
};
|
||||
|
||||
const oldRes = resolve(origin);
|
||||
const newRes = resolve(data);
|
||||
|
||||
return oldRes.keyStr === newRes.keyStr && oldRes.valueStr === newRes.valueStr;
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
import { TangoRemoteServicesType } from '@music163/tango-helpers';
|
||||
import { createContext, useContext } from 'react';
|
||||
import { IFormModel } from './form-model';
|
||||
|
||||
@@ -11,54 +10,48 @@ export const useFormModel = () => {
|
||||
return useContext(FormModelContext);
|
||||
};
|
||||
|
||||
export type VariableNode = {
|
||||
label?: string;
|
||||
value?: string;
|
||||
type?: string;
|
||||
children?: VariableNode[];
|
||||
};
|
||||
|
||||
// TODO: 删掉不需要的
|
||||
export interface FormVariableContextType {
|
||||
/**
|
||||
* 是否允许表单项切换到表达式设置器
|
||||
*/
|
||||
disableSwitchExpressionSetter?: boolean;
|
||||
/**
|
||||
* 探测输入上下文的对象
|
||||
*/
|
||||
evaluateContext?: object;
|
||||
/**
|
||||
* 表单动作发生时的回调
|
||||
*/
|
||||
onAction?: (fn: string, args: unknown[]) => void;
|
||||
/**
|
||||
* 状态变量列表
|
||||
*/
|
||||
modelVariables?: any[];
|
||||
/**
|
||||
* 动作变量列表
|
||||
*/
|
||||
actionVariables?: any[];
|
||||
/**
|
||||
* 表达式变量列表(含状态变量和动作变量)
|
||||
*/
|
||||
expressionVariables?: any[];
|
||||
/**
|
||||
* 路由选项
|
||||
*/
|
||||
routeOptions?: any[];
|
||||
/**
|
||||
* 可选弹窗列表
|
||||
*/
|
||||
modalOptions?: any[];
|
||||
/**
|
||||
* 关联的表单域列表
|
||||
*/
|
||||
formFieldsOptions?: any[];
|
||||
/**
|
||||
* 远程服务
|
||||
*/
|
||||
remoteServices?: TangoRemoteServicesType;
|
||||
// /**
|
||||
// * 探测输入上下文的对象
|
||||
// */
|
||||
// evaluateContext?: object;
|
||||
// /**
|
||||
// * 表单动作发生时的回调
|
||||
// */
|
||||
// onAction?: (fn: string, args: unknown[]) => void;
|
||||
// /**
|
||||
// * 状态变量列表
|
||||
// */
|
||||
// modelVariables?: any[];
|
||||
// /**
|
||||
// * 动作变量列表
|
||||
// */
|
||||
// actionVariables?: any[];
|
||||
// /**
|
||||
// * 表达式变量列表(含状态变量和动作变量)
|
||||
// */
|
||||
// expressionVariables?: any[];
|
||||
// /**
|
||||
// * 路由选项
|
||||
// */
|
||||
// routeOptions?: any[];
|
||||
// /**
|
||||
// * 可选弹窗列表
|
||||
// */
|
||||
// modalOptions?: any[];
|
||||
// /**
|
||||
// * 关联的表单域列表
|
||||
// */
|
||||
// formFieldsOptions?: any[];
|
||||
// /**
|
||||
// * 远程服务
|
||||
// */
|
||||
// remoteServices?: TangoRemoteServicesType;
|
||||
}
|
||||
|
||||
const FormVariableContext = createContext<FormVariableContextType>(null);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
clone,
|
||||
ComponentPropType,
|
||||
isVariableString,
|
||||
logger,
|
||||
SetterOnChangeDetailType,
|
||||
useBoolean,
|
||||
} from '@music163/tango-helpers';
|
||||
@@ -12,9 +13,8 @@ import { IconFont, ToggleButton } from '@music163/tango-ui';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { InputProps, Tooltip } from 'antd';
|
||||
import { INTERNAL_SETTERS } from './setters';
|
||||
import { ExpressionSetter } from './setters/expression-setter';
|
||||
import { useFormModel, useFormVariable } from './context';
|
||||
import { FormControl, FormControlProps } from './form-ui';
|
||||
import { FormControl } from './form-ui';
|
||||
|
||||
export interface FormItemProps extends ComponentPropType {
|
||||
extra?: React.ReactNode;
|
||||
@@ -29,15 +29,15 @@ export interface FormItemComponentProps<T = any> {
|
||||
[prop: string]: any;
|
||||
}
|
||||
|
||||
export interface FormItemCreateOptionsType {
|
||||
export interface IFormItemCreateOptions {
|
||||
/**
|
||||
* 设置器调用名
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 别名
|
||||
* 别名列表
|
||||
*/
|
||||
alias?: string;
|
||||
alias?: string[];
|
||||
/**
|
||||
* 渲染的组件
|
||||
*/
|
||||
@@ -50,10 +50,6 @@ export interface FormItemCreateOptionsType {
|
||||
* 是否禁用变量设置器
|
||||
*/
|
||||
disableVariableSetter?: boolean;
|
||||
/**
|
||||
* 表单项类型
|
||||
*/
|
||||
type?: FormControlProps['type'];
|
||||
/**
|
||||
* 子表单是否默认折叠
|
||||
*/
|
||||
@@ -64,25 +60,12 @@ export interface FormItemCreateOptionsType {
|
||||
validate?: (value: string) => string | Promise<any>;
|
||||
}
|
||||
|
||||
function normalizeCreateOptions(
|
||||
options: FormItemCreateOptionsType,
|
||||
): Required<Omit<FormItemCreateOptionsType, 'component' | 'alias'>> {
|
||||
const render = options.render ?? ((props: any) => React.createElement(options.component, props));
|
||||
return {
|
||||
name: options.name,
|
||||
render,
|
||||
disableVariableSetter: options.disableVariableSetter ?? false,
|
||||
type: options.type || 'formItem',
|
||||
defaultCollapsed: options.defaultCollapsed ?? true,
|
||||
validate: options.validate,
|
||||
};
|
||||
}
|
||||
|
||||
const defaultGetSetterProps = () => ({});
|
||||
const defaultGetVisible = () => true;
|
||||
|
||||
export function createFormItem(options: FormItemCreateOptionsType) {
|
||||
const _options = normalizeCreateOptions(options);
|
||||
export function createFormItem(options: IFormItemCreateOptions) {
|
||||
const renderSetter =
|
||||
options.render ?? ((props: any) => React.createElement(options.component, props));
|
||||
|
||||
function FormItem({
|
||||
name,
|
||||
@@ -95,7 +78,7 @@ export function createFormItem(options: FormItemCreateOptionsType) {
|
||||
setterProps,
|
||||
defaultValue,
|
||||
options: setterOptions,
|
||||
disableVariableSetter: disableVariableSetterProp,
|
||||
disableVariableSetter: disableVariableSetterProp = options.disableVariableSetter,
|
||||
getVisible: getVisibleProp,
|
||||
getSetterProps: getSetterPropsProp,
|
||||
extra,
|
||||
@@ -104,16 +87,15 @@ export function createFormItem(options: FormItemCreateOptionsType) {
|
||||
const model = useFormModel();
|
||||
const field = model.getField(name);
|
||||
const value = toJS(field.value ?? defaultValue);
|
||||
const disableVariableSetter =
|
||||
disableSwitchExpressionSetter || disableVariableSetterProp || _options.disableVariableSetter;
|
||||
const disableVariableSetter = disableSwitchExpressionSetter ?? disableVariableSetterProp;
|
||||
const [isVariable, { toggle: toggleIsVariable }] = useBoolean(
|
||||
() => !disableVariableSetter && isVariableString(value),
|
||||
);
|
||||
|
||||
const setterName = isVariable ? 'expressionSetter' : setter;
|
||||
const validate = SETTERS_VALIDATE_DICT[setterName];
|
||||
|
||||
field.setConfig({
|
||||
validate,
|
||||
validate: options.validate,
|
||||
});
|
||||
|
||||
const baseComponentProps = clone(
|
||||
@@ -129,6 +111,8 @@ export function createFormItem(options: FormItemCreateOptionsType) {
|
||||
) as FormItemComponentProps;
|
||||
|
||||
let expProps = {};
|
||||
|
||||
// FIXME: 重新考虑这段代码的位置
|
||||
if (['expressionSetter', 'actionSetter', 'eventSetter'].includes(setter) || isVariable) {
|
||||
expProps = {
|
||||
modalTitle: title,
|
||||
@@ -138,11 +122,13 @@ export function createFormItem(options: FormItemCreateOptionsType) {
|
||||
}
|
||||
|
||||
const getSetterProps = getSetterPropsProp || defaultGetSetterProps;
|
||||
// 从注册表中获取 expSetter
|
||||
const ExpressionSetter = REGISTERED_FORM_ITEM_MAP['expressionSetter']?.config?.component;
|
||||
|
||||
const setterNode = isVariable ? (
|
||||
<ExpressionSetter {...expProps} {...baseComponentProps} />
|
||||
) : (
|
||||
_options.render({
|
||||
renderSetter({
|
||||
...expProps,
|
||||
...baseComponentProps,
|
||||
...setterProps, // setterProps 优先级大于快捷属性
|
||||
@@ -155,8 +141,7 @@ export function createFormItem(options: FormItemCreateOptionsType) {
|
||||
return (
|
||||
<FormControl
|
||||
visible={getVisible(model)}
|
||||
type={_options.type}
|
||||
defaultCollapsed={_options.defaultCollapsed}
|
||||
defaultCollapsed={options.defaultCollapsed}
|
||||
label={title}
|
||||
note={name}
|
||||
tip={tip}
|
||||
@@ -189,28 +174,27 @@ export function createFormItem(options: FormItemCreateOptionsType) {
|
||||
);
|
||||
}
|
||||
|
||||
FormItem.displayName = `FormItem_${_options.name}`;
|
||||
FormItem.displayName = `FormItem_${options.name}`;
|
||||
FormItem.config = options;
|
||||
|
||||
return observer(FormItem);
|
||||
}
|
||||
|
||||
// setter 查找表
|
||||
const SETTERS_DICT: Record<string, React.FunctionComponent<any>> = {};
|
||||
// setter 校验函数查找表
|
||||
const SETTERS_VALIDATE_DICT: Record<string, FormItemCreateOptionsType['validate']> = {};
|
||||
// 已注册的 setter 查找表
|
||||
const REGISTERED_FORM_ITEM_MAP: Record<string, ReturnType<typeof createFormItem>> = {};
|
||||
|
||||
INTERNAL_SETTERS.forEach((item) => {
|
||||
SETTERS_DICT[item.name] = createFormItem(item);
|
||||
if (item.alias) {
|
||||
SETTERS_DICT[item.alias] = SETTERS_DICT[item.name];
|
||||
}
|
||||
if (item.validate) {
|
||||
SETTERS_VALIDATE_DICT[item.name] = item.validate;
|
||||
if (item.alias) {
|
||||
SETTERS_VALIDATE_DICT[item.alias] = SETTERS_VALIDATE_DICT[item.name];
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Setter 注册
|
||||
* @param config 注册选项
|
||||
*/
|
||||
export function register(config: IFormItemCreateOptions) {
|
||||
const names = [config.name, ...(config.alias ?? [])];
|
||||
names.forEach((name) => {
|
||||
REGISTERED_FORM_ITEM_MAP[name] = createFormItem(config);
|
||||
});
|
||||
}
|
||||
|
||||
INTERNAL_SETTERS.forEach(register);
|
||||
|
||||
const iconStyle = {
|
||||
color: 'red',
|
||||
@@ -218,9 +202,9 @@ const iconStyle = {
|
||||
|
||||
export function SettingFormItem(props: FormItemProps) {
|
||||
const { setter } = props;
|
||||
const Comp = SETTERS_DICT[setter];
|
||||
const Comp = REGISTERED_FORM_ITEM_MAP[setter];
|
||||
if (Comp == null) {
|
||||
const Fallback = SETTERS_DICT.expressionSetter;
|
||||
const Fallback = REGISTERED_FORM_ITEM_MAP.expressionSetter;
|
||||
return (
|
||||
<Fallback
|
||||
{...props}
|
||||
@@ -234,14 +218,3 @@ export function SettingFormItem(props: FormItemProps) {
|
||||
}
|
||||
return React.createElement(Comp, props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter 注册
|
||||
* @param options
|
||||
*/
|
||||
export function register(options: FormItemCreateOptionsType) {
|
||||
if (SETTERS_DICT[options.name]) {
|
||||
throw Error(`Duplicate setter name: <${options.name}>`);
|
||||
}
|
||||
SETTERS_DICT[options.name] = createFormItem(options);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '@music163/tango-helpers';
|
||||
import { Search, Tabs } from '@music163/tango-ui';
|
||||
import { SettingFormItem, register } from './form-item';
|
||||
import { FormModelProvider, FormVariableProvider, FormVariableContextType } from './context';
|
||||
import { FormModelProvider, FormVariableProvider } from './context';
|
||||
import { FormModel, FormModelOptionsType } from './form-model';
|
||||
import { SettingFormObject } from './form-object';
|
||||
import { isValidNestProps } from './helpers';
|
||||
@@ -53,19 +53,19 @@ const formStyle = css`
|
||||
}
|
||||
`;
|
||||
|
||||
type GroupOptionType = {
|
||||
interface IFormTabsGroupOption {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
const internalGroups: GroupOptionType[] = [
|
||||
const internalGroups: IFormTabsGroupOption[] = [
|
||||
{ label: '常用', value: 'basic' },
|
||||
{ label: '事件', value: 'event' },
|
||||
{ label: '样式', value: 'style' },
|
||||
{ label: '高级', value: 'advanced' },
|
||||
];
|
||||
|
||||
export interface SettingFormProps extends FormVariableContextType {
|
||||
export interface SettingFormProps {
|
||||
/**
|
||||
* 表单状态管理模型实例
|
||||
*/
|
||||
@@ -85,24 +85,19 @@ export interface SettingFormProps extends FormVariableContextType {
|
||||
/**
|
||||
* 选项分组信息
|
||||
*/
|
||||
groupOptions?: GroupOptionType[];
|
||||
groupOptions?: IFormTabsGroupOption[];
|
||||
/**
|
||||
* 是否允许表单项切换到表达式设置器
|
||||
*/
|
||||
disableSwitchExpressionSetter?: boolean;
|
||||
}
|
||||
|
||||
export function SettingForm({
|
||||
prototype,
|
||||
disableSwitchExpressionSetter,
|
||||
modelVariables = [],
|
||||
actionVariables = [],
|
||||
expressionVariables = [],
|
||||
routeOptions = [],
|
||||
modalOptions = [],
|
||||
formFieldsOptions = [],
|
||||
evaluateContext,
|
||||
model: modelProp,
|
||||
defaultValue,
|
||||
onChange = noop,
|
||||
onAction = noop,
|
||||
remoteServices,
|
||||
groupOptions = internalGroups,
|
||||
}: SettingFormProps) {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
@@ -155,21 +150,8 @@ export function SettingForm({
|
||||
}
|
||||
}, [modelProp]);
|
||||
|
||||
const formVariable = {
|
||||
disableSwitchExpressionSetter,
|
||||
onAction,
|
||||
modelVariables,
|
||||
actionVariables,
|
||||
expressionVariables,
|
||||
routeOptions,
|
||||
modalOptions,
|
||||
formFieldsOptions,
|
||||
evaluateContext,
|
||||
remoteServices,
|
||||
};
|
||||
|
||||
return (
|
||||
<FormVariableProvider value={formVariable}>
|
||||
<FormVariableProvider value={{ disableSwitchExpressionSetter }}>
|
||||
<FormModelProvider value={model}>
|
||||
<Box className="SettingForm" mb="xl" css={formStyle}>
|
||||
<Box className="SettingFormHeader" position="sticky" top="0" bg="white" zIndex="2">
|
||||
|
||||
@@ -3,6 +3,4 @@ export * from './form-item';
|
||||
export * from './form-object';
|
||||
export * from './form-model';
|
||||
export * from './context';
|
||||
|
||||
export { EditableVariableTree } from './components';
|
||||
export type { EditableVariableTreeProps, VariableTreeNodeType } from './components';
|
||||
export * from './setters';
|
||||
@@ -9,14 +9,6 @@ interface ChoiceSetterProps {
|
||||
options?: OptionType[];
|
||||
}
|
||||
|
||||
function RadioButton({ tip, ...rest }: RadioProps & { tip?: string }) {
|
||||
let ret = <Radio.Button {...rest} />;
|
||||
if (tip) {
|
||||
ret = <Tooltip title={tip}>{ret}</Tooltip>;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function ChoiceSetter({
|
||||
options,
|
||||
mode = 'text',
|
||||
@@ -45,3 +37,11 @@ export function ChoiceSetter({
|
||||
</Radio.Group>
|
||||
);
|
||||
}
|
||||
|
||||
function RadioButton({ tip, ...rest }: RadioProps & { tip?: string }) {
|
||||
let ret = <Radio.Button {...rest} />;
|
||||
if (tip) {
|
||||
ret = <Tooltip title={tip}>{ret}</Tooltip>;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { SingleMonacoEditor } from '@music163/tango-ui';
|
||||
import { Box } from 'coral-system';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form/src/form-item';
|
||||
|
||||
export function ExpressionSetter({ value, onChange }: FormItemComponentProps) {
|
||||
return (
|
||||
<Box height="120px" border="solid" borderColor="line.normal" borderRadius="s">
|
||||
<SingleMonacoEditor
|
||||
defaultValue={value}
|
||||
onBlur={(newValue) => {
|
||||
if (newValue !== value) {
|
||||
onChange(`{${newValue}}`);
|
||||
}
|
||||
}}
|
||||
language="javascript"
|
||||
options={{
|
||||
lineNumbers: 'off',
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import React from 'react';
|
||||
import { css } from 'styled-components';
|
||||
import { Box } from 'coral-system';
|
||||
import { Select } from 'antd';
|
||||
// import { IconPro } from '@ant-design/icons';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
|
||||
const acStyle = css`
|
||||
.ant-select {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const ALL_ICON_NAMES: string[] = [];
|
||||
|
||||
/**
|
||||
* Icon 组件设置器
|
||||
* onChange 返回 `{<Icon type="iconName" />}`
|
||||
*/
|
||||
export function IconSetter({ onChange, ...rest }: FormItemComponentProps) {
|
||||
const handleChange = (val: string) => {
|
||||
onChange &&
|
||||
onChange(val, {
|
||||
relatedImports: ['Icon'],
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Box css={acStyle}>
|
||||
<Select showSearch allowClear placeholder="请选择图标" onChange={handleChange} {...rest}>
|
||||
{ALL_ICON_NAMES.map((icon) => {
|
||||
const key = `{<Icon type="${icon}" />}`;
|
||||
return (
|
||||
<Select.Option key={key}>
|
||||
<Box display="inline-block" mr="m">
|
||||
{/* <IconPro type={icon as any} size="14px" /> */}
|
||||
</Box>
|
||||
{icon}
|
||||
</Select.Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Empty, Modal, Pagination, Radio } from 'antd';
|
||||
import { Box, css, Grid, GridItem, Text } from 'coral-system';
|
||||
import { isFunction, useBoolean } from '@music163/tango-helpers';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { useFormVariable } from '../context';
|
||||
|
||||
const wrapperStyle = css`
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 150px;
|
||||
}
|
||||
`;
|
||||
|
||||
export function ImageSetter({ value, onChange }: FormItemComponentProps) {
|
||||
const [visible, { on, off }] = useBoolean();
|
||||
const label = value ? '更新图片' : '上传图片';
|
||||
const { remoteServices } = useFormVariable();
|
||||
return (
|
||||
<Box css={wrapperStyle}>
|
||||
<Box border="solid" borderColor="line.normal" textAlign="center" mb="m">
|
||||
{value ? <img src={value} alt="preview image" /> : null}
|
||||
</Box>
|
||||
<Button block onClick={on}>
|
||||
{label}
|
||||
</Button>
|
||||
<Modal title={label} visible={visible} onCancel={off} width="60%">
|
||||
<ImageCenter
|
||||
imageService={remoteServices?.ImageService}
|
||||
onSelect={(url) => {
|
||||
onChange(url);
|
||||
off();
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const categories = [
|
||||
{ label: '我的素材', value: 'listMy' },
|
||||
{ label: '我的收藏', value: 'listFav' },
|
||||
{ label: '公共素材', value: 'listPub' },
|
||||
];
|
||||
|
||||
interface ImageCenterProps {
|
||||
onSelect?: ImageListProps['onSelect'];
|
||||
imageService?: Record<string, (...args: any[]) => Promise<any>>;
|
||||
}
|
||||
|
||||
const pageSize = 20;
|
||||
|
||||
function ImageCenter({ imageService, onSelect }: ImageCenterProps) {
|
||||
const [cate, setCate] = useState('listMy');
|
||||
const [page, setPage] = useState(1);
|
||||
const [data, setData] = useState<any>({});
|
||||
useEffect(() => {
|
||||
imageService?.[cate]?.({ limit: pageSize, offset: (page - 1) * pageSize }).then((res: any) => {
|
||||
setData(res || {});
|
||||
});
|
||||
}, [imageService, cate, page]);
|
||||
return (
|
||||
<Box>
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Radio.Group
|
||||
value={cate}
|
||||
onChange={(e) => {
|
||||
setCate(e.target.value);
|
||||
}}
|
||||
options={categories}
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
href="https://music-fn.hz.netease.com/s/music-deer-web/user/image/myupload"
|
||||
target="_blank"
|
||||
>
|
||||
我要上传
|
||||
</Button>
|
||||
</Box>
|
||||
{data.list?.length ? (
|
||||
<ImageList dataSource={data.list} onSelect={onSelect} />
|
||||
) : (
|
||||
<Empty description="没有数据或数据服务请求出错" />
|
||||
)}
|
||||
{data.count > pageSize ? (
|
||||
<Pagination current={page} total={data.count} pageSize={pageSize} onChange={setPage} />
|
||||
) : null}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const imageListWrapper = css`
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 120px;
|
||||
}
|
||||
|
||||
button {
|
||||
border-color: transparent;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--tango-colors-brand);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface ImageListProps {
|
||||
dataSource?: any[];
|
||||
onSelect?: (url: string) => void;
|
||||
}
|
||||
|
||||
function ImageList({ dataSource = [], onSelect }: ImageListProps) {
|
||||
return (
|
||||
<Grid my="m" columns={4} gap="12px" height="400px" overflow="auto" css={imageListWrapper}>
|
||||
{dataSource.map((item) => (
|
||||
<GridItem
|
||||
key={item.id}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
bg="gray.20"
|
||||
>
|
||||
<Box
|
||||
as="button"
|
||||
display="inline-flex"
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
gap="m"
|
||||
bg="transparent"
|
||||
onClick={() => {
|
||||
isFunction(onSelect) && onSelect(item.url);
|
||||
}}
|
||||
>
|
||||
<img src={item.url} />
|
||||
<Text fontSize="12px" color="text.note">
|
||||
{item.name}
|
||||
</Text>
|
||||
</Box>
|
||||
</GridItem>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -1,216 +1,40 @@
|
||||
import { ActionListSetter } from './action-list-setter';
|
||||
import { EnumSetter } from './enum-setter';
|
||||
import { TextAreaSetter, TextSetter } from './text-setter';
|
||||
import { PickerSetter } from './picker-setter';
|
||||
import { ChoiceSetter } from './choice-setter';
|
||||
import { FormItemCreateOptionsType } from '../form-item';
|
||||
import { ListSetter } from './list-setter';
|
||||
import { IconSetter } from './icon-setter';
|
||||
import { ColumnSetter } from './column-setter';
|
||||
import { NumberSetter, SliderSetter } from './number-setter';
|
||||
import { ExpressionSetter, expressionValueValidate, jsonValueValidate } from './expression-setter';
|
||||
import { OptionSetter } from './option-setter';
|
||||
import { DateSetter, DateRangeSetter, TimeSetter, TimeRangeSetter } from './date-setter';
|
||||
import { JsxSetter } from './jsx-setter';
|
||||
import { EventSetter } from './event-setter';
|
||||
import {
|
||||
SpacingSetter,
|
||||
ColorSetter,
|
||||
BgSetter,
|
||||
BorderSetter,
|
||||
FlexGapSetter,
|
||||
DisplaySetter,
|
||||
FlexJustifyContentSetter,
|
||||
FlexAlignItemsSetter,
|
||||
FlexDirectionSetter,
|
||||
} from './style-setter';
|
||||
import { CssSetter } from './css-setter';
|
||||
import { ListenerSetter } from './listener-setter';
|
||||
import { RuleSetter } from './rule-setter';
|
||||
import { ModelSetter } from './model-setter';
|
||||
import {
|
||||
RenderSetter,
|
||||
TableCellSetter,
|
||||
TableExpandableSetter,
|
||||
XToolbarSetter,
|
||||
} from './render-props-setter';
|
||||
import { ImageSetter } from './image-setter';
|
||||
import { RouterSetter } from './router-setter';
|
||||
import { BoolSetter } from './bool-setter';
|
||||
import { IFormItemCreateOptions } from '../form-item';
|
||||
import { ExpressionSetter } from './exp-setter';
|
||||
|
||||
export const INTERNAL_SETTERS: FormItemCreateOptionsType[] = [
|
||||
{
|
||||
name: 'actionListSetter',
|
||||
component: ActionListSetter,
|
||||
},
|
||||
{
|
||||
name: 'jsxSetter',
|
||||
component: JsxSetter,
|
||||
},
|
||||
{
|
||||
name: 'modelSetter',
|
||||
component: ModelSetter,
|
||||
disableVariableSetter: true,
|
||||
validate(value: string) {
|
||||
if (value.split('.').length < 2) {
|
||||
return '请输入合法的模型变量路径,如:modelName.variableName';
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'eventSetter',
|
||||
alias: 'actionSetter',
|
||||
component: EventSetter,
|
||||
disableVariableSetter: true,
|
||||
},
|
||||
export const INTERNAL_SETTERS: IFormItemCreateOptions[] = [
|
||||
{
|
||||
name: 'boolSetter',
|
||||
component: BoolSetter,
|
||||
},
|
||||
{
|
||||
name: 'expressionSetter',
|
||||
alias: 'expSetter',
|
||||
name: 'choiceSetter',
|
||||
component: ChoiceSetter,
|
||||
},
|
||||
{
|
||||
name: 'expSetter',
|
||||
alias: ['expressionSetter'],
|
||||
component: ExpressionSetter,
|
||||
disableVariableSetter: true,
|
||||
validate: expressionValueValidate,
|
||||
},
|
||||
{
|
||||
name: 'jsonSetter',
|
||||
component: ExpressionSetter,
|
||||
disableVariableSetter: true,
|
||||
validate: jsonValueValidate,
|
||||
},
|
||||
{
|
||||
name: 'dateSetter',
|
||||
component: DateSetter,
|
||||
},
|
||||
{
|
||||
name: 'enumSetter',
|
||||
component: EnumSetter,
|
||||
},
|
||||
{
|
||||
name: 'dateRangeSetter',
|
||||
component: DateRangeSetter,
|
||||
},
|
||||
{
|
||||
name: 'timeSetter',
|
||||
component: TimeSetter,
|
||||
},
|
||||
{
|
||||
name: 'timeRangeSetter',
|
||||
component: TimeRangeSetter,
|
||||
name: 'numberSetter',
|
||||
component: NumberSetter,
|
||||
},
|
||||
{
|
||||
name: 'textSetter',
|
||||
component: TextSetter,
|
||||
},
|
||||
{ name: 'textAreaSetter', component: TextAreaSetter },
|
||||
{
|
||||
name: 'numberSetter',
|
||||
component: NumberSetter,
|
||||
},
|
||||
{
|
||||
name: 'sliderSetter',
|
||||
component: SliderSetter,
|
||||
},
|
||||
{
|
||||
name: 'pickerSetter',
|
||||
alias: 'selectSetter',
|
||||
component: PickerSetter,
|
||||
},
|
||||
{
|
||||
name: 'choiceSetter',
|
||||
component: ChoiceSetter,
|
||||
},
|
||||
{
|
||||
name: 'iconSetter',
|
||||
component: IconSetter,
|
||||
},
|
||||
{
|
||||
name: 'optionSetter',
|
||||
component: OptionSetter,
|
||||
},
|
||||
// {
|
||||
// name: 'jsonSetter',
|
||||
// alias: 'styleSetter',
|
||||
// component: JSONSetter,
|
||||
// },
|
||||
{
|
||||
name: 'listSetter',
|
||||
component: ListSetter as any,
|
||||
},
|
||||
// {
|
||||
// name: 'stateSetter',
|
||||
// component: StateSetter,
|
||||
// disableVariableSetter: true,
|
||||
// },
|
||||
{
|
||||
name: 'cssSetter',
|
||||
component: CssSetter,
|
||||
type: 'subForm',
|
||||
defaultCollapsed: false,
|
||||
},
|
||||
{
|
||||
name: 'displaySetter',
|
||||
component: DisplaySetter,
|
||||
},
|
||||
{ name: 'flexDirectionSetter', component: FlexDirectionSetter },
|
||||
{
|
||||
name: 'flexGapSetter',
|
||||
component: FlexGapSetter,
|
||||
},
|
||||
{
|
||||
name: 'flexJustifyContentSetter',
|
||||
component: FlexJustifyContentSetter,
|
||||
},
|
||||
{
|
||||
name: 'flexAlignItemsSetter',
|
||||
component: FlexAlignItemsSetter,
|
||||
},
|
||||
{
|
||||
name: 'spacingSetter',
|
||||
component: SpacingSetter,
|
||||
},
|
||||
{
|
||||
name: 'colorSetter',
|
||||
component: ColorSetter,
|
||||
},
|
||||
{ name: 'bgSetter', component: BgSetter },
|
||||
{ name: 'borderSetter', component: BorderSetter },
|
||||
{
|
||||
name: 'columnSetter',
|
||||
component: ColumnSetter,
|
||||
},
|
||||
{
|
||||
name: 'listenerSetter',
|
||||
component: ListenerSetter,
|
||||
},
|
||||
{
|
||||
name: 'ruleSetter',
|
||||
component: RuleSetter,
|
||||
},
|
||||
{
|
||||
name: 'renderPropsSetter',
|
||||
component: RenderSetter,
|
||||
},
|
||||
{
|
||||
name: 'tableCellSetter',
|
||||
component: TableCellSetter,
|
||||
},
|
||||
{
|
||||
name: 'tableExpandableSetter',
|
||||
component: TableExpandableSetter,
|
||||
},
|
||||
{
|
||||
name: 'xtoolbarSetter',
|
||||
component: XToolbarSetter,
|
||||
},
|
||||
{
|
||||
name: 'imageSetter',
|
||||
component: ImageSetter,
|
||||
},
|
||||
{
|
||||
name: 'routerSetter',
|
||||
component: RouterSetter,
|
||||
},
|
||||
];
|
||||
|
||||
export * from './bool-setter';
|
||||
export * from './choice-setter';
|
||||
export * from './number-setter';
|
||||
export * from './text-setter';
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
import React, { FunctionComponent, useState } from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { Select, Input, Tabs, Button } from 'antd';
|
||||
import { FieldStringOutlined } from '@ant-design/icons';
|
||||
import { SingleMonacoEditor } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { useFormVariable } from '../context';
|
||||
import { ListSetter } from './list-setter';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
// TODO: 需考虑 option选项联动 & 组件属性联动等面板
|
||||
const CustomComponent: FunctionComponent<any> = ({ value = {}, onChange }) => {
|
||||
// FIXME: 这里之前判断逻辑有问题,导致每次 value 变更都执行 parseExpression
|
||||
const [tabIndex, setTabIndex] = useState(typeof value === 'string' ? '2' : '1');
|
||||
|
||||
const triggerChange = (changedValue: any) => {
|
||||
onChange({
|
||||
...value,
|
||||
...changedValue,
|
||||
});
|
||||
};
|
||||
|
||||
const customListenerDefaultValue = `/* {(valid, field, form) => {
|
||||
form.fieldMap.input1.setValue("联动改变值");
|
||||
}}*/`;
|
||||
return (
|
||||
<Tabs type="card" activeKey={tabIndex} onChange={setTabIndex}>
|
||||
<TabPane tab="常用配置" key="1">
|
||||
<Box display="flex" flexDirection="column" gap="20px">
|
||||
<Select
|
||||
value={value?.status}
|
||||
onChange={(v) => {
|
||||
triggerChange({
|
||||
status: v,
|
||||
});
|
||||
}}
|
||||
placeholder="UI状态"
|
||||
options={[
|
||||
{
|
||||
label: '编辑',
|
||||
value: 'edit',
|
||||
},
|
||||
{
|
||||
label: '禁用',
|
||||
value: 'disabled',
|
||||
},
|
||||
{
|
||||
label: '隐藏',
|
||||
value: 'hidden',
|
||||
},
|
||||
{
|
||||
label: '预览',
|
||||
value: 'preview',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
value={value?.ui?.required}
|
||||
onChange={(v) => {
|
||||
triggerChange({
|
||||
ui: {
|
||||
...value?.ui,
|
||||
required: v,
|
||||
},
|
||||
});
|
||||
}}
|
||||
placeholder="必填"
|
||||
options={[
|
||||
{
|
||||
label: '必填',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
label: '不必填',
|
||||
value: false,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Input
|
||||
placeholder="设置label标题"
|
||||
value={value?.ui?.label}
|
||||
onChange={(e) => {
|
||||
triggerChange({
|
||||
ui: {
|
||||
...value?.ui,
|
||||
label: e.target.value,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
placeholder="设置字段值"
|
||||
value={value?.value}
|
||||
onChange={(e) => {
|
||||
triggerChange({
|
||||
value: e.target.value,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</TabPane>
|
||||
<TabPane tab="自定义配置" key="2">
|
||||
<SingleMonacoEditor
|
||||
defaultValue={customListenerDefaultValue}
|
||||
value={
|
||||
Object.prototype.toString.call(value) === '[object Object]'
|
||||
? customListenerDefaultValue
|
||||
: value
|
||||
}
|
||||
onChange={(v) => onChange(v.trim())}
|
||||
hasBorder
|
||||
height="150px"
|
||||
options={{
|
||||
lineNumbers: 'off',
|
||||
fontSize: 12,
|
||||
wordWrap: 'on',
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
const renderSetterItem = (item: any) => {
|
||||
return (
|
||||
<>
|
||||
<FieldStringOutlined />
|
||||
<Box display="inline-block" ml="m">
|
||||
{item.title}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* XFormItem响应器
|
||||
*/
|
||||
export const ListenerSetter = (props: FormItemComponentProps<any[]>) => {
|
||||
const { formFieldsOptions } = useFormVariable();
|
||||
|
||||
return (
|
||||
<ListSetter
|
||||
getListItemKey={(item) => item.key || item.dataIndex}
|
||||
addBtnText="配置响应器"
|
||||
listItemFormFields={[
|
||||
{
|
||||
label: '来源字段',
|
||||
name: 'watch',
|
||||
required: true,
|
||||
component: <Select mode="tags" options={formFieldsOptions} />,
|
||||
extra: <span>请选择依赖的字段。</span>,
|
||||
},
|
||||
{
|
||||
label: '触发条件',
|
||||
name: 'condition',
|
||||
required: true,
|
||||
component: (
|
||||
<SingleMonacoEditor
|
||||
hasBorder
|
||||
height="50px"
|
||||
defaultValue="// input1.value === '123'"
|
||||
options={{
|
||||
lineNumbers: 'off',
|
||||
fontSize: 12,
|
||||
wordWrap: 'on',
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
),
|
||||
extra: <span>请输入一个布尔表达式,满足条件时即触发响应。</span>,
|
||||
},
|
||||
{
|
||||
label: '响应行为',
|
||||
name: 'set',
|
||||
required: true,
|
||||
component: <CustomComponent />,
|
||||
width: '400px',
|
||||
extra: (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
'https://music-cms.hz.netease.com/xform-docs/docs/tutorial-basics/listeners#set-%E4%B8%BA-function',
|
||||
)
|
||||
}
|
||||
>
|
||||
文档地址
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
newItemDefaultValues={[]}
|
||||
renderItem={renderSetterItem}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,175 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { AutoComplete, Button, Select, Switch } from 'antd';
|
||||
import { FieldStringOutlined } from '@ant-design/icons';
|
||||
import { SingleMonacoEditor } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '../form-item';
|
||||
import { ListSetter, NewOptionFormFieldType } from './list-setter';
|
||||
|
||||
// 常用正则
|
||||
const enumPattern = [
|
||||
{
|
||||
label: '纯数字',
|
||||
value: '^[0-9]*$',
|
||||
},
|
||||
{
|
||||
label: '纯汉字',
|
||||
value: '^[\u4e00-\u9fa5]{0,}$',
|
||||
},
|
||||
{
|
||||
label: '手机号',
|
||||
value: '^[1][3-8][0-9]{9}$',
|
||||
},
|
||||
{
|
||||
label: 'IP地址',
|
||||
value: 'd+.d+.d+.d+',
|
||||
},
|
||||
{
|
||||
label: '身份证号',
|
||||
value: '^d{15}|d{18}$',
|
||||
},
|
||||
{
|
||||
label: '邮政编码',
|
||||
value: '[1-9]d{5}(?!d)',
|
||||
},
|
||||
{
|
||||
label: '图片URL',
|
||||
value: '(https?:[^:<>"]*/)([^:<>"]*)(.((png!thumbnail)|(png)|(jpg)|(webp)|(gif)))',
|
||||
},
|
||||
];
|
||||
|
||||
// async-validator type
|
||||
// https://github.com/yiminghe/async-validator#type
|
||||
const type = [
|
||||
'string', // // Must be of type string. This is the default type.
|
||||
// 'number', Input TextArea出来的value都是string / InputNumber是number。不在async-validator做数字类型的校验
|
||||
'boolean', // Must be of type boolean.
|
||||
'regexp', // Must be an instance of RegExp or a string that does not generate an exception when creating a new RegExp.
|
||||
'integer', // Must be of type number and an integer.
|
||||
'float', // Must be of type number and a floating point number.
|
||||
'array', // Must be an array as determined by Array.isArray.
|
||||
'object', // Must be of type object and not Array.isArray.
|
||||
'date', // Value // Must be valid as determined by Date
|
||||
'url', // Must be of type url.
|
||||
'email', // Must be of type email.
|
||||
];
|
||||
|
||||
const trigger = ['change', 'submit'];
|
||||
|
||||
const status = ['error', 'warning'];
|
||||
|
||||
/**
|
||||
* 正则输入框
|
||||
*/
|
||||
const PatternInput: React.FunctionComponent<any> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<AutoComplete options={enumPattern} onSelect={props.onChange} {...props} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 必填选择
|
||||
*/
|
||||
const RequiredInput: React.FunctionComponent<any> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<Switch onChange={props.onChange} checked={props.value} {...props} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const optionFormFields: NewOptionFormFieldType[] = [
|
||||
{
|
||||
label: '字段类型',
|
||||
name: 'type',
|
||||
required: true,
|
||||
component: <Select options={type.map((t) => ({ label: t, value: t }))} />,
|
||||
},
|
||||
{
|
||||
label: '是否必填',
|
||||
name: 'required',
|
||||
component: <RequiredInput />,
|
||||
},
|
||||
{
|
||||
label: '触发方式',
|
||||
name: 'trigger',
|
||||
component: <Select options={trigger.map((t) => ({ label: t, value: t }))} />,
|
||||
},
|
||||
{
|
||||
label: '触发状态',
|
||||
name: 'status',
|
||||
component: <Select options={status.map((t) => ({ label: t, value: t }))} />,
|
||||
},
|
||||
{
|
||||
label: '错误提示语',
|
||||
name: 'message',
|
||||
extra: '自定义错误提示',
|
||||
},
|
||||
{
|
||||
label: '正则表达式',
|
||||
name: 'pattern',
|
||||
extra: '复杂场景,可以自定义正则。',
|
||||
component: <PatternInput placeholder="例如: ^[0-9]*$" />,
|
||||
},
|
||||
{
|
||||
label: '自定义校验',
|
||||
name: 'validator',
|
||||
width: '400px',
|
||||
extra: (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => window.open('https://github.com/yiminghe/async-validator#validator')}
|
||||
>
|
||||
文档地址
|
||||
</Button>
|
||||
),
|
||||
component: (
|
||||
<SingleMonacoEditor
|
||||
hasBorder
|
||||
height="108px"
|
||||
defaultValue="/** {(rule, value, callback) => { return value === 'test';}}*/"
|
||||
options={{
|
||||
lineNumbers: 'off',
|
||||
fontSize: 12,
|
||||
wordWrap: 'on',
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const renderSetterItem = (item: any) => {
|
||||
return (
|
||||
<>
|
||||
<FieldStringOutlined />
|
||||
<Box display="inline-block" ml="m">
|
||||
{item.title}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验属性配置
|
||||
*/
|
||||
export function RuleSetter(props: FormItemComponentProps<any[]>) {
|
||||
return (
|
||||
<ListSetter
|
||||
getListItemKey={(item) => item.key || item.dataIndex}
|
||||
addBtnText="配置校验器"
|
||||
listItemFormFields={optionFormFields}
|
||||
newItemDefaultValues={{
|
||||
type: 'string',
|
||||
trigger: 'change',
|
||||
status: 'error',
|
||||
}}
|
||||
renderItem={renderSetterItem}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import { FormItemComponentProps } from '../form-item';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
type TextSetterProps = {
|
||||
interface TextSetterProps extends Omit<InputProps, 'onChange' | 'value'> {
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
} & Omit<InputProps, 'onChange' | 'value'>;
|
||||
}
|
||||
|
||||
export function TextSetter({
|
||||
value: valueProp,
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
declare module 'cssjson';
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.1.3](https://github.com/netease/tango/compare/@music163/tango-ui@0.1.2...@music163/tango-ui@0.1.3) (2023-09-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- update exports ([443ae58](https://github.com/netease/tango/commit/443ae589f67255f3815b587fd8f928c2b8742e9a))
|
||||
|
||||
## 0.1.2 (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @music163/tango-ui
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@music163/tango-ui",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "ui widgets of tango",
|
||||
"keywords": [
|
||||
"react",
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { IconFontProps } from '@ant-design/icons/es/components/IconFont';
|
||||
/**
|
||||
* 只能在 Designer 里面用,其他地方用不了,依赖 iconfont 脚本提前载入
|
||||
*/
|
||||
export const IconFont = React.forwardRef<HTMLSpanElement, IconFontProps<string>>((props, ref) => {
|
||||
export function IconFont(props: IconFontProps<string>) {
|
||||
const { type, children, ...restProps } = props;
|
||||
|
||||
// children > type
|
||||
@@ -16,12 +16,7 @@ export const IconFont = React.forwardRef<HTMLSpanElement, IconFontProps<string>>
|
||||
if (children) {
|
||||
content = children;
|
||||
}
|
||||
return (
|
||||
// @ts-ignore
|
||||
<Icon {...restProps} ref={ref}>
|
||||
{content}
|
||||
</Icon>
|
||||
);
|
||||
});
|
||||
return <Icon {...(restProps as any)}>{content}</Icon>;
|
||||
}
|
||||
|
||||
IconFont.displayName = 'IconFont';
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"@music163/tango-setting-form": ["packages/setting-form/src/index.ts"],
|
||||
"@music163/tango-helpers": ["packages/helpers/src/index.ts"],
|
||||
"@music163/tango": ["packages/designer/src/index.ts"],
|
||||
"@music163/tango-ui": ["packages/ui/src/index.ts"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1765,7 +1765,7 @@
|
||||
resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz"
|
||||
integrity sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==
|
||||
|
||||
"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0", "@discoveryjs/json-ext@^0.5.3":
|
||||
"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.3":
|
||||
version "0.5.7"
|
||||
resolved "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz"
|
||||
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
||||
@@ -4379,10 +4379,10 @@
|
||||
resolved "https://registry.npmmirror.com/@types/color-name/-/color-name-1.1.1.tgz"
|
||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||
|
||||
"@types/color@^3.0.3":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.npmmirror.com/@types/color/-/color-3.0.3.tgz"
|
||||
integrity sha512-X//qzJ3d3Zj82J9sC/C18ZY5f43utPbAJ6PhYt/M7uG6etcF6MRpKdN880KBy43B0BMzSfeT96MzrsNjFI3GbA==
|
||||
"@types/color@^3.0.4":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.npmmirror.com/@types/color/-/color-3.0.4.tgz#53ceca0946660c7779442948878172c6ace13777"
|
||||
integrity sha512-OpisS4bqJJwbkkQRrMvURf3DOxBoAg9mysHYI7WgrWpSYHqHGKYBULHdz4ih77SILcLDo/zyHGFyfIl9yb8NZQ==
|
||||
dependencies:
|
||||
"@types/color-convert" "*"
|
||||
|
||||
@@ -4705,7 +4705,7 @@
|
||||
|
||||
"@types/react-color@^3.0.6":
|
||||
version "3.0.6"
|
||||
resolved "https://registry.npmmirror.com/@types/react-color/-/react-color-3.0.6.tgz"
|
||||
resolved "https://registry.npmmirror.com/@types/react-color/-/react-color-3.0.6.tgz#602fed023802b2424e7cd6ff3594ccd3d5055f9a"
|
||||
integrity sha512-OzPIO5AyRmLA7PlOyISlgabpYUa3En74LP8mTMa0veCA719SvYQov4WLMsHvCgXP+L+KI9yGhYnqZafVGG0P4w==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
@@ -5524,23 +5524,6 @@
|
||||
"@webassemblyjs/wast-parser" "1.9.0"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webpack-cli/configtest@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmmirror.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz"
|
||||
integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==
|
||||
|
||||
"@webpack-cli/info@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmmirror.com/@webpack-cli/info/-/info-1.5.0.tgz"
|
||||
integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==
|
||||
dependencies:
|
||||
envinfo "^7.7.3"
|
||||
|
||||
"@webpack-cli/serve@^1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.npmmirror.com/@webpack-cli/serve/-/serve-1.7.0.tgz"
|
||||
integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz"
|
||||
@@ -7122,14 +7105,6 @@ chalk@^2.4.1, chalk@^2.4.2:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz"
|
||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz"
|
||||
@@ -7505,7 +7480,7 @@ color-support@^1.1.2, color-support@^1.1.3:
|
||||
|
||||
color@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmmirror.com/color/-/color-4.2.3.tgz"
|
||||
resolved "https://registry.npmmirror.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
|
||||
integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
|
||||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
@@ -7516,7 +7491,7 @@ colord@^2.9.1:
|
||||
resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz"
|
||||
integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
|
||||
|
||||
colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.20:
|
||||
colorette@^2.0.10, colorette@^2.0.20:
|
||||
version "2.0.20"
|
||||
resolved "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz"
|
||||
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
|
||||
@@ -7571,7 +7546,7 @@ commander@^6.2.1:
|
||||
resolved "https://registry.npmmirror.com/commander/-/commander-6.2.1.tgz"
|
||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||
|
||||
commander@^7.0.0, commander@^7.2.0:
|
||||
commander@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz"
|
||||
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||
@@ -7942,13 +7917,6 @@ cookie@0.5.0:
|
||||
resolved "https://registry.npmmirror.com/cookie/-/cookie-0.5.0.tgz"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
copy-anything@^2.0.1:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz"
|
||||
integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==
|
||||
dependencies:
|
||||
is-what "^3.14.1"
|
||||
|
||||
copy-concurrently@^1.0.0:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmmirror.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz"
|
||||
@@ -8373,7 +8341,7 @@ cssesc@^3.0.0:
|
||||
|
||||
cssjson@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmmirror.com/cssjson/-/cssjson-2.1.3.tgz"
|
||||
resolved "https://registry.npmmirror.com/cssjson/-/cssjson-2.1.3.tgz#b855aa06c5979df16bf6375a14a455e415660169"
|
||||
integrity sha512-VKzsSbYW4gwfS6Fg+z1mEII+cvurP/Vr7G3cDLEkvR0tcQD20LpF/ljOOFVVT9XYkOFo4TQWRcB/mSmbrKsXxA==
|
||||
|
||||
cssnano-preset-advanced@^5.3.8:
|
||||
@@ -8523,7 +8491,7 @@ debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, de
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^3.0.0, debug@^3.2.6, debug@^3.2.7:
|
||||
debug@^3.0.0, debug@^3.2.7:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz"
|
||||
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
|
||||
@@ -9082,7 +9050,7 @@ env-paths@^2.2.0:
|
||||
resolved "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz"
|
||||
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
|
||||
|
||||
envinfo@^7.7.3, envinfo@^7.7.4:
|
||||
envinfo@^7.7.4:
|
||||
version "7.10.0"
|
||||
resolved "https://registry.npmmirror.com/envinfo/-/envinfo-7.10.0.tgz"
|
||||
integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==
|
||||
@@ -9092,7 +9060,7 @@ err-code@^2.0.2:
|
||||
resolved "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz"
|
||||
integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
|
||||
|
||||
errno@^0.1.1, errno@^0.1.3, errno@~0.1.7:
|
||||
errno@^0.1.3, errno@~0.1.7:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz"
|
||||
integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
|
||||
@@ -9808,11 +9776,6 @@ fast-url-parser@1.1.3:
|
||||
dependencies:
|
||||
punycode "^1.3.2"
|
||||
|
||||
fastest-levenshtein@^1.0.12:
|
||||
version "1.0.16"
|
||||
resolved "https://registry.npmmirror.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz"
|
||||
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
|
||||
|
||||
fastq@^1.6.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz"
|
||||
@@ -11234,7 +11197,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3:
|
||||
iconv-lite@0.6.3, iconv-lite@^0.6.2:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz"
|
||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||
@@ -11294,11 +11257,6 @@ image-size@^1.0.1:
|
||||
dependencies:
|
||||
queue "6.0.2"
|
||||
|
||||
image-size@~0.5.0:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz"
|
||||
integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
|
||||
|
||||
immer@^9.0.7:
|
||||
version "9.0.21"
|
||||
resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz"
|
||||
@@ -12003,11 +11961,6 @@ is-weakset@^2.0.1:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.1.1"
|
||||
|
||||
is-what@^3.14.1:
|
||||
version "3.14.1"
|
||||
resolved "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz"
|
||||
integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==
|
||||
|
||||
is-whitespace-character@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmmirror.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz"
|
||||
@@ -13038,32 +12991,6 @@ lerna@^6.6.1:
|
||||
yargs "16.2.0"
|
||||
yargs-parser "20.2.4"
|
||||
|
||||
less-loader@^7.3.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.npmmirror.com/less-loader/-/less-loader-7.3.0.tgz"
|
||||
integrity sha512-Mi8915g7NMaLlgi77mgTTQvK022xKRQBIVDSyfl3ErTuBhmZBQab0mjeJjNNqGbdR+qrfTleKXqbGI4uEFavxg==
|
||||
dependencies:
|
||||
klona "^2.0.4"
|
||||
loader-utils "^2.0.0"
|
||||
schema-utils "^3.0.0"
|
||||
|
||||
less@^4.1.2:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmmirror.com/less/-/less-4.2.0.tgz"
|
||||
integrity sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==
|
||||
dependencies:
|
||||
copy-anything "^2.0.1"
|
||||
parse-node-version "^1.0.1"
|
||||
tslib "^2.3.0"
|
||||
optionalDependencies:
|
||||
errno "^0.1.1"
|
||||
graceful-fs "^4.1.2"
|
||||
image-size "~0.5.0"
|
||||
make-dir "^2.1.0"
|
||||
mime "^1.4.1"
|
||||
needle "^3.1.0"
|
||||
source-map "~0.6.0"
|
||||
|
||||
leven@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmmirror.com/leven/-/leven-3.1.0.tgz"
|
||||
@@ -13751,7 +13678,7 @@ mime@1.4.1:
|
||||
resolved "https://registry.npmmirror.com/mime/-/mime-1.4.1.tgz"
|
||||
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
|
||||
|
||||
mime@1.6.0, mime@^1.4.1, mime@^1.6.0:
|
||||
mime@1.6.0, mime@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
@@ -13796,15 +13723,6 @@ mini-create-react-context@^0.4.0:
|
||||
"@babel/runtime" "^7.12.1"
|
||||
tiny-warning "^1.0.3"
|
||||
|
||||
mini-css-extract-plugin@^1.6.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.npmmirror.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz"
|
||||
integrity sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
schema-utils "^3.0.0"
|
||||
webpack-sources "^1.1.0"
|
||||
|
||||
mini-css-extract-plugin@^2.6.1:
|
||||
version "2.7.6"
|
||||
resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz"
|
||||
@@ -14028,9 +13946,9 @@ modify-values@^1.0.0:
|
||||
resolved "https://registry.npmmirror.com/modify-values/-/modify-values-1.0.1.tgz"
|
||||
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
|
||||
|
||||
moment@^2.24.0, moment@^2.29.2, moment@^2.29.3:
|
||||
moment@^2.24.0, moment@^2.29.2, moment@^2.29.4:
|
||||
version "2.29.4"
|
||||
resolved "https://registry.npmmirror.com/moment/-/moment-2.29.4.tgz"
|
||||
resolved "https://registry.npmmirror.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||
|
||||
monaco-editor-textmate@^4.0.0:
|
||||
@@ -14143,15 +14061,6 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
needle@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmmirror.com/needle/-/needle-3.2.0.tgz"
|
||||
integrity sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==
|
||||
dependencies:
|
||||
debug "^3.2.6"
|
||||
iconv-lite "^0.6.3"
|
||||
sax "^1.2.4"
|
||||
|
||||
negotiator@0.6.3, negotiator@^0.6.3:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz"
|
||||
@@ -15156,11 +15065,6 @@ parse-json@^5.0.0, parse-json@^5.2.0:
|
||||
json-parse-even-better-errors "^2.3.0"
|
||||
lines-and-columns "^1.1.6"
|
||||
|
||||
parse-node-version@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz"
|
||||
integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==
|
||||
|
||||
parse-numeric-range@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz"
|
||||
@@ -16230,19 +16134,6 @@ process@^0.11.10:
|
||||
resolved "https://registry.npmmirror.com/process/-/process-0.11.10.tgz"
|
||||
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
||||
|
||||
progress-bar-webpack-plugin@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmmirror.com/progress-bar-webpack-plugin/-/progress-bar-webpack-plugin-2.1.0.tgz"
|
||||
integrity sha512-UtlZbnxpYk1wufEWfhIjRn2U52zlY38uvnzFhs8rRxJxC1hSqw88JNR2Mbpqq9Kix8L1nGb3uQ+/1BiUWbigAg==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
progress "^2.0.3"
|
||||
|
||||
progress@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
||||
promise-all-reject-late@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz"
|
||||
@@ -16928,7 +16819,7 @@ react-base16-styling@^0.6.0:
|
||||
|
||||
react-color@^2.19.3:
|
||||
version "2.19.3"
|
||||
resolved "https://registry.npmmirror.com/react-color/-/react-color-2.19.3.tgz"
|
||||
resolved "https://registry.npmmirror.com/react-color/-/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d"
|
||||
integrity sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==
|
||||
dependencies:
|
||||
"@icons/material" "^0.2.4"
|
||||
@@ -17417,13 +17308,6 @@ rechoir@^0.6.2:
|
||||
dependencies:
|
||||
resolve "^1.1.6"
|
||||
|
||||
rechoir@^0.7.0:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.npmmirror.com/rechoir/-/rechoir-0.7.1.tgz"
|
||||
integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==
|
||||
dependencies:
|
||||
resolve "^1.9.0"
|
||||
|
||||
recursive-readdir@^2.2.2:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz"
|
||||
@@ -17750,7 +17634,7 @@ resolve.exports@^2.0.0:
|
||||
resolved "https://registry.npmmirror.com/resolve.exports/-/resolve.exports-2.0.2.tgz"
|
||||
integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
|
||||
|
||||
resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.4, resolve@^1.3.2, resolve@^1.9.0:
|
||||
resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.4, resolve@^1.3.2:
|
||||
version "1.22.4"
|
||||
resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.4.tgz"
|
||||
integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==
|
||||
@@ -20281,24 +20165,6 @@ webpack-chain@6.5.1:
|
||||
deepmerge "^1.5.2"
|
||||
javascript-stringify "^2.0.1"
|
||||
|
||||
webpack-cli@^4.9.2:
|
||||
version "4.10.0"
|
||||
resolved "https://registry.npmmirror.com/webpack-cli/-/webpack-cli-4.10.0.tgz"
|
||||
integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==
|
||||
dependencies:
|
||||
"@discoveryjs/json-ext" "^0.5.0"
|
||||
"@webpack-cli/configtest" "^1.2.0"
|
||||
"@webpack-cli/info" "^1.5.0"
|
||||
"@webpack-cli/serve" "^1.7.0"
|
||||
colorette "^2.0.14"
|
||||
commander "^7.0.0"
|
||||
cross-spawn "^7.0.3"
|
||||
fastest-levenshtein "^1.0.12"
|
||||
import-local "^3.0.2"
|
||||
interpret "^2.2.0"
|
||||
rechoir "^0.7.0"
|
||||
webpack-merge "^5.7.3"
|
||||
|
||||
webpack-dev-middleware@^3.7.3:
|
||||
version "3.7.3"
|
||||
resolved "https://registry.npmmirror.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz"
|
||||
@@ -20379,7 +20245,7 @@ webpack-log@^2.0.0:
|
||||
ansi-colors "^3.0.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
webpack-merge@^5.7.3, webpack-merge@^5.8.0:
|
||||
webpack-merge@^5.8.0:
|
||||
version "5.9.0"
|
||||
resolved "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-5.9.0.tgz"
|
||||
integrity sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==
|
||||
@@ -20387,7 +20253,7 @@ webpack-merge@^5.7.3, webpack-merge@^5.8.0:
|
||||
clone-deep "^4.0.1"
|
||||
wildcard "^2.0.0"
|
||||
|
||||
webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
|
||||
webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-1.4.3.tgz"
|
||||
integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
|
||||
@@ -20407,7 +20273,7 @@ webpack-virtual-modules@^0.2.2:
|
||||
dependencies:
|
||||
debug "^3.0.0"
|
||||
|
||||
webpack@4, webpack@^4.46.0:
|
||||
webpack@4:
|
||||
version "4.46.0"
|
||||
resolved "https://registry.npmmirror.com/webpack/-/webpack-4.46.0.tgz"
|
||||
integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==
|
||||
|
||||
Reference in New Issue
Block a user