基于 AntD 封装的可编辑 tabs

您所在的位置:网站首页 antd分页可编辑 基于 AntD 封装的可编辑 tabs

基于 AntD 封装的可编辑 tabs

2024-06-08 10:16| 来源: 网络整理| 查看: 265

AntD 的 Tabs 只支持新增/删除页签,而不支持修改,故我们需要扩展一下

# 功能

新增:点击最右侧“+”新增,默认名称为:“未命名”(可通过 defaultTabName 配置),如名称已重复,则命名为“未命名(1)”,括号中数字累加,新增后选中该 tabs

切换和改名:点击页签切换到该页签,聚焦并选中页签文字,并可进行更改页签名称,重名则改名失败,恢复原名称,toast 提示:名称已存在!;如清空页签文字,默认名称为“未命名”,重名与新增处理一致

删除:只有当前选中的页签显示删除图标:“X”。点击“X”可以删除页签

编辑时 Input 默认无边框,可通过传入 inputProps.bordered 配置;默认名称字符个数不限制,可通过传入 inputProps.maxLength 限制;inputProps 支持大部分 Input 属性;

默认名称全部显示,可通过传入 tabPaneWidth 控制宽度,默认超长显示 ...;宽度超出屏幕后,样式与 Tabs 一致

重名处理时,tabs 全部名称默认取当前所有页签名称,可通过传入  tabNames 自定义

页签前可通过传入 icon 展示额外状态:两种方式:

通过传入 statusIcon (type: Dom | Function) 方式,自定义程度高 组件预置几种状态,通过传入 statusIcon (type: string)  方式,受限于预置,自定义程度低 # 定义 Props import { TabsProps, TabPaneProps } from 'antd/lib/tabs' import RealInput, { RealInputProps } from '../RealInput' export interface EditableTabPaneProps extends Omit { tab: string key: string statusIcon?: 'error' | 'success' | React.ReactNode | Function style?: React.CSSProperties } export interface EditableTabsInputProps extends Omit {} export interface EditableTabsProps extends Omit { activeKey: string inputProps?: EditableTabsInputProps defaultTabName?: string tabNames?: string[] tabPaneWidth?: number children: React.ReactElement[] onEdit?: ( e: React.MouseEvent | React.KeyboardEvent | string, action: 'add' | 'remove' | 'rename', value?: string ) => void } 12345678910111213141516171819202122232425# 接收 Props,定义 state const { inputProps, defaultTabName = '未命名', tabNames, tabPaneWidth, children, activeKey, type = 'editable-card', onEdit, ...res } = props const [state, updateState] = useImmer({ tabNames: [], tabKey: Date.now() }) const inputRef = useRef(null) 123456789101112131415161718# 监听事件 useEffect(() => { if (tabNames) return updateState(draft => { draft.tabNames = children.map(item => item.props.tab) }) }, [children]) useEffect(() => { const { current } = inputRef current?.focus() current?.select() }, [activeKey]) 123456789101112# 处理原始 onEdit /** * 编辑 * @param e event | activeKey * @param action add | remove */ function handleEdit( e: string | React.MouseEvent | React.KeyboardEvent, action: 'add' | 'remove' ) { if (action === 'add') { const value = generateName(tabNames || state.tabNames, defaultTabName) onEdit?.(e, action, value) } else { onEdit?.(e, action) } } 12345678910111213141516# 渲染 TabPane /** * 渲染 TabPane * @returns TabPane */ function renderTabPane() { return children.map(item => { const { children, tab, disabled, style, ...res } = item.props return ( {children} ) }) } /** * 渲染 tab 标签头 * @param currentKey tab key * @param props EditableTabPaneProps * @returns tab 标签头 */ function tabDom(currentKey: string, props: GITabPaneProps) { const { disabled, tab, statusIcon, style } = props const active = activeKey === currentKey let icon = null let iconClass = null if (statusIcon) { switch (typeof statusIcon) { case 'string': icon = statusIconMap[statusIcon as string] iconClass = statusIcon break case 'function': icon = (statusIcon as Function)() break default: icon = statusIcon break } } let result = ( {icon} ) if (type === 'editable-card' && active && !disabled) { result = ( { // Prevent "space" blocking from rc-tabs: https://github.com/react-component/tabs/issues/309 e.stopPropagation() }} onChange={(value: string) => { handleInputChange(value, currentKey) }} {...inputProps} /> ) } return result } /** * input change * @param value value * @param currentKey tab key */ function handleInputChange(value: string, currentKey: string) { if (!value) { value = generateName(tabNames || state.tabNames, defaultTabName) onEdit?.(currentKey, 'rename', value) } else { if ((tabNames || state.tabNames).includes(value)) { message.warning('名称已存在!') updateState(draft => { draft.tabKey = Date.now() }) } else { onEdit?.(currentKey, 'rename', value) } } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394# 重命名逻辑 /** * 生成新增节点名称 * @param namePool * @param addName 新增的名字 * @returns */ export function generateName(namePool: string[], addName: string) { if (!namePool || !namePool.length) return addName const getName = addName => { let result = '' const repeatName = namePool.find(item => item === addName) if (repeatName) { if (/\(\d+\)$/.test(addName)) { let addStr = addName.match(/\(([^)]+)\)/)[1] result = addName.replace(/\(\d+\)$/, `(${Number(addStr) + 1})`) } else { result = addName + '(1)' } return getName(result) } else { return addName } } return getName(addName) } 1234567891011121314151617181920212223242526# 使用 const [state, updateState] = useImmer({ tabs: [ { tab: 'Tab 1', key: 'first', statusIcon: }, { tab: 'Tab 2', key: 'second', style: { width: '50px', color: 'red' } }, { tab: 'Tab 3', key: 'third', statusIcon: 'error' }, { tab: 'Tab 4', key: 'fourth' } ], contents: [1, 2, 3, 4] }) 123456789 {state.tabs.map((item, index) => ( content: {state.contents[index] || '找不到啦~'} ))} 1234567


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3