Skip to content

Commit

Permalink
feat: adjust custom color styling
Browse files Browse the repository at this point in the history
- select color on single click
- open color selector on double click
- add tooltip hinting that double click opens selector
- add custom icon to show a visual difference with the color directly on the left
- adjust general color selector tool styling
  • Loading branch information
kkoomen committed Feb 11, 2024
1 parent 91ce0de commit 4ea1ca6
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 67 deletions.
1 change: 1 addition & 0 deletions src/assets/icons/adjust.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 50 additions & 35 deletions src/components/Paper/components/Palette/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,25 @@ import styles from './styles.module.css';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { PALETTE_DARK, PALETTE_LIGHT } from './../../constants';
import { isDarkColor } from './../../../../helpers';
import { useSelector } from 'react-redux';
import { memo } from 'react';
import { useState } from 'react';
import { SketchPicker } from 'react-color';
import Draggable from 'react-draggable';
import { ReactComponent as CloseIcon } from './../../../../assets/icons/close.svg';
import { ReactComponent as AdjustIcon } from './../../../../assets/icons/adjust.svg';
import { useEffect } from 'react';
import Tooltip from 'rc-tooltip';

function Palette(props) {
const isDarkMode = useSelector((state) => state.settings.isDarkMode);
const palette = isDarkMode ? PALETTE_DARK : PALETTE_LIGHT;
let selectedColor = props.selectedColor;

const [paletteColor, setPaletteColor] = useState('#fff');
const [selectingColor, setSelectingColor] = useState(false);
const [paletteColor, setPaletteColor] = useState(isDarkMode ? '#fff' : '#000');
const [customColor, setCustomColor] = useState(false);
const [colorSelectorToolVisible, enableColorSelectorTool] = useState(false);

// If the user did select some shapes, we want to check if the shapes are all
// of the same color. If so, we select that color. If not, we do not select
Expand All @@ -33,40 +37,44 @@ function Palette(props) {
}
}

// change in custom color palette menu
// Change in custom color palette menu.
const handlePaletteChange = (color) => {
setPaletteColor(color.hex);
}
};

// change in custom color palette menu completed
// Change in custom color palette menu completed.
const handleChangeComplete = (color) => {
setPaletteColor(color.hex);
props.onSelectColor(color.hex);
}
};

// custom color selected in top palette
// Custom color selected in top palette.
const handleSelectCustom = () => {
setSelectingColor(!selectingColor);
setCustomColor(true);
props.onSelectColor(paletteColor);
}
};

// non-custom color selected in top palette
// Non-custom color selected in top palette.
const handleSelectColor = (color) => {
setSelectingColor(false);
setCustomColor(false);
props.onSelectColor(color);
}
};

const handleColorSelectorTool = () => {
enableColorSelectorTool(!colorSelectorToolVisible);
};

useEffect(() => {
const handleClickOutside = (event) => {
const selector = document.querySelector(`.${styles['color-palette__selector']}`);
if (selector && !selector.contains(event.target)) {
setSelectingColor(false);
enableColorSelectorTool(false);
}
}
};

document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [setSelectingColor]);
}, [enableColorSelectorTool]);

return (
<div>
Expand All @@ -76,43 +84,50 @@ function Palette(props) {
key={`color-palette__${color.substring(1)}`}
onClick={() => handleSelectColor(color)}
className={classNames(styles['color-palette__color'], {
[styles['color-palette__color-active']]: color === selectedColor,
[styles['color-palette__color-active']]: color === selectedColor && !customColor,
})}
style={{ backgroundColor: color }}
/>
))}
<div
className={classNames(styles['color-palette__color'], {
[styles['color-palette__color-active']]: paletteColor === selectedColor,
})}
style={{ backgroundColor: paletteColor }}
onClick={handleSelectCustom}
>
</div>

<Tooltip placement="bottom" overlay="Double click to select custom colors">
<div
className={classNames(
styles['color-palette__color'],
styles['color-palette__custom-color'],
{
[styles['color-palette__color-active']]:
paletteColor === selectedColor && customColor,
[styles['color-palette__custom-color--dark']]: isDarkColor(paletteColor),
},
)}
style={{ backgroundColor: paletteColor }}
onClick={handleSelectCustom}
onDoubleClick={handleColorSelectorTool}
>
<AdjustIcon />
</div>
</Tooltip>
</div>
{selectingColor &&
<Draggable handle='.handle'>
{colorSelectorToolVisible && (
<Draggable handle={`.${classNames(styles['custom-color-container__handle'])}`}>
<div className={styles['color-palette__selector']}>
<div
className='handle'
style={{backgroundColor: '#fff', borderRadius: '5px 5px 0 0', display: 'flex', justifyContent: 'space-between'}}
>
<div style={{ flexGrow: 1 }}/>
<div className={classNames(styles['custom-color-container__handle'])}>
<div
className={classNames(styles['close-btn'])}
onClick={() => setSelectingColor(false)}
className={classNames(styles['custom-color-container__close-btn'])}
onClick={() => enableColorSelectorTool(false)}
>
<CloseIcon />
</div>
</div>
<SketchPicker
<SketchPicker
color={paletteColor}
handleChange={handlePaletteChange}
onChangeComplete={handleChangeComplete}
/>
</div>
</Draggable>
}
)}
</div>
);
}
Expand Down
71 changes: 39 additions & 32 deletions src/components/Paper/components/Palette/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
}

.color-palette__selector {
background: #fff;
cursor: default;
position: fixed;
top: 10rem;
Expand All @@ -21,21 +22,6 @@
display: block;
align-items: center;
justify-content: center;
padding: 5px;
border-radius: 7px;
box-shadow: 0 0 15px 0 rgba(0, 0, 0, .2);
}

.color-palette__selector-header {
cursor: default;
position: fixed;
top: 7rem;
left: 50%;
transform: translateX(-50%);
display: block;
align-items: center;
justify-content: center;
padding: 5px;
border-radius: 7px;
box-shadow: 0 0 15px 0 rgba(0, 0, 0, .2);
}
Expand All @@ -62,35 +48,56 @@
transform: scale(0.6);
}

@media (prefers-color-scheme: dark) {
.color-palette__container {
background: #323232;
}
.color-palette__custom-color svg {
pointer-events: none;
width: 100%;
height: 100%;
transform: scale(0.7) rotate(90deg);
fill: #000;
}

@media (prefers-color-scheme: light) {
.color-palette__container {
background: #fff;
border: 1px solid #d1d1d1;
}
.color-palette__custom-color--dark svg {
fill: #fff;
}

.custom-color-container__handle {
background-color: #fff;
border-radius: 5px 5px 0 0;
display: flex;
justify-content: flex-end;
position: relative;
margin: 3px;
top: 3px;
}

.close-btn svg {
fill: #fd5865;
.custom-color-container__close-btn svg {
height: 100%;
min-width: 2.5rem;
min-height: 2.5rem;
max-width: 2.5rem;
max-height: 2.5rem;
padding: 3px;
background-color: #ccc;
padding: 5px;
border-radius: 100%;
margin: 1px;
display: flex;
justify-content: space-between;
}

.close-btn svg:hover {
.custom-color-container__close-btn svg:hover {
cursor: pointer;
background-color: #bbb;
}
}

@media (prefers-color-scheme: dark) {
.color-palette__container {
background: #323232;
}
}

@media (prefers-color-scheme: light) {
.color-palette__selector {
border: 1px solid #d1d1d1;
}
.color-palette__container {
background: #fff;
border: 1px solid #d1d1d1;
}
}
22 changes: 22 additions & 0 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,25 @@ export function isEqual(obj1, obj2) {

return true;
}

/**
* Check if a color is dark or light.
*
* @param {string} hex - The hex code to check.
* @returns {boolean} True when the color is considered a dark color.
*/
export function isDarkColor(hex) {
if (hex.length === 4) {
hex = hex.replace(/#(.)(.)(.)/, '#$1$1$2$2$3$3');
}

// Convert hex to RGB.
let r = parseInt(hex.substring(1, 3), 16);
let g = parseInt(hex.substring(3, 5), 16);
let b = parseInt(hex.substring(5, 7), 16);

let luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;

// Check if the color is dark.
return luminance < 0.5;
}
5 changes: 5 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ hr {
}
}

.sketch-picker {
-webkit-box-shadow: none !important;
box-shadow: none !important;
}

@media (prefers-color-scheme: light) {
hr {
border-top: 1px solid #d1d1d1;
Expand Down

0 comments on commit 4ea1ca6

Please sign in to comment.