766 lines
24 KiB
JavaScript
766 lines
24 KiB
JavaScript
import { compose, castArray, parseFloat as parseFloat$1, matchPercent } from '@react-pdf/fns';
|
|
import matchMedia from 'media-engine';
|
|
import hlsToHex from 'hsl-to-hex';
|
|
import colorString from 'color-string';
|
|
import parse$1 from 'postcss-value-parser/lib/parse.js';
|
|
import parseUnit from 'postcss-value-parser/lib/unit.js';
|
|
|
|
/**
|
|
* Remove nil values from array
|
|
*
|
|
* @param array - Style array
|
|
* @returns Style array without nils
|
|
*/
|
|
const compact = (array) => array.filter(Boolean);
|
|
/**
|
|
* Merges style objects array
|
|
*
|
|
* @param styles - Style array
|
|
* @returns Merged style object
|
|
*/
|
|
const mergeStyles = (styles) => styles.reduce((acc, style) => {
|
|
const s = Array.isArray(style) ? flatten(style) : style;
|
|
Object.keys(s).forEach((key) => {
|
|
if (s[key] !== null && s[key] !== undefined) {
|
|
acc[key] = s[key];
|
|
}
|
|
});
|
|
return acc;
|
|
}, {});
|
|
/**
|
|
* Flattens an array of style objects, into one aggregated style object.
|
|
*
|
|
* @param styles - Style or style array
|
|
* @returns Flattened style object
|
|
*/
|
|
const flatten = compose(mergeStyles, compact, (castArray));
|
|
|
|
/**
|
|
* Resolves media queries in styles object
|
|
*
|
|
* @param container - Container for which styles are resolved
|
|
* @param style - Style description
|
|
* @returns Resolved style object
|
|
*/
|
|
const resolveMediaQueries = (container, style) => {
|
|
return Object.keys(style).reduce((acc, key) => {
|
|
if (/@media/.test(key)) {
|
|
return {
|
|
...acc,
|
|
...matchMedia({ [key]: style[key] }, container),
|
|
};
|
|
}
|
|
return { ...acc, [key]: style[key] };
|
|
}, {});
|
|
};
|
|
|
|
const isRgb = (value) => /rgba?/g.test(value);
|
|
const isHsl = (value) => /hsla?/g.test(value);
|
|
/**
|
|
* Transform rgb color to hexa
|
|
*
|
|
* @param value - Styles value
|
|
* @returns Transformed value
|
|
*/
|
|
const parseRgb = (value) => {
|
|
const rgb = colorString.get.rgb(value);
|
|
return colorString.to.hex(rgb);
|
|
};
|
|
/**
|
|
* Transform Hsl color to hexa
|
|
*
|
|
* @param value - Styles value
|
|
* @returns Transformed value
|
|
*/
|
|
const parseHsl = (value) => {
|
|
const hsl = colorString.get.hsl(value).map(Math.round);
|
|
const hex = hlsToHex(...hsl);
|
|
return hex.toUpperCase();
|
|
};
|
|
/**
|
|
* Transform given color to hexa
|
|
*
|
|
* @param value - Styles value
|
|
* @returns Transformed value
|
|
*/
|
|
const transformColor = (value) => {
|
|
if (isRgb(value))
|
|
return parseRgb(value);
|
|
if (isHsl(value))
|
|
return parseHsl(value);
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* Parses scalar value in value and unit pairs
|
|
*
|
|
* @param value - Scalar value
|
|
* @returns Parsed value
|
|
*/
|
|
const parseValue = (value) => {
|
|
if (typeof value === 'number')
|
|
return { value, unit: undefined };
|
|
const match = /^(-?\d*\.?\d+)(in|mm|cm|pt|vh|vw|px|rem)?$/g.exec(value);
|
|
return match
|
|
? { value: parseFloat(match[1]), unit: match[2] || 'pt' }
|
|
: { value, unit: undefined };
|
|
};
|
|
/**
|
|
* Transform given scalar value
|
|
*
|
|
* @param container
|
|
* @param value - Styles value
|
|
* @returns Transformed value
|
|
*/
|
|
const transformUnit = (container, value) => {
|
|
const scalar = parseValue(value);
|
|
const outputDpi = 72;
|
|
const inputDpi = container.dpi || 72;
|
|
const mmFactor = (1 / 25.4) * outputDpi;
|
|
const cmFactor = (1 / 2.54) * outputDpi;
|
|
if (typeof scalar.value !== 'number')
|
|
return scalar.value;
|
|
switch (scalar.unit) {
|
|
case 'rem':
|
|
return scalar.value * (container.remBase || 18);
|
|
case 'in':
|
|
return scalar.value * outputDpi;
|
|
case 'mm':
|
|
return scalar.value * mmFactor;
|
|
case 'cm':
|
|
return scalar.value * cmFactor;
|
|
case 'vh':
|
|
return scalar.value * (container.height / 100);
|
|
case 'vw':
|
|
return scalar.value * (container.width / 100);
|
|
case 'px':
|
|
return Math.round(scalar.value * (outputDpi / inputDpi));
|
|
default:
|
|
return scalar.value;
|
|
}
|
|
};
|
|
|
|
const processNumberValue = (key, value) => ({
|
|
[key]: parseFloat$1(value),
|
|
});
|
|
const processUnitValue = (key, value, container) => ({
|
|
[key]: transformUnit(container, value),
|
|
});
|
|
const processColorValue = (key, value) => {
|
|
const result = { [key]: transformColor(value) };
|
|
return result;
|
|
};
|
|
const processNoopValue = (key, value) => ({
|
|
[key]: value,
|
|
});
|
|
|
|
const BORDER_SHORTHAND_REGEX = /(-?\d+(\.\d+)?(in|mm|cm|pt|vw|vh|px|rem)?)\s(\S+)\s(.+)/;
|
|
const matchBorderShorthand = (value) => value.match(BORDER_SHORTHAND_REGEX) || [];
|
|
const resolveBorderShorthand = (key, value, container) => {
|
|
const match = matchBorderShorthand(`${value}`);
|
|
if (match) {
|
|
const widthMatch = match[1] || value;
|
|
const styleMatch = match[4] || value;
|
|
const colorMatch = match[5] || value;
|
|
const style = styleMatch;
|
|
const color = colorMatch ? transformColor(colorMatch) : undefined;
|
|
const width = widthMatch ? transformUnit(container, widthMatch) : undefined;
|
|
if (key.match(/(Top|Right|Bottom|Left)$/)) {
|
|
return {
|
|
[`${key}Color`]: color,
|
|
[`${key}Style`]: style,
|
|
[`${key}Width`]: width,
|
|
};
|
|
}
|
|
if (key.match(/Color$/)) {
|
|
return {
|
|
borderTopColor: color,
|
|
borderRightColor: color,
|
|
borderBottomColor: color,
|
|
borderLeftColor: color,
|
|
};
|
|
}
|
|
if (key.match(/Style$/)) {
|
|
if (typeof style === 'number')
|
|
throw new Error(`Invalid border style: ${style}`);
|
|
return {
|
|
borderTopStyle: style,
|
|
borderRightStyle: style,
|
|
borderBottomStyle: style,
|
|
borderLeftStyle: style,
|
|
};
|
|
}
|
|
if (key.match(/Width$/)) {
|
|
if (typeof width !== 'number')
|
|
throw new Error(`Invalid border width: ${width}`);
|
|
return {
|
|
borderTopWidth: width,
|
|
borderRightWidth: width,
|
|
borderBottomWidth: width,
|
|
borderLeftWidth: width,
|
|
};
|
|
}
|
|
if (key.match(/Radius$/)) {
|
|
const radius = value ? transformUnit(container, value) : undefined;
|
|
if (typeof radius !== 'number')
|
|
throw new Error(`Invalid border radius: ${radius}`);
|
|
return {
|
|
borderTopLeftRadius: radius,
|
|
borderTopRightRadius: radius,
|
|
borderBottomRightRadius: radius,
|
|
borderBottomLeftRadius: radius,
|
|
};
|
|
}
|
|
if (typeof width !== 'number')
|
|
throw new Error(`Invalid border width: ${width}`);
|
|
if (typeof style === 'number')
|
|
throw new Error(`Invalid border style: ${style}`);
|
|
return {
|
|
borderTopColor: color,
|
|
borderTopStyle: style,
|
|
borderTopWidth: width,
|
|
borderRightColor: color,
|
|
borderRightStyle: style,
|
|
borderRightWidth: width,
|
|
borderBottomColor: color,
|
|
borderBottomStyle: style,
|
|
borderBottomWidth: width,
|
|
borderLeftColor: color,
|
|
borderLeftStyle: style,
|
|
borderLeftWidth: width,
|
|
};
|
|
}
|
|
return { [key]: value };
|
|
};
|
|
const handlers$b = {
|
|
border: (resolveBorderShorthand),
|
|
borderBottom: (resolveBorderShorthand),
|
|
borderBottomColor: (processColorValue),
|
|
borderBottomLeftRadius: (processUnitValue),
|
|
borderBottomRightRadius: (processUnitValue),
|
|
borderBottomStyle: (processNoopValue),
|
|
borderBottomWidth: (processUnitValue),
|
|
borderColor: (resolveBorderShorthand),
|
|
borderLeft: (resolveBorderShorthand),
|
|
borderLeftColor: (processColorValue),
|
|
borderLeftStyle: (processNoopValue),
|
|
borderLeftWidth: (processUnitValue),
|
|
borderRadius: (resolveBorderShorthand),
|
|
borderRight: (resolveBorderShorthand),
|
|
borderRightColor: (processColorValue),
|
|
borderRightStyle: (processNoopValue),
|
|
borderRightWidth: (processUnitValue),
|
|
borderStyle: (resolveBorderShorthand),
|
|
borderTop: (resolveBorderShorthand),
|
|
borderTopColor: (processColorValue),
|
|
borderTopLeftRadius: (processUnitValue),
|
|
borderTopRightRadius: (processUnitValue),
|
|
borderTopStyle: (processNoopValue),
|
|
borderTopWidth: (processUnitValue),
|
|
borderWidth: (resolveBorderShorthand),
|
|
};
|
|
|
|
const handlers$a = {
|
|
backgroundColor: (processColorValue),
|
|
color: (processColorValue),
|
|
opacity: (processNumberValue),
|
|
};
|
|
|
|
const handlers$9 = {
|
|
height: (processUnitValue),
|
|
maxHeight: (processUnitValue),
|
|
maxWidth: (processUnitValue),
|
|
minHeight: (processUnitValue),
|
|
minWidth: (processUnitValue),
|
|
width: (processUnitValue),
|
|
};
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/CSS/flex#values
|
|
// TODO: change flex defaults to [0, 1, 'auto'] as in spec in next major release
|
|
const flexDefaults = [1, 1, 0];
|
|
const flexAuto = [1, 1, 'auto'];
|
|
const processFlexShorthand = (key, value, container) => {
|
|
let defaults = flexDefaults;
|
|
let matches = [];
|
|
if (value === 'auto') {
|
|
defaults = flexAuto;
|
|
}
|
|
else {
|
|
matches = `${value}`.split(' ');
|
|
}
|
|
const flexGrow = parseFloat$1(matches[0] || defaults[0]);
|
|
const flexShrink = parseFloat$1(matches[1] || defaults[1]);
|
|
const flexBasis = transformUnit(container, matches[2] || defaults[2]);
|
|
return { flexGrow, flexShrink, flexBasis };
|
|
};
|
|
const handlers$8 = {
|
|
alignContent: (processNoopValue),
|
|
alignItems: (processNoopValue),
|
|
alignSelf: (processNoopValue),
|
|
flex: (processFlexShorthand),
|
|
flexBasis: (processUnitValue),
|
|
flexDirection: (processNoopValue),
|
|
flexFlow: (processNoopValue),
|
|
flexGrow: (processNumberValue),
|
|
flexShrink: (processNumberValue),
|
|
flexWrap: (processNoopValue),
|
|
justifyContent: (processNoopValue),
|
|
justifySelf: (processNoopValue),
|
|
};
|
|
|
|
const processGapShorthand = (key, value, container) => {
|
|
const match = `${value}`.split(' ');
|
|
const rowGap = transformUnit(container, match?.[0] || value);
|
|
const columnGap = transformUnit(container, match?.[1] || value);
|
|
return { rowGap, columnGap };
|
|
};
|
|
const handlers$7 = {
|
|
gap: (processGapShorthand),
|
|
columnGap: (processUnitValue),
|
|
rowGap: (processUnitValue),
|
|
};
|
|
|
|
const handlers$6 = {
|
|
aspectRatio: (processNumberValue),
|
|
bottom: (processUnitValue),
|
|
display: (processNoopValue),
|
|
left: (processUnitValue),
|
|
position: (processNoopValue),
|
|
right: (processUnitValue),
|
|
top: (processUnitValue),
|
|
overflow: (processNoopValue),
|
|
zIndex: (processNumberValue),
|
|
};
|
|
|
|
const BOX_MODEL_UNITS = 'px,in,mm,cm,pt,%,vw,vh';
|
|
const logError = (style, value) => {
|
|
const name = style.toString();
|
|
// eslint-disable-next-line no-console
|
|
console.error(`
|
|
@react-pdf/stylesheet parsing error:
|
|
${name}: ${value},
|
|
${' '.repeat(name.length + 2)}^
|
|
Unsupported ${name} value format
|
|
`);
|
|
};
|
|
/**
|
|
* @param options
|
|
* @param [options.expandsTo]
|
|
* @param [options.maxValues]
|
|
* @param [options.autoSupported]
|
|
*/
|
|
const expandBoxModel = ({ expandsTo, maxValues = 1, autoSupported = false, } = {}) => (model, value, container) => {
|
|
const nodes = parse$1(`${value}`);
|
|
const parts = [];
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
const node = nodes[i];
|
|
// value contains `calc`, `url` or other css function
|
|
// `,`, `/` or strings that unsupported by margin and padding
|
|
if (node.type === 'function' ||
|
|
node.type === 'string' ||
|
|
node.type === 'div') {
|
|
logError(model, value);
|
|
return {};
|
|
}
|
|
if (node.type === 'word') {
|
|
if (node.value === 'auto' && autoSupported) {
|
|
parts.push(node.value);
|
|
}
|
|
else {
|
|
const result = parseUnit(node.value);
|
|
// when unit isn't specified this condition is true
|
|
if (result && BOX_MODEL_UNITS.includes(result.unit)) {
|
|
parts.push(node.value);
|
|
}
|
|
else {
|
|
logError(model, value);
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// checks that we have enough parsed values
|
|
if (parts.length > maxValues) {
|
|
logError(model, value);
|
|
return {};
|
|
}
|
|
const first = transformUnit(container, parts[0]);
|
|
if (expandsTo) {
|
|
const second = transformUnit(container, parts[1] || parts[0]);
|
|
const third = transformUnit(container, parts[2] || parts[0]);
|
|
const fourth = transformUnit(container, parts[3] || parts[1] || parts[0]);
|
|
return expandsTo({ first, second, third, fourth });
|
|
}
|
|
return {
|
|
[model]: first,
|
|
};
|
|
};
|
|
|
|
const processMargin = expandBoxModel({
|
|
expandsTo: ({ first, second, third, fourth }) => ({
|
|
marginTop: first,
|
|
marginRight: second,
|
|
marginBottom: third,
|
|
marginLeft: fourth,
|
|
}),
|
|
maxValues: 4,
|
|
autoSupported: true,
|
|
});
|
|
const processMarginVertical = expandBoxModel({
|
|
expandsTo: ({ first, second }) => ({
|
|
marginTop: first,
|
|
marginBottom: second,
|
|
}),
|
|
maxValues: 2,
|
|
autoSupported: true,
|
|
});
|
|
const processMarginHorizontal = expandBoxModel({
|
|
expandsTo: ({ first, second }) => ({
|
|
marginRight: first,
|
|
marginLeft: second,
|
|
}),
|
|
maxValues: 2,
|
|
autoSupported: true,
|
|
});
|
|
const processMarginSingle = expandBoxModel({
|
|
autoSupported: true,
|
|
});
|
|
const handlers$5 = {
|
|
margin: (processMargin),
|
|
marginBottom: (processMarginSingle),
|
|
marginHorizontal: (processMarginHorizontal),
|
|
marginLeft: (processMarginSingle),
|
|
marginRight: (processMarginSingle),
|
|
marginTop: (processMarginSingle),
|
|
marginVertical: (processMarginVertical),
|
|
};
|
|
|
|
const processPadding = expandBoxModel({
|
|
expandsTo: ({ first, second, third, fourth }) => ({
|
|
paddingTop: first,
|
|
paddingRight: second,
|
|
paddingBottom: third,
|
|
paddingLeft: fourth,
|
|
}),
|
|
maxValues: 4,
|
|
});
|
|
const processPaddingVertical = expandBoxModel({
|
|
expandsTo: ({ first, second }) => ({
|
|
paddingTop: first,
|
|
paddingBottom: second,
|
|
}),
|
|
maxValues: 2,
|
|
});
|
|
const processPaddingHorizontal = expandBoxModel({
|
|
expandsTo: ({ first, second }) => ({
|
|
paddingRight: first,
|
|
paddingLeft: second,
|
|
}),
|
|
maxValues: 2,
|
|
});
|
|
const processPaddingSingle = expandBoxModel();
|
|
const handlers$4 = {
|
|
padding: (processPadding),
|
|
paddingBottom: (processPaddingSingle),
|
|
paddingHorizontal: (processPaddingHorizontal),
|
|
paddingLeft: (processPaddingSingle),
|
|
paddingRight: (processPaddingSingle),
|
|
paddingTop: (processPaddingSingle),
|
|
paddingVertical: (processPaddingVertical),
|
|
};
|
|
|
|
const offsetKeyword = (value) => {
|
|
switch (value) {
|
|
case 'top':
|
|
case 'left':
|
|
return '0%';
|
|
case 'right':
|
|
case 'bottom':
|
|
return '100%';
|
|
case 'center':
|
|
return '50%';
|
|
default:
|
|
return value;
|
|
}
|
|
};
|
|
|
|
const processObjectPosition = (key, value, container) => {
|
|
const match = `${value}`.split(' ');
|
|
const objectPositionX = offsetKeyword(transformUnit(container, match?.[0] || value));
|
|
const objectPositionY = offsetKeyword(transformUnit(container, match?.[1] || value));
|
|
return { objectPositionX, objectPositionY };
|
|
};
|
|
const processObjectPositionValue = (key, value, container) => ({
|
|
[key]: offsetKeyword(transformUnit(container, value)),
|
|
});
|
|
const handlers$3 = {
|
|
objectPosition: (processObjectPosition),
|
|
objectPositionX: (processObjectPositionValue),
|
|
objectPositionY: (processObjectPositionValue),
|
|
objectFit: (processNoopValue),
|
|
};
|
|
|
|
const castInt = (value) => {
|
|
if (typeof value === 'number')
|
|
return value;
|
|
return parseInt(value, 10);
|
|
};
|
|
|
|
const FONT_WEIGHTS = {
|
|
thin: 100,
|
|
hairline: 100,
|
|
ultralight: 200,
|
|
extralight: 200,
|
|
light: 300,
|
|
normal: 400,
|
|
medium: 500,
|
|
semibold: 600,
|
|
demibold: 600,
|
|
bold: 700,
|
|
ultrabold: 800,
|
|
extrabold: 800,
|
|
heavy: 900,
|
|
black: 900,
|
|
};
|
|
const transformFontWeight = (value) => {
|
|
if (!value)
|
|
return FONT_WEIGHTS.normal;
|
|
if (typeof value === 'number')
|
|
return value;
|
|
const lv = value.toLowerCase();
|
|
if (FONT_WEIGHTS[lv])
|
|
return FONT_WEIGHTS[lv];
|
|
return castInt(value);
|
|
};
|
|
const processFontWeight = (key, value) => {
|
|
return { [key]: transformFontWeight(value) };
|
|
};
|
|
const transformLineHeight = (value, styles, container) => {
|
|
if (value === '')
|
|
return value;
|
|
const fontSize = transformUnit(container, styles.fontSize || 18);
|
|
const lineHeight = transformUnit(container, value);
|
|
// Percent values: use this number multiplied by the element's font size
|
|
const { percent } = matchPercent(lineHeight) || {};
|
|
if (percent)
|
|
return percent * fontSize;
|
|
// Unitless values: use this number multiplied by the element's font size
|
|
return isNaN(value) ? lineHeight : lineHeight * fontSize;
|
|
};
|
|
const processLineHeight = (key, value, container, styles) => {
|
|
return {
|
|
[key]: transformLineHeight(value, styles, container),
|
|
};
|
|
};
|
|
const handlers$2 = {
|
|
direction: (processNoopValue),
|
|
fontFamily: (processNoopValue),
|
|
fontSize: (processUnitValue),
|
|
fontStyle: (processNoopValue),
|
|
fontWeight: (processFontWeight),
|
|
letterSpacing: (processUnitValue),
|
|
lineHeight: (processLineHeight),
|
|
maxLines: (processNumberValue),
|
|
textAlign: (processNoopValue),
|
|
textDecoration: (processNoopValue),
|
|
textDecorationColor: (processColorValue),
|
|
textDecorationStyle: (processNoopValue),
|
|
textIndent: (processNoopValue),
|
|
textOverflow: (processNoopValue),
|
|
textTransform: (processNoopValue),
|
|
verticalAlign: (processNoopValue),
|
|
};
|
|
|
|
const matchNumber = (value) => typeof value === 'string' && /^-?\d*\.?\d*$/.test(value);
|
|
const castFloat = (value) => {
|
|
if (typeof value !== 'string')
|
|
return value;
|
|
if (matchNumber(value))
|
|
return parseFloat(value);
|
|
return value;
|
|
};
|
|
|
|
const parse = (transformString) => {
|
|
const transforms = transformString.trim().split(/\)[ ,]|\)/);
|
|
// Handle "initial", "inherit", "unset".
|
|
if (transforms.length === 1) {
|
|
return [[transforms[0], true]];
|
|
}
|
|
const parsed = [];
|
|
for (let i = 0; i < transforms.length; i += 1) {
|
|
const transform = transforms[i];
|
|
if (transform) {
|
|
const [name, rawValue] = transform.split('(');
|
|
const splitChar = rawValue.indexOf(',') >= 0 ? ',' : ' ';
|
|
const value = rawValue.split(splitChar).map((val) => val.trim());
|
|
parsed.push({ operation: name.trim(), value });
|
|
}
|
|
}
|
|
return parsed;
|
|
};
|
|
const parseAngle = (value) => {
|
|
const unitsRegexp = /(-?\d*\.?\d*)(\w*)?/i;
|
|
const [, angle, unit] = unitsRegexp.exec(value);
|
|
const number = Number.parseFloat(angle);
|
|
return unit === 'rad' ? (number * 180) / Math.PI : number;
|
|
};
|
|
const normalizeTransformOperation = ({ operation, value }) => {
|
|
switch (operation) {
|
|
case 'scale': {
|
|
const [scaleX, scaleY = scaleX] = value.map((num) => Number.parseFloat(num));
|
|
return { operation: 'scale', value: [scaleX, scaleY] };
|
|
}
|
|
case 'scaleX': {
|
|
return { operation: 'scale', value: [Number.parseFloat(value), 1] };
|
|
}
|
|
case 'scaleY': {
|
|
return { operation: 'scale', value: [1, Number.parseFloat(value)] };
|
|
}
|
|
case 'rotate': {
|
|
return { operation: 'rotate', value: [parseAngle(value)] };
|
|
}
|
|
case 'translate': {
|
|
return {
|
|
operation: 'translate',
|
|
value: value.map((num) => Number.parseFloat(num)),
|
|
};
|
|
}
|
|
case 'translateX': {
|
|
return {
|
|
operation: 'translate',
|
|
value: [Number.parseFloat(value), 0],
|
|
};
|
|
}
|
|
case 'translateY': {
|
|
return { operation: 'translate', value: [0, Number.parseFloat(value)] };
|
|
}
|
|
case 'skew': {
|
|
return { operation: 'skew', value: value.map(parseAngle) };
|
|
}
|
|
case 'skewX': {
|
|
return { operation: 'skew', value: [parseAngle(value), 0] };
|
|
}
|
|
case 'skewY': {
|
|
return { operation: 'skew', value: [0, parseAngle(value)] };
|
|
}
|
|
default: {
|
|
return { operation, value: value.map((num) => Number.parseFloat(num)) };
|
|
}
|
|
}
|
|
};
|
|
const normalize = (operations) => {
|
|
return operations.map((operation) => normalizeTransformOperation(operation));
|
|
};
|
|
const processTransform = (key, value) => {
|
|
if (typeof value !== 'string')
|
|
return { [key]: value };
|
|
return { [key]: normalize(parse(value)) };
|
|
};
|
|
const Y_AXIS_SHORTHANDS = { top: true, bottom: true };
|
|
const sortTransformOriginPair = (a, b) => {
|
|
if (Y_AXIS_SHORTHANDS[a])
|
|
return 1;
|
|
if (Y_AXIS_SHORTHANDS[b])
|
|
return -1;
|
|
return 0;
|
|
};
|
|
const getTransformOriginPair = (values) => {
|
|
if (!values || values.length === 0)
|
|
return ['center', 'center'];
|
|
const pair = values.length === 1 ? [values[0], 'center'] : values;
|
|
return pair.sort(sortTransformOriginPair);
|
|
};
|
|
// Transforms shorthand transformOrigin values
|
|
const processTransformOriginShorthand = (key, value, container) => {
|
|
const match = `${value}`.split(' ');
|
|
const pair = getTransformOriginPair(match);
|
|
const transformOriginX = transformUnit(container, pair[0]);
|
|
const transformOriginY = transformUnit(container, pair[1]);
|
|
return {
|
|
transformOriginX: offsetKeyword(transformOriginX) || castFloat(transformOriginX),
|
|
transformOriginY: offsetKeyword(transformOriginY) || castFloat(transformOriginY),
|
|
};
|
|
};
|
|
const processTransformOriginValue = (key, value, container) => {
|
|
const v = transformUnit(container, value);
|
|
return { [key]: offsetKeyword(v) || castFloat(v) };
|
|
};
|
|
const handlers$1 = {
|
|
transform: processTransform,
|
|
gradientTransform: processTransform,
|
|
transformOrigin: (processTransformOriginShorthand),
|
|
transformOriginX: (processTransformOriginValue),
|
|
transformOriginY: (processTransformOriginValue),
|
|
};
|
|
|
|
const handlers = {
|
|
fill: (processColorValue),
|
|
stroke: (processColorValue),
|
|
strokeDasharray: (processNoopValue),
|
|
strokeWidth: (processUnitValue),
|
|
fillOpacity: (processNumberValue),
|
|
strokeOpacity: (processNumberValue),
|
|
fillRule: (processNoopValue),
|
|
textAnchor: (processNoopValue),
|
|
strokeLinecap: (processNoopValue),
|
|
strokeLinejoin: (processNoopValue),
|
|
visibility: (processNoopValue),
|
|
clipPath: (processNoopValue),
|
|
dominantBaseline: (processNoopValue),
|
|
};
|
|
|
|
const shorthands = {
|
|
...handlers$b,
|
|
...handlers$a,
|
|
...handlers$9,
|
|
...handlers$8,
|
|
...handlers$7,
|
|
...handlers$6,
|
|
...handlers$5,
|
|
...handlers$4,
|
|
...handlers$3,
|
|
...handlers$2,
|
|
...handlers$1,
|
|
...handlers,
|
|
};
|
|
/**
|
|
* Expand the shorthand properties.
|
|
*
|
|
* @param style - Style object
|
|
* @returns Expanded style object
|
|
*/
|
|
const resolve = (container) => (style) => {
|
|
const propsArray = Object.keys(style);
|
|
const resolvedStyle = {};
|
|
for (let i = 0; i < propsArray.length; i += 1) {
|
|
const key = propsArray[i];
|
|
const value = style[key];
|
|
if (!shorthands[key]) {
|
|
resolvedStyle[key] = value;
|
|
continue;
|
|
}
|
|
const resolved = shorthands[key](key, value, container, style);
|
|
const keys = Object.keys(resolved);
|
|
for (let j = 0; j < keys.length; j += 1) {
|
|
const propName = keys[j];
|
|
const propValue = resolved[propName];
|
|
resolvedStyle[propName] = propValue;
|
|
}
|
|
}
|
|
return resolvedStyle;
|
|
};
|
|
|
|
/**
|
|
* Resolves styles
|
|
*
|
|
* @param container
|
|
* @param style - Style
|
|
* @returns Resolved style
|
|
*/
|
|
const resolveStyles = (container, style) => {
|
|
const computeMediaQueries = (value) => resolveMediaQueries(container, value);
|
|
return compose(resolve(container), computeMediaQueries, flatten)(style);
|
|
};
|
|
|
|
export { resolveStyles as default, flatten, transformColor };
|