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:
parent
1c431e7a59
commit
6773a3ae35
5 changed files with 119 additions and 44 deletions
|
@ -44,6 +44,7 @@
|
||||||
"@types/mdx": "^2.0.1",
|
"@types/mdx": "^2.0.1",
|
||||||
"@types/mdx-js__react": "^1.5.5",
|
"@types/mdx-js__react": "^1.5.5",
|
||||||
"@types/react": "^18.0.31",
|
"@types/react": "^18.0.31",
|
||||||
|
"@types/react-color": "^3.0.6",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
"@types/react-helmet": "^6.1.6",
|
"@types/react-helmet": "^6.1.6",
|
||||||
"@types/react-modal": "^3.13.1",
|
"@types/react-modal": "^3.13.1",
|
||||||
|
@ -76,6 +77,7 @@
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-animate-height": "^3.0.4",
|
"react-animate-height": "^3.0.4",
|
||||||
|
"react-color": "^2.19.3",
|
||||||
"react-dnd": "^16.0.0",
|
"react-dnd": "^16.0.0",
|
||||||
"react-dnd-html5-backend": "^16.0.0",
|
"react-dnd-html5-backend": "^16.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
|
|
|
@ -7,48 +7,24 @@
|
||||||
transition-property: outline, border;
|
transition-property: outline, border;
|
||||||
transition-timing-function: ease-in-out;
|
transition-timing-function: ease-in-out;
|
||||||
transition-duration: 0.2s;
|
transition-duration: 0.2s;
|
||||||
padding: _.unit(2) _.unit(3);
|
padding: _.unit(1.5) _.unit(3);
|
||||||
font: var(--font-body-2);
|
font: var(--font-body-2);
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&:focus-within {
|
&:focus,
|
||||||
|
&.highlight {
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
outline-color: var(--color-focused-variant);
|
outline-color: var(--color-focused-variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
.brick {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: inline-block;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
padding: 0;
|
|
||||||
border: 1px solid var(--color-divider);
|
border: 1px solid var(--color-divider);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
margin-right: _.unit(2);
|
||||||
|
|
||||||
&::-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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,53 @@
|
||||||
import { nanoid } from 'nanoid';
|
import classNames from 'classnames';
|
||||||
import type { ChangeEventHandler } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { useState } from 'react';
|
import { ChromePicker } from 'react-color';
|
||||||
|
|
||||||
|
import { onKeyDownHandler } from '@/utils/a11y';
|
||||||
|
|
||||||
|
import Dropdown from '../Dropdown';
|
||||||
|
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value?: string;
|
value?: string;
|
||||||
onChange?: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ColorPicker({ onChange, value = '#000000' }: Props) {
|
function ColorPicker({ onChange, value = '#000000' }: Props) {
|
||||||
const [id, setId] = useState(nanoid());
|
const anchorRef = useRef<HTMLSpanElement>(null);
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const handleChange: ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
||||||
onChange?.(event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div
|
||||||
<input type="color" id={id} value={value} onChange={handleChange} />
|
tabIndex={0}
|
||||||
<label htmlFor={id}>{value.toUpperCase()}</label>
|
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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ColorPicker;
|
export default ColorPicker;
|
||||||
|
|
|
@ -79,7 +79,14 @@ function Dropdown({
|
||||||
}}
|
}}
|
||||||
className={classNames(styles.content, positionState.verticalAlign === 'top' && styles.onTop)}
|
className={classNames(styles.content, positionState.verticalAlign === 'top' && styles.onTop)}
|
||||||
overlayClassName={styles.overlay}
|
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}
|
onAfterOpen={mutate}
|
||||||
>
|
>
|
||||||
<div ref={overlayRef} className={styles.dropdownContainer}>
|
<div ref={overlayRef} className={styles.dropdownContainer}>
|
||||||
|
|
|
@ -398,6 +398,9 @@ importers:
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.0.31
|
specifier: ^18.0.31
|
||||||
version: 18.0.31
|
version: 18.0.31
|
||||||
|
'@types/react-color':
|
||||||
|
specifier: ^3.0.6
|
||||||
|
version: 3.0.6
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: ^18.0.0
|
specifier: ^18.0.0
|
||||||
version: 18.0.6
|
version: 18.0.6
|
||||||
|
@ -494,6 +497,9 @@ importers:
|
||||||
react-animate-height:
|
react-animate-height:
|
||||||
specifier: ^3.0.4
|
specifier: ^3.0.4
|
||||||
version: 3.0.4(react-dom@18.2.0)(react@18.2.0)
|
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:
|
react-dnd:
|
||||||
specifier: ^16.0.0
|
specifier: ^16.0.0
|
||||||
version: 16.0.0(@types/node@18.11.18)(@types/react@18.0.31)(react@18.2.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==}
|
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||||
dev: true
|
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:
|
/@istanbuljs/load-nyc-config@1.1.0:
|
||||||
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
|
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -5048,6 +5062,13 @@ packages:
|
||||||
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
|
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
|
||||||
dev: true
|
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:
|
/@types/react-dom@18.0.6:
|
||||||
resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==}
|
resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5095,6 +5116,12 @@ packages:
|
||||||
csstype: 3.0.11
|
csstype: 3.0.11
|
||||||
dev: true
|
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:
|
/@types/retry@0.12.1:
|
||||||
resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==}
|
resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -10632,6 +10659,10 @@ packages:
|
||||||
p-locate: 6.0.0
|
p-locate: 6.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/lodash-es@4.17.21:
|
||||||
|
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/lodash.camelcase@4.3.0:
|
/lodash.camelcase@4.3.0:
|
||||||
resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=}
|
resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -10798,6 +10829,10 @@ packages:
|
||||||
resolution: {integrity: sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==}
|
resolution: {integrity: sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/material-colors@1.2.6:
|
||||||
|
resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/mathml-tag-names@2.1.3:
|
/mathml-tag-names@2.1.3:
|
||||||
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
|
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -12810,6 +12845,21 @@ packages:
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: true
|
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):
|
/react-device-detect@2.2.2(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-zSN1gIAztUekp5qUT/ybHwQ9fmOqVT1psxpSlTn1pe0CO+fnJHKRLOWWac5nKxOxvOpD/w84hk1I+EydrJp7SA==}
|
resolution: {integrity: sha512-zSN1gIAztUekp5qUT/ybHwQ9fmOqVT1psxpSlTn1pe0CO+fnJHKRLOWWac5nKxOxvOpD/w84hk1I+EydrJp7SA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -13140,6 +13190,15 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
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:
|
/read-pkg-up@7.0.1:
|
||||||
resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
|
resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -14576,6 +14635,10 @@ packages:
|
||||||
globrex: 0.1.2
|
globrex: 0.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tinycolor2@1.6.0:
|
||||||
|
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/tmp@0.0.33:
|
/tmp@0.0.33:
|
||||||
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
|
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
|
||||||
engines: {node: '>=0.6.0'}
|
engines: {node: '>=0.6.0'}
|
||||||
|
|
Loading…
Reference in a new issue