UNPKG

babel-plugin-styled-components-px2vw

Version:
480 lines (400 loc) 18.1 kB
# babel-plugin-styled-components-px2vw [![npm version](https://badge.fury.io/js/babel-plugin-styled-components-px2vw.svg)](https://badge.fury.io/js/babel-plugin-styled-components-px2vw) ![NPM Downloads](https://badgen.net/npm/dt/babel-plugin-styled-components-px2vw) [![Build Status](https://travis-ci.com/jackluson/babel-plugin-styled-components-px2vw.svg?branch=main)](https://travis-ci.com/jackluson/babel-plugin-styled-components-px2vw) [![codecov](https://codecov.io/gh/jackluson/babel-plugin-styled-components-px2vw/branch/main/graph/badge.svg)](https://codecov.io/gh/jackluson/babel-plugin-styled-components-px2vw) [![MIT](https://img.shields.io/github/license/jackluson/babel-plugin-styled-components-px2vw?style=plastic)](https://github.com/jackluson/babel-plugin-styled-components-px2vw/blob/main/LICENSE) English | [中文](README_ZH.md) [Babel](https://babeljs.io/) plugin for convert `px` to `vw` units of [styled-components](https://www.styled-components.com/). 1. Use [postcss-px-to-viewport](https://github.com/evrone/postcss-px-to-viewport) to process all css text in template strings. 2. Add a runtime `px2vw` function polyfill to process expression embedded in template strings when enable [transformRuntime](#transform-runtime) option. its inspiration comes from [babel-plugin-styled-components-px2rem](https://github.com/xuyuanxiang/babel-plugin-styled-components-px2rem). Thanks ## Getting Started ### Installation Add via npm `$ npm install babel-plugin-styled-components-px2vw --save-dev` or yarn `$ yarn add -D babel-plugin-styled-components-px2vw` ### Configuration babel.config.js: ```javascript module.exports = { plugins: [ [ 'styled-components-px2vw', { unitToConvert: 'px', unitPrecision: 5, minPixelValue: 0, }, ], ], }; ``` or .babelrc: ```javascript { "plugins": [ [ "styled-components-px2vw", { "unitToConvert": 'px', "unitPrecision": 5, "minPixelValue": 0 } ] ] } ``` ### Composition It should be put before [babel-plugin-styled-components](https://github.com/styled-components/babel-plugin-styled-components#readme) ```json { "plugins": ["styled-components-px2vw", "styled-components"] } ``` see [example](example) 1. `npm install` 2. Typing: `npm test` to run unit tests 3. Typing: `npm run build` to compile `src/index.jsx` to `dist/index-jsx.js` ### Online Demo click [react-boilerplate-typescript-mobile](https://jackluson.github.io/react-boilerplate-typescript-mobile/) and toggle device toolbar,see the style of the page,this is source code [repo](https://github.com/jackluson/react-boilerplate-typescript-mobile) ### Options > The options of the plugin are based on some of the option of [postcss-px-to-viewport](https://github.com/evrone/postcss-px-to-viewport), but not all options properties work, such as `mediaQuery`、`landscape` | name | type | required | default | description | | :--------------- | :-----------------------: | :------: | :-------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | tags | string[] | false | ["styled", "css", "createGlobalStyle", "keyframes"] | [styled-components](https://www.styled-components.com/) template literal [tagged](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) | | unitToConvert | string | false | px | unit to convert | | viewportWidth | number | false | 750 | The width of the viewport. | | unitPrecision | number | false | 5 | The decimal numbers to allow the vw units to grow to. | | propList | string[] | false | ["*"] | The properties that can change from px to vw. more detail see [postcss-px-to-viewport propList opions](https://github.com/evrone/postcss-px-to-viewport) | | viewportUnit | string | false | vw | Expected units. | | fontViewportUnit | string | false | vw | Expected units for font. | | minPixelValue | number | false | 1 | Set the minimum pixel value to replace. | | replace | boolean | false | false | replaces rules containing vw instead of adding fallbacks. | | exclude | Regexp or Array of Regexp | false | undefined | Ignore some files like 'node_modules', If value is regexp, will ignore the matches files. if value is array, the elements of the array are regexp. | | include | Regexp or Array of Regexp | false | undefined | (Regexp or Array of Regexp) If include is set, only matching files will be converted, for example, only files under src/mobile/ (include: /\/src\/mobile\//), If the value is regexp, the matching file will be included, otherwise it will be excluded. If value is array, the elements of the array are regexp. | | transformRuntime | boolean | false | false | since 1.1.0,enable transformation of all expressions that embedded in template strings | ## Demo ### Transform Runtime If enabled `transformRuntime` option, all supported expressions embedded in template strings are processed as follows: **Note:** Only expression that end with `px` will be processed. #### FunctionExpression source code: ```javascript import styled from 'styled-components'; export const FunctionExpression = styled.button` height: ${function (props) { return props.height; }}px; `; ``` compiled: ```javascript import styled from 'styled-components'; export const FunctionExpression = styled.button` height: ${(...args) => _px2rem(function (props) { return props.height; }, ...args)}; `; function _px2rem(input, ...args) { if (typeof input === 'function') return _px2rem(input(...args), ...args); var value = typeof input === 'string' ? parseFloat(input) : typeof input === 'number' ? input : 0; var pixels = Number.isNaN(value) ? 0 : value; if (Math.abs(pixels) < 0) return pixels + 'px'; var mul = Math.pow(10, 5 + 1); return (Math.round(Math.floor(((pixels * 1) / 100) * mul) / 10) * 10) / mul + 'rem'; } ``` #### ArrowFunctionExpression source code: ```javascript export const ArrowFunction = styled.input.attrs((props) => ({ type: 'password', size: props.size || '16px', width: props.width || 100, }))` color: palevioletred; font-size: 14px; border: 1px solid palevioletred; border-radius: 8px; /* setting propList: ['*', '!border-*'] */ width: ${(props) => props.width}px; /* PropertyAccess Body */ height: ${() => height}px; /* Identifier Body */ line-height: ${() => '44'}px; /* StringLiteral Body */ margin: ${() => 32}px; /* NumericLiteral Body */ padding: ${(props) => props.size}; `; export const ArrowFunctionWithBlockBody = styled.button` width: ${(props) => { if (props.width) { return props.width; } else { return 0; } }}px; /* Block Body */ ${(props) => (props.disabled ? 'height: 400px' : 'height: 200px')} `; export const ArrowFunctionWithBinaryBody = styled.button` ${(props) => props.disabled && ` width: 200px; font-size: 14px; `}; height: ${(props) => !props.disabled && props.height}px; /* ArrowFunction with a LogicalExpression Body */ width: ${() => 44 + 50}px; /* ArrowFunction with a BinaryExpression Body */ `; export const ArrowFunctionWithConditionalBody = styled.button` height: ${(props) => (props.height ? height : 100)}px; /* ArrowFunction with a ConditionalExpression Body */ `; ``` compiled: ```javascript export const ArrowFunction = styled.input.attrs((props) => ({ type: 'password', size: props.size || '16px', width: props.width || 100, }))` color: palevioletred; font-size: 1.86667vw; border: 1px solid palevioletred; border-radius: 8px; /* setting propList: ['*', '!border-*'] */ width: ${(props) => _px2vw(props.width)}; /* PropertyAccess Body */ height: ${() => _px2vw(height)}; /* Identifier Body */ line-height: ${() => _px2vw('44')}; /* StringLiteral Body */ margin: ${() => _px2vw(32)}; /* NumericLiteral Body */ padding: ${(props) => props.size}; `; export const ArrowFunctionWithBlockBody = styled.button` width: ${(props) => _px2vw(() => { if (props.width) { return props.width; } else { return 0; } })}; /* Block Body */ ${(props) => (props.disabled ? 'height: 53.33333vw' : 'height: 26.66667vw')} `; export const ArrowFunctionWithBinaryBody = styled.button` ${(props) => props.disabled && ` width: 26.66667vw; font-size: 1.86667vw; `}; height: ${(props) => _px2vw(!props.disabled && props.height)}; /* ArrowFunction with a LogicalExpression Body */ width: ${() => _px2vw(44 + 50)}; /* ArrowFunction with a BinaryExpression Body */ `; export const ArrowFunctionWithConditionalBody = styled.button` height: ${(props) => props.height ? _px2vw(height) : _px2vw(100)}; /* ArrowFunction with a ConditionalExpression Body */ `; ``` #### ConditionalExpression source code ```javascript export const ConditionalExpression = function ({ fontSize, spacing } = {}) { const StyledButton = styled.button` font-size: ${typeof fontSize === 'number' ? fontSize : (props) => props.theme.fontSize}px; ${spacing ? ` padding: 8px 16px 0 32px; margin: 16px 0; ` : ''} `; return <StyledButton />; }; export const ConditionalExpressionWhenTrue = function ({ fontSize } = {}) { const StyledButton = styled.button` font-size: ${typeof fontSize !== 'number' ? (props) => props.theme.fontSize : fontSize}px; `; return <StyledButton />; }; export const ConditionalExpressionWhenFalse = function ({ fontSize } = {}) { const StyledButton = styled.button` font-size: ${typeof fontSize === 'number' ? fontSize : 16}px; `; return <StyledButton />; }; ``` compiled: ```javascript export const ConditionalExpression = function ({ fontSize, spacing } = {}) { const StyledButton = styled.button` font-size: ${typeof fontSize === 'number' ? _px2vw(fontSize) : (props) => _px2vw(props.theme.fontSize)}; ${spacing ? ` padding: 1.06667vw 2.13333vw 0 4.26667vw; margin: 2.13333vw 0; ` : ''} `; return /*#__PURE__*/ React.createElement(StyledButton, null); }; export const ConditionalExpressionWhenTrue = function ({ fontSize } = {}) { const StyledButton = styled.button` font-size: ${typeof fontSize !== 'number' ? (props) => _px2vw(props.theme.fontSize) : _px2vw(fontSize)}; `; return /*#__PURE__*/ React.createElement(StyledButton, null); }; export const ConditionalExpressionWhenFalse = function ({ fontSize } = {}) { const StyledButton = styled.button` font-size: ${typeof fontSize === 'number' ? _px2vw(fontSize) : _px2vw(16)}; `; return /*#__PURE__*/ React.createElement(StyledButton, null); }; const condition = false; ``` #### Other Expressions Identifier, CallExpression, BinaryExpress, StringLiteral, NumericLiteral, MemberExpression, LogicalExpression... ### Disable transformRuntime (default) If disable transformRuntime option, all supported expressions embedded in template strings are not processed as follows: <details> <summary> the input code show as below </summary> ```javascript import styled, { css, createGlobalStyle, keyframes } from 'styled-components'; const mixins = css` padding: 0 18px; margin: 16px 32px 16px 32em; padding-top: 10px; padding-bottom: 10px; border: 2px solid black; `; const Animation = keyframes` from { transform: translateX(100px); } to { transform: translateX(-100px); } `; const Input = styled.input.attrs((props) => ({ type: 'password', size: props.size || '1em', }))` color: palevioletred; font-size: 14px; border: 1px solid palevioletred; border-radius: 8px; margin: ${(props) => props.size}; padding: ${(props) => props.size}; `; const GlobalStyle = createGlobalStyle` section { font-size: 16px; } html body { font-size: 18px; section { font-size: 16px } } `; const BlockButton = styled.button` ${mixins}; display: block; width: 100%; height: 96px; line-height: 96px; `; const InlineButton = styled.button` ${mixins}; display: inline; width: ${(props) => props.width}px; height: 96px; line-height: 96px; `; const ExtendedButton = styled(InlineButton)` width: 120px; height: 32px; line-height: 32px; font-size: 14px; `; const SizeableButton = styled.button( (props) => ` display: inline; width: ${props.width}px; height: ${props.height}px; line-height: ${props.height}px; font-size: 16px;`, ); ``` </details> <details> <summary> the output code show as below </summary> ```javascript import styled, { css, createGlobalStyle, keyframes } from 'styled-components'; const mixins = css` padding: 0 2.4vw; margin: 2.133vw 4.267vw 2.133vw 32em; padding-top: 1.333vw; padding-bottom: 1.333vw; border: 0.267vw solid black; `; const Animation = keyframes` from { transform: translateX(13.333vw); } to { transform: translateX(-13.333vw); } `; const Input = styled.input.attrs((props) => ({ type: 'password', size: props.size || '1em', }))` color: palevioletred; font-size: 1.867vw; border: 1px solid palevioletred; border-radius: 8px; margin: ${(props) => props.size}; padding: ${(props) => props.size}; `; const GlobalStyle = createGlobalStyle` section { font-size: 2.133vw; } html body { font-size: 2.4vw; section { font-size: 2.133vw } } `; const BlockButton = styled.button` ${mixins}; display: block; width: 100%; height: 12.8vw; line-height: 12.8vw; `; const InlineButton = styled.button` ${mixins}; display: inline; width: ${(props) => props.width}px; height: 12.8vw; line-height: 12.8vw; `; const ExtendedButton = styled(InlineButton)` width: 16vw; height: 4.267vw; line-height: 4.267vw; font-size: 1.867vw; `; const SizeableButton = styled.button( (props) => ` display: inline; width: ${props.width}px; height: ${props.height}px; line-height: ${props.height}px; font-size: 2.133vw; `, ); ``` </details> ### License This project is licensed under the [MIT License](https://github.com/jackluson/babel-plugin-styled-components-px2vw/blob/master/LICENSE).