@mui/system
Version:
MUI System is a set of CSS utilities to help you build custom designs more efficiently. It makes it possible to rapidly lay out custom designs.
375 lines (360 loc) • 13 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.alpha = alpha;
exports.blend = blend;
exports.colorChannel = void 0;
exports.darken = darken;
exports.decomposeColor = decomposeColor;
exports.emphasize = emphasize;
exports.getContrastRatio = getContrastRatio;
exports.getLuminance = getLuminance;
exports.hexToRgb = hexToRgb;
exports.hslToRgb = hslToRgb;
exports.lighten = lighten;
exports.private_safeAlpha = private_safeAlpha;
exports.private_safeColorChannel = void 0;
exports.private_safeDarken = private_safeDarken;
exports.private_safeEmphasize = private_safeEmphasize;
exports.private_safeLighten = private_safeLighten;
exports.recomposeColor = recomposeColor;
exports.rgbToHex = rgbToHex;
var _formatMuiErrorMessage = _interopRequireDefault(require("@mui/utils/formatMuiErrorMessage"));
var _clamp = _interopRequireDefault(require("@mui/utils/clamp"));
/* eslint-disable @typescript-eslint/naming-convention */
/**
* 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 clampWrapper(value, min = 0, max = 1) {
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 (0, _clamp.default)(value, min, 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
*/
function hexToRgb(color) {
color = color.slice(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);
}
if (process.env.NODE_ENV !== 'production') {
if (color.length !== color.trim().length) {
console.error(`MUI: The color: "${color}" is invalid. Make sure the color input doesn't contain leading/trailing space.`);
}
}
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) {
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(), color()
* @returns {object} - A MUI color object: {type: string, values: number[]}
*/
function decomposeColor(color) {
// Idempotent
if (color.type) {
return color;
}
if (color.charAt(0) === '#') {
return decomposeColor(hexToRgb(color));
}
const marker = color.indexOf('(');
const type = color.substring(0, marker);
if (!['rgb', 'rgba', 'hsl', 'hsla', 'color'].includes(type)) {
throw new Error(process.env.NODE_ENV !== "production" ? `MUI: Unsupported \`${color}\` color.\n` + 'The following formats are supported: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color().' : (0, _formatMuiErrorMessage.default)(9, color));
}
let values = color.substring(marker + 1, color.length - 1);
let colorSpace;
if (type === 'color') {
values = values.split(' ');
colorSpace = values.shift();
if (values.length === 4 && values[3].charAt(0) === '/') {
values[3] = values[3].slice(1);
}
if (!['srgb', 'display-p3', 'a98-rgb', 'prophoto-rgb', 'rec-2020'].includes(colorSpace)) {
throw new Error(process.env.NODE_ENV !== "production" ? `MUI: unsupported \`${colorSpace}\` color space.\n` + 'The following color spaces are supported: srgb, display-p3, a98-rgb, prophoto-rgb, rec-2020.' : (0, _formatMuiErrorMessage.default)(10, colorSpace));
}
} else {
values = values.split(',');
}
values = values.map(value => parseFloat(value));
return {
type,
values,
colorSpace
};
}
/**
* Returns a channel created from the input color.
*
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla(), color()
* @returns {string} - The channel for the color, that can be used in rgba or hsla colors
*/
const colorChannel = color => {
const decomposedColor = decomposeColor(color);
return decomposedColor.values.slice(0, 3).map((val, idx) => decomposedColor.type.includes('hsl') && idx !== 0 ? `${val}%` : val).join(' ');
};
exports.colorChannel = colorChannel;
const private_safeColorChannel = (color, warning) => {
try {
return colorChannel(color);
} catch (error) {
if (warning && process.env.NODE_ENV !== 'production') {
console.warn(warning);
}
return color;
}
};
/**
* 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', 'color'
* @param {array} color.values - [n,n,n] or [n,n,n,n]
* @returns {string} A CSS color string
*/
exports.private_safeColorChannel = private_safeColorChannel;
function recomposeColor(color) {
const {
type,
colorSpace
} = color;
let {
values
} = color;
if (type.includes('rgb')) {
// Only convert the first 3 values to int (i.e. not alpha)
values = values.map((n, i) => i < 3 ? parseInt(n, 10) : n);
} else if (type.includes('hsl')) {
values[1] = `${values[1]}%`;
values[2] = `${values[2]}%`;
}
if (type.includes('color')) {
values = `${colorSpace} ${values.join(' ')}`;
} else {
values = `${values.join(', ')}`;
}
return `${type}(${values})`;
}
/**
* 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
*/
function rgbToHex(color) {
// Idempotent
if (color.startsWith('#')) {
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
*/
function hslToRgb(color) {
color = decomposeColor(color);
const {
values
} = color;
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, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
let type = 'rgb';
const rgb = [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)];
if (color.type === 'hsla') {
type += 'a';
rgb.push(values[3]);
}
return recomposeColor({
type,
values: rgb
});
}
/**
* 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
*/
function getLuminance(color) {
color = decomposeColor(color);
let rgb = color.type === 'hsl' || color.type === 'hsla' ? decomposeColor(hslToRgb(color)).values : color.values;
rgb = rgb.map(val => {
if (color.type !== 'color') {
val /= 255; // normalized
}
return val <= 0.03928 ? val / 12.92 : ((val + 0.055) / 1.055) ** 2.4;
});
// 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.
*/
function getContrastRatio(foreground, background) {
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
*/
function alpha(color, value) {
color = decomposeColor(color);
value = clampWrapper(value);
if (color.type === 'rgb' || color.type === 'hsl') {
color.type += 'a';
}
if (color.type === 'color') {
color.values[3] = `/${value}`;
} else {
color.values[3] = value;
}
return recomposeColor(color);
}
function private_safeAlpha(color, value, warning) {
try {
return alpha(color, value);
} catch (error) {
if (warning && process.env.NODE_ENV !== 'production') {
console.warn(warning);
}
return color;
}
}
/**
* 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
*/
function darken(color, coefficient) {
color = decomposeColor(color);
coefficient = clampWrapper(coefficient);
if (color.type.includes('hsl')) {
color.values[2] *= 1 - coefficient;
} else if (color.type.includes('rgb') || color.type.includes('color')) {
for (let i = 0; i < 3; i += 1) {
color.values[i] *= 1 - coefficient;
}
}
return recomposeColor(color);
}
function private_safeDarken(color, coefficient, warning) {
try {
return darken(color, coefficient);
} catch (error) {
if (warning && process.env.NODE_ENV !== 'production') {
console.warn(warning);
}
return color;
}
}
/**
* 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
*/
function lighten(color, coefficient) {
color = decomposeColor(color);
coefficient = clampWrapper(coefficient);
if (color.type.includes('hsl')) {
color.values[2] += (100 - color.values[2]) * coefficient;
} else if (color.type.includes('rgb')) {
for (let i = 0; i < 3; i += 1) {
color.values[i] += (255 - color.values[i]) * coefficient;
}
} else if (color.type.includes('color')) {
for (let i = 0; i < 3; i += 1) {
color.values[i] += (1 - color.values[i]) * coefficient;
}
}
return recomposeColor(color);
}
function private_safeLighten(color, coefficient, warning) {
try {
return lighten(color, coefficient);
} catch (error) {
if (warning && process.env.NODE_ENV !== 'production') {
console.warn(warning);
}
return color;
}
}
/**
* 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
*/
function emphasize(color, coefficient = 0.15) {
return getLuminance(color) > 0.5 ? darken(color, coefficient) : lighten(color, coefficient);
}
function private_safeEmphasize(color, coefficient, warning) {
try {
return emphasize(color, coefficient);
} catch (error) {
if (warning && process.env.NODE_ENV !== 'production') {
console.warn(warning);
}
return color;
}
}
/**
* Blend a transparent overlay color with a background color, resulting in a single
* RGB color.
* @param {string} background - CSS color
* @param {string} overlay - CSS color
* @param {number} opacity - Opacity multiplier in the range 0 - 1
* @param {number} [gamma=1.0] - Gamma correction factor. For gamma-correct blending, 2.2 is usual.
*/
function blend(background, overlay, opacity, gamma = 1.0) {
const blendChannel = (b, o) => Math.round((b ** (1 / gamma) * (1 - opacity) + o ** (1 / gamma) * opacity) ** gamma);
const backgroundColor = decomposeColor(background);
const overlayColor = decomposeColor(overlay);
const rgb = [blendChannel(backgroundColor.values[0], overlayColor.values[0]), blendChannel(backgroundColor.values[1], overlayColor.values[1]), blendChannel(backgroundColor.values[2], overlayColor.values[2])];
return recomposeColor({
type: 'rgb',
values: rgb
});
}
;