|
| 1 | +# @mikespa/kit |
| 2 | + |
| 3 | +A small, framework-agnostic personal toolkit. Built to stop copy-pasting the |
| 4 | +same `formatCHF` / `slugify` / `clamp` functions between Next.js projects. |
| 5 | + |
| 6 | +No runtime dependencies. Tree-shakeable via subpath exports. |
| 7 | + |
| 8 | +## Install |
| 9 | + |
| 10 | +```bash |
| 11 | +npm install @mikespa/kit |
| 12 | +``` |
| 13 | + |
| 14 | +## Usage |
| 15 | + |
| 16 | +Import only what you need via subpaths — this keeps bundles small and is |
| 17 | +self-documenting at the call site. |
| 18 | + |
| 19 | +```ts |
| 20 | +import { formatDate, formatCHF, formatSecs, formatHours } from '@mikespa/kit/format'; |
| 21 | +import { slugify, truncate } from '@mikespa/kit/string'; |
| 22 | +import { clamp, calcTimeHours } from '@mikespa/kit/math'; |
| 23 | + |
| 24 | +formatDate('2026-06-19'); // "19.06.2026" |
| 25 | +formatCHF(1234.5); // "1'234.50 CHF" |
| 26 | +formatSecs(9000); // "2h30" |
| 27 | +formatHours(2.5); // "2h30" |
| 28 | +slugify('Hello, World!'); // "hello-world" |
| 29 | +clamp(15, 0, 10); // 10 |
| 30 | +calcTimeHours('08:00', '10:30'); // 2.5 |
| 31 | +``` |
| 32 | + |
| 33 | +Or import everything from the root if you don't care about tree-shaking: |
| 34 | + |
| 35 | +```ts |
| 36 | +import { formatCHF, slugify, clamp } from '@mikespa/kit'; |
| 37 | +``` |
| 38 | + |
| 39 | +## Modules |
| 40 | + |
| 41 | +### `./format` |
| 42 | + |
| 43 | +| Function | Description | Default output | |
| 44 | +| --- | --- | --- | |
| 45 | +| `formatDate(input)` | Date as DD.MM.YYYY | "19.06.2026" | |
| 46 | +| `formatTime(input)` | Time (24h) | "15:30" | |
| 47 | +| `formatDateTime(input)` | Date + time | "19 juin 2026, 15:30" | |
| 48 | +| `formatRelative(input)` | Relative to now | "il y a 3 heures" | |
| 49 | +| `formatNumber(value)` | Locale-aware thousands | "1 234 567" | |
| 50 | +| `formatCHF(amount)` | Swiss CHF, apostrophe sep. | "1'234.50 CHF" | |
| 51 | +| `formatCurrency(value)` | Intl currency (CHF default) | "1 234.50 CHF" | |
| 52 | +| `formatCompact(value)` | Compact notation | "1,2 k" | |
| 53 | +| `formatPercent(value)` | Percentage | "45,6%" | |
| 54 | +| `formatBytes(bytes)` | Human-readable file size | "1.5 KB" | |
| 55 | +| `formatSecs(seconds)` | Duration from seconds | "2h30" / "45min" | |
| 56 | +| `formatHours(hours)` | Duration from decimal hours | "2h30" / "45min" | |
| 57 | + |
| 58 | +All `format*` functions that wrap `Intl` accept an optional `locale` parameter |
| 59 | +(defaults to `'fr-CH'`) so consuming projects can override per-call. |
| 60 | + |
| 61 | +### `./string` |
| 62 | + |
| 63 | +| Function | Description | |
| 64 | +| --- | --- | |
| 65 | +| `slugify(input)` | URL-friendly slug | |
| 66 | +| `truncate(input, maxLength)` | Truncate with suffix | |
| 67 | +| `capitalize(input)` | First letter uppercase | |
| 68 | +| `pluralize(count, singular)` | Simple pluralizer | |
| 69 | + |
| 70 | +### `./math` |
| 71 | + |
| 72 | +| Function | Description | |
| 73 | +| --- | --- | |
| 74 | +| `clamp(value, min, max)` | Clamp to range | |
| 75 | +| `lerp(start, end, t)` | Linear interpolate | |
| 76 | +| `round(value, decimals)` | Round to N decimals | |
| 77 | +| `calcTimeHours(start, end)` | Hours between "HH:MM" strings | |
| 78 | + |
| 79 | +## Development |
| 80 | + |
| 81 | +```bash |
| 82 | +npm install |
| 83 | +npm run dev # build in watch mode |
| 84 | +npm run build # build once (output -> dist/) |
| 85 | +npm test # run tests |
| 86 | +npm run test:watch |
| 87 | +npm run typecheck |
| 88 | +``` |
| 89 | + |
| 90 | +## Publishing |
| 91 | + |
| 92 | +```bash |
| 93 | +npm version patch # or minor / major |
| 94 | +git push --follow-tags |
| 95 | +``` |
| 96 | + |
| 97 | +The `publish` workflow runs automatically on any `v*.*.*` tag push. |
| 98 | +`prepublishOnly` runs the build, so `dist/` is always fresh. |
| 99 | + |
| 100 | +You need an `NPM_TOKEN` secret set in the GitHub repo settings. |
| 101 | + |
| 102 | +## License |
| 103 | + |
| 104 | +MIT |
0 commit comments