From ac33ee0c717819e9a020b8f59b1544af3b01747a Mon Sep 17 00:00:00 2001 From: Henrique Bruno Fantauzzi de Almeida Date: Thu, 3 Sep 2020 04:24:00 -0300 Subject: [PATCH] Added moderateVerticalScale (#46) --- README.md | 38 +++++++++++-------- __tests__/ScaledSheet.spec.js | 52 +++++++++++++++++++------- __tests__/scaling-utils-alias.spec.js | 13 ++++++- __tests__/scaling-utils.extend.spec.js | 13 ++++++- __tests__/scaling-utils.spec.js | 13 ++++++- extend.d.ts | 6 ++- extend.js | 4 +- index.d.ts | 6 ++- index.js | 4 +- lib/ScaledSheet.js | 30 +++++++++------ lib/extend/scaling-utils.extend.js | 6 ++- lib/scaling-utils.js | 6 ++- 12 files changed, 133 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 22cd38a..24f4c54 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ yarn add react-native-size-matters ``` ## Motivation -When developing with react-native, you need to manually adjust your app to look great on a variety of different screen sizes. That's a tedious job. -react-native-size-matters provides some simple tooling to make your scaling a whole lot easier. -The idea is to develop once on a standard ~5" screen mobile device and then simply apply the provided utils. +When developing with react-native, you need to manually adjust your app to look great on a variety of different screen sizes. That's a tedious job. +react-native-size-matters provides some simple tooling to make your scaling a whole lot easier. +The idea is to develop once on a standard ~5" screen mobile device and then simply apply the provided utils. 📖 You can read more about what led to this library on my blog post, which can be found in [this repo](./examples/BlogPost) or at [Medium](https://medium.com/soluto-engineering/size-matters-5aeeb462900a). ## Api @@ -37,22 +37,24 @@ const Component = props => ``` -* `scale(size: number)` +* `scale(size: number)` Will return a linear scaled result of the provided size, based on your device's screen width. -* `verticalScale(size: number)` +* `verticalScale(size: number)` Will return a linear scaled result of the provided size, based on your device's screen height. -* `moderateScale(size: number, factor?: number)` -Sometimes you don't want to scale everything in a linear manner, that's where moderateScale comes in. -The cool thing about it is that you can control the resize factor (default is 0.5). -If normal scale will increase your size by +2X, moderateScale will only increase it by +X, for example: -➡️ scale(10) = 20 -➡️ moderateScale(10) = 15 -➡️ moderateScale(10, 0.1) = 11 +* `moderateScale(size: number, factor?: number)` +Sometimes you don't want to scale everything in a linear manner, that's where moderateScale comes in. +The cool thing about it is that you can control the resize factor (default is 0.5). +If normal scale will increase your size by +2X, moderateScale will only increase it by +X, for example: +➡️ scale(10) = 20 +➡️ moderateScale(10) = 15 +➡️ moderateScale(10, 0.1) = 11 +* `moderateVerticalScale(size: number, factor?: number)` +Same as moderateScale, but using verticalScale instead of scale. All scale functions can be imported using their shorthand alias as well: ```js -import { s, vs, ms } from 'react-native-size-matters'; +import { s, vs, ms, mvs } from 'react-native-size-matters'; ``` @@ -67,9 +69,11 @@ ScaleSheet will take the same stylesObject a regular StyleSheet will take, plus * `@s` - will apply `scale` function on `size`. * `@vs` - will apply `verticalScale` function on `size`. * `@ms` - will apply `moderateScale` function with resize factor of 0.5 on `size`. +* `@mvs` - will apply `moderateVerticalScale` function with resize factor of 0.5 on `size`. * `@ms` - will apply `moderateScale` function with resize factor of `factor` on size. +* `@mvs` - will apply `moderateVerticalScale` function with resize factor of `factor` on size. -ScaledSheet also supports rounding the result, simply add `r` at the end of the annotation. +ScaledSheet also supports rounding the result, simply add `r` at the end of the annotation. Example: ```js @@ -85,6 +89,10 @@ const styles = ScaledSheet.create({ row: { padding: '10@ms0.3', // = moderateScale(10, 0.3) height: '50@ms' // = moderateScale(50) + }, + titleBar: { + paddingBottom: '30@mvs0.3', // = moderateVerticalScale(30, 0.3) + height: '30@mvs' } }); ``` @@ -92,5 +100,5 @@ const styles = ScaledSheet.create({
* [Changing the Default Guideline Sizes](./examples/change-guideline-sizes.md) -* [Examples](./examples/README.md) +* [Examples](./examples/README.md) diff --git a/__tests__/ScaledSheet.spec.js b/__tests__/ScaledSheet.spec.js index 430d357..4d25f9c 100644 --- a/__tests__/ScaledSheet.spec.js +++ b/__tests__/ScaledSheet.spec.js @@ -1,54 +1,72 @@ jest.mock('react-native'); -import { ScaledSheet, scale, verticalScale, moderateScale } from '..'; +import { ScaledSheet, scale, verticalScale, moderateScale, moderateVerticalScale } from '..'; const getRandomInt = (min = 1, max = 100) => Math.floor(Math.random() * (max - min + 1)) + min; describe('ScaledSheet', () => { test('Scale works', () => { const number = getRandomInt(); - const input = { test: `${number}@s` } + const input = { test: `${number}@s` }; expect(ScaledSheet.create(input).test).toBe(scale(number)); }); test('verticalScale works', () => { const number = getRandomInt(); - const input = { test: `${number}@vs` } + const input = { test: `${number}@vs` }; expect(ScaledSheet.create(input).test).toBe(verticalScale(number)); }); test('moderateScale with default factor works', () => { const number = getRandomInt(); - const input = { test: `${number}@ms` } + const input = { test: `${number}@ms` }; expect(ScaledSheet.create(input).test).toBe(moderateScale(number)); }); + test('moderateVerticalScale with default factor works', () => { + const number = getRandomInt(); + const input = { test: `${number}@mvs` }; + expect(ScaledSheet.create(input).test).toBe(moderateVerticalScale(number)); + }); + test('moderateScale with custom factor works', () => { const number = getRandomInt(); - const input = { test: `${number}@ms0.7` } + const input = { test: `${number}@ms0.7` }; expect(ScaledSheet.create(input).test).toBe(moderateScale(number, 0.7)); }); + test('moderateVerticalScale with custom factor works', () => { + const number = getRandomInt(); + const input = { test: `${number}@mvs0.7` }; + expect(ScaledSheet.create(input).test).toBe(moderateVerticalScale(number, 0.7)); + }); + test('Scale works with a negative value', () => { const number = getRandomInt(-100, -1); - const input = { test: `${number}@s` } + const input = { test: `${number}@s` }; expect(ScaledSheet.create(input).test).toBe(scale(number)); }); test('moderateScale works with a negative value', () => { const number = getRandomInt(-100, -1); - const input = { test: `${number}@ms0.3` } + const input = { test: `${number}@ms0.3` }; expect(ScaledSheet.create(input).test).toBe(moderateScale(number, 0.3)); }); + test('moderateVerticalScale works with a negative value', () => { + const number = getRandomInt(-100, -1); + const input = { test: `${number}@mvs0.3` }; + expect(ScaledSheet.create(input).test).toBe(moderateVerticalScale(number, 0.3)); + }); + test('Scale works on a deeply nested object', () => { const number = getRandomInt(); - const input = { test: { test: { test: `${number}@s` } } } + const input = { test: { test: { test: `${number}@s` } } }; expect(ScaledSheet.create(input).test.test.test).toBe(scale(number)); }); test('No special annotation should leave the number intact', () => { const number = getRandomInt(); - const input = { test: number } + const input = { test: number }; expect(ScaledSheet.create(input).test).toBe(number); }); @@ -60,11 +78,13 @@ describe('ScaledSheet', () => { margin: { width: 12, height: '12@s', + paddingTop: '6.3@mvs', paddingBottom: -1 } }, row: { - padding: '10@ms0.3', + paddingLeft: '10@ms0.3', + paddingBottom: '10@mvs0.4', height: '34@ms', marginRight: '0.5@ms0.9', marginLeft: '-0.5@ms0.9', @@ -75,7 +95,8 @@ describe('ScaledSheet', () => { top: '11.3@sr', bottom: '22.75@vsr', left: '35.1@msr', - right: '-20.19@ms0.3r' + right: '-20.19@ms0.3r', + height: '-14.13@mvs0.4r' } }; @@ -86,11 +107,13 @@ describe('ScaledSheet', () => { margin: { width: 12, height: scale(12), + paddingTop: moderateVerticalScale(6.3), paddingBottom: -1 } }, row: { - padding: moderateScale(10, 0.3), + paddingLeft: moderateScale(10, 0.3), + paddingBottom: moderateVerticalScale(10, 0.4), height: moderateScale(34), marginRight: moderateScale(0.5, 0.9), marginLeft: moderateScale(-0.5, 0.9), @@ -101,10 +124,11 @@ describe('ScaledSheet', () => { top: Math.round(scale(11.3)), bottom: Math.round(verticalScale(22.75)), left: Math.round(moderateScale(35.1)), - right: Math.round(moderateScale(-20.19, 0.3)) + right: Math.round(moderateScale(-20.19, 0.3)), + height: Math.round(moderateVerticalScale(-14.13, 0.4)) } }; expect(JSON.stringify(ScaledSheet.create(input))).toBe(JSON.stringify(expectedOutput)); }); -}) +}); diff --git a/__tests__/scaling-utils-alias.spec.js b/__tests__/scaling-utils-alias.spec.js index 8643642..fc0a69d 100644 --- a/__tests__/scaling-utils-alias.spec.js +++ b/__tests__/scaling-utils-alias.spec.js @@ -1,5 +1,5 @@ jest.mock('react-native'); -import { s, vs, ms } from '..'; +import { s, vs, ms, mvs } from '..'; describe('scaling-utils', () => { test('scale returns the expected result based on mocked Dimensions', () => { @@ -22,4 +22,13 @@ describe('scaling-utils', () => { expect(ms(100, 0.9)).toBe(190); expect(ms(100, 2)).toBe(300); }); -}) \ No newline at end of file + + test('moderateVerticalScale returns the expected result based on mocked Dimensions', () => { + expect(mvs(100)).toBe(125); + expect(mvs(100, 0.1)).toBe(105); + expect(mvs(100, 0.3)).toBe(115); + expect(mvs(100, 0.6)).toBe(130); + expect(mvs(100, 0.9)).toBe(145); + expect(mvs(100, 2)).toBe(200); + }); +}); \ No newline at end of file diff --git a/__tests__/scaling-utils.extend.spec.js b/__tests__/scaling-utils.extend.spec.js index 360a79f..8ae9ec8 100644 --- a/__tests__/scaling-utils.extend.spec.js +++ b/__tests__/scaling-utils.extend.spec.js @@ -1,6 +1,6 @@ jest.mock('react-native'); jest.mock('react-native-dotenv'); -import { scale, verticalScale, moderateScale } from '../extend'; +import { scale, verticalScale, moderateScale, moderateVerticalScale } from '../extend'; describe('scaling-utils when guideline sizes are set using react-native-dotenv', () => { test('scale returns the expected result based on mocked Dimensions and mocked guideline sizes', () => { @@ -23,4 +23,13 @@ describe('scaling-utils when guideline sizes are set using react-native-dotenv', expect(Math.floor(moderateScale(100, 0.9))).toBe(129); expect(Math.floor(moderateScale(100, 2))).toBe(166); }); -}) \ No newline at end of file + + test('moderateVerticalScale returns the expected result based on mocked Dimensions and mocked guideline sizes', () => { + expect(Math.floor(moderateVerticalScale(100))).toBe(100); + expect(Math.floor(moderateVerticalScale(100, 0.1))).toBe(100); + expect(Math.floor(moderateVerticalScale(100, 0.3))).toBe(100); + expect(Math.floor(moderateVerticalScale(100, 0.6))).toBe(100); + expect(Math.floor(moderateVerticalScale(100, 0.9))).toBe(100); + expect(Math.floor(moderateVerticalScale(100, 2))).toBe(100); + }); +}); \ No newline at end of file diff --git a/__tests__/scaling-utils.spec.js b/__tests__/scaling-utils.spec.js index c201e60..8591a60 100644 --- a/__tests__/scaling-utils.spec.js +++ b/__tests__/scaling-utils.spec.js @@ -1,5 +1,5 @@ jest.mock('react-native'); -import { scale, verticalScale, moderateScale } from '..'; +import { scale, verticalScale, moderateScale, moderateVerticalScale } from '..'; describe('scaling-utils', () => { test('scale returns the expected result based on mocked Dimensions', () => { @@ -22,4 +22,13 @@ describe('scaling-utils', () => { expect(moderateScale(100, 0.9)).toBe(190); expect(moderateScale(100, 2)).toBe(300); }); -}) \ No newline at end of file + + test('moderateVerticalScale returns the expected result based on mocked Dimensions', () => { + expect(moderateVerticalScale(100)).toBe(125); + expect(moderateVerticalScale(100, 0.1)).toBe(105); + expect(moderateVerticalScale(100, 0.3)).toBe(115); + expect(moderateVerticalScale(100, 0.6)).toBe(130); + expect(moderateVerticalScale(100, 0.9)).toBe(145); + expect(moderateVerticalScale(100, 2)).toBe(200); + }); +}); \ No newline at end of file diff --git a/extend.d.ts b/extend.d.ts index 87eb1b1..f523da7 100644 --- a/extend.d.ts +++ b/extend.d.ts @@ -5,13 +5,15 @@ declare module "react-native-size-matters/extend" { export function scale(size: number): number; export function verticalScale(size: number): number; export function moderateScale(size: number, factor?: number): number; + export function moderateVerticalScale(size: number, factor?: number): number; export function s(size: number): number; export function vs(size: number): number; export function ms(size: number, factor?: number): number; + export function mvs(size: number, factor?: number): number; export namespace ScaledSheet { - export function create | NamedStyles>(stylesObject: T): { - [P in keyof T]: RN.RegisteredStyle, number>> + export function create | NamedStyles>(stylesObject: T): { + [P in keyof T]: RN.RegisteredStyle, number>> }; } } \ No newline at end of file diff --git a/extend.js b/extend.js index 63ce50c..bd64594 100644 --- a/extend.js +++ b/extend.js @@ -1,5 +1,5 @@ import scaledSheetCreator from './lib/ScaledSheet'; -import { scale, verticalScale, moderateScale } from './lib/extend/scaling-utils.extend'; +import { scale, verticalScale, moderateScale, moderateVerticalScale } from './lib/extend/scaling-utils.extend'; -export const ScaledSheet = scaledSheetCreator(scale, verticalScale, moderateScale); +export const ScaledSheet = scaledSheetCreator(scale, verticalScale, moderateScale, moderateVerticalScale); export * from './lib/extend/scaling-utils.extend'; diff --git a/index.d.ts b/index.d.ts index 5ac7cc2..71c459d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -29,15 +29,17 @@ declare module "react-native-size-matters" { export function scale(size: number): number; export function verticalScale(size: number): number; export function moderateScale(size: number, factor?: number): number; + export function moderateVerticalScale(size: number, factor?: number): number; export function s(size: number): number; export function vs(size: number): number; export function ms(size: number, factor?: number): number; + export function mvs(size: number, factor?: number): number; type NamedStyles = { [P in keyof T]: RN.ViewStyle | RN.TextStyle | RN.ImageStyle | StringifiedStyles }; export namespace ScaledSheet { - export function create | NamedStyles>(stylesObject: T): { - [P in keyof T]: RN.RegisteredStyle, number>> + export function create | NamedStyles>(stylesObject: T): { + [P in keyof T]: RN.RegisteredStyle, number>> }; } } \ No newline at end of file diff --git a/index.js b/index.js index 2d996e0..3987feb 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ import scaledSheetCreator from './lib/ScaledSheet'; -import { scale, verticalScale, moderateScale } from './lib/scaling-utils'; +import { scale, verticalScale, moderateScale, moderateVerticalScale } from './lib/scaling-utils'; -export const ScaledSheet = scaledSheetCreator(scale, verticalScale, moderateScale); +export const ScaledSheet = scaledSheetCreator(scale, verticalScale, moderateScale, moderateVerticalScale); export * from './lib/scaling-utils'; \ No newline at end of file diff --git a/lib/ScaledSheet.js b/lib/ScaledSheet.js index 4578af8..233d43f 100644 --- a/lib/ScaledSheet.js +++ b/lib/ScaledSheet.js @@ -1,16 +1,24 @@ import { StyleSheet } from 'react-native'; import deepMap from './deep-map'; -const validScaleSheetRegex = /^(\-?\d+(\.\d{1,3})?)@(ms(\d+(\.\d{1,2})?)?|s|vs)(r?)$/; +// Groups Size Func Factor +// 1 2 3 +const validScaleSheetRegex = /^(\-?\d+(?:\.\d{1,3})?)@(mv?s(\d+(?:\.\d{1,2})?)?|s|vs)r?$/; -const scaleByAnnotation = (scale, verticalScale, moderateScale) => (value) => { +const scaleByAnnotation = (scale, verticalScale, moderateScale, moderateVerticalScale) => (value) => { if (!validScaleSheetRegex.test(value)) { return value; } const regexExecResult = validScaleSheetRegex.exec(value); + const size = parseFloat(regexExecResult[1]); - const scaleFunc = regexExecResult[3]; + let scaleFunc = regexExecResult[2]; + const scaleFactor = regexExecResult[3]; // string or undefined + + if (scaleFactor) + scaleFunc = scaleFunc.slice(0, - scaleFactor.length); // Remove the factor from it + const shouldRound = value.endsWith('r'); let result; @@ -23,21 +31,21 @@ const scaleByAnnotation = (scale, verticalScale, moderateScale) => (value) => { result = verticalScale(size); break; case 'ms': - result = moderateScale(size); + result = moderateScale(size, scaleFactor); + break; + case 'mvs': + result = moderateVerticalScale(size, scaleFactor); break; - default: - const scaleFactor = value.split('ms')[1].replace('r', ''); - result = moderateScale(size, parseFloat(scaleFactor)); } return shouldRound ? Math.round(result) : result; }; -const scaledSheetCreator = (scale, verticalScale, moderateScale) => { - const scaleFunc = scaleByAnnotation(scale, verticalScale, moderateScale); +const scaledSheetCreator = (scale, verticalScale, moderateScale, moderateVerticalScale) => { + const scaleFunc = scaleByAnnotation(scale, verticalScale, moderateScale, moderateVerticalScale); return { create: styleSheet => StyleSheet.create(deepMap(styleSheet, scaleFunc)) - } -} + }; +}; export default scaledSheetCreator; \ No newline at end of file diff --git a/lib/extend/scaling-utils.extend.js b/lib/extend/scaling-utils.extend.js index 85aa3c5..fb1561f 100644 --- a/lib/extend/scaling-utils.extend.js +++ b/lib/extend/scaling-utils.extend.js @@ -10,8 +10,10 @@ const guidelineBaseHeight = SIZE_MATTERS_BASE_HEIGHT || 680; export const scale = size => shortDimension / guidelineBaseWidth * size; export const verticalScale = size => longDimension / guidelineBaseHeight * size; -export const moderateScale = (size, factor = 0.5) => size + ( scale(size) - size ) * factor; +export const moderateScale = (size, factor = 0.5) => size + (scale(size) - size) * factor; +export const moderateVerticalScale = (size, factor = 0.5) => size + (verticalScale(size) - size) * factor; export const s = scale; export const vs = verticalScale; -export const ms = moderateScale; \ No newline at end of file +export const ms = moderateScale; +export const mvs = moderateVerticalScale; \ No newline at end of file diff --git a/lib/scaling-utils.js b/lib/scaling-utils.js index c3aeafc..e89372a 100644 --- a/lib/scaling-utils.js +++ b/lib/scaling-utils.js @@ -9,8 +9,10 @@ const guidelineBaseHeight = 680; export const scale = size => shortDimension / guidelineBaseWidth * size; export const verticalScale = size => longDimension / guidelineBaseHeight * size; -export const moderateScale = (size, factor = 0.5) => size + ( scale(size) - size ) * factor; +export const moderateScale = (size, factor = 0.5) => size + (scale(size) - size) * factor; +export const moderateVerticalScale = (size, factor = 0.5) => size + (verticalScale(size) - size) * factor; export const s = scale; export const vs = verticalScale; -export const ms = moderateScale; \ No newline at end of file +export const ms = moderateScale; +export const mvs = moderateVerticalScale; \ No newline at end of file