需求
类似designable-antd平台的这个切换功能:
- 点击右边按钮,可以切换不同的输入框样式。
实现
- 维护一个type-component的类型数组
- 遍历数组,找到当前组件类型并渲染
- 当切换输入框样式的时候,获取下一个组件类型并渲染。如果为最后一个组件,则重新循环为第一个组件类型并展示
- 初始化的时候,需要根据value校验当前组件的类型,并渲染
polyInput.tsx:
import React, { useEffect, useState, useMemo } from 'react';
import { Button, Row, Tooltip } from 'antd';
import { useField } from '@formily/react';
import { Field } from '@formily/core';
export interface IInput {
value: any;
onChange: (value: any) => void;
}
export interface IPolyType {
type: string;
icon: JSX.Element;
component?: any;
checker: (value: any) => boolean;
}
export type PolyTypes = IPolyType[];
export function createPolyInput(polyTypes: PolyTypes = []): React.FC<IInput> {
return ({ value, ...props }) => {
const [curType, setCurType] = useState(polyTypes[0]?.type);
const field: Field = useField();
const curTypeItem = polyTypes.filter((i) => i.type === curType)[0];
useEffect(() => {
// 设置默认类型
polyTypes?.forEach(({ checker, type }) => {
if (checker(value)) { // here
setCurType(type);
}
});
}, []);
const getNextType = () => {
const currentIndex = polyTypes?.findIndex(({ type }) => type === curType);
const nextIndex = currentIndex + 1 > polyTypes?.length - 1 ? 0 : currentIndex + 1;
return polyTypes[nextIndex];
};
const renderContent = useMemo(() => {
return (
<>
{curTypeItem?.component && (
<div style={{ flex: 1, marginRight: 5 }}>
{/* @ts-ignore */}
{React.createElement(curTypeItem?.component, {
...props, // here
value,
})}
</div>
)}
<Tooltip title={curTypeItem.type}>
<Button
icon={curTypeItem.icon}
onClick={() => {
const nextType = getNextType(); // here
if (nextType.type === curType) {
return;
}
setCurType(nextType?.type);
field?.setValue(undefined); // 切换类型的时候,赋空值
}}
/>
</Tooltip>
</>
);
}, [curTypeItem]);
return <Row justify="start">{renderContent}</Row>;
};
}
使用:
import { FieldStringOutlined, SwitcherOutlined } from '@ant-design/icons';
import { isObject } from 'lodash';
import { Switch } from '@formily/antd';
import LanguageTextRule, { LanguageComponentType } from '../LanguageTextRule';
import { createPolyInput } from '../PolyInput';
export enum ValidatorType {
BOOLEAN = 'boolean',
LANGUAGE_MESSAGE = 'message',
}
export const isBoolean = (value: any) => typeof value === 'boolean';
export const isObj = (value: any) => isObject(value);
export const ValidatorTypesMap = [
{
type: ValidatorType.BOOLEAN,
icon: <SwitcherOutlined />,
component: Switch,
checker: isBoolean,
},
{
type: ValidatorType.LANGUAGE_MESSAGE,
icon: <FieldStringOutlined />,
component: (props: any) => {
return (
<LanguageTextRule
title="Error Message"
componentType={LanguageComponentType.TEXT}
buttonText="+ Error Message"
isModal
{...props}
/>
);
},
checker: isObj,
},
];
export default createPolyInput(ValidatorTypesMap);