123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- import {css} from '@emotion/react';
- import styled from '@emotion/styled';
- import {PlatformIcon} from 'platformicons';
- import Tooltip from 'sentry/components/tooltip';
- import {PlatformKey} from 'sentry/data/platformCategories';
- import {tn} from 'sentry/locale';
- import getPlatformName from 'sentry/utils/getPlatformName';
- import {Theme} from 'sentry/utils/theme';
- type Props = {
- className?: string;
- /**
- * Will set container width to be size of having `this.props.max` icons
- * This is good for lists where the project name is displayed
- */
- consistentWidth?: boolean;
- direction?: 'right' | 'left';
- /**
- * Maximum number of platform icons to display
- */
- max?: number;
- platforms?: PlatformKey[];
- /**
- * If true and if the number of children is greater than the max prop,
- * a counter will be displayed at the end of the stack
- */
- showCounter?: boolean;
- /**
- * Platform icon size in pixels
- */
- size?: number;
- };
- type WrapperProps = Required<
- Pick<Props, 'showCounter' | 'size' | 'direction' | 'consistentWidth' | 'max'>
- >;
- function PlatformList({
- platforms = [],
- direction = 'right',
- max = 3,
- size = 16,
- consistentWidth = false,
- showCounter = false,
- className,
- }: Props) {
- const visiblePlatforms = platforms.slice(0, max);
- const numNotVisiblePlatforms = platforms.length - visiblePlatforms.length;
- const displayCounter = showCounter && !!numNotVisiblePlatforms;
- function renderContent() {
- if (!platforms.length) {
- return <StyledPlatformIcon size={size} platform="default" />;
- }
- const platformIcons = visiblePlatforms.slice().reverse();
- if (displayCounter) {
- return (
- <InnerWrapper>
- <PlatformIcons>
- {platformIcons.map((visiblePlatform, index) => (
- <Tooltip
- key={visiblePlatform + index}
- title={getPlatformName(visiblePlatform)}
- containerDisplayMode="inline-flex"
- >
- <StyledPlatformIcon platform={visiblePlatform} size={size} />
- </Tooltip>
- ))}
- </PlatformIcons>
- <Tooltip
- title={tn('%s other platform', '%s other platforms', numNotVisiblePlatforms)}
- containerDisplayMode="inline-flex"
- >
- <Counter>
- {numNotVisiblePlatforms}
- <Plus>{'\u002B'}</Plus>
- </Counter>
- </Tooltip>
- </InnerWrapper>
- );
- }
- return (
- <PlatformIcons>
- {platformIcons.map((visiblePlatform, index) => (
- <StyledPlatformIcon
- data-test-id={`platform-icon-${visiblePlatform}`}
- key={visiblePlatform + index}
- platform={visiblePlatform}
- size={size}
- />
- ))}
- </PlatformIcons>
- );
- }
- return (
- <Wrapper
- consistentWidth={consistentWidth}
- className={className}
- size={size}
- showCounter={displayCounter}
- direction={direction}
- max={max}
- >
- {renderContent()}
- </Wrapper>
- );
- }
- export default PlatformList;
- function getOverlapWidth(size: number) {
- return Math.round(size / 4);
- }
- const commonStyles = ({theme}: {theme: Theme}) => `
- cursor: default;
- border-radius: ${theme.borderRadius};
- box-shadow: 0 0 0 1px ${theme.background};
- :hover {
- z-index: 1;
- }
- `;
- const PlatformIcons = styled('div')`
- display: flex;
- `;
- const InnerWrapper = styled('div')`
- display: flex;
- position: relative;
- `;
- const Plus = styled('span')`
- font-size: 10px;
- `;
- const StyledPlatformIcon = styled(PlatformIcon)`
- ${p => commonStyles(p)};
- `;
- const Counter = styled('div')`
- ${p => commonStyles(p)};
- display: flex;
- align-items: center;
- justify-content: center;
- text-align: center;
- font-weight: 600;
- font-size: ${p => p.theme.fontSizeExtraSmall};
- background-color: ${p => p.theme.gray200};
- color: ${p => p.theme.gray300};
- padding: 0 1px;
- position: absolute;
- right: -1px;
- `;
- const Wrapper = styled('div')<WrapperProps>`
- display: flex;
- flex-shrink: 0;
- justify-content: ${p => (p.direction === 'right' ? 'flex-end' : 'flex-start')};
- ${p =>
- p.consistentWidth && `width: ${p.size + (p.max - 1) * getOverlapWidth(p.size)}px;`};
- ${PlatformIcons} {
- ${p =>
- p.showCounter
- ? css`
- z-index: 1;
- flex-direction: row-reverse;
- > * :not(:first-child) {
- margin-right: ${p.size * -1 + getOverlapWidth(p.size)}px;
- }
- `
- : css`
- > * :not(:first-child) {
- margin-left: ${p.size * -1 + getOverlapWidth(p.size)}px;
- }
- `}
- }
- ${InnerWrapper} {
- padding-right: ${p => p.size / 2 + 1}px;
- }
- ${Counter} {
- height: ${p => p.size}px;
- min-width: ${p => p.size}px;
- }
- `;
|