123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- import {cloneElement, isValidElement} from 'react';
- import styled from '@emotion/styled';
- import Button from 'sentry/components/button';
- import ExternalLink from 'sentry/components/links/externalLink';
- import Link, {LinkProps} from 'sentry/components/links/link';
- import Tooltip from 'sentry/components/tooltip';
- import {IconClose, IconOpen} from 'sentry/icons';
- import {t} from 'sentry/locale';
- import space from 'sentry/styles/space';
- import {defined} from 'sentry/utils';
- import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
- import theme, {Color, Theme} from 'sentry/utils/theme';
- const TAG_HEIGHT = '20px';
- interface Props extends React.HTMLAttributes<HTMLSpanElement> {
- /**
- * Makes the tag clickable. Use for external links.
- * If no icon is passed, it defaults to IconOpen (can be removed by passing icon={null})
- */
- href?: string;
- /**
- * Icon on the left side.
- */
- icon?: React.ReactNode;
- /**
- * Triggered when the item is clicked
- */
- onClick?: (eventKey: any) => void;
- /**
- * Shows clickable IconClose on the right side.
- */
- onDismiss?: () => void;
- /**
- * Max width of the tag's text
- */
- textMaxWidth?: number;
- /**
- * Makes the tag clickable. Use for internal links handled by react router.
- * If no icon is passed, it defaults to IconOpen (can be removed by passing icon={null})
- */
- to?: LinkProps['to'];
- /**
- * Text to show up on a hover.
- */
- tooltipText?: React.ComponentProps<typeof Tooltip>['title'];
- /**
- * Dictates color scheme of the tag.
- */
- type?: keyof Theme['tag'];
- }
- function Tag({
- type = 'default',
- icon,
- tooltipText,
- to,
- onClick,
- href,
- onDismiss,
- children,
- textMaxWidth = 150,
- ...props
- }: Props) {
- const iconsProps = {
- size: '11px',
- color: theme.tag[type].iconColor as Color,
- };
- const tag = (
- <Tooltip title={tooltipText} containerDisplayMode="inline-flex">
- <Background type={type}>
- {tagIcon()}
- <Text type={type} maxWidth={textMaxWidth}>
- {children}
- </Text>
- {defined(onDismiss) && (
- <DismissButton
- onClick={handleDismiss}
- size="zero"
- priority="link"
- aria-label={t('Dismiss')}
- >
- <IconClose isCircled {...iconsProps} />
- </DismissButton>
- )}
- </Background>
- </Tooltip>
- );
- function handleDismiss(event: React.MouseEvent) {
- event.preventDefault();
- onDismiss?.();
- }
- const trackClickEvent = () => {
- trackAdvancedAnalyticsEvent('tag.clicked', {
- is_clickable: defined(onClick) || defined(to) || defined(href),
- organization: null,
- });
- };
- function tagIcon() {
- if (isValidElement(icon)) {
- return <IconWrapper>{cloneElement(icon, {...iconsProps})}</IconWrapper>;
- }
- if ((defined(href) || defined(to)) && icon === undefined) {
- return (
- <IconWrapper>
- <IconOpen {...iconsProps} />
- </IconWrapper>
- );
- }
- return null;
- }
- function tagWithParent() {
- if (defined(href)) {
- return <ExternalLink href={href}>{tag}</ExternalLink>;
- }
- if (defined(to) && defined(onClick)) {
- return (
- <Link to={to} onClick={onClick}>
- {tag}
- </Link>
- );
- }
- if (defined(to)) {
- return <Link to={to}>{tag}</Link>;
- }
- return tag;
- }
- return (
- <TagWrapper {...props} onClick={trackClickEvent}>
- {tagWithParent()}
- </TagWrapper>
- );
- }
- const TagWrapper = styled('span')`
- font-size: ${p => p.theme.fontSizeSmall};
- `;
- export const Background = styled('div')<{type: keyof Theme['tag']}>`
- display: inline-flex;
- align-items: center;
- height: ${TAG_HEIGHT};
- border-radius: ${TAG_HEIGHT};
- background-color: ${p => p.theme.tag[p.type].background};
- border: solid 1px ${p => p.theme.tag[p.type].border};
- padding: 0 ${space(1)};
- `;
- const IconWrapper = styled('span')`
- margin-right: ${space(0.5)};
- display: inline-flex;
- `;
- const Text = styled('span')<{maxWidth: number; type: keyof Theme['tag']}>`
- color: ${p =>
- ['black', 'white'].includes(p.type)
- ? p.theme.tag[p.type].iconColor
- : p.theme.textColor};
- max-width: ${p => p.maxWidth}px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- line-height: ${TAG_HEIGHT};
- `;
- const DismissButton = styled(Button)`
- margin-left: ${space(0.5)};
- border: none;
- `;
- export default Tag;
|