Compare commits

...

7 Commits

Author SHA1 Message Date
wwsun 8a3ab3c093 chore(release): publish
- @music163/tango-context@1.1.10
 - @music163/tango-core@1.4.4
 - @music163/tango-designer@1.4.6
 - @music163/tango-helpers@1.2.4
 - @music163/tango-sandbox@1.0.13
 - @music163/tango-setting-form@1.2.15
 - @music163/tango-ui@1.4.5
2024-09-09 15:58:16 +08:00
Wells 55602fc961 fix: add classNameSetter and classNameInput (#207)
* feat: add basic classNameInput

* fix: add classNameSetter
2024-09-09 15:50:56 +08:00
864907600cc a3c0e463ca chore: update code editor (#205) 2024-09-09 15:08:02 +08:00
BoBoooooo 79f9a8c179 fix: service support set description field (#206)
* fix: service support set description field

* fix: undefined string bug

* fix: update title display
2024-09-09 15:07:38 +08:00
Wells ea96872261 fix: renderSetter with template (#202)
* fix: renderSetter with template

* fix: update types
2024-09-03 16:52:59 +08:00
wwsun a86e0e049c chore(release): publish
- @music163/tango-context@1.1.9
 - @music163/tango-core@1.4.3
 - @music163/tango-designer@1.4.5
 - @music163/tango-helpers@1.2.3
 - @music163/tango-sandbox@1.0.12
 - @music163/tango-setting-form@1.2.14
 - @music163/tango-ui@1.4.4
2024-09-02 10:50:59 +08:00
Wells c4e1ed67f5 fix: add prop value template & fix components popover (#201)
* fix: clear value in CodeSetter

* fix: update variable path in codesetter

* fix: add prop value template

* fix: update  recommendedList for ComponentsPopover

* fix: update icon size in line mode for ComponentsPanel
2024-08-30 18:09:01 +08:00
37 changed files with 716 additions and 249 deletions
+7 -1
View File
@@ -153,6 +153,7 @@ import { definePage } from "@music163/tango-boot";
import {
Page,
Section,
Box,
Button,
Input,
FormilyForm,
@@ -167,7 +168,9 @@ class App extends React.Component {
render() {
return (
<Page title={tango.stores.app.title} subTitle={<><Button>hello</Button></>}>
<Section tid="section0" />
<Section tid="section0">
<Box></Box>
</Section>
<Section tid="section1" title="Section Title">
your input: <Input tid="input1" defaultValue="hello" />
copy input: <Input value={tango.page.input1?.value} />
@@ -374,13 +377,16 @@ export default defineServices({
add: {
url: 'https://nei.hz.netease.com/api/apimock-v2/c45109399a1d33d83e32a59984b25b00/api/users',
method: 'post',
description: '新增用户'
},
update: {
url: 'https://nei.hz.netease.com/api/apimock-v2/c45109399a1d33d83e32a59984b25b00/api/users',
method: 'post',
description: '更新用户'
},
delete: {
url: 'https://nei.hz.netease.com/api/apimock-v2/c45109399a1d33d83e32a59984b25b00/api/users?id=1',
description: '删除用户'
},
});
`;
+52 -1
View File
@@ -48,7 +48,6 @@ const Snippet3ColumnLayout: IComponentPrototype = {
icon: 'icon-column-3',
type: 'snippet',
package: '@music163/antd',
// FIXME: Column 组件 需要更新为 defineComponent
initChildren: `
<Columns columns={12}>
<Column colSpan={4}>
@@ -169,6 +168,32 @@ const prototypes: Dict<IComponentPrototype> = {
Snippet2ColumnLayout,
Snippet3ColumnLayout,
SnippetButtonGroup,
Section: {
...basePrototypes['Section'],
props: [
// ...(basePrototypes['Section'].props as any),
{
name: 'title',
title: '标题',
setter: 'textSetter',
},
{
name: 'extra',
title: '额外内容',
setter: 'renderSetter',
options: [
{
label: '按钮组',
value: 'ButtonGroup',
renderBody:
'<ButtonGroup><Button>button1</Button><Button>button2</Button></ButtonGroup>',
relatedImports: ['ButtonGroup', 'Button'],
},
],
template: '(v) => {\n return {{content}};\n}',
},
],
},
Box: {
name: 'Box',
title: '盒子',
@@ -200,6 +225,32 @@ const prototypes: Dict<IComponentPrototype> = {
title: 'd',
setter: 'textSetter',
},
{
name: 'onClick',
title: '点击事件',
setter: 'eventSetter',
template: '(e) => {\n {{content}}\n}',
tip: '回调参数说明:e 为事件对象',
},
{
name: 'renderFoo',
title: 'foo自定义渲染',
setter: 'renderSetter',
options: [
{
label: '占位空间',
value: 'Box',
render: '() => <Box>test</Box>',
relatedImports: ['Box'],
},
{
label: '占位文本',
value: 'Text',
render: '() => <Text>text</Text>',
relatedImports: ['Text'],
},
],
},
],
},
Columns: {
+1 -1
View File
@@ -3,7 +3,7 @@ import { SystemProvider } from 'coral-system';
import 'antd/dist/antd.css';
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
actions: { argTypesRegex: '^on.*' },
controls: {
matchers: {
color: /(background|color)$/i,
@@ -0,0 +1,29 @@
import React from 'react';
import { ActionSelect } from '@music163/tango-ui';
export default {
title: 'UI/ActionSelect',
component: ActionSelect,
};
const options = [
{ label: 'action1', value: 'action1' },
{ label: 'action2', value: 'action2' },
{ label: 'action3', value: 'action3' },
];
export const Basic = {
args: {
defaultText: '选择动作',
options,
onSelect: console.log,
},
};
export const showInput = {
args: {
text: '选择动作',
options,
showInput: true,
},
};
@@ -0,0 +1,15 @@
import React, { useState } from 'react';
import { ClassNameInput } from '@music163/tango-ui';
export default {
title: 'UI/ClassNameInput',
};
export function Basic() {
return <ClassNameInput defaultValue="hello world" onChange={console.log} />;
}
export function Controlled() {
const [value, setValue] = useState('');
return <ClassNameInput value={value} onChange={setValue} />;
}
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.10](https://github.com/netease/tango/compare/@music163/tango-context@1.1.9...@music163/tango-context@1.1.10) (2024-09-09)
### Bug Fixes
- service support set description field ([#206](https://github.com/netease/tango/issues/206)) ([79f9a8c](https://github.com/netease/tango/commit/79f9a8c1791424f35f194ab2a9aa7c263ef39f40))
## [1.1.9](https://github.com/netease/tango/compare/@music163/tango-context@1.1.8...@music163/tango-context@1.1.9) (2024-09-02)
**Note:** Version bump only for package @music163/tango-context
## [1.1.8](https://github.com/netease/tango/compare/@music163/tango-context@1.1.7...@music163/tango-context@1.1.8) (2024-08-06)
**Note:** Version bump only for package @music163/tango-context
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-context",
"version": "1.1.8",
"version": "1.1.10",
"description": "react context for tango-apps",
"keywords": [
"react",
@@ -31,8 +31,8 @@
"react": ">= 16.8"
},
"dependencies": {
"@music163/tango-core": "^1.4.2",
"@music163/tango-helpers": "^1.2.2",
"@music163/tango-core": "^1.4.4",
"@music163/tango-helpers": "^1.2.4",
"mobx-react-lite": "4.0.7"
},
"publishConfig": {
+9 -6
View File
@@ -90,12 +90,15 @@ export const useWorkspaceData = () => {
key: prefix,
selectable: false,
showAddButton: true,
children: Object.keys(file.serviceFunctions || {}).map((key) => ({
title: key,
key: [prefix, key].join('.'),
type: 'function',
showRemoveButton: true,
})),
children: Object.entries(file.serviceFunctions || {}).map(([key, value]) => {
const title = value.description ? `${key}(${value.description})` : key;
return {
title,
key: [prefix, key].join('.'),
type: 'function',
showRemoveButton: true,
};
}),
});
});
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.4](https://github.com/netease/tango/compare/@music163/tango-core@1.4.3...@music163/tango-core@1.4.4) (2024-09-09)
**Note:** Version bump only for package @music163/tango-core
## [1.4.3](https://github.com/netease/tango/compare/@music163/tango-core@1.4.2...@music163/tango-core@1.4.3) (2024-09-02)
**Note:** Version bump only for package @music163/tango-core
## [1.4.2](https://github.com/netease/tango/compare/@music163/tango-core@1.4.1...@music163/tango-core@1.4.2) (2024-08-06)
### Bug Fixes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-core",
"version": "1.4.2",
"version": "1.4.4",
"description": "tango core",
"author": "wwsun <ww.sun@outlook.com>",
"homepage": "",
@@ -28,7 +28,7 @@
"@babel/parser": "^7.23.5",
"@babel/traverse": "^7.23.5",
"@babel/types": "^7.23.5",
"@music163/tango-helpers": "^1.2.2",
"@music163/tango-helpers": "^1.2.4",
"@types/babel__generator": "^7.6.7",
"@types/babel__traverse": "^7.20.4",
"mobx": "6.12.3",
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.6](https://github.com/netease/tango/compare/@music163/tango-designer@1.4.5...@music163/tango-designer@1.4.6) (2024-09-09)
### Bug Fixes
- add classNameSetter and classNameInput ([#207](https://github.com/netease/tango/issues/207)) ([55602fc](https://github.com/netease/tango/commit/55602fc961b5c20d923891ce13d7a51588bd047f))
- renderSetter with template ([#202](https://github.com/netease/tango/issues/202)) ([ea96872](https://github.com/netease/tango/commit/ea9687226123c839f7a9e001b4c01aa8315a135d))
- service support set description field ([#206](https://github.com/netease/tango/issues/206)) ([79f9a8c](https://github.com/netease/tango/commit/79f9a8c1791424f35f194ab2a9aa7c263ef39f40))
## [1.4.5](https://github.com/netease/tango/compare/@music163/tango-designer@1.4.4...@music163/tango-designer@1.4.5) (2024-09-02)
### Bug Fixes
- add prop value template & fix components popover ([#201](https://github.com/netease/tango/issues/201)) ([c4e1ed6](https://github.com/netease/tango/commit/c4e1ed67f55deb17dae740559602cd0adcfc8eb4))
## [1.4.4](https://github.com/netease/tango/compare/@music163/tango-designer@1.4.3...@music163/tango-designer@1.4.4) (2024-08-16)
### Bug Fixes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-designer",
"version": "1.4.4",
"version": "1.4.6",
"description": "lowcode designer",
"keywords": [
"react"
@@ -33,12 +33,12 @@
"dependencies": {
"@ant-design/icons": "^4.8.0",
"@music163/request": "^0.2.0",
"@music163/tango-context": "^1.1.8",
"@music163/tango-core": "^1.4.2",
"@music163/tango-helpers": "^1.2.2",
"@music163/tango-sandbox": "^1.0.11",
"@music163/tango-setting-form": "^1.2.13",
"@music163/tango-ui": "^1.4.3",
"@music163/tango-context": "^1.1.10",
"@music163/tango-core": "^1.4.4",
"@music163/tango-helpers": "^1.2.4",
"@music163/tango-sandbox": "^1.0.13",
"@music163/tango-setting-form": "^1.2.15",
"@music163/tango-ui": "^1.4.5",
"antd": "^4.24.2",
"cash-dom": "^8.1.2",
"classnames": "^2.5.1",
@@ -34,17 +34,17 @@ export const ComponentsPopover = observer(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
workspace.componentPrototypes.get(selectedNode?.name) ?? ({} as IComponentPrototype);
// 推荐使用的子组件
const insertedList = useMemo(
() =>
Array.isArray(prototype?.childrenName)
? prototype?.childrenName
: [prototype?.childrenName].filter(Boolean),
[prototype?.childrenName],
);
// 推荐使用的代码片段
const siblingList = useMemo(() => prototype?.siblingNames ?? [], [prototype.siblingNames]);
const recommendedList = useMemo(() => {
if (type === 'inner') {
return prototype?.childrenName
? Array.isArray(prototype?.childrenName)
? prototype.childrenName
: [prototype.childrenName]
: [];
}
// 默认推荐使用相同类型的组件作为兄弟节点
return prototype.siblingNames || [prototype.name];
}, [prototype.childrenName, prototype.siblingNames, prototype.name, type]);
const tipsTextMap = useMemo(
() => ({
@@ -82,22 +82,15 @@ export const ComponentsPopover = observer(
const menuList = JSON.parse(JSON.stringify(designer.menuData));
const commonList = menuList['common'] ?? [];
if (commonList?.length && siblingList?.length) {
commonList.unshift({
title: '代码片段',
items: siblingList,
});
}
if (commonList?.length && insertedList?.length) {
if (commonList?.length && recommendedList.length) {
commonList.unshift({
title: '推荐使用',
items: insertedList,
items: recommendedList,
});
}
return menuList;
}, [insertedList, siblingList, designer.menuData]);
}, [recommendedList, designer.menuData]);
const innerTypeProps =
// 手动触发 适用于 点击添加组件
@@ -88,6 +88,10 @@ export function AddServiceForm({
>
<Input placeholder="请输入数据服务调用名称" disabled={disabled || isModifyMode} />
</Form.Item>
{/* 备注 */}
<Form.Item label="备注" name="description">
<Input placeholder="请输入备注信息" />
</Form.Item>
<Form.Item label="路径" name="url" rules={[{ required: true, type: 'url' }]}>
<Input placeholder="请输入数据服务调用名称" disabled={disabled || isModifyMode} />
</Form.Item>
@@ -1,11 +1,11 @@
import React, { useCallback } from 'react';
import { Radio, RadioProps, Tooltip } from 'antd';
import type { OptionType } from '@music163/tango-helpers';
import type { IOptionItem } from '@music163/tango-helpers';
import { FormItemComponentProps } from '@music163/tango-setting-form';
import { IconFont } from '@music163/tango-ui';
interface ChoiceSetterProps {
options?: OptionType[];
options?: IOptionItem[];
}
export function ChoiceSetter({
@@ -0,0 +1,7 @@
import { FormItemComponentProps } from '@music163/tango-setting-form';
import { ClassNameInput } from '@music163/tango-ui';
import React from 'react';
export function ClassNameSetter({ value, onChange }: FormItemComponentProps<string>) {
return <ClassNameInput value={value} onChange={onChange} />;
}
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react';
import { Box, Text, css } from 'coral-system';
import { Dropdown, Button } from 'antd';
import { isValidExpressionCode } from '@music163/tango-core';
import { getValue, IVariableTreeNode, noop } from '@music163/tango-helpers';
import { getValue, interpolate, IVariableTreeNode, noop } from '@music163/tango-helpers';
import { CloseCircleFilled, MenuOutlined } from '@ant-design/icons';
import {
Panel,
@@ -34,6 +34,19 @@ export const jsonValueValidate = (value: string) => {
}
};
/**
*
* @param value
* @param template
* @returns
*/
export function getCallbackValue(value: string, template?: string) {
if (!value) {
return;
}
return template ? interpolate(template, { content: value }) : `() => {\n ${value}\n}`;
}
const suffixStyle = css`
display: flex;
align-items: center;
@@ -45,7 +58,7 @@ const suffixStyle = css`
}
`;
export interface ExpressionSetterProps extends FormItemComponentProps<string> {
export interface CodeSetterProps extends FormItemComponentProps<string> {
modalTitle?: string;
modalTip?: string;
autoCompleteOptions?: string[];
@@ -57,12 +70,13 @@ export interface ExpressionSetterProps extends FormItemComponentProps<string> {
expressionType?: 'cssObject';
}
export function ExpressionSetter(props: ExpressionSetterProps) {
export function CodeSetter(props: CodeSetterProps) {
const {
onChange,
modalTitle,
modalTip,
autoCompleteOptions,
template,
placeholder = '在这里输入代码',
value: valueProp,
status,
@@ -105,7 +119,7 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
<CloseCircleFilled
title="清空"
onClick={() => {
change('');
change(undefined);
}}
/>
)}
@@ -134,6 +148,7 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
subTitle={modalTip}
placeholder={placeholder}
autoCompleteOptions={autoCompleteOptions}
template={template}
newStoreTemplate={newStoreTemplate}
value={inputValue}
expressionType={expressionType}
@@ -166,6 +181,10 @@ export interface ExpressionPopoverProps extends InputCodeProps {
onOk?: (value: string) => void;
dataSource?: IVariableTreeNode[];
autoCompleteOptions?: string[];
/**
*
*/
template?: string;
/**
* store
*/
@@ -186,6 +205,7 @@ export function ExpressionPopover({
value,
dataSource,
autoCompleteOptions,
template,
newStoreTemplate = CODE_TEMPLATES.newStoreTemplate,
children,
expressionType,
@@ -245,19 +265,18 @@ export function ExpressionPopover({
autoCompleteOptions={autoCompleteOptions}
/>
{error ? (
<Text color="red" fontSize="12px" as="p">
<Text color="red" fontSize="12px" as="div">
</Text>
) : null}
{subTitle && (
<Text fontSize="12px" color="text3" as="p">
{subTitle}
<Box fontSize="12px" color="text2">
<Text display="block"></Text>
{subTitle && <Text display="block">{subTitle}</Text>}
<Text display="block">
javascript 使 jsx
</Text>
)}
<Text fontSize="12px" color="text3" as="p">
javascript 使 jsx
</Text>
</Box>
</Box>
<Panel
title="从变量列表中选中"
@@ -296,7 +315,11 @@ export function ExpressionPopover({
}
let str;
if (/^(stores|services)\./.test(node.key)) {
str = `tango.${node.key.replaceAll('.', '?.')}`;
// 从匹配到的第二个点开始替换为 ?.,因为第一个点是 stores 或 services
str = `tango.${node.key.replace(/(?<=\..*?)\./g, '?.')}`;
if (node.type === 'function') {
str = getCallbackValue(`${str}();`, template);
}
} else {
str = `${node.key}`;
}
+27 -31
View File
@@ -1,11 +1,11 @@
import React, { useCallback, useMemo, useState } from 'react';
import { css, Box, Text } from 'coral-system';
import { AutoComplete } from 'antd';
import { AutoComplete, Input } from 'antd';
import { ActionSelect } from '@music163/tango-ui';
import { FormItemComponentProps } from '@music163/tango-setting-form';
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
import { Dict, wrapCode } from '@music163/tango-helpers';
import { ExpressionPopover } from './expression-setter';
import { ExpressionPopover, getCallbackValue } from './code-setter';
import { value2code } from '@music163/tango-core';
enum EventAction {
@@ -31,7 +31,7 @@ export type EventSetterProps = FormItemComponentProps<string>;
* 事件监听函数绑定器
*/
export function EventSetter(props: EventSetterProps) {
const { value, onChange, modalTitle } = props;
const { value, onChange, modalTitle, modalTip, template } = props;
const [type, setType] = useState<EventAction>(); // 事件类型
const [temp, setTemp] = useState(''); // 二级暂存值
const { actionVariables, routeOptions } = useWorkspaceData();
@@ -59,7 +59,9 @@ export function EventSetter(props: EventSetterProps) {
label: (
<ExpressionPopover
title={modalTitle}
subTitle={modalTip}
value={value}
template={template}
onOk={(nextValue) => {
handleChange(nextValue);
}}
@@ -74,30 +76,34 @@ export function EventSetter(props: EventSetterProps) {
{ label: '打开弹窗', value: EventAction.OpenModal },
{ label: '关闭弹窗', value: EventAction.CloseModal },
],
[modalTitle, value, actionVariables, handleChange],
[modalTitle, value, actionVariables, template, handleChange],
);
const onAction = (key: string) => {
setType(key as EventAction); // 记录事件类型
setTemp(''); // 重置二级选项值
switch (key) {
case EventAction.ConsoleLog:
handleChange('(...args) => console.log(...args)');
break;
case EventAction.NoAction:
handleChange(undefined);
break;
default:
break;
if (key === EventAction.NoAction) {
handleChange(undefined);
return;
}
};
const actionText = getActionText(type, temp, code);
return (
<Box css={wrapperStyle}>
<ActionSelect options={options} onSelect={onAction} text={actionText} />
<ActionSelect options={options} onSelect={onAction} defaultText="请选择动作类型" />
{type === EventAction.ConsoleLog && (
<Input
placeholder="输入 Console.log 日志内容"
value={temp}
onChange={(e) => setTemp(e.target.value)}
onBlur={() => {
if (temp) {
handleChange(getExpressionValue(type, temp));
}
}}
/>
)}
{type === EventAction.NavigateTo && (
<AutoComplete
placeholder="选择或输入页面路由"
@@ -142,25 +148,15 @@ export function EventSetter(props: EventSetterProps) {
}
const handlerMap: Dict = {
[EventAction.OpenModal]: 'openModal',
[EventAction.CloseModal]: 'closeModal',
[EventAction.NavigateTo]: 'navigateTo',
[EventAction.ConsoleLog]: 'console.log',
[EventAction.OpenModal]: 'tango.openModal',
[EventAction.CloseModal]: 'tango.closeModal',
[EventAction.NavigateTo]: 'tango.navigateTo',
};
function getActionText(type: EventAction, temp: string, fallbackCode: string) {
let text;
if (handlerMap[type]) {
text = getExpressionValue(type, temp);
} else if (fallbackCode) {
text = fallbackCode;
}
text = text || '请选择';
return text;
}
function getExpressionValue(type: EventAction, value = '') {
const handler = handlerMap[type];
if (handler) {
return `() => tango.${handler}("${value}")`;
return getCallbackValue(`${handler}("${value}");`);
}
}
+10 -15
View File
@@ -5,14 +5,14 @@ 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 { CodeSetter } from './code-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-setter';
import { RenderSetter } from './render-setter';
import { RouterSetter } from './router-setter';
import {
SpacingSetter,
@@ -26,6 +26,7 @@ import {
FlexDirectionSetter,
} from './style-setter';
import { ChoiceSetter } from './choice-setter';
import { ClassNameSetter } from './classname-setter';
import { isValidExpressionCode } from '@music163/tango-core';
const codeValidate: IFormItemCreateOptions['validate'] = (value, field) => {
@@ -49,10 +50,14 @@ export const BUILT_IN_SETTERS: IFormItemCreateOptions[] = [
{
name: 'codeSetter',
alias: ['expSetter', 'expressionSetter'],
component: ExpressionSetter,
component: CodeSetter,
type: 'code',
validate: codeValidate,
},
{
name: 'classNameSetter',
component: ClassNameSetter,
},
{
name: 'radioGroupSetter',
alias: ['choiceSetter'],
@@ -126,22 +131,12 @@ export const BUILT_IN_SETTERS: IFormItemCreateOptions[] = [
component: PickerSetter,
},
{
name: 'renderPropsSetter',
name: 'renderSetter',
alias: ['renderPropsSetter'],
component: RenderSetter,
type: 'code',
validate: codeValidate,
},
{
name: 'tableCellSetter',
component: TableCellSetter,
type: 'code',
},
{
name: 'tableExpandableSetter',
component: TableExpandableSetter,
type: 'code',
validate: codeValidate,
},
{
name: 'routerSetter',
component: RouterSetter,
+10 -80
View File
@@ -2,22 +2,16 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { ActionSelect, InputCode } from '@music163/tango-ui';
import { FormItemComponentProps } from '@music163/tango-setting-form';
import { Box } from 'coral-system';
import { Dict } from '@music163/tango-helpers';
interface IRenderOption {
label: string;
value: string;
render?: string;
relatedImports?: string[];
}
import { Dict, IOptionItem } from '@music163/tango-helpers';
import { getCallbackValue } from './code-setter';
export interface RenderSetterProps {
text?: string;
options?: IRenderOption[];
fallbackOption?: IRenderOption;
options?: IOptionItem[];
fallbackOption?: IOptionItem;
}
const defaultOptions: IRenderOption[] = [
const defaultOptions: IOptionItem[] = [
{ label: '取消自定义', value: '' },
{ label: '自定义渲染', value: 'Box', render: '() => <Box></Box>' },
];
@@ -30,6 +24,7 @@ export function RenderSetter({
onChange,
text = '自定义渲染为',
options = defaultOptions,
template = `() => {{content}}`,
fallbackOption,
}: FormItemComponentProps & RenderSetterProps) {
const [inputValue, setInputValue] = useState(value || '');
@@ -47,13 +42,14 @@ export function RenderSetter({
const onSelect = useCallback(
(key: string) => {
const option = optionsMap[key] || fallbackOption;
if (option?.render) {
onChange(option.render, { relatedImports: option.relatedImports });
const next = option?.render || getCallbackValue(option.renderBody, template);
if (next) {
onChange(next, { relatedImports: option.relatedImports });
} else {
onChange(undefined);
}
},
[optionsMap, fallbackOption, onChange],
[optionsMap, fallbackOption, onChange, template],
);
return (
<Box>
@@ -68,69 +64,3 @@ export function RenderSetter({
</Box>
);
}
const getRender = (content: string, type?: 'tableCell' | 'tableExpandable') => {
let code;
switch (type) {
case 'tableCell':
code = `(value, record, index) => ${content}`;
break;
case 'tableExpandable':
code = `{
expandedRowRender: (record) => ${content},
rowExpandable: (record) => true
}`;
break;
default:
code = `() => ${content}`;
break;
}
return code;
};
const tableCellOptions: RenderSetterProps['options'] = [
{ label: '取消自定义', value: '' },
{
label: '自定义区域',
value: 'Box',
render: getRender('<Box></Box>', 'tableCell'),
relatedImports: ['Box'],
},
{
label: '标签',
value: 'Tag',
render: getRender('<Tag>tag</Tag>', 'tableCell'),
relatedImports: ['Tag'],
},
{
label: '按钮',
value: 'Button',
render: getRender('<Button>button</Button>', 'tableCell'),
relatedImports: ['Button'],
},
{
label: '图片',
value: 'Image',
render: getRender('<Image width={150} src="https://picsum.photos/100" />', 'tableCell'),
relatedImports: ['Image'],
},
];
const tableExpandableOptions: RenderSetterProps['options'] = [
{
label: '设置可展开行',
value: 'Box',
render: getRender('<Box></Box>', 'tableExpandable'),
relatedImports: ['Box'],
},
{ label: '取消可展开行', value: '' },
];
export function TableCellSetter(props: FormItemComponentProps) {
return <RenderSetter options={tableCellOptions} {...props} />;
}
// FIXME: 应该直接用 props 嵌套的模式
export function TableExpandableSetter(props: FormItemComponentProps) {
return <RenderSetter options={tableExpandableOptions} text="配置表格可展开行" {...props} />;
}
@@ -365,7 +365,7 @@ function MaterialGrid({ data }: MaterialProps) {
<IconFont className="material-icon" type={data.icon || 'icon-placeholder'} />
) : (
<Box className="material-icon">
<img src={icon} alt={data.name} />
<img src={icon} alt={data.name} height="32" />
</Box>
);
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.4](https://github.com/netease/tango/compare/@music163/tango-helpers@1.2.3...@music163/tango-helpers@1.2.4) (2024-09-09)
### Bug Fixes
- renderSetter with template ([#202](https://github.com/netease/tango/issues/202)) ([ea96872](https://github.com/netease/tango/commit/ea9687226123c839f7a9e001b4c01aa8315a135d))
## [1.2.3](https://github.com/netease/tango/compare/@music163/tango-helpers@1.2.2...@music163/tango-helpers@1.2.3) (2024-09-02)
### Bug Fixes
- add prop value template & fix components popover ([#201](https://github.com/netease/tango/issues/201)) ([c4e1ed6](https://github.com/netease/tango/commit/c4e1ed67f55deb17dae740559602cd0adcfc8eb4))
## [1.2.2](https://github.com/netease/tango/compare/@music163/tango-helpers@1.2.1...@music163/tango-helpers@1.2.2) (2024-08-05)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-helpers",
"version": "1.2.2",
"version": "1.2.4",
"description": "Shared types, helpers, and hooks of tango-apps",
"keywords": [
"shared",
+14
View File
@@ -125,3 +125,17 @@ export function parseDndId(str: string): DndIdParsedType {
id: str,
};
}
/**
* 替换模板中的变量
* @example interpolate('hello {{name}}', { name: 'world' }) -> 'hello world'
*
* @param template 带模板变量的字符串
* @param props 变量字典
* @returns 返回替换后的字符串
*/
export function interpolate(template: string, props: Record<string, any>) {
return template.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
return props[key];
});
}
+20 -4
View File
@@ -10,11 +10,19 @@ export type MousePoint = {
y: number;
};
export type OptionType = {
/**
* @deprecated 使用 IOptionItem 代替
*/
export type OptionType = IOptionItem<any>;
/**
* 列表项
*/
export interface IOptionItem<T = string> {
/**
* 值
*/
value: any;
value: T;
/**
* 显示的文本
*/
@@ -28,10 +36,18 @@ export type OptionType = {
*/
tip?: string;
/**
* 关联的导入
* 自定义渲染函数,仅用于 renderSetter
*/
render?: string;
/**
* 自定义渲染函数的 body(可以用来和 template 配合),仅用于 renderSetter
*/
renderBody?: string;
/**
* 关联的导入,仅用于 renderSetter
*/
relatedImports?: string[];
};
}
/**
* 变量树节点类型
+7 -2
View File
@@ -2,7 +2,7 @@
* 物料描述类型
*/
import { OptionType } from './advanced';
import { IOptionItem } from './advanced';
import { PartialRecord } from './base';
/**
@@ -88,6 +88,11 @@ export interface IComponentProp<T = any> {
* 自动补全的提示值,仅对 ExpressionSetter 有效
*/
autoCompleteOptions?: string[];
/**
* value 的模板,一般用于函数类型的属性,便于 setter 用来拼装返回值
* @example "(arg1, arg2, arg3) => { {{content}}}"
*/
template?: string;
/**
* 设置器
*/
@@ -108,7 +113,7 @@ export interface IComponentProp<T = any> {
/**
* 配置项的可选值,setterProps.options 的简写
*/
options?: OptionType[];
options?: IOptionItem[];
/**
* 是否禁用变量绑定
*/
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.13](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.12...@music163/tango-sandbox@1.0.13) (2024-09-09)
**Note:** Version bump only for package @music163/tango-sandbox
## [1.0.12](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.11...@music163/tango-sandbox@1.0.12) (2024-09-02)
**Note:** Version bump only for package @music163/tango-sandbox
## [1.0.11](https://github.com/netease/tango/compare/@music163/tango-sandbox@1.0.10...@music163/tango-sandbox@1.0.11) (2024-08-06)
**Note:** Version bump only for package @music163/tango-sandbox
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-sandbox",
"version": "1.0.11",
"version": "1.0.13",
"description": "sandbox of tango apps",
"author": "wwsun <ww.sun@outlook.com>",
"homepage": "",
@@ -29,8 +29,8 @@
},
"dependencies": {
"@ant-design/icons": "^4.8.0",
"@music163/tango-core": "^1.4.2",
"@music163/tango-helpers": "^1.2.2",
"@music163/tango-core": "^1.4.4",
"@music163/tango-helpers": "^1.2.4",
"crypto-js": "^4.1.1",
"lodash.isequal": "4.5.0",
"react-frame-component": "^5.2.4"
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.15](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.14...@music163/tango-setting-form@1.2.15) (2024-09-09)
### Bug Fixes
- renderSetter with template ([#202](https://github.com/netease/tango/issues/202)) ([ea96872](https://github.com/netease/tango/commit/ea9687226123c839f7a9e001b4c01aa8315a135d))
## [1.2.14](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.13...@music163/tango-setting-form@1.2.14) (2024-09-02)
### Bug Fixes
- add prop value template & fix components popover ([#201](https://github.com/netease/tango/issues/201)) ([c4e1ed6](https://github.com/netease/tango/commit/c4e1ed67f55deb17dae740559602cd0adcfc8eb4))
## [1.2.13](https://github.com/netease/tango/compare/@music163/tango-setting-form@1.2.12...@music163/tango-setting-form@1.2.13) (2024-08-16)
### Bug Fixes
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-setting-form",
"version": "1.2.13",
"version": "1.2.15",
"description": "setting form of tango-apps",
"author": "wwsun <ww.sun@outlook.com>",
"homepage": "",
@@ -28,9 +28,9 @@
},
"dependencies": {
"@ant-design/icons": "^4.8.0",
"@music163/tango-core": "^1.4.2",
"@music163/tango-helpers": "^1.2.2",
"@music163/tango-ui": "^1.4.3",
"@music163/tango-core": "^1.4.4",
"@music163/tango-helpers": "^1.2.4",
"@music163/tango-ui": "^1.4.5",
"antd": "^4.24.2",
"coral-system": "^1.0.5",
"mobx": "6.12.3",
+10 -3
View File
@@ -182,6 +182,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
placeholder,
docs,
autoCompleteOptions,
template,
setter: setterProp,
setterProps,
defaultValue,
@@ -230,14 +231,20 @@ export function createFormItem(options: IFormItemCreateOptions) {
// FIXME: 重新考虑这段代码的位置,外置这个逻辑
if (
['codeSetter', 'expressionSetter', 'expSetter', 'actionSetter', 'eventSetter'].includes(
setter,
)
[
'codeSetter',
'expressionSetter',
'expSetter',
'actionSetter',
'eventSetter',
'renderSetter',
].includes(setter)
) {
expProps = {
modalTitle: title,
modalTip: tip,
autoCompleteOptions,
template,
};
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.5](https://github.com/netease/tango/compare/@music163/tango-ui@1.4.4...@music163/tango-ui@1.4.5) (2024-09-09)
### Bug Fixes
- add classNameSetter and classNameInput ([#207](https://github.com/netease/tango/issues/207)) ([55602fc](https://github.com/netease/tango/commit/55602fc961b5c20d923891ce13d7a51588bd047f))
## [1.4.4](https://github.com/netease/tango/compare/@music163/tango-ui@1.4.3...@music163/tango-ui@1.4.4) (2024-09-02)
### Bug Fixes
- add prop value template & fix components popover ([#201](https://github.com/netease/tango/issues/201)) ([c4e1ed6](https://github.com/netease/tango/commit/c4e1ed67f55deb17dae740559602cd0adcfc8eb4))
## [1.4.3](https://github.com/netease/tango/compare/@music163/tango-ui@1.4.2...@music163/tango-ui@1.4.3) (2024-08-16)
### Bug Fixes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@music163/tango-ui",
"version": "1.4.3",
"version": "1.4.5",
"description": "ui widgets of tango",
"keywords": [
"react",
@@ -38,7 +38,7 @@
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lint": "^6.7.1",
"@codemirror/search": "^6.5.6",
"@music163/tango-helpers": "^1.2.2",
"@music163/tango-helpers": "^1.2.4",
"@uiw/react-codemirror": "^4.22.0",
"antd": "^4.24.2",
"classnames": "^2.5.1",
@@ -46,7 +46,7 @@
"eslint-linter-browserify": "^8.51.0",
"react-draggable": "^4.4.5",
"react-json-view": "^1.21.3",
"react-monaco-editor-lite": "^1.3.11"
"react-monaco-editor-lite": "^1.3.16"
},
"publishConfig": {
"access": "public",
+21 -12
View File
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { Box, css, Text } from 'coral-system';
import { Button, Dropdown, Input, Menu } from 'antd';
import { DownOutlined, PlusSquareOutlined } from '@ant-design/icons';
@@ -23,9 +23,13 @@ interface ActionSelectProps {
*/
onInputChange?: (value: string) => void;
/**
* 提示文本
* 受控的提示文本
*/
text?: string;
/**
* 默认的提示文本
*/
defaultText?: string;
/**
* 选择菜单时的回调
*/
@@ -50,11 +54,6 @@ const actionInputStyle = css`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--tango-colors-text3);
&:hover {
color: var(--tango-colors-text2);
}
}
.anticon-down {
@@ -71,15 +70,25 @@ const inputStyle = { width: 'calc(100% - 82px)' };
export function ActionSelect({
showInput = false,
defaultInputValue,
text,
text: textProp,
defaultText,
options = [],
onSelect,
onInputChange,
}: ActionSelectProps) {
const [text, setText] = useState<string>(defaultText);
const menu = (
<Menu onClick={({ key }) => onSelect(key)}>
<Menu>
{options.map((item) => (
<Menu.Item key={item.value}>
<Menu.Item
key={item.value}
onClick={() => {
onSelect?.(item.value);
if (!textProp) {
setText(item.label as string);
}
}}
>
<Box display="flex" alignItems="center" minWidth={200} columnGap="m">
<Box flex="1">{item.label}</Box>
<Text fontSize="12px" color="text.note">
@@ -122,8 +131,8 @@ export function ActionSelect({
mb="m"
css={actionInputStyle}
>
<Box className="action-input-text" color="text.body">
{text}
<Box className="action-input-text" color="text2">
{textProp ?? text}
</Box>
<DownOutlined />
</Box>
+312
View File
@@ -0,0 +1,312 @@
import React, { useState, useRef, KeyboardEvent, ChangeEvent, useEffect } from 'react';
import { Dropdown, Menu, Tag } from 'antd';
import styled from 'styled-components';
const InputWrapper = styled.div`
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 4px 11px;
border: 1px solid #d9d9d9;
border-radius: 2px;
min-height: 32px;
&:hover {
border-color: #40a9ff;
}
&:focus-within {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
`;
const Input = styled.input`
flex: 1;
border: none;
outline: none;
padding: 0;
font-size: 14px;
min-width: 50px;
height: 24px;
line-height: 24px;
`;
const StyledTag = styled(Tag)`
margin: 2px 4px 2px 0;
`;
// Tailwind CSS 基础类名列表
const tailwindClasses = [
// 布局
'container',
'flex',
'grid',
'block',
'inline',
'inline-block',
'hidden',
// 弹性布局
'flex-row',
'flex-col',
'flex-wrap',
'flex-nowrap',
'justify-start',
'justify-end',
'justify-center',
'justify-between',
'justify-around',
'items-start',
'items-end',
'items-center',
'items-baseline',
'items-stretch',
// 网格布局
'grid-cols-1',
'grid-cols-2',
'grid-cols-3',
'grid-cols-4',
'grid-cols-5',
'grid-cols-6',
'grid-cols-12',
// 间距
'p-0',
'p-1',
'p-2',
'p-3',
'p-4',
'p-5',
'p-6',
'p-8',
'p-10',
'p-12',
'p-16',
'p-20',
'm-0',
'm-1',
'm-2',
'm-3',
'm-4',
'm-5',
'm-6',
'm-8',
'm-10',
'm-12',
'm-16',
'm-20',
// 尺寸
'w-full',
'w-auto',
'w-1/2',
'w-1/3',
'w-2/3',
'w-1/4',
'w-3/4',
'h-full',
'h-auto',
'h-screen',
// 字体
'text-xs',
'text-sm',
'text-base',
'text-lg',
'text-xl',
'text-2xl',
'text-3xl',
'text-4xl',
'text-5xl',
'font-thin',
'font-light',
'font-normal',
'font-medium',
'font-semibold',
'font-bold',
'font-extrabold',
// 文本颜色
'text-black',
'text-white',
'text-gray-100',
'text-gray-200',
'text-gray-300',
'text-gray-400',
'text-gray-500',
'text-red-500',
'text-blue-500',
'text-green-500',
'text-yellow-500',
'text-purple-500',
'text-pink-500',
// 背景颜色
'bg-transparent',
'bg-black',
'bg-white',
'bg-gray-100',
'bg-gray-200',
'bg-gray-300',
'bg-gray-400',
'bg-gray-500',
'bg-red-500',
'bg-blue-500',
'bg-green-500',
'bg-yellow-500',
'bg-purple-500',
'bg-pink-500',
// 边框
'border',
'border-0',
'border-2',
'border-4',
'border-8',
'border-black',
'border-white',
'border-gray-300',
'border-gray-400',
'border-gray-500',
// 圆角
'rounded-none',
'rounded-sm',
'rounded',
'rounded-lg',
'rounded-full',
// 阴影
'shadow-sm',
'shadow',
'shadow-md',
'shadow-lg',
'shadow-xl',
'shadow-2xl',
'shadow-none',
// 不透明度
'opacity-0',
'opacity-25',
'opacity-50',
'opacity-75',
'opacity-100',
];
interface ClassNameInputProps {
value?: string;
defaultValue?: string;
onChange?: (value: string) => void;
}
export function ClassNameInput({ value, defaultValue, onChange }: ClassNameInputProps) {
const [inputValue, setInputValue] = useState('');
const [suggestions, setSuggestions] = useState<string[]>([]);
const [highlightedIndex, setHighlightedIndex] = useState(-1);
const inputRef = useRef<HTMLInputElement>(null);
const [internalValue, setInternalValue] = useState(defaultValue || '');
const isControlled = value !== undefined;
const classNames = (isControlled ? value : internalValue).split(' ').filter(Boolean);
useEffect(() => {
if (isControlled) {
setInternalValue(value);
}
}, [isControlled, value]);
const isValidClassName = (className: string) => {
return /^[a-zA-Z0-9_-]+(?::[a-zA-Z0-9_-]+)*$/.test(className);
};
const updateValue = (newClassNames: string[]) => {
const newValue = newClassNames.join(' ');
if (isControlled) {
onChange?.(newValue);
} else {
setInternalValue(newValue);
onChange?.(newValue);
}
};
const handleInputKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
event.preventDefault();
if (highlightedIndex >= 0 && highlightedIndex < suggestions.length) {
addClassName(suggestions[highlightedIndex]);
} else {
addClassName(inputValue.trim());
}
} else if (event.key === 'ArrowDown') {
event.preventDefault();
setHighlightedIndex((prev) => (prev < suggestions.length - 1 ? prev + 1 : 0));
} else if (event.key === 'ArrowUp') {
event.preventDefault();
setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : suggestions.length - 1));
} else if (event.key === 'Backspace' && inputValue === '' && classNames.length > 0) {
event.preventDefault();
const newClassNames = [...classNames];
newClassNames.pop();
updateValue(newClassNames);
}
};
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const input = event.target.value;
setInputValue(input);
if (input) {
const matchedSuggestions = tailwindClasses
.filter(
(className) =>
className.toLowerCase().includes(input.toLowerCase()) &&
!classNames.includes(className),
)
.slice(0, 10);
setSuggestions(matchedSuggestions);
setHighlightedIndex(-1);
} else {
setSuggestions([]);
}
};
const addClassName = (className: string) => {
if (className && !classNames.includes(className) && isValidClassName(className)) {
updateValue([...classNames, className]);
setInputValue('');
setSuggestions([]);
setHighlightedIndex(-1);
}
};
const removeClassName = (removedTag: string) => {
const newClassNames = classNames.filter((tag) => tag !== removedTag);
updateValue(newClassNames);
};
const handleSuggestionClick = (suggestion: string) => {
addClassName(suggestion);
};
const menu = (
<Menu>
{suggestions.map((suggestion, index) => (
<Menu.Item
key={index}
onClick={() => handleSuggestionClick(suggestion)}
className={index === highlightedIndex ? 'ant-dropdown-menu-item-active' : ''}
>
{suggestion}
</Menu.Item>
))}
</Menu>
);
return (
<Dropdown overlay={menu} visible={suggestions.length > 0} placement="bottomLeft">
<InputWrapper onClick={() => inputRef.current?.focus()}>
{classNames.map((className) => (
<StyledTag key={className} closable onClose={() => removeClassName(className)}>
{className}
</StyledTag>
))}
<Input
ref={inputRef}
type="text"
value={inputValue}
onKeyDown={handleInputKeyDown}
onChange={handleInputChange}
placeholder={classNames.length === 0 ? '输入 class 名称' : ''}
/>
</InputWrapper>
</Dropdown>
);
}
+1
View File
@@ -26,3 +26,4 @@ export * from './tag-select';
export * from './popover';
export * from './drag-panel';
export * from './context-action';
export * from './classname-input';
+7 -32
View File
@@ -16195,10 +16195,10 @@ react-merge-refs@^1.1.0:
resolved "https://registry.npmmirror.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz"
integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==
react-monaco-editor-lite@^1.3.11:
version "1.3.11"
resolved "https://registry.npmmirror.com/react-monaco-editor-lite/-/react-monaco-editor-lite-1.3.11.tgz#b81b681d23f3ca46f54d4e01f13781bcff6a4a03"
integrity sha512-+9xlIn98Yp2hD8OFjRNpQiBx+wLFzsvvT5EgNTxReFrDhFAPPjivdLUCiex1ktzpkGTdo3fxDFUokckvO7hVvQ==
react-monaco-editor-lite@^1.3.16:
version "1.3.16"
resolved "https://registry.npmmirror.com/react-monaco-editor-lite/-/react-monaco-editor-lite-1.3.16.tgz#066d744c7414df3b08b4cda65750057c2dd42f45"
integrity sha512-FP3DeGZ709XEz+ZRQiQvw/yAa9fhjJm0989kv85o6f0dKcNBmb4gCADGZdZpoqbYBrT+7wIXBxvH1LQ2N2AodQ==
dependencies:
monaco-editor "^0.38.0"
monaco-editor-textmate "^4.0.0"
@@ -17697,16 +17697,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -17819,7 +17810,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -17833,13 +17824,6 @@ strip-ansi@^3.0.1:
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
version "7.1.0"
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz"
@@ -19376,7 +19360,7 @@ workerpool@^9.1.1:
resolved "https://registry.npmmirror.com/workerpool/-/workerpool-9.1.1.tgz#9ba4d534a79a5517c1e1b9d1014151516829be8d"
integrity sha512-EFoFTSEo9m4V4wNrwzVRjxnf/E/oBpOzcI/R5CIugJhl9RsCiq525rszo4AtqcjQQoqFdu2E3H82AnbtpaQHvg==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -19394,15 +19378,6 @@ wrap-ansi@^6.0.1:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz"