Skip to content

Commit

Permalink
Adding Random button to edit tag modal which suggests a random, but d…
Browse files Browse the repository at this point in the history
…istinct tag color.
  • Loading branch information
lrnselfreliance committed Dec 19, 2024
1 parent 75071d0 commit 31e3af8
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 8 deletions.
43 changes: 35 additions & 8 deletions app/src/Tags.js
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -248,6 +255,12 @@ function EditTagsModal() {
setTagNameError(tagNameRegex.test(value) ? {content: 'Invalid Tag Name'} : null);
}

const handleRandomClick = () => {
const colors = tags.map(i => i.color);
const newColor = getDistinctColor(colors);
setTagColor(newColor);
}

const tableHeaders = [
{key: 'delete', text: 'Delete', sortBy: null, width: 2},
{key: 'edit', text: 'Edit', sortBy: null, width: 2},
Expand Down Expand Up @@ -282,13 +295,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>
<APIButton
color='orange'
onClick={handleRandomClick}
style={{marginTop: '2em'}}
type='button'
>Random</APIButton>
</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/>

Expand Down
72 changes: 72 additions & 0 deletions app/src/components/Common.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit 31e3af8

Please sign in to comment.