csx
Version:
Utility functions for TypeStyle
712 lines (695 loc) • 25.3 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.csx = {}));
}(this, function (exports) { 'use strict';
/**
* Returns the value with '' around it. Any 's will be escaped \' in the output
*/
function calc(exp) {
return "calc(" + exp + ")";
}
/**
* Returns the value with '' around it. Any 's will be escaped \' in the output
*/
function quote(val) {
var val2 = (val || val === 0 ? val.toString() : '').replace(/\'/g, "\\'");
return "'" + val2 + "'";
}
/**
* Returns the value with !important on the end. If the value provided is a CSSHelper, it will
* be converted to a string by necessity, but will look like it is the original type to TypeScript.
*/
function important(val) {
if (!val && val !== 0) {
return '';
}
return val.toString() + " !important";
}
/**
* Returns the string in a url()
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/url
*/
function url(val) {
return "url(" + (val || '') + ")";
}
/**
* Returns the value as a string or an empty string if null or undefined.
* @param value
* @param fallbackValue
*/
function coalesce(value) {
return !value && value !== 0 ? '' : value.toString();
}
function background() {
var output = '';
for (var i = 0; i < arguments.length; i++) {
var background_1 = arguments[i];
var backgroundSize = background_1.size
? '/' + background_1.size
: '';
var backgroundParts = [
coalesce(background_1.image),
coalesce(background_1.position) + backgroundSize,
coalesce(background_1.repeat),
coalesce(background_1.origin),
coalesce(background_1.clip),
coalesce(background_1.attachment),
coalesce(background_1.color),
];
var backgroundString = backgroundParts.filter(Boolean).join(' ');
output += (output.length && backgroundString ? ', ' : '') + backgroundString;
}
return output;
}
var functionExpression = /[\s]*([a-z-]+)[\s]*\([\s]*([^\)]+)[\s]*\)[\s]*/i;
var floatExpression = /^(\-?\d+\.?\d{0,5})/;
var formatUnit = function (unit) { return function (val) { return (val + unit); }; };
var toFloat = parseFloat;
function ensurePercent(value) {
return typeof value === 'number'
? value
: toFloat(value) * .01;
}
function formatPercent(value) {
return (formatFloat(value * 100)) + '%';
}
/**
* Returns a number formatted to a max number of 5 decimal places
*/
function formatFloat(n) {
return floatExpression.exec(n.toString())[1];
}
function ensureLength(value) {
if (value === null || value === undefined) {
return undefined;
}
// convert to number
var number = +value;
// validate conversion worked (NaN will not equal NaN)
if (number === number) {
return value + 'px';
}
return value;
}
function parseCSSFunction(stringValue) {
var matches = functionExpression.exec(stringValue);
if (!matches || !matches.length) {
return undefined;
}
return [matches[1]].concat(matches[2].split(','));
}
function cssFunction(functionName, params) {
var parts = Array.prototype.join.call(params, ', ');
return functionName + "(" + parts + ")";
}
function createFunction(name) {
return (function () {
return cssFunction(name, arguments);
});
}
var filter = function (args, condition) {
return Array.prototype.filter.call(args, condition);
};
/**
* Returns the number with a suffix of %
*/
var percent = formatUnit('%');
/**
* Returns the number with a suffix of deg
*/
var deg = formatUnit('deg');
/**
* Returns the number with a suffix of em
*/
var em = formatUnit('em');
/**
* Returns the number with a suffix of ex
*/
var ex = formatUnit('ex');
/**
* Returns the number with a suffix of px
*/
var px = formatUnit('px');
/**
* Returns the number with a suffix of rad
*/
var rad = formatUnit('rad');
/**
* Returns the number with a suffix of rem
*/
var rem = formatUnit('rem');
/**
* Returns the number with a suffix of vh
*/
var viewHeight = formatUnit('vh');
/**
* Returns the number with a suffix of vw
*/
var viewWidth = formatUnit('vw');
/**
* Returns the number with a suffix of turn
*/
var turn = formatUnit('turn');
var delimited = function (delimiter) {
return function () {
return filter(arguments, function (s) { return s || s === 0; })
.map(function (s) { return typeof s === 'number' ? px(s) : s.toString(); })
.join(delimiter);
};
};
var params = delimited(' ');
var list = delimited(',');
/**
* Returns the value with '' around it. Any 's will be escaped \' in the output
*/
function border(p) {
return params(p.color, p.style, ensureLength(p.width));
}
var borderColor = params;
var borderStyle = params;
var borderWidth = params;
var math = Math;
var round = math.round;
/**
* Rounds a decimal by multiplying it by a factor, rounding it, and then dividing it by that same factor
* @param n number to round
* @param factor to use 100 = scale of 2, 100000 = scale of 5
*/
function roundFloat(n, factor) {
return round(n * factor) / factor;
}
var _a;
var RGB = 'rgb', HSL = 'hsl';
var converters = (_a = {},
_a[RGB + HSL] = RGBtoHSL,
_a[HSL + RGB] = HSLtoRGB,
_a);
/**
* Describe the ceiling for each color channel for each format
*/
var maxChannelValues = {
r: 255,
g: 255,
b: 255,
h: 360,
s: 1,
l: 1,
a: 1
};
/**
* Creates a color from a hex color code or named color.
* e.g. color('red') or color('#FF0000') or color('#F00'))
*/
function color(value) {
return parseHexCode(value) || parseColorFunction(value) || rgb(255, 0, 0);
}
/**
* Creates a color from hue, saturation, and lightness. Alpha is automatically set to 100%
* @param hue The hue of the color. This should be a number between 0-360.
* @param saturation The saturation of the color. This should be a number between 0-1 or a percentage string between 0%-100%.
* @param lightness The lightness of the color. This should be a number between 0-1 or a percentage string between 0%-100%.
* @param alpha The alpha of the color. This should be a number between 0-1 or a percentage string between 0%-100%. If not specified, this defaults to 1.
*/
function hsl(hue, saturation, lightness, alpha) {
return new ColorHelper(HSL, modDegrees(hue), ensurePercent(saturation), ensurePercent(lightness), (alpha === undefined ? 1 : ensurePercent(alpha)), alpha !== undefined /* hasAlpha*/);
}
/**
* Creates a color from hue, saturation, lightness, and alpha
* @param hue The hue of the color. This should be a number between 0-360.
* @param saturation The saturation of the color. This should be a number between 0-1 or a percentage string between 0%-100%.
* @param lightness The lightness of the color. This should be a number between 0-1 or a percentage string between 0%-100%.
* @param alpha The alpha of the color. This should be a number between 0-1 or a percentage string between 0%-100%.
*/
function hsla(hue, saturation, lightness, alpha) {
return new ColorHelper(HSL, modDegrees(hue), ensurePercent(saturation), ensurePercent(lightness), ensurePercent(alpha), true);
}
/**
* Creates a color form the red, blue, and green color space. Alpha is automatically set to 100%
* @param red The red channel of the color. This should be a number between 0-255.
* @param blue The blue channel of the color. This should be a number between 0-255.
* @param green The green channel of the color. This should be a number between 0-255.
* @param alpha The alpha of the color. This should be a number between 0-1 or a percentage string between 0%-100%. If not specified, this defaults to 1.
*/
function rgb(red, blue, green, alpha) {
return new ColorHelper(RGB, red, blue, green, (alpha === undefined ? 1 : ensurePercent(alpha)), alpha !== undefined /* hasAlpha*/);
}
/**
* Creates a color form the red, blue, green, and alpha in the color space
* @param red The red channel of the color. This should be a number between 0-255.
* @param blue The blue channel of the color. This should be a number between 0-255.
* @param green The green channel of the color. This should be a number between 0-255.
* @param alpha The alpha of the color. This should be a number between 0-1 or a percentage string between 0%-100%.
*/
function rgba(red, blue, green, alpha) {
return new ColorHelper(RGB, red, blue, green, ensurePercent(alpha), true);
}
function convertHelper(toFormat, helper, forceAlpha) {
var fromFormat = helper.f, r = helper.r, g = helper.g, b = helper.b, a = helper.a;
var newAlpha = forceAlpha === undefined ? helper.o : forceAlpha;
if (fromFormat !== toFormat) {
return converters[fromFormat + toFormat](r, g, b, a, newAlpha);
}
return forceAlpha === undefined ? helper : new ColorHelper(fromFormat, r, g, b, a, newAlpha);
}
/**
* A CSS Color. Includes utilities for converting between color types
*/
var ColorHelper = /** @class */ (function () {
function ColorHelper(format, r, g, b, a, hasAlpha) {
var self = this;
self.f = format;
self.o = hasAlpha;
var isHSL = format === HSL;
self.r = clampColor(isHSL ? 'h' : 'r', r);
self.g = clampColor(isHSL ? 's' : 'g', g);
self.b = clampColor(isHSL ? 'l' : 'b', b);
self.a = clampColor('a', a);
}
/**
* Converts the stored color into string form (which is used by Free Style)
*/
ColorHelper.prototype.toString = function () {
var _a = this, hasAlpha = _a.o, format = _a.f, r = _a.r, g = _a.g, b = _a.b, a = _a.a;
var fnName;
var params;
// find function name and resolve first three channels
if (format === RGB) {
fnName = hasAlpha ? 'rgba' : RGB;
params = [round(r), round(g), round(b)];
}
else if (format === HSL) {
fnName = hasAlpha ? 'hsla' : HSL;
params = [round(r), formatPercent(roundFloat(g, 100)), formatPercent(roundFloat(b, 100))];
}
else {
throw new Error('Invalid color format');
}
// add alpha channel if needed
if (hasAlpha) {
params.push(formatFloat(roundFloat(a, 100000)));
}
// return as a string
return cssFunction(fnName, params);
};
/**
* Converts to hex rgb(255, 255, 255) to #FFFFFF
*/
ColorHelper.prototype.toHexString = function () {
var color = convertHelper(RGB, this);
return '#' + (toHex(color.r) + toHex(color.g) + toHex(color.b)).toUpperCase();
};
/**
* Converts to the Hue, Saturation, Lightness color space
*/
ColorHelper.prototype.toHSL = function () {
return convertHelper(HSL, this, false);
};
/**
* Converts to the Hue, Saturation, Lightness color space and adds an alpha channel
*/
ColorHelper.prototype.toHSLA = function () {
return convertHelper(HSL, this, true);
};
/**
* Converts to the Red, Green, Blue color space
*/
ColorHelper.prototype.toRGB = function () {
return convertHelper(RGB, this, false);
};
/**
* Converts to the Red, Green, Blue color space and adds an alpha channel
*/
ColorHelper.prototype.toRGBA = function () {
return convertHelper(RGB, this, true);
};
ColorHelper.prototype.red = function () {
var _ = this;
return (_.f === RGB ? _ : _.toRGB()).r;
};
ColorHelper.prototype.green = function () {
var _ = this;
return (_.f === RGB ? _ : _.toRGB()).g;
};
ColorHelper.prototype.blue = function () {
var _ = this;
return (_.f === RGB ? _ : _.toRGB()).b;
};
ColorHelper.prototype.hue = function () {
var _ = this;
return (_.f === HSL ? _ : _.toHSL()).r;
};
ColorHelper.prototype.saturation = function () {
var _ = this;
return (_.f === HSL ? _ : _.toHSL()).g;
};
ColorHelper.prototype.lightness = function () {
var _ = this;
return (_.f === HSL ? _ : _.toHSL()).b;
};
ColorHelper.prototype.alpha = function () {
return this.a;
};
ColorHelper.prototype.opacity = function () {
return this.a;
};
ColorHelper.prototype.invert = function () {
var _ = this;
var color2 = convertHelper(RGB, _);
return convertHelper(_.f, new ColorHelper(RGB, 255 - color2.r, 255 - color2.g, 255 - color2.b, _.a, _.o));
};
ColorHelper.prototype.lighten = function (percent, relative) {
var _ = this;
var color2 = convertHelper(HSL, _);
var max = maxChannelValues.l;
var l = color2.b + (relative ? max - color2.b : max) * ensurePercent(percent);
return convertHelper(_.f, new ColorHelper(HSL, color2.r, color2.g, l, _.a, _.o));
};
ColorHelper.prototype.darken = function (percent, relative) {
var _ = this;
var color2 = convertHelper(HSL, _);
var l = color2.b - (relative ? color2.b : maxChannelValues.l) * ensurePercent(percent);
return convertHelper(_.f, new ColorHelper(HSL, color2.r, color2.g, l, _.a, _.o));
};
ColorHelper.prototype.saturate = function (percent, relative) {
var _ = this;
var color2 = convertHelper(HSL, _);
var max = maxChannelValues.s;
var s = color2.g + (relative ? max - color2.g : max) * ensurePercent(percent);
return convertHelper(_.f, new ColorHelper(HSL, color2.r, s, color2.b, _.a, _.o));
};
ColorHelper.prototype.desaturate = function (percent, relative) {
var _ = this;
var color2 = convertHelper(HSL, _);
var max = maxChannelValues.s;
var s = color2.g - (relative ? color2.g : max) * ensurePercent(percent);
return convertHelper(_.f, new ColorHelper(HSL, color2.r, s, color2.b, _.a, _.o));
};
ColorHelper.prototype.grayscale = function () {
return this.desaturate(1);
};
ColorHelper.prototype.fade = function (percent) {
var _ = this;
var a = clampColor('a', ensurePercent(percent));
return convertHelper(_.f, new ColorHelper(_.f, _.r, _.g, _.b, a, true));
};
ColorHelper.prototype.fadeOut = function (percent, relative) {
var _ = this;
var max = 1;
var a = clampColor('a', _.a - (relative ? _.a : max) * ensurePercent(percent));
return convertHelper(_.f, new ColorHelper(_.f, _.r, _.g, _.b, a, true));
};
ColorHelper.prototype.fadeIn = function (percent, relative) {
var _ = this;
var max = 1;
var a = clampColor('a', _.a + (relative ? _.a : max) * ensurePercent(percent));
return convertHelper(_.f, new ColorHelper(_.f, _.r, _.g, _.b, a, true));
};
ColorHelper.prototype.mix = function (mixin, weight) {
var _ = this;
var color2 = ensureColor(mixin);
var g = convertHelper(RGB, _);
var b = convertHelper(RGB, color2);
var p = weight === undefined ? 0.5 : weight;
var w = 2 * p - 1;
var a = Math.abs(g.a - b.a);
var w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
var w2 = 1 - w1;
var helper = new ColorHelper(RGB, round(g.r * w1 + b.r * w2), round(g.g * w1 + b.g * w2), round(g.b * w1 + b.b * w2), g.a * p + b.a * (1 - p), _.o || color2.o);
return convertHelper(this.f, helper);
};
ColorHelper.prototype.tint = function (weight) {
return rgb(255, 255, 255).mix(this, weight);
};
ColorHelper.prototype.shade = function (weight) {
return rgb(0, 0, 0).mix(this, weight);
};
ColorHelper.prototype.spin = function (degrees) {
var _ = this;
var color2 = convertHelper(HSL, _);
return convertHelper(_.f, new ColorHelper(HSL, modDegrees(color2.r + degrees), color2.g, color2.b, _.a, _.o));
};
return ColorHelper;
}());
function toHex(n) {
var i = round(n);
return (i < 16 ? '0' : '') + i.toString(16);
}
function modDegrees(n) {
// note: maybe there is a way to simplify this
return ((n < 0 ? 360 : 0) + n % 360) % 360;
}
function RGBtoHSL(r, g, b, a, hasAlpha) {
var newR = r / 255;
var newG = g / 255;
var newB = b / 255;
var min = Math.min(newR, newG, newB);
var max = Math.max(newR, newG, newB);
var l = (min + max) / 2;
var delta = max - min;
var h;
if (max === min) {
h = 0;
}
else if (newR === max) {
h = (newG - newB) / delta;
}
else if (newG === max) {
h = 2 + (newB - newR) / delta;
}
else if (newB === max) {
h = 4 + (newR - newG) / delta;
}
else {
h = 0;
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
var s;
if (max === min) {
s = 0;
}
else if (l <= 0.5) {
s = delta / (max + min);
}
else {
s = delta / (2 - max - min);
}
return new ColorHelper(HSL, h, s, l, a, hasAlpha);
}
function HSLtoRGB(r, g, b, a, hasAlpha) {
var newH = r / 360;
var newS = g;
var newL = b;
if (newS === 0) {
var val = newL * 255;
return new ColorHelper(RGB, val, val, val, a, hasAlpha);
}
var t2 = newL < 0.5 ? newL * (1 + newS) : newL + newS - newL * newS;
var t1 = 2 * newL - t2;
var newR = 0, newG = 0, newB = 0;
for (var i = 0; i < 3; i++) {
var t3 = newH + 1 / 3 * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
var val = void 0;
if (6 * t3 < 1) {
val = t1 + (t2 - t1) * 6 * t3;
}
else if (2 * t3 < 1) {
val = t2;
}
else if (3 * t3 < 2) {
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
}
else {
val = t1;
}
val *= 255;
// manually set variables instead of using an array
if (i === 0) {
newR = val;
}
else if (i === 1) {
newG = val;
}
else {
newB = val;
}
}
return new ColorHelper(RGB, newR, newG, newB, a, hasAlpha);
}
function clampColor(channel, value) {
var min = 0;
var max = maxChannelValues[channel];
return value < min ? min : value > max ? max : value;
}
function ensureColor(c) {
return c instanceof ColorHelper ? c : color(c);
}
function parseHexCode(stringValue) {
var match = stringValue.match(/#(([a-f0-9]{6})|([a-f0-9]{3}))$/i);
if (!match) {
return undefined;
}
var hex = match[1];
var hexColor = parseInt(hex.length === 3 ? hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] : hex, 16);
var r = (hexColor >> 16) & 0xff;
var b = (hexColor >> 8) & 0xff;
var g = hexColor & 0xff;
return new ColorHelper(RGB, r, b, g, 1, false);
}
function parseColorFunction(colorString) {
var cssParts = parseCSSFunction(colorString);
if (!cssParts || !(cssParts.length === 4 || cssParts.length === 5)) {
return undefined;
}
var fn = cssParts[0];
var isRGBA = fn === 'rgba';
var isHSLA = fn === 'hsla';
var isRGB = fn === RGB;
var isHSL = fn === HSL;
var hasAlpha = isHSLA || isRGBA;
var type;
if (isRGB || isRGBA) {
type = RGB;
}
else if (isHSL || isHSLA) {
type = HSL;
}
else {
throw new Error('unsupported color string');
}
var r = toFloat(cssParts[1]);
var g = isRGB || isRGBA ? toFloat(cssParts[2]) : ensurePercent(cssParts[2]);
var b = isRGB || isRGBA ? toFloat(cssParts[3]) : ensurePercent(cssParts[3]);
var a = hasAlpha ? toFloat(cssParts[4]) : 1;
return new ColorHelper(type, r, g, b, a, hasAlpha);
}
/**
* Helper for the linear-gradient function in CSS
* https://drafts.csswg.org/css-images-3/#funcdef-linear-gradient
*/
function linearGradient(position) {
var colors = [];
for (var _i = 1; _i < arguments.length; _i++) {
colors[_i - 1] = arguments[_i];
}
return cssFunction('linear-gradient', [position].concat(colors.map(flattenColorStops)));
}
/**
* Helper for the repeating-linear-gradient function in CSS
* https://drafts.csswg.org/css-images-3/#funcdef-repeating-linear-gradient
*/
function repeatingLinearGradient(position) {
var colors = [];
for (var _i = 1; _i < arguments.length; _i++) {
colors[_i - 1] = arguments[_i];
}
return cssFunction('repeating-linear-gradient', [position].concat(colors.map(flattenColorStops)));
}
/**
* Single CSSColorStop => string conversion is like:
* 'x'=>'x'
* ['x', '50%'] => 'x 50%'
**/
function flattenColorStops(c) {
return Array.isArray(c) ? c.map(function (s) { return s.toString(); }).join(' ') : c.toString();
}
var margin = params;
var padding = params;
/**
* The CSS transform property lets you modify the coordinate space of the CSS visual formatting model. Using it, elements can be translated, rotated, scaled, and skewed.
* Returns the transforms as a delimited string by space or returns 'none' if no arguments are provided
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/transform
*/
function transform() {
var transforms = [];
for (var _i = 0; _i < arguments.length; _i++) {
transforms[_i] = arguments[_i];
}
return transforms.length ? transforms.join(' ') : 'none';
}
var matrix = createFunction('matrix');
var matrix3d = createFunction('matrix3d');
var perspective = createFunction('perspective');
var rotate = createFunction('rotate');
var rotate3d = createFunction('rotate3d');
var rotateX = createFunction('rotateX');
var rotateY = createFunction('rotateY');
var rotateZ = createFunction('rotateZ');
var scale = createFunction('scale');
var scale3d = createFunction('scale3d');
var scaleX = createFunction('scaleX');
var scaleY = createFunction('scaleY');
var scaleZ = createFunction('scaleZ');
var skew = createFunction('skew');
var skewX = createFunction('skewX');
var skewY = createFunction('skewY');
var translate = createFunction('translate');
var translate3d = createFunction('translate3d');
var translateX = createFunction('translateX');
var translateY = createFunction('translateY');
var translateZ = createFunction('translateZ');
exports.ColorHelper = ColorHelper;
exports.background = background;
exports.border = border;
exports.borderColor = borderColor;
exports.borderStyle = borderStyle;
exports.borderWidth = borderWidth;
exports.calc = calc;
exports.coalesce = coalesce;
exports.color = color;
exports.deg = deg;
exports.em = em;
exports.ex = ex;
exports.hsl = hsl;
exports.hsla = hsla;
exports.important = important;
exports.linearGradient = linearGradient;
exports.list = list;
exports.margin = margin;
exports.matrix = matrix;
exports.matrix3d = matrix3d;
exports.padding = padding;
exports.params = params;
exports.percent = percent;
exports.perspective = perspective;
exports.px = px;
exports.quote = quote;
exports.rad = rad;
exports.rem = rem;
exports.repeatingLinearGradient = repeatingLinearGradient;
exports.rgb = rgb;
exports.rgba = rgba;
exports.rotate = rotate;
exports.rotate3d = rotate3d;
exports.rotateX = rotateX;
exports.rotateY = rotateY;
exports.rotateZ = rotateZ;
exports.scale = scale;
exports.scale3d = scale3d;
exports.scaleX = scaleX;
exports.scaleY = scaleY;
exports.scaleZ = scaleZ;
exports.skew = skew;
exports.skewX = skewX;
exports.skewY = skewY;
exports.transform = transform;
exports.translate = translate;
exports.translate3d = translate3d;
exports.translateX = translateX;
exports.translateY = translateY;
exports.translateZ = translateZ;
exports.turn = turn;
exports.url = url;
exports.viewHeight = viewHeight;
exports.viewWidth = viewWidth;
Object.defineProperty(exports, '__esModule', { value: true });
}));