From 8df00cd3f9479b1c510fe3a6363e2ea2b46d59d3 Mon Sep 17 00:00:00 2001 From: Jacob Parker Date: Sun, 27 Aug 2017 11:31:24 +0100 Subject: [PATCH] Implement support for box-shadow shorthand --- README.md | 2 ++ src/TokenStream.js | 4 +-- src/index.test.js | 42 +++++++++++++++++++++++++++++++ src/transforms/boxShadow.js | 50 +++++++++++++++++++++++++++++++++++++ src/transforms/index.js | 2 ++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/transforms/boxShadow.js diff --git a/README.md b/README.md index 22c70c4..32de7df 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ margin: 5px 7px 2px; Shorthands will only accept values that are supported in React, so `background` will only accept a colour, `backgroundColor` +There is also support for the `box-shadow` shorthand, and this converts into `shadow-` properties. Note that these only work on iOS. + #### Shorthand Notes `border{Top,Right,Bottom,Left}` shorthands are not supported, because `borderStyle` cannot be applied to individual border sides. diff --git a/src/TokenStream.js b/src/TokenStream.js index 2e712e2..b827360 100644 --- a/src/TokenStream.js +++ b/src/TokenStream.js @@ -58,7 +58,7 @@ module.exports = class TokenStream { return this.throw(); } - matchFunction() { + matchesFunction() { const node = this.node; if (node.type !== 'function') return null; const value = new TokenStream(node.nodes, node); @@ -69,7 +69,7 @@ module.exports = class TokenStream { } expectFunction() { - const value = this.matchFunction(); + const value = this.matchesFunction(); if (value !== null) return value; return this.throw(); } diff --git a/src/index.test.js b/src/index.test.js index 7e047d4..c9f95a6 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -442,6 +442,48 @@ it('does not transform invalid flex', () => { expect(() => transformCss([['flex', '1 2px 3']])).toThrow(); }); +it('transforms box-shadow into shadow- properties', () => runTest([ + ['box-shadow', '10px 20px 30px red'], +], { + shadowOffset: { width: 10, height: 20 }, + shadowRadius: 30, + shadowColor: 'red', +})); + +it('transforms box-shadow without blur-radius', () => runTest([ + ['box-shadow', '10px 20px red'], +], { + shadowOffset: { width: 10, height: 20 }, + shadowRadius: 0, + shadowColor: 'red', +})); + +it('transforms box-shadow without color', () => runTest([ + ['box-shadow', '10px 20px red'], +], { + shadowOffset: { width: 10, height: 20 }, + shadowRadius: 0, + shadowColor: 'red', +})); + +it('transforms box-shadow without blur-radius, color', () => runTest([ + ['box-shadow', '10px 20px'], +], { + shadowOffset: { width: 10, height: 20 }, + shadowRadius: 0, + shadowColor: 'black', +})); + +it('transforms box-shadow enforces offset to be present', () => { + expect(() => transformCss([['box-shadow', 'red']])) + .toThrow('Failed to parse declaration "boxShadow: red"'); +}); + +it('transforms box-shadow and enforces offset-y if offset-x present', () => { + expect(() => transformCss([['box-shadow', '10px']])) + .toThrow('Failed to parse declaration "boxShadow: 10px"'); +}); + it('allows blacklisting shorthands', () => { const actualStyles = transformCss([['border-radius', '50']], ['borderRadius']); expect(actualStyles).toEqual({ borderRadius: 50 }); diff --git a/src/transforms/boxShadow.js b/src/transforms/boxShadow.js new file mode 100644 index 0000000..c9930bd --- /dev/null +++ b/src/transforms/boxShadow.js @@ -0,0 +1,50 @@ +const { tokens } = require('../tokenTypes'); + +const { NONE, SPACE, WORD, LENGTH } = tokens; + +module.exports = (tokenStream) => { + let offsetX; + let offsetY; + let blurRadius; + let color; + + if (tokenStream.matches(NONE)) { + tokenStream.expectEmpty(); + return { + $merge: { shadowOffset: { width: 0, height: 0 }, shadowRadius: 0, shadowColor: 'black' }, + }; + } + + let didParseFirst = false; + while (tokenStream.hasTokens()) { + if (didParseFirst) tokenStream.expect(SPACE); + + if (offsetX === undefined && tokenStream.matches(LENGTH)) { + offsetX = tokenStream.lastValue; + tokenStream.expect(SPACE); + offsetY = tokenStream.expect(LENGTH); + + if (tokenStream.lookAhead().matches(LENGTH)) { + tokenStream.expect(SPACE); + blurRadius = tokenStream.expect(LENGTH); + } + } else if (color === undefined && ( + tokenStream.matchesFunction() || tokenStream.matches(WORD) + )) { + color = String(tokenStream.lastValue); + } else { + tokenStream.throw(); + } + + didParseFirst = true; + } + + if (offsetX === undefined) tokenStream.throw(); + + const $merge = { + shadowOffset: { width: offsetX, height: offsetY }, + shadowRadius: blurRadius !== undefined ? blurRadius : 0, + shadowColor: color !== undefined ? color : 'black', + }; + return { $merge }; +}; diff --git a/src/transforms/index.js b/src/transforms/index.js index 64c3667..960e229 100644 --- a/src/transforms/index.js +++ b/src/transforms/index.js @@ -1,4 +1,5 @@ const { regExpToken, tokens } = require('../tokenTypes'); +const boxShadow = require('./boxShadow'); const flex = require('./flex'); const font = require('./font'); const fontFamily = require('./fontFamily'); @@ -56,6 +57,7 @@ module.exports = { borderColor, borderRadius, borderWidth, + boxShadow, flex, flexFlow, font,