colorChip.tsx 2.6 KB

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