mirror of
https://github.com/NetEase/tango.git
synced 2026-08-29 02:01:34 +08:00
fix: refactor action, formHeader, selectionDropdown (#114)
* fix: add new action component * fix: refactor form header component and add documentation link * fix: refactor dropdown menu for selected item * fix: update * fix: add store variable in modelSetter * fix: code styles
This commit is contained in:
@@ -20,10 +20,12 @@
|
||||
"mobx-react-lite": "4.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ant-design/icons": "^4.8.0",
|
||||
"@storybook/addon-actions": "^6.5.14",
|
||||
"@storybook/addon-essentials": "^6.5.14",
|
||||
"@storybook/addon-links": "^6.5.14",
|
||||
"@storybook/react": "^6.5.14",
|
||||
"antd": "^4.24.2",
|
||||
"babel-plugin-styled-components": "^2.0.7",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import { Action } from '@music163/tango-ui';
|
||||
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { Space } from 'antd';
|
||||
|
||||
export default {
|
||||
title: 'UI/Action',
|
||||
component: Action,
|
||||
};
|
||||
|
||||
export const Basic = {
|
||||
args: {
|
||||
icon: <PlusOutlined />,
|
||||
tooltip: '添加服务函数',
|
||||
},
|
||||
};
|
||||
|
||||
export const Link = {
|
||||
args: {
|
||||
icon: <QuestionCircleOutlined />,
|
||||
tooltip: '查看帮助文档',
|
||||
href: 'https://music.163.com',
|
||||
},
|
||||
};
|
||||
|
||||
export const Outline = {
|
||||
args: {
|
||||
icon: <PlusOutlined />,
|
||||
tooltip: '添加服务函数',
|
||||
shape: 'outline',
|
||||
},
|
||||
};
|
||||
|
||||
export const Small = {
|
||||
args: {
|
||||
size: 'small',
|
||||
icon: <PlusOutlined />,
|
||||
tooltip: '添加服务函数',
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled = () => {
|
||||
return (
|
||||
<Space>
|
||||
<Action icon={<PlusOutlined />} tooltip="添加服务函数" disabled />
|
||||
<Action icon={<PlusOutlined />} tooltip="添加服务函数" disabled shape="outline" />
|
||||
<Action
|
||||
icon={<QuestionCircleOutlined />}
|
||||
tooltip="查看帮助文档"
|
||||
href="https://music.163.com"
|
||||
disabled
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export const List = () => {
|
||||
return (
|
||||
<Space>
|
||||
<Action icon={<PlusOutlined />} tooltip="添加服务函数" />
|
||||
<Action
|
||||
icon={<QuestionCircleOutlined />}
|
||||
tooltip="查看帮助文档"
|
||||
href="https://music.163.com"
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { IconButton, InputCode } from '@music163/tango-ui';
|
||||
import { Action, InputCode } from '@music163/tango-ui';
|
||||
import { BlockOutlined } from '@ant-design/icons';
|
||||
|
||||
export default {
|
||||
@@ -22,7 +22,7 @@ const context = {
|
||||
export function Basic() {
|
||||
return (
|
||||
<InputCode
|
||||
suffix={<IconButton icon={<BlockOutlined />} />}
|
||||
suffix={<Action icon={<BlockOutlined />} size="small" />}
|
||||
autoCompleteContext={{ tango: context }}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box, css, Group } from 'coral-system';
|
||||
import { IconButton, ToggleButton } from '@music163/tango-ui';
|
||||
import { Action, ToggleButton } from '@music163/tango-ui';
|
||||
import { observer, useDesigner } from '@music163/tango-context';
|
||||
import { Input } from 'antd';
|
||||
import {
|
||||
@@ -84,9 +84,9 @@ export class Navigator extends React.Component<NavigatorProps, NavigatorState> {
|
||||
css={navigatorStyle}
|
||||
>
|
||||
<Box>
|
||||
<IconButton icon={<ArrowLeftOutlined />} onClick={onBack} title="返回" />
|
||||
<IconButton icon={<ArrowRightOutlined />} onClick={onForward} title="前进" />
|
||||
<IconButton icon={<ReloadOutlined />} onClick={onRefresh} title="刷新" />
|
||||
<Action size="small" icon={<ArrowLeftOutlined />} onClick={onBack} title="返回" />
|
||||
<Action size="small" icon={<ArrowRightOutlined />} onClick={onForward} title="前进" />
|
||||
<Action size="small" icon={<ReloadOutlined />} onClick={onRefresh} title="刷新" />
|
||||
</Box>
|
||||
<Input
|
||||
className="navigatorInput"
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
IVariableTreeNode,
|
||||
} from '@music163/tango-helpers';
|
||||
import { CloseCircleFilled, ExpandAltOutlined } from '@ant-design/icons';
|
||||
import { IconButton, Panel, InputCode } from '@music163/tango-ui';
|
||||
import { Panel, InputCode, Action } from '@music163/tango-ui';
|
||||
import { FormItemComponentProps } from '@music163/tango-setting-form';
|
||||
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
|
||||
import { VariableTree } from '../components';
|
||||
@@ -130,10 +130,11 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<IconButton
|
||||
<Action
|
||||
tooltip="打开表达式变量选择面板"
|
||||
icon={<ExpandAltOutlined />}
|
||||
onClick={on}
|
||||
size="small"
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@ export function ModelSetter({
|
||||
if (!definedVariables.includes(keyPath)) {
|
||||
ret.showAddButton = false;
|
||||
ret.showRemoveButton = false;
|
||||
} else if (/^stores\.[a-zA-Z0-9]+$/.test(keyPath)) {
|
||||
ret.showAddButton = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -20,7 +20,7 @@ export const SettingPanel = observer((props: SettingPanelProps) => {
|
||||
const workspace = useWorkspace();
|
||||
const designer = useDesigner();
|
||||
|
||||
if (!designer.showRightPanel) {
|
||||
if (!designer.showRightPanel || !workspace.selectSource.isSelected) {
|
||||
return <Box className="SettingPanel" />;
|
||||
}
|
||||
|
||||
@@ -93,21 +93,19 @@ export const SettingPanel = observer((props: SettingPanelProps) => {
|
||||
headerProps={headerProps}
|
||||
className="SettingPanel"
|
||||
>
|
||||
{workspace.selectSource.isSelected ? (
|
||||
prototype ? (
|
||||
<SettingForm
|
||||
key={workspace.selectSource.first.id}
|
||||
model={formModel}
|
||||
prototype={prototype}
|
||||
showIdentifier={{
|
||||
identifierKey: 'tid',
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<Box p="m">没有找到与该组件相关的配置信息</Box>
|
||||
)
|
||||
) : null}
|
||||
{prototype ? (
|
||||
<SettingForm
|
||||
key={workspace.selectSource.first.id}
|
||||
model={formModel}
|
||||
prototype={prototype}
|
||||
showIdentifier={{
|
||||
identifierKey: 'tid',
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<Box p="m">没有找到与该组件相关的配置信息</Box>
|
||||
)}
|
||||
</Panel>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
import styled, { css, keyframes } from 'styled-components';
|
||||
import { Box, Button, Group, HTMLCoralProps } from 'coral-system';
|
||||
import { Dropdown, DropdownProps } from 'antd';
|
||||
import { PlusSquareOutlined, HolderOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { ISelectedItemData, isString, noop } from '@music163/tango-helpers';
|
||||
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
|
||||
import { IconButton } from '@music163/tango-ui';
|
||||
import { Action, IconFont } from '@music163/tango-ui';
|
||||
import { getDragGhostElement } from '../helpers';
|
||||
import { getWidget } from '../widgets';
|
||||
|
||||
@@ -58,6 +59,13 @@ const selectionBoxStyle = css`
|
||||
top: 0;
|
||||
`;
|
||||
|
||||
interface IInsertedData {
|
||||
name: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface SelectionBoxProps {
|
||||
/**
|
||||
* 是否显示操作按钮
|
||||
@@ -90,15 +98,20 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
||||
const isPage = prototype?.type === 'page';
|
||||
|
||||
// 如果声明了 childrenName,提供快捷子元素创建入口
|
||||
let insertedList: ISelectedItemData[] = [];
|
||||
let insertedList: IInsertedData[] = [];
|
||||
if (prototype?.childrenName) {
|
||||
const names = Array.isArray(prototype?.childrenName)
|
||||
? prototype.childrenName
|
||||
: [prototype.childrenName];
|
||||
insertedList = names.map((child) => ({
|
||||
id: child,
|
||||
name: child,
|
||||
}));
|
||||
insertedList = names.map((child) => {
|
||||
const proto = workspace.componentPrototypes.get(child);
|
||||
return {
|
||||
name: child,
|
||||
label: proto?.title || child,
|
||||
icon: proto?.icon,
|
||||
description: proto?.help,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let selectionHelpersAlign: SelectionHelperAlignType = 'top-right';
|
||||
@@ -166,41 +179,28 @@ function SelectionBox({ showActions, actions, data }: SelectionBoxProps) {
|
||||
}}
|
||||
/>
|
||||
{!isFromCurrentFile && (
|
||||
<IconButton
|
||||
<Action
|
||||
as="span"
|
||||
color="white"
|
||||
tooltip={`该节点来自其他文件(${data.filename})`}
|
||||
icon={<InfoCircleOutlined />}
|
||||
size="small"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{insertedList.length > 0 && (
|
||||
<SelectionHelper
|
||||
icon={<PlusSquareOutlined />}
|
||||
label={
|
||||
insertedList.length === 1 ? (
|
||||
<span
|
||||
onClick={() => {
|
||||
workspace.insertToSelectedNode(insertedList[0].name);
|
||||
}}
|
||||
>
|
||||
添加 {insertedList[0].name}
|
||||
</span>
|
||||
) : (
|
||||
<NameSelector
|
||||
label="添加"
|
||||
parents={insertedList}
|
||||
onSelect={(item) => {
|
||||
workspace.insertToSelectedNode(item.name);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<SelectionToolSet>{!isPage && actions}</SelectionToolSet>
|
||||
{insertedList.length > 0 && (
|
||||
<InsertedDropdown
|
||||
options={insertedList}
|
||||
onSelect={(name) => {
|
||||
workspace.insertToSelectedNode(name);
|
||||
}}
|
||||
>
|
||||
<SelectionHelper icon={<PlusSquareOutlined />} />
|
||||
</InsertedDropdown>
|
||||
)}
|
||||
</SelectionHelpers>
|
||||
)}
|
||||
</Box>
|
||||
@@ -370,3 +370,83 @@ const NameSelector = ({ label, parents = [], onSelect = noop }: NameSelectorProp
|
||||
</NameSelectorWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
interface InsertedDropdownProps extends DropdownProps {
|
||||
options?: IInsertedData[];
|
||||
onSelect?: (name: string) => void;
|
||||
}
|
||||
|
||||
function InsertedDropdown({ options = [], onSelect, ...props }: InsertedDropdownProps) {
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
dropdownRender={() => {
|
||||
return (
|
||||
<Box
|
||||
bg="#FFF"
|
||||
borderRadius="m"
|
||||
boxShadow="lowDown"
|
||||
border="solid"
|
||||
borderColor="line2"
|
||||
overflow="hidden"
|
||||
>
|
||||
<Box px="l" py="m" color="text2">
|
||||
为当前节点添加子元素
|
||||
</Box>
|
||||
<Box>
|
||||
{options.map((item) => (
|
||||
<InsertedItem
|
||||
key={item.name}
|
||||
label={item.label}
|
||||
icon={item.icon}
|
||||
description={item.description}
|
||||
onClick={() => onSelect?.(item.name)}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const insertedItemStyle = css`
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tango-colors-fill2);
|
||||
}
|
||||
`;
|
||||
|
||||
function InsertedItem({
|
||||
label,
|
||||
icon = 'icon-placeholder',
|
||||
description,
|
||||
}: HTMLCoralProps<'div'> & Omit<IInsertedData, 'name'>) {
|
||||
return (
|
||||
<Box display="flex" columnGap="m" px="l" py="m" fontSize="12px" css={insertedItemStyle}>
|
||||
<Box
|
||||
size="35px"
|
||||
fontSize="32px"
|
||||
background="fill1"
|
||||
border="1px solid"
|
||||
borderColor="line2"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
{icon.startsWith('icon-') ? (
|
||||
<IconFont className="material-icon" type={icon} />
|
||||
) : (
|
||||
<img src={icon} alt={label} />
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
<Box fontWeight="500">{label}</Box>
|
||||
<Box color="text2">{description}</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
export const logger = {
|
||||
group(title: string, body: string) {
|
||||
console.groupCollapsed(title);
|
||||
|
||||
@@ -231,18 +231,24 @@ export function FieldSet({ title, extra, children }: FieldSetProps) {
|
||||
);
|
||||
}
|
||||
|
||||
interface FormIdentifierProps {
|
||||
interface FormHeaderProps {
|
||||
title?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
subTitle?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function FormIdentifier({ title, children }: FormIdentifierProps) {
|
||||
export function FormHeader({ title, extra, subTitle }: FormHeaderProps) {
|
||||
return (
|
||||
<Box className="FormIdentifier" display="flex" alignItems="center">
|
||||
<Box fontSize="16px" fontWeight="500" mr="s">
|
||||
{title}
|
||||
<Box className="FormHeader">
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box flex="1" display="flex" alignItems="center">
|
||||
<Box fontSize="16px" fontWeight="500" mr="s">
|
||||
{title}
|
||||
</Box>
|
||||
{subTitle && <Box>{subTitle}</Box>}
|
||||
</Box>
|
||||
{extra && <Box>{extra}</Box>}
|
||||
</Box>
|
||||
<Box>{children}</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,14 +8,15 @@ import {
|
||||
filterTreeData,
|
||||
mapTreeData,
|
||||
} from '@music163/tango-helpers';
|
||||
import { Search, Tabs } from '@music163/tango-ui';
|
||||
import { Action, Search, Tabs } from '@music163/tango-ui';
|
||||
import { SettingFormItem, register } from './form-item';
|
||||
import { FormModelProvider, FormVariableProvider } from './context';
|
||||
import { FormModel, FormModelOptionsType } from './form-model';
|
||||
import { SettingFormObject } from './form-object';
|
||||
import { isValidNestProps } from './helpers';
|
||||
import { registerBuiltinSetters } from './setter';
|
||||
import { FormIdentifier } from './form-ui';
|
||||
import { FormHeader } from './form-ui';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
registerBuiltinSetters();
|
||||
|
||||
@@ -169,8 +170,8 @@ export function SettingForm({
|
||||
}, [keyword, componentProps, tabKey, model, showGroups, componentPropGroups]);
|
||||
|
||||
const renderProps = useCallback(
|
||||
(props: ComponentPropType[]) =>
|
||||
props.map((item) => {
|
||||
(list: ComponentPropType[]) =>
|
||||
list.map((item) => {
|
||||
const { getProp, ...rest } = item;
|
||||
const childProp = {
|
||||
...rest,
|
||||
@@ -201,9 +202,25 @@ export function SettingForm({
|
||||
<Box className="SettingFormHeader" position="sticky" top="0" bg="white" zIndex="2">
|
||||
<Box px="l" py="m">
|
||||
{showIdentifier && (
|
||||
<FormIdentifier title={prototype.title}>
|
||||
<SettingFormItem noStyle setter="idSetter" name={showIdentifier.identifierKey} />
|
||||
</FormIdentifier>
|
||||
<FormHeader
|
||||
title={prototype.title}
|
||||
subTitle={
|
||||
<SettingFormItem
|
||||
noStyle
|
||||
setter="idSetter"
|
||||
name={showIdentifier.identifierKey}
|
||||
/>
|
||||
}
|
||||
extra={
|
||||
prototype?.docs ? (
|
||||
<Action
|
||||
icon={<QuestionCircleOutlined />}
|
||||
tooltip="查看组件文档"
|
||||
href={prototype.docs}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{showSearch && (
|
||||
<Box className="SettingFormSearch" mt="m">
|
||||
|
||||
@@ -10,7 +10,7 @@ export function splitToPath(name: string) {
|
||||
}
|
||||
|
||||
function isNumericKey(key: string) {
|
||||
return String(Number.parseInt(key)) === key;
|
||||
return String(Number.parseInt(key, 10)) === key;
|
||||
}
|
||||
|
||||
/** lodash.get(...) for mobx observables */
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"@music163/tango-helpers": "^1.0.0-alpha.2",
|
||||
"@uiw/react-codemirror": "^4.21.21",
|
||||
"antd": "^4.24.2",
|
||||
"classnames": "^2.5.1",
|
||||
"coral-system": "^1.0.5",
|
||||
"eslint-linter-browserify": "^8.51.0",
|
||||
"react-json-view": "^1.21.3",
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { Tooltip } from 'antd';
|
||||
import React from 'react';
|
||||
import { css, Button, Link, HTMLCoralProps } from 'coral-system';
|
||||
import cx from 'classnames';
|
||||
|
||||
const actionStyle = css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
outline: none;
|
||||
border: 0;
|
||||
border-radius: var(--tango-radii-s);
|
||||
color: var(--tango-colors-text2);
|
||||
background-color: transparent;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tango-colors-fill2);
|
||||
}
|
||||
|
||||
&.small {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&.outline {
|
||||
background-color: var(--tango-colors-fill1);
|
||||
}
|
||||
|
||||
&.outline:hover {
|
||||
background-color: var(--tango-colors-fill3);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
color: var(--tango-colors-text4);
|
||||
}
|
||||
`;
|
||||
|
||||
export interface ActionProps extends HTMLCoralProps<any> {
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
icon?: React.ReactNode;
|
||||
/**
|
||||
* 提示文本
|
||||
*/
|
||||
tooltip?: string;
|
||||
/**
|
||||
* 超链接
|
||||
*/
|
||||
href?: string;
|
||||
/**
|
||||
* 尺寸
|
||||
*/
|
||||
size?: 'small';
|
||||
/**
|
||||
* 外观:outline-有边框
|
||||
*/
|
||||
shape?: 'outline';
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* 自定义样式
|
||||
*/
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Action({
|
||||
icon,
|
||||
tooltip,
|
||||
href,
|
||||
size,
|
||||
shape,
|
||||
disabled,
|
||||
className,
|
||||
children,
|
||||
...rest
|
||||
}: ActionProps) {
|
||||
const classNames = cx(
|
||||
{
|
||||
'tango-action': true,
|
||||
'link-action': !!href,
|
||||
small: size === 'small',
|
||||
outline: shape === 'outline',
|
||||
disabled,
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
let ret;
|
||||
if (href) {
|
||||
ret = (
|
||||
<Link className={classNames} css={actionStyle} href={href} isExternal {...rest}>
|
||||
{icon ?? children}
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
ret = (
|
||||
<Button type="button" className={classNames} css={actionStyle} {...rest}>
|
||||
{icon ?? children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (tooltip) {
|
||||
return <Tooltip title={tooltip}>{ret}</Tooltip>;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box } from 'coral-system';
|
||||
import { logger } from '@music163/tango-helpers';
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
children: React.ReactNode;
|
||||
@@ -10,20 +11,20 @@ interface ErrorBoundaryState {
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||
static getDerivedStateFromError(error: any) {
|
||||
// Update state so the next render will show the fallback UI.
|
||||
logger.error(error);
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
constructor(props: ErrorBoundaryProps) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: any) {
|
||||
// Update state so the next render will show the fallback UI.
|
||||
console.log(error);
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error: any, errorInfo: any) {
|
||||
// You can also log the error to an error reporting service
|
||||
console.error(error, errorInfo);
|
||||
logger.error(error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Button, Link, css, HTMLCoralProps } from 'coral-system';
|
||||
import { Tooltip } from 'antd';
|
||||
|
||||
const btnStyle = css`
|
||||
&:hover {
|
||||
color: var(--tango-colors-brand);
|
||||
}
|
||||
`;
|
||||
|
||||
export interface IconButtonProps extends HTMLCoralProps<'button'> {
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
icon?: React.ReactNode;
|
||||
/**
|
||||
* 气泡提示
|
||||
*/
|
||||
tooltip?: string;
|
||||
/**
|
||||
* 点击跳转的链接地址
|
||||
*/
|
||||
href?: string;
|
||||
}
|
||||
|
||||
export function IconButton({ tooltip, icon, href, children, ...rest }: IconButtonProps) {
|
||||
const btn = href ? (
|
||||
<Link href={href} target="_blank" color="text2" css={btnStyle} {...(rest as any)}>
|
||||
{icon ?? children}
|
||||
</Link>
|
||||
) : (
|
||||
<Button border="0" bg="transparent" color="text2" px="s" css={btnStyle} {...rest}>
|
||||
{icon ?? children}
|
||||
</Button>
|
||||
);
|
||||
if (tooltip) {
|
||||
return (
|
||||
<Tooltip title={tooltip} placement="topRight">
|
||||
{btn}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return btn;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
export * from './icons';
|
||||
export * from './action';
|
||||
export { Action as IconButton } from './action'; // 兼容旧版本
|
||||
export * from './action-select';
|
||||
export * from './code-editor';
|
||||
export * from './chat-input';
|
||||
@@ -9,7 +11,6 @@ export * from './iconfont';
|
||||
export * from './menu';
|
||||
export * from './panel';
|
||||
export * from './toggle-button';
|
||||
export * from './icon-button';
|
||||
export * from './input-code';
|
||||
export * from './input-list';
|
||||
export * from './search';
|
||||
|
||||
@@ -29,13 +29,13 @@ const eslintConfig = {
|
||||
};
|
||||
|
||||
function completeProperties(from: number, object: Object) {
|
||||
const options = [];
|
||||
for (const name in object) {
|
||||
const options: object[] = [];
|
||||
Object.entries(object).forEach(([key, val]) => {
|
||||
options.push({
|
||||
label: name,
|
||||
type: typeof object[name] === 'function' ? 'function' : 'variable',
|
||||
label: key,
|
||||
type: typeof val === 'function' ? 'function' : 'variable',
|
||||
});
|
||||
}
|
||||
});
|
||||
return {
|
||||
from,
|
||||
options,
|
||||
@@ -58,10 +58,10 @@ function buildAutoComplete(scope: any = window, customOptions: Array<{ label: st
|
||||
|
||||
if (
|
||||
completePropertyAfter.includes(nodeBefore.name) &&
|
||||
nodeBefore.parent?.name == 'MemberExpression'
|
||||
nodeBefore.parent?.name === 'MemberExpression'
|
||||
) {
|
||||
const object = nodeBefore.parent.getChild('Expression');
|
||||
if (object?.name == 'VariableName') {
|
||||
if (object?.name === 'VariableName') {
|
||||
const from = /\./.test(nodeBefore.name) ? nodeBefore.to : nodeBefore.from;
|
||||
const variableName = context.state.sliceDoc(object.from, object.to);
|
||||
if (typeof scope[variableName] === 'object') {
|
||||
@@ -83,11 +83,11 @@ function buildAutoComplete(scope: any = window, customOptions: Array<{ label: st
|
||||
return completeProperties(from, result);
|
||||
}
|
||||
}
|
||||
} else if (nodeBefore.name == 'VariableName') {
|
||||
} else if (nodeBefore.name === 'VariableName') {
|
||||
return completeProperties(nodeBefore.from, scope);
|
||||
} else if (context.explicit && !dontCompleteIn.includes(nodeBefore.name)) {
|
||||
return completeProperties(context.pos, scope);
|
||||
} else if (!context.explicit && nodeBefore.name == '{') {
|
||||
} else if (!context.explicit && nodeBefore.name === '{') {
|
||||
return {
|
||||
from: context.pos,
|
||||
options: customOptions,
|
||||
@@ -155,7 +155,6 @@ export function InputCode({
|
||||
}, [autoCompleteContext, autoCompleteOptions]);
|
||||
const extensions = [javascript({ jsx: true }), globalJavaScriptCompletions];
|
||||
if (enableESLint) {
|
||||
// @ts-ignore
|
||||
extensions.push(lintGutter(), linter(esLint(new eslint.Linter(), eslintConfig)));
|
||||
}
|
||||
const { rootStyle, codeSetup } = useInputCode({ shape, status, showLineNumbers, showFoldGutter });
|
||||
|
||||
@@ -6,6 +6,7 @@ import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
export interface InputListProps {
|
||||
value?: string[];
|
||||
onChange?: (value: string[]) => void;
|
||||
// TODO: 支持 unique value
|
||||
}
|
||||
|
||||
export function InputList({ value = [], onChange }: InputListProps) {
|
||||
|
||||
@@ -154,6 +154,9 @@ export interface ToggleButtonProps extends Omit<HTMLCoralProps<'button'>, 'type'
|
||||
dropdownProps?: DropDownProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: 使用 Action 重构
|
||||
*/
|
||||
export function ToggleButton(props: ToggleButtonProps) {
|
||||
const {
|
||||
size = 'm',
|
||||
|
||||
@@ -6760,6 +6760,11 @@ classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classna
|
||||
resolved "https://registry.npmmirror.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
||||
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
||||
|
||||
classnames@^2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
|
||||
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
|
||||
|
||||
clean-css@^4.2.3:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.npmmirror.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178"
|
||||
|
||||
Reference in New Issue
Block a user