From 8785bef523a253eb289e20ab94aad4db3a5c940d Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Sat, 11 Jan 2020 19:10:41 -0800 Subject: [PATCH] Support transparent background + inline picker (#325) Unfortunately, react-color has a bug where transparent color doesn't trigger onChange. I've been annoyed by the huge dependency anyway so decided to take the generated html (which is awesome) and reimplement a specific component for it. I also made sure that we don't actually render anything when the background is transparent on rough (I looked at the generated path and made sure it didn't have the commands for the background) --- package-lock.json | 37 +------------- package.json | 1 - src/components/ColorPicker.css | 89 ++++++++++++++++++++++++++++++++++ src/components/ColorPicker.tsx | 74 ++++++++++++++++++++++++---- src/renderer/renderElement.ts | 15 ++++-- 5 files changed, 166 insertions(+), 50 deletions(-) create mode 100644 src/components/ColorPicker.css diff --git a/package-lock.json b/package-lock.json index c93986759e..50af876dc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1034,11 +1034,6 @@ "@hapi/hoek": "^8.3.0" } }, - "@icons/material": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", - "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" - }, "@jest/console": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", @@ -1492,6 +1487,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/nanoid/-/nanoid-2.1.0.tgz", "integrity": "sha512-xdkn/oRTA0GSNPLIKZgHWqDTWZsVrieKomxJBOQUK9YDD+zfSgmwD5t4WJYra5S7XyhTw7tfvwznW+pFexaepQ==", + "dev": true, "requires": { "@types/node": "*" } @@ -9400,11 +9396,6 @@ "object-visit": "^1.0.0" } }, - "material-colors": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", - "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -12135,19 +12126,6 @@ "whatwg-fetch": "^3.0.0" } }, - "react-color": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.17.3.tgz", - "integrity": "sha512-1dtO8LqAVotPIChlmo6kLtFS1FP89ll8/OiA8EcFRDR+ntcK+0ukJgByuIQHRtzvigf26dV5HklnxDIvhON9VQ==", - "requires": { - "@icons/material": "^0.2.4", - "lodash": "^4.17.11", - "material-colors": "^1.2.1", - "prop-types": "^15.5.10", - "reactcss": "^1.2.0", - "tinycolor2": "^1.4.1" - } - }, "react-dev-utils": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.0.0.tgz", @@ -12369,14 +12347,6 @@ "workbox-webpack-plugin": "4.3.1" } }, - "reactcss": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", - "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", - "requires": { - "lodash": "^4.0.1" - } - }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -14459,11 +14429,6 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, - "tinycolor2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", - "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", diff --git a/package.json b/package.json index 432faa66f9..26ac9ca084 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "dependencies": { "nanoid": "^2.1.9", "react": "16.12.0", - "react-color": "^2.17.3", "react-dom": "16.12.0", "react-scripts": "3.3.0", "roughjs": "3.1.0" diff --git a/src/components/ColorPicker.css b/src/components/ColorPicker.css new file mode 100644 index 0000000000..febd7bfd12 --- /dev/null +++ b/src/components/ColorPicker.css @@ -0,0 +1,89 @@ +.color-picker { + width: 205px; + background: rgb(255, 255, 255); + border: 0px solid rgba(0, 0, 0, 0.25); + box-shadow: rgba(0, 0, 0, 0.25) 0px 1px 4px; + border-radius: 4px; + position: relative; +} + +.color-picker-triangle-shadow { + width: 0px; + height: 0px; + border-style: solid; + border-width: 0px 9px 10px; + border-color: transparent transparent rgba(0, 0, 0, 0.1); + position: absolute; + top: -11px; + left: 12px; +} + +.color-picker-triangle { + width: 0px; + height: 0px; + border-style: solid; + border-width: 0px 9px 10px; + border-color: transparent transparent rgb(255, 255, 255); + position: absolute; + top: -10px; + left: 12px; +} + +.color-picker-content { + padding: 15px 9px 9px 15px; +} + +.color-picker-swatch { + height: 30px; + width: 30px; + cursor: pointer; + position: relative; + outline: none; + float: left; + border-radius: 4px; + margin: 0px 6px 6px 0px; +} + +.color-picker-swatch:focus { + /* Note: in the original react-color it uses the color of the element, + but it's too annoying to set from JavaScript without a css lib... */ + box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); +} + +.color-picker-transparent { + border-radius: 4px; + box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0px 1px inset; + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") + left center; +} + +.color-picker-hash { + background: rgb(240, 240, 240); + height: 30px; + width: 30px; + border-radius: 4px 0px 0px 4px; + float: left; + color: rgb(152, 161, 164); + display: flex; + align-items: center; + justify-content: center; +} + +.color-picker-input { + width: 100px; + font-size: 14px; + color: rgb(102, 102, 102); + border: 0px; + outline: none; + height: 28px; + box-shadow: rgb(240, 240, 240) 0px 0px 0px 1px inset; + box-sizing: content-box; + border-radius: 0px 4px 4px 0px; + float: left; + padding-left: 8px; +} diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index 3090ed3977..bb7276d047 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -1,11 +1,66 @@ -import React, { lazy } from "react"; +import React from "react"; import { Popover } from "./Popover"; -const TwitterPicker = lazy(() => - import( - /* webpackPrefetch: true */ "react-color/lib/components/twitter/Twitter" - ) -); +import "./ColorPicker.css"; + +// This is a narrow reimplementation of the awesome react-color Twitter component +// https://github.com/casesandberg/react-color/blob/master/src/components/twitter/Twitter.js + +const Picker = function({ + colors, + color, + onChange +}: { + colors: string[]; + color: string | undefined; + onChange: (color: string) => void; +}) { + const [innerValue, setInnerValue] = React.useState(color); + React.useEffect(() => { + setInnerValue(color); + }, [color]); + return ( +
+
+
+
+ {colors.map(color => ( +
{ + onChange(color); + }} + title={color} + tabIndex={0} + style={{ backgroundColor: color }} + > + {color === "transparent" ? ( +
+ ) : ( + undefined + )} +
+ ))} +
#
+
+ { + const value = e.target.value; + if (value.match(/^([0-9a-f]{3}|[0-9a-f]{6}|transparent)$/)) { + onChange(value === "transparent" ? "transparent" : "#" + value); + } + setInnerValue(value); + }} + value={(innerValue || "").replace(/^#/, "")} + /> +
+
+
+
+ ); +}; export function ColorPicker({ type, @@ -27,12 +82,11 @@ export function ColorPicker({ {isActive ? ( setActive(false)}> - { - onChange(changedColor.hex); + onChange(changedColor); }} /> @@ -74,7 +128,7 @@ const colors = { "#FF9DA7", "#9C755F", "#BAB0AB", - "#FFFFFF" + "transparent" ], elementStroke: [ "#324E6B", diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index 75e4bf05af..d468d33d9e 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -22,7 +22,10 @@ export function renderElement( element.shape = withCustomMathRandom(element.seed, () => { return generator.rectangle(0, 0, element.width, element.height, { stroke: element.strokeColor, - fill: element.backgroundColor, + fill: + element.backgroundColor === "transparent" + ? undefined + : element.backgroundColor, fillStyle: element.fillStyle, strokeWidth: element.strokeWidth, roughness: element.roughness @@ -55,7 +58,10 @@ export function renderElement( ], { stroke: element.strokeColor, - fill: element.backgroundColor, + fill: + element.backgroundColor === "transparent" + ? undefined + : element.backgroundColor, fillStyle: element.fillStyle, strokeWidth: element.strokeWidth, roughness: element.roughness @@ -77,7 +83,10 @@ export function renderElement( element.height, { stroke: element.strokeColor, - fill: element.backgroundColor, + fill: + element.backgroundColor === "transparent" + ? undefined + : element.backgroundColor, fillStyle: element.fillStyle, strokeWidth: element.strokeWidth, roughness: element.roughness