From 2745c52a12f10bcbbc2f8fd3b79f849e1a7be947 Mon Sep 17 00:00:00 2001 From: Nathan Horrigan Date: Sun, 23 May 2021 16:43:48 +0100 Subject: [PATCH] Add custom units support --- src/__tests__/units.js | 18 +++++++++++++++++- src/customUnits.js | 36 ++++++++++++++++++++++++++++++++++++ src/index.js | 6 ++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/customUnits.js diff --git a/src/__tests__/units.js b/src/__tests__/units.js index d1a13f8..082be7f 100644 --- a/src/__tests__/units.js +++ b/src/__tests__/units.js @@ -1,4 +1,4 @@ -import transformCss from '..' +import transformCss, { declareCustomUnit } from '..' // List of units from: // https://developer.mozilla.org/en-US/docs/Web/CSS/length @@ -123,6 +123,22 @@ lengthUnits.forEach(unit => { shadowOpacity: 1, }) }) + + it('allows custom units with function scaler', () => { + // Set rpx to scale factor 0.5 + declareCustomUnit('rpx', size => 0.5 * size) + + // Test that 2em == 28px + expect(transformCss([['font-size', '20rpx']])).toEqual({ fontSize: 10 }) + }) + + it('allows custom units with constant scaler', () => { + // Set 1mpx to 14px + declareCustomUnit('mpx', 14) + + // Test that 2mpx == 28px + expect(transformCss([['font-size', '2mpx']])).toEqual({ fontSize: 28 }) + }) }) it('throws for unit that is not supported', () => { diff --git a/src/customUnits.js b/src/customUnits.js new file mode 100644 index 0000000..69d4ff8 --- /dev/null +++ b/src/customUnits.js @@ -0,0 +1,36 @@ +const customUnitRe = unit => + new RegExp(`^([+-]?(?:\\d*\\.)?\\d+(?:e[+-]?\\d+)?)${unit}?$`, 'i') + +// Contains all the units registered via 'declareCustomUnit' +const customUnits = [] + +// Add a custom unit to registry +export const declareCustomUnit = (unit, scaleValue) => { + customUnits.push({ + suffix: unit, + regex: customUnitRe(unit), + scaleValue, + }) +} + +// Check if the value matches against any declared units +export const matchesCustomUnit = token => { + for (let i = 0; i < customUnits.length; i += 1) { + const unit = customUnits[i] + + // Check if string matches RegEx + const customUnitMatch = token.match(unit.regex) + if (customUnitMatch !== null) { + // Get number value from match result + const value = Number(customUnitMatch[1]) + + // Apply scale factor to number + if (typeof unit.scaleValue === 'function') { + return unit.scaleValue(value) + } + return value * unit.scaleValue + } + } + + return null +} diff --git a/src/index.js b/src/index.js index 0afce0a..273337b 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import camelizeStyleName from 'camelize' import transforms from './transforms/index' import devPropertiesWithoutUnitsRegExp from './devPropertiesWithoutUnitsRegExp' import TokenStream from './TokenStream' +import { matchesCustomUnit, declareCustomUnit } from './customUnits' // Note if this is wrong, you'll need to change tokenTypes.js too const numberOrLengthRe = /^([+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?)(?:px)?$/i @@ -27,6 +28,9 @@ export const transformRawValue = (propName, value) => { } } + const customUnitMatch = matchesCustomUnit(value) + if (customUnitMatch !== null) return customUnitMatch + const numberMatch = value.match(numberOrLengthRe) if (numberMatch !== null) return Number(numberMatch[1]) @@ -88,3 +92,5 @@ export default (rules, shorthandBlacklist = []) => getStylesForProperty(propertyName, value, allowShorthand) ) }, {}) + +export { declareCustomUnit }