Design system.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

109 řádky
2.8 KiB

  1. import * as React from 'react';
  2. import Color from 'color';
  3. import * as convert from 'color-convert';
  4. import { tailwind } from '@tesseract-design/web-base';
  5. const { tw } = tailwind;
  6. const SwatchDerivedElementComponent = 'input' as const;
  7. /**
  8. * Derived HTML element of the {@link Swatch} component.
  9. */
  10. export type SwatchDerivedElement = HTMLElementTagNameMap[
  11. typeof SwatchDerivedElementComponent
  12. ];
  13. type ColorValue = ConstructorParameters<typeof Color>[0];
  14. type ColorMode = keyof typeof convert;
  15. /**
  16. * Props of the {@link Swatch} component.
  17. */
  18. export interface SwatchProps extends Omit<React.HTMLProps<SwatchDerivedElement>, 'color'> {
  19. color: NonNullable<ColorValue>;
  20. mode?: ColorMode;
  21. }
  22. export const useSwatchControls = () => {
  23. const id = React.useId();
  24. const copyColor: React.ReactEventHandler<SwatchDerivedElement> = React.useCallback(async (e) => {
  25. const { value } = e.currentTarget;
  26. await window.navigator.clipboard.writeText(value);
  27. }, []);
  28. return React.useMemo(() => ({
  29. id,
  30. copyColor,
  31. }), [id, copyColor]);
  32. };
  33. /**
  34. * Component for displaying a color.
  35. */
  36. export const Swatch = React.forwardRef<SwatchDerivedElement, SwatchProps>(({
  37. // todo unify color and mode into one "value" attribute
  38. color,
  39. mode = 'rgb',
  40. className,
  41. style,
  42. ...etcProps
  43. }, forwardedRef) => {
  44. const { id, copyColor } = useSwatchControls();
  45. const colorInternal = React.useMemo(() => new Color(color, mode), [color, mode]);
  46. const colorValue = colorInternal.hex();
  47. return (
  48. <span
  49. className={tw(
  50. 'inline-block align-middle',
  51. className,
  52. )}
  53. style={style}
  54. >
  55. <SwatchDerivedElementComponent
  56. {...etcProps}
  57. ref={forwardedRef}
  58. type="text"
  59. value={colorInternal.toString()}
  60. className="sr-only select-all peer"
  61. readOnly
  62. id={id}
  63. onSelect={copyColor}
  64. />
  65. <label
  66. className={tw(
  67. 'relative rounded ring-secondary/50 whitespace-nowrap inline-block align-top leading-none cursor-pointer', // todo eyedropper cursor
  68. 'peer-focus:outline-0 peer-focus:ring-4',
  69. 'peer-active:ring-tertiary/50',
  70. 'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed',
  71. )}
  72. title={colorValue}
  73. htmlFor={id}
  74. >
  75. {/* todo border primary */}
  76. <span
  77. className="inline-block w-5 h-5 align-middle border border-[#ffffff]"
  78. >
  79. <span
  80. className="block w-full h-full border border-[#000000]"
  81. style={{
  82. backgroundColor: colorInternal.hex(),
  83. }}
  84. />
  85. </span>
  86. <span className="tabular-nums text-xs sr-only">
  87. {colorInternal.toString()}
  88. </span>
  89. </label>
  90. </span>
  91. );
  92. });
  93. Swatch.displayName = 'Swatch';
  94. Swatch.defaultProps = {
  95. mode: 'rgb',
  96. };