@eifi1/ui-kit 0.11.0 → 0.13.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/README.md +59 -34
- package/dist/chart.d.ts +3 -1
- package/dist/chart.js +2 -0
- package/dist/chart.js.map +1 -1
- package/dist/components/account-settings-labels.d.ts +105 -0
- package/dist/components/account-settings-labels.js +81 -0
- package/dist/components/account-settings-labels.js.map +1 -0
- package/dist/components/account-settings.d.ts +36 -52
- package/dist/components/account-settings.js +44 -8
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/amount-input.d.ts +40 -4
- package/dist/components/amount-input.js +50 -12
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/authed-image.d.ts +68 -0
- package/dist/components/authed-image.js +117 -0
- package/dist/components/authed-image.js.map +1 -0
- package/dist/components/breadcrumbs.d.ts +2 -1
- package/dist/components/breadcrumbs.js +5 -2
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/bulk-action-bar.d.ts +10 -2
- package/dist/components/bulk-action-bar.js.map +1 -1
- package/dist/components/button-group.d.ts +51 -2
- package/dist/components/button-group.js +5 -1
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/calculator.d.ts +15 -4
- package/dist/components/calendar-heatmap.d.ts +41 -3
- package/dist/components/calendar-heatmap.js +48 -7
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/chart.js +14 -8
- package/dist/components/chart.js.map +1 -1
- package/dist/components/chip.d.ts +2 -1
- package/dist/components/chip.js +26 -18
- package/dist/components/chip.js.map +1 -1
- package/dist/components/choice-card.d.ts +35 -3
- package/dist/components/choice-card.js +83 -25
- package/dist/components/choice-card.js.map +1 -1
- package/dist/components/combobox-core.d.ts +3 -1
- package/dist/components/combobox-core.js +18 -5
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +2 -2
- package/dist/components/combobox.js +31 -9
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/copy-button.d.ts +51 -70
- package/dist/components/copy-button.js +1 -1
- package/dist/components/copy-button.js.map +1 -1
- package/dist/components/data-table-cells.d.ts +63 -0
- package/dist/components/data-table-cells.js +64 -0
- package/dist/components/data-table-cells.js.map +1 -0
- package/dist/components/data-table-filter-popover.d.ts +1 -1
- package/dist/components/data-table-filter-popover.js +8 -1
- package/dist/components/data-table-filter-popover.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-filters.js +31 -1
- package/dist/components/data-table-filters.js.map +1 -1
- package/dist/components/data-table-labels.d.ts +13 -0
- package/dist/components/data-table-labels.js +8 -1
- package/dist/components/data-table-labels.js.map +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +125 -27
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +3 -1
- package/dist/components/date-picker.js +15 -2
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/description-list.d.ts +23 -3
- package/dist/components/description-list.js +23 -3
- package/dist/components/description-list.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +1 -0
- package/dist/components/entity-combobox.d.ts +1 -1
- package/dist/components/entity-combobox.js +40 -20
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/error-boundary.d.ts +281 -0
- package/dist/components/error-boundary.js +302 -0
- package/dist/components/error-boundary.js.map +1 -0
- package/dist/components/field-sync.js +90 -83
- package/dist/components/field-sync.js.map +1 -1
- package/dist/components/field.d.ts +30 -4
- package/dist/components/field.js +40 -17
- package/dist/components/field.js.map +1 -1
- package/dist/components/file-button.d.ts +50 -208
- package/dist/components/file-dropzone.d.ts +49 -2
- package/dist/components/floating-panel.d.ts +13 -4
- package/dist/components/floating-panel.js +7 -3
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +53 -0
- package/dist/components/form-actions.js +98 -0
- package/dist/components/form-actions.js.map +1 -0
- package/dist/components/full-bleed-dialog.js +4 -0
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/hover-menu.js +64 -56
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/image-grid.d.ts +67 -0
- package/dist/components/image-grid.js +112 -0
- package/dist/components/image-grid.js.map +1 -0
- package/dist/components/lightbox.d.ts +104 -0
- package/dist/components/lightbox.js +229 -0
- package/dist/components/lightbox.js.map +1 -0
- package/dist/components/line-items.d.ts +114 -0
- package/dist/components/line-items.js +238 -0
- package/dist/components/line-items.js.map +1 -0
- package/dist/components/list.d.ts +37 -4
- package/dist/components/list.js +35 -14
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-state.d.ts +24 -0
- package/dist/components/loading-state.js +32 -0
- package/dist/components/loading-state.js.map +1 -0
- package/dist/components/menu-item.d.ts +2 -1
- package/dist/components/menu-item.js +5 -1
- package/dist/components/menu-item.js.map +1 -1
- package/dist/components/mini-calendar.d.ts +13 -1
- package/dist/components/mini-calendar.js +46 -2
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +20 -1
- package/dist/components/modal.js +26 -2
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +21 -1
- package/dist/components/month-picker.js +139 -26
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +1 -1
- package/dist/components/multi-entity-combobox.js +42 -20
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/nav-pills.d.ts +2 -1
- package/dist/components/nav-pills.js +5 -1
- package/dist/components/nav-pills.js.map +1 -1
- package/dist/components/number-field.d.ts +24 -5
- package/dist/components/number-field.js +5 -7
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +25 -5
- package/dist/components/number-input.js +7 -1
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +20 -5
- package/dist/components/numpad-sheet.js +3 -2
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/page-header.d.ts +35 -6
- package/dist/components/page-header.js +7 -1
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/passkeys-setting.d.ts +67 -0
- package/dist/components/passkeys-setting.js +214 -0
- package/dist/components/passkeys-setting.js.map +1 -0
- package/dist/components/picker-sheet.js +5 -1
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/pie-chart-labels.d.ts +20 -0
- package/dist/components/pie-chart-labels.js +12 -0
- package/dist/components/pie-chart-labels.js.map +1 -0
- package/dist/components/pie-chart.d.ts +101 -0
- package/dist/components/pie-chart.js +283 -0
- package/dist/components/pie-chart.js.map +1 -0
- package/dist/components/popover.js +3 -0
- package/dist/components/popover.js.map +1 -1
- package/dist/components/progress-bar.d.ts +44 -3
- package/dist/components/progress-bar.js +58 -22
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/qr-code.d.ts +34 -0
- package/dist/components/qr-code.js +57 -0
- package/dist/components/qr-code.js.map +1 -0
- package/dist/components/search-field.d.ts +15 -3
- package/dist/components/search-field.js +5 -1
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/settings-fields.d.ts +49 -1
- package/dist/components/signed-amount.d.ts +122 -0
- package/dist/components/signed-amount.js +152 -0
- package/dist/components/signed-amount.js.map +1 -0
- package/dist/components/skeleton.d.ts +10 -3
- package/dist/components/skeleton.js +12 -2
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/stat-tile.d.ts +2 -1
- package/dist/components/stat-tile.js +6 -3
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/swipeable-row.js +4 -3
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/table.d.ts +39 -5
- package/dist/components/table.js +45 -11
- package/dist/components/table.js.map +1 -1
- package/dist/components/text-link.d.ts +238 -0
- package/dist/components/text-link.js +144 -0
- package/dist/components/text-link.js.map +1 -0
- package/dist/components/text.d.ts +26 -2
- package/dist/components/text.js +16 -2
- package/dist/components/text.js.map +1 -1
- package/dist/components/time-input.d.ts +49 -1
- package/dist/components/toggle-group.d.ts +35 -0
- package/dist/components/toggle-group.js +43 -14
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +53 -3
- package/dist/components/toggle-legend.js +54 -1
- package/dist/components/toggle-legend.js.map +1 -1
- package/dist/components/tooltip.d.ts +51 -1
- package/dist/components/tooltip.js +59 -35
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/ui.d.ts +51 -779
- package/dist/components/ui.js +364 -34
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +3 -52
- package/dist/components/use-table-state.js +128 -30
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/{data-table-filters-Dh9uF_S-.d.ts → data-table-Drun8Fk9.d.ts} +305 -61
- package/dist/data-table.d.ts +2 -1
- package/dist/data-table.js +5 -1
- package/dist/data-table.js.map +1 -1
- package/dist/feedback/feedback-attachment.js +4 -6
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-dialog.d.ts +26 -3
- package/dist/feedback/feedback-dialog.js +21 -5
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.js +86 -75
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +173 -0
- package/dist/feedback/feedback-thread.js +264 -0
- package/dist/feedback/feedback-thread.js.map +1 -0
- package/dist/feedback.d.ts +2 -1
- package/dist/feedback.js +1 -0
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-authed-src.d.ts +71 -0
- package/dist/hooks/use-authed-src.js +63 -0
- package/dist/hooks/use-authed-src.js.map +1 -0
- package/dist/hooks/use-file-drop.d.ts +49 -2
- package/dist/hooks/use-hotkey.d.ts +59 -0
- package/dist/hooks/use-hotkey.js +105 -0
- package/dist/hooks/use-hotkey.js.map +1 -0
- package/dist/hooks/use-search-param-state.d.ts +83 -0
- package/dist/hooks/use-search-param-state.js +94 -0
- package/dist/hooks/use-search-param-state.js.map +1 -0
- package/dist/i18n/defaults.d.ts +15 -4
- package/dist/i18n/defaults.js +31 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +46 -312
- package/dist/i18n/kit-labels.js +20 -5
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/de-CH-informal.d.ts +15 -4
- package/dist/i18n/locales/de-CH.d.ts +15 -4
- package/dist/i18n/locales/de-informal.d.ts +15 -4
- package/dist/i18n/locales/de.d.ts +15 -4
- package/dist/i18n/locales/de.js +164 -3
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +15 -4
- package/dist/i18n/locales/es.js +161 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +15 -4
- package/dist/i18n/locales/fr.js +161 -3
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +15 -4
- package/dist/i18n/locales/hu.js +161 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +15 -4
- package/dist/i18n/locales/it.js +161 -3
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +15 -4
- package/dist/i18n/locales/zh.js +161 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.d.ts +39 -19
- package/dist/index.js +40 -1
- package/dist/index.js.map +1 -1
- package/dist/kit-labels-Mz8zZHIF.d.ts +1744 -0
- package/dist/lib/format.d.ts +162 -0
- package/dist/lib/format.js +151 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/qr-encode.d.ts +44 -0
- package/dist/lib/qr-encode.js +338 -0
- package/dist/lib/qr-encode.js.map +1 -0
- package/dist/rhf/fields.d.ts +283 -0
- package/dist/rhf/fields.js +631 -0
- package/dist/rhf/fields.js.map +1 -0
- package/dist/rhf/form.d.ts +49 -1
- package/dist/rhf/line-items.d.ts +31 -0
- package/dist/rhf/line-items.js +59 -0
- package/dist/rhf/line-items.js.map +1 -0
- package/dist/rhf.d.ts +61 -1
- package/dist/rhf.js +2 -0
- package/dist/rhf.js.map +1 -1
- package/dist/search/command-palette.js +20 -14
- package/dist/search/command-palette.js.map +1 -1
- package/dist/shell/app-shell.d.ts +99 -2
- package/dist/shell/app-shell.js +120 -57
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/auth-layout.d.ts +74 -0
- package/dist/shell/auth-layout.js +97 -0
- package/dist/shell/auth-layout.js.map +1 -0
- package/dist/shell/top-bar-brand.d.ts +81 -0
- package/dist/shell/top-bar-brand.js +47 -0
- package/dist/shell/top-bar-brand.js.map +1 -0
- package/dist/shell/topbar-action-menu.d.ts +21 -4
- package/dist/shell/topbar-action-menu.js +18 -2
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell.d.ts +56 -1
- package/dist/shell.js +2 -0
- package/dist/shell.js.map +1 -1
- package/dist/tour/tour.js +10 -4
- package/dist/tour/tour.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +48 -1
- package/dist/wizard/use-wizard.js +4 -2
- package/dist/wizard/use-wizard.js.map +1 -1
- package/dist/wizard/wizard-context.d.ts +24 -1
- package/dist/wizard/wizard-context.js +19 -2
- package/dist/wizard/wizard-context.js.map +1 -1
- package/dist/wizard/wizard-step.d.ts +30 -5
- package/dist/wizard/wizard-step.js +34 -3
- package/dist/wizard/wizard-step.js.map +1 -1
- package/dist/wizard.d.ts +50 -3
- package/package.json +6 -6
- package/src/chart.ts +4 -0
- package/src/components/account-settings-labels.ts +190 -0
- package/src/components/account-settings.tsx +110 -63
- package/src/components/amount-input.tsx +109 -10
- package/src/components/authed-image.tsx +190 -0
- package/src/components/breadcrumbs.tsx +8 -2
- package/src/components/bulk-action-bar.tsx +16 -3
- package/src/components/button-group.tsx +7 -2
- package/src/components/calendar-heatmap.tsx +105 -10
- package/src/components/chart.tsx +19 -8
- package/src/components/chip.tsx +14 -4
- package/src/components/choice-card.tsx +136 -25
- package/src/components/combobox-core.tsx +33 -9
- package/src/components/combobox.tsx +33 -9
- package/src/components/copy-button.tsx +3 -2
- package/src/components/data-table-cells.tsx +119 -0
- package/src/components/data-table-filter-popover.tsx +13 -5
- package/src/components/data-table-filters.ts +77 -0
- package/src/components/data-table-labels.ts +23 -0
- package/src/components/data-table.tsx +230 -13
- package/src/components/date-picker.tsx +19 -3
- package/src/components/description-list.tsx +52 -3
- package/src/components/entity-combobox.tsx +25 -7
- package/src/components/error-boundary.tsx +640 -0
- package/src/components/field-sync.tsx +48 -40
- package/src/components/field.tsx +56 -5
- package/src/components/floating-panel.tsx +20 -7
- package/src/components/form-actions.tsx +191 -0
- package/src/components/full-bleed-dialog.tsx +4 -0
- package/src/components/hover-menu.tsx +26 -17
- package/src/components/image-grid.tsx +181 -0
- package/src/components/lightbox.tsx +374 -0
- package/src/components/line-items.tsx +385 -0
- package/src/components/list.tsx +82 -19
- package/src/components/loading-state.tsx +47 -0
- package/src/components/menu-item.tsx +7 -2
- package/src/components/mini-calendar.tsx +72 -3
- package/src/components/modal.tsx +65 -3
- package/src/components/month-picker.tsx +181 -26
- package/src/components/multi-entity-combobox.tsx +27 -7
- package/src/components/nav-pills.tsx +8 -1
- package/src/components/number-field.tsx +15 -10
- package/src/components/number-input.tsx +17 -1
- package/src/components/numpad-sheet.tsx +6 -1
- package/src/components/page-header.tsx +42 -5
- package/src/components/passkeys-setting.tsx +331 -0
- package/src/components/picker-sheet.tsx +10 -1
- package/src/components/pie-chart-labels.ts +32 -0
- package/src/components/pie-chart.tsx +479 -0
- package/src/components/popover.tsx +3 -0
- package/src/components/progress-bar.tsx +129 -19
- package/src/components/qr-code.tsx +83 -0
- package/src/components/search-field.tsx +20 -4
- package/src/components/signed-amount.tsx +295 -0
- package/src/components/skeleton.tsx +21 -3
- package/src/components/stat-tile.tsx +11 -3
- package/src/components/swipeable-row.tsx +7 -7
- package/src/components/table.tsx +102 -13
- package/src/components/text-link.tsx +356 -0
- package/src/components/text.tsx +46 -2
- package/src/components/toggle-group.tsx +93 -20
- package/src/components/toggle-legend.tsx +108 -2
- package/src/components/tooltip.tsx +79 -8
- package/src/components/ui.tsx +707 -49
- package/src/components/use-table-state.ts +265 -33
- package/src/data-table.ts +10 -0
- package/src/feedback/feedback-attachment.tsx +10 -7
- package/src/feedback/feedback-dialog.tsx +49 -7
- package/src/feedback/feedback-inbox.tsx +7 -0
- package/src/feedback/feedback-thread.tsx +510 -0
- package/src/feedback.ts +1 -0
- package/src/hooks/use-authed-src.ts +161 -0
- package/src/hooks/use-hotkey.ts +172 -0
- package/src/hooks/use-search-param-state.ts +187 -0
- package/src/i18n/defaults.ts +30 -0
- package/src/i18n/kit-labels.tsx +118 -8
- package/src/i18n/locales/de.ts +161 -0
- package/src/i18n/locales/es.ts +158 -0
- package/src/i18n/locales/fr.ts +161 -0
- package/src/i18n/locales/hu.ts +159 -0
- package/src/i18n/locales/it.ts +159 -0
- package/src/i18n/locales/zh.ts +158 -0
- package/src/index.ts +59 -0
- package/src/lib/format.ts +348 -0
- package/src/lib/qr-encode.ts +420 -0
- package/src/rhf/fields.tsx +1135 -0
- package/src/rhf/line-items.tsx +130 -0
- package/src/rhf.ts +6 -0
- package/src/search/command-palette.tsx +29 -19
- package/src/shell/app-shell.tsx +153 -3
- package/src/shell/auth-layout.tsx +190 -0
- package/src/shell/top-bar-brand.tsx +76 -0
- package/src/shell/topbar-action-menu.tsx +32 -4
- package/src/shell.ts +2 -0
- package/src/tour/tour.tsx +16 -5
- package/src/wizard/use-wizard.ts +8 -4
- package/src/wizard/wizard-context.tsx +41 -1
- package/src/wizard/wizard-step.tsx +75 -6
- package/tokens.css +12 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number, money, percentage, date and relative-time formatting — `Intl`, in one place,
|
|
3
|
+
* for the figures an app prints OUTSIDE a kit component.
|
|
4
|
+
*
|
|
5
|
+
* Each app grew its own: kastlan's `utils/formatters.ts` (a Swiss-pinned
|
|
6
|
+
* `formatNumber`/`formatCurrency`, a date-fns `formatDate`, and a `formatRelativeTime`
|
|
7
|
+
* whose "5m ago" / "3h ago" / "2d ago" stayed English in every language), its tenancy
|
|
8
|
+
* `format.ts` (`formatChf`, a `formatPct` gluing a "+" onto `toFixed(2)`), keksdose's
|
|
9
|
+
* own set. Every one of them is `Intl` with a locale bolted on, so the only thing
|
|
10
|
+
* worth sharing is the locale — and the kit already has one, the provider's.
|
|
11
|
+
*
|
|
12
|
+
* The plain functions take a `locale` (none: the runtime default, as every `Intl` API
|
|
13
|
+
* reads `undefined`). Inside a component, {@link useKitFormat} hands back the same
|
|
14
|
+
* functions bound to the `<UiKitProvider locale>`, so a page formats exactly like the
|
|
15
|
+
* kit components on it.
|
|
16
|
+
*
|
|
17
|
+
* A missing value — `null`, `undefined`, `NaN`, an unparseable date — formats as
|
|
18
|
+
* `empty` (default "—", the dash `StatTile` shows for no value), never as "NaN" or
|
|
19
|
+
* "Invalid Date".
|
|
20
|
+
*/
|
|
21
|
+
/** What every formatter here prints for a missing value, unless told otherwise. */
|
|
22
|
+
declare const EMPTY_FORMATTED_VALUE = "\u2014";
|
|
23
|
+
/** Fraction digits: a number pins both the minimum and the maximum (`2` → "3.10");
|
|
24
|
+
* an object sets them apart (a meter reading, 1–3). */
|
|
25
|
+
type FormatDigits = number | {
|
|
26
|
+
min?: number;
|
|
27
|
+
max?: number;
|
|
28
|
+
};
|
|
29
|
+
interface BaseOptions {
|
|
30
|
+
/** BCP 47 tag. Left out: the runtime default (the hook: the provider's). */
|
|
31
|
+
locale?: string;
|
|
32
|
+
/** Printed for a missing value. Default {@link EMPTY_FORMATTED_VALUE}. */
|
|
33
|
+
empty?: string;
|
|
34
|
+
}
|
|
35
|
+
/** The options every number formatter shares (money and percent carry their own unit). */
|
|
36
|
+
interface FormatNumberBaseOptions extends BaseOptions {
|
|
37
|
+
digits?: FormatDigits;
|
|
38
|
+
/** Compact notation: 12 400 → "12K". */
|
|
39
|
+
compact?: boolean;
|
|
40
|
+
/** `"exceptZero"` for a change ("+3", "−3", "0"). Default: the minus only. */
|
|
41
|
+
signDisplay?: Intl.NumberFormatOptions["signDisplay"];
|
|
42
|
+
/** Any further `Intl.NumberFormat` options, merged over the ones above. */
|
|
43
|
+
options?: Intl.NumberFormatOptions;
|
|
44
|
+
}
|
|
45
|
+
/** The options {@link formatNumber} takes: the shared ones plus a free-text `unit`. */
|
|
46
|
+
interface FormatNumberOptions extends FormatNumberBaseOptions {
|
|
47
|
+
/**
|
|
48
|
+
* A unit printed after the figure, joined by a narrow no-break space (U+202F):
|
|
49
|
+
* "3.400 N", "12 kg", "4,5 m²". lenkbank (P8) prints forces, masses and lengths in
|
|
50
|
+
* every table and summary, and each call site glued `${n} N` together with a plain
|
|
51
|
+
* space — which wraps the "N" onto the next line in a narrow column and reads loose
|
|
52
|
+
* beside the digit grouping. The narrow no-break space is the SI brochure's and the
|
|
53
|
+
* Swiss typesetting rule's separator between value and unit, and it never breaks.
|
|
54
|
+
*
|
|
55
|
+
* Free text, not `Intl`'s `style: "unit"`: that one only knows the ECMA-402
|
|
56
|
+
* sanctioned units (no "N", no "kN", no "m³/h"), so an app would still be gluing.
|
|
57
|
+
* A missing value prints `empty` ALONE — "— N" says there is a newton-shaped
|
|
58
|
+
* nothing, which is noise in a column that already heads its unit.
|
|
59
|
+
*/
|
|
60
|
+
unit?: string;
|
|
61
|
+
}
|
|
62
|
+
interface FormatMoneyOptions extends FormatNumberBaseOptions {
|
|
63
|
+
/** `"symbol"` (default), `"narrowSymbol"`, `"code"` ("CHF 12.00") or `"name"`. */
|
|
64
|
+
currencyDisplay?: Intl.NumberFormatOptions["currencyDisplay"];
|
|
65
|
+
}
|
|
66
|
+
interface FormatPercentOptions extends FormatNumberBaseOptions {
|
|
67
|
+
/**
|
|
68
|
+
* `true` (default): `value` is a RATIO, as `Intl` and `StatTile`'s percent delta read
|
|
69
|
+
* it — `0.12` is 12 %. `false`: it is already a percentage — `12` is 12 %
|
|
70
|
+
* (kastlan's rent `change_pct`).
|
|
71
|
+
*/
|
|
72
|
+
ratio?: boolean;
|
|
73
|
+
}
|
|
74
|
+
/** A number in `locale`: "1,234.5", "1.234,5", "1’234.5" — with `unit`, "3.400 N". */
|
|
75
|
+
declare function formatNumber(value: number | null | undefined, options?: FormatNumberOptions): string;
|
|
76
|
+
/** An amount of `currency` (ISO 4217) in `locale`: "CHF 1’234.50", "1.234,50 €". */
|
|
77
|
+
declare function formatMoney(value: number | null | undefined, currency: string, options?: FormatMoneyOptions): string;
|
|
78
|
+
/** A percentage in `locale` ("12%", "12 %", "+1.25%"). See {@link FormatPercentOptions.ratio}
|
|
79
|
+
* for what `value` means. Default digits: at most one. */
|
|
80
|
+
declare function formatPercent(value: number | null | undefined, options?: FormatPercentOptions): string;
|
|
81
|
+
/**
|
|
82
|
+
* The shapes a date is printed in, each a set of `Intl.DateTimeFormat` options:
|
|
83
|
+
* - `short` / `medium` / `long` / `full` — the date alone, at that `dateStyle`
|
|
84
|
+
* ("08/07/2026", "8 Jul 2026", "8 July 2026", "Wednesday, 8 July 2026" in en-GB).
|
|
85
|
+
* - `dateTime` — medium date and short time: a timestamp ("8 Jul 2026, 14:05").
|
|
86
|
+
* - `time` — the short time alone ("14:05").
|
|
87
|
+
* - `monthYear` — "July 2026".
|
|
88
|
+
*
|
|
89
|
+
* Or pass `Intl.DateTimeFormatOptions` for anything else. A date-fns PATTERN
|
|
90
|
+
* ("dd.MM.yyyy") is deliberately not accepted: a pattern is one locale's order baked
|
|
91
|
+
* into a string, which is how an English page ends up with German dates.
|
|
92
|
+
*/
|
|
93
|
+
type FormatDateStyle = "short" | "medium" | "long" | "full" | "dateTime" | "time" | "monthYear";
|
|
94
|
+
interface FormatDateOptions extends BaseOptions {
|
|
95
|
+
/** An IANA zone ("Europe/Zurich") for a timestamp. Left out: the device's. */
|
|
96
|
+
timeZone?: string;
|
|
97
|
+
}
|
|
98
|
+
/** What {@link formatDate} and {@link formatRelativeTime} accept. */
|
|
99
|
+
type DateInput = string | number | Date | null | undefined;
|
|
100
|
+
/**
|
|
101
|
+
* `value` as a Date, or null. A bare "YYYY-MM-DD" is a CALENDAR day and is read on the
|
|
102
|
+
* local calendar (`parseIsoDate`) — `new Date("2026-07-08")` is UTC midnight, which is
|
|
103
|
+
* the 7th anywhere west of Greenwich. Anything else (a full ISO timestamp, epoch
|
|
104
|
+
* milliseconds, a Date) is an instant and goes through `Date` as is.
|
|
105
|
+
*/
|
|
106
|
+
declare function toDate(value: DateInput): Date | null;
|
|
107
|
+
/**
|
|
108
|
+
* A date or timestamp in `locale`. Takes an ISO date ("2026-07-08", read on the local
|
|
109
|
+
* calendar), an ISO timestamp ("2026-07-08T14:05:00Z"), epoch milliseconds or a Date.
|
|
110
|
+
* `style` defaults to `medium`; see {@link FormatDateStyle}.
|
|
111
|
+
*
|
|
112
|
+
* `formatIsoDate` (`@eifi1/ui-kit/dates`) remains for a plain "YYYY-MM-DD" with an
|
|
113
|
+
* explicit locale; this is its superset for timestamps and the named styles.
|
|
114
|
+
*/
|
|
115
|
+
declare function formatDate(value: DateInput, style?: FormatDateStyle | Intl.DateTimeFormatOptions, options?: FormatDateOptions): string;
|
|
116
|
+
interface FormatRelativeTimeOptions extends BaseOptions {
|
|
117
|
+
/** The moment it is measured from. Default: now. */
|
|
118
|
+
now?: DateInput;
|
|
119
|
+
/** `"auto"` (default): "yesterday", "now", "next week". `"always"`: "1 day ago". */
|
|
120
|
+
numeric?: Intl.RelativeTimeFormatNumeric;
|
|
121
|
+
/** `"long"` (default) "3 hours ago", `"short"` "3 hr. ago", `"narrow"` "3h ago". */
|
|
122
|
+
style?: Intl.RelativeTimeFormatStyle;
|
|
123
|
+
/**
|
|
124
|
+
* Past this many days either way, print the date instead ({@link formatDate} in
|
|
125
|
+
* `absoluteStyle`) — kastlan's feed switches to the date after a week, because "5
|
|
126
|
+
* weeks ago" makes a reader do sums. Default: never.
|
|
127
|
+
*/
|
|
128
|
+
absoluteAfterDays?: number;
|
|
129
|
+
/** The style of that date. Default `medium`. */
|
|
130
|
+
absoluteStyle?: FormatDateStyle | Intl.DateTimeFormatOptions;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* How long ago (or until) `value` is, in `locale`'s words, through
|
|
134
|
+
* `Intl.RelativeTimeFormat`: "3 hours ago", "vor 3 Stunden", "il y a 3 heures",
|
|
135
|
+
* "in 2 days", and under 45 seconds "now". The unit is picked by size — seconds,
|
|
136
|
+
* minutes, hours, days, weeks, months, years — and the count is rounded.
|
|
137
|
+
*/
|
|
138
|
+
declare function formatRelativeTime(value: DateInput, options?: FormatRelativeTimeOptions): string;
|
|
139
|
+
type WithoutLocale<T> = Omit<T, "locale"> & {
|
|
140
|
+
locale?: string;
|
|
141
|
+
};
|
|
142
|
+
/** What {@link useKitFormat} returns: the formatters above with the provider's locale
|
|
143
|
+
* as their default (an explicit `locale` in the options still wins). */
|
|
144
|
+
interface KitFormat {
|
|
145
|
+
/** The locale they format in — the provider's, or `undefined` (runtime default). */
|
|
146
|
+
locale: string | undefined;
|
|
147
|
+
formatNumber: (value: number | null | undefined, options?: WithoutLocale<FormatNumberOptions>) => string;
|
|
148
|
+
formatMoney: (value: number | null | undefined, currency: string, options?: WithoutLocale<FormatMoneyOptions>) => string;
|
|
149
|
+
formatPercent: (value: number | null | undefined, options?: WithoutLocale<FormatPercentOptions>) => string;
|
|
150
|
+
formatDate: (value: DateInput, style?: FormatDateStyle | Intl.DateTimeFormatOptions, options?: WithoutLocale<FormatDateOptions>) => string;
|
|
151
|
+
formatRelativeTime: (value: DateInput, options?: WithoutLocale<FormatRelativeTimeOptions>) => string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The formatters, bound to the `<UiKitProvider locale>` (or to `locale`, which wins
|
|
155
|
+
* over it — the same precedence as a component's `locale` prop):
|
|
156
|
+
*
|
|
157
|
+
* const { formatMoney, formatRelativeTime } = useKitFormat();
|
|
158
|
+
* formatMoney(row.balance, "CHF");
|
|
159
|
+
*/
|
|
160
|
+
declare function useKitFormat(locale?: string): KitFormat;
|
|
161
|
+
|
|
162
|
+
export { type DateInput, EMPTY_FORMATTED_VALUE, type FormatDateOptions, type FormatDateStyle, type FormatDigits, type FormatMoneyOptions, type FormatNumberBaseOptions, type FormatNumberOptions, type FormatPercentOptions, type FormatRelativeTimeOptions, type KitFormat, formatDate, formatMoney, formatNumber, formatPercent, formatRelativeTime, toDate, useKitFormat };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { useKitLocale } from "../i18n/kit-labels.js";
|
|
4
|
+
import { parseIsoDate } from "./dates.js";
|
|
5
|
+
const EMPTY_FORMATTED_VALUE = "\u2014";
|
|
6
|
+
const UNIT_SEPARATOR = "\u202F";
|
|
7
|
+
const numberFormatters = /* @__PURE__ */ new Map();
|
|
8
|
+
function numberFormatter(locale, options) {
|
|
9
|
+
const key = `${locale ?? ""}|${JSON.stringify(options)}`;
|
|
10
|
+
let f = numberFormatters.get(key);
|
|
11
|
+
if (!f) {
|
|
12
|
+
f = new Intl.NumberFormat(locale, options);
|
|
13
|
+
numberFormatters.set(key, f);
|
|
14
|
+
}
|
|
15
|
+
return f;
|
|
16
|
+
}
|
|
17
|
+
function digitOptions(digits) {
|
|
18
|
+
if (digits === void 0) return {};
|
|
19
|
+
if (typeof digits === "number") return { minimumFractionDigits: digits, maximumFractionDigits: digits };
|
|
20
|
+
return {
|
|
21
|
+
...digits.min !== void 0 ? { minimumFractionDigits: digits.min } : null,
|
|
22
|
+
...digits.max !== void 0 ? { maximumFractionDigits: digits.max } : null
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function numberOptions(o) {
|
|
26
|
+
return {
|
|
27
|
+
...digitOptions(o.digits),
|
|
28
|
+
...o.compact ? { notation: "compact" } : null,
|
|
29
|
+
...o.signDisplay ? { signDisplay: o.signDisplay } : null,
|
|
30
|
+
...o.options
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function isMissing(value) {
|
|
34
|
+
return value === null || value === void 0 || Number.isNaN(value);
|
|
35
|
+
}
|
|
36
|
+
function formatNumber(value, options = {}) {
|
|
37
|
+
if (isMissing(value)) return options.empty ?? EMPTY_FORMATTED_VALUE;
|
|
38
|
+
const figure = numberFormatter(options.locale, numberOptions(options)).format(value);
|
|
39
|
+
return options.unit ? `${figure}${UNIT_SEPARATOR}${options.unit}` : figure;
|
|
40
|
+
}
|
|
41
|
+
function formatMoney(value, currency, options = {}) {
|
|
42
|
+
if (isMissing(value)) return options.empty ?? EMPTY_FORMATTED_VALUE;
|
|
43
|
+
return numberFormatter(options.locale, {
|
|
44
|
+
style: "currency",
|
|
45
|
+
currency,
|
|
46
|
+
...options.currencyDisplay ? { currencyDisplay: options.currencyDisplay } : null,
|
|
47
|
+
...numberOptions(options)
|
|
48
|
+
}).format(value);
|
|
49
|
+
}
|
|
50
|
+
function formatPercent(value, options = {}) {
|
|
51
|
+
if (isMissing(value)) return options.empty ?? EMPTY_FORMATTED_VALUE;
|
|
52
|
+
const ratio = options.ratio === false ? value / 100 : value;
|
|
53
|
+
return numberFormatter(options.locale, {
|
|
54
|
+
style: "percent",
|
|
55
|
+
...options.digits === void 0 ? { maximumFractionDigits: 1 } : null,
|
|
56
|
+
...numberOptions(options)
|
|
57
|
+
}).format(ratio);
|
|
58
|
+
}
|
|
59
|
+
const DATE_STYLES = {
|
|
60
|
+
short: { dateStyle: "short" },
|
|
61
|
+
medium: { dateStyle: "medium" },
|
|
62
|
+
long: { dateStyle: "long" },
|
|
63
|
+
full: { dateStyle: "full" },
|
|
64
|
+
dateTime: { dateStyle: "medium", timeStyle: "short" },
|
|
65
|
+
time: { timeStyle: "short" },
|
|
66
|
+
monthYear: { month: "long", year: "numeric" }
|
|
67
|
+
};
|
|
68
|
+
function toDate(value) {
|
|
69
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
70
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) return parseIsoDate(value);
|
|
71
|
+
const d = value instanceof Date ? value : new Date(value);
|
|
72
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
73
|
+
}
|
|
74
|
+
const dateFormatters = /* @__PURE__ */ new Map();
|
|
75
|
+
function dateFormatter(locale, options) {
|
|
76
|
+
const key = `${locale ?? ""}|${JSON.stringify(options)}`;
|
|
77
|
+
let f = dateFormatters.get(key);
|
|
78
|
+
if (!f) {
|
|
79
|
+
f = new Intl.DateTimeFormat(locale, options);
|
|
80
|
+
dateFormatters.set(key, f);
|
|
81
|
+
}
|
|
82
|
+
return f;
|
|
83
|
+
}
|
|
84
|
+
function formatDate(value, style = "medium", options = {}) {
|
|
85
|
+
const d = toDate(value);
|
|
86
|
+
if (!d) return options.empty ?? EMPTY_FORMATTED_VALUE;
|
|
87
|
+
const base = typeof style === "string" ? DATE_STYLES[style] : style;
|
|
88
|
+
return dateFormatter(options.locale, options.timeZone ? { ...base, timeZone: options.timeZone } : base).format(d);
|
|
89
|
+
}
|
|
90
|
+
const SECOND = 1e3;
|
|
91
|
+
const MINUTE = 60 * SECOND;
|
|
92
|
+
const HOUR = 60 * MINUTE;
|
|
93
|
+
const DAY = 24 * HOUR;
|
|
94
|
+
const RELATIVE_STEPS = [
|
|
95
|
+
[45 * SECOND, "second", SECOND],
|
|
96
|
+
[45 * MINUTE, "minute", MINUTE],
|
|
97
|
+
[22 * HOUR, "hour", HOUR],
|
|
98
|
+
[6.5 * DAY, "day", DAY],
|
|
99
|
+
[26 * DAY, "week", 7 * DAY],
|
|
100
|
+
[320 * DAY, "month", 30.44 * DAY],
|
|
101
|
+
[Infinity, "year", 365.25 * DAY]
|
|
102
|
+
];
|
|
103
|
+
const relativeFormatters = /* @__PURE__ */ new Map();
|
|
104
|
+
function relativeFormatter(locale, numeric, style) {
|
|
105
|
+
const key = `${locale ?? ""}|${numeric}|${style}`;
|
|
106
|
+
let f = relativeFormatters.get(key);
|
|
107
|
+
if (!f) {
|
|
108
|
+
f = new Intl.RelativeTimeFormat(locale, { numeric, style });
|
|
109
|
+
relativeFormatters.set(key, f);
|
|
110
|
+
}
|
|
111
|
+
return f;
|
|
112
|
+
}
|
|
113
|
+
function formatRelativeTime(value, options = {}) {
|
|
114
|
+
const d = toDate(value);
|
|
115
|
+
if (!d) return options.empty ?? EMPTY_FORMATTED_VALUE;
|
|
116
|
+
const now = toDate(options.now ?? /* @__PURE__ */ new Date()) ?? /* @__PURE__ */ new Date();
|
|
117
|
+
const diff = d.getTime() - now.getTime();
|
|
118
|
+
const abs = Math.abs(diff);
|
|
119
|
+
if (options.absoluteAfterDays !== void 0 && abs > options.absoluteAfterDays * DAY) {
|
|
120
|
+
return formatDate(d, options.absoluteStyle ?? "medium", options);
|
|
121
|
+
}
|
|
122
|
+
const rtf = relativeFormatter(options.locale, options.numeric ?? "auto", options.style ?? "long");
|
|
123
|
+
const [, unit, size] = RELATIVE_STEPS.find(([limit]) => abs < limit);
|
|
124
|
+
const count = unit === "second" ? 0 : Math.round(diff / size);
|
|
125
|
+
return rtf.format(count + 0, unit);
|
|
126
|
+
}
|
|
127
|
+
function useKitFormat(locale) {
|
|
128
|
+
const kitLocale = useKitLocale(locale);
|
|
129
|
+
return useMemo(
|
|
130
|
+
() => ({
|
|
131
|
+
locale: kitLocale,
|
|
132
|
+
formatNumber: (value, options) => formatNumber(value, { locale: kitLocale, ...options }),
|
|
133
|
+
formatMoney: (value, currency, options) => formatMoney(value, currency, { locale: kitLocale, ...options }),
|
|
134
|
+
formatPercent: (value, options) => formatPercent(value, { locale: kitLocale, ...options }),
|
|
135
|
+
formatDate: (value, style, options) => formatDate(value, style, { locale: kitLocale, ...options }),
|
|
136
|
+
formatRelativeTime: (value, options) => formatRelativeTime(value, { locale: kitLocale, ...options })
|
|
137
|
+
}),
|
|
138
|
+
[kitLocale]
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
EMPTY_FORMATTED_VALUE,
|
|
143
|
+
formatDate,
|
|
144
|
+
formatMoney,
|
|
145
|
+
formatNumber,
|
|
146
|
+
formatPercent,
|
|
147
|
+
formatRelativeTime,
|
|
148
|
+
toDate,
|
|
149
|
+
useKitFormat
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/format.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useKitLocale } from \"../i18n/kit-labels\";\nimport { parseIsoDate } from \"./dates\";\n\n/**\n * Number, money, percentage, date and relative-time formatting — `Intl`, in one place,\n * for the figures an app prints OUTSIDE a kit component.\n *\n * Each app grew its own: kastlan's `utils/formatters.ts` (a Swiss-pinned\n * `formatNumber`/`formatCurrency`, a date-fns `formatDate`, and a `formatRelativeTime`\n * whose \"5m ago\" / \"3h ago\" / \"2d ago\" stayed English in every language), its tenancy\n * `format.ts` (`formatChf`, a `formatPct` gluing a \"+\" onto `toFixed(2)`), keksdose's\n * own set. Every one of them is `Intl` with a locale bolted on, so the only thing\n * worth sharing is the locale — and the kit already has one, the provider's.\n *\n * The plain functions take a `locale` (none: the runtime default, as every `Intl` API\n * reads `undefined`). Inside a component, {@link useKitFormat} hands back the same\n * functions bound to the `<UiKitProvider locale>`, so a page formats exactly like the\n * kit components on it.\n *\n * A missing value — `null`, `undefined`, `NaN`, an unparseable date — formats as\n * `empty` (default \"—\", the dash `StatTile` shows for no value), never as \"NaN\" or\n * \"Invalid Date\".\n */\n\n/** What every formatter here prints for a missing value, unless told otherwise. */\nexport const EMPTY_FORMATTED_VALUE = \"—\";\n\n/** Fraction digits: a number pins both the minimum and the maximum (`2` → \"3.10\");\n * an object sets them apart (a meter reading, 1–3). */\nexport type FormatDigits = number | { min?: number; max?: number };\n\ninterface BaseOptions {\n /** BCP 47 tag. Left out: the runtime default (the hook: the provider's). */\n locale?: string;\n /** Printed for a missing value. Default {@link EMPTY_FORMATTED_VALUE}. */\n empty?: string;\n}\n\n/** The options every number formatter shares (money and percent carry their own unit). */\nexport interface FormatNumberBaseOptions extends BaseOptions {\n digits?: FormatDigits;\n /** Compact notation: 12 400 → \"12K\". */\n compact?: boolean;\n /** `\"exceptZero\"` for a change (\"+3\", \"−3\", \"0\"). Default: the minus only. */\n signDisplay?: Intl.NumberFormatOptions[\"signDisplay\"];\n /** Any further `Intl.NumberFormat` options, merged over the ones above. */\n options?: Intl.NumberFormatOptions;\n}\n\n/** The options {@link formatNumber} takes: the shared ones plus a free-text `unit`. */\nexport interface FormatNumberOptions extends FormatNumberBaseOptions {\n /**\n * A unit printed after the figure, joined by a narrow no-break space (U+202F):\n * \"3.400 N\", \"12 kg\", \"4,5 m²\". lenkbank (P8) prints forces, masses and lengths in\n * every table and summary, and each call site glued `${n} N` together with a plain\n * space — which wraps the \"N\" onto the next line in a narrow column and reads loose\n * beside the digit grouping. The narrow no-break space is the SI brochure's and the\n * Swiss typesetting rule's separator between value and unit, and it never breaks.\n *\n * Free text, not `Intl`'s `style: \"unit\"`: that one only knows the ECMA-402\n * sanctioned units (no \"N\", no \"kN\", no \"m³/h\"), so an app would still be gluing.\n * A missing value prints `empty` ALONE — \"— N\" says there is a newton-shaped\n * nothing, which is noise in a column that already heads its unit.\n */\n unit?: string;\n}\n\n/** Narrow no-break space (U+202F): between a figure and its {@link FormatNumberOptions.unit}. */\nconst UNIT_SEPARATOR = \"\\u202F\";\n\nexport interface FormatMoneyOptions extends FormatNumberBaseOptions {\n /** `\"symbol\"` (default), `\"narrowSymbol\"`, `\"code\"` (\"CHF 12.00\") or `\"name\"`. */\n currencyDisplay?: Intl.NumberFormatOptions[\"currencyDisplay\"];\n}\n\nexport interface FormatPercentOptions extends FormatNumberBaseOptions {\n /**\n * `true` (default): `value` is a RATIO, as `Intl` and `StatTile`'s percent delta read\n * it — `0.12` is 12 %. `false`: it is already a percentage — `12` is 12 %\n * (kastlan's rent `change_pct`).\n */\n ratio?: boolean;\n}\n\n// One formatter per option set: a table formats the same way thousands of times, and\n// constructing an Intl formatter is the expensive part.\nconst numberFormatters = new Map<string, Intl.NumberFormat>();\nfunction numberFormatter(locale: string | undefined, options: Intl.NumberFormatOptions): Intl.NumberFormat {\n const key = `${locale ?? \"\"}|${JSON.stringify(options)}`;\n let f = numberFormatters.get(key);\n if (!f) {\n f = new Intl.NumberFormat(locale, options);\n numberFormatters.set(key, f);\n }\n return f;\n}\n\nfunction digitOptions(digits: FormatDigits | undefined): Intl.NumberFormatOptions {\n if (digits === undefined) return {};\n if (typeof digits === \"number\") return { minimumFractionDigits: digits, maximumFractionDigits: digits };\n return {\n ...(digits.min !== undefined ? { minimumFractionDigits: digits.min } : null),\n ...(digits.max !== undefined ? { maximumFractionDigits: digits.max } : null),\n };\n}\n\nfunction numberOptions(o: FormatNumberBaseOptions): Intl.NumberFormatOptions {\n return {\n ...digitOptions(o.digits),\n ...(o.compact ? { notation: \"compact\" } : null),\n ...(o.signDisplay ? { signDisplay: o.signDisplay } : null),\n ...o.options,\n };\n}\n\nfunction isMissing(value: number | null | undefined): value is null | undefined {\n return value === null || value === undefined || Number.isNaN(value);\n}\n\n/** A number in `locale`: \"1,234.5\", \"1.234,5\", \"1’234.5\" — with `unit`, \"3.400 N\". */\nexport function formatNumber(value: number | null | undefined, options: FormatNumberOptions = {}): string {\n if (isMissing(value)) return options.empty ?? EMPTY_FORMATTED_VALUE;\n const figure = numberFormatter(options.locale, numberOptions(options)).format(value);\n return options.unit ? `${figure}${UNIT_SEPARATOR}${options.unit}` : figure;\n}\n\n/** An amount of `currency` (ISO 4217) in `locale`: \"CHF 1’234.50\", \"1.234,50 €\". */\nexport function formatMoney(\n value: number | null | undefined,\n currency: string,\n options: FormatMoneyOptions = {},\n): string {\n if (isMissing(value)) return options.empty ?? EMPTY_FORMATTED_VALUE;\n return numberFormatter(options.locale, {\n style: \"currency\",\n currency,\n ...(options.currencyDisplay ? { currencyDisplay: options.currencyDisplay } : null),\n ...numberOptions(options),\n }).format(value);\n}\n\n/** A percentage in `locale` (\"12%\", \"12 %\", \"+1.25%\"). See {@link FormatPercentOptions.ratio}\n * for what `value` means. Default digits: at most one. */\nexport function formatPercent(value: number | null | undefined, options: FormatPercentOptions = {}): string {\n if (isMissing(value)) return options.empty ?? EMPTY_FORMATTED_VALUE;\n const ratio = options.ratio === false ? value / 100 : value;\n return numberFormatter(options.locale, {\n style: \"percent\",\n ...(options.digits === undefined ? { maximumFractionDigits: 1 } : null),\n ...numberOptions(options),\n }).format(ratio);\n}\n\n/* ── Dates ─────────────────────────────────────────────────────────────────── */\n\n/**\n * The shapes a date is printed in, each a set of `Intl.DateTimeFormat` options:\n * - `short` / `medium` / `long` / `full` — the date alone, at that `dateStyle`\n * (\"08/07/2026\", \"8 Jul 2026\", \"8 July 2026\", \"Wednesday, 8 July 2026\" in en-GB).\n * - `dateTime` — medium date and short time: a timestamp (\"8 Jul 2026, 14:05\").\n * - `time` — the short time alone (\"14:05\").\n * - `monthYear` — \"July 2026\".\n *\n * Or pass `Intl.DateTimeFormatOptions` for anything else. A date-fns PATTERN\n * (\"dd.MM.yyyy\") is deliberately not accepted: a pattern is one locale's order baked\n * into a string, which is how an English page ends up with German dates.\n */\nexport type FormatDateStyle = \"short\" | \"medium\" | \"long\" | \"full\" | \"dateTime\" | \"time\" | \"monthYear\";\n\nconst DATE_STYLES: Record<FormatDateStyle, Intl.DateTimeFormatOptions> = {\n short: { dateStyle: \"short\" },\n medium: { dateStyle: \"medium\" },\n long: { dateStyle: \"long\" },\n full: { dateStyle: \"full\" },\n dateTime: { dateStyle: \"medium\", timeStyle: \"short\" },\n time: { timeStyle: \"short\" },\n monthYear: { month: \"long\", year: \"numeric\" },\n};\n\nexport interface FormatDateOptions extends BaseOptions {\n /** An IANA zone (\"Europe/Zurich\") for a timestamp. Left out: the device's. */\n timeZone?: string;\n}\n\n/** What {@link formatDate} and {@link formatRelativeTime} accept. */\nexport type DateInput = string | number | Date | null | undefined;\n\n/**\n * `value` as a Date, or null. A bare \"YYYY-MM-DD\" is a CALENDAR day and is read on the\n * local calendar (`parseIsoDate`) — `new Date(\"2026-07-08\")` is UTC midnight, which is\n * the 7th anywhere west of Greenwich. Anything else (a full ISO timestamp, epoch\n * milliseconds, a Date) is an instant and goes through `Date` as is.\n */\nexport function toDate(value: DateInput): Date | null {\n if (value === null || value === undefined || value === \"\") return null;\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return parseIsoDate(value);\n const d = value instanceof Date ? value : new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n}\n\nconst dateFormatters = new Map<string, Intl.DateTimeFormat>();\nfunction dateFormatter(locale: string | undefined, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = `${locale ?? \"\"}|${JSON.stringify(options)}`;\n let f = dateFormatters.get(key);\n if (!f) {\n f = new Intl.DateTimeFormat(locale, options);\n dateFormatters.set(key, f);\n }\n return f;\n}\n\n/**\n * A date or timestamp in `locale`. Takes an ISO date (\"2026-07-08\", read on the local\n * calendar), an ISO timestamp (\"2026-07-08T14:05:00Z\"), epoch milliseconds or a Date.\n * `style` defaults to `medium`; see {@link FormatDateStyle}.\n *\n * `formatIsoDate` (`@eifi1/ui-kit/dates`) remains for a plain \"YYYY-MM-DD\" with an\n * explicit locale; this is its superset for timestamps and the named styles.\n */\nexport function formatDate(\n value: DateInput,\n style: FormatDateStyle | Intl.DateTimeFormatOptions = \"medium\",\n options: FormatDateOptions = {},\n): string {\n const d = toDate(value);\n if (!d) return options.empty ?? EMPTY_FORMATTED_VALUE;\n const base = typeof style === \"string\" ? DATE_STYLES[style] : style;\n return dateFormatter(options.locale, options.timeZone ? { ...base, timeZone: options.timeZone } : base).format(d);\n}\n\nexport interface FormatRelativeTimeOptions extends BaseOptions {\n /** The moment it is measured from. Default: now. */\n now?: DateInput;\n /** `\"auto\"` (default): \"yesterday\", \"now\", \"next week\". `\"always\"`: \"1 day ago\". */\n numeric?: Intl.RelativeTimeFormatNumeric;\n /** `\"long\"` (default) \"3 hours ago\", `\"short\"` \"3 hr. ago\", `\"narrow\"` \"3h ago\". */\n style?: Intl.RelativeTimeFormatStyle;\n /**\n * Past this many days either way, print the date instead ({@link formatDate} in\n * `absoluteStyle`) — kastlan's feed switches to the date after a week, because \"5\n * weeks ago\" makes a reader do sums. Default: never.\n */\n absoluteAfterDays?: number;\n /** The style of that date. Default `medium`. */\n absoluteStyle?: FormatDateStyle | Intl.DateTimeFormatOptions;\n}\n\nconst SECOND = 1000;\nconst MINUTE = 60 * SECOND;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\n/** [the largest |difference| a unit is used for, the unit, its length]. */\nconst RELATIVE_STEPS: Array<[number, Intl.RelativeTimeFormatUnit, number]> = [\n [45 * SECOND, \"second\", SECOND],\n [45 * MINUTE, \"minute\", MINUTE],\n [22 * HOUR, \"hour\", HOUR],\n [6.5 * DAY, \"day\", DAY],\n [26 * DAY, \"week\", 7 * DAY],\n [320 * DAY, \"month\", 30.44 * DAY],\n [Infinity, \"year\", 365.25 * DAY],\n];\n\nconst relativeFormatters = new Map<string, Intl.RelativeTimeFormat>();\nfunction relativeFormatter(\n locale: string | undefined,\n numeric: Intl.RelativeTimeFormatNumeric,\n style: Intl.RelativeTimeFormatStyle,\n): Intl.RelativeTimeFormat {\n const key = `${locale ?? \"\"}|${numeric}|${style}`;\n let f = relativeFormatters.get(key);\n if (!f) {\n f = new Intl.RelativeTimeFormat(locale, { numeric, style });\n relativeFormatters.set(key, f);\n }\n return f;\n}\n\n/**\n * How long ago (or until) `value` is, in `locale`'s words, through\n * `Intl.RelativeTimeFormat`: \"3 hours ago\", \"vor 3 Stunden\", \"il y a 3 heures\",\n * \"in 2 days\", and under 45 seconds \"now\". The unit is picked by size — seconds,\n * minutes, hours, days, weeks, months, years — and the count is rounded.\n */\nexport function formatRelativeTime(value: DateInput, options: FormatRelativeTimeOptions = {}): string {\n const d = toDate(value);\n if (!d) return options.empty ?? EMPTY_FORMATTED_VALUE;\n const now = toDate(options.now ?? new Date()) ?? new Date();\n const diff = d.getTime() - now.getTime();\n const abs = Math.abs(diff);\n if (options.absoluteAfterDays !== undefined && abs > options.absoluteAfterDays * DAY) {\n return formatDate(d, options.absoluteStyle ?? \"medium\", options);\n }\n const rtf = relativeFormatter(options.locale, options.numeric ?? \"auto\", options.style ?? \"long\");\n const [, unit, size] = RELATIVE_STEPS.find(([limit]) => abs < limit)!;\n // Under 45 seconds is \"now\" rather than \"12 seconds ago\": a feed that ticks through\n // the seconds is noise, and nothing reads a timestamp to the second.\n const count = unit === \"second\" ? 0 : Math.round(diff / size);\n // `+ 0` folds -0 into 0: `format(-0, …)` says \"0 seconds ago\" instead of \"now\".\n return rtf.format(count + 0, unit);\n}\n\n/* ── Bound to the provider ─────────────────────────────────────────────────── */\n\ntype WithoutLocale<T> = Omit<T, \"locale\"> & { locale?: string };\n\n/** What {@link useKitFormat} returns: the formatters above with the provider's locale\n * as their default (an explicit `locale` in the options still wins). */\nexport interface KitFormat {\n /** The locale they format in — the provider's, or `undefined` (runtime default). */\n locale: string | undefined;\n formatNumber: (value: number | null | undefined, options?: WithoutLocale<FormatNumberOptions>) => string;\n formatMoney: (\n value: number | null | undefined,\n currency: string,\n options?: WithoutLocale<FormatMoneyOptions>,\n ) => string;\n formatPercent: (value: number | null | undefined, options?: WithoutLocale<FormatPercentOptions>) => string;\n formatDate: (\n value: DateInput,\n style?: FormatDateStyle | Intl.DateTimeFormatOptions,\n options?: WithoutLocale<FormatDateOptions>,\n ) => string;\n formatRelativeTime: (value: DateInput, options?: WithoutLocale<FormatRelativeTimeOptions>) => string;\n}\n\n/**\n * The formatters, bound to the `<UiKitProvider locale>` (or to `locale`, which wins\n * over it — the same precedence as a component's `locale` prop):\n *\n * const { formatMoney, formatRelativeTime } = useKitFormat();\n * formatMoney(row.balance, \"CHF\");\n */\nexport function useKitFormat(locale?: string): KitFormat {\n const kitLocale = useKitLocale(locale);\n return useMemo<KitFormat>(\n () => ({\n locale: kitLocale,\n formatNumber: (value, options) => formatNumber(value, { locale: kitLocale, ...options }),\n formatMoney: (value, currency, options) => formatMoney(value, currency, { locale: kitLocale, ...options }),\n formatPercent: (value, options) => formatPercent(value, { locale: kitLocale, ...options }),\n formatDate: (value, style, options) => formatDate(value, style, { locale: kitLocale, ...options }),\n formatRelativeTime: (value, options) => formatRelativeTime(value, { locale: kitLocale, ...options }),\n }),\n [kitLocale],\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A QR code encoder: text in, a square of dark / light modules out (ISO/IEC 18004).
|
|
3
|
+
*
|
|
4
|
+
* WHY IT IS IN THE KIT rather than a dependency. `TwoFactorSetting` needs one QR code —
|
|
5
|
+
* an `otpauth://` URI, around 100–200 bytes — and every app that shows it was pulling
|
|
6
|
+
* in `qrcode.react` plus `react-dom/server` to render that one code to a static SVG
|
|
7
|
+
* string, then base64-encoding it so the kit could draw it back as an `<img>`.
|
|
8
|
+
* A dependency here would land in all three apps whether they show 2FA or not; an
|
|
9
|
+
* optional peer would mean a lazy import and a loading state for something that is
|
|
10
|
+
* a few hundred lines of arithmetic. So it is those lines: pure, no imports, no module
|
|
11
|
+
* state beyond two constant tables, safe under `sideEffects: false`, and
|
|
12
|
+
* tree-shaken out of every app that never renders a {@link QrCode}.
|
|
13
|
+
*
|
|
14
|
+
* Scope, chosen for what the kit renders: BYTE mode only (UTF-8), all 40 versions,
|
|
15
|
+
* all four error-correction levels, automatic version and mask selection. Numeric,
|
|
16
|
+
* alphanumeric and Kanji modes are denser for their alphabets but would not shrink an
|
|
17
|
+
* otpauth URI (lower-case letters are not in the alphanumeric set). The algorithm and
|
|
18
|
+
* its penalty rules follow the reference implementation by Project Nayuki (MIT),
|
|
19
|
+
* which is also what `qrcode.react` wraps — so for the same input, level and boost
|
|
20
|
+
* the modules match that library's output exactly (the test suite pins a matrix).
|
|
21
|
+
*/
|
|
22
|
+
type QrErrorCorrection = "L" | "M" | "Q" | "H";
|
|
23
|
+
interface QrEncodeOptions {
|
|
24
|
+
/** How much of the code may be damaged and still read. Default `"M"` (~15 %). */
|
|
25
|
+
errorCorrection?: QrErrorCorrection;
|
|
26
|
+
/** Raise the level as far as the chosen version still fits, for free. Default true. */
|
|
27
|
+
boostErrorCorrection?: boolean;
|
|
28
|
+
}
|
|
29
|
+
interface QrMatrix {
|
|
30
|
+
/** 1–40. */
|
|
31
|
+
version: number;
|
|
32
|
+
/** Modules per side: `version * 4 + 17`. */
|
|
33
|
+
size: number;
|
|
34
|
+
/** The level actually used — higher than asked when boosted. */
|
|
35
|
+
errorCorrection: QrErrorCorrection;
|
|
36
|
+
/** 0–7. */
|
|
37
|
+
mask: number;
|
|
38
|
+
/** `modules[y][x]`, true = dark. No quiet zone: the renderer adds its own. */
|
|
39
|
+
modules: boolean[][];
|
|
40
|
+
}
|
|
41
|
+
/** Encode `text` (as UTF-8 bytes). Throws a `RangeError` past version 40's capacity. */
|
|
42
|
+
declare function encodeQr(text: string, options?: QrEncodeOptions): QrMatrix;
|
|
43
|
+
|
|
44
|
+
export { type QrEncodeOptions, type QrErrorCorrection, type QrMatrix, encodeQr };
|