@just-web/toolkits 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +13 -2
- package/dist/index.d.cts +8 -2
- package/dist/index.d.mts +8 -2
- package/dist/index.mjs +7 -2
- package/dist/style/css-properties.d.cts +2 -1
- package/dist/style/css-properties.d.cts.map +1 -1
- package/dist/style/css-properties.d.mts +2 -1
- package/dist/style/css-properties.d.mts.map +1 -1
- package/dist/units/convert-css-unit.cjs +173 -0
- package/dist/units/convert-css-unit.cjs.map +1 -0
- package/dist/units/convert-css-unit.d.cts +25 -0
- package/dist/units/convert-css-unit.d.cts.map +1 -0
- package/dist/units/convert-css-unit.d.mts +25 -0
- package/dist/units/convert-css-unit.d.mts.map +1 -0
- package/dist/units/convert-css-unit.mjs +173 -0
- package/dist/units/convert-css-unit.mjs.map +1 -0
- package/dist/units/create-css-unit-converter.cjs +33 -0
- package/dist/units/create-css-unit-converter.cjs.map +1 -0
- package/dist/units/create-css-unit-converter.d.cts +28 -0
- package/dist/units/create-css-unit-converter.d.cts.map +1 -0
- package/dist/units/create-css-unit-converter.d.mts +28 -0
- package/dist/units/create-css-unit-converter.d.mts.map +1 -0
- package/dist/units/create-css-unit-converter.mjs +33 -0
- package/dist/units/create-css-unit-converter.mjs.map +1 -0
- package/dist/units/css-unit-converter.types.d.cts +35 -0
- package/dist/units/css-unit-converter.types.d.cts.map +1 -0
- package/dist/units/css-unit-converter.types.d.mts +35 -0
- package/dist/units/css-unit-converter.types.d.mts.map +1 -0
- package/dist/units/get-css-unit.cjs +29 -0
- package/dist/units/get-css-unit.cjs.map +1 -0
- package/dist/units/get-css-unit.d.cts +23 -0
- package/dist/units/get-css-unit.d.cts.map +1 -0
- package/dist/units/get-css-unit.d.mts +23 -0
- package/dist/units/get-css-unit.d.mts.map +1 -0
- package/dist/units/get-css-unit.mjs +29 -0
- package/dist/units/get-css-unit.mjs.map +1 -0
- package/dist/units/is-effectively-zero.cjs +35 -0
- package/dist/units/is-effectively-zero.cjs.map +1 -0
- package/dist/units/is-effectively-zero.d.cts +28 -0
- package/dist/units/is-effectively-zero.d.cts.map +1 -0
- package/dist/units/is-effectively-zero.d.mts +28 -0
- package/dist/units/is-effectively-zero.d.mts.map +1 -0
- package/dist/units/is-effectively-zero.mjs +35 -0
- package/dist/units/is-effectively-zero.mjs.map +1 -0
- package/dist/units/parse-css-number.cjs +29 -0
- package/dist/units/parse-css-number.cjs.map +1 -0
- package/dist/units/parse-css-number.d.cts +24 -0
- package/dist/units/parse-css-number.d.cts.map +1 -0
- package/dist/units/parse-css-number.d.mts +24 -0
- package/dist/units/parse-css-number.d.mts.map +1 -0
- package/dist/units/parse-css-number.mjs +29 -0
- package/dist/units/parse-css-number.mjs.map +1 -0
- package/dist/units/parse-css-value.cjs +32 -0
- package/dist/units/parse-css-value.cjs.map +1 -0
- package/dist/units/parse-css-value.d.cts +22 -0
- package/dist/units/parse-css-value.d.cts.map +1 -0
- package/dist/units/parse-css-value.d.mts +22 -0
- package/dist/units/parse-css-value.d.mts.map +1 -0
- package/dist/units/parse-css-value.mjs +31 -0
- package/dist/units/parse-css-value.mjs.map +1 -0
- package/dist/units/px-2-rem.cjs +8 -5
- package/dist/units/px-2-rem.cjs.map +1 -1
- package/dist/units/px-2-rem.d.cts +9 -7
- package/dist/units/px-2-rem.d.cts.map +1 -1
- package/dist/units/px-2-rem.d.mts +9 -7
- package/dist/units/px-2-rem.d.mts.map +1 -1
- package/dist/units/px-2-rem.mjs +8 -5
- package/dist/units/px-2-rem.mjs.map +1 -1
- package/dist/units/rem-2-px.cjs +8 -5
- package/dist/units/rem-2-px.cjs.map +1 -1
- package/dist/units/rem-2-px.d.cts +9 -7
- package/dist/units/rem-2-px.d.cts.map +1 -1
- package/dist/units/rem-2-px.d.mts +9 -7
- package/dist/units/rem-2-px.d.mts.map +1 -1
- package/dist/units/rem-2-px.mjs +8 -5
- package/dist/units/rem-2-px.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +7 -0
- package/src/style/css-properties.ts +3 -1
- package/src/units/convert-css-unit.ts +292 -0
- package/src/units/create-css-unit-converter.ts +30 -0
- package/src/units/css-unit-converter.types.ts +49 -0
- package/src/units/get-css-unit.ts +24 -0
- package/src/units/is-effectively-zero.ts +35 -0
- package/src/units/parse-css-number.ts +26 -0
- package/src/units/parse-css-value.ts +35 -0
- package/src/units/px-2-num.ts +5 -4
- package/src/units/px-2-rem.ts +12 -8
- package/src/units/rem-2-px.ts +11 -9
- package/dist/units/px-2-num.cjs +0 -23
- package/dist/units/px-2-num.cjs.map +0 -1
- package/dist/units/px-2-num.d.cts +0 -19
- package/dist/units/px-2-num.d.cts.map +0 -1
- package/dist/units/px-2-num.d.mts +0 -19
- package/dist/units/px-2-num.d.mts.map +0 -1
- package/dist/units/px-2-num.mjs +0 -22
- package/dist/units/px-2-num.mjs.map +0 -1
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import type { Required } from 'type-plus'
|
|
2
|
+
import type { ConvertCssUnitOptions, CssLengthUnit } from './css-unit-converter.types.ts'
|
|
3
|
+
import { getRemToPxScale } from './get-rem-to-px-scale.ts'
|
|
4
|
+
import { parseCssValue } from './parse-css-value.ts'
|
|
5
|
+
|
|
6
|
+
const PX_PER_IN = 96
|
|
7
|
+
const PT_PER_IN = 72
|
|
8
|
+
const PC_PER_IN = 6
|
|
9
|
+
const CM_PER_IN = 2.54
|
|
10
|
+
const MM_PER_IN = 25.4
|
|
11
|
+
|
|
12
|
+
const DEFAULT_ELEMENT_FONT_SIZE = 16
|
|
13
|
+
const DEFAULT_PRECISION = 4
|
|
14
|
+
|
|
15
|
+
const ABSOLUTE_UNITS: CssLengthUnit[] = ['px', 'pt', 'pc', 'in', 'cm', 'mm']
|
|
16
|
+
const LINE_UNITS: CssLengthUnit[] = ['lh', 'rlh']
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Converts a CSS length value from one unit to another.
|
|
20
|
+
*
|
|
21
|
+
* @param value - The value to convert. Can be a number or string (e.g. '16px', '1.5rem'). Pass-through for null/undefined.
|
|
22
|
+
* @param toUnit - The target unit.
|
|
23
|
+
* @param options - Conversion context. When omitted, uses browser auto-detect for rootFontSize and viewport when available.
|
|
24
|
+
* @returns The converted numeric value, or null/undefined when input is null/undefined.
|
|
25
|
+
* @throws When required context is missing (viewport, lineHeight, percentReference) or percentReference is 0 for % conversion.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* convertCssUnit('16px', 'rem') // 1
|
|
30
|
+
* convertCssUnit('1rem', 'px') // 16
|
|
31
|
+
* convertCssUnit('50%', 'px', { percentReference: 200 }) // 100
|
|
32
|
+
* convertCssUnit('10vw', 'px', { viewportWidth: 375 }) // 37.5
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function convertCssUnit(
|
|
36
|
+
value: number | string | null | undefined,
|
|
37
|
+
toUnit: CssLengthUnit,
|
|
38
|
+
options?: ConvertCssUnitOptions | undefined
|
|
39
|
+
): number | null | undefined {
|
|
40
|
+
const [num, parsedUnit] = parseCssValue(value)
|
|
41
|
+
if (num === null || num === undefined) return num
|
|
42
|
+
if (Number.isNaN(num)) {
|
|
43
|
+
throw new Error(`Invalid CSS value: ${value}`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const fromUnit: CssLengthUnit = options?.fromUnit ?? normalizeUnit(parsedUnit) ?? 'px'
|
|
47
|
+
|
|
48
|
+
const resolved = resolveOptions(options)
|
|
49
|
+
|
|
50
|
+
if (fromUnit === toUnit) {
|
|
51
|
+
return Number(num.toFixed(resolved.precision))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let result: number
|
|
55
|
+
|
|
56
|
+
if (fromUnit === 'rem' && toUnit === 'em') {
|
|
57
|
+
result = remToEmDirect(num, resolved)
|
|
58
|
+
} else if (fromUnit === 'em' && toUnit === 'rem') {
|
|
59
|
+
result = emToRemDirect(num, resolved)
|
|
60
|
+
} else {
|
|
61
|
+
const px = valueToPx(num, fromUnit, resolved)
|
|
62
|
+
result = pxToValue(px, toUnit, resolved)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return Number(result.toFixed(resolved.precision))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeUnit(unit: string | undefined): CssLengthUnit | undefined {
|
|
69
|
+
if (!unit) return undefined
|
|
70
|
+
const u = unit.toLowerCase()
|
|
71
|
+
if (
|
|
72
|
+
u === 'px' ||
|
|
73
|
+
u === 'pt' ||
|
|
74
|
+
u === 'pc' ||
|
|
75
|
+
u === 'in' ||
|
|
76
|
+
u === 'cm' ||
|
|
77
|
+
u === 'mm' ||
|
|
78
|
+
u === 'rem' ||
|
|
79
|
+
u === 'em' ||
|
|
80
|
+
u === 'vw' ||
|
|
81
|
+
u === 'vh' ||
|
|
82
|
+
u === 'vmin' ||
|
|
83
|
+
u === 'vmax' ||
|
|
84
|
+
u === 'lh' ||
|
|
85
|
+
u === 'rlh' ||
|
|
86
|
+
u === 'ch' ||
|
|
87
|
+
u === '%'
|
|
88
|
+
) {
|
|
89
|
+
return u as CssLengthUnit
|
|
90
|
+
}
|
|
91
|
+
return undefined
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function resolveOptions(
|
|
95
|
+
options?: ConvertCssUnitOptions | undefined
|
|
96
|
+
): Required<
|
|
97
|
+
Pick<
|
|
98
|
+
ConvertCssUnitOptions,
|
|
99
|
+
| 'rootFontSize'
|
|
100
|
+
| 'elementFontSize'
|
|
101
|
+
| 'viewportWidth'
|
|
102
|
+
| 'viewportHeight'
|
|
103
|
+
| 'lineHeight'
|
|
104
|
+
| 'chWidth'
|
|
105
|
+
| 'percentReference'
|
|
106
|
+
| 'precision'
|
|
107
|
+
>
|
|
108
|
+
> {
|
|
109
|
+
const rootFontSize = options?.rootFontSize ?? getRemToPxScale()
|
|
110
|
+
const elementFontSize = options?.elementFontSize ?? DEFAULT_ELEMENT_FONT_SIZE
|
|
111
|
+
const precision = options?.precision ?? DEFAULT_PRECISION
|
|
112
|
+
|
|
113
|
+
let viewportWidth = options?.viewportWidth
|
|
114
|
+
let viewportHeight = options?.viewportHeight
|
|
115
|
+
if (typeof window !== 'undefined') {
|
|
116
|
+
viewportWidth ??= window.innerWidth
|
|
117
|
+
viewportHeight ??= window.innerHeight
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const lineHeight = options?.lineHeight
|
|
121
|
+
const chWidth = options?.chWidth ?? elementFontSize * 0.5
|
|
122
|
+
const percentReference = options?.percentReference ?? 0
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
rootFontSize,
|
|
126
|
+
elementFontSize,
|
|
127
|
+
viewportWidth: viewportWidth ?? 0,
|
|
128
|
+
viewportHeight: viewportHeight ?? 0,
|
|
129
|
+
lineHeight: lineHeight ?? 0,
|
|
130
|
+
chWidth,
|
|
131
|
+
percentReference,
|
|
132
|
+
precision
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function remToEmDirect(value: number, resolved: ReturnType<typeof resolveOptions>): number {
|
|
137
|
+
return value * (resolved.rootFontSize / resolved.elementFontSize)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function emToRemDirect(value: number, resolved: ReturnType<typeof resolveOptions>): number {
|
|
141
|
+
return value * (resolved.elementFontSize / resolved.rootFontSize)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function valueToPx(
|
|
145
|
+
value: number,
|
|
146
|
+
fromUnit: CssLengthUnit,
|
|
147
|
+
resolved: ReturnType<typeof resolveOptions>
|
|
148
|
+
): number {
|
|
149
|
+
if (ABSOLUTE_UNITS.includes(fromUnit)) {
|
|
150
|
+
return toPxFromAbsolute(value, fromUnit)
|
|
151
|
+
}
|
|
152
|
+
if (fromUnit === 'rem') {
|
|
153
|
+
return value * (resolved.rootFontSize ?? 0)
|
|
154
|
+
}
|
|
155
|
+
if (fromUnit === 'em') {
|
|
156
|
+
return value * (resolved.elementFontSize ?? 0)
|
|
157
|
+
}
|
|
158
|
+
if (fromUnit === 'vw') {
|
|
159
|
+
if (resolved.viewportWidth === 0) {
|
|
160
|
+
throw new Error('viewportWidth is required for vw conversion')
|
|
161
|
+
}
|
|
162
|
+
return (value / 100) * resolved.viewportWidth
|
|
163
|
+
}
|
|
164
|
+
if (fromUnit === 'vh') {
|
|
165
|
+
if (resolved.viewportHeight === 0) {
|
|
166
|
+
throw new Error('viewportHeight is required for vh conversion')
|
|
167
|
+
}
|
|
168
|
+
return (value / 100) * resolved.viewportHeight
|
|
169
|
+
}
|
|
170
|
+
if (fromUnit === 'vmin') {
|
|
171
|
+
if (resolved.viewportWidth === 0 || resolved.viewportHeight === 0) {
|
|
172
|
+
throw new Error('viewportWidth and viewportHeight are required for vmin conversion')
|
|
173
|
+
}
|
|
174
|
+
return (value / 100) * Math.min(resolved.viewportWidth, resolved.viewportHeight)
|
|
175
|
+
}
|
|
176
|
+
if (fromUnit === 'vmax') {
|
|
177
|
+
if (resolved.viewportWidth === 0 || resolved.viewportHeight === 0) {
|
|
178
|
+
throw new Error('viewportWidth and viewportHeight are required for vmax conversion')
|
|
179
|
+
}
|
|
180
|
+
return (value / 100) * Math.max(resolved.viewportWidth, resolved.viewportHeight)
|
|
181
|
+
}
|
|
182
|
+
if (LINE_UNITS.includes(fromUnit)) {
|
|
183
|
+
if (resolved.lineHeight === 0) {
|
|
184
|
+
throw new Error('lineHeight is required for lh/rlh conversion')
|
|
185
|
+
}
|
|
186
|
+
return value * resolved.lineHeight
|
|
187
|
+
}
|
|
188
|
+
if (fromUnit === 'ch') {
|
|
189
|
+
return value * resolved.chWidth
|
|
190
|
+
}
|
|
191
|
+
if (fromUnit === '%') {
|
|
192
|
+
if (resolved.percentReference === 0) {
|
|
193
|
+
throw new Error('percentReference is required for % conversion')
|
|
194
|
+
}
|
|
195
|
+
return (value / 100) * resolved.percentReference
|
|
196
|
+
}
|
|
197
|
+
throw new Error(`Unsupported unit: ${fromUnit}`)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function pxToValue(
|
|
201
|
+
px: number,
|
|
202
|
+
toUnit: CssLengthUnit,
|
|
203
|
+
resolved: ReturnType<typeof resolveOptions>
|
|
204
|
+
): number {
|
|
205
|
+
if (ABSOLUTE_UNITS.includes(toUnit)) {
|
|
206
|
+
return fromPxToAbsolute(px, toUnit)
|
|
207
|
+
}
|
|
208
|
+
if (toUnit === 'rem') {
|
|
209
|
+
return px / resolved.rootFontSize
|
|
210
|
+
}
|
|
211
|
+
if (toUnit === 'em') {
|
|
212
|
+
return px / resolved.elementFontSize
|
|
213
|
+
}
|
|
214
|
+
if (toUnit === 'vw') {
|
|
215
|
+
if (resolved.viewportWidth === 0) {
|
|
216
|
+
throw new Error('viewportWidth is required for vw conversion')
|
|
217
|
+
}
|
|
218
|
+
return (px / resolved.viewportWidth) * 100
|
|
219
|
+
}
|
|
220
|
+
if (toUnit === 'vh') {
|
|
221
|
+
if (resolved.viewportHeight === 0) {
|
|
222
|
+
throw new Error('viewportHeight is required for vh conversion')
|
|
223
|
+
}
|
|
224
|
+
return (px / resolved.viewportHeight) * 100
|
|
225
|
+
}
|
|
226
|
+
if (toUnit === 'vmin') {
|
|
227
|
+
if (resolved.viewportWidth === 0 || resolved.viewportHeight === 0) {
|
|
228
|
+
throw new Error('viewportWidth and viewportHeight are required for vmin conversion')
|
|
229
|
+
}
|
|
230
|
+
return (px / Math.min(resolved.viewportWidth, resolved.viewportHeight)) * 100
|
|
231
|
+
}
|
|
232
|
+
if (toUnit === 'vmax') {
|
|
233
|
+
if (resolved.viewportWidth === 0 || resolved.viewportHeight === 0) {
|
|
234
|
+
throw new Error('viewportWidth and viewportHeight are required for vmax conversion')
|
|
235
|
+
}
|
|
236
|
+
return (px / Math.max(resolved.viewportWidth, resolved.viewportHeight)) * 100
|
|
237
|
+
}
|
|
238
|
+
if (LINE_UNITS.includes(toUnit)) {
|
|
239
|
+
if (resolved.lineHeight === 0) {
|
|
240
|
+
throw new Error('lineHeight is required for lh/rlh conversion')
|
|
241
|
+
}
|
|
242
|
+
return px / resolved.lineHeight
|
|
243
|
+
}
|
|
244
|
+
if (toUnit === 'ch') {
|
|
245
|
+
return px / resolved.chWidth
|
|
246
|
+
}
|
|
247
|
+
if (toUnit === '%') {
|
|
248
|
+
if (resolved.percentReference === 0) {
|
|
249
|
+
throw new Error('percentReference is required and must be non-zero for conversion to %')
|
|
250
|
+
}
|
|
251
|
+
return (px / resolved.percentReference) * 100
|
|
252
|
+
}
|
|
253
|
+
throw new Error(`Unsupported unit: ${toUnit}`)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function toPxFromAbsolute(value: number, unit: CssLengthUnit): number {
|
|
257
|
+
switch (unit) {
|
|
258
|
+
case 'px':
|
|
259
|
+
return value
|
|
260
|
+
case 'pt':
|
|
261
|
+
return (value * PX_PER_IN) / PT_PER_IN
|
|
262
|
+
case 'pc':
|
|
263
|
+
return (value * PX_PER_IN) / PC_PER_IN
|
|
264
|
+
case 'in':
|
|
265
|
+
return value * PX_PER_IN
|
|
266
|
+
case 'cm':
|
|
267
|
+
return (value * PX_PER_IN) / CM_PER_IN
|
|
268
|
+
case 'mm':
|
|
269
|
+
return (value * PX_PER_IN) / MM_PER_IN
|
|
270
|
+
default:
|
|
271
|
+
return value
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function fromPxToAbsolute(px: number, unit: CssLengthUnit): number {
|
|
276
|
+
switch (unit) {
|
|
277
|
+
case 'px':
|
|
278
|
+
return px
|
|
279
|
+
case 'pt':
|
|
280
|
+
return (px * PT_PER_IN) / PX_PER_IN
|
|
281
|
+
case 'pc':
|
|
282
|
+
return (px * PC_PER_IN) / PX_PER_IN
|
|
283
|
+
case 'in':
|
|
284
|
+
return px / PX_PER_IN
|
|
285
|
+
case 'cm':
|
|
286
|
+
return (px * CM_PER_IN) / PX_PER_IN
|
|
287
|
+
case 'mm':
|
|
288
|
+
return (px * MM_PER_IN) / PX_PER_IN
|
|
289
|
+
default:
|
|
290
|
+
return px
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { convertCssUnit } from './convert-css-unit.ts'
|
|
2
|
+
import type { CssLengthUnit, CssUnitConverterContext } from './css-unit-converter.types.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a pre-configured CSS unit converter with fixed context.
|
|
6
|
+
*
|
|
7
|
+
* @param context - Root font size, viewport, line height, etc. Omitted values use browser auto-detect when available.
|
|
8
|
+
* @returns A converter function that accepts value and toUnit (and optional fromUnit override).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const convert = createCssUnitConverter({
|
|
13
|
+
* rootFontSize: 16,
|
|
14
|
+
* viewportWidth: 375,
|
|
15
|
+
* viewportHeight: 812,
|
|
16
|
+
* })
|
|
17
|
+
* convert('1rem', 'px') // 16
|
|
18
|
+
* convert('10vw', 'px') // 37.5
|
|
19
|
+
* convert(16, 'rem', { fromUnit: 'px' }) // 1
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function createCssUnitConverter(context?: CssUnitConverterContext) {
|
|
23
|
+
return function convert(
|
|
24
|
+
value: number | string,
|
|
25
|
+
toUnit: CssLengthUnit,
|
|
26
|
+
options?: { fromUnit?: CssLengthUnit | undefined } | undefined
|
|
27
|
+
): number | null | undefined {
|
|
28
|
+
return convertCssUnit(value, toUnit, { ...context, ...options })
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported CSS length units for conversion.
|
|
3
|
+
*/
|
|
4
|
+
export type CssLengthUnit =
|
|
5
|
+
| 'px'
|
|
6
|
+
| 'pt'
|
|
7
|
+
| 'pc'
|
|
8
|
+
| 'in'
|
|
9
|
+
| 'cm'
|
|
10
|
+
| 'mm'
|
|
11
|
+
| 'rem'
|
|
12
|
+
| 'em'
|
|
13
|
+
| 'vw'
|
|
14
|
+
| 'vh'
|
|
15
|
+
| 'vmin'
|
|
16
|
+
| 'vmax'
|
|
17
|
+
| 'lh'
|
|
18
|
+
| 'rlh'
|
|
19
|
+
| 'ch'
|
|
20
|
+
| '%'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for convertCssUnit.
|
|
24
|
+
*/
|
|
25
|
+
export interface ConvertCssUnitOptions {
|
|
26
|
+
/** Override when value is number or unitless string. Default: 'px'. */
|
|
27
|
+
fromUnit?: CssLengthUnit | undefined
|
|
28
|
+
/** Root font size in px for rem. Auto: getRemToPxScale() in browser. Default: 16. */
|
|
29
|
+
rootFontSize?: number | undefined
|
|
30
|
+
/** Element font size in px for em. Default: 16. */
|
|
31
|
+
elementFontSize?: number | undefined
|
|
32
|
+
/** Viewport width in px for vw, vmin, vmax. Auto: window.innerWidth in browser. */
|
|
33
|
+
viewportWidth?: number | undefined
|
|
34
|
+
/** Viewport height in px for vh, vmin, vmax. Auto: window.innerHeight in browser. */
|
|
35
|
+
viewportHeight?: number | undefined
|
|
36
|
+
/** Line height in px for lh, rlh. */
|
|
37
|
+
lineHeight?: number | undefined
|
|
38
|
+
/** Width of "0" character in px for ch. Default: ~0.5em. */
|
|
39
|
+
chWidth?: number | undefined
|
|
40
|
+
/** Value that 100% equals (for % conversions). */
|
|
41
|
+
percentReference?: number | undefined
|
|
42
|
+
/** Decimal places for output. Default: 4. */
|
|
43
|
+
precision?: number | undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Context for createCssUnitConverter (pre-configured values).
|
|
48
|
+
*/
|
|
49
|
+
export type CssUnitConverterContext = Omit<ConvertCssUnitOptions, 'fromUnit'>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { parseCssValue } from './parse-css-value.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts the unit from a CSS value string.
|
|
5
|
+
* Thin wrapper around parseCssValue.
|
|
6
|
+
*
|
|
7
|
+
* @param value - The CSS value to parse (e.g. '16px', '1.5rem', '100%'). Pass-through for null/undefined.
|
|
8
|
+
* @returns The unit string, undefined for numbers or unitless strings, or null/undefined when input is null/undefined
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* getCssUnit('16px') // 'px'
|
|
13
|
+
* getCssUnit('1rem') // 'rem'
|
|
14
|
+
* getCssUnit('100%') // '%'
|
|
15
|
+
* getCssUnit('0') // undefined
|
|
16
|
+
* getCssUnit('16') // undefined
|
|
17
|
+
* getCssUnit(null) // null
|
|
18
|
+
* getCssUnit(undefined) // undefined
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function getCssUnit(value: number | string | null | undefined): string | null | undefined {
|
|
22
|
+
if (value === null || value === undefined) return value
|
|
23
|
+
return parseCssValue(value)[1]
|
|
24
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { parseCssNumber } from './parse-css-number.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Determines if a CSS value is effectively 0 regardless of unit.
|
|
5
|
+
*
|
|
6
|
+
* @param value - The CSS value to check. Can be a number or string (e.g. '0px', '0rem', '0%'). Pass-through for null/undefined.
|
|
7
|
+
* @param options - Optional configuration
|
|
8
|
+
* @param options.epsilon - Floating-point tolerance. Default 1e-10. Use 0 for strict equality.
|
|
9
|
+
* @returns true if the value is effectively zero, false otherwise, or null/undefined when input is null/undefined
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* isEffectivelyZero(0) // true
|
|
14
|
+
* isEffectivelyZero('0px') // true
|
|
15
|
+
* isEffectivelyZero('0rem') // true
|
|
16
|
+
* isEffectivelyZero('0%') // true
|
|
17
|
+
* isEffectivelyZero('1px') // false
|
|
18
|
+
* isEffectivelyZero(0.00000000001) // true (within default epsilon)
|
|
19
|
+
* isEffectivelyZero(0.0001, { epsilon: 0.001 }) // true
|
|
20
|
+
* isEffectivelyZero(null) // null
|
|
21
|
+
* isEffectivelyZero(undefined) // undefined
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function isEffectivelyZero(
|
|
25
|
+
value: number | string | null | undefined,
|
|
26
|
+
options?: { epsilon?: number | undefined } | undefined
|
|
27
|
+
): boolean | null | undefined {
|
|
28
|
+
const parsed = parseCssNumber(value)
|
|
29
|
+
if (parsed === null || parsed === undefined) return parsed
|
|
30
|
+
if (!Number.isFinite(parsed)) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
const epsilon = options?.epsilon ?? 1e-10
|
|
34
|
+
return Math.abs(parsed) <= epsilon
|
|
35
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { parseCssValue } from './parse-css-value.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts the numeric part from any CSS length/percentage value.
|
|
5
|
+
* Thin wrapper around parseCssValue.
|
|
6
|
+
*
|
|
7
|
+
* @param value - The CSS value to parse. Can be a number or string (e.g. '16px', '1.5rem', '100%')
|
|
8
|
+
* @returns The numeric value, or NaN for invalid input. Passes through null and undefined.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* parseCssNumber('16px') // 16
|
|
13
|
+
* parseCssNumber('1.5rem') // 1.5
|
|
14
|
+
* parseCssNumber('100%') // 100
|
|
15
|
+
* parseCssNumber('0lh') // 0
|
|
16
|
+
* parseCssNumber(16) // 16
|
|
17
|
+
* parseCssNumber('abc') // NaN
|
|
18
|
+
* parseCssNumber(null) // null
|
|
19
|
+
* parseCssNumber(undefined) // undefined
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function parseCssNumber(
|
|
23
|
+
value: number | string | null | undefined
|
|
24
|
+
): number | null | undefined {
|
|
25
|
+
return parseCssValue(value)[0]
|
|
26
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a CSS value in one pass and returns both the numeric part and the unit.
|
|
3
|
+
* Powers parseCssNumber, getCssUnit, and isEffectivelyZero.
|
|
4
|
+
*
|
|
5
|
+
* @param value - The CSS value to parse. Can be a number or string (e.g. '16px', '1.5rem', '100%')
|
|
6
|
+
* @returns A tuple of [number, unit | undefined]. Unit is undefined for numbers or unitless strings.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* parseCssValue('16px') // [16, 'px']
|
|
11
|
+
* parseCssValue('1.5rem') // [1.5, 'rem']
|
|
12
|
+
* parseCssValue('100%') // [100, '%']
|
|
13
|
+
* parseCssValue('0') // [0, undefined]
|
|
14
|
+
* parseCssValue(16) // [16, undefined]
|
|
15
|
+
* parseCssValue('abc') // [NaN, undefined]
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function parseCssValue(
|
|
19
|
+
value: number | string | null | undefined
|
|
20
|
+
): [number | null | undefined, string | undefined] {
|
|
21
|
+
if (value === undefined || value === null) {
|
|
22
|
+
return [value, undefined]
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === 'number') {
|
|
25
|
+
return [value, undefined]
|
|
26
|
+
}
|
|
27
|
+
const s = String(value).trim()
|
|
28
|
+
const match = s.match(/^(-?\d*\.?\d+)\s*(.*)$/)
|
|
29
|
+
if (!match) {
|
|
30
|
+
return [Number.NaN, undefined]
|
|
31
|
+
}
|
|
32
|
+
const num = Number.parseFloat(match[1] ?? '')
|
|
33
|
+
const unit = (match[2] ?? '').trim()
|
|
34
|
+
return [num, unit === '' ? undefined : unit]
|
|
35
|
+
}
|
package/src/units/px-2-num.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Converts pixel values to numbers.
|
|
3
|
+
* Alias for {@link parseCssNumber}. Passes through null and undefined.
|
|
3
4
|
*
|
|
4
5
|
* @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')
|
|
5
|
-
* @returns The numeric value
|
|
6
|
+
* @returns The numeric value, or null/undefined when input is null/undefined
|
|
6
7
|
*
|
|
7
8
|
* @example
|
|
8
9
|
* ```ts
|
|
@@ -10,8 +11,8 @@
|
|
|
10
11
|
* px2num('32px') // 32
|
|
11
12
|
* px2num('12.5px') // 12.5
|
|
12
13
|
* px2num('0px') // 0
|
|
14
|
+
* px2num(null) // null
|
|
15
|
+
* px2num(undefined) // undefined
|
|
13
16
|
* ```
|
|
14
17
|
*/
|
|
15
|
-
export
|
|
16
|
-
return typeof px === 'string' ? Number.parseFloat(px.replace(/px$/, '')) : Number(px)
|
|
17
|
-
}
|
|
18
|
+
export { parseCssNumber as px2num } from './parse-css-number.ts'
|
package/src/units/px-2-rem.ts
CHANGED
|
@@ -5,20 +5,24 @@
|
|
|
5
5
|
* @param options - Optional configuration
|
|
6
6
|
* @param options.base - Base pixel value to calculate rem units from. Defaults to 16
|
|
7
7
|
* @param options.precision - Number of decimal places in the output. Defaults to 4
|
|
8
|
-
* @returns The converted value
|
|
8
|
+
* @returns The converted value, or null/undefined if input is null/undefined
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
12
|
-
* px2rem(16) //
|
|
13
|
-
* px2rem('32px') //
|
|
14
|
-
* px2rem(20, { base: 20 }) //
|
|
15
|
-
* px2rem(13, { precision: 2 }) //
|
|
12
|
+
* px2rem(16) // 1
|
|
13
|
+
* px2rem('32px') // 2
|
|
14
|
+
* px2rem(20, { base: 20 }) // 1
|
|
15
|
+
* px2rem(13, { precision: 2 }) // 0.81
|
|
16
|
+
* px2rem(null) // null
|
|
17
|
+
* px2rem(undefined) // undefined
|
|
16
18
|
* ```
|
|
17
19
|
*/
|
|
18
20
|
export function px2rem(
|
|
19
|
-
px: number | string,
|
|
20
|
-
options?: { base?: number | undefined; precision?: number | undefined }
|
|
21
|
-
): number {
|
|
21
|
+
px: number | string | null | undefined,
|
|
22
|
+
options?: { base?: number | undefined; precision?: number | undefined } | undefined
|
|
23
|
+
): number | null | undefined {
|
|
24
|
+
if (px === null || px === undefined) return px
|
|
25
|
+
|
|
22
26
|
const { base = 16, precision = 4 } = options ?? {}
|
|
23
27
|
|
|
24
28
|
if (typeof px === 'string') {
|
package/src/units/rem-2-px.ts
CHANGED
|
@@ -5,26 +5,28 @@
|
|
|
5
5
|
* @param options - Optional configuration
|
|
6
6
|
* @param options.base - Base pixel value to calculate pixels from. Defaults to 16
|
|
7
7
|
* @param options.precision - Number of decimal places in the output. Defaults to 4
|
|
8
|
-
* @returns The converted value
|
|
8
|
+
* @returns The converted value, or null/undefined if input is null/undefined
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
12
|
-
* rem2px(1) //
|
|
13
|
-
* rem2px('2rem') //
|
|
14
|
-
* rem2px(1, { base: 20 }) //
|
|
15
|
-
* rem2px(0.8125, { precision: 2 }) //
|
|
12
|
+
* rem2px(1) // 16
|
|
13
|
+
* rem2px('2rem') // 32
|
|
14
|
+
* rem2px(1, { base: 20 }) // 20
|
|
15
|
+
* rem2px(0.8125, { precision: 2 }) // 13
|
|
16
|
+
* rem2px(null) // null
|
|
17
|
+
* rem2px(undefined) // undefined
|
|
16
18
|
* ```
|
|
17
19
|
*/
|
|
18
20
|
export function rem2px(
|
|
19
|
-
rem: number | string,
|
|
21
|
+
rem: number | string | null | undefined,
|
|
20
22
|
options?: { base?: number | undefined; precision?: number | undefined }
|
|
21
|
-
): number {
|
|
22
|
-
|
|
23
|
+
): number | null | undefined {
|
|
24
|
+
if (rem === null || rem === undefined) return rem
|
|
23
25
|
|
|
26
|
+
const { base = 16, precision = 4 } = options ?? {}
|
|
24
27
|
if (typeof rem === 'string') {
|
|
25
28
|
rem = rem.replace(/rem$/, '')
|
|
26
29
|
rem = Number.parseFloat(rem)
|
|
27
30
|
}
|
|
28
|
-
|
|
29
31
|
return Number((rem * base).toFixed(precision))
|
|
30
32
|
}
|
package/dist/units/px-2-num.cjs
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
//#region src/units/px-2-num.ts
|
|
3
|
-
/**
|
|
4
|
-
* Converts pixel values to numbers.
|
|
5
|
-
*
|
|
6
|
-
* @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')
|
|
7
|
-
* @returns The numeric value
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* px2num(16) // 16
|
|
12
|
-
* px2num('32px') // 32
|
|
13
|
-
* px2num('12.5px') // 12.5
|
|
14
|
-
* px2num('0px') // 0
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
function px2num(px) {
|
|
18
|
-
return typeof px === "string" ? Number.parseFloat(px.replace(/px$/, "")) : Number(px);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
exports.px2num = px2num;
|
|
23
|
-
//# sourceMappingURL=px-2-num.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"px-2-num.cjs","names":[],"sources":["../../src/units/px-2-num.ts"],"sourcesContent":["/**\n * Converts pixel values to numbers.\n *\n * @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')\n * @returns The numeric value\n *\n * @example\n * ```ts\n * px2num(16) // 16\n * px2num('32px') // 32\n * px2num('12.5px') // 12.5\n * px2num('0px') // 0\n * ```\n */\nexport function px2num(px: number | string | undefined): number {\n\treturn typeof px === 'string' ? Number.parseFloat(px.replace(/px$/, '')) : Number(px)\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAcA,SAAgB,OAAO,IAAyC;AAC/D,QAAO,OAAO,OAAO,WAAW,OAAO,WAAW,GAAG,QAAQ,OAAO,GAAG,CAAC,GAAG,OAAO,GAAG"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
//#region src/units/px-2-num.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Converts pixel values to numbers.
|
|
4
|
-
*
|
|
5
|
-
* @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')
|
|
6
|
-
* @returns The numeric value
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* px2num(16) // 16
|
|
11
|
-
* px2num('32px') // 32
|
|
12
|
-
* px2num('12.5px') // 12.5
|
|
13
|
-
* px2num('0px') // 0
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
declare function px2num(px: number | string | undefined): number;
|
|
17
|
-
//#endregion
|
|
18
|
-
export { px2num };
|
|
19
|
-
//# sourceMappingURL=px-2-num.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"px-2-num.d.cts","names":[],"sources":["../../src/units/px-2-num.ts"],"sourcesContent":[],"mappings":";;AAcA;;;;;;;;;;;;;iBAAgB,MAAA"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
//#region src/units/px-2-num.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Converts pixel values to numbers.
|
|
4
|
-
*
|
|
5
|
-
* @param px - The pixel value to convert. Can be a number or string (e.g. '16px' or '16')
|
|
6
|
-
* @returns The numeric value
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* px2num(16) // 16
|
|
11
|
-
* px2num('32px') // 32
|
|
12
|
-
* px2num('12.5px') // 12.5
|
|
13
|
-
* px2num('0px') // 0
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
declare function px2num(px: number | string | undefined): number;
|
|
17
|
-
//#endregion
|
|
18
|
-
export { px2num };
|
|
19
|
-
//# sourceMappingURL=px-2-num.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"px-2-num.d.mts","names":[],"sources":["../../src/units/px-2-num.ts"],"sourcesContent":[],"mappings":";;AAcA;;;;;;;;;;;;;iBAAgB,MAAA"}
|