Design system.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

110 行
2.9 KiB

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