aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/mui/colors
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-03-09 14:00:02 -0300
committerSebastian <sebasjm@gmail.com>2022-03-09 14:00:02 -0300
commit1607c728bca19a003ca08b64b4d2afc73e4d1e2a (patch)
treea02fc28342cf343cf91b182955cc903915989478 /packages/taler-wallet-webextension/src/mui/colors
parent6bc244cc1e0e08c4d86161fdf2b6a52b3f9f452e (diff)
downloadwallet-core-1607c728bca19a003ca08b64b4d2afc73e4d1e2a.tar.xz
first banner implementation with mui
Diffstat (limited to 'packages/taler-wallet-webextension/src/mui/colors')
-rw-r--r--packages/taler-wallet-webextension/src/mui/colors/constants.ts348
-rw-r--r--packages/taler-wallet-webextension/src/mui/colors/manipulation.test.ts305
-rw-r--r--packages/taler-wallet-webextension/src/mui/colors/manipulation.ts273
3 files changed, 926 insertions, 0 deletions
diff --git a/packages/taler-wallet-webextension/src/mui/colors/constants.ts b/packages/taler-wallet-webextension/src/mui/colors/constants.ts
new file mode 100644
index 000000000..a6e58caa9
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/colors/constants.ts
@@ -0,0 +1,348 @@
+export const amber = {
+ 50: '#fff8e1',
+ 100: '#ffecb3',
+ 200: '#ffe082',
+ 300: '#ffd54f',
+ 400: '#ffca28',
+ 500: '#ffc107',
+ 600: '#ffb300',
+ 700: '#ffa000',
+ 800: '#ff8f00',
+ 900: '#ff6f00',
+ A100: '#ffe57f',
+ A200: '#ffd740',
+ A400: '#ffc400',
+ A700: '#ffab00',
+};
+
+
+export const blueGrey = {
+ 50: '#eceff1',
+ 100: '#cfd8dc',
+ 200: '#b0bec5',
+ 300: '#90a4ae',
+ 400: '#78909c',
+ 500: '#607d8b',
+ 600: '#546e7a',
+ 700: '#455a64',
+ 800: '#37474f',
+ 900: '#263238',
+ A100: '#cfd8dc',
+ A200: '#b0bec5',
+ A400: '#78909c',
+ A700: '#455a64',
+};
+
+
+export const blue = {
+ 50: '#e3f2fd',
+ 100: '#bbdefb',
+ 200: '#90caf9',
+ 300: '#64b5f6',
+ 400: '#42a5f5',
+ 500: '#2196f3',
+ 600: '#1e88e5',
+ 700: '#1976d2',
+ 800: '#1565c0',
+ 900: '#0d47a1',
+ A100: '#82b1ff',
+ A200: '#448aff',
+ A400: '#2979ff',
+ A700: '#2962ff',
+};
+
+
+export const brown = {
+ 50: '#efebe9',
+ 100: '#d7ccc8',
+ 200: '#bcaaa4',
+ 300: '#a1887f',
+ 400: '#8d6e63',
+ 500: '#795548',
+ 600: '#6d4c41',
+ 700: '#5d4037',
+ 800: '#4e342e',
+ 900: '#3e2723',
+ A100: '#d7ccc8',
+ A200: '#bcaaa4',
+ A400: '#8d6e63',
+ A700: '#5d4037',
+};
+
+
+export const common = {
+ black: '#000',
+ white: '#fff',
+};
+
+
+export const cyan = {
+ 50: '#e0f7fa',
+ 100: '#b2ebf2',
+ 200: '#80deea',
+ 300: '#4dd0e1',
+ 400: '#26c6da',
+ 500: '#00bcd4',
+ 600: '#00acc1',
+ 700: '#0097a7',
+ 800: '#00838f',
+ 900: '#006064',
+ A100: '#84ffff',
+ A200: '#18ffff',
+ A400: '#00e5ff',
+ A700: '#00b8d4',
+};
+
+
+export const deepOrange = {
+ 50: '#fbe9e7',
+ 100: '#ffccbc',
+ 200: '#ffab91',
+ 300: '#ff8a65',
+ 400: '#ff7043',
+ 500: '#ff5722',
+ 600: '#f4511e',
+ 700: '#e64a19',
+ 800: '#d84315',
+ 900: '#bf360c',
+ A100: '#ff9e80',
+ A200: '#ff6e40',
+ A400: '#ff3d00',
+ A700: '#dd2c00',
+};
+
+
+export const deepPurple = {
+ 50: '#ede7f6',
+ 100: '#d1c4e9',
+ 200: '#b39ddb',
+ 300: '#9575cd',
+ 400: '#7e57c2',
+ 500: '#673ab7',
+ 600: '#5e35b1',
+ 700: '#512da8',
+ 800: '#4527a0',
+ 900: '#311b92',
+ A100: '#b388ff',
+ A200: '#7c4dff',
+ A400: '#651fff',
+ A700: '#6200ea',
+};
+
+
+export const green = {
+ 50: '#e8f5e9',
+ 100: '#c8e6c9',
+ 200: '#a5d6a7',
+ 300: '#81c784',
+ 400: '#66bb6a',
+ 500: '#4caf50',
+ 600: '#43a047',
+ 700: '#388e3c',
+ 800: '#2e7d32',
+ 900: '#1b5e20',
+ A100: '#b9f6ca',
+ A200: '#69f0ae',
+ A400: '#00e676',
+ A700: '#00c853',
+};
+
+
+export const grey = {
+ 50: '#fafafa',
+ 100: '#f5f5f5',
+ 200: '#eeeeee',
+ 300: '#e0e0e0',
+ 400: '#bdbdbd',
+ 500: '#9e9e9e',
+ 600: '#757575',
+ 700: '#616161',
+ 800: '#424242',
+ 900: '#212121',
+ A100: '#f5f5f5',
+ A200: '#eeeeee',
+ A400: '#bdbdbd',
+ A700: '#616161',
+};
+
+
+export const indigo = {
+ 50: '#e8eaf6',
+ 100: '#c5cae9',
+ 200: '#9fa8da',
+ 300: '#7986cb',
+ 400: '#5c6bc0',
+ 500: '#3f51b5',
+ 600: '#3949ab',
+ 700: '#303f9f',
+ 800: '#283593',
+ 900: '#1a237e',
+ A100: '#8c9eff',
+ A200: '#536dfe',
+ A400: '#3d5afe',
+ A700: '#304ffe',
+};
+
+
+export const lightBlue = {
+ 50: '#e1f5fe',
+ 100: '#b3e5fc',
+ 200: '#81d4fa',
+ 300: '#4fc3f7',
+ 400: '#29b6f6',
+ 500: '#03a9f4',
+ 600: '#039be5',
+ 700: '#0288d1',
+ 800: '#0277bd',
+ 900: '#01579b',
+ A100: '#80d8ff',
+ A200: '#40c4ff',
+ A400: '#00b0ff',
+ A700: '#0091ea',
+};
+
+
+export const lightGreen = {
+ 50: '#f1f8e9',
+ 100: '#dcedc8',
+ 200: '#c5e1a5',
+ 300: '#aed581',
+ 400: '#9ccc65',
+ 500: '#8bc34a',
+ 600: '#7cb342',
+ 700: '#689f38',
+ 800: '#558b2f',
+ 900: '#33691e',
+ A100: '#ccff90',
+ A200: '#b2ff59',
+ A400: '#76ff03',
+ A700: '#64dd17',
+};
+
+
+export const lime = {
+ 50: '#f9fbe7',
+ 100: '#f0f4c3',
+ 200: '#e6ee9c',
+ 300: '#dce775',
+ 400: '#d4e157',
+ 500: '#cddc39',
+ 600: '#c0ca33',
+ 700: '#afb42b',
+ 800: '#9e9d24',
+ 900: '#827717',
+ A100: '#f4ff81',
+ A200: '#eeff41',
+ A400: '#c6ff00',
+ A700: '#aeea00',
+};
+
+
+export const orange = {
+ 50: '#fff3e0',
+ 100: '#ffe0b2',
+ 200: '#ffcc80',
+ 300: '#ffb74d',
+ 400: '#ffa726',
+ 500: '#ff9800',
+ 600: '#fb8c00',
+ 700: '#f57c00',
+ 800: '#ef6c00',
+ 900: '#e65100',
+ A100: '#ffd180',
+ A200: '#ffab40',
+ A400: '#ff9100',
+ A700: '#ff6d00',
+};
+
+
+export const pink = {
+ 50: '#fce4ec',
+ 100: '#f8bbd0',
+ 200: '#f48fb1',
+ 300: '#f06292',
+ 400: '#ec407a',
+ 500: '#e91e63',
+ 600: '#d81b60',
+ 700: '#c2185b',
+ 800: '#ad1457',
+ 900: '#880e4f',
+ A100: '#ff80ab',
+ A200: '#ff4081',
+ A400: '#f50057',
+ A700: '#c51162',
+};
+
+
+export const purple = {
+ 50: '#f3e5f5',
+ 100: '#e1bee7',
+ 200: '#ce93d8',
+ 300: '#ba68c8',
+ 400: '#ab47bc',
+ 500: '#9c27b0',
+ 600: '#8e24aa',
+ 700: '#7b1fa2',
+ 800: '#6a1b9a',
+ 900: '#4a148c',
+ A100: '#ea80fc',
+ A200: '#e040fb',
+ A400: '#d500f9',
+ A700: '#aa00ff',
+};
+
+
+export const red = {
+ 50: '#ffebee',
+ 100: '#ffcdd2',
+ 200: '#ef9a9a',
+ 300: '#e57373',
+ 400: '#ef5350',
+ 500: '#f44336',
+ 600: '#e53935',
+ 700: '#d32f2f',
+ 800: '#c62828',
+ 900: '#b71c1c',
+ A100: '#ff8a80',
+ A200: '#ff5252',
+ A400: '#ff1744',
+ A700: '#d50000',
+};
+
+
+export const teal = {
+ 50: '#e0f2f1',
+ 100: '#b2dfdb',
+ 200: '#80cbc4',
+ 300: '#4db6ac',
+ 400: '#26a69a',
+ 500: '#009688',
+ 600: '#00897b',
+ 700: '#00796b',
+ 800: '#00695c',
+ 900: '#004d40',
+ A100: '#a7ffeb',
+ A200: '#64ffda',
+ A400: '#1de9b6',
+ A700: '#00bfa5',
+};
+
+
+export const yellow = {
+ 50: '#fffde7',
+ 100: '#fff9c4',
+ 200: '#fff59d',
+ 300: '#fff176',
+ 400: '#ffee58',
+ 500: '#ffeb3b',
+ 600: '#fdd835',
+ 700: '#fbc02d',
+ 800: '#f9a825',
+ 900: '#f57f17',
+ A100: '#ffff8d',
+ A200: '#ffff00',
+ A400: '#ffea00',
+ A700: '#ffd600',
+};
+
+
diff --git a/packages/taler-wallet-webextension/src/mui/colors/manipulation.test.ts b/packages/taler-wallet-webextension/src/mui/colors/manipulation.test.ts
new file mode 100644
index 000000000..77b3ec884
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/colors/manipulation.test.ts
@@ -0,0 +1,305 @@
+import { expect } from 'chai';
+import {
+ recomposeColor,
+ hexToRgb,
+ rgbToHex,
+ hslToRgb,
+ darken,
+ decomposeColor,
+ emphasize,
+ alpha,
+ getContrastRatio,
+ getLuminance,
+ lighten,
+} from './manipulation';
+
+describe('utils/colorManipulator', () => {
+ describe('recomposeColor', () => {
+ it('converts a decomposed rgb color object to a string` ', () => {
+ expect(
+ recomposeColor({
+ type: 'rgb',
+ values: [255, 255, 255],
+ }),
+ ).to.equal('rgb(255, 255, 255)');
+ });
+
+ it('converts a decomposed rgba color object to a string` ', () => {
+ expect(
+ recomposeColor({
+ type: 'rgba',
+ values: [255, 255, 255, 0.5],
+ }),
+ ).to.equal('rgba(255, 255, 255, 0.5)');
+ });
+
+ it('converts a decomposed hsl color object to a string` ', () => {
+ expect(
+ recomposeColor({
+ type: 'hsl',
+ values: [100, 50, 25],
+ }),
+ ).to.equal('hsl(100, 50%, 25%)');
+ });
+
+ it('converts a decomposed hsla color object to a string` ', () => {
+ expect(
+ recomposeColor({
+ type: 'hsla',
+ values: [100, 50, 25, 0.5],
+ }),
+ ).to.equal('hsla(100, 50%, 25%, 0.5)');
+ });
+ });
+
+ describe('hexToRgb', () => {
+ it('converts a short hex color to an rgb color` ', () => {
+ expect(hexToRgb('#9f3')).to.equal('rgb(153, 255, 51)');
+ });
+
+ it('converts a long hex color to an rgb color` ', () => {
+ expect(hexToRgb('#a94fd3')).to.equal('rgb(169, 79, 211)');
+ });
+
+ it('converts a long alpha hex color to an argb color` ', () => {
+ expect(hexToRgb('#111111f8')).to.equal('rgba(17, 17, 17, 0.973)');
+ });
+ });
+
+ describe('rgbToHex', () => {
+ it('converts an rgb color to a hex color` ', () => {
+ expect(rgbToHex('rgb(169, 79, 211)')).to.equal('#a94fd3');
+ });
+
+ it('converts an rgba color to a hex color` ', () => {
+ expect(rgbToHex('rgba(169, 79, 211, 1)')).to.equal('#a94fd3ff');
+ });
+
+ it('idempotent', () => {
+ expect(rgbToHex('#A94FD3')).to.equal('#A94FD3');
+ });
+ });
+
+ describe('hslToRgb', () => {
+ it('converts an hsl color to an rgb color` ', () => {
+ expect(hslToRgb('hsl(281, 60%, 57%)')).to.equal('rgb(169, 80, 211)');
+ });
+
+ it('converts an hsla color to an rgba color` ', () => {
+ expect(hslToRgb('hsla(281, 60%, 57%, 0.5)')).to.equal('rgba(169, 80, 211, 0.5)');
+ });
+
+ it('allow to convert values only', () => {
+ expect(hslToRgb('hsl(281, 60%, 57%)')).to.equal('rgb(169, 80, 211)');
+ });
+ });
+
+ describe('decomposeColor', () => {
+ it('converts an rgb color string to an object with `type` and `value` keys', () => {
+ const { type, values } = decomposeColor('rgb(255, 255, 255)');
+ expect(type).to.equal('rgb');
+ expect(values).to.deep.equal([255, 255, 255]);
+ });
+
+ it('converts an rgba color string to an object with `type` and `value` keys', () => {
+ const { type, values } = decomposeColor('rgba(255, 255, 255, 0.5)');
+ expect(type).to.equal('rgba');
+ expect(values).to.deep.equal([255, 255, 255, 0.5]);
+ });
+
+ it('converts an hsl color string to an object with `type` and `value` keys', () => {
+ const { type, values } = decomposeColor('hsl(100, 50%, 25%)');
+ expect(type).to.equal('hsl');
+ expect(values).to.deep.equal([100, 50, 25]);
+ });
+
+ it('converts an hsla color string to an object with `type` and `value` keys', () => {
+ const { type, values } = decomposeColor('hsla(100, 50%, 25%, 0.5)');
+ expect(type).to.equal('hsla');
+ expect(values).to.deep.equal([100, 50, 25, 0.5]);
+ });
+
+ it('converts rgba hex', () => {
+ const decomposed = decomposeColor('#111111f8');
+ expect(decomposed).to.deep.equal({
+ type: 'rgba',
+ colorSpace: undefined,
+ values: [17, 17, 17, 0.973],
+ });
+ });
+ });
+
+ describe('getContrastRatio', () => {
+ it('returns a ratio for black : white', () => {
+ expect(getContrastRatio('#000', '#FFF')).to.equal(21);
+ });
+
+ it('returns a ratio for black : black', () => {
+ expect(getContrastRatio('#000', '#000')).to.equal(1);
+ });
+
+ it('returns a ratio for white : white', () => {
+ expect(getContrastRatio('#FFF', '#FFF')).to.equal(1);
+ });
+
+ it('returns a ratio for dark-grey : light-grey', () => {
+ expect(getContrastRatio('#707070', '#E5E5E5')).to.be.approximately(3.93, 0.01);
+ });
+
+ it('returns a ratio for black : light-grey', () => {
+ expect(getContrastRatio('#000', '#888')).to.be.approximately(5.92, 0.01);
+ });
+ });
+
+ describe('getLuminance', () => {
+
+ it('returns a valid luminance for rgb white ', () => {
+ expect(getLuminance('rgba(255, 255, 255)')).to.equal(1);
+ expect(getLuminance('rgb(255, 255, 255)')).to.equal(1);
+ });
+
+ it('returns a valid luminance for rgb mid-grey', () => {
+ expect(getLuminance('rgba(127, 127, 127)')).to.equal(0.212);
+ expect(getLuminance('rgb(127, 127, 127)')).to.equal(0.212);
+ });
+
+ it('returns a valid luminance for an rgb color', () => {
+ expect(getLuminance('rgb(255, 127, 0)')).to.equal(0.364);
+ });
+
+ it('returns a valid luminance from an hsl color', () => {
+ expect(getLuminance('hsl(100, 100%, 50%)')).to.equal(0.735);
+ });
+
+ it('returns an equal luminance for the same color in different formats', () => {
+ const hsl = 'hsl(100, 100%, 50%)';
+ const rgb = 'rgb(85, 255, 0)';
+ expect(getLuminance(hsl)).to.equal(getLuminance(rgb));
+ });
+
+ });
+
+ describe('emphasize', () => {
+ it('lightens a dark rgb color with the coefficient provided', () => {
+ expect(emphasize('rgb(1, 2, 3)', 0.4)).to.equal(lighten('rgb(1, 2, 3)', 0.4));
+ });
+
+ it('darkens a light rgb color with the coefficient provided', () => {
+ expect(emphasize('rgb(250, 240, 230)', 0.3)).to.equal(darken('rgb(250, 240, 230)', 0.3));
+ });
+
+ it('lightens a dark rgb color with the coefficient 0.15 by default', () => {
+ expect(emphasize('rgb(1, 2, 3)')).to.equal(lighten('rgb(1, 2, 3)', 0.15));
+ });
+
+ it('darkens a light rgb color with the coefficient 0.15 by default', () => {
+ expect(emphasize('rgb(250, 240, 230)')).to.equal(darken('rgb(250, 240, 230)', 0.15));
+ });
+
+ });
+
+ describe('alpha', () => {
+ it('converts an rgb color to an rgba color with the value provided', () => {
+ expect(alpha('rgb(1, 2, 3)', 0.4)).to.equal('rgba(1, 2, 3, 0.4)');
+ });
+
+ it('updates an rgba color with the alpha value provided', () => {
+ expect(alpha('rgba(255, 0, 0, 0.2)', 0.5)).to.equal('rgba(255, 0, 0, 0.5)');
+ });
+
+ it('converts an hsl color to an hsla color with the value provided', () => {
+ expect(alpha('hsl(0, 100%, 50%)', 0.1)).to.equal('hsla(0, 100%, 50%, 0.1)');
+ });
+
+ it('updates an hsla color with the alpha value provided', () => {
+ expect(alpha('hsla(0, 100%, 50%, 0.2)', 0.5)).to.equal('hsla(0, 100%, 50%, 0.5)');
+ });
+
+ });
+
+ describe('darken', () => {
+ it("doesn't modify rgb black", () => {
+ expect(darken('rgb(0, 0, 0)', 0.1)).to.equal('rgb(0, 0, 0)');
+ });
+
+ it('darkens rgb white to black when coefficient is 1', () => {
+ expect(darken('rgb(255, 255, 255)', 1)).to.equal('rgb(0, 0, 0)');
+ });
+
+ it('retains the alpha value in an rgba color', () => {
+ expect(darken('rgba(0, 0, 0, 0.5)', 0.1)).to.equal('rgba(0, 0, 0, 0.5)');
+ });
+
+ it('darkens rgb white by 10% when coefficient is 0.1', () => {
+ expect(darken('rgb(255, 255, 255)', 0.1)).to.equal('rgb(229, 229, 229)');
+ });
+
+ it('darkens rgb red by 50% when coefficient is 0.5', () => {
+ expect(darken('rgb(255, 0, 0)', 0.5)).to.equal('rgb(127, 0, 0)');
+ });
+
+ it('darkens rgb grey by 50% when coefficient is 0.5', () => {
+ expect(darken('rgb(127, 127, 127)', 0.5)).to.equal('rgb(63, 63, 63)');
+ });
+
+ it("doesn't modify rgb colors when coefficient is 0", () => {
+ expect(darken('rgb(255, 255, 255)', 0)).to.equal('rgb(255, 255, 255)');
+ });
+
+ it('darkens hsl red by 50% when coefficient is 0.5', () => {
+ expect(darken('hsl(0, 100%, 50%)', 0.5)).to.equal('hsl(0, 100%, 25%)');
+ });
+
+ it("doesn't modify hsl colors when coefficient is 0", () => {
+ expect(darken('hsl(0, 100%, 50%)', 0)).to.equal('hsl(0, 100%, 50%)');
+ });
+
+ it("doesn't modify hsl colors when l is 0%", () => {
+ expect(darken('hsl(0, 50%, 0%)', 0.5)).to.equal('hsl(0, 50%, 0%)');
+ });
+
+ });
+
+ describe('lighten', () => {
+ it("doesn't modify rgb white", () => {
+ expect(lighten('rgb(255, 255, 255)', 0.1)).to.equal('rgb(255, 255, 255)');
+ });
+
+ it('lightens rgb black to white when coefficient is 1', () => {
+ expect(lighten('rgb(0, 0, 0)', 1)).to.equal('rgb(255, 255, 255)');
+ });
+
+ it('retains the alpha value in an rgba color', () => {
+ expect(lighten('rgba(255, 255, 255, 0.5)', 0.1)).to.equal('rgba(255, 255, 255, 0.5)');
+ });
+
+ it('lightens rgb black by 10% when coefficient is 0.1', () => {
+ expect(lighten('rgb(0, 0, 0)', 0.1)).to.equal('rgb(25, 25, 25)');
+ });
+
+ it('lightens rgb red by 50% when coefficient is 0.5', () => {
+ expect(lighten('rgb(255, 0, 0)', 0.5)).to.equal('rgb(255, 127, 127)');
+ });
+
+ it('lightens rgb grey by 50% when coefficient is 0.5', () => {
+ expect(lighten('rgb(127, 127, 127)', 0.5)).to.equal('rgb(191, 191, 191)');
+ });
+
+ it("doesn't modify rgb colors when coefficient is 0", () => {
+ expect(lighten('rgb(127, 127, 127)', 0)).to.equal('rgb(127, 127, 127)');
+ });
+
+ it('lightens hsl red by 50% when coefficient is 0.5', () => {
+ expect(lighten('hsl(0, 100%, 50%)', 0.5)).to.equal('hsl(0, 100%, 75%)');
+ });
+
+ it("doesn't modify hsl colors when coefficient is 0", () => {
+ expect(lighten('hsl(0, 100%, 50%)', 0)).to.equal('hsl(0, 100%, 50%)');
+ });
+
+ it("doesn't modify hsl colors when `l` is 100%", () => {
+ expect(lighten('hsl(0, 50%, 100%)', 0.5)).to.equal('hsl(0, 50%, 100%)');
+ });
+
+ });
+});
diff --git a/packages/taler-wallet-webextension/src/mui/colors/manipulation.ts b/packages/taler-wallet-webextension/src/mui/colors/manipulation.ts
new file mode 100644
index 000000000..633c80c94
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/mui/colors/manipulation.ts
@@ -0,0 +1,273 @@
+
+export type ColorFormat = ColorFormatWithAlpha | ColorFormatWithoutAlpha
+export type ColorFormatWithAlpha = 'rgb' | 'hsl';
+export type ColorFormatWithoutAlpha = 'rgba' | 'hsla';
+export type ColorObject = ColorObjectWithAlpha | ColorObjectWithoutAlpha
+export interface ColorObjectWithAlpha {
+ type: ColorFormatWithAlpha;
+ values: [number, number, number];
+ colorSpace?: 'srgb' | 'display-p3' | 'a98-rgb' | 'prophoto-rgb' | 'rec-2020';
+}
+export interface ColorObjectWithoutAlpha {
+ type: ColorFormatWithoutAlpha;
+ values: [number, number, number, number];
+ colorSpace?: 'srgb' | 'display-p3' | 'a98-rgb' | 'prophoto-rgb' | 'rec-2020';
+}
+
+
+/**
+ * Returns a number whose value is limited to the given range.
+ * @param {number} value The value to be clamped
+ * @param {number} min The lower boundary of the output range
+ * @param {number} max The upper boundary of the output range
+ * @returns {number} A number in the range [min, max]
+ */
+function clamp(value: number, min: number = 0, max: number = 1): number {
+ // if (process.env.NODE_ENV !== 'production') {
+ // if (value < min || value > max) {
+ // console.error(`MUI: The value provided ${value} is out of range [${min}, ${max}].`);
+ // }
+ // }
+
+ return Math.min(Math.max(min, value), max);
+}
+
+/**
+ * Converts a color from CSS hex format to CSS rgb format.
+ * @param {string} color - Hex color, i.e. #nnn or #nnnnnn
+ * @returns {string} A CSS rgb color string
+ */
+export function hexToRgb(color: string): string {
+ color = color.substr(1);
+
+ const re = new RegExp(`.{1,${color.length >= 6 ? 2 : 1}}`, 'g');
+ let colors = color.match(re);
+
+ if (colors && colors[0].length === 1) {
+ colors = colors.map((n) => n + n);
+ }
+
+ return colors
+ ? `rgb${colors.length === 4 ? 'a' : ''}(${colors
+ .map((n, index) => {
+ return index < 3 ? parseInt(n, 16) : Math.round((parseInt(n, 16) / 255) * 1000) / 1000;
+ })
+ .join(', ')})`
+ : '';
+}
+
+function intToHex(int: number): string {
+ const hex = int.toString(16);
+ return hex.length === 1 ? `0${hex}` : hex;
+}
+
+/**
+ * Returns an object with the type and values of a color.
+ *
+ * Note: Does not support rgb % values.
+ * @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
+ * @returns {object} - A MUI color object: {type: string, values: number[]}
+ */
+export function decomposeColor(color: string): ColorObject {
+ const colorSpace = undefined;
+ if (color.charAt(0) === '#') {
+ return decomposeColor(hexToRgb(color));
+ }
+
+ const marker = color.indexOf('(');
+ const type = color.substring(0, marker);
+ if (type != 'rgba' && type != 'hsla' && type != 'rgb' && type != 'hsl') {
+ }
+
+ const values = color.substring(marker + 1, color.length - 1).split(',')
+ if (type == 'rgb' || type == 'hsl') {
+ return { type, colorSpace, values: [parseFloat(values[0]), parseFloat(values[1]), parseFloat(values[2])] }
+ }
+ if (type == 'rgba' || type == 'hsla') {
+ return { type, colorSpace, values: [parseFloat(values[0]), parseFloat(values[1]), parseFloat(values[2]), parseFloat(values[3])] }
+ }
+ throw new Error(`Unsupported '${color}' color. The following formats are supported: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()`)
+}
+
+/**
+ * Converts a color object with type and values to a string.
+ * @param {object} color - Decomposed color
+ * @param {string} color.type - One of: 'rgb', 'rgba', 'hsl', 'hsla'
+ * @param {array} color.values - [n,n,n] or [n,n,n,n]
+ * @returns {string} A CSS color string
+ */
+export function recomposeColor(color: ColorObject): string {
+ const { type, values: valuesNum } = color;
+
+ const valuesStr: string[] = [];
+ if (type.indexOf('rgb') !== -1) {
+ // Only convert the first 3 values to int (i.e. not alpha)
+ valuesNum.map((n, i) => (i < 3 ? parseInt(String(n), 10) : n)).forEach((n, i) => valuesStr[i] = String(n));
+ } else if (type.indexOf('hsl') !== -1) {
+ valuesStr[0] = String(valuesNum[0])
+ valuesStr[1] = `${valuesNum[1]}%`;
+ valuesStr[2] = `${valuesNum[2]}%`;
+ if (type === 'hsla') {
+ valuesStr[3] = String(valuesNum[3])
+ }
+ }
+
+ return `${type}(${valuesStr.join(', ')})`;
+}
+
+/**
+ * Converts a color from CSS rgb format to CSS hex format.
+ * @param {string} color - RGB color, i.e. rgb(n, n, n)
+ * @returns {string} A CSS rgb color string, i.e. #nnnnnn
+ */
+export function rgbToHex(color: string): string {
+ // Idempotent
+ if (color.indexOf('#') === 0) {
+ return color;
+ }
+
+ const { values } = decomposeColor(color);
+ return `#${values.map((n, i) => intToHex(i === 3 ? Math.round(255 * n) : n)).join('')}`;
+}
+
+/**
+ * Converts a color from hsl format to rgb format.
+ * @param {string} color - HSL color values
+ * @returns {string} rgb color values
+ */
+export function hslToRgb(color: string): string {
+ const colorObj = decomposeColor(color);
+ const { values } = colorObj;
+ const h = values[0];
+ const s = values[1] / 100;
+ const l = values[2] / 100;
+ const a = s * Math.min(l, 1 - l);
+ const f = (n: number, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
+
+ if (colorObj.type === 'hsla') {
+ return recomposeColor({
+ type: 'rgba', values: [
+ Math.round(f(0) * 255),
+ Math.round(f(8) * 255),
+ Math.round(f(4) * 255),
+ colorObj.values[3]
+ ]
+ })
+ }
+
+ return recomposeColor({
+ type: 'rgb', values: [
+ Math.round(f(0) * 255),
+ Math.round(f(8) * 255),
+ Math.round(f(4) * 255)]
+ });
+}
+/**
+ * The relative brightness of any point in a color space,
+ * normalized to 0 for darkest black and 1 for lightest white.
+ *
+ * Formula: https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
+ * @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color()
+ * @returns {number} The relative brightness of the color in the range 0 - 1
+ */
+export function getLuminance(color: string): number {
+ const colorObj = decomposeColor(color);
+
+ const rgb2 = colorObj.type === 'hsl' ? decomposeColor(hslToRgb(color)).values : colorObj.values;
+ const rgb = rgb2.map((val) => {
+ val /= 255; // normalized
+ return val <= 0.03928 ? val / 12.92 : ((val + 0.055) / 1.055) ** 2.4;
+ }) as typeof rgb2;
+
+ // Truncate at 3 digits
+ return Number((0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]).toFixed(3));
+}
+
+/**
+ * Calculates the contrast ratio between two colors.
+ *
+ * Formula: https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
+ * @param {string} foreground - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
+ * @param {string} background - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
+ * @returns {number} A contrast ratio value in the range 0 - 21.
+ */
+export function getContrastRatio(foreground: string, background: string): number {
+ const lumA = getLuminance(foreground);
+ const lumB = getLuminance(background);
+ return (Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05);
+}
+
+/**
+ * Sets the absolute transparency of a color.
+ * Any existing alpha values are overwritten.
+ * @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color()
+ * @param {number} value - value to set the alpha channel to in the range 0 - 1
+ * @returns {string} A CSS color string. Hex input values are returned as rgb
+ */
+export function alpha(color: string, value: number): string {
+ const colorObj = decomposeColor(color);
+ value = clamp(value);
+
+ if (colorObj.type === 'rgb' || colorObj.type === 'hsl') {
+ colorObj.type += 'a';
+ }
+ colorObj.values[3] = value;
+
+ return recomposeColor(colorObj);
+}
+
+/**
+ * Darkens a color.
+ * @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color()
+ * @param {number} coefficient - multiplier in the range 0 - 1
+ * @returns {string} A CSS color string. Hex input values are returned as rgb
+ */
+export function darken(color: string, coefficient: number): string {
+ const colorObj = decomposeColor(color);
+ coefficient = clamp(coefficient);
+
+ if (colorObj.type.indexOf('hsl') !== -1) {
+ colorObj.values[2] *= 1 - coefficient;
+ } else if (colorObj.type.indexOf('rgb') !== -1 || colorObj.type.indexOf('color') !== -1) {
+ for (let i = 0; i < 3; i += 1) {
+ colorObj.values[i] *= 1 - coefficient;
+ }
+ }
+ return recomposeColor(colorObj);
+}
+
+/**
+ * Lightens a color.
+ * @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color()
+ * @param {number} coefficient - multiplier in the range 0 - 1
+ * @returns {string} A CSS color string. Hex input values are returned as rgb
+ */
+export function lighten(color: string, coefficient: number): string {
+ const colorObj = decomposeColor(color);
+ coefficient = clamp(coefficient);
+
+ if (colorObj.type.indexOf('hsl') !== -1) {
+ colorObj.values[2] += (100 - colorObj.values[2]) * coefficient;
+ } else if (colorObj.type.indexOf('rgb') !== -1) {
+ for (let i = 0; i < 3; i += 1) {
+ colorObj.values[i] += (255 - colorObj.values[i]) * coefficient;
+ }
+ } else if (colorObj.type.indexOf('color') !== -1) {
+ for (let i = 0; i < 3; i += 1) {
+ colorObj.values[i] += (1 - colorObj.values[i]) * coefficient;
+ }
+ }
+
+ return recomposeColor(colorObj);
+}
+
+/**
+ * Darken or lighten a color, depending on its luminance.
+ * Light colors are darkened, dark colors are lightened.
+ * @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color()
+ * @param {number} coefficient=0.15 - multiplier in the range 0 - 1
+ * @returns {string} A CSS color string. Hex input values are returned as rgb
+ */
+export function emphasize(color: string, coefficient: number = 0.15): string {
+ return getLuminance(color) > 0.5 ? darken(color, coefficient) : lighten(color, coefficient);
+}