@react-pdf/stylesheet
Advanced tools
Comparing version 5.2.2 to 6.0.0
1329
lib/index.js
import { compose, castArray, 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'; | ||
import hlsToHex from 'hsl-to-hex'; | ||
import colorString from 'color-string'; | ||
import matchMedia from 'media-engine'; | ||
/** | ||
* 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(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]; | ||
/** | ||
* @type {(number | 'auto')[]} | ||
*/ | ||
const flexAuto = [1, 1, 'auto']; | ||
const expandFlex = (key, value) => { | ||
/** | ||
* @type {(number | 'auto')[]} | ||
*/ | ||
let defaults = flexDefaults; | ||
let matches = []; | ||
if (value === 'auto') { | ||
defaults = flexAuto; | ||
} else { | ||
matches = `${value}`.split(' '); | ||
} | ||
const flexGrow = matches[0] || defaults[0]; | ||
const flexShrink = matches[1] || defaults[1]; | ||
const flexBasis = matches[2] || defaults[2]; | ||
return { | ||
flexGrow, | ||
flexShrink, | ||
flexBasis | ||
}; | ||
const processFlexShorthand = (key, value, container) => { | ||
let defaults = flexDefaults; | ||
let matches = []; | ||
if (value === 'auto') { | ||
defaults = flexAuto; | ||
} | ||
else { | ||
matches = `${value}`.split(' '); | ||
} | ||
const flexGrow = transformUnit(container, matches[0] || defaults[0]); | ||
const flexShrink = transformUnit(container, 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: processUnitValue, | ||
flexShrink: processUnitValue, | ||
flexWrap: processNoopValue, | ||
justifyContent: processNoopValue, | ||
justifySelf: processNoopValue, | ||
}; | ||
/* eslint-disable no-plusplus */ | ||
// This file is ran directly with Node - needs to have .js extension | ||
// eslint-disable-next-line import/extensions | ||
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) => { | ||
console.error(` | ||
const name = style.toString(); | ||
// eslint-disable-next-line no-console | ||
console.error(` | ||
@react-pdf/stylesheet parsing error: | ||
${style}: ${value}, | ||
${' '.repeat(style.length + 2)}^ | ||
Unsupported ${style} value format | ||
${name}: ${value}, | ||
${' '.repeat(name.length + 2)}^ | ||
Unsupported ${name} value format | ||
`); | ||
}; | ||
/** | ||
@@ -57,699 +352,399 @@ * @param {Object} options | ||
*/ | ||
const expandBoxModel = function (_temp) { | ||
let { | ||
expandsTo, | ||
maxValues = 1, | ||
autoSupported = false | ||
} = _temp === void 0 ? {} : _temp; | ||
return (model, value) => { | ||
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 { | ||
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 {}; | ||
logError(model, value); | ||
return {}; | ||
} | ||
const first = parts[0]; | ||
const first = transformUnit(container, parts[0]); | ||
if (expandsTo) { | ||
const second = parts[1] || parts[0]; | ||
const third = parts[2] || parts[0]; | ||
const fourth = parts[3] || parts[1] || parts[0]; | ||
return expandsTo({ | ||
first, | ||
second, | ||
third, | ||
fourth | ||
}); | ||
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 | ||
[model]: first, | ||
}; | ||
}; | ||
}; | ||
const processMargin = expandBoxModel({ | ||
expandsTo: _ref => { | ||
let { | ||
first, | ||
second, | ||
third, | ||
fourth | ||
} = _ref; | ||
return { | ||
marginTop: first, | ||
marginRight: second, | ||
marginBottom: third, | ||
marginLeft: fourth | ||
}; | ||
}, | ||
maxValues: 4, | ||
autoSupported: true | ||
expandsTo: ({ first, second, third, fourth }) => ({ | ||
marginTop: first, | ||
marginRight: second, | ||
marginBottom: third, | ||
marginLeft: fourth, | ||
}), | ||
maxValues: 4, | ||
autoSupported: true, | ||
}); | ||
const processMarginVertical = expandBoxModel({ | ||
expandsTo: _ref2 => { | ||
let { | ||
first, | ||
second | ||
} = _ref2; | ||
return { | ||
marginTop: first, | ||
marginBottom: second | ||
}; | ||
}, | ||
maxValues: 2, | ||
autoSupported: true | ||
expandsTo: ({ first, second }) => ({ | ||
marginTop: first, | ||
marginBottom: second, | ||
}), | ||
maxValues: 2, | ||
autoSupported: true, | ||
}); | ||
const processMarginHorizontal = expandBoxModel({ | ||
expandsTo: _ref3 => { | ||
let { | ||
first, | ||
second | ||
} = _ref3; | ||
return { | ||
marginRight: first, | ||
marginLeft: second | ||
}; | ||
}, | ||
maxValues: 2, | ||
autoSupported: true | ||
expandsTo: ({ first, second }) => ({ | ||
marginRight: first, | ||
marginLeft: second, | ||
}), | ||
maxValues: 2, | ||
autoSupported: true, | ||
}); | ||
const processMarginSingle = expandBoxModel({ | ||
autoSupported: true | ||
autoSupported: true, | ||
}); | ||
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 expandBorders = (key, value) => { | ||
const match = matchBorderShorthand(`${value}`); | ||
if (match) { | ||
const color = match[5] || value; | ||
const style = match[4] || value; | ||
const width = match[1] || value; | ||
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$/)) { | ||
return { | ||
borderTopStyle: style, | ||
borderRightStyle: style, | ||
borderBottomStyle: style, | ||
borderLeftStyle: style | ||
}; | ||
} | ||
if (key.match(/Width$/)) { | ||
return { | ||
borderTopWidth: width, | ||
borderRightWidth: width, | ||
borderBottomWidth: width, | ||
borderLeftWidth: width | ||
}; | ||
} | ||
if (key.match(/Radius$/)) { | ||
return { | ||
borderTopLeftRadius: value, | ||
borderTopRightRadius: value, | ||
borderBottomRightRadius: value, | ||
borderBottomLeftRadius: value | ||
}; | ||
} | ||
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 value; | ||
const handlers$5 = { | ||
margin: processMargin, | ||
marginBottom: processMarginSingle, | ||
marginHorizontal: processMarginHorizontal, | ||
marginLeft: processMarginSingle, | ||
marginRight: processMarginSingle, | ||
marginTop: processMarginSingle, | ||
marginVertical: processMarginVertical, | ||
}; | ||
const processPadding = expandBoxModel({ | ||
expandsTo: _ref => { | ||
let { | ||
first, | ||
second, | ||
third, | ||
fourth | ||
} = _ref; | ||
return { | ||
paddingTop: first, | ||
paddingRight: second, | ||
paddingBottom: third, | ||
paddingLeft: fourth | ||
}; | ||
}, | ||
maxValues: 4 | ||
expandsTo: ({ first, second, third, fourth }) => ({ | ||
paddingTop: first, | ||
paddingRight: second, | ||
paddingBottom: third, | ||
paddingLeft: fourth, | ||
}), | ||
maxValues: 4, | ||
}); | ||
const processPaddingVertical = expandBoxModel({ | ||
expandsTo: _ref2 => { | ||
let { | ||
first, | ||
second | ||
} = _ref2; | ||
return { | ||
paddingTop: first, | ||
paddingBottom: second | ||
}; | ||
}, | ||
maxValues: 2 | ||
expandsTo: ({ first, second }) => ({ | ||
paddingTop: first, | ||
paddingBottom: second, | ||
}), | ||
maxValues: 2, | ||
}); | ||
const processPaddingHorizontal = expandBoxModel({ | ||
expandsTo: _ref3 => { | ||
let { | ||
first, | ||
second | ||
} = _ref3; | ||
return { | ||
paddingRight: first, | ||
paddingLeft: second | ||
}; | ||
}, | ||
maxValues: 2 | ||
expandsTo: ({ first, second }) => ({ | ||
paddingRight: first, | ||
paddingLeft: second, | ||
}), | ||
maxValues: 2, | ||
}); | ||
const processPaddingSingle = expandBoxModel(); | ||
const expandObjectPosition = (key, value) => { | ||
const match = `${value}`.split(' '); | ||
return { | ||
objectPositionX: (match === null || match === void 0 ? void 0 : match[0]) || value, | ||
objectPositionY: (match === null || match === void 0 ? void 0 : match[1]) || value | ||
}; | ||
const handlers$4 = { | ||
padding: processPadding, | ||
paddingBottom: processPaddingSingle, | ||
paddingHorizontal: processPaddingHorizontal, | ||
paddingLeft: processPaddingSingle, | ||
paddingRight: processPaddingSingle, | ||
paddingTop: processPaddingSingle, | ||
paddingVertical: processPaddingVertical, | ||
}; | ||
const Y_AXIS_SHORTHANDS = { | ||
top: true, | ||
bottom: true | ||
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 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 expandTransformOrigin = (key, value) => { | ||
const match = `${value}`.split(' '); | ||
const pair = getTransformOriginPair(match); | ||
return { | ||
transformOriginX: pair[0], | ||
transformOriginY: pair[1] | ||
}; | ||
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 expandGap = (key, value) => { | ||
const match = `${value}`.split(' '); | ||
return { | ||
rowGap: (match === null || match === void 0 ? void 0 : match[0]) || value, | ||
columnGap: (match === null || match === void 0 ? void 0 : match[1]) || value | ||
}; | ||
const processObjectPositionValue = (key, value, container) => ({ | ||
[key]: offsetKeyword(transformUnit(container, value)), | ||
}); | ||
const handlers$3 = { | ||
objectPosition: processObjectPosition, | ||
objectPositionX: processObjectPositionValue, | ||
objectPositionY: processObjectPositionValue, | ||
objectFit: processNoopValue, | ||
}; | ||
const shorthands = { | ||
flex: expandFlex, | ||
gap: expandGap, | ||
margin: processMargin, | ||
marginHorizontal: processMarginHorizontal, | ||
marginVertical: processMarginVertical, | ||
marginTop: processMarginSingle, | ||
marginRight: processMarginSingle, | ||
marginBottom: processMarginSingle, | ||
marginLeft: processMarginSingle, | ||
padding: processPadding, | ||
paddingHorizontal: processPaddingHorizontal, | ||
paddingVertical: processPaddingVertical, | ||
paddingTop: processPaddingSingle, | ||
paddingRight: processPaddingSingle, | ||
paddingBottom: processPaddingSingle, | ||
paddingLeft: processPaddingSingle, | ||
border: expandBorders, | ||
borderTop: expandBorders, | ||
borderRight: expandBorders, | ||
borderBottom: expandBorders, | ||
borderLeft: expandBorders, | ||
borderColor: expandBorders, | ||
borderRadius: expandBorders, | ||
borderStyle: expandBorders, | ||
borderWidth: expandBorders, | ||
objectPosition: expandObjectPosition, | ||
transformOrigin: expandTransformOrigin | ||
const castInt = (value) => { | ||
if (typeof value === 'number') | ||
return value; | ||
return parseInt(value, 10); | ||
}; | ||
/** | ||
* Transforms style key-value | ||
* | ||
* @param {string} key style key | ||
* @param {string} value style value | ||
* @returns {string | Number} transformed style values | ||
*/ | ||
const expandStyle = (key, value) => { | ||
return shorthands[key] ? shorthands[key](key, value) : { | ||
[key]: value | ||
}; | ||
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, | ||
}; | ||
/** | ||
* Expand the shorthand properties. | ||
* | ||
* @param {Object} style object | ||
* @returns {Object} expanded style object | ||
*/ | ||
const expand = style => { | ||
if (!style) return 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]; | ||
const extended = expandStyle(key, value); | ||
const keys = Object.keys(extended); | ||
for (let j = 0; j < keys.length; j += 1) { | ||
const propName = keys[j]; | ||
const propValue = extended[propName]; | ||
resolvedStyle[propName] = propValue; | ||
} | ||
} | ||
return resolvedStyle; | ||
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); | ||
}; | ||
/** | ||
* Remove nil values from array | ||
* | ||
* @template T | ||
* @param {(T | null | undefined)[]} array | ||
* @returns {T[]} array without nils | ||
*/ | ||
const compact = array => array.filter(Boolean); | ||
/** | ||
* Merges style objects array | ||
* | ||
* @param {Object[]} styles style objects array | ||
* @returns {Object} 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 {Object[]} styles style objects array | ||
* @returns {Object} flattened style object | ||
*/ | ||
const flatten = compose(mergeStyles, compact, castArray); | ||
/** | ||
* Parses scalar value in value and unit pairs | ||
* | ||
* @param {string} value scalar value | ||
* @returns {Object} parsed value | ||
*/ | ||
const parseValue = value => { | ||
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 | ||
}; | ||
const processFontWeight = (key, value) => { | ||
return { [key]: transformFontWeight(value) }; | ||
}; | ||
/** | ||
* Transform given scalar value | ||
* | ||
* @param {Object} container | ||
* @param {string} value styles value | ||
* @returns {Object} 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; | ||
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 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 isRgb = value => /rgba?/g.test(value); | ||
const isHsl = value => /hsla?/g.test(value); | ||
/** | ||
* Transform rgb color to hexa | ||
* | ||
* @param {string} value styles value | ||
* @returns {Object} transformed value | ||
*/ | ||
const parseRgb = value => { | ||
const rgb = colorString.get.rgb(value); | ||
return colorString.to.hex(rgb); | ||
const processLineHeight = (key, value, container, styles) => { | ||
return { | ||
[key]: transformLineHeight(value, styles, container), | ||
}; | ||
}; | ||
/** | ||
* Transform Hsl color to hexa | ||
* | ||
* @param {string} value styles value | ||
* @returns {Object} transformed value | ||
*/ | ||
const parseHsl = value => { | ||
const hsl = colorString.get.hsl(value).map(Math.round); | ||
const hex = hlsToHex(...hsl); | ||
return hex.toUpperCase(); | ||
const handlers$2 = { | ||
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, | ||
}; | ||
/** | ||
* Transform given color to hexa | ||
* | ||
* @param {string} value styles value | ||
* @returns {Object} transformed value | ||
*/ | ||
const transformColor = value => { | ||
if (isRgb(value)) return parseRgb(value); | ||
if (isHsl(value)) return parseHsl(value); | ||
return value; | ||
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 | ||
}); | ||
const parse = (transformString) => { | ||
const transforms = transformString.trim().split(/\)[ ,]|\)/); | ||
// Handle "initial", "inherit", "unset". | ||
if (transforms.length === 1) { | ||
return [[transforms[0], true]]; | ||
} | ||
} | ||
return parsed; | ||
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 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 = _ref => { | ||
let { | ||
operation, | ||
value | ||
} = _ref; | ||
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 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 normalize = (operations) => { | ||
return operations.map((operation) => normalizeTransformOperation(operation)); | ||
}; | ||
const processTransform = value => { | ||
if (typeof value !== 'string') return value; | ||
return normalize(parse(value)); | ||
const processTransform = (key, value) => { | ||
if (typeof value !== 'string') | ||
return { [key]: value }; | ||
return { [key]: normalize(parse(value)) }; | ||
}; | ||
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 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 processFontWeight = 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 value; | ||
const getTransformOriginPair = (values) => { | ||
if (!values || values.length === 0) | ||
return ['center', 'center']; | ||
const pair = values.length === 1 ? [values[0], 'center'] : values; | ||
return pair.sort(sortTransformOriginPair); | ||
}; | ||
/* eslint-disable no-restricted-globals */ | ||
const processLineHeight = (value, styles) => { | ||
if (value === '') return value; | ||
const { | ||
fontSize = 18 | ||
} = styles; | ||
// Percent values: use this number multiplied by the element's font size | ||
const { | ||
percent | ||
} = matchPercent(value) || {}; | ||
if (percent) return percent * fontSize; | ||
// Unitless values: use this number multiplied by the element's font size | ||
return isNaN(value) ? value : value * fontSize; | ||
// 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 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 processTransformOriginValue = (key, value, container) => { | ||
const v = transformUnit(container, value); | ||
return { [key]: offsetKeyword(v) || castFloat(v) }; | ||
}; | ||
const offsetKeyword = value => { | ||
switch (value) { | ||
case 'top': | ||
case 'left': | ||
return '0%'; | ||
case 'right': | ||
case 'bottom': | ||
return '100%'; | ||
case 'center': | ||
return '50%'; | ||
default: | ||
return null; | ||
} | ||
const handlers$1 = { | ||
transform: processTransform, | ||
transformOrigin: processTransformOriginShorthand, | ||
transformOriginX: processTransformOriginValue, | ||
transformOriginY: processTransformOriginValue, | ||
}; | ||
const transformObjectPosition = value => offsetKeyword(value) || castFloat(value); | ||
const transformTransformOrigin = value => offsetKeyword(value) || castFloat(value); | ||
const handlers = { | ||
transform: processTransform, | ||
fontWeight: processFontWeight, | ||
lineHeight: processLineHeight, | ||
objectPositionX: transformObjectPosition, | ||
objectPositionY: transformObjectPosition, | ||
transformOriginX: transformTransformOrigin, | ||
transformOriginY: transformTransformOrigin | ||
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 transformStyle = (key, value, styles, container) => { | ||
const result = handlers[key] ? handlers[key](value, styles) : value; | ||
return transformColor(transformUnit(container, castFloat(result))); | ||
}; | ||
/** | ||
* @typedef {Function} Transform | ||
* @param {Object} style styles object | ||
* @returns {Object} transformed styles | ||
*/ | ||
/** | ||
* Transform styles values | ||
* | ||
* @param {Object} container | ||
* @returns {Transform} transform function | ||
*/ | ||
const transform = container => styles => { | ||
if (!styles) return styles; | ||
const propsArray = Object.keys(styles); | ||
const resolvedStyle = {}; | ||
for (let i = 0; i < propsArray.length; i += 1) { | ||
const key = propsArray[i]; | ||
const value = styles[key]; | ||
const transformed = transformStyle(key, value, styles, container); | ||
resolvedStyle[key] = transformed; | ||
} | ||
return resolvedStyle; | ||
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, | ||
}; | ||
/** | ||
* Resolves media queries in styles object | ||
* Expand the shorthand properties. | ||
* | ||
* @param {Object} container | ||
* @param {Object} styles object | ||
* @param style - Style object | ||
* @returns Expanded style object | ||
*/ | ||
const resolveMediaQueries = (container, styles) => { | ||
return Object.keys(styles).reduce((acc, key) => { | ||
if (/@media/.test(key)) { | ||
return { | ||
...acc, | ||
...matchMedia({ | ||
[key]: styles[key] | ||
}, container) | ||
}; | ||
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 { | ||
...acc, | ||
[key]: styles[key] | ||
}; | ||
}, {}); | ||
return resolvedStyle; | ||
}; | ||
@@ -760,11 +755,11 @@ | ||
* | ||
* @param {Object} container | ||
* @param {Object} style object | ||
* @returns {Object} resolved style object | ||
* @param container | ||
* @param style - Style | ||
* @returns Resolved style | ||
*/ | ||
const resolveStyles = (container, style) => { | ||
const computeMediaQueries = value => resolveMediaQueries(container, value); | ||
return compose(transform(container), expand, computeMediaQueries, flatten)(style); | ||
const computeMediaQueries = (value) => resolveMediaQueries(container, value); | ||
return compose(resolve(container), computeMediaQueries, flatten)(style); | ||
}; | ||
export { resolveStyles as default, flatten, processTransform, transformColor }; | ||
export { resolveStyles as default, flatten, transformColor }; |
{ | ||
"name": "@react-pdf/stylesheet", | ||
"version": "5.2.2", | ||
"version": "6.0.0", | ||
"license": "MIT", | ||
@@ -10,2 +10,3 @@ "description": "A styles engine for Node and the browser", | ||
"main": "./lib/index.js", | ||
"types": "./lib/index.d.ts", | ||
"repository": { | ||
@@ -22,5 +23,4 @@ "type": "git", | ||
"dependencies": { | ||
"@babel/runtime": "^7.20.13", | ||
"@react-pdf/fns": "3.1.0", | ||
"@react-pdf/types": "^2.7.1", | ||
"@react-pdf/fns": "3.1.1", | ||
"@react-pdf/types": "^2.8.0", | ||
"color-string": "^1.9.1", | ||
@@ -27,0 +27,0 @@ "hsl-to-hex": "^1.0.0", |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
35756
6
4
1019
1
- Removed@babel/runtime@^7.20.13
- Removed@babel/runtime@7.26.9(transitive)
- Removed@react-pdf/fns@3.1.0(transitive)
- Removed@react-pdf/stylesheet@6.0.0(transitive)
- Removedregenerator-runtime@0.14.1(transitive)
Updated@react-pdf/fns@3.1.1
Updated@react-pdf/types@^2.8.0