From 6773a3ae3583736d37164655dab8fe68afe2d2a2 Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Mon, 3 Apr 2023 10:38:31 +0800 Subject: [PATCH] refactor(console): improve color picker (#3648) --- packages/console/package.json | 2 + .../components/ColorPicker/index.module.scss | 38 +++-------- .../src/components/ColorPicker/index.tsx | 51 +++++++++++---- .../console/src/components/Dropdown/index.tsx | 9 ++- pnpm-lock.yaml | 63 +++++++++++++++++++ 5 files changed, 119 insertions(+), 44 deletions(-) diff --git a/packages/console/package.json b/packages/console/package.json index 1a454d30c..24ce4e0d8 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -44,6 +44,7 @@ "@types/mdx": "^2.0.1", "@types/mdx-js__react": "^1.5.5", "@types/react": "^18.0.31", + "@types/react-color": "^3.0.6", "@types/react-dom": "^18.0.0", "@types/react-helmet": "^6.1.6", "@types/react-modal": "^3.13.1", @@ -76,6 +77,7 @@ "prop-types": "^15.8.1", "react": "^18.0.0", "react-animate-height": "^3.0.4", + "react-color": "^2.19.3", "react-dnd": "^16.0.0", "react-dnd-html5-backend": "^16.0.0", "react-dom": "^18.0.0", diff --git a/packages/console/src/components/ColorPicker/index.module.scss b/packages/console/src/components/ColorPicker/index.module.scss index 5e96ac373..bca61d8f7 100644 --- a/packages/console/src/components/ColorPicker/index.module.scss +++ b/packages/console/src/components/ColorPicker/index.module.scss @@ -7,48 +7,24 @@ transition-property: outline, border; transition-timing-function: ease-in-out; transition-duration: 0.2s; - padding: _.unit(2) _.unit(3); + padding: _.unit(1.5) _.unit(3); font: var(--font-body-2); - position: relative; display: flex; align-items: center; - &:focus-within { + &:focus, + &.highlight { border-color: var(--color-primary); outline-color: var(--color-focused-variant); } - input { + .brick { + flex-shrink: 0; + display: inline-block; width: 24px; height: 24px; - padding: 0; border: 1px solid var(--color-divider); border-radius: 4px; - overflow: hidden; - - &::-moz-color-swatch-wrapper { - padding: 0; - width: 24px; - height: 24px; - } - - &::-webkit-color-swatch-wrapper { - padding: 0; - width: 24px; - height: 24px; - } - - &::-moz-color-swatch { - border-style: none; - } - - &::-webkit-color-swatch { - border-style: none; - } - } - - label { - flex: 1; - margin-left: _.unit(2); + margin-right: _.unit(2); } } diff --git a/packages/console/src/components/ColorPicker/index.tsx b/packages/console/src/components/ColorPicker/index.tsx index c17e0a263..89cc3502a 100644 --- a/packages/console/src/components/ColorPicker/index.tsx +++ b/packages/console/src/components/ColorPicker/index.tsx @@ -1,26 +1,53 @@ -import { nanoid } from 'nanoid'; -import type { ChangeEventHandler } from 'react'; -import { useState } from 'react'; +import classNames from 'classnames'; +import { useRef, useState } from 'react'; +import { ChromePicker } from 'react-color'; + +import { onKeyDownHandler } from '@/utils/a11y'; + +import Dropdown from '../Dropdown'; import * as styles from './index.module.scss'; type Props = { value?: string; - onChange?: (value: string) => void; + onChange: (value: string) => void; }; function ColorPicker({ onChange, value = '#000000' }: Props) { - const [id, setId] = useState(nanoid()); - - const handleChange: ChangeEventHandler = (event) => { - onChange?.(event.target.value); - }; + const anchorRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); return ( -
- - +
{ + setIsOpen(true); + }} + onKeyDown={onKeyDownHandler(() => { + setIsOpen(true); + })} + > + + {value.toUpperCase()} + { + setIsOpen(false); + }} + > + { + onChange(hex); + }} + /> +
); } + export default ColorPicker; diff --git a/packages/console/src/components/Dropdown/index.tsx b/packages/console/src/components/Dropdown/index.tsx index 15636de3c..dcb6ea828 100644 --- a/packages/console/src/components/Dropdown/index.tsx +++ b/packages/console/src/components/Dropdown/index.tsx @@ -79,7 +79,14 @@ function Dropdown({ }} className={classNames(styles.content, positionState.verticalAlign === 'top' && styles.onTop)} overlayClassName={styles.overlay} - onRequestClose={onClose} + onRequestClose={(event) => { + /** + * Note: + * we should stop propagation to prevent the event from bubbling up when we click on the overlay to close the dropdown. + */ + event.stopPropagation(); + onClose?.(); + }} onAfterOpen={mutate} >
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbeafde3a..5e689e3b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -398,6 +398,9 @@ importers: '@types/react': specifier: ^18.0.31 version: 18.0.31 + '@types/react-color': + specifier: ^3.0.6 + version: 3.0.6 '@types/react-dom': specifier: ^18.0.0 version: 18.0.6 @@ -494,6 +497,9 @@ importers: react-animate-height: specifier: ^3.0.4 version: 3.0.4(react-dom@18.2.0)(react@18.2.0) + react-color: + specifier: ^2.19.3 + version: 2.19.3(react@18.2.0) react-dnd: specifier: ^16.0.0 version: 16.0.0(@types/node@18.11.18)(@types/react@18.0.31)(react@18.2.0) @@ -2542,6 +2548,14 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true + /@icons/material@0.2.4(react@18.2.0): + resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} + peerDependencies: + react: '*' + dependencies: + react: 18.2.0 + dev: true + /@istanbuljs/load-nyc-config@1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -5048,6 +5062,13 @@ packages: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} dev: true + /@types/react-color@3.0.6: + resolution: {integrity: sha512-OzPIO5AyRmLA7PlOyISlgabpYUa3En74LP8mTMa0veCA719SvYQov4WLMsHvCgXP+L+KI9yGhYnqZafVGG0P4w==} + dependencies: + '@types/react': 18.0.31 + '@types/reactcss': 1.2.6 + dev: true + /@types/react-dom@18.0.6: resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} dependencies: @@ -5095,6 +5116,12 @@ packages: csstype: 3.0.11 dev: true + /@types/reactcss@1.2.6: + resolution: {integrity: sha512-qaIzpCuXNWomGR1Xq8SCFTtF4v8V27Y6f+b9+bzHiv087MylI/nTCqqdChNeWS7tslgROmYB7yeiruWX7WnqNg==} + dependencies: + '@types/react': 18.0.31 + dev: true + /@types/retry@0.12.1: resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} dev: false @@ -10632,6 +10659,10 @@ packages: p-locate: 6.0.0 dev: false + /lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: true + /lodash.camelcase@4.3.0: resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=} dev: true @@ -10798,6 +10829,10 @@ packages: resolution: {integrity: sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==} dev: true + /material-colors@1.2.6: + resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} + dev: true + /mathml-tag-names@2.1.3: resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} dev: true @@ -12810,6 +12845,21 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /react-color@2.19.3(react@18.2.0): + resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} + peerDependencies: + react: '*' + dependencies: + '@icons/material': 0.2.4(react@18.2.0) + lodash: 4.17.21 + lodash-es: 4.17.21 + material-colors: 1.2.6 + prop-types: 15.8.1 + react: 18.2.0 + reactcss: 1.2.3(react@18.2.0) + tinycolor2: 1.6.0 + dev: true + /react-device-detect@2.2.2(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-zSN1gIAztUekp5qUT/ybHwQ9fmOqVT1psxpSlTn1pe0CO+fnJHKRLOWWac5nKxOxvOpD/w84hk1I+EydrJp7SA==} peerDependencies: @@ -13140,6 +13190,15 @@ packages: dependencies: loose-envify: 1.4.0 + /reactcss@1.2.3(react@18.2.0): + resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} + peerDependencies: + react: '*' + dependencies: + lodash: 4.17.21 + react: 18.2.0 + dev: true + /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -14576,6 +14635,10 @@ packages: globrex: 0.1.2 dev: true + /tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + dev: true + /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'}