From db3d318e256daa3de9a123159da09697a896ac34 Mon Sep 17 00:00:00 2001 From: Simon Bigelmayr Date: Mon, 19 Aug 2024 12:44:56 +0200 Subject: [PATCH] feat: add stringToHslaColor --- src/stringToColor.test.ts | 34 +++++++++++++++++++++++++++++++ src/stringToColor.ts | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/stringToColor.test.ts create mode 100644 src/stringToColor.ts diff --git a/src/stringToColor.test.ts b/src/stringToColor.test.ts new file mode 100644 index 0000000..3ec56fa --- /dev/null +++ b/src/stringToColor.test.ts @@ -0,0 +1,34 @@ +import { uniq } from 'lodash'; + +import { randomString, stringToHslaColor } from './stringToColor'; + +describe('stringToHslaColor', () => { + it.each(['test', 'test'])( + 'is a pure function returning the same hsla for the same string', + (testString) => { + expect(stringToHslaColor(testString)).toStrictEqual( + 'hsla(58, 98%, 48%, 1)', + ); + }, + ); + + it('returns various hsla colors for various inputs', () => { + const testStrings = ['t', 'te', 'tes', 'test']; + expect(uniq(testStrings.map(stringToHslaColor)).length).toBe( + testStrings.length, + ); + }); + + it('returns a valid color for a random string', () => { + expect(isValidColor(stringToHslaColor(randomString()))).toBe(true); + }); +}); + +function isValidColor(color: string): boolean { + // Use the browser's validation. Create a dummy HTML element, assign the color and check if it's set. + const element = document.createElement('div'); + element.style.borderColor = ''; + element.style.borderColor = color; + + return element.style.borderColor.length !== 0; +} \ No newline at end of file diff --git a/src/stringToColor.ts b/src/stringToColor.ts new file mode 100644 index 0000000..10d94e3 --- /dev/null +++ b/src/stringToColor.ts @@ -0,0 +1,42 @@ +function hashCode(string: string): number { + let hash = 0; + // eslint-disable-next-line no-plusplus + for (let i = 0; i < string.length; i++) { + // eslint-disable-next-line no-bitwise + hash = string.charCodeAt(i) + ((hash << 5) - hash); + } + + return hash; +} + +function getHslColor(hash: number): string { + const h = range(hash, 0, 360); + const s = range(hash, 50, 100); + const l = range(hash, 20, 50); + + return `hsla(${h}, ${s}%, ${l}%, 1)`; +} + +function range(hash: number, min: number, max: number): number { + const diff = max - min; + const x = ((hash % diff) + diff) % diff; + + return x + min; +} + +export function stringToHslaColor(string: string): string { + return getHslColor(hashCode(string)); +} + +/** + * Generates a random string with a length of 1-11 chars. + */ +export function randomString(): string { + // Cut off the constant 0. from the beginning + const fractionStart = 2; + + // Unequal distribution at the edges, but sufficiently random for the purposes of this function + const randomLengthEnd = Math.round(Math.random() * 11) + 3; + + return Math.random().toString(36).substring(fractionStart, randomLengthEnd); +}