colorChip.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import {useTheme} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import Color from 'color';
  4. import space from 'sentry/styles/space';
  5. import {Theme} from 'sentry/utils/theme';
  6. type SizeProp = {
  7. size: 'md' | 'lg';
  8. };
  9. type Props = SizeProp & {
  10. /**
  11. * value is either a CSS color string (e.g. #000)
  12. * or a key in the theme object (e.g. 'blue300')
  13. */
  14. value: string | keyof Theme;
  15. noText?: boolean;
  16. /**
  17. * to replace the parsed color name with a custom name
  18. */
  19. textOverwrite?: string;
  20. };
  21. type WrapperProps = SizeProp & {
  22. noText: boolean;
  23. };
  24. type ColorSwatchProps = SizeProp & {
  25. background: string;
  26. border: boolean;
  27. };
  28. function ColorChip({value, size = 'md', noText = false, textOverwrite}: Props) {
  29. const theme = useTheme();
  30. const isThemeColor = value in theme;
  31. const color = Color(isThemeColor ? theme[value] : value);
  32. const colorString = isThemeColor
  33. ? value.split(/(\d+)/).join(' ').trim()
  34. : color?.hex?.();
  35. return (
  36. <OuterWrap size={size}>
  37. <Wrapper size={size} noText={noText}>
  38. <ColorSwatch
  39. size={size}
  40. background={color?.hex?.()}
  41. border={color?.luminosity?.() > 0.8}
  42. />
  43. {!noText && <Text size={size}>{textOverwrite ?? colorString}</Text>}
  44. </Wrapper>
  45. </OuterWrap>
  46. );
  47. }
  48. export default ColorChip;
  49. const OuterWrap = styled('span')<SizeProp>`
  50. align-items: center;
  51. ${p =>
  52. p.size === 'lg'
  53. ? `
  54. display: flex;
  55. margin: ${space(2)} auto;
  56. `
  57. : `
  58. display: inline-flex;
  59. height: 1em;
  60. `}
  61. `;
  62. const Wrapper = styled('span')<WrapperProps>`
  63. display: flex;
  64. align-items: center;
  65. border-radius: ${p => p.theme.borderRadius};
  66. ${p => !p.noText && `border: solid 1px ${p.theme.border};`}
  67. ${p =>
  68. p.size === 'lg'
  69. ? `
  70. flex-direction: column;
  71. ${!p.noText && `padding: ${space(0.5)}`}
  72. `
  73. : `
  74. transform: translateY(0.25em);
  75. ${
  76. !p.noText &&
  77. `padding: ${space(0.25)} ${space(0.5)} ${space(0.25)} ${space(0.25)};`
  78. }
  79. `};
  80. `;
  81. const Text = styled('span')<SizeProp>`
  82. margin-bottom: 0;
  83. line-height: 1.2;
  84. text-transform: capitalize;
  85. ${p => (p.size === 'lg' ? `margin-top: ${space(0.5)};` : `margin-left: ${space(0.5)};`)}
  86. `;
  87. const ColorSwatch = styled('span')<ColorSwatchProps>`
  88. display: inline;
  89. border-radius: ${p => p.theme.borderRadius};
  90. background-color: ${p => p.background};
  91. ${p => p.border && `border: solid 1px ${p.theme.border};`}
  92. ${p =>
  93. p.size === 'lg'
  94. ? `
  95. width: 4.5em;
  96. height: 4.5em;
  97. `
  98. : `
  99. width: 1.2em;
  100. height: 1.2em;
  101. `};
  102. `;