css-calipers 0.0.0 → 0.3.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/LICENSE.txt +1 -1
- package/README.md +298 -0
- package/RELEASING.md +62 -0
- package/dist/cjs/core.js +279 -0
- package/dist/cjs/index.js +30 -0
- package/dist/cjs/internal/errors.js +17 -0
- package/dist/cjs/unitDefinitions.js +67 -0
- package/dist/cjs/units/absolute.js +11 -0
- package/dist/cjs/units/angle.js +8 -0
- package/dist/cjs/units/container.js +10 -0
- package/dist/cjs/units/font-relative.js +16 -0
- package/dist/cjs/units/frequency.js +6 -0
- package/dist/cjs/units/grid.js +5 -0
- package/dist/cjs/units/percent.js +7 -0
- package/dist/cjs/units/resolution.js +7 -0
- package/dist/cjs/units/time.js +6 -0
- package/dist/cjs/units/viewport-dynamic.js +10 -0
- package/dist/cjs/units/viewport-large.js +10 -0
- package/dist/cjs/units/viewport-small.js +10 -0
- package/dist/cjs/units/viewport.js +10 -0
- package/dist/esm/core.d.ts +307 -0
- package/dist/esm/core.d.ts.map +1 -0
- package/dist/esm/core.js +264 -0
- package/dist/esm/index.d.ts +15 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/internal/errors.d.ts +28 -0
- package/dist/esm/internal/errors.d.ts.map +1 -0
- package/dist/esm/internal/errors.js +12 -0
- package/dist/esm/unitDefinitions.d.ts +259 -0
- package/dist/esm/unitDefinitions.d.ts.map +1 -0
- package/dist/esm/unitDefinitions.js +64 -0
- package/dist/esm/units/absolute.d.ts +16 -0
- package/dist/esm/units/absolute.d.ts.map +1 -0
- package/dist/esm/units/absolute.js +8 -0
- package/dist/esm/units/angle.d.ts +10 -0
- package/dist/esm/units/angle.d.ts.map +1 -0
- package/dist/esm/units/angle.js +5 -0
- package/dist/esm/units/container.d.ts +14 -0
- package/dist/esm/units/container.d.ts.map +1 -0
- package/dist/esm/units/container.js +7 -0
- package/dist/esm/units/font-relative.d.ts +26 -0
- package/dist/esm/units/font-relative.d.ts.map +1 -0
- package/dist/esm/units/font-relative.js +13 -0
- package/dist/esm/units/frequency.d.ts +6 -0
- package/dist/esm/units/frequency.d.ts.map +1 -0
- package/dist/esm/units/frequency.js +3 -0
- package/dist/esm/units/grid.d.ts +4 -0
- package/dist/esm/units/grid.d.ts.map +1 -0
- package/dist/esm/units/grid.js +2 -0
- package/dist/esm/units/percent.d.ts +6 -0
- package/dist/esm/units/percent.d.ts.map +1 -0
- package/dist/esm/units/percent.js +4 -0
- package/dist/esm/units/resolution.d.ts +8 -0
- package/dist/esm/units/resolution.d.ts.map +1 -0
- package/dist/esm/units/resolution.js +4 -0
- package/dist/esm/units/time.d.ts +6 -0
- package/dist/esm/units/time.d.ts.map +1 -0
- package/dist/esm/units/time.js +3 -0
- package/dist/esm/units/viewport-dynamic.d.ts +14 -0
- package/dist/esm/units/viewport-dynamic.d.ts.map +1 -0
- package/dist/esm/units/viewport-dynamic.js +7 -0
- package/dist/esm/units/viewport-large.d.ts +14 -0
- package/dist/esm/units/viewport-large.d.ts.map +1 -0
- package/dist/esm/units/viewport-large.js +7 -0
- package/dist/esm/units/viewport-small.d.ts +14 -0
- package/dist/esm/units/viewport-small.d.ts.map +1 -0
- package/dist/esm/units/viewport-small.js +7 -0
- package/dist/esm/units/viewport.d.ts +14 -0
- package/dist/esm/units/viewport.d.ts.map +1 -0
- package/dist/esm/units/viewport.js +7 -0
- package/package.json +47 -7
- package/README.txt +0 -21
package/LICENSE.txt
CHANGED
package/README.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# CSS-Calipers
|
|
2
|
+
|
|
3
|
+
**CSS is code. Measure it like one.**
|
|
4
|
+
Compile-time unit safety for numeric, unit-bearing CSS values, no surprises at runtime.
|
|
5
|
+
|
|
6
|
+
CSS-Calipers is a tiny layer for typed CSS measurements. Stop parsing CSS
|
|
7
|
+
strings and concatenating units. Do your math on real numbers, get
|
|
8
|
+
compile-time unit safety, and output CSS only at the edges.
|
|
9
|
+
|
|
10
|
+
At a glance:
|
|
11
|
+
|
|
12
|
+
- Create measurements with `m` from a number and a unit (defaults to `px`).
|
|
13
|
+
- Do unit-safe math with methods like `add` and `multiply`, then call `.css()`
|
|
14
|
+
at the edge to get a CSS string (for example "10px").
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install css-calipers
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Status & support
|
|
23
|
+
|
|
24
|
+
> 🚧 Work in progress.
|
|
25
|
+
> API surface and docs may change between `0.x` releases until the first stable version.
|
|
26
|
+
|
|
27
|
+
- Status: early `0.x` release. Backwards compatibility is not guaranteed until `1.0.0`.
|
|
28
|
+
- Questions or bugs: open an issue on GitHub (see the repository link at the top of this page or in `package.json`).
|
|
29
|
+
- Tooling: tested primarily with TypeScript 5.6+ on Node 18+.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { m } from "css-calipers";
|
|
37
|
+
|
|
38
|
+
// Declare vars
|
|
39
|
+
const paddingBase = m(4); // defaults to "px" with no unit specified
|
|
40
|
+
const rotation = m(45, "deg"); // equivalent to a dedicated helper like mDeg(45)
|
|
41
|
+
|
|
42
|
+
// Do safe arithmetic
|
|
43
|
+
const margins = paddingBase.add(4);
|
|
44
|
+
const offset = paddingBase.add(margins).multiply(2).subtract(1);
|
|
45
|
+
|
|
46
|
+
// Emit only at the end in CSS (at runtime or in a build step)
|
|
47
|
+
const style = {
|
|
48
|
+
padding: paddingBase.css(),
|
|
49
|
+
transform: `rotate(${rotation.double().css()})`, // 90deg
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- **Compile-time unit validation.** Prevents mixing incompatible units.
|
|
58
|
+
- **Arithmetic safety.** Operate only within matching units; explicit when
|
|
59
|
+
converting.
|
|
60
|
+
- **Explicit emission.** `.css()` outputs a typed string literal only when
|
|
61
|
+
needed.
|
|
62
|
+
- **Light runtime footprint.** Near-zero cost when emitted at build time.
|
|
63
|
+
- **Framework-agnostic.** Works anywhere TypeScript does.
|
|
64
|
+
|
|
65
|
+
Any numeric, unit-bearing CSS value is supported: `m` accepts any unit string you’d use in CSS
|
|
66
|
+
(`'px'`, `'rem'`, `'%'`, `'vw'`, `'deg'`, `'ms'`, …), and you can model new
|
|
67
|
+
measurements without waiting for a dedicated helper. For convenience and better
|
|
68
|
+
types, every standard CSS unit also has a named helper (for example
|
|
69
|
+
`mPx`, `mPercent`, `mVw`, `mEm`, `mMs`, `mFr`), which are equivalent to calling
|
|
70
|
+
`m(value, 'unit')` directly.
|
|
71
|
+
|
|
72
|
+
CSS-Calipers focuses exclusively on numeric, unit-bearing CSS values. Keywords
|
|
73
|
+
like `auto`, `fit-content`, or `max-content`, full shorthand strings,
|
|
74
|
+
`var(--token)`, or `calc(...)` expressions should remain explicit strings or
|
|
75
|
+
dedicated keyword types in your app or styling layer. Everything else stays as
|
|
76
|
+
plain CSS (see "Philosophy & Boundaries" below for more detail).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Should I use this?
|
|
81
|
+
|
|
82
|
+
CSS-Calipers is a good fit if:
|
|
83
|
+
|
|
84
|
+
- You already use TypeScript (or plan to) and want compile-time guarantees around CSS units.
|
|
85
|
+
- You have a design system or token layer where layout math and unit conversions matter.
|
|
86
|
+
- You care about catching unit mismatches and layout invariants early, in dev or tests.
|
|
87
|
+
|
|
88
|
+
It’s probably overkill if:
|
|
89
|
+
|
|
90
|
+
- Your project has minimal custom layout math or relies mostly on utility classes/framework CSS.
|
|
91
|
+
- You don’t use TypeScript and aren’t looking for stronger typing around CSS values.
|
|
92
|
+
- You’re comfortable relying on manual discipline instead of typed measurements for units.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Layout tokens example
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { m, mPercent, mVw, mVh, mFr, assertCondition } from "css-calipers";
|
|
100
|
+
|
|
101
|
+
// Token-style measurements (px by default)
|
|
102
|
+
const spacing = m(8); // Defaults to px; equivalent to mPx(8)
|
|
103
|
+
const cardPadding = spacing.multiply(2); // 16px
|
|
104
|
+
const gutter = spacing.multiply(1.5); // 12px
|
|
105
|
+
|
|
106
|
+
// Responsive bounds expressed in other units
|
|
107
|
+
const minWidthPercent = mPercent(75); // 75%; same as m(75, "%")
|
|
108
|
+
const maxWidthViewport = mVw(100); // 100vw; same as m(100, "vw")
|
|
109
|
+
|
|
110
|
+
// Derived content width in px
|
|
111
|
+
const contentBase = m(320);
|
|
112
|
+
const minCardWidth = m(260);
|
|
113
|
+
const maxCardWidth = m(360);
|
|
114
|
+
|
|
115
|
+
// In real code, these bounds typically come from tokens or configuration,
|
|
116
|
+
// so keeping the clamp in measurement space ensures units stay consistent.
|
|
117
|
+
const cardWidth = contentBase.clamp(minCardWidth, maxCardWidth);
|
|
118
|
+
|
|
119
|
+
// Unitless ratio you can reuse elsewhere
|
|
120
|
+
const ratio = contentBase.getValue() / spacing.getValue(); // returns a number, no unit
|
|
121
|
+
|
|
122
|
+
// Apply ratio to a different unit family
|
|
123
|
+
const heroHeight = mVh(40).multiply(ratio);
|
|
124
|
+
|
|
125
|
+
// Invalid arithmetic (different units) fails at compile time
|
|
126
|
+
const exampleError = cardWidth.add(heroHeight); // ❌ Type error (px vs vh) see error handling below
|
|
127
|
+
|
|
128
|
+
// Use plain numbers where they are just counts
|
|
129
|
+
const columns = 3;
|
|
130
|
+
|
|
131
|
+
// In development, enforce simple invariants so layout mistakes fail fast.
|
|
132
|
+
// In production, you can either rely on earlier validation or add a separate
|
|
133
|
+
// fallback path if this invariant is ever broken.
|
|
134
|
+
if (process.env.NODE_ENV !== "production") {
|
|
135
|
+
assertCondition(
|
|
136
|
+
() => columns > 0,
|
|
137
|
+
"cardGridStyles: columns must be greater than zero"
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Compose a simple grid layout
|
|
142
|
+
const cardGridStyles = {
|
|
143
|
+
display: "grid",
|
|
144
|
+
gap: gutter.css(),
|
|
145
|
+
gridTemplateColumns: `repeat(${columns}, ${mFr(1).css()})`,
|
|
146
|
+
// width driven by card width + gutters
|
|
147
|
+
width: cardWidth
|
|
148
|
+
.multiply(columns)
|
|
149
|
+
.add(gutter.multiply(columns - 1))
|
|
150
|
+
.css(),
|
|
151
|
+
// container bounds in percent/viewport units
|
|
152
|
+
minWidth: minWidthPercent.css(),
|
|
153
|
+
maxWidth: maxWidthViewport.css(),
|
|
154
|
+
// derived hero height based on px ratio, expressed in vh and used inside a calc() string
|
|
155
|
+
// calc() stays plain CSS; css-calipers only provides the numeric pieces
|
|
156
|
+
minHeight: `calc(${heroHeight.css()} + 10vh)`,
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Do custom checks your way
|
|
163
|
+
|
|
164
|
+
CSS-Calipers will happily enforce units anywhere you choose, but it stays
|
|
165
|
+
unopinionated about where those guards live. Drop assertions in a component, in
|
|
166
|
+
a theme overwrite, hardcode a debug routine, or wire a global invariant; the
|
|
167
|
+
structure is up to you:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { assertMatchingUnits } from "css-calipers";
|
|
171
|
+
import { formTokens } from "@/tokens/forms.tokens";
|
|
172
|
+
|
|
173
|
+
if (process.env.NODE_ENV !== "production") {
|
|
174
|
+
assertMatchingUnits(
|
|
175
|
+
formTokens.field.paddingBlock,
|
|
176
|
+
formTokens.field.paddingInline,
|
|
177
|
+
"Form control padding mismatch"
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
You can apply the same checks globally (e.g., during app bootstrap) or only
|
|
183
|
+
inside the components that need them. CSS-Calipers gives you the tools;
|
|
184
|
+
placement is a design decision.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Error behavior
|
|
189
|
+
|
|
190
|
+
- Operations are fail-fast: when you call helpers like `add`, `divide`, `clamp`,
|
|
191
|
+
`measurementMin` / `measurementMax`, or the assertion helpers with invalid
|
|
192
|
+
input (for example, mismatched units or non-finite values), css-calipers
|
|
193
|
+
throws a normal `Error`.
|
|
194
|
+
- Error messages include the operation name (for example,
|
|
195
|
+
`css-calipers.Measurement.divide` or `css-calipers.assertMatchingUnits`), the
|
|
196
|
+
relevant values/units, and any context string you pass in.
|
|
197
|
+
- The library does not catch these errors for you. You choose where to place
|
|
198
|
+
assertions and where (if anywhere) to catch and handle exceptions.
|
|
199
|
+
- In production, a common pattern is to wrap assertions in dev-only guards
|
|
200
|
+
(such as `if (process.env.NODE_ENV !== 'production')`) or to enforce
|
|
201
|
+
invariants in tests, so checks stay cheap and predictable at runtime.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Co-existing with other systems
|
|
206
|
+
|
|
207
|
+
You don’t have to convert everything at once, or at all. If it fits your setup, you can write small adapters that accept existing CSS strings, css-calipers measurements, or plain numbers and turn them into CSS values. The example below is just one possible adapter pattern, not a recommendation or default.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { m, isMeasurement } from "css-calipers";
|
|
211
|
+
|
|
212
|
+
type SpacingInput = string | number | ReturnType<typeof m>;
|
|
213
|
+
|
|
214
|
+
const toSpacingCss = (value: SpacingInput): string => {
|
|
215
|
+
if (typeof value === "string") {
|
|
216
|
+
// Already a CSS value (for example, "auto" or "var(--gap)")
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const measurement = isMeasurement(value) ? value : m(value);
|
|
221
|
+
return measurement.css();
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Later, callers can pass tokens, raw CSS strings, or measurements:
|
|
225
|
+
toSpacingCss("var(--card-gap)");
|
|
226
|
+
toSpacingCss(8); // becomes "8px"
|
|
227
|
+
toSpacingCss(m(12, "px"));
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Advanced
|
|
233
|
+
|
|
234
|
+
### String Literal Type Exclusion
|
|
235
|
+
|
|
236
|
+
When helpers must _exclude_ CSS-Calipers–emitted numeric, unit-bearing CSS values from a keyword union,
|
|
237
|
+
use the exported `MeasurementString` type together with your existing CSS
|
|
238
|
+
property typings (for example, the `Property` types from the `csstype`
|
|
239
|
+
package):
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import type { MeasurementString } from "css-calipers";
|
|
243
|
+
import type { Property } from "csstype";
|
|
244
|
+
|
|
245
|
+
type SpacingKeyword = Exclude<
|
|
246
|
+
Extract<Property.Margin, string>,
|
|
247
|
+
MeasurementString
|
|
248
|
+
>;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
This lets helpers stay strict: `IMeasurement` for numeric, unit-bearing CSS values; targeted string
|
|
252
|
+
keywords for symbolic CSS values, without reintroducing vague unions like
|
|
253
|
+
`MeasurementLike`.
|
|
254
|
+
|
|
255
|
+
### Integration Patterns
|
|
256
|
+
|
|
257
|
+
- **Typed helpers:** Accept either `IMeasurement` or a constrained keyword type,
|
|
258
|
+
never a generic string.
|
|
259
|
+
- **Pre-emission transforms:** Compose all math in CSS-Calipers, emit once at
|
|
260
|
+
the style boundary.
|
|
261
|
+
- **Build-time pipelines:** Run measurement math in Node or a build step
|
|
262
|
+
(scripts, codegen, or bundler plugins) and emit plain CSS or tokens for your
|
|
263
|
+
existing styling system so runtime only sees static values.
|
|
264
|
+
- **Unit guards in debug:** Use `assertUnit()` in dev-only blocks to confirm
|
|
265
|
+
consistency between related measurements.
|
|
266
|
+
- **CSS variables:** Pass CSS-Calipers `.css()` output into style layers that
|
|
267
|
+
interpolate them, but don’t try to store `var(--token)` inside the library.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Philosophy & Boundaries
|
|
272
|
+
|
|
273
|
+
- **Measurement math lives here; string composition lives elsewhere.**
|
|
274
|
+
Use CSS-Calipers for unit-aware calculations, then hand results to
|
|
275
|
+
helpers/adapters that emit CSS literals. Keep `calc()`/`clamp()` logic outside
|
|
276
|
+
the library so measurement objects remain pure.
|
|
277
|
+
|
|
278
|
+
- **`.css()` at runtime is an edge, not a habit.**
|
|
279
|
+
You can call `.css()` at runtime, but prefer emitting once to avoid hot-path
|
|
280
|
+
string churn.
|
|
281
|
+
|
|
282
|
+
- **Numbers are operands, not CSS-Calipers values.**
|
|
283
|
+
You cannot create a CSS-Calipers value without a unit. Pass plain numbers as
|
|
284
|
+
operands (`add`, `subtract`, `multiply`, `divide`) or combine with another
|
|
285
|
+
`IMeasurement`, but never store bare numbers inside library state. If a value
|
|
286
|
+
lacks both number and unit, CSS-Calipers won’t track it; you own whatever
|
|
287
|
+
logic wraps it.
|
|
288
|
+
|
|
289
|
+
- **Model keywords explicitly (not “escape hatches”).**
|
|
290
|
+
If a helper needs symbolic CSS (e.g., `'auto'`, `'fit-content'`), define a
|
|
291
|
+
precise keyword type and purposely exclude the emitted string type from
|
|
292
|
+
CSS-Calipers so numeric, unit-bearing CSS values remain the default path.
|
|
293
|
+
|
|
294
|
+
- **CSS custom properties coexist; they don’t mix.**
|
|
295
|
+
Third-party primitives exposing `var(--token)` should keep those values as raw
|
|
296
|
+
CSS strings. Feed CSS-Calipers `.css()` output into them where possible, but
|
|
297
|
+
don’t wrap CSS variables inside the library; treat them as parallel pipes
|
|
298
|
+
that meet in the style layer.
|
package/RELEASING.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Releasing css-calipers
|
|
2
|
+
|
|
3
|
+
This document describes how to publish a new version of `css-calipers` to npm
|
|
4
|
+
using the local release script.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
- You have push access to the `main` branch of the repository.
|
|
9
|
+
- You are logged in to npm with an account that can publish the
|
|
10
|
+
`css-calipers` package (`npm whoami` should work).
|
|
11
|
+
- If your npm account uses 2FA, be ready to enter your OTP during
|
|
12
|
+
`npm publish`.
|
|
13
|
+
- The supported runtime target for published builds is Node 18 or newer.
|
|
14
|
+
|
|
15
|
+
## Release flow (local, scripted)
|
|
16
|
+
|
|
17
|
+
Releases should be performed via the `release` script rather than calling
|
|
18
|
+
`npm publish` directly.
|
|
19
|
+
|
|
20
|
+
1. Ensure your local `main` branch is up to date.
|
|
21
|
+
2. From the project root, run `npm run release`.
|
|
22
|
+
|
|
23
|
+
3. The script will:
|
|
24
|
+
- Verify that the current branch is `main` (abort otherwise).
|
|
25
|
+
- Warn if there are uncommitted changes and ask whether to continue.
|
|
26
|
+
- Run the core tests (`npm run test:core`).
|
|
27
|
+
- Run the build (`npm run build`) to produce `dist/cjs` and `dist/esm`.
|
|
28
|
+
- Check that the expected build outputs exist.
|
|
29
|
+
- Run the dist tests (`npm run test:dist`) against both CJS and ESM outputs.
|
|
30
|
+
- Run the type tests (`npm run test:types`).
|
|
31
|
+
- Prompt you to choose the version bump (`patch`, `minor`, or `major`).
|
|
32
|
+
- Run `npm version <type>` to bump the version and create a tag.
|
|
33
|
+
- Show the new version and ask for final confirmation before publishing.
|
|
34
|
+
- On confirmation, run `npm publish` (with a special environment variable
|
|
35
|
+
set), which also triggers the `prepublishOnly` hook and publishes under
|
|
36
|
+
the default `latest` dist-tag.
|
|
37
|
+
|
|
38
|
+
If any step fails (tests, build, or publish), the script will exit with a
|
|
39
|
+
non-zero status and print an error message.
|
|
40
|
+
|
|
41
|
+
## prepublishOnly safety net
|
|
42
|
+
|
|
43
|
+
The `prepublishOnly` npm script is configured to refuse direct `npm publish`
|
|
44
|
+
unless the `CSS_CALIPERS_RELEASE` environment variable is set. It does not
|
|
45
|
+
re-run tests or builds; those are handled by the release script.
|
|
46
|
+
|
|
47
|
+
The `prepublishOnly` hook runs automatically on every `npm publish` and will
|
|
48
|
+
block publishing if the environment guard fails.
|
|
49
|
+
|
|
50
|
+
## Dist-tag strategy
|
|
51
|
+
|
|
52
|
+
- All releases are currently published with npm’s default `latest` dist-tag.
|
|
53
|
+
- There is no separate `next`/`beta` channel yet; if that changes, both this
|
|
54
|
+
document and the release script should be updated to reflect the new
|
|
55
|
+
tagging behavior.
|
|
56
|
+
|
|
57
|
+
## Notes and future improvements
|
|
58
|
+
|
|
59
|
+
- Direct `npm publish` is discouraged; prefer `npm run release` so all checks
|
|
60
|
+
run consistently.
|
|
61
|
+
- In the future, publishing can be moved to CI (for example, from tags on
|
|
62
|
+
`main`) without changing the core expectations described here.
|
package/dist/cjs/core.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _Measurement_instances, _a, _Measurement_value, _Measurement_unit, _Measurement_clone;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.assertCondition = exports.assertUnit = exports.measurementMax = exports.measurementMin = exports.hasCssMethod = exports.makeUnitAssert = exports.makeUnitGuard = exports.measurementUnitMetadata = exports.makeUnitHelperFromDefinition = exports.makeUnitHelper = exports.m = exports.isMeasurement = void 0;
|
|
16
|
+
exports.assertMatchingUnits = assertMatchingUnits;
|
|
17
|
+
const unitDefinitions_1 = require("./unitDefinitions");
|
|
18
|
+
const errors_1 = require("./internal/errors");
|
|
19
|
+
function assertMatchingUnits(left, right, context) {
|
|
20
|
+
const leftUnit = left.getUnit();
|
|
21
|
+
const rightUnit = right.getUnit();
|
|
22
|
+
if (leftUnit !== rightUnit) {
|
|
23
|
+
(0, errors_1.throwHelperError)({
|
|
24
|
+
operation: 'css-calipers.assertMatchingUnits',
|
|
25
|
+
params: [left, right],
|
|
26
|
+
message: `measurement unit mismatch: ${leftUnit} vs ${rightUnit}`,
|
|
27
|
+
context,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const deltaToNumber = (base, delta) => {
|
|
32
|
+
if (typeof delta === 'number')
|
|
33
|
+
return delta;
|
|
34
|
+
assertMatchingUnits(base, delta, 'deltaToNumber');
|
|
35
|
+
return delta.getValue();
|
|
36
|
+
};
|
|
37
|
+
class Measurement {
|
|
38
|
+
constructor(value, unit) {
|
|
39
|
+
_Measurement_instances.add(this);
|
|
40
|
+
_Measurement_value.set(this, void 0);
|
|
41
|
+
_Measurement_unit.set(this, void 0);
|
|
42
|
+
if (!Number.isFinite(value)) {
|
|
43
|
+
(0, errors_1.throwHelperError)({
|
|
44
|
+
operation: 'css-calipers.Measurement.constructor',
|
|
45
|
+
params: [],
|
|
46
|
+
message: `Non-finite measurement value: ${value}`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const normalizedUnit = unit.toLowerCase();
|
|
50
|
+
__classPrivateFieldSet(this, _Measurement_value, value, "f");
|
|
51
|
+
__classPrivateFieldSet(this, _Measurement_unit, normalizedUnit, "f");
|
|
52
|
+
Object.defineProperty(this, '__unitBrand', {
|
|
53
|
+
value: normalizedUnit,
|
|
54
|
+
enumerable: false,
|
|
55
|
+
configurable: false,
|
|
56
|
+
writable: false,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
css() {
|
|
60
|
+
return `${__classPrivateFieldGet(this, _Measurement_value, "f")}${__classPrivateFieldGet(this, _Measurement_unit, "f")}`;
|
|
61
|
+
}
|
|
62
|
+
toString() {
|
|
63
|
+
return this.css();
|
|
64
|
+
}
|
|
65
|
+
getUnit() {
|
|
66
|
+
return __classPrivateFieldGet(this, _Measurement_unit, "f");
|
|
67
|
+
}
|
|
68
|
+
getValue() {
|
|
69
|
+
return __classPrivateFieldGet(this, _Measurement_value, "f");
|
|
70
|
+
}
|
|
71
|
+
valueOf() {
|
|
72
|
+
return __classPrivateFieldGet(this, _Measurement_value, "f");
|
|
73
|
+
}
|
|
74
|
+
[(_Measurement_value = new WeakMap(), _Measurement_unit = new WeakMap(), _Measurement_instances = new WeakSet(), Symbol.toPrimitive)](hint) {
|
|
75
|
+
if (hint === 'number')
|
|
76
|
+
return __classPrivateFieldGet(this, _Measurement_value, "f");
|
|
77
|
+
return this.css();
|
|
78
|
+
}
|
|
79
|
+
isUnit(expected) {
|
|
80
|
+
return __classPrivateFieldGet(this, _Measurement_unit, "f") === expected.toLowerCase();
|
|
81
|
+
}
|
|
82
|
+
assertUnit(expected, context) {
|
|
83
|
+
if (!this.isUnit(expected)) {
|
|
84
|
+
(0, errors_1.throwMeasurementMethodError)({
|
|
85
|
+
operation: 'css-calipers.Measurement.assertUnit',
|
|
86
|
+
caller: this,
|
|
87
|
+
params: [],
|
|
88
|
+
message: `Expected unit "${expected}", received "${__classPrivateFieldGet(this, _Measurement_unit, "f")}".`,
|
|
89
|
+
context,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
assert(predicate, message) {
|
|
94
|
+
if (!predicate(this)) {
|
|
95
|
+
(0, errors_1.throwMeasurementMethodError)({
|
|
96
|
+
operation: 'css-calipers.Measurement.assert',
|
|
97
|
+
caller: this,
|
|
98
|
+
params: [],
|
|
99
|
+
message,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
equals(other, strict = true) {
|
|
104
|
+
const otherUnit = other.getUnit();
|
|
105
|
+
if (__classPrivateFieldGet(this, _Measurement_unit, "f") !== otherUnit) {
|
|
106
|
+
if (strict) {
|
|
107
|
+
assertMatchingUnits(this, other, 'equals(strict)');
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
return __classPrivateFieldGet(this, _Measurement_value, "f") === other.getValue();
|
|
112
|
+
}
|
|
113
|
+
compare(other, strict = true) {
|
|
114
|
+
if (strict) {
|
|
115
|
+
assertMatchingUnits(this, other, 'compare(strict)');
|
|
116
|
+
}
|
|
117
|
+
else if (__classPrivateFieldGet(this, _Measurement_unit, "f") !== other.getUnit()) {
|
|
118
|
+
return __classPrivateFieldGet(this, _Measurement_unit, "f") < other.getUnit() ? -1 : 1;
|
|
119
|
+
}
|
|
120
|
+
const diff = __classPrivateFieldGet(this, _Measurement_value, "f") - other.getValue();
|
|
121
|
+
if (diff === 0)
|
|
122
|
+
return 0;
|
|
123
|
+
return diff < 0 ? -1 : 1;
|
|
124
|
+
}
|
|
125
|
+
add(delta) {
|
|
126
|
+
const next = __classPrivateFieldGet(this, _Measurement_value, "f") + deltaToNumber(this, delta);
|
|
127
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, next);
|
|
128
|
+
}
|
|
129
|
+
subtract(delta) {
|
|
130
|
+
const next = __classPrivateFieldGet(this, _Measurement_value, "f") - deltaToNumber(this, delta);
|
|
131
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, next);
|
|
132
|
+
}
|
|
133
|
+
multiply(factor) {
|
|
134
|
+
if (factor === 1)
|
|
135
|
+
return this;
|
|
136
|
+
if (factor === 0)
|
|
137
|
+
return new _a(0, __classPrivateFieldGet(this, _Measurement_unit, "f"));
|
|
138
|
+
if (factor === -1)
|
|
139
|
+
return new _a(-__classPrivateFieldGet(this, _Measurement_value, "f"), __classPrivateFieldGet(this, _Measurement_unit, "f"));
|
|
140
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, __classPrivateFieldGet(this, _Measurement_value, "f") * factor);
|
|
141
|
+
}
|
|
142
|
+
divide(divisor) {
|
|
143
|
+
if (divisor === 1)
|
|
144
|
+
return this;
|
|
145
|
+
if (divisor === 0) {
|
|
146
|
+
(0, errors_1.throwMeasurementMethodError)({
|
|
147
|
+
operation: 'css-calipers.Measurement.divide',
|
|
148
|
+
caller: this,
|
|
149
|
+
params: [],
|
|
150
|
+
message: `Cannot divide ${this.css()} by zero`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const result = __classPrivateFieldGet(this, _Measurement_value, "f") / divisor;
|
|
154
|
+
if (!Number.isFinite(result)) {
|
|
155
|
+
(0, errors_1.throwMeasurementMethodError)({
|
|
156
|
+
operation: 'css-calipers.Measurement.divide',
|
|
157
|
+
caller: this,
|
|
158
|
+
params: [],
|
|
159
|
+
message: 'Non-finite result',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, result);
|
|
163
|
+
}
|
|
164
|
+
double() {
|
|
165
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, __classPrivateFieldGet(this, _Measurement_value, "f") * 2);
|
|
166
|
+
}
|
|
167
|
+
half() {
|
|
168
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, __classPrivateFieldGet(this, _Measurement_value, "f") / 2);
|
|
169
|
+
}
|
|
170
|
+
negation(shouldNegate = true) {
|
|
171
|
+
return shouldNegate ? __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, -__classPrivateFieldGet(this, _Measurement_value, "f")) : this;
|
|
172
|
+
}
|
|
173
|
+
absolute() {
|
|
174
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, Math.abs(__classPrivateFieldGet(this, _Measurement_value, "f")));
|
|
175
|
+
}
|
|
176
|
+
round(precision = 0) {
|
|
177
|
+
const next = precision === 0
|
|
178
|
+
? Math.round(__classPrivateFieldGet(this, _Measurement_value, "f"))
|
|
179
|
+
: Number(__classPrivateFieldGet(this, _Measurement_value, "f").toFixed(precision));
|
|
180
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, next);
|
|
181
|
+
}
|
|
182
|
+
floor() {
|
|
183
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, Math.floor(__classPrivateFieldGet(this, _Measurement_value, "f")));
|
|
184
|
+
}
|
|
185
|
+
ceil() {
|
|
186
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, Math.ceil(__classPrivateFieldGet(this, _Measurement_value, "f")));
|
|
187
|
+
}
|
|
188
|
+
clamp(min, max) {
|
|
189
|
+
assertMatchingUnits(this, min, 'clamp(min)');
|
|
190
|
+
assertMatchingUnits(this, max, 'clamp(max)');
|
|
191
|
+
const minValue = min.getValue();
|
|
192
|
+
const maxValue = max.getValue();
|
|
193
|
+
if (!Number.isFinite(minValue) || !Number.isFinite(maxValue)) {
|
|
194
|
+
(0, errors_1.throwMeasurementMethodError)({
|
|
195
|
+
operation: 'css-calipers.Measurement.clamp',
|
|
196
|
+
caller: this,
|
|
197
|
+
params: [min, max],
|
|
198
|
+
message: 'clamp: expected finite bounds',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (minValue > maxValue) {
|
|
202
|
+
(0, errors_1.throwMeasurementMethodError)({
|
|
203
|
+
operation: 'css-calipers.Measurement.clamp',
|
|
204
|
+
caller: this,
|
|
205
|
+
params: [min, max],
|
|
206
|
+
message: `clamp: min (${min.css()}) must be <= max (${max.css()})`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
const clamped = Math.min(maxValue, Math.max(minValue, __classPrivateFieldGet(this, _Measurement_value, "f")));
|
|
210
|
+
return __classPrivateFieldGet(this, _Measurement_instances, "m", _Measurement_clone).call(this, clamped);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
_a = Measurement, _Measurement_clone = function _Measurement_clone(value) {
|
|
214
|
+
return new _a(value, __classPrivateFieldGet(this, _Measurement_unit, "f"));
|
|
215
|
+
};
|
|
216
|
+
const createMeasurement = (value, unit) => new Measurement(value, unit);
|
|
217
|
+
const isMeasurement = (x) => x instanceof Measurement;
|
|
218
|
+
exports.isMeasurement = isMeasurement;
|
|
219
|
+
const m = (value, unit = 'px') => createMeasurement(value, unit.toLowerCase());
|
|
220
|
+
exports.m = m;
|
|
221
|
+
const makeUnitHelper = (unit) => {
|
|
222
|
+
const normalizedUnit = unit.toLowerCase();
|
|
223
|
+
const factory = (value) => createMeasurement(value, normalizedUnit);
|
|
224
|
+
return Object.assign(factory, {
|
|
225
|
+
unit: normalizedUnit,
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
exports.makeUnitHelper = makeUnitHelper;
|
|
229
|
+
const makeUnitHelperFromDefinition = (name) => (0, exports.makeUnitHelper)(unitDefinitions_1.UNIT_DEFINITIONS[name].unit);
|
|
230
|
+
exports.makeUnitHelperFromDefinition = makeUnitHelperFromDefinition;
|
|
231
|
+
exports.measurementUnitMetadata = unitDefinitions_1.UNIT_DEFINITIONS;
|
|
232
|
+
const makeUnitGuard = (helper) => {
|
|
233
|
+
return (value) => (0, exports.isMeasurement)(value) && value.isUnit(helper.unit);
|
|
234
|
+
};
|
|
235
|
+
exports.makeUnitGuard = makeUnitGuard;
|
|
236
|
+
const makeUnitAssert = (helper) => {
|
|
237
|
+
const guard = (0, exports.makeUnitGuard)(helper);
|
|
238
|
+
return (value, context) => {
|
|
239
|
+
if (!guard(value)) {
|
|
240
|
+
(0, errors_1.throwHelperError)({
|
|
241
|
+
operation: 'css-calipers.makeUnitAssert',
|
|
242
|
+
params: (0, exports.isMeasurement)(value) ? [value] : [],
|
|
243
|
+
message: `Expected unit "${helper.unit}".`,
|
|
244
|
+
context,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
exports.makeUnitAssert = makeUnitAssert;
|
|
250
|
+
const hasCssMethod = (x) => {
|
|
251
|
+
return (typeof x === 'object' &&
|
|
252
|
+
x !== null &&
|
|
253
|
+
'css' in x &&
|
|
254
|
+
typeof x.css === 'function');
|
|
255
|
+
};
|
|
256
|
+
exports.hasCssMethod = hasCssMethod;
|
|
257
|
+
const measurementMin = (a, b) => {
|
|
258
|
+
assertMatchingUnits(a, b, 'measurementMin');
|
|
259
|
+
return a.getValue() <= b.getValue() ? a : b;
|
|
260
|
+
};
|
|
261
|
+
exports.measurementMin = measurementMin;
|
|
262
|
+
const measurementMax = (a, b) => {
|
|
263
|
+
assertMatchingUnits(a, b, 'measurementMax');
|
|
264
|
+
return a.getValue() >= b.getValue() ? a : b;
|
|
265
|
+
};
|
|
266
|
+
exports.measurementMax = measurementMax;
|
|
267
|
+
const assertUnit = (measurement, expectedUnit, context) => measurement.assertUnit(expectedUnit, context);
|
|
268
|
+
exports.assertUnit = assertUnit;
|
|
269
|
+
const assertCondition = (condition, message) => {
|
|
270
|
+
const passed = typeof condition === 'function' ? condition() : condition;
|
|
271
|
+
if (!passed) {
|
|
272
|
+
(0, errors_1.throwHelperError)({
|
|
273
|
+
operation: 'css-calipers.assertCondition',
|
|
274
|
+
params: [],
|
|
275
|
+
message,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
exports.assertCondition = assertCondition;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./core"), exports);
|
|
18
|
+
__exportStar(require("./units/percent"), exports);
|
|
19
|
+
__exportStar(require("./units/absolute"), exports);
|
|
20
|
+
__exportStar(require("./units/font-relative"), exports);
|
|
21
|
+
__exportStar(require("./units/viewport"), exports);
|
|
22
|
+
__exportStar(require("./units/viewport-small"), exports);
|
|
23
|
+
__exportStar(require("./units/viewport-large"), exports);
|
|
24
|
+
__exportStar(require("./units/viewport-dynamic"), exports);
|
|
25
|
+
__exportStar(require("./units/container"), exports);
|
|
26
|
+
__exportStar(require("./units/angle"), exports);
|
|
27
|
+
__exportStar(require("./units/time"), exports);
|
|
28
|
+
__exportStar(require("./units/frequency"), exports);
|
|
29
|
+
__exportStar(require("./units/resolution"), exports);
|
|
30
|
+
__exportStar(require("./units/grid"), exports);
|