aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/mui/colors/manipulation.ts
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/manipulation.ts
parent6bc244cc1e0e08c4d86161fdf2b6a52b3f9f452e (diff)
downloadwallet-core-1607c728bca19a003ca08b64b4d2afc73e4d1e2a.tar.xz
first banner implementation with mui
Diffstat (limited to 'packages/taler-wallet-webextension/src/mui/colors/manipulation.ts')
-rw-r--r--packages/taler-wallet-webextension/src/mui/colors/manipulation.ts273
1 files changed, 273 insertions, 0 deletions
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);
+}