about summary refs log tree commit diff
path: root/src/alf/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/alf/util')
-rw-r--r--src/alf/util/__tests__/colors.test.ts48
-rw-r--r--src/alf/util/colorGeneration.ts28
2 files changed, 76 insertions, 0 deletions
diff --git a/src/alf/util/__tests__/colors.test.ts b/src/alf/util/__tests__/colors.test.ts
new file mode 100644
index 000000000..350b6ff4a
--- /dev/null
+++ b/src/alf/util/__tests__/colors.test.ts
@@ -0,0 +1,48 @@
+import {jest} from '@jest/globals'
+
+import {logger} from '#/logger'
+import {transparentifyColor} from '../colorGeneration'
+
+jest.mock('#/logger', () => ({
+  logger: {warn: jest.fn()},
+}))
+
+describe('transparentifyColor', () => {
+  beforeEach(() => {
+    jest.clearAllMocks()
+  })
+
+  it('converts hsl() to hsla()', () => {
+    const result = transparentifyColor('hsl(120 100% 50%)', 0.5)
+    expect(result).toBe('hsla(120 100% 50%, 0.5)')
+  })
+
+  it('converts hsl() to hsla() - fully transparent', () => {
+    const result = transparentifyColor('hsl(120 100% 50%)', 0)
+    expect(result).toBe('hsla(120 100% 50%, 0)')
+  })
+
+  it('converts rgb() to rgba()', () => {
+    const result = transparentifyColor('rgb(255 0 0)', 0.75)
+    expect(result).toBe('rgba(255 0 0, 0.75)')
+  })
+
+  it('expands 3-digit hex and appends alpha channel', () => {
+    const result = transparentifyColor('#abc', 0.4)
+    expect(result).toBe('#aabbcc66')
+  })
+
+  it('appends alpha to 6-digit hex', () => {
+    const result = transparentifyColor('#aabbcc', 0.4)
+    expect(result).toBe('#aabbcc66')
+  })
+
+  it('returns the original string and warns for unsupported formats', () => {
+    const unsupported = 'blue'
+    const result = transparentifyColor(unsupported, 0.5)
+    expect(result).toBe(unsupported)
+    expect(logger.warn).toHaveBeenCalledWith(
+      `Could not make '${unsupported}' transparent`,
+    )
+  })
+})
diff --git a/src/alf/util/colorGeneration.ts b/src/alf/util/colorGeneration.ts
index 8d769b51b..574ab0a49 100644
--- a/src/alf/util/colorGeneration.ts
+++ b/src/alf/util/colorGeneration.ts
@@ -1,3 +1,5 @@
+import {logger} from '#/logger'
+
 export const BLUE_HUE = 211
 export const RED_HUE = 346
 export const GREEN_HUE = 152
@@ -19,3 +21,29 @@ export function generateScale(start: number, end: number) {
 export const defaultScale = generateScale(6, 100)
 // dim shifted 6% lighter
 export const dimScale = generateScale(12, 100)
+
+export function transparentifyColor(color: string, alpha: number) {
+  if (color.startsWith('hsl(')) {
+    return 'hsla(' + color.slice('hsl('.length, -1) + `, ${alpha})`
+  } else if (color.startsWith('rgb(')) {
+    return 'rgba(' + color.slice('rgb('.length, -1) + `, ${alpha})`
+  } else if (color.startsWith('#')) {
+    if (color.length === 7) {
+      const alphaHex = Math.round(alpha * 255).toString(16)
+      // Per MDN: If there is only one number, it is duplicated: e means ee
+      // https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color
+      return color.slice(0, 7) + alphaHex.padStart(2, alphaHex)
+    } else if (color.length === 4) {
+      // convert to 6-digit hex before adding alpha
+      const [r, g, b] = color.slice(1).split('')
+      const alphaHex = Math.round(alpha * 255).toString(16)
+      return `#${r.repeat(2)}${g.repeat(2)}${b.repeat(2)}${alphaHex.padStart(
+        2,
+        alphaHex,
+      )}`
+    }
+  } else {
+    logger.warn(`Could not make '${color}' transparent`)
+  }
+  return color
+}