0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

refactor(console): improve color picker (#3648)

This commit is contained in:
Xiao Yijun 2023-04-03 10:38:31 +08:00 committed by GitHub
parent 1c431e7a59
commit 6773a3ae35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 119 additions and 44 deletions

View file

@ -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",

View file

@ -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);
}
}

View file

@ -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<HTMLInputElement> = (event) => {
onChange?.(event.target.value);
};
const anchorRef = useRef<HTMLSpanElement>(null);
const [isOpen, setIsOpen] = useState(false);
return (
<div className={styles.container}>
<input type="color" id={id} value={value} onChange={handleChange} />
<label htmlFor={id}>{value.toUpperCase()}</label>
<div
tabIndex={0}
role="button"
className={classNames(styles.container, isOpen && styles.highlight)}
onClick={() => {
setIsOpen(true);
}}
onKeyDown={onKeyDownHandler(() => {
setIsOpen(true);
})}
>
<span ref={anchorRef} className={styles.brick} style={{ backgroundColor: value }} />
<span>{value.toUpperCase()}</span>
<Dropdown
anchorRef={anchorRef}
isOpen={isOpen}
horizontalAlign="start"
onClose={() => {
setIsOpen(false);
}}
>
<ChromePicker
color={value}
onChange={({ hex }) => {
onChange(hex);
}}
/>
</Dropdown>
</div>
);
}
export default ColorPicker;

View file

@ -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}
>
<div ref={overlayRef} className={styles.dropdownContainer}>

View file

@ -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'}