import classnames from 'classnames'; import { TOKEN_COLOR_BORDER_TAG_DISABLED } from '@swipebox/morphe-design-tokens'; import Box from './Box'; import { useDefaultLabelContext } from './contexts/DefaultLabelProvider'; import focusStyles from './Focus.css'; import Icon from './Icon'; import IconCompact from './IconCompact'; import styles from './Tag.css'; import touchableStyles from './TapArea.css'; import Text from './Text'; import useFocusVisible from './useFocusVisible'; const backgroundColorByType = Object.freeze({ default: 'secondary', error: 'errorBase', warning: 'warningBase', }); const foregroundColorByType = Object.freeze({ default: 'default', error: 'inverse', warning: 'inverse', }); const iconsByType = Object.freeze({ error: 'compact-workflow-status-problem', warning: 'compact-workflow-status-warning', }); type Props = { /** * If your app uses DefaultLabelProvider, a default value for this label will be used. Using this prop will override the default label value with a more specific label if desired. This populates the `aria-label` on the remove icon. */ accessibilityRemoveIconLabel?: string; /** * Disabled tags appear inactive and cannot be interacted with. */ disabled?: boolean; /** * Callback fired when the user dismisses the tag. This handler should take care of state updates to no longer render the Tag. */ onRemove: (arg1: { event: React.MouseEvent }) => void; /** * Size of the Tag. Default is `md`. See [size variant](https://gestalt.pinterest.systems/web/tag#Size) for more details. */ size?: 'sm' | 'md' | 'lg'; /** * Short text to render inside the Tag. */ text: string; /** * Communicate a "warning" or "error" state to the user, with an accompanying icon and specific background color. */ type?: 'default' | 'error' | 'warning'; }; const applyDensityTheme = (size: 'sm' | 'md' | 'lg') => { switch (size) { case 'sm': return { rounding: 1, paddingX: 1, paddingY: undefined, height: 24, iconSize: 12, removeIconGap: 2, removeIconSize: 8, fontSize: '100', }; case 'lg': return { rounding: 3, paddingX: 4, paddingY: 3, height: 48, iconSize: 16, removeIconGap: 4, removeIconSize: 8, fontSize: '200', }; case 'md': default: return { rounding: 2, paddingX: 2, paddingY: 1, height: 32, iconSize: 12, removeIconGap: 3, removeIconSize: 8, fontSize: '200', }; } }; /** * [Tags](https://gestalt.pinterest.systems/web/tag) can be used to categorize, classify or filter content, usually via keywords. They can appear within [TextFields](https://gestalt.pinterest.systems/web/textfield#tagsExample), [TextAreas](https://gestalt.pinterest.systems/web/textarea#tagsExample), [ComboBox](https://gestalt.pinterest.systems/web/combobox#Tags) or as standalone components. * * ![Tag light mode](https://raw.githubusercontent.com/pinterest/gestalt/master/playwright/visual-test/Tag.spec.ts-snapshots/Tag-chromium-darwin.png) * ![Tag dark mode](https://raw.githubusercontent.com/pinterest/gestalt/master/playwright/visual-test/Tag-dark.spec.ts-snapshots/Tag-dark-chromium-darwin.png) */ export default function Tag({ accessibilityRemoveIconLabel, disabled = false, onRemove, size = 'md', text, type = 'default', }: Props) { const hasIcon = ['error', 'warning'].includes(type); const bgColor = backgroundColorByType[type]; const fgColor = disabled && !hasIcon ? 'subtle' : foregroundColorByType[type]; const { accessibilityErrorIconLabel, accessibilityRemoveIconLabel: accessibilityRemoveIconLabelDefault, accessibilityWarningIconLabel, } = useDefaultLabelContext('Tag'); const accessibilityLabels = { error: accessibilityErrorIconLabel, warning: accessibilityWarningIconLabel, } as const; const { isFocusVisible } = useFocusVisible(); const removeIconClasses = classnames( styles.closeButton, styles[type], touchableStyles.tapTransition, { [focusStyles.hideOutline]: !isFocusVisible, [focusStyles.accessibilityOutline]: isFocusVisible, }, styles[size], ); const { height, rounding, paddingX, paddingY, fontSize, iconSize, removeIconSize } = applyDensityTheme(size); return ( {(type === 'error' || type === 'warning') && ( )} {text} {!disabled && ( // @ts-expect-error - TS2322 - Type '(arg1: { event: MouseEvent; }) => void' is not assignable to type 'MouseEventHandler'. )} ); } Tag.displayName = 'Tag';