packages/rc-components/rc-actions/src/actions.tsx (177 lines of code) (raw):

import React, { CSSProperties, ReactNode, ReactElement, createContext, useMemo, } from 'react' import { Dropdown, Menu, Icon } from '@alicloud/console-components' import { DropdownProps } from '@alicloud/console-components/types/dropdown' import { MenuProps } from '@alicloud/console-components/types/menu' import cls from 'classnames' import { GetFusionConfig, IFusionConfigProps, getWrapperProps, partitionWithThreshold, renderActionsChildren, } from './utils' import { LinkButton } from './linkButton' import { baseClassName, itemClassName, triggerClassName, expandMenuClassName, collapsedItemClassName, } from './constants' import { SActions, DropDownStyle } from './styles' import { IActionsProps } from './types/IActionsProps.type' export type { IActionsProps } const Context = createContext<{ menuProps: MenuProps dropdownProps: DropdownProps prefix: string }>({ menuProps: {}, dropdownProps: {}, prefix: 'next-', }) /** * 多个操作器(如按钮、链接)的布局容器。 * @public */ const Actions: React.FC<IActionsProps & IFusionConfigProps> = (props) => { const wrapperProps = getWrapperProps(props, { className: baseClassName }) const { children, threshold = 3, dataSource, expandTrigger = <Icon type="more" size="xs" tabIndex={0} />, expandTriggerType = 'click' as IActionsProps['expandTriggerType'], wrap = false, dropdownProps = {}, menuProps = {}, fusionConfig = {}, } = props /** * 根据threshold将子节点分为两组:`[前threshold个, 剩下的x个]` */ const partitionFn: PartitionFn = ((childrenArg) => partitionWithThreshold(childrenArg, threshold)) as PartitionFn /** * partitionFn已经将子元素分为了两组:`[前threshold个, 剩下的x个]`,它会将前面这组直接展示,并用竖线分开,将后面这组收敛在一个下拉菜单中 */ const renderItemsByParts: RenderItemsByParts = defaultRenderItemsByParts.bind( null, expandTrigger, expandTriggerType ) const { prefix = 'next-' } = fusionConfig const providerValue = useMemo( () => ({ dropdownProps, menuProps, prefix, }), [dropdownProps, menuProps, prefix] ) return ( <Context.Provider value={providerValue}> <SActions {...wrapperProps} wrap={wrap}> {renderActionsChildren( children || dataSource || [], partitionFn, renderItemsByParts )} </SActions> </Context.Provider> ) } export default GetFusionConfig(Actions) function defaultRenderDisplayedItems(items: ReactElement[]) { return items.map((item, index) => ( // eslint-disable-next-line react/no-array-index-key <span key={index} className={itemClassName}> {item} </span> )) } function defaultRenderShrinkItems( items: ReactElement[], config: { expandTriggerType: IActionsProps['expandTriggerType'] expandTrigger: IActionsProps['expandTrigger'] } ) { if (items.length === 0) { return null } const { expandTrigger, expandTriggerType } = config return ( <Context.Consumer> {({ prefix, dropdownProps, menuProps }) => { return ( <> <DropDownStyle prefix={prefix} /> <Dropdown {...dropdownProps} trigger={ <span className={triggerClassName}>{expandTrigger}</span> } triggerType={expandTriggerType} > <Menu {...menuProps} className={cls(expandMenuClassName, menuProps.className || '')} > {items.map((item, i) => { const { props: { disabled }, type, } = item if (type && (type as any).__windType === 'LinkButton') { return <Menu.Item {...item.props} /> } return ( // eslint-disable-next-line react/no-array-index-key <Menu.Item disabled={disabled} key={i}> <span className={collapsedItemClassName}>{item}</span> </Menu.Item> ) })} </Menu> </Dropdown> </> ) }} </Context.Consumer> ) } function defaultRenderItemsByParts( expandTrigger: ReactNode, expandTriggerType: IActionsProps['expandTriggerType'], displayedItems: ReactElement[], shrinkItems: ReactElement[] ) { return ( <> {defaultRenderDisplayedItems(displayedItems)} {defaultRenderShrinkItems(shrinkItems, { expandTrigger, expandTriggerType, })} </> ) } /** * 这个函数将子元素划分为多个“部分”。比如,可以分为【需要直接展示的元素】和【需要藏在下拉菜单的元素】。 * 用户可以通过这个API来自定义如何将子元素**过滤、排序、分类**。分好类以后会被传给 `IActionsProps.renderItemsByParts` 处理。 * @param children - Actions组件的所有子元素。 * @returns 经过过滤、排序、分类后的子元素。比如可以返回`[Array<需要直接展示的元素>, Array<需要隐藏在下拉菜单的元素>]`。 * @public */ export type PartitionFn = ( children: ReactElement[] ) => [ReactElement[], ReactElement[]] /** * 这个函数接受经过 {@link IActionsProps.partitionFn | partitionFn}处理的子元素。返回需要渲染的元素。 * 比如,如果 {@link IActionsProps.partitionFn | partitionFn}将子元素划分为`[Array<displayedItem>, Array<shrinkItems>]`,那么这个函数的签名就应该是:"(displayedItems: ReactElement[], shrinkItems: ReactElement[]) =\> ReactNode" * @param parts - 经过 {@link IActionsProps.partitionFn | partitionFn}分类以后的数组。 * @returns 最终需要渲染的ReactNode。 * @internal */ export type RenderItemsByParts = (...parts: ReactElement[][]) => ReactNode