@nibapp/ui 0.0.0-stage → 0.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/CHANGELOG.md +35 -0
- package/LICENSE +21 -0
- package/README.md +124 -2
- package/TRADEMARKS.md +22 -0
- package/dist/_bundled/money/index.d.ts +98 -0
- package/dist/_bundled/money/index.js +152 -0
- package/dist/_bundled/tokens/index.d.ts +2 -0
- package/dist/_bundled/tokens/index.js +2 -0
- package/dist/_bundled/tokens/palette.gen.d.ts +51 -0
- package/dist/_bundled/tokens/palette.gen.js +206 -0
- package/dist/_bundled/tokens/spec.d.ts +291 -0
- package/dist/_bundled/tokens/spec.js +221 -0
- package/dist/components/accordion.d.ts +27 -0
- package/dist/components/accordion.js +66 -0
- package/dist/components/alert-dialog.d.ts +59 -0
- package/dist/components/alert-dialog.js +82 -0
- package/dist/components/alert.d.ts +141 -0
- package/dist/components/alert.js +55 -0
- package/dist/components/appear.d.ts +21 -0
- package/dist/components/appear.js +42 -0
- package/dist/components/avatar.d.ts +196 -0
- package/dist/components/avatar.js +135 -0
- package/dist/components/badge.d.ts +183 -0
- package/dist/components/badge.js +68 -0
- package/dist/components/block-stack.d.ts +33 -0
- package/dist/components/block-stack.js +197 -0
- package/dist/components/button.d.ts +327 -0
- package/dist/components/button.js +101 -0
- package/dist/components/card.d.ts +131 -0
- package/dist/components/card.js +100 -0
- package/dist/components/checkbox.d.ts +34 -0
- package/dist/components/checkbox.js +37 -0
- package/dist/components/choice-row.d.ts +104 -0
- package/dist/components/choice-row.js +56 -0
- package/dist/components/clamped-text.d.ts +17 -0
- package/dist/components/clamped-text.js +47 -0
- package/dist/components/code-block.d.ts +48 -0
- package/dist/components/code-block.js +124 -0
- package/dist/components/code-input.d.ts +49 -0
- package/dist/components/code-input.js +59 -0
- package/dist/components/combobox.d.ts +194 -0
- package/dist/components/combobox.js +176 -0
- package/dist/components/command-palette.d.ts +57 -0
- package/dist/components/command-palette.js +83 -0
- package/dist/components/context-menu.d.ts +24 -0
- package/dist/components/context-menu.js +30 -0
- package/dist/components/copy-button.d.ts +9 -0
- package/dist/components/copy-button.js +20 -0
- package/dist/components/copy-command.d.ts +18 -0
- package/dist/components/copy-command.js +19 -0
- package/dist/components/data-table.d.ts +240 -0
- package/dist/components/data-table.js +63 -0
- package/dist/components/dialog.d.ts +80 -0
- package/dist/components/dialog.js +85 -0
- package/dist/components/diff-lines.d.ts +20 -0
- package/dist/components/diff-lines.js +10 -0
- package/dist/components/diff-view.d.ts +128 -0
- package/dist/components/diff-view.js +675 -0
- package/dist/components/disclosure.d.ts +33 -0
- package/dist/components/disclosure.js +51 -0
- package/dist/components/drawn-check.d.ts +8 -0
- package/dist/components/drawn-check.js +16 -0
- package/dist/components/edge-resizer.d.ts +26 -0
- package/dist/components/edge-resizer.js +53 -0
- package/dist/components/empty-state.d.ts +112 -0
- package/dist/components/empty-state.js +56 -0
- package/dist/components/expand-toggle.d.ts +19 -0
- package/dist/components/expand-toggle.js +10 -0
- package/dist/components/field.d.ts +48 -0
- package/dist/components/field.js +60 -0
- package/dist/components/filter-chip.d.ts +47 -0
- package/dist/components/filter-chip.js +33 -0
- package/dist/components/group.d.ts +10 -0
- package/dist/components/group.js +9 -0
- package/dist/components/icon-text.d.ts +11 -0
- package/dist/components/icon-text.js +10 -0
- package/dist/components/image-sources.d.ts +57 -0
- package/dist/components/image-sources.js +58 -0
- package/dist/components/image-thumbnail.d.ts +34 -0
- package/dist/components/image-thumbnail.js +58 -0
- package/dist/components/image-viewer.d.ts +60 -0
- package/dist/components/image-viewer.js +411 -0
- package/dist/components/inline-edit.d.ts +22 -0
- package/dist/components/inline-edit.js +51 -0
- package/dist/components/input.d.ts +155 -0
- package/dist/components/input.js +109 -0
- package/dist/components/item-label.d.ts +11 -0
- package/dist/components/item-label.js +5 -0
- package/dist/components/item-row.d.ts +23 -0
- package/dist/components/item-row.js +19 -0
- package/dist/components/kbd.d.ts +55 -0
- package/dist/components/kbd.js +86 -0
- package/dist/components/link-policy.d.ts +24 -0
- package/dist/components/link-policy.js +32 -0
- package/dist/components/linked-text.d.ts +15 -0
- package/dist/components/linked-text.js +48 -0
- package/dist/components/loading-region.d.ts +18 -0
- package/dist/components/loading-region.js +17 -0
- package/dist/components/markdown.d.ts +34 -0
- package/dist/components/markdown.js +307 -0
- package/dist/components/menu.d.ts +132 -0
- package/dist/components/menu.js +102 -0
- package/dist/components/message-bubble.d.ts +57 -0
- package/dist/components/message-bubble.js +28 -0
- package/dist/components/meta-line.d.ts +28 -0
- package/dist/components/meta-line.js +36 -0
- package/dist/components/money-input.d.ts +23 -0
- package/dist/components/money-input.js +34 -0
- package/dist/components/native-select.d.ts +120 -0
- package/dist/components/native-select.js +85 -0
- package/dist/components/page-trail.d.ts +30 -0
- package/dist/components/page-trail.js +43 -0
- package/dist/components/page.d.ts +80 -0
- package/dist/components/page.js +60 -0
- package/dist/components/path-text.d.ts +8 -0
- package/dist/components/path-text.js +11 -0
- package/dist/components/percent-input.d.ts +20 -0
- package/dist/components/percent-input.js +32 -0
- package/dist/components/popover.d.ts +52 -0
- package/dist/components/popover.js +36 -0
- package/dist/components/progress.d.ts +389 -0
- package/dist/components/progress.js +142 -0
- package/dist/components/radio.d.ts +65 -0
- package/dist/components/radio.js +54 -0
- package/dist/components/remote-image-gate.d.ts +34 -0
- package/dist/components/remote-image-gate.js +60 -0
- package/dist/components/resizable.d.ts +23 -0
- package/dist/components/resizable.js +30 -0
- package/dist/components/rolling-number.d.ts +13 -0
- package/dist/components/rolling-number.js +69 -0
- package/dist/components/scroll-area.d.ts +17 -0
- package/dist/components/scroll-area.js +25 -0
- package/dist/components/section-label.d.ts +10 -0
- package/dist/components/section-label.js +21 -0
- package/dist/components/section-nav.d.ts +22 -0
- package/dist/components/section-nav.js +80 -0
- package/dist/components/segmented-control.d.ts +125 -0
- package/dist/components/segmented-control.js +101 -0
- package/dist/components/select.d.ts +120 -0
- package/dist/components/select.js +89 -0
- package/dist/components/separator.d.ts +10 -0
- package/dist/components/separator.js +12 -0
- package/dist/components/sheet.d.ts +101 -0
- package/dist/components/sheet.js +129 -0
- package/dist/components/shortcut-recorder.d.ts +31 -0
- package/dist/components/shortcut-recorder.js +67 -0
- package/dist/components/skeleton.d.ts +31 -0
- package/dist/components/skeleton.js +33 -0
- package/dist/components/slider.d.ts +138 -0
- package/dist/components/slider.js +98 -0
- package/dist/components/spinner.d.ts +37 -0
- package/dist/components/spinner.js +16 -0
- package/dist/components/switch.d.ts +59 -0
- package/dist/components/switch.js +56 -0
- package/dist/components/tab-strip.d.ts +29 -0
- package/dist/components/tab-strip.js +42 -0
- package/dist/components/tabs.d.ts +13 -0
- package/dist/components/tabs.js +68 -0
- package/dist/components/text-link.d.ts +54 -0
- package/dist/components/text-link.js +38 -0
- package/dist/components/textarea.d.ts +75 -0
- package/dist/components/textarea.js +90 -0
- package/dist/components/toast.d.ts +74 -0
- package/dist/components/toast.js +128 -0
- package/dist/components/tooltip.d.ts +50 -0
- package/dist/components/tooltip.js +46 -0
- package/dist/components/truncated-text.d.ts +15 -0
- package/dist/components/truncated-text.js +19 -0
- package/dist/i18n/defaults.d.ts +7 -0
- package/dist/i18n/defaults.js +7 -0
- package/dist/i18n/format.d.ts +84 -0
- package/dist/i18n/format.js +169 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.js +8 -0
- package/dist/i18n/locales/ru-formatters.d.ts +3 -0
- package/dist/i18n/locales/ru-formatters.js +22 -0
- package/dist/i18n/locales/ru.d.ts +5 -0
- package/dist/i18n/locales/ru.js +185 -0
- package/dist/i18n/plural.d.ts +20 -0
- package/dist/i18n/plural.js +26 -0
- package/dist/i18n/provider.d.ts +36 -0
- package/dist/i18n/provider.js +39 -0
- package/dist/i18n/rich.d.ts +14 -0
- package/dist/i18n/rich.js +44 -0
- package/dist/i18n/ru-plural.d.ts +3 -0
- package/dist/i18n/ru-plural.js +4 -0
- package/dist/i18n/strings/alert-dialog.d.ts +5 -0
- package/dist/i18n/strings/alert-dialog.js +5 -0
- package/dist/i18n/strings/block-stack.d.ts +17 -0
- package/dist/i18n/strings/block-stack.js +11 -0
- package/dist/i18n/strings/code-block.d.ts +18 -0
- package/dist/i18n/strings/code-block.js +13 -0
- package/dist/i18n/strings/combobox.d.ts +12 -0
- package/dist/i18n/strings/combobox.js +10 -0
- package/dist/i18n/strings/command-palette.d.ts +17 -0
- package/dist/i18n/strings/command-palette.js +11 -0
- package/dist/i18n/strings/diff-view.d.ts +92 -0
- package/dist/i18n/strings/diff-view.js +68 -0
- package/dist/i18n/strings/image-viewer.d.ts +43 -0
- package/dist/i18n/strings/image-viewer.js +32 -0
- package/dist/i18n/strings/index.d.ts +314 -0
- package/dist/i18n/strings/index.js +43 -0
- package/dist/i18n/strings/kbd.d.ts +42 -0
- package/dist/i18n/strings/kbd.js +39 -0
- package/dist/i18n/strings/markdown.d.ts +23 -0
- package/dist/i18n/strings/markdown.js +18 -0
- package/dist/i18n/strings/money.d.ts +7 -0
- package/dist/i18n/strings/money.js +5 -0
- package/dist/i18n/strings/primitives.d.ts +25 -0
- package/dist/i18n/strings/primitives.js +18 -0
- package/dist/i18n/strings/progress.d.ts +4 -0
- package/dist/i18n/strings/progress.js +4 -0
- package/dist/i18n/strings/shortcut-recorder.d.ts +15 -0
- package/dist/i18n/strings/shortcut-recorder.js +11 -0
- package/dist/i18n/strings/time.d.ts +41 -0
- package/dist/i18n/strings/time.js +20 -0
- package/dist/i18n/strings/toast.d.ts +6 -0
- package/dist/i18n/strings/toast.js +6 -0
- package/dist/i18n/time-zones.d.ts +20 -0
- package/dist/i18n/time-zones.js +64 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +88 -0
- package/dist/lib/ansi.d.ts +55 -0
- package/dist/lib/ansi.js +438 -0
- package/dist/lib/clock.d.ts +32 -0
- package/dist/lib/clock.js +106 -0
- package/dist/lib/cn.d.ts +12 -0
- package/dist/lib/cn.js +28 -0
- package/dist/lib/control-props.d.ts +21 -0
- package/dist/lib/control-props.js +14 -0
- package/dist/lib/direction.d.ts +13 -0
- package/dist/lib/direction.js +25 -0
- package/dist/lib/image-size.d.ts +12 -0
- package/dist/lib/image-size.js +66 -0
- package/dist/lib/keycaps.d.ts +21 -0
- package/dist/lib/keycaps.js +62 -0
- package/dist/lib/keyed.d.ts +9 -0
- package/dist/lib/keyed.js +14 -0
- package/dist/lib/keys.d.ts +49 -0
- package/dist/lib/keys.js +159 -0
- package/dist/lib/kit-motion.d.ts +9 -0
- package/dist/lib/kit-motion.js +12 -0
- package/dist/lib/link-check-dialog.d.ts +11 -0
- package/dist/lib/link-check-dialog.js +18 -0
- package/dist/lib/motion-features.d.ts +1 -0
- package/dist/lib/motion-features.js +4 -0
- package/dist/lib/motion-provider.d.ts +5 -0
- package/dist/lib/motion-provider.js +8 -0
- package/dist/lib/motion.d.ts +39 -0
- package/dist/lib/motion.js +49 -0
- package/dist/lib/number-format.d.ts +114 -0
- package/dist/lib/number-format.js +313 -0
- package/dist/lib/patch.d.ts +72 -0
- package/dist/lib/patch.js +344 -0
- package/dist/lib/path.d.ts +34 -0
- package/dist/lib/path.js +85 -0
- package/dist/lib/search.d.ts +10 -0
- package/dist/lib/search.js +20 -0
- package/dist/lib/shiki-themes.d.ts +7 -0
- package/dist/lib/shiki-themes.js +115 -0
- package/dist/lib/shiki.d.ts +101 -0
- package/dist/lib/shiki.js +353 -0
- package/dist/lib/shortcut-context.d.ts +23 -0
- package/dist/lib/shortcut-context.js +29 -0
- package/dist/lib/text.d.ts +56 -0
- package/dist/lib/text.js +149 -0
- package/dist/lib/theme-scope.d.ts +18 -0
- package/dist/lib/theme-scope.js +24 -0
- package/dist/lib/theme.d.ts +40 -0
- package/dist/lib/theme.js +96 -0
- package/dist/lib/tree-keys.d.ts +72 -0
- package/dist/lib/tree-keys.js +126 -0
- package/dist/lib/url.d.ts +17 -0
- package/dist/lib/url.js +38 -0
- package/dist/lib/use-copy-to-clipboard.d.ts +13 -0
- package/dist/lib/use-copy-to-clipboard.js +40 -0
- package/dist/lib/use-delayed-loading.d.ts +11 -0
- package/dist/lib/use-delayed-loading.js +31 -0
- package/dist/lib/use-file-picker.d.ts +26 -0
- package/dist/lib/use-file-picker.js +43 -0
- package/dist/lib/use-hydrated.d.ts +6 -0
- package/dist/lib/use-hydrated.js +11 -0
- package/dist/lib/use-latest.d.ts +12 -0
- package/dist/lib/use-latest.js +22 -0
- package/dist/lib/use-resolved-theme.d.ts +12 -0
- package/dist/lib/use-resolved-theme.js +25 -0
- package/dist/styles/base.css +393 -0
- package/dist/styles/code.css +35 -0
- package/dist/styles/fonts.css +12 -0
- package/dist/styles/index.css +13 -0
- package/dist/styles/tokens.gen.css +494 -0
- package/package.json +208 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Every change to Nib UI (`@nibapp/ui`) that you can see or build against. The kit follows semantic
|
|
4
|
+
versioning. Before 1.0 a minor version may break, and its entry says how to move. Entries and components
|
|
5
|
+
marked preview may still change.
|
|
6
|
+
|
|
7
|
+
## 0.1.0 — 2026-11-05
|
|
8
|
+
|
|
9
|
+
The first release.
|
|
10
|
+
|
|
11
|
+
- **Requirements.** React 19 and Tailwind CSS 4, which your app brings as peer dependencies. Browsers that
|
|
12
|
+
support `light-dark()` and OKLCH colours.
|
|
13
|
+
- **Server rendering.** Every module that needs the browser starts with `"use client"`, so an app on React
|
|
14
|
+
Server Components imports the kit from its server components. The formatters, `cn` and the strings have no
|
|
15
|
+
directive and run on the server too.
|
|
16
|
+
- **Foundations.** The tokens as CSS: OKLCH ramps and semantic tokens, type, radii, elevation and motion.
|
|
17
|
+
Two themes, Paper (light) and Graphite (dark), which any part of a page can switch with `data-theme`. The
|
|
18
|
+
motion presets and `MotionProvider`; an animated component loads its animation code the first time one
|
|
19
|
+
renders and needs no provider. Self-hosted fonts: Inter, Source Serif 4 and Geist Mono.
|
|
20
|
+
- **Components.** Actions, inputs, overlays, navigation, feedback, data and code, on Base UI, each with a
|
|
21
|
+
live example in both themes at https://ui.nib.gg. Preview: DiffView, Markdown, DataTable, BlockStack,
|
|
22
|
+
CommandPalette and CodeBlock.
|
|
23
|
+
- **Keyboard and overlays.** Off a Mac, Ctrl+K pressed in the CommandPalette reaches your toggle, so it closes
|
|
24
|
+
the palette, or opens it while it's closing; Ctrl+J, Ctrl+K, Ctrl+N and Ctrl+P don't move its list, and the
|
|
25
|
+
arrow keys, Home and End do. A closing backdrop (Dialog, AlertDialog, CommandPalette, ImageViewer and Sheet)
|
|
26
|
+
lets clicks through to the page while it fades. ThemeProvider pauses transitions only while a theme swaps,
|
|
27
|
+
under React's Strict Mode too, with or without `themeInitScript`.
|
|
28
|
+
- **Words.** The kit's own words in English, and in Russian from `@nibapp/ui/locales/ru`. Formatting takes
|
|
29
|
+
any BCP 47 tag, and layout follows the provider's writing direction. There is no right-to-left
|
|
30
|
+
language's table yet.
|
|
31
|
+
- **Utilities.** Keys and shortcuts, number and money formatting, grapheme-aware text, unified diffs, tree
|
|
32
|
+
keys, ANSI text and URLs, and the clock every countdown reads. Preview: `@nibapp/ui/path`,
|
|
33
|
+
`@nibapp/ui/keyed` and `@nibapp/ui/shiki`.
|
|
34
|
+
- **Licence.** The code is under the MIT licence. Nib™ and Nib UI™, the Nib logo, wordmark, app icon and
|
|
35
|
+
mascot are trade marks of Wex Labs Limited and aren't in the package: see TRADEMARKS.md.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wex Labs Limited
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,125 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Nib UI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Nib UI is the design system of Nib, by Wex Labs Limited.
|
|
4
|
+
|
|
5
|
+
`@nibapp/ui` is the tokens as CSS, two themes (Paper, light, and Graphite, dark), motion, and accessible
|
|
6
|
+
components on Base UI, with the kit's own words in English and Russian. The docs, with every component
|
|
7
|
+
live in both themes, are at https://ui.nib.gg.
|
|
8
|
+
|
|
9
|
+
## Use it in an app
|
|
10
|
+
|
|
11
|
+
```css
|
|
12
|
+
/* app.css */
|
|
13
|
+
@import "tailwindcss";
|
|
14
|
+
@import "@nibapp/ui/styles.css";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import "@nibapp/ui/fonts.css";
|
|
19
|
+
import "./app.css";
|
|
20
|
+
import { MotionProvider, ThemeProvider, TooltipProvider, themeInitScript } from "@nibapp/ui";
|
|
21
|
+
import { Toaster } from "@nibapp/ui/components/toast";
|
|
22
|
+
|
|
23
|
+
// In <head>: <script dangerouslySetInnerHTML={{ __html: themeInitScript() }} /> (no theme flash)
|
|
24
|
+
export function Root({ children }: { children: React.ReactNode }) {
|
|
25
|
+
return (
|
|
26
|
+
<ThemeProvider>
|
|
27
|
+
<MotionProvider>
|
|
28
|
+
<TooltipProvider>
|
|
29
|
+
{children}
|
|
30
|
+
<Toaster />
|
|
31
|
+
</TooltipProvider>
|
|
32
|
+
</MotionProvider>
|
|
33
|
+
</ThemeProvider>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The package ships TypeScript source (no build step); Vite/TanStack Start consume it directly, Next.js
|
|
39
|
+
needs `transpilePackages: ["@nibapp/ui"]`. The stylesheet's `@source` makes Tailwind scan this package
|
|
40
|
+
wherever it lives. Import components by their own entry point (`@nibapp/ui/components/button`); the
|
|
41
|
+
root barrel is there for convenience.
|
|
42
|
+
|
|
43
|
+
## Entry points
|
|
44
|
+
|
|
45
|
+
Entries marked preview may still change before 1.0.
|
|
46
|
+
|
|
47
|
+
| import | contents |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `@nibapp/ui` | everything below as a barrel, and the root-only `ClockProvider` / `useNow` / `useCountdown` / `createManualClock` (the one clock every countdown and relative time reads), `useFilePicker`, and `ThemeScope` (a part of the page in one theme, its popups included: they mount inside it, `usePortalContainer`) |
|
|
50
|
+
| `@nibapp/ui/components/<name>` | one component family, e.g. `button`, `dialog`, `command-palette`, `markdown` |
|
|
51
|
+
| `@nibapp/ui/keys` | the key machinery the kit's components use, by keys only: `matchesShortcut()` and `shortcutKeys()` (a key event as the keys a chord names, on any layout), `ariaKeyShortcuts()` / `keyShortcutsProps()` for controls, `isComposing()` (the IME guard every Enter handler uses), `isMacPlatform()`. Without a `ShortcutProvider`, tooltips and menus know only the kit's own actions (`KIT_SHORTCUTS`) |
|
|
52
|
+
| `@nibapp/ui/keycaps` | keys as text with no React and no DOM, the platform passed in: `keyGlyph` (`mod` → ⌘ or Ctrl), `shortcutText` (a chord as plain text), `acceleratorKeys` (a desktop accelerator such as "CommandOrControl+Shift+Space" as logical keys) |
|
|
53
|
+
| `@nibapp/ui/number-format` | `createNumberFormatters(locale, currency)`, `formatCount` and `formatMoney` ("58.7k", "$0.21" in en-GB; "58,7 тыс.", "0,21 $" in ru), `parseAmount`: the one count/money formatter; the locale is required, and a plain number needs the currency it's in (`Money` carries its own) |
|
|
54
|
+
| `@nibapp/ui/i18n` | `UiI18nProvider`, `useUiStrings`, `useUiLocale`, `useFormatters`, `EN_UI_STRINGS` / `UiStrings` (the kit's words), `plural`, `richText` — see [Localization](#localization) |
|
|
55
|
+
| `@nibapp/ui/locales/ru` | `RU_UI_STRINGS`: the kit's words in Russian, a complete `UiStrings`; for writing a Russian table, `ru()` (the plural form), `NBSP` and the formatter words `RU_TIME` and `RU_MONEY` |
|
|
56
|
+
| `@nibapp/ui/i18n/format`, `@nibapp/ui/i18n/plural`, `@nibapp/ui/i18n/strings`, `@nibapp/ui/i18n/time-zones` | the same i18n, one module each and no React: `createFormatters` / `englishFormatters`, `plural`, `EN_UI_STRINGS` / `UiStrings`, the time-zone helpers — for code outside a React tree (a CLI, an extension host) |
|
|
57
|
+
| `@nibapp/ui/ansi` | `parseAnsi` (ANSI SGR → styled segments, never HTML), `stripAnsi`, `ansiStyleToCss`, `ansiColorToCss` |
|
|
58
|
+
| `@nibapp/ui/path` | preview: `splitPath`, `basename`, `dirname`, `joinPath`, `normalizePath`, `pathUnder`, `isAbsolutePath` for paths from any machine (either separator kept) |
|
|
59
|
+
| `@nibapp/ui/keyed` | preview: `keyed(items, keyOf)`, stable React keys for a list without ids |
|
|
60
|
+
| `@nibapp/ui/shiki` | preview, like CodeBlock: the shared lazy Shiki highlighter (`loadLanguage`, `tokenize`, `languageForPath`, `StreamingTokenizer`, …) |
|
|
61
|
+
| `@nibapp/ui/motion` | `spring`, `tween`, `ease`, `duration`, `fade` / `rise` / `pop` / `stagger` |
|
|
62
|
+
| `@nibapp/ui/motion-provider`, `@nibapp/ui/cn`, `@nibapp/ui/clock`, `@nibapp/ui/theme` | what the root also exports, one module each: `MotionProvider`; `cn` / `tv`; `ClockProvider`, `useNow`, `useCountdown`, `createManualClock`; `ThemeProvider`, `useTheme`, `themeInitScript`, `useResolvedTheme` |
|
|
63
|
+
| `@nibapp/ui/styles.css`, `@nibapp/ui/fonts.css` | Tailwind theme + base layer, self-hosted fonts (Inter, Source Serif 4 and Geist Mono, OFL-1.1) |
|
|
64
|
+
| `@nibapp/ui/styles/tokens.css`, `@nibapp/ui/styles/base.css`, `@nibapp/ui/styles/code.css` | the generated token variables, the base layer and the code highlighting alone, for a host that builds its own stylesheet |
|
|
65
|
+
| `@nibapp/ui/text` | `truncateText` and other text helpers that count graphemes (`Intl.Segmenter`) |
|
|
66
|
+
| `@nibapp/ui/patch` | unified diffs, read one way everywhere (CRLF, ``, C-quoted paths, renames, new and deleted files): `splitPatch` (a patch's files with path, change and `+`/`-` counts), `parseHunks`, `patchStats`, `lineText`, `isUnifiedDiff`, `unquoteGitPath`, `normalizePatch`; `buildFilePatch` and `newFilePatch` write one; `getDiffStats` and `getHunkExcerpt` read a parsed `@pierre/diffs` diff |
|
|
67
|
+
| `@nibapp/ui/direction` | `useInlineSign()`: +1 left to right, -1 right to left, for horizontal motion and keys |
|
|
68
|
+
| `@nibapp/ui/tree-keys` | `useTreeKeys()`: the WAI-ARIA tree's keys for any tree (arrows in the reading direction, Home/End, parent and child, `*`, locale-aware type-ahead), with the pure `treeKeyMove` and `treeTypeAhead` |
|
|
69
|
+
| `@nibapp/ui/use-latest` | `useLatest(value)`: a ref holding the last committed value, for effects and stable callbacks |
|
|
70
|
+
| `@nibapp/ui/use-hydrated` | `useHydrated()`: false on the server and while hydrating, so a client-only difference doesn't break the first render |
|
|
71
|
+
| `@nibapp/ui/url` | `urlHost` (a URL's host, or undefined), `exactHostOf` (the host as it is, `www.` kept, for what a site may do), `hostOf` (the site as people say it), `webUrl` (a remote party's link when it's http(s), else undefined) |
|
|
72
|
+
| `@nibapp/ui/image-size` | `imageSize(bytes)` and `base64ImageSize(base64)`: an image's own size in pixels, from its file header |
|
|
73
|
+
| `@nibapp/ui/search`, `@nibapp/ui/clipboard` | search terms and normalization; copy to the clipboard through the host |
|
|
74
|
+
| `@nibapp/ui/vite-deps` | `KIT_PREBUNDLE`: the kit's own dependencies a host's Vite dev server pre-bundles (`optimizeDeps.include`) |
|
|
75
|
+
|
|
76
|
+
## Localization
|
|
77
|
+
|
|
78
|
+
The kit's components render English with "en-GB" formatting, left to right, until a host wraps them
|
|
79
|
+
in `UiI18nProvider`. The provider takes any BCP 47 tag as it is, the kit's words in that language, the
|
|
80
|
+
currency a plain number is money in, the time zone and the writing direction:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { UiI18nProvider } from "@nibapp/ui/i18n";
|
|
84
|
+
import { RU_UI_STRINGS } from "@nibapp/ui/locales/ru";
|
|
85
|
+
|
|
86
|
+
<UiI18nProvider locale="ru-RU" strings={RU_UI_STRINGS} currency="EUR" timeZone={zone}>
|
|
87
|
+
{children}
|
|
88
|
+
</UiI18nProvider>;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- **Strings.** `EN_UI_STRINGS` (type `UiStrings`, deep-readonly) holds every word the kit's components
|
|
92
|
+
show, aria-labels and tooltips included, namespaced per component in camelCase (`combobox.empty`,
|
|
93
|
+
`diffView.*`) plus the shared formatter words (`time.*`, `money.*`). `RU_UI_STRINGS` is the same table
|
|
94
|
+
in Russian, typed complete. Another language is a table typed as `UiStrings`.
|
|
95
|
+
- **Parameters.** Anything parameterised is a function of ONE params object —
|
|
96
|
+
`(p: { count: number }) => string`. Plurals use
|
|
97
|
+
`plural(locale, n, { one, few, many, other })` (CLDR via `Intl.PluralRules`, `#` = the number), which
|
|
98
|
+
also serves hand-written tables: `plural("ru", 5, { one: "# файл", few: "# файла", many: "# файлов", other: "# файла" })`.
|
|
99
|
+
Components put elements (a `<Money>`, a `<code>`) into string messages with `richText(message, params)`.
|
|
100
|
+
- **Formatting.** `useFormatters()` gives `count`, `money`, `wholeMoney`, `exactMoney`, `moneyRange`,
|
|
101
|
+
`unitPrice`, `currencySymbol`, `number`, `percent`, `bytes`, `memory`, `duration`, `elapsed` (a live
|
|
102
|
+
counter), `span`, `future` and `past` (a relative time as a phrase), `relativeTime` (a column label),
|
|
103
|
+
`shortDate`, `clockTime` and `spokenMoney` in the provider's locale. `Money` is shown in its own
|
|
104
|
+
currency; a plain number in the provider's `currency`, and without one showing it fails (the kit
|
|
105
|
+
never assumes a currency). Every language, English included, formats through `Intl` ("58.7k" and
|
|
106
|
+
"$1,204" in en-GB, "58,7 тыс." and "12,30 $" in ru) with exactly the tag the app passed
|
|
107
|
+
(`useUiLocale()` returns it unchanged). Nothing locale-bound is shared between locales (one SSR
|
|
108
|
+
process may render several): the provider keeps one set of formatters per locale, and the text
|
|
109
|
+
helpers keep each `Intl` object keyed by its locale.
|
|
110
|
+
- **Writing direction.** The provider gives Base UI its `direction`, so arrow keys, sliders and popup
|
|
111
|
+
sides follow it. Layout uses logical classes (`ps-*`, `start-*`, `text-end`), sheets open from `start`
|
|
112
|
+
or `end`, `EdgeResizer` sits on a `start` or `end` edge, and horizontal motion multiplies its offset by
|
|
113
|
+
`useInlineSign()`.
|
|
114
|
+
- **Props win.** A label passed as a prop (`placeholder`, `title`, `label`, …) always beats the table.
|
|
115
|
+
|
|
116
|
+
## Feedback
|
|
117
|
+
|
|
118
|
+
Pull requests aren't accepted yet, and there's no public issue tracker before the public launch. To report
|
|
119
|
+
a problem or ask for help, use the support page: https://nib.gg/support.
|
|
120
|
+
|
|
121
|
+
## Licence and trade marks
|
|
122
|
+
|
|
123
|
+
The code is under the MIT licence: see [LICENSE](./LICENSE). The licence covers the code only. Nib™ and
|
|
124
|
+
Nib UI™, the Nib logo, wordmark, app icon and mascot are trade marks of Wex Labs Limited and aren't in
|
|
125
|
+
this package: see [TRADEMARKS.md](./TRADEMARKS.md) and the policy at https://nib.gg/legal/trademarks.
|
package/TRADEMARKS.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Trade marks
|
|
2
|
+
|
|
3
|
+
Nib UI's code is licensed under the MIT licence (see LICENSE). The licence covers the code only. It
|
|
4
|
+
doesn't give you any right to use Wex Labs Limited's names, logos or other brand assets.
|
|
5
|
+
|
|
6
|
+
**Our marks.** Nib™ and Nib UI™ are trade marks of Wex Labs Limited, as are the Nib logo, the Nib
|
|
7
|
+
wordmark, the Nib app icon and Nib the mascot. None of them is included in this package.
|
|
8
|
+
|
|
9
|
+
**You can, without asking:**
|
|
10
|
+
- say in plain text that your project uses Nib UI or `@nibapp/ui`, for example "Built with Nib UI";
|
|
11
|
+
- list `@nibapp/ui` as a dependency;
|
|
12
|
+
- link to Nib's website and docs.
|
|
13
|
+
|
|
14
|
+
**Please don't:**
|
|
15
|
+
- use "Nib", or a name confusingly similar to it, in the name of your product, company, app, domain or
|
|
16
|
+
package (for example `nib-ui-pro` or `@acme/nib-ui`);
|
|
17
|
+
- use our logo, wordmark, app icon or mascot;
|
|
18
|
+
- suggest that Wex Labs Limited made, endorses, sponsors or partners with your project;
|
|
19
|
+
- present a modified version of the kit as Nib UI. If you fork it, give the fork its own name. You may
|
|
20
|
+
say in plain text that it's "based on Nib UI".
|
|
21
|
+
|
|
22
|
+
The full policy is at https://nib.gg/legal/trademarks. Questions and reports: legal@nib.gg.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Money, in code: an integer count of micro-units (millionths of the currency's major unit, because
|
|
3
|
+
* usage costs are fractions of a cent) and its ISO 4217 currency. Never a float, so sums and
|
|
4
|
+
* comparisons are exact. Arithmetic rounds half to even at the micro; people see amounts through
|
|
5
|
+
* `formatMoney` and `moneyFormat` (`Intl.NumberFormat` with their locale and the amount's currency),
|
|
6
|
+
* so no code assumes a currency, a symbol or a number format. A host's wire schemas may hold this same
|
|
7
|
+
* shape.
|
|
8
|
+
*/
|
|
9
|
+
/** Micro-units in one major unit of any currency: 1 USD is 1 000 000 micros. */
|
|
10
|
+
export declare const MICROS_PER_UNIT = 1000000;
|
|
11
|
+
/** An ISO 4217 currency code (`USD`, `EUR`, `JPY`). */
|
|
12
|
+
export type CurrencyCode = string;
|
|
13
|
+
/** An amount: `micros` of `currency`, negative for a refund or a credit. */
|
|
14
|
+
export type Money = {
|
|
15
|
+
micros: number;
|
|
16
|
+
currency: CurrencyCode;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Where a host registers the amounts a person may see as they are, when some may not, by declaration
|
|
20
|
+
* merging on this module (`interface MoneyRegister { readonly showable: MyShowableMoney }`): every
|
|
21
|
+
* formatter below then refuses any other amount at compile time. Without a registration, any `Money` is
|
|
22
|
+
* showable.
|
|
23
|
+
*/
|
|
24
|
+
export interface MoneyRegister {
|
|
25
|
+
}
|
|
26
|
+
/** Money a person may see as it is: the host's registered type (`MoneyRegister`), else any `Money`. */
|
|
27
|
+
export type ShowableMoney = MoneyRegister extends {
|
|
28
|
+
readonly showable: infer Showable extends Money;
|
|
29
|
+
} ? Showable : Money;
|
|
30
|
+
/** Two amounts in different currencies were added, subtracted or compared. */
|
|
31
|
+
export declare class CurrencyMismatchError extends Error {
|
|
32
|
+
readonly left: CurrencyCode;
|
|
33
|
+
readonly right: CurrencyCode;
|
|
34
|
+
constructor(left: CurrencyCode, right: CurrencyCode);
|
|
35
|
+
}
|
|
36
|
+
export declare function money(micros: number, currency: CurrencyCode): Money;
|
|
37
|
+
/**
|
|
38
|
+
* A decimal amount in major units (`"12.34"`, `"-0.000001"`, `"1e-3"`), exactly. More than six
|
|
39
|
+
* decimal places is refused: say how to round with `moneyFromUnits` instead.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseMoney(amount: string, currency: CurrencyCode): Money;
|
|
42
|
+
/**
|
|
43
|
+
* A number in major units (a stored double, a price read from a provider), rounded half to even at
|
|
44
|
+
* the micro. The number's shortest decimal form is what's rounded, so `0.1` is exactly 100 000 micros.
|
|
45
|
+
*/
|
|
46
|
+
export declare function moneyFromUnits(units: number, currency: CurrencyCode): Money;
|
|
47
|
+
/**
|
|
48
|
+
* The amount as a number of major units, the nearest double to the exact decimal, for a reader that
|
|
49
|
+
* still takes a number: a slider, totals, an export's cost field, a number formatter.
|
|
50
|
+
*/
|
|
51
|
+
export declare function moneyToUnits(amount: ShowableMoney): number;
|
|
52
|
+
/**
|
|
53
|
+
* Any amount as a number of major units, for accounting that takes plain numbers: a chart's scale,
|
|
54
|
+
* a sort, metrics, an external format. Never for what a person reads.
|
|
55
|
+
*/
|
|
56
|
+
export declare function accountingUnits(amount: Money): number;
|
|
57
|
+
/** The amount in major units as an exact decimal (`"12.34"`, `"-0.000001"`, `"0"`). */
|
|
58
|
+
export declare function moneyToDecimal(amount: ShowableMoney): `${number}`;
|
|
59
|
+
/**
|
|
60
|
+
* An amount in text a program reads (tool results, diagnostics): the exact decimal and its currency
|
|
61
|
+
* code, `0.012345 USD`. People see amounts through `formatMoney` in their locale, never this.
|
|
62
|
+
*/
|
|
63
|
+
export declare function moneyText(amount: ShowableMoney): string;
|
|
64
|
+
export declare function addMoney(left: Money, right: Money): Money;
|
|
65
|
+
export declare function subtractMoney(left: Money, right: Money): Money;
|
|
66
|
+
/** The total of `amounts`, all in `currency` (zero when there are none). */
|
|
67
|
+
export declare function sumMoney(amounts: readonly Money[], currency: CurrencyCode): Money;
|
|
68
|
+
/**
|
|
69
|
+
* The amount times `factor` (a fee rate, a share, a token count over a per-million price), rounded
|
|
70
|
+
* half to even at the micro. The factor's shortest decimal form is multiplied exactly.
|
|
71
|
+
*/
|
|
72
|
+
export declare function scaleMoney(amount: Money, factor: number): Money;
|
|
73
|
+
/** -1, 0 or 1 as `left` is less than, equal to or more than `right`. */
|
|
74
|
+
export declare function compareMoney(left: Money, right: Money): -1 | 0 | 1;
|
|
75
|
+
/** Digits in the currency's minor unit (USD 2, JPY 0, KWD 3), as `Intl` knows the currency. */
|
|
76
|
+
export declare function currencyExponent(currency: CurrencyCode): number;
|
|
77
|
+
/**
|
|
78
|
+
* The amount in its currency's minor unit (cents, for a payment provider), exactly: an amount that
|
|
79
|
+
* isn't a whole number of minor units is refused, never rounded, so a charge is never silently changed.
|
|
80
|
+
*/
|
|
81
|
+
export declare function toMinorUnits(amount: ShowableMoney): number;
|
|
82
|
+
/** An amount given in its currency's minor unit (a payment provider's cents). */
|
|
83
|
+
export declare function fromMinorUnits(minor: number, currency: CurrencyCode): Money;
|
|
84
|
+
/**
|
|
85
|
+
* The amount for people, in `locale` (a BCP 47 tag) with its currency's symbol and digits. Usage
|
|
86
|
+
* costs below the currency's smallest unit need more digits: pass `maximumFractionDigits`.
|
|
87
|
+
*/
|
|
88
|
+
export declare function formatMoney(amount: ShowableMoney, locale: string, options?: Intl.NumberFormatOptions): string;
|
|
89
|
+
/**
|
|
90
|
+
* `currency` in `locale`'s pattern with its narrow symbol: "$5.50" in every English region (en-GB's
|
|
91
|
+
* own symbol would be "US$5.50"), "5,50 $" in ru. Exactly `fraction` decimals when given, else the
|
|
92
|
+
* currency's own (2 for USD, 0 for JPY).
|
|
93
|
+
*/
|
|
94
|
+
export declare function moneyFormat(locale: string, currency: string, fraction?: number): Intl.NumberFormat;
|
|
95
|
+
/** Decimals of `currency`'s minor unit (ISO 4217, from `Intl`): 2 for USD, 0 for JPY, 3 for KWD. */
|
|
96
|
+
export declare function currencyDigits(currency: string, locale: string): number;
|
|
97
|
+
/** The smallest amount `currency` writes: 0.01 for USD, 1 for JPY. */
|
|
98
|
+
export declare function minorUnit(currency: string, locale: string): number;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
export const MICROS_PER_UNIT = 1e6;
|
|
2
|
+
const MICRO_DIGITS = 6;
|
|
3
|
+
|
|
4
|
+
export class CurrencyMismatchError extends Error {
|
|
5
|
+
left;
|
|
6
|
+
right;
|
|
7
|
+
constructor(left, right) {
|
|
8
|
+
super(`can't combine ${left} with ${right}`);
|
|
9
|
+
this.name = "CurrencyMismatchError";
|
|
10
|
+
this.left = left;
|
|
11
|
+
this.right = right;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function money(micros, currency) {
|
|
15
|
+
if (!Number.isSafeInteger(micros))
|
|
16
|
+
throw new RangeError(`${micros} is not a whole number of micros`);
|
|
17
|
+
return { micros, currency };
|
|
18
|
+
}
|
|
19
|
+
export function parseMoney(amount, currency) {
|
|
20
|
+
return money(scaledInteger(amount, MICRO_DIGITS, "exact"), currency);
|
|
21
|
+
}
|
|
22
|
+
export function moneyFromUnits(units, currency) {
|
|
23
|
+
if (!Number.isFinite(units))
|
|
24
|
+
throw new RangeError(`${units} is not an amount`);
|
|
25
|
+
return money(scaledInteger(String(units), MICRO_DIGITS, "half-even"), currency);
|
|
26
|
+
}
|
|
27
|
+
export function moneyToUnits(amount) {
|
|
28
|
+
return Number(moneyToDecimal(amount));
|
|
29
|
+
}
|
|
30
|
+
export function accountingUnits(amount) {
|
|
31
|
+
return Number(decimalOf(amount));
|
|
32
|
+
}
|
|
33
|
+
export function moneyToDecimal(amount) {
|
|
34
|
+
return decimalOf(amount);
|
|
35
|
+
}
|
|
36
|
+
export function moneyText(amount) {
|
|
37
|
+
return `${moneyToDecimal(amount)} ${amount.currency}`;
|
|
38
|
+
}
|
|
39
|
+
export function addMoney(left, right) {
|
|
40
|
+
sameCurrency(left, right);
|
|
41
|
+
return money(left.micros + right.micros, left.currency);
|
|
42
|
+
}
|
|
43
|
+
export function subtractMoney(left, right) {
|
|
44
|
+
sameCurrency(left, right);
|
|
45
|
+
return money(left.micros - right.micros, left.currency);
|
|
46
|
+
}
|
|
47
|
+
export function sumMoney(amounts, currency) {
|
|
48
|
+
return amounts.reduce((total, amount) => addMoney(total, amount), money(0, currency));
|
|
49
|
+
}
|
|
50
|
+
export function scaleMoney(amount, factor) {
|
|
51
|
+
if (!Number.isFinite(factor))
|
|
52
|
+
throw new RangeError(`${factor} is not a factor`);
|
|
53
|
+
const { digits, scale } = decimalParts(String(factor));
|
|
54
|
+
const product = BigInt(amount.micros) * digits;
|
|
55
|
+
return money(Number(roundScaled(product, scale, "half-even")), amount.currency);
|
|
56
|
+
}
|
|
57
|
+
export function compareMoney(left, right) {
|
|
58
|
+
sameCurrency(left, right);
|
|
59
|
+
return left.micros < right.micros ? -1 : left.micros > right.micros ? 1 : 0;
|
|
60
|
+
}
|
|
61
|
+
export function currencyExponent(currency) {
|
|
62
|
+
const digits = new Intl.NumberFormat(undefined, { style: "currency", currency }).resolvedOptions().maximumFractionDigits;
|
|
63
|
+
if (digits === undefined || digits > MICRO_DIGITS)
|
|
64
|
+
throw new RangeError(`${currency} has no minor unit we can hold`);
|
|
65
|
+
return digits;
|
|
66
|
+
}
|
|
67
|
+
export function toMinorUnits(amount) {
|
|
68
|
+
const divisor = 10 ** (MICRO_DIGITS - currencyExponent(amount.currency));
|
|
69
|
+
if (amount.micros % divisor !== 0) {
|
|
70
|
+
throw new RangeError(`${moneyText(amount)} isn't a whole number of minor units`);
|
|
71
|
+
}
|
|
72
|
+
return amount.micros / divisor;
|
|
73
|
+
}
|
|
74
|
+
export function fromMinorUnits(minor, currency) {
|
|
75
|
+
if (!Number.isSafeInteger(minor))
|
|
76
|
+
throw new RangeError(`${minor} is not a whole number of minor units`);
|
|
77
|
+
return money(minor * 10 ** (MICRO_DIGITS - currencyExponent(currency)), currency);
|
|
78
|
+
}
|
|
79
|
+
export function formatMoney(amount, locale, options = {}) {
|
|
80
|
+
return new Intl.NumberFormat(locale, { ...options, style: "currency", currency: amount.currency }).format(moneyToDecimal(amount));
|
|
81
|
+
}
|
|
82
|
+
const moneyFormats = new Map;
|
|
83
|
+
export function moneyFormat(locale, currency, fraction) {
|
|
84
|
+
const key = `${locale} ${currency} ${fraction ?? ""}`;
|
|
85
|
+
let format = moneyFormats.get(key);
|
|
86
|
+
if (format === undefined) {
|
|
87
|
+
format = new Intl.NumberFormat(locale, {
|
|
88
|
+
style: "currency",
|
|
89
|
+
currency,
|
|
90
|
+
currencyDisplay: "narrowSymbol",
|
|
91
|
+
...fraction === undefined ? {} : { minimumFractionDigits: fraction, maximumFractionDigits: fraction }
|
|
92
|
+
});
|
|
93
|
+
moneyFormats.set(key, format);
|
|
94
|
+
}
|
|
95
|
+
return format;
|
|
96
|
+
}
|
|
97
|
+
export function currencyDigits(currency, locale) {
|
|
98
|
+
return moneyFormat(locale, currency).resolvedOptions().maximumFractionDigits ?? 0;
|
|
99
|
+
}
|
|
100
|
+
export function minorUnit(currency, locale) {
|
|
101
|
+
return 10 ** -currencyDigits(currency, locale);
|
|
102
|
+
}
|
|
103
|
+
function sameCurrency(left, right) {
|
|
104
|
+
if (left.currency !== right.currency)
|
|
105
|
+
throw new CurrencyMismatchError(left.currency, right.currency);
|
|
106
|
+
}
|
|
107
|
+
function decimalOf(amount) {
|
|
108
|
+
const sign = amount.micros < 0 ? "-" : "";
|
|
109
|
+
const digits = String(Math.abs(amount.micros)).padStart(MICRO_DIGITS + 1, "0");
|
|
110
|
+
const whole = digits.slice(0, -MICRO_DIGITS);
|
|
111
|
+
const fraction = digits.slice(-MICRO_DIGITS).replace(/0+$/u, "");
|
|
112
|
+
const text = fraction === "" ? `${sign}${whole}` : `${sign}${whole}.${fraction}`;
|
|
113
|
+
if (!isDecimal(text))
|
|
114
|
+
throw new RangeError(`${amount.micros} micros has no decimal form`);
|
|
115
|
+
return text;
|
|
116
|
+
}
|
|
117
|
+
const DECIMAL = /^([+-])?(\d+)(?:\.(\d*))?(?:e([+-]?\d+))?$/iu;
|
|
118
|
+
function isDecimal(text) {
|
|
119
|
+
return DECIMAL.test(text);
|
|
120
|
+
}
|
|
121
|
+
function decimalParts(text) {
|
|
122
|
+
const match = DECIMAL.exec(text.trim());
|
|
123
|
+
if (match === null)
|
|
124
|
+
throw new RangeError(`${text} is not a decimal amount`);
|
|
125
|
+
const [, sign = "", whole = "", fraction = "", exponent = "0"] = match;
|
|
126
|
+
const scale = fraction.length - Number(exponent);
|
|
127
|
+
const magnitude = BigInt(`${whole}${fraction}`);
|
|
128
|
+
const digits = sign === "-" ? -magnitude : magnitude;
|
|
129
|
+
return scale >= 0 ? { digits, scale } : { digits: digits * 10n ** BigInt(-scale), scale: 0 };
|
|
130
|
+
}
|
|
131
|
+
function scaledInteger(text, places, mode) {
|
|
132
|
+
const { digits, scale } = decimalParts(text);
|
|
133
|
+
const value = scale <= places ? digits * 10n ** BigInt(places - scale) : roundScaled(digits, scale - places, mode, text);
|
|
134
|
+
return Number(value);
|
|
135
|
+
}
|
|
136
|
+
function roundScaled(value, scale, mode, text = String(value)) {
|
|
137
|
+
if (scale === 0)
|
|
138
|
+
return value;
|
|
139
|
+
const divisor = 10n ** BigInt(scale);
|
|
140
|
+
const negative = value < 0n;
|
|
141
|
+
const magnitude = negative ? -value : value;
|
|
142
|
+
let quotient = magnitude / divisor;
|
|
143
|
+
const remainder = magnitude % divisor;
|
|
144
|
+
if (remainder !== 0n) {
|
|
145
|
+
if (mode === "exact")
|
|
146
|
+
throw new RangeError(`${text} has more than ${MICRO_DIGITS} decimal places`);
|
|
147
|
+
const twice = remainder * 2n;
|
|
148
|
+
if (twice > divisor || twice === divisor && quotient % 2n === 1n)
|
|
149
|
+
quotient += 1n;
|
|
150
|
+
}
|
|
151
|
+
return negative ? -quotient : quotient;
|
|
152
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export declare const palette: {
|
|
2
|
+
readonly light: {
|
|
3
|
+
readonly gray: readonly ["#fdfcf9", "#f9f8f5", "#f4f2ee", "#eeece8", "#e9e6e1", "#e1ded9", "#d5d3cd", "#c2bfb9", "#82807b", "#676460", "#514e4a", "#1b1a17"];
|
|
4
|
+
readonly blue: readonly ["#fafcff", "#f4f8ff", "#ebf3ff", "#e2edff", "#d9e8ff", "#cde0ff", "#bad4ff", "#9bc0ff", "#0169ff", "#005de5", "#125bd1", "#001643"];
|
|
5
|
+
readonly green: readonly ["#f9fefa", "#f1fbf3", "#e6f8e9", "#daf5e0", "#cef1d6", "#c1ebca", "#ade2ba", "#8cd39f", "#008743", "#00773b", "#097038", "#00210c"];
|
|
6
|
+
readonly amber: readonly ["#fffbf7", "#fff7ed", "#fef0de", "#fee9d0", "#fde2c1", "#fad8b0", "#f4cb98", "#eab470", "#f3a52b", "#e29721", "#905c00", "#271600"];
|
|
7
|
+
readonly red: readonly ["#fffbfb", "#fff6f5", "#ffeeec", "#ffe6e3", "#ffdeda", "#ffd2ce", "#ffc1bb", "#ffa39b", "#d2393a", "#bf2d30", "#ac3434", "#370406"];
|
|
8
|
+
readonly violet: readonly ["#fcfbff", "#f8f7ff", "#f3f0ff", "#ede9ff", "#e8e2ff", "#e1d8ff", "#d6caff", "#c4b2fb", "#8264c8", "#7558b7", "#6952a1", "#1d1234"];
|
|
9
|
+
};
|
|
10
|
+
readonly dark: {
|
|
11
|
+
readonly gray: readonly ["#191917", "#1f1e1c", "#252422", "#2c2b28", "#32312e", "#3b3936", "#484643", "#5e5c58", "#767470", "#a19f9a", "#bfbdb9", "#f1f0ed"];
|
|
12
|
+
readonly blue: readonly ["#16191d", "#191e27", "#1b2535", "#1d2b43", "#1e3151", "#213961", "#27457a", "#2f59a2", "#4c8eff", "#669eff", "#8eb8ff", "#e8f1ff"];
|
|
13
|
+
readonly green: readonly ["#171a17", "#1a201b", "#1d281f", "#1f2f23", "#213727", "#25412d", "#2c5036", "#366945", "#36ac62", "#4db972", "#7dd596", "#d2fddc"];
|
|
14
|
+
readonly amber: readonly ["#1b1816", "#221d18", "#2b2319", "#352819", "#3e2e19", "#4a351a", "#5b411d", "#785421", "#faab35", "#ffbe6a", "#febc67", "#ffedd8"];
|
|
15
|
+
readonly red: readonly ["#1c1717", "#251b1b", "#301f1e", "#3c2321", "#482724", "#552c29", "#6a3531", "#8c433e", "#e9504d", "#f6625e", "#ff958d", "#ffebe9"];
|
|
16
|
+
readonly violet: readonly ["#19181b", "#1e1d23", "#25232e", "#2c2839", "#332d44", "#3b3450", "#494064", "#5f5284", "#9477dd", "#a186e9", "#c3afff", "#f1edff"];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/** Syntax colours (the kit's src/lib/shiki-themes.ts), each held to 4.5:1 on every background code sits on. */
|
|
20
|
+
export declare const syntax: {
|
|
21
|
+
readonly light: {
|
|
22
|
+
readonly fg: "#1b1a17";
|
|
23
|
+
readonly muted: "#514e4a";
|
|
24
|
+
readonly comment: "#56534f";
|
|
25
|
+
readonly keyword: "#5e4694";
|
|
26
|
+
readonly string: "#00622f";
|
|
27
|
+
readonly number: "#764a00";
|
|
28
|
+
readonly fn: "#004cbd";
|
|
29
|
+
readonly type: "#005c78";
|
|
30
|
+
readonly tag: "#933333";
|
|
31
|
+
readonly attribute: "#764a00";
|
|
32
|
+
readonly regex: "#9e2629";
|
|
33
|
+
readonly inserted: "#00622f";
|
|
34
|
+
readonly deleted: "#9e2629";
|
|
35
|
+
};
|
|
36
|
+
readonly dark: {
|
|
37
|
+
readonly fg: "#e6e5e2";
|
|
38
|
+
readonly muted: "#bfbdb9";
|
|
39
|
+
readonly comment: "#bcbab4";
|
|
40
|
+
readonly keyword: "#c2b2f2";
|
|
41
|
+
readonly string: "#89d19c";
|
|
42
|
+
readonly number: "#f3bc76";
|
|
43
|
+
readonly fn: "#99bbf4";
|
|
44
|
+
readonly type: "#90c5c8";
|
|
45
|
+
readonly tag: "#faa29b";
|
|
46
|
+
readonly attribute: "#f3bc76";
|
|
47
|
+
readonly regex: "#faa29b";
|
|
48
|
+
readonly inserted: "#89d19c";
|
|
49
|
+
readonly deleted: "#faa29b";
|
|
50
|
+
};
|
|
51
|
+
};
|