mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Adding Random button to edit tag modal which suggests a random, but distinct tag color.
This commit is contained in:
parent
75071d0847
commit
ea8ccae1eb
2 changed files with 113 additions and 9 deletions
|
|
@ -1,7 +1,14 @@
|
|||
import React, {useEffect} from "react";
|
||||
import {deleteTag, getTags, saveTag} from "./api";
|
||||
import {Dimmer, Divider, Form, Grid, GridColumn, GridRow, Label, TableCell, TableRow,} from "semantic-ui-react";
|
||||
import {APIButton, contrastingColor, ErrorMessage, fuzzyMatch, scrollToTopOfElement} from "./components/Common";
|
||||
import {
|
||||
APIButton,
|
||||
contrastingColor,
|
||||
ErrorMessage,
|
||||
fuzzyMatch,
|
||||
getDistinctColor,
|
||||
scrollToTopOfElement
|
||||
} from "./components/Common";
|
||||
import {
|
||||
Button,
|
||||
FormInput,
|
||||
|
|
@ -200,6 +207,9 @@ function EditTagsModal() {
|
|||
const {fetchTags, tags} = React.useContext(TagsContext);
|
||||
const {inverted} = React.useContext(ThemeContext);
|
||||
|
||||
// Return a random, but distinct Hex color.
|
||||
const getRandomColor = () => getDistinctColor((tags || []).map(i => i.color));
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [tagId, setTagId] = React.useState(null);
|
||||
const [tagName, setTagName] = React.useState('');
|
||||
|
|
@ -208,6 +218,13 @@ function EditTagsModal() {
|
|||
const [tagNameError, setTagNameError] = React.useState(null);
|
||||
const disabled = !!!tagName || !!tagNameError;
|
||||
|
||||
const setRandomColor = () => setTagColor(getRandomColor());
|
||||
|
||||
// Open modal with random color.
|
||||
React.useEffect(() => {
|
||||
setRandomColor();
|
||||
}, [open]);
|
||||
|
||||
const localOnClose = () => {
|
||||
setOpen(false);
|
||||
setTagName('');
|
||||
|
|
@ -238,7 +255,8 @@ function EditTagsModal() {
|
|||
}
|
||||
setTagName('');
|
||||
setTagId(null);
|
||||
setTagColor(DEFAULT_TAG_COLOR);
|
||||
// Change suggested color after save.
|
||||
setRandomColor();
|
||||
}
|
||||
|
||||
const handleTagNameChange = (e, {value}) => {
|
||||
|
|
@ -282,13 +300,27 @@ function EditTagsModal() {
|
|||
|
||||
<HexColorPicker color={tagColor} onChange={setTagColor} style={{marginTop: '1em'}}/>
|
||||
|
||||
<APIButton
|
||||
color='violet'
|
||||
size='big'
|
||||
onClick={localSaveTag}
|
||||
style={{marginTop: '2em'}}
|
||||
disabled={disabled}
|
||||
>Save</APIButton>
|
||||
<Grid>
|
||||
<Grid.Row columns={2}>
|
||||
<Grid.Column>
|
||||
<Button
|
||||
color='orange'
|
||||
onClick={setRandomColor}
|
||||
style={{marginTop: '2em'}}
|
||||
type='button'
|
||||
>Random</Button>
|
||||
</Grid.Column>
|
||||
<Grid.Column textAlign='right'>
|
||||
<APIButton
|
||||
color='violet'
|
||||
size='big'
|
||||
onClick={localSaveTag}
|
||||
style={{marginTop: '2em'}}
|
||||
disabled={disabled}
|
||||
>Save</APIButton>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
|
||||
<Divider/>
|
||||
|
||||
|
|
|
|||
|
|
@ -1876,3 +1876,75 @@ export function mergeDeep(target, source) {
|
|||
return result;
|
||||
}
|
||||
|
||||
export function getDistinctColor(hexColors) {
|
||||
function hexToHSL(hex) {
|
||||
let r = parseInt(hex.slice(1, 3), 16) / 255;
|
||||
let g = parseInt(hex.slice(3, 5), 16) / 255;
|
||||
let b = parseInt(hex.slice(5, 7), 16) / 255;
|
||||
|
||||
let cmax = Math.max(r, g, b), cmin = Math.min(r, g, b);
|
||||
let delta = cmax - cmin;
|
||||
let h, s, l = (cmax + cmin) / 2;
|
||||
|
||||
if (delta === 0) h = 0;
|
||||
else if (cmax === r) h = ((g - b) / delta) % 6;
|
||||
else if (cmax === g) h = (b - r) / delta + 2;
|
||||
else h = (r - g) / delta + 4;
|
||||
|
||||
h = Math.round(h * 60);
|
||||
if (h < 0) h += 360;
|
||||
|
||||
s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
|
||||
|
||||
return {h: h, s: s, l: l};
|
||||
}
|
||||
|
||||
function hslToHex(h, s, l) {
|
||||
l /= 100;
|
||||
const a = s * Math.min(l, 1 - l) / 100;
|
||||
const f = n => {
|
||||
const k = (n + h / 30) % 12;
|
||||
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
||||
return Math.round(255 * color).toString(16).padStart(2, '0');
|
||||
};
|
||||
return `#${f(0)}${f(8)}${f(4)}`;
|
||||
}
|
||||
|
||||
function generateRandomHSL() {
|
||||
return {
|
||||
h: Math.random() * 360,
|
||||
s: Math.random() * 100,
|
||||
l: Math.random() * 100
|
||||
};
|
||||
}
|
||||
|
||||
function isDistinct(newColor, existingColors, threshold) {
|
||||
const hslNew = newColor;
|
||||
for (let color of existingColors) {
|
||||
let hslColor = hexToHSL(color);
|
||||
if (Math.abs(hslNew.h - hslColor.h) < threshold &&
|
||||
Math.abs(hslNew.s * 100 - hslColor.s * 100) < threshold &&
|
||||
Math.abs(hslNew.l - hslColor.l * 100) < threshold) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let newColor, attempt = 0;
|
||||
const maxAttempts = 1000; // Max attempts before returning any color
|
||||
const baseThreshold = 30; // Starting threshold
|
||||
|
||||
do {
|
||||
newColor = generateRandomHSL();
|
||||
// Decreasing threshold as attempts increase, but never below 10 for distinctiveness
|
||||
let threshold = Math.max(baseThreshold - (attempt / 10), 10);
|
||||
if (isDistinct(newColor, hexColors, threshold)) {
|
||||
return hslToHex(newColor.h, newColor.s, newColor.l);
|
||||
}
|
||||
attempt++;
|
||||
} while (attempt < maxAttempts);
|
||||
|
||||
// If we've tried maxAttempts times, return the last generated color regardless
|
||||
return hslToHex(newColor.h, newColor.s, newColor.l);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue