ColorPicker.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import React from "react";
  2. import { Popover } from "./Popover";
  3. import "./ColorPicker.css";
  4. // This is a narrow reimplementation of the awesome react-color Twitter component
  5. // https://github.com/casesandberg/react-color/blob/master/src/components/twitter/Twitter.js
  6. const Picker = function({
  7. colors,
  8. color,
  9. onChange
  10. }: {
  11. colors: string[];
  12. color: string | undefined;
  13. onChange: (color: string) => void;
  14. }) {
  15. const [innerValue, setInnerValue] = React.useState(color);
  16. React.useEffect(() => {
  17. setInnerValue(color);
  18. }, [color]);
  19. return (
  20. <div className="color-picker">
  21. <div className="color-picker-triangle-shadow"></div>
  22. <div className="color-picker-triangle"></div>
  23. <div className="color-picker-content">
  24. {colors.map(color => (
  25. <div
  26. className="color-picker-swatch"
  27. onClick={() => {
  28. onChange(color);
  29. }}
  30. title={color}
  31. tabIndex={0}
  32. style={{ backgroundColor: color }}
  33. >
  34. {color === "transparent" ? (
  35. <div className="color-picker-transparent"></div>
  36. ) : (
  37. undefined
  38. )}
  39. </div>
  40. ))}
  41. <div className="color-picker-hash">#</div>
  42. <div style={{ position: "relative" }}>
  43. <input
  44. spellCheck={false}
  45. className="color-picker-input"
  46. onChange={e => {
  47. const value = e.target.value;
  48. if (value.match(/^([0-9a-f]{3}|[0-9a-f]{6}|transparent)$/)) {
  49. onChange(value === "transparent" ? "transparent" : "#" + value);
  50. }
  51. setInnerValue(value);
  52. }}
  53. value={(innerValue || "").replace(/^#/, "")}
  54. />
  55. </div>
  56. <div style={{ clear: "both" }}></div>
  57. </div>
  58. </div>
  59. );
  60. };
  61. export function ColorPicker({
  62. type,
  63. color,
  64. onChange
  65. }: {
  66. type: "canvasBackground" | "elementBackground" | "elementStroke";
  67. color: string | null;
  68. onChange: (color: string) => void;
  69. }) {
  70. const [isActive, setActive] = React.useState(false);
  71. return (
  72. <div>
  73. <button
  74. className="swatch"
  75. style={color ? { backgroundColor: color } : undefined}
  76. onClick={() => setActive(!isActive)}
  77. />
  78. <React.Suspense fallback="">
  79. {isActive ? (
  80. <Popover onCloseRequest={() => setActive(false)}>
  81. <Picker
  82. colors={colors[type]}
  83. color={color || undefined}
  84. onChange={changedColor => {
  85. onChange(changedColor);
  86. }}
  87. />
  88. </Popover>
  89. ) : null}
  90. </React.Suspense>
  91. <input
  92. type="text"
  93. className="swatch-input"
  94. value={color || ""}
  95. onPaste={e => onChange(e.clipboardData.getData("text"))}
  96. onChange={e => onChange(e.target.value)}
  97. />
  98. </div>
  99. );
  100. }
  101. const colors = {
  102. canvasBackground: [
  103. "#DEE6EF",
  104. "#FCEAD8",
  105. "#F9E0E0",
  106. "#E6F1F1",
  107. "#E0EDDF",
  108. "#FBF5DD",
  109. "#F0E6ED",
  110. "#FFEDEF",
  111. "#EDE5E1",
  112. "#F2F0EF",
  113. "#FFFFFF"
  114. ],
  115. elementBackground: [
  116. "#4E79A7",
  117. "#F28E2C",
  118. "#E15759",
  119. "#76B7B2",
  120. "#59A14F",
  121. "#EDC949",
  122. "#AF7AA1",
  123. "#FF9DA7",
  124. "#9C755F",
  125. "#BAB0AB",
  126. "transparent"
  127. ],
  128. elementStroke: [
  129. "#324E6B",
  130. "#9B5B1D",
  131. "#903839",
  132. "#4C7572",
  133. "#396733",
  134. "#AD9336",
  135. "#805976",
  136. "#BA737A",
  137. "#725646",
  138. "#88817D",
  139. "#000000"
  140. ]
  141. };