import * as React from 'react'; import styles from '@patternfly/react-styles/css/components/Label/label-group'; import labelStyles from '@patternfly/react-styles/css/components/Label/label'; import { css } from '@patternfly/react-styles'; import { Button } from '../Button'; import { Label } from './Label'; import { Tooltip, TooltipPosition } from '../Tooltip'; import TimesCircleIcon from '@patternfly/react-icons/dist/esm/icons/times-circle-icon'; import { fillTemplate } from '../../helpers'; import { GenerateId } from '../../helpers/GenerateId/GenerateId'; export interface LabelGroupProps extends React.HTMLProps { /** Content rendered inside the label group. Should be elements. */ children?: React.ReactNode; /** Additional classes added to the label item */ className?: string; /** Flag for having the label group default to expanded */ defaultIsOpen?: boolean; /** Customizable "Show Less" text string */ expandedText?: string; /** Customizeable template string. Use variable "${remaining}" for the overflow label count. */ collapsedText?: string; /** Category name text for the label group category. If this prop is supplied the label group with have a label and category styling applied */ categoryName?: string; /** Aria label for label group that does not have a category name */ 'aria-label'?: string; /** Set number of labels to show before overflow */ numLabels?: number; /** Flag if label group can be closed */ isClosable?: boolean; /** Flag indicating the labels in the group are compact */ isCompact?: boolean; /** Aria label for close button */ closeBtnAriaLabel?: string; /** Function that is called when clicking on the label group close button */ onClick?: (event: React.MouseEvent) => void; /** Position of the tooltip which is displayed if the category name text is longer */ tooltipPosition?: | TooltipPosition | 'auto' | 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end'; /** Flag to implement a vertical layout */ isVertical?: boolean; /** Flag indicating contained labels are editable. Allows spacing for a text input after the labels. */ isEditable?: boolean; /** Flag indicating the editable label group should be appended with a textarea. */ hasEditableTextArea?: boolean; /** Additional props passed to the editable textarea. */ editableTextAreaProps?: any; /** Control for adding new labels */ addLabelControl?: React.ReactNode; } interface LabelGroupState { isOpen: boolean; isTooltipVisible: boolean; } class LabelGroup extends React.Component { static displayName = 'LabelGroup'; constructor(props: LabelGroupProps) { super(props); this.state = { isOpen: this.props.defaultIsOpen, isTooltipVisible: false }; } private headingRef = React.createRef(); static defaultProps: LabelGroupProps = { expandedText: 'Show Less', collapsedText: '${remaining} more', categoryName: '', defaultIsOpen: false, numLabels: 3, isClosable: false, isCompact: false, // eslint-disable-next-line @typescript-eslint/no-unused-vars onClick: (_e: React.MouseEvent) => undefined as any, closeBtnAriaLabel: 'Close label group', tooltipPosition: 'top', 'aria-label': 'Label group category', isVertical: false, isEditable: false, hasEditableTextArea: false }; componentDidMount() { this.setState({ isTooltipVisible: Boolean( this.headingRef.current && this.headingRef.current.offsetWidth < this.headingRef.current.scrollWidth ) }); } toggleCollapse = () => { this.setState((prevState) => ({ isOpen: !prevState.isOpen, isTooltipVisible: Boolean( this.headingRef.current && this.headingRef.current.offsetWidth < this.headingRef.current.scrollWidth ) })); }; renderLabel(id: string) { const { categoryName, tooltipPosition } = this.props; const { isTooltipVisible } = this.state; return isTooltipVisible ? ( {categoryName} ) : ( {categoryName} ); } render() { const { categoryName, children, className, isClosable, isCompact, closeBtnAriaLabel, 'aria-label': ariaLabel, onClick, numLabels, expandedText, collapsedText, /* eslint-disable @typescript-eslint/no-unused-vars */ defaultIsOpen, tooltipPosition, isVertical, isEditable, hasEditableTextArea, editableTextAreaProps, addLabelControl, /* eslint-enable @typescript-eslint/no-unused-vars */ ...rest } = this.props; const { isOpen } = this.state; const renderedChildren = React.Children.toArray(children); const numChildren = renderedChildren.length; const collapsedTextResult = fillTemplate(collapsedText as string, { remaining: numChildren - numLabels }); const renderLabelGroup = (id: string) => { const labelArray = !isOpen ? renderedChildren.slice(0, numLabels) : renderedChildren; const content = ( {categoryName && this.renderLabel(id)}

{labelArray.map((child, i) => (
- {child}
))} {numChildren > numLabels && (
- {isOpen ? expandedText : collapsedTextResult}
)} {addLabelControl &&
- {addLabelControl}
} {isEditable && hasEditableTextArea && (
)}

); const close = (
); return (
{
{content}
} {isClosable && close}
); }; return numChildren === 0 && addLabelControl === undefined ? null : ( {(randomId) => renderLabelGroup(this.props.id || randomId)} ); } } export { LabelGroup };