Skip to content

Commit 409d001

Browse files
authored
Merge pull request #7 from aaronlademann-wf/rem_util-update
Allow unsupported units in toRem / toPx functions
2 parents f94e9b4 + 14b0c41 commit 409d001

File tree

2 files changed

+146
-56
lines changed

2 files changed

+146
-56
lines changed

lib/src/util/rem_util.dart

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -84,58 +84,96 @@ void recomputeRootFontSize() {
8484
}
8585
}
8686

87-
/// Converts a pixel (`px`) value to its `rem` equivalent using the current root font size.
87+
/// Converts a pixel (`px`) [value] to its `rem` equivalent using the current root font size.
88+
///
89+
/// * If [value] is a [String] or [CssValue]:
90+
/// * And [value] already has the correct unit, it will not be converted.
91+
/// * And [CssValue.unit] is not `'px'` or `'rem'`, an error will be thrown unless
92+
/// [passThroughUnsupportedUnits] is `true`, in which case no conversion will take place.
93+
/// * If [value] is a [num], it will be treated as a `px` and converted, unless [treatNumAsRem] is `true`.
94+
/// * If [value] is `null`, `null` will be returned.
8895
///
8996
/// Example input:
9097
///
9198
/// * `'15px'`
92-
/// * `'15'`
93-
/// * 15
99+
/// * `new CssValue(15, 'px')`
100+
/// * `15`
101+
/// * `1.5, treatNumAsRem: true`
102+
/// * `'1.5rem'`
103+
/// * `new CssValue(1.5, 'rem')`
94104
///
95105
/// Example output (assuming 1rem = 10px):
96106
///
97107
/// * `1.5rem`
98-
CssValue toRem(dynamic pxValue) {
99-
num pxValueNum;
108+
CssValue toRem(dynamic value, {bool treatNumAsRem: false, bool passThroughUnsupportedUnits: false}) {
109+
if (value == null) return null;
100110

101-
if (pxValue is num) {
102-
pxValueNum = pxValue;
111+
num remValueNum;
112+
113+
if (value is num) {
114+
remValueNum = treatNumAsRem ? value : value / rootFontSize;
103115
} else {
104-
var parsedPxValue = pxValue is CssValue ? pxValue : new CssValue.parse(pxValue);
105-
if (parsedPxValue?.unit != 'px') {
106-
throw new ArgumentError.value(pxValue, 'pxValue', 'must be a num or a String px value');
116+
var parsedValue = value is CssValue ? value : new CssValue.parse(value);
117+
118+
if (parsedValue?.unit == 'rem') {
119+
remValueNum = parsedValue.number;
120+
} else if (parsedValue?.unit == 'px') {
121+
remValueNum = parsedValue.number / rootFontSize;
122+
} else {
123+
if (passThroughUnsupportedUnits) {
124+
return parsedValue;
125+
}
126+
127+
throw new ArgumentError.value(value, 'value', 'must be a px num or a String px/rem value');
107128
}
108-
109-
pxValueNum = parsedPxValue.number;
110129
}
111130

112-
return new CssValue(pxValueNum / rootFontSize, 'rem');
131+
return new CssValue(remValueNum, 'rem');
113132
}
114133

115-
/// Converts a rem value to its pixel (`px`) equivalent using the current root font size.
134+
/// Converts a rem [value] to its pixel (`px`) equivalent using the current root font size.
135+
///
136+
/// * If [value] is a [String] or [CssValue]:
137+
/// * And [value] already has the correct unit, it will not be converted.
138+
/// * And [CssValue.unit] is not `'px'` or `'rem'`, an error will be thrown unless
139+
/// [passThroughUnsupportedUnits] is `true`, in which case no conversion will take place.
140+
/// * If [value] is a [num], it will be treated as a `rem` and converted, unless [treatNumAsPx] is `true`.
141+
/// * If [value] is `null`, `null` will be returned.
116142
///
117143
/// Example input:
118144
///
119145
/// * `'1.5rem'`
120-
/// * `'1.5'`
121-
/// * 1.5
146+
/// * `new CssValue(1.5, 'rem')`
147+
/// * `1.5`
148+
/// * `15, treatNumAsPx: true`
149+
/// * `15px`
150+
/// * `new CssValue(15, 'px')`
122151
///
123152
/// Example output (assuming 1rem = 10px):
124153
///
125154
/// * `15px`
126-
CssValue toPx(dynamic remValue) {
127-
num remValueNum;
155+
CssValue toPx(dynamic value, {bool treatNumAsPx: false, bool passThroughUnsupportedUnits: false}) {
156+
if (value == null) return null;
128157

129-
if (remValue is num) {
130-
remValueNum = remValue;
158+
num pxValueNum;
159+
160+
if (value is num) {
161+
pxValueNum = treatNumAsPx ? value : value * rootFontSize;
131162
} else {
132-
var parsedRemValue = remValue is CssValue ? remValue : new CssValue.parse(remValue);
133-
if (parsedRemValue?.unit != 'rem') {
134-
throw new ArgumentError.value(remValue, 'remValue', 'must be a num or a String rem value');
163+
var parsedValue = value is CssValue ? value : new CssValue.parse(value);
164+
165+
if (parsedValue?.unit == 'px') {
166+
pxValueNum = parsedValue.number;
167+
} else if (parsedValue?.unit == 'rem') {
168+
pxValueNum = parsedValue.number * rootFontSize;
169+
} else {
170+
if (passThroughUnsupportedUnits) {
171+
return parsedValue;
172+
}
173+
174+
throw new ArgumentError.value(value, 'value', 'must be a rem num or a String px/rem value');
135175
}
136-
137-
remValueNum = parsedRemValue.number;
138176
}
139177

140-
return new CssValue(remValueNum * rootFontSize, 'px');
178+
return new CssValue(pxValueNum, 'px');
141179
}

test/over_react/util/rem_util_test.dart

Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -51,40 +51,66 @@ main() {
5151
expect(toRem(new CssValue.parse('15px')), new CssValue(0.75, 'rem'));
5252
});
5353

54-
test('converts an int value (treated as px) to rem', () {
54+
test('converts nums (treated as px) to rem', () {
5555
expect(toRem(15), new CssValue(0.75, 'rem'));
56+
expect(toRem(20.2), new CssValue(1.01, 'rem'));
5657
});
5758

58-
test('converts a double (treated as px) to rem', () {
59-
expect(toRem(20.2), new CssValue(1.01, 'rem'));
59+
test('does not convert nums when `treatNumAsRem` is true', () {
60+
expect(toRem(15, treatNumAsRem: true), new CssValue(15, 'rem'));
61+
expect(toRem(20.2, treatNumAsRem: true), new CssValue(20.2, 'rem'));
62+
});
63+
64+
test('gracefully handles a rem String, not doing any conversion', () {
65+
expect(toRem('1334rem'), new CssValue(1334, 'rem'));
66+
});
67+
68+
test('gracefully handles a rem CssValue, not doing any conversion', () {
69+
expect(toRem(new CssValue(1334, 'rem')), new CssValue(1334, 'rem'));
70+
});
71+
72+
test('gracefully passes through `null`', () {
73+
expect(toRem(null), null);
6074
});
6175

6276
test('throws when passed an invalid value', () {
6377
expect(() => toRem(new Object()), allOf(
6478
throwsArgumentError,
65-
throwsA(hasToStringValue(contains('must be a num or a String px value'))))
79+
throwsA(hasToStringValue(contains('must be a px num or a String px/rem value'))))
6680
);
6781
});
6882

6983
test('throws when passed a malformed CSS value string', () {
7084
expect(() => toRem(''), allOf(
7185
throwsArgumentError,
72-
throwsA(hasToStringValue(contains('must be a num or a String px value'))))
86+
throwsA(hasToStringValue(contains('must be a px num or a String px/rem value'))))
7387
);
7488
});
7589

76-
test('throws when passed a CSS value string with a unit other than px', () {
77-
expect(() => toRem('1em'), allOf(
78-
throwsArgumentError,
79-
throwsA(hasToStringValue(contains('must be a num or a String px value'))))
80-
);
90+
group('throws when passed a CSS value string with a unit other than px/rem', () {
91+
test('', () {
92+
expect(() => toRem('1em'), allOf(
93+
throwsArgumentError,
94+
throwsA(hasToStringValue(contains('must be a px num or a String px/rem value'))))
95+
);
96+
});
97+
98+
test('unless `passThroughUnsupportedUnits` is true', () {
99+
expect(toRem('1em', passThroughUnsupportedUnits: true), new CssValue.parse('1em'));
100+
});
81101
});
82102

83-
test('throws when passed a CssValue instance with a unit other than px', () {
84-
expect(() => toRem(new CssValue.parse('1em')), allOf(
85-
throwsArgumentError,
86-
throwsA(hasToStringValue(contains('must be a num or a String px value'))))
87-
);
103+
group('throws when passed a CssValue instance with a unit other than px/rem', () {
104+
test('', () {
105+
expect(() => toRem(new CssValue.parse('1em')), allOf(
106+
throwsArgumentError,
107+
throwsA(hasToStringValue(contains('must be a px num or a String px/rem value'))))
108+
);
109+
});
110+
111+
test('unless `passThroughUnsupportedUnits` is true', () {
112+
expect(toRem(new CssValue.parse('1em'), passThroughUnsupportedUnits: true), new CssValue.parse('1em'));
113+
});
88114
});
89115
});
90116

@@ -105,40 +131,66 @@ main() {
105131
expect(toPx(new CssValue.parse('0.75rem')), new CssValue(15, 'px'));
106132
});
107133

108-
test('converts an int value (treated as rem) to px', () {
134+
test('converts nums (treated as rem) to px', () {
109135
expect(toPx(3), new CssValue(60, 'px'));
136+
expect(toPx(1.01), new CssValue(20.2, 'px'));
110137
});
111138

112-
test('converts a double (treated as rem) to px', () {
113-
expect(toPx(1.01), new CssValue(20.2, 'px'));
139+
test('does not convert nums when `treatNumAsPx` is true', () {
140+
expect(toPx(3, treatNumAsPx: true), new CssValue(3, 'px'));
141+
expect(toPx(1.01, treatNumAsPx: true), new CssValue(1.01, 'px'));
142+
});
143+
144+
test('gracefully handles a px String, not doing any conversion', () {
145+
expect(toPx('1334px'), new CssValue(1334, 'px'));
146+
});
147+
148+
test('gracefully handles a px CssValue, not doing any conversion', () {
149+
expect(toPx(new CssValue(1334, 'px')), new CssValue(1334, 'px'));
150+
});
151+
152+
test('gracefully passes through `null`', () {
153+
expect(toPx(null), null);
114154
});
115155

116156
test('throws when passed an invalid value', () {
117157
expect(() => toPx(new Object()), allOf(
118158
throwsArgumentError,
119-
throwsA(hasToStringValue(contains('must be a num or a String rem value'))))
159+
throwsA(hasToStringValue(contains('must be a rem num or a String px/rem value'))))
120160
);
121161
});
122162

123163
test('throws when passed a malformed CSS value string', () {
124164
expect(() => toPx(''), allOf(
125165
throwsArgumentError,
126-
throwsA(hasToStringValue(contains('must be a num or a String rem value'))))
166+
throwsA(hasToStringValue(contains('must be a rem num or a String px/rem value'))))
127167
);
128168
});
129169

130-
test('throws when passed a CSS value string with a unit other than px', () {
131-
expect(() => toPx('1em'), allOf(
132-
throwsArgumentError,
133-
throwsA(hasToStringValue(contains('must be a num or a String rem value'))))
134-
);
170+
group('throws when passed a CSS value string with a unit other than px/rem', () {
171+
test('', () {
172+
expect(() => toPx('1em'), allOf(
173+
throwsArgumentError,
174+
throwsA(hasToStringValue(contains('must be a rem num or a String px/rem value'))))
175+
);
176+
});
177+
178+
test('unless `passThroughUnsupportedUnits` is true', () {
179+
expect(toPx('1em', passThroughUnsupportedUnits: true), new CssValue.parse('1em'));
180+
});
135181
});
136182

137-
test('throws when passed a CssValue instance with a unit other than px', () {
138-
expect(() => toPx(new CssValue.parse('1em')), allOf(
139-
throwsArgumentError,
140-
throwsA(hasToStringValue(contains('must be a num or a String rem value'))))
141-
);
183+
group('throws when passed a CssValue instance with a unit other than px/rem', () {
184+
test('', () {
185+
expect(() => toPx(new CssValue.parse('1em')), allOf(
186+
throwsArgumentError,
187+
throwsA(hasToStringValue(contains('must be a rem num or a String px/rem value'))))
188+
);
189+
});
190+
191+
test('unless `passThroughUnsupportedUnits` is true', () {
192+
expect(toPx(new CssValue.parse('1em'), passThroughUnsupportedUnits: true), new CssValue.parse('1em'));
193+
});
142194
});
143195
});
144196

0 commit comments

Comments
 (0)