Skip to content

Values parser #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 24, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Cleanup
  • Loading branch information
jacobp100 committed Jan 23, 2017
commit cd87b38672f4b7909ce6cf53f745b96646d0986f
24 changes: 11 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
Converts CSS text to a React Native stylesheet object.

```css
font-size: 18;
line-height: 24;
font-size: 18px;
line-height: 24px;
color: red;
```

Expand All @@ -21,9 +21,9 @@ Converts all number-like values to numbers, and string-like to strings.
Automatically converts indirect values to their React Native equivalents.

```css
text-shadow-offset: 10 5;
text-shadow-offset: 10px 5px;
font-variant: small-caps;
transform: translate(10, 5) scale(5);
transform: translate(10px, 5px) scale(5);
```

```js
Expand All @@ -42,8 +42,8 @@ transform: translate(10, 5) scale(5);
Also allows shorthand values.

```css
font: bold 14/16 "Helvetica";
margin: 5 7 2;
font: bold 14px/16px "Helvetica";
margin: 5px 7px 2px;
```

```js
Expand All @@ -67,8 +67,6 @@ Shorthands will only accept values that are supported in React, so `background`

`border{Top,Right,Bottom,Left}` shorthands are not supported, because `borderStyle` cannot be applied to individual border sides.

`flex` does not support putting `flexBasis` before `flexGrow`. The supported syntax is `flex: <flex-grow> <flex-shrink> <flex-basis>`.

# API

The API is mostly for implementors. However, the main API may be useful for non-impmentors. The main API is,
Expand All @@ -78,9 +76,9 @@ import transform from 'css-to-react-native';
// or const transform = require('css-to-react-native').default;

transform([
['font', 'bold 14/16 "Helvetica"'],
['margin', '5 7 2'],
['border-left-width', '5'],
['font', 'bold 14px/16px "Helvetica"'],
['margin', '5px 7px 2px'],
['border-left-width', '5px'],
]); // => { fontFamily: 'Helvetica', ... }
```

Expand All @@ -90,13 +88,13 @@ For implementors, there is also,
import { getPropertyName, getStylesForProperty } from 'css-to-react-native';

getPropertyName('border-width'); // => 'borderWidth'
getStylesForProperty('borderWidth', '1 0 2 0'); // => { borderTopWidth: 1, ... }
getStylesForProperty('borderWidth', '1px 0px 2px 0px'); // => { borderTopWidth: 1, ... }
```

Should you wish to opt-out of transforming certain shorthands, an array of property names in camelCase can be passed as a second argument to `transform`.

```js
transform([['border-radius', '50']], ['borderRadius']);
transform([['border-radius', '50px']], ['borderRadius']);
// { borderRadius: 50 } rather than { borderTopLeft: ... }
```

Expand Down
30 changes: 17 additions & 13 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@ const camelizeStyleName = require('fbjs/lib/camelizeStyleName');
const transforms = require('./transforms');
const TokenStream = require('./TokenStream');

const transformRawValue = input => (
(input !== '' && !isNaN(input))
? Number(input)
: input
);
// Note if this is wrong, you'll need to change tokenTypes.js too
const numberOrLengthRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)(?:px)?$/;

export const parseProp = (propName, value) => {
const ast = parse(value).nodes;
const tokenStream = new TokenStream(ast);
return transforms[propName](tokenStream);
// Undocumented export
export const transformRawValue = (input) => {
const value = input.trim().match(numberOrLengthRe);
return value ? Number(value[1]) : input;
};

export const getStylesForProperty = (propName, inputValue, allowShorthand) => {
const value = inputValue.trim();
// Undocumented: allow ast to be passed in
let propValue;

const propValue = (allowShorthand && (propName in transforms))
? parseProp(propName, value)
: transformRawValue(value);
const isRawValue = (allowShorthand === false) || !(propName in transforms);
if (isRawValue) {
const value = typeof inputValue === 'string' ? inputValue : parse.stringify(inputValue);
propValue = transformRawValue(value);
} else {
const ast = typeof inputValue === 'string' ? parse(inputValue.trim()) : inputValue;
const tokenStream = new TokenStream(ast.nodes);
propValue = transforms[propName](tokenStream);
}

return (propValue && propValue.$merge)
? propValue.$merge
Expand Down
30 changes: 19 additions & 11 deletions src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* global jest it, expect */
import transformCss, { parseProp } from '.';
import transformCss, { getStylesForProperty } from '.';

const runTest = (inputCss, expectedStyles) => {
const actualStyles = transformCss(inputCss);
Expand All @@ -13,17 +13,25 @@ it('transforms numbers', () => runTest([
['bottom', '0'],
], { top: 0, left: 0, right: 0, bottom: 0 }));

it('allows pixels in unspecialized transform', () => runTest([
['top', '0px'],
], { top: 0 }));

it('allows percent in unspecialized transform', () => runTest([
['top', '0%'],
], { top: '0%' }));

it('allows decimal values', () => {
expect(parseProp('margin', '0.5px').$merge.marginTop).toBe(0.5);
expect(parseProp('margin', '1.5px').$merge.marginTop).toBe(1.5);
expect(parseProp('margin', '10.5px').$merge.marginTop).toBe(10.5);
expect(parseProp('margin', '100.5px').$merge.marginTop).toBe(100.5);
expect(parseProp('margin', '-0.5px').$merge.marginTop).toBe(-0.5);
expect(parseProp('margin', '-1.5px').$merge.marginTop).toBe(-1.5);
expect(parseProp('margin', '-10.5px').$merge.marginTop).toBe(-10.5);
expect(parseProp('margin', '-100.5px').$merge.marginTop).toBe(-100.5);
expect(parseProp('margin', '.5px').$merge.marginTop).toBe(0.5);
expect(parseProp('margin', '-.5px').$merge.marginTop).toBe(-0.5);
expect(getStylesForProperty('margin', '0.5px').marginTop).toBe(0.5);
expect(getStylesForProperty('margin', '1.5px').marginTop).toBe(1.5);
expect(getStylesForProperty('margin', '10.5px').marginTop).toBe(10.5);
expect(getStylesForProperty('margin', '100.5px').marginTop).toBe(100.5);
expect(getStylesForProperty('margin', '-0.5px').marginTop).toBe(-0.5);
expect(getStylesForProperty('margin', '-1.5px').marginTop).toBe(-1.5);
expect(getStylesForProperty('margin', '-10.5px').marginTop).toBe(-10.5);
expect(getStylesForProperty('margin', '-100.5px').marginTop).toBe(-100.5);
expect(getStylesForProperty('margin', '.5px').marginTop).toBe(0.5);
expect(getStylesForProperty('margin', '-.5px').marginTop).toBe(-0.5);
});

it('allows decimal values in transformed values', () => runTest([
Expand Down
1 change: 1 addition & 0 deletions src/tokenTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const matchColor = (node) => {
};

const noneRe = /^(none)$/;
// Note if these are wrong, you'll need to change index.js too
const numberRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)$/;
const lengthRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)px$/;
const angleRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?(?:deg|rad))$/;
Expand Down
2 changes: 0 additions & 2 deletions src/transforms/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ const xyTransformFactory = tokenType => (key, valueIfOmitted) => (functionStream

let y;
if (functionStream.hasTokens()) {
functionStream.match(SPACE); // optional space
functionStream.expect(COMMA);
functionStream.match(SPACE); // optional space
y = functionStream.expect(tokenType);
} else if (valueIfOmitted !== undefined) {
y = valueIfOmitted;
Expand Down