From 96c9301d43ee0d76f350d965644387ac85a561f7 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sat, 15 Feb 2020 14:59:13 +0100 Subject: [PATCH] [theme] Built-in convertLength method --- .../typography/ResponsiveFontSizesChart.js | 3 +- packages/material-ui/package.json | 1 - packages/material-ui/src/styles/cssUtils.js | 60 +++++++++++++++++++ .../material-ui/src/styles/cssUtils.test.js | 56 ++++++++++++++--- .../src/styles/responsiveFontSizes.js | 7 +-- yarn.lock | 5 -- 6 files changed, 112 insertions(+), 20 deletions(-) diff --git a/docs/src/pages/customization/typography/ResponsiveFontSizesChart.js b/docs/src/pages/customization/typography/ResponsiveFontSizesChart.js index 24055d04c372e5..785f46a447b147 100644 --- a/docs/src/pages/customization/typography/ResponsiveFontSizesChart.js +++ b/docs/src/pages/customization/typography/ResponsiveFontSizesChart.js @@ -1,5 +1,6 @@ +/* eslint-disable material-ui/restricted-path-imports */ import React from 'react'; -import convertLength from 'convert-css-length'; +import { convertLength } from '@material-ui/core/styles/cssUtils'; import { makeStyles, createMuiTheme, responsiveFontSizes } from '@material-ui/core/styles'; import { Legend, diff --git a/packages/material-ui/package.json b/packages/material-ui/package.json index 18486dba1f0110..5b259576cbd7e0 100644 --- a/packages/material-ui/package.json +++ b/packages/material-ui/package.json @@ -54,7 +54,6 @@ "@material-ui/utils": "^4.7.1", "@types/react-transition-group": "^4.2.0", "clsx": "^1.0.2", - "convert-css-length": "^2.0.1", "hoist-non-react-statics": "^3.3.2", "popper.js": "^1.14.1", "prop-types": "^15.7.2", diff --git a/packages/material-ui/src/styles/cssUtils.js b/packages/material-ui/src/styles/cssUtils.js index b31c9e3b3f0bf4..8c3a1a140d34b6 100644 --- a/packages/material-ui/src/styles/cssUtils.js +++ b/packages/material-ui/src/styles/cssUtils.js @@ -1,3 +1,63 @@ +export function isUnitless(value) { + return String(parseFloat(value)).length === String(value).length; +} + +// Ported from Compass +// https://github.com/Compass/compass/blob/master/core/stylesheets/compass/typography/_units.scss +// Emulate the sass function "unit" +export function getUnit(input) { + return String(input).match(/[\d.\-+]*\s*(.*)/)[1] || ''; +} + +// Emulate the sass function "unitless" +export function toUnitless(length) { + return parseFloat(length); +} + +// Convert any CSS or value to any another. +// From https://github.com/KyleAMathews/convert-css-length +export function convertLength(baseFontSize) { + return (length, toUnit) => { + const fromUnit = getUnit(length); + + // Optimize for cases where `from` and `to` units are accidentally the same. + if (fromUnit === toUnit) { + return length; + } + + // Convert input length to pixels. + let pxLength = toUnitless(length); + + if (fromUnit !== 'px') { + if (fromUnit === 'em') { + pxLength = toUnitless(length) * toUnitless(baseFontSize); + } else if (fromUnit === 'rem') { + pxLength = toUnitless(length) * toUnitless(baseFontSize); + } else if (fromUnit === 'ex') { + pxLength = toUnitless(length) * toUnitless(baseFontSize) * 2; + } else { + return length; + } + } + + // Convert length in pixels to the output unit + let outputLength = pxLength; + if (toUnit !== 'px') { + if (toUnit === 'em') { + outputLength = pxLength / toUnitless(baseFontSize); + } else if (toUnit === 'rem') { + outputLength = pxLength / toUnitless(baseFontSize); + } else if (toUnit === 'ex') { + outputLength = pxLength / toUnitless(baseFontSize) / 2; + } else { + return length; + } + } + + return parseFloat(outputLength.toFixed(5)) + toUnit; + }; +} + export function alignProperty({ size, grid }) { const sizeBelow = size - (size % grid); const sizeAbove = sizeBelow + grid; diff --git a/packages/material-ui/src/styles/cssUtils.test.js b/packages/material-ui/src/styles/cssUtils.test.js index c4ec0692b3396f..266f0c85be7752 100644 --- a/packages/material-ui/src/styles/cssUtils.test.js +++ b/packages/material-ui/src/styles/cssUtils.test.js @@ -1,7 +1,49 @@ -import { assert } from 'chai'; -import { alignProperty, fontGrid, responsiveProperty } from './cssUtils'; +import { expect } from 'chai'; +import { + isUnitless, + getUnit, + toUnitless, + convertLength, + alignProperty, + fontGrid, + responsiveProperty, +} from './cssUtils'; describe('cssUtils', () => { + describe('isUnitless', () => { + it('should work as expected', () => { + expect(isUnitless('20px')).to.equal(false); + expect(isUnitless('2.5 px')).to.equal(false); + expect(isUnitless('2.5 %')).to.equal(false); + expect(isUnitless('-2.5')).to.equal(true); + }); + }); + + describe('getUnit', () => { + it('should work as expected', () => { + expect(getUnit('20px')).to.equal('px'); + expect(getUnit('2.5 px')).to.equal('px'); + expect(getUnit('2.5 %')).to.equal('%'); + expect(getUnit('-2.5')).to.equal(''); + }); + }); + + describe('toUnitless', () => { + it('should work as expected', () => { + expect(toUnitless('20px')).to.equal(20); + expect(toUnitless('2.5 px')).to.equal(2.5); + expect(toUnitless('2.5 %')).to.equal(2.5); + expect(toUnitless('-2.5')).to.equal(-2.5); + }); + }); + + describe('convertLength', () => { + it('should work as expected', () => { + const convert = convertLength('16px'); + expect(convert('32px', 'rem')).to.equal('2rem'); + }); + }); + describe('alignProperty', () => { const tests = [ { args: { size: 8, grid: 4 }, expected: 8 }, @@ -19,7 +61,7 @@ describe('cssUtils', () => { it(`aligns ${size} on grid ${grid} to ${expected}`, () => { const sizeAligned = alignProperty({ size, grid }); - assert.strictEqual(sizeAligned, expected); + expect(sizeAligned).to.equal(expected); }); }); }); @@ -40,7 +82,7 @@ describe('cssUtils', () => { it(`should return a font grid such that the relative lineHeight is aligned`, () => { const absoluteLineHeight = grid * lineHeight * htmlFontSize; - assert.strictEqual(Math.round((absoluteLineHeight % pixels) * 100000) / 100000, 0); + expect(Math.round((absoluteLineHeight % pixels) * 100000) / 100000).to.equal(0); }); }); @@ -49,7 +91,7 @@ describe('cssUtils', () => { there is no smaller font aligning the lineHeight`, () => { const grid = fontGrid({ lineHeight, pixels, htmlFontSize }); const absoluteLineHeight = grid * lineHeight * htmlFontSize; - assert.strictEqual(Math.floor(absoluteLineHeight / pixels), 1); + expect(Math.floor(absoluteLineHeight / pixels)).to.equal(1); }); }); }); @@ -65,7 +107,7 @@ describe('cssUtils', () => { breakpoints: [300, 600], }); - assert.deepEqual(result, { + expect(result).to.deep.equal({ fontSize: '15px', '@media (min-width:300px)': { fontSize: '17.5px', @@ -87,7 +129,7 @@ describe('cssUtils', () => { breakpoints: [500], }); - assert.deepEqual(result, { + expect(result).to.deep.equal({ fontSize: '0.875rem', '@media (min-width:500px)': { fontSize: '1rem', diff --git a/packages/material-ui/src/styles/responsiveFontSizes.js b/packages/material-ui/src/styles/responsiveFontSizes.js index 25e972f00021c6..05ac2ca61b7492 100644 --- a/packages/material-ui/src/styles/responsiveFontSizes.js +++ b/packages/material-ui/src/styles/responsiveFontSizes.js @@ -1,9 +1,4 @@ -import convertLength from 'convert-css-length'; -import { responsiveProperty, alignProperty, fontGrid } from './cssUtils'; - -function isUnitless(value) { - return String(parseFloat(value)).length === String(value).length; -} +import { isUnitless, convertLength, responsiveProperty, alignProperty, fontGrid } from './cssUtils'; export default function responsiveFontSizes(themeInput, options = {}) { const { diff --git a/yarn.lock b/yarn.lock index 5404a1b8c9d78d..6c5b4f6eb77bf0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4845,11 +4845,6 @@ conventional-recommended-bump@^5.0.0: meow "^4.0.0" q "^1.5.1" -convert-css-length@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/convert-css-length/-/convert-css-length-2.0.1.tgz#90a76bde5bfd24d72881a5b45d02249b2c1d257c" - integrity sha512-iGpbcvhLPRKUbBc0Quxx7w/bV14AC3ItuBEGMahA5WTYqB8lq9jH0kTXFheCBASsYnqeMFZhiTruNxr1N59Axg== - convert-source-map@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"