basalt-ui 1.28.0 → 1.29.1
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/AGENTS.md +12 -19
- package/CHANGELOG.md +23 -0
- package/MIGRATING.md +171 -7
- package/README.md +66 -105
- package/agent/rules/basalt-batteries.md +60 -183
- package/agent/rules/basalt-charts.md +56 -75
- package/agent/rules/basalt-controls.md +38 -69
- package/agent/rules/basalt-mantine.md +58 -103
- package/agent/rules/basalt-state.md +42 -60
- package/agent/rules/basalt-tokens.md +46 -67
- package/agent/skills/basalt-app/SKILL.md +2 -0
- package/agent/templates/CLAUDE-block.md.tpl +14 -14
- package/agent/templates/query-client.ts.tpl +1 -1
- package/configs/oxlint-plugin.js +84 -254
- package/configs/oxlint-plugin.test.ts +23 -266
- package/configs/oxlint.json +0 -2
- package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
- package/dist/agent/ai-sdk-transport.js +7 -4
- package/dist/agent/coalesce.js +1 -1
- package/dist/agent/merge.js +1 -1
- package/dist/agent/transport.d.ts +13 -2
- package/dist/agent/transport.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.d.ts +12 -1
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +20 -6
- package/dist/agent-chat/index.d.ts +1 -1
- package/dist/agent-chat/index.d.ts.map +1 -1
- package/dist/agent-chat/relative-time.js +1 -1
- package/dist/agent-chat/thread-detail-panel.js +1 -1
- package/dist/agent-chat/thread-feed-row.d.ts +32 -7
- package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed-row.js +121 -86
- package/dist/agent-chat/thread-feed.d.ts +11 -1
- package/dist/agent-chat/thread-feed.d.ts.map +1 -1
- package/dist/agent-chat/thread-feed.js +10 -6
- package/dist/agent-chat/thread-message.js +1 -1
- package/dist/agent-chat/virtualize.d.ts +39 -0
- package/dist/agent-chat/virtualize.d.ts.map +1 -1
- package/dist/agent-chat/virtualize.js +15 -0
- package/dist/charts/index.d.ts +3 -3
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +8 -4
- package/dist/charts/kinds/BandStrip.js +1 -1
- package/dist/charts/kinds/Bars.d.ts +1 -3
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +1 -1
- package/dist/charts/kinds/DualPanel.d.ts +3 -1
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +1 -1
- package/dist/charts/kinds/StackedArea.js +1 -1
- package/dist/charts/kinds/ZonedLine.d.ts +1 -3
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.d.ts +21 -2
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +34 -2
- package/dist/charts/primitives/ChartCard.d.ts +20 -4
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +62 -33
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +8 -1
- package/dist/charts/primitives/ChartPending.d.ts +12 -6
- package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
- package/dist/charts/primitives/ChartPending.js +1 -1
- package/dist/charts/utils/format.d.ts +5 -92
- package/dist/charts/utils/format.d.ts.map +1 -1
- package/dist/charts/utils/format.js +10 -80
- package/dist/cli/audit-allows.d.ts +24 -0
- package/dist/cli/audit-allows.d.ts.map +1 -0
- package/dist/cli/audit-allows.js +240 -0
- package/dist/cli/check-theme.d.ts +10 -0
- package/dist/cli/check-theme.d.ts.map +1 -0
- package/dist/cli/check-theme.js +139 -0
- package/dist/cli/doctor.d.ts +47 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +197 -0
- package/dist/cli/guard-hook.d.ts +9 -0
- package/dist/cli/guard-hook.d.ts.map +1 -0
- package/dist/cli/guard-hook.js +93 -0
- package/dist/cli/help.d.ts +9 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +53 -0
- package/dist/cli/index.d.ts +360 -270
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +85 -1901
- package/dist/cli/init.d.ts +4 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +165 -0
- package/dist/cli/sync.d.ts +43 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +206 -0
- package/dist/cli/tokens-css.d.ts +51 -0
- package/dist/cli/tokens-css.d.ts.map +1 -0
- package/dist/cli/tokens-css.js +165 -0
- package/dist/commands/define-commands.js +1 -1
- package/dist/commands/define-overlays.js +1 -1
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +6 -0
- package/dist/commands/overlays-mount.d.ts +7 -9
- package/dist/commands/overlays-mount.d.ts.map +1 -1
- package/dist/commands/shell-bridge.d.ts +57 -0
- package/dist/commands/shell-bridge.d.ts.map +1 -0
- package/dist/commands/shell-bridge.js +32 -0
- package/dist/common/errors.d.ts +35 -0
- package/dist/common/errors.d.ts.map +1 -1
- package/dist/common/errors.js +29 -1
- package/dist/common/index.d.ts +2 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/common/index.js +6 -0
- package/dist/common/is-dev.d.ts.map +1 -0
- package/dist/{motion/index.d.ts → common/motion.d.ts} +1 -1
- package/dist/common/motion.d.ts.map +1 -0
- package/dist/common/props.d.ts +1 -1
- package/dist/common/props.d.ts.map +1 -1
- package/dist/common/validate.js +1 -1
- package/dist/content/markdown.d.ts +3 -4
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +1 -4
- package/dist/controls/actions.d.ts +1 -1
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/panel-row.js +1 -1
- package/dist/controls/range-filter.js +1 -1
- package/dist/dashboard/delta-badge.d.ts +18 -10
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +33 -1
- package/dist/dashboard/empty-state.d.ts +1 -4
- package/dist/dashboard/empty-state.d.ts.map +1 -1
- package/dist/dashboard/empty-state.js +1 -2
- package/dist/dashboard/index.d.ts +4 -2
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +7 -1
- package/dist/dashboard/query-state.d.ts +4 -13
- package/dist/dashboard/query-state.d.ts.map +1 -1
- package/dist/dashboard/query-state.js +4 -7
- package/dist/{section → dashboard}/section.d.ts +2 -2
- package/dist/{section → dashboard}/section.d.ts.map +1 -1
- package/dist/{section → dashboard}/section.js +2 -2
- package/dist/dashboard/settings-section.d.ts +1 -1
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +1 -1
- package/dist/dashboard/stat-card.d.ts +1 -1
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +1 -1
- package/dist/dashboard/widget-header.d.ts.map +1 -0
- package/dist/data/data-table.js +2 -2
- package/dist/data/table.d.ts +1 -1
- package/dist/data/table.d.ts.map +1 -1
- package/dist/data/table.js +2 -16
- package/dist/data/virtual.d.ts +3 -3
- package/dist/dev-dock.d.ts +26 -0
- package/dist/dev-dock.d.ts.map +1 -0
- package/dist/dev-dock.js +85 -0
- package/dist/format/date.d.ts +62 -0
- package/dist/format/date.d.ts.map +1 -0
- package/dist/format/date.js +106 -0
- package/dist/format/index.d.ts +39 -0
- package/dist/format/index.d.ts.map +1 -0
- package/dist/format/index.js +54 -0
- package/dist/format/measure.d.ts +15 -0
- package/dist/format/measure.d.ts.map +1 -0
- package/dist/format/measure.js +16 -0
- package/dist/format/number.d.ts +69 -0
- package/dist/format/number.d.ts.map +1 -0
- package/dist/format/number.js +62 -0
- package/dist/format/shared.d.ts +27 -0
- package/dist/format/shared.d.ts.map +1 -0
- package/dist/format/shared.js +29 -0
- package/dist/forms/field.d.ts +4 -20
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +0 -4
- package/dist/forms/form-layout.d.ts +1 -1
- package/dist/forms/form-layout.d.ts.map +1 -1
- package/dist/forms/form-layout.js +1 -1
- package/dist/forms/index.d.ts +1 -1
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/index.js +1 -2
- package/dist/forms/use-form-submit.d.ts +1 -1
- package/dist/forms/use-form-submit.js +1 -1
- package/dist/guard/index.d.ts +2 -9
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +53 -32
- package/dist/guard/types.d.ts +8 -10
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +16 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -29
- package/dist/notifications/index.d.ts +5 -5
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +0 -2
- package/dist/notifications/mount-guard.d.ts.map +1 -1
- package/dist/notifications/mount-guard.js +2 -2
- package/dist/provider/connectivity-indicator.d.ts.map +1 -0
- package/dist/{connectivity → provider}/connectivity-provider.d.ts +1 -1
- package/dist/provider/connectivity-provider.d.ts.map +1 -0
- package/dist/{connectivity/types.d.ts → provider/connectivity-types.d.ts} +1 -1
- package/dist/provider/connectivity-types.d.ts.map +1 -0
- package/dist/provider/error-fallback.module.css +21 -0
- package/dist/provider/error-fallback.module.css.d.ts +9 -0
- package/dist/provider/index.d.ts +20 -36
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +42 -32
- package/dist/provider/lab-theme.js +1 -1
- package/dist/provider/use-connectivity.d.ts +3 -0
- package/dist/provider/use-connectivity.d.ts.map +1 -0
- package/dist/query-client.d.ts +67 -0
- package/dist/query-client.d.ts.map +1 -0
- package/dist/query-client.js +35 -0
- package/dist/{query/devtools.d.ts → query-devtools.d.ts} +2 -2
- package/dist/query-devtools.d.ts.map +1 -0
- package/dist/register.d.ts +17 -3
- package/dist/register.d.ts.map +1 -1
- package/dist/router-tanstack/index.d.ts +4 -7
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +1 -9
- package/dist/router-tanstack/nav.d.ts +1 -1
- package/dist/router-tanstack/nav.d.ts.map +1 -1
- package/dist/router-tanstack/search-store.d.ts +3 -6
- package/dist/router-tanstack/search-store.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.d.ts +1 -1
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.d.ts +1 -1
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-main.module.css +11 -0
- package/dist/shell/app-mobile-nav.d.ts +1 -1
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +24 -2
- package/dist/shell/app-sidebar.d.ts +1 -1
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +31 -2
- package/dist/shell/app-sidebar.module.css +10 -0
- package/dist/shell/index.d.ts +10 -4
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +13 -17
- package/dist/shell/mobile-nav-model.d.ts +1 -1
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/{nav/types.d.ts → shell/nav-types.d.ts} +1 -1
- package/dist/shell/nav-types.d.ts.map +1 -0
- package/dist/shell/page-aside.d.ts.map +1 -1
- package/dist/shell/page-aside.js +2 -29
- package/dist/shell/page-title.d.ts +14 -0
- package/dist/shell/page-title.d.ts.map +1 -0
- package/dist/shell/page-title.js +25 -0
- package/dist/shell/page-title.module.css +33 -0
- package/dist/shell/page-title.module.css.d.ts +11 -0
- package/dist/shell/sidebar-block-model.d.ts +1 -1
- package/dist/shell/sidebar-block-model.d.ts.map +1 -1
- package/dist/shell/sidebar-blocks.d.ts +1 -1
- package/dist/shell/sidebar-blocks.d.ts.map +1 -1
- package/dist/shell/use-breakpoint.d.ts +11 -0
- package/dist/shell/use-breakpoint.d.ts.map +1 -0
- package/dist/shell/use-breakpoint.js +33 -0
- package/dist/state/fields.d.ts +3 -19
- package/dist/state/fields.d.ts.map +1 -1
- package/dist/state/fields.js +6 -16
- package/dist/styles.css +36 -0
- package/dist/surfaces.d.ts +18 -41
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +48 -60
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/{theme-toggle/index.d.ts → theme/theme-toggle.d.ts} +1 -1
- package/dist/theme/theme-toggle.d.ts.map +1 -0
- package/dist/{theme-toggle/index.js → theme/theme-toggle.js} +1 -1
- package/dist/tokens/index.d.ts +1 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +10 -0
- package/dist/tokens/palette.d.ts +12 -0
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +13 -1
- package/dist/tokens.css +1 -0
- package/llms.txt +9 -24
- package/package.json +12 -16
- package/src/agent/ai-sdk-transport.ts +11 -4
- package/src/agent/coalesce.ts +1 -1
- package/src/agent/merge.ts +1 -1
- package/src/agent/transport.ts +15 -2
- package/src/agent/use-agent-thread-runs.ts +65 -15
- package/src/agent-chat/index.ts +6 -4
- package/src/agent-chat/relative-time.ts +1 -1
- package/src/agent-chat/thread-detail-panel.tsx +1 -1
- package/src/agent-chat/thread-feed-row.tsx +105 -30
- package/src/agent-chat/thread-feed.tsx +51 -25
- package/src/agent-chat/thread-message.tsx +1 -1
- package/src/agent-chat/virtualize.ts +48 -0
- package/src/charts/index.ts +7 -10
- package/src/charts/kinds/BandStrip.tsx +1 -1
- package/src/charts/kinds/Bars.tsx +2 -5
- package/src/charts/kinds/DualPanel.tsx +4 -3
- package/src/charts/kinds/StackedArea.tsx +1 -1
- package/src/charts/kinds/ZonedLine.tsx +1 -4
- package/src/charts/primitives/Axes.tsx +49 -0
- package/src/charts/primitives/ChartCard.tsx +49 -6
- package/src/charts/primitives/ChartFrame.tsx +5 -1
- package/src/charts/primitives/ChartPending.tsx +14 -8
- package/src/charts/utils/format.ts +17 -186
- package/src/cli/audit-allows.ts +371 -0
- package/src/cli/check-theme.ts +197 -0
- package/src/cli/doctor.ts +331 -0
- package/src/cli/guard-hook.ts +115 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +269 -3439
- package/src/cli/init.ts +247 -0
- package/src/cli/sync.ts +324 -0
- package/src/cli/tokens-css.ts +252 -0
- package/src/commands/define-commands.ts +1 -1
- package/src/commands/define-overlays.ts +1 -1
- package/src/commands/index.ts +10 -1
- package/src/commands/overlays-mount.tsx +8 -10
- package/src/commands/shell-bridge.ts +86 -0
- package/src/common/errors.ts +70 -0
- package/src/common/index.ts +2 -0
- package/src/common/props.ts +1 -1
- package/src/common/validate.ts +1 -1
- package/src/content/markdown.tsx +4 -8
- package/src/controls/actions.tsx +1 -1
- package/src/controls/panel-row.tsx +1 -1
- package/src/controls/range-filter.tsx +1 -1
- package/src/dashboard/delta-badge.tsx +74 -7
- package/src/dashboard/empty-state.tsx +2 -6
- package/src/dashboard/index.ts +11 -2
- package/src/dashboard/query-state.tsx +5 -18
- package/src/{section → dashboard}/section.tsx +4 -4
- package/src/dashboard/settings-section.tsx +2 -2
- package/src/dashboard/stat-card.tsx +2 -2
- package/src/data/data-table.tsx +3 -3
- package/src/data/table.ts +6 -25
- package/src/data/virtual-list.tsx +1 -1
- package/src/data/virtual.ts +3 -3
- package/src/dev-dock.tsx +162 -0
- package/src/format/date.ts +184 -0
- package/src/format/index.ts +69 -0
- package/src/format/measure.ts +31 -0
- package/src/format/number.ts +129 -0
- package/src/format/shared.ts +63 -0
- package/src/forms/field.ts +4 -24
- package/src/forms/form-layout.tsx +2 -2
- package/src/forms/index.ts +1 -2
- package/src/forms/use-form-submit.ts +2 -2
- package/src/guard/index.ts +80 -69
- package/src/guard/types.ts +7 -10
- package/src/index.ts +38 -29
- package/src/notifications/index.ts +5 -7
- package/src/notifications/mount-guard.ts +7 -9
- package/src/{connectivity → provider}/connectivity-indicator.tsx +1 -1
- package/src/{connectivity → provider}/connectivity-provider.tsx +1 -1
- package/src/provider/error-fallback.module.css +21 -0
- package/src/provider/error-fallback.module.css.d.ts +9 -0
- package/src/provider/index.tsx +71 -71
- package/src/provider/lab-theme.ts +1 -1
- package/src/{connectivity → provider}/use-connectivity.ts +3 -3
- package/src/query-client.ts +105 -0
- package/src/{query/devtools.tsx → query-devtools.tsx} +5 -4
- package/src/register.ts +20 -4
- package/src/router-tanstack/index.ts +4 -15
- package/src/router-tanstack/nav.ts +1 -1
- package/src/router-tanstack/search-store.ts +3 -4
- package/src/router-tanstack/use-nav.ts +1 -1
- package/src/shell/app-breadcrumbs.tsx +1 -1
- package/src/shell/app-main.module.css +11 -0
- package/src/shell/app-mobile-nav.tsx +28 -1
- package/src/shell/app-sidebar.module.css +10 -0
- package/src/shell/app-sidebar.tsx +31 -2
- package/src/shell/index.tsx +23 -28
- package/src/shell/mobile-nav-model.ts +1 -1
- package/src/shell/page-aside.tsx +2 -52
- package/src/shell/page-title.module.css +33 -0
- package/src/shell/page-title.module.css.d.ts +11 -0
- package/src/shell/page-title.tsx +57 -0
- package/src/shell/sidebar-block-model.ts +1 -1
- package/src/shell/sidebar-blocks.tsx +1 -1
- package/src/shell/use-breakpoint.ts +73 -0
- package/src/state/fields.ts +7 -43
- package/src/styles.css +36 -0
- package/src/surfaces.ts +47 -61
- package/src/theme/index.ts +7 -0
- package/src/{theme-toggle/index.tsx → theme/theme-toggle.tsx} +1 -1
- package/src/tokens/index.ts +10 -0
- package/src/tokens/palette.ts +12 -0
- package/dist/connectivity/connectivity-indicator.d.ts.map +0 -1
- package/dist/connectivity/connectivity-provider.d.ts.map +0 -1
- package/dist/connectivity/index.d.ts +0 -14
- package/dist/connectivity/index.d.ts.map +0 -1
- package/dist/connectivity/index.js +0 -8
- package/dist/connectivity/types.d.ts.map +0 -1
- package/dist/connectivity/use-connectivity.d.ts +0 -3
- package/dist/connectivity/use-connectivity.d.ts.map +0 -1
- package/dist/data/index.d.ts +0 -38
- package/dist/data/index.d.ts.map +0 -1
- package/dist/data/index.js +0 -24
- package/dist/motion/index.d.ts.map +0 -1
- package/dist/nav/types.d.ts.map +0 -1
- package/dist/notifications/overlay.d.ts +0 -20
- package/dist/notifications/overlay.d.ts.map +0 -1
- package/dist/notifications/overlay.js +0 -14
- package/dist/query/devtools.d.ts.map +0 -1
- package/dist/query/error-message.d.ts +0 -41
- package/dist/query/error-message.d.ts.map +0 -1
- package/dist/query/error-message.js +0 -30
- package/dist/query/index.d.ts +0 -47
- package/dist/query/index.d.ts.map +0 -1
- package/dist/query/index.js +0 -56
- package/dist/router-tanstack/field.d.ts +0 -14
- package/dist/router-tanstack/field.d.ts.map +0 -1
- package/dist/router-tanstack/field.js +0 -4
- package/dist/router-tanstack/multi-search-param-store.d.ts +0 -43
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +0 -1
- package/dist/router-tanstack/multi-search-param-store.js +0 -30
- package/dist/router-tanstack/search-param-store.d.ts +0 -42
- package/dist/router-tanstack/search-param-store.d.ts.map +0 -1
- package/dist/router-tanstack/search-param-store.js +0 -25
- package/dist/section/index.d.ts +0 -2
- package/dist/section/index.d.ts.map +0 -1
- package/dist/section/index.js +0 -4
- package/dist/theme-toggle/index.d.ts.map +0 -1
- package/dist/utils/is-dev.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.d.ts +0 -19
- package/dist/widget-header/delta-badge.d.ts.map +0 -1
- package/dist/widget-header/delta-badge.js +0 -36
- package/dist/widget-header/index.d.ts +0 -3
- package/dist/widget-header/index.d.ts.map +0 -1
- package/dist/widget-header/index.js +0 -8
- package/dist/widget-header/widget-header.d.ts.map +0 -1
- package/src/connectivity/index.ts +0 -19
- package/src/data/index.ts +0 -69
- package/src/notifications/overlay.tsx +0 -66
- package/src/query/error-message.ts +0 -70
- package/src/query/index.ts +0 -97
- package/src/router-tanstack/field.ts +0 -36
- package/src/router-tanstack/multi-search-param-store.ts +0 -106
- package/src/router-tanstack/search-param-store.ts +0 -100
- package/src/section/index.ts +0 -1
- package/src/widget-header/delta-badge.tsx +0 -77
- package/src/widget-header/index.ts +0 -10
- /package/dist/{utils → common}/is-dev.d.ts +0 -0
- /package/dist/{utils → common}/is-dev.js +0 -0
- /package/dist/{motion/index.js → common/motion.js} +0 -0
- /package/dist/{section → dashboard}/section.module.css +0 -0
- /package/dist/{section → dashboard}/section.module.css.d.ts +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.d.ts +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.js +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.module.css +0 -0
- /package/dist/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
- /package/dist/{connectivity → provider}/connectivity-indicator.d.ts +0 -0
- /package/dist/{connectivity → provider}/connectivity-indicator.js +0 -0
- /package/dist/{connectivity → provider}/connectivity-provider.js +0 -0
- /package/dist/{connectivity/types.js → provider/connectivity-types.js} +0 -0
- /package/dist/{connectivity → provider}/use-connectivity.js +0 -0
- /package/dist/{query/devtools.js → query-devtools.js} +0 -0
- /package/dist/{nav/types.js → shell/nav-types.js} +0 -0
- /package/src/{utils → common}/is-dev.ts +0 -0
- /package/src/{motion/index.ts → common/motion.ts} +0 -0
- /package/src/{section → dashboard}/section.module.css +0 -0
- /package/src/{section → dashboard}/section.module.css.d.ts +0 -0
- /package/src/{widget-header → dashboard}/widget-header.module.css +0 -0
- /package/src/{widget-header → dashboard}/widget-header.module.css.d.ts +0 -0
- /package/src/{widget-header → dashboard}/widget-header.tsx +0 -0
- /package/src/{connectivity/types.ts → provider/connectivity-types.ts} +0 -0
- /package/src/{nav/types.ts → shell/nav-types.ts} +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Date/time/duration formatting. `fmtAxisDate`/`fmtTooltipDate` moved here from
|
|
3
|
+
* `src/charts/utils/format.ts` (C5 consolidation) — that file now re-exports from this module.
|
|
4
|
+
* The rest (`clock`/`weekday`/`relativeTime`/`duration`/`durationClock`) are new, seeded from argo
|
|
5
|
+
* consumers (`features/{walking-pad,reading,astro-window,garmin-health}/{formatters,format,
|
|
6
|
+
* formulas}.ts` — read those files' own JSDoc for the exact call sites this generalizes).
|
|
7
|
+
*/
|
|
8
|
+
import { dateFormat, NON_FINITE, toDate } from './shared'
|
|
9
|
+
import type { LocaleOption } from './shared'
|
|
10
|
+
|
|
11
|
+
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
12
|
+
const SHORT_DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
13
|
+
|
|
14
|
+
/** Axis-friendly short date: DD.MM. A non-finite or invalid date prints {@link NON_FINITE}. */
|
|
15
|
+
export function fmtAxisDate(value: unknown): string {
|
|
16
|
+
if (typeof value === 'number' && !Number.isFinite(value)) return NON_FINITE
|
|
17
|
+
if (value instanceof Date) {
|
|
18
|
+
if (Number.isNaN(value.getTime())) return NON_FINITE
|
|
19
|
+
const dd = String(value.getDate()).padStart(2, '0')
|
|
20
|
+
const mm = String(value.getMonth() + 1).padStart(2, '0')
|
|
21
|
+
return `${dd}.${mm}`
|
|
22
|
+
}
|
|
23
|
+
const s = String(value ?? '')
|
|
24
|
+
const match = s.match(/(\d{4})-(\d{2})-(\d{2})/)
|
|
25
|
+
if (match) return `${match[3]}.${match[2]}`
|
|
26
|
+
return s
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Tooltip-friendly long date: "Mon Apr 21 2026". A non-finite or invalid date prints
|
|
30
|
+
* {@link NON_FINITE}. */
|
|
31
|
+
export function fmtTooltipDate(date: unknown): string {
|
|
32
|
+
if (typeof date === 'number' && !Number.isFinite(date)) return NON_FINITE
|
|
33
|
+
if (date instanceof Date) {
|
|
34
|
+
if (Number.isNaN(date.getTime())) return NON_FINITE
|
|
35
|
+
return `${SHORT_DAYS[date.getDay()]} ${MONTHS[date.getMonth()]} ${date.getDate()} ${date.getFullYear()}`
|
|
36
|
+
}
|
|
37
|
+
const s = String(date ?? '')
|
|
38
|
+
const match = s.match(/(\d{4})-(\d{2})-(\d{2})/)
|
|
39
|
+
if (!match) return s
|
|
40
|
+
const d = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]))
|
|
41
|
+
return `${SHORT_DAYS[d.getDay()]} ${MONTHS[d.getMonth()]} ${d.getDate()} ${d.getFullYear()}`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type ClockOptions = LocaleOption & {
|
|
45
|
+
/** IANA zone (`'Europe/Vienna'`). Default the runtime's own zone — the same "local" argo's
|
|
46
|
+
* `d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })` call sites meant. */
|
|
47
|
+
timeZone?: string
|
|
48
|
+
/** Render `:ss` too. Default false. */
|
|
49
|
+
seconds?: boolean
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A local clock reading — `"09:41"`. Seeds: `m365-explorer/explorer-page.tsx`,
|
|
53
|
+
* `walking-pad/session-history.tsx` (both `toLocaleTimeString([...], { hour: '2-digit', minute:
|
|
54
|
+
* '2-digit' })`). A non-finite/unparseable input prints {@link NON_FINITE}. */
|
|
55
|
+
export function clock(value: Date | number | string, options: ClockOptions = {}): string {
|
|
56
|
+
const date = toDate(value)
|
|
57
|
+
if (date === null) return NON_FINITE
|
|
58
|
+
const { locale, timeZone, seconds = false } = options
|
|
59
|
+
return dateFormat(locale, {
|
|
60
|
+
hour: '2-digit',
|
|
61
|
+
minute: '2-digit',
|
|
62
|
+
...(seconds && { second: '2-digit' }),
|
|
63
|
+
hour12: false,
|
|
64
|
+
...(timeZone !== undefined && { timeZone }),
|
|
65
|
+
}).format(date)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type WeekdayOptions = LocaleOption & {
|
|
69
|
+
/** `Intl.DateTimeFormat`'s own weekday widths. Default `'short'` (`"Mon"`). */
|
|
70
|
+
format?: 'short' | 'long' | 'narrow'
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Weekday name — seeded from `astro-window/formulas.ts#fmtWeekday`. A plain `YYYY-MM-DD` string
|
|
74
|
+
* is parsed as a LOCAL calendar date (never UTC), matching that seed's own rationale: the weekday
|
|
75
|
+
* must not shift a day depending on the reader's timezone. A non-finite/unparseable input prints
|
|
76
|
+
* {@link NON_FINITE}. */
|
|
77
|
+
export function weekday(value: Date | number | string, options: WeekdayOptions = {}): string {
|
|
78
|
+
const date =
|
|
79
|
+
typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value)
|
|
80
|
+
? (() => {
|
|
81
|
+
const [y, m, d] = value.split('-').map(Number)
|
|
82
|
+
return new Date(y ?? 1970, (m ?? 1) - 1, d ?? 1)
|
|
83
|
+
})()
|
|
84
|
+
: toDate(value)
|
|
85
|
+
if (date === null) return NON_FINITE
|
|
86
|
+
const { locale, format = 'short' } = options
|
|
87
|
+
return dateFormat(locale, { weekday: format }).format(date)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type RelativeTimeOptions = LocaleOption & {
|
|
91
|
+
/** The reference "now" instant, epoch ms. Default `Date.now()` — mainly for tests. */
|
|
92
|
+
now?: number
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const RELATIVE_TIME_UNITS: readonly {
|
|
96
|
+
readonly unit: Intl.RelativeTimeFormatUnit
|
|
97
|
+
readonly ms: number
|
|
98
|
+
}[] = [
|
|
99
|
+
{ unit: 'year', ms: 31_536_000_000 },
|
|
100
|
+
{ unit: 'month', ms: 2_628_000_000 },
|
|
101
|
+
{ unit: 'week', ms: 604_800_000 },
|
|
102
|
+
{ unit: 'day', ms: 86_400_000 },
|
|
103
|
+
{ unit: 'hour', ms: 3_600_000 },
|
|
104
|
+
{ unit: 'minute', ms: 60_000 },
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
const relativeTimeFormatterCache = new Map<string, Intl.RelativeTimeFormat>()
|
|
108
|
+
|
|
109
|
+
function relativeTimeFormat(locale: string | undefined): Intl.RelativeTimeFormat {
|
|
110
|
+
const key = locale ?? ''
|
|
111
|
+
const cached = relativeTimeFormatterCache.get(key)
|
|
112
|
+
if (cached !== undefined) return cached
|
|
113
|
+
const created = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })
|
|
114
|
+
relativeTimeFormatterCache.set(key, created)
|
|
115
|
+
return created
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Short relative phrase — `"3 hours ago"`, `"in 2 days"`, `"just now"`. Seeded from
|
|
120
|
+
* `agent-chat/relative-time.ts#formatRelativeTime` (epoch-ms, 'en'-hardcoded), generalized with a
|
|
121
|
+
* `locale` option and accepting `Date`/ISO-string inputs too (argo's
|
|
122
|
+
* `reading/format.ts#relativeTime` and `garmin-health/formulas.ts#formatRelativeTime` both take an
|
|
123
|
+
* ISO string). A non-finite/unparseable input prints {@link NON_FINITE} rather than throwing —
|
|
124
|
+
* `Intl.RelativeTimeFormat.format` throws a `RangeError` on non-finite input, and this is
|
|
125
|
+
* render-path code where one bad timestamp must not blank a whole list.
|
|
126
|
+
*/
|
|
127
|
+
export function relativeTime(
|
|
128
|
+
value: number | Date | string,
|
|
129
|
+
options: RelativeTimeOptions = {},
|
|
130
|
+
): string {
|
|
131
|
+
const { locale, now = Date.now() } = options
|
|
132
|
+
const timestamp =
|
|
133
|
+
value instanceof Date ? value.getTime() : typeof value === 'number' ? value : Date.parse(value)
|
|
134
|
+
if (!Number.isFinite(timestamp)) return NON_FINITE
|
|
135
|
+
const diffMs = timestamp - now
|
|
136
|
+
const absMs = Math.abs(diffMs)
|
|
137
|
+
if (absMs < 60_000) return 'just now'
|
|
138
|
+
const unit = RELATIVE_TIME_UNITS.find(({ ms }) => absMs >= ms) ?? RELATIVE_TIME_UNITS.at(-1)!
|
|
139
|
+
return relativeTimeFormat(locale).format(Math.round(diffMs / unit.ms), unit.unit)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type DurationUnit = 'seconds' | 'minutes'
|
|
143
|
+
|
|
144
|
+
export type DurationOptions = {
|
|
145
|
+
/** Unit of the input value. Default `'seconds'`. */
|
|
146
|
+
unit?: DurationUnit
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Human duration — `"1h 02m"`, `"5m"`, `"42s"`. Minutes zero-pad once an hour is present (`"1h
|
|
151
|
+
* 02m"`, a clock-reading convention); alone they don't (`"5m"`). Seconds show only under a minute
|
|
152
|
+
* (`"42s"`). A non-finite value prints {@link NON_FINITE}.
|
|
153
|
+
*
|
|
154
|
+
* The one signature standing in for argo's four duration spellings: `walking-pad/
|
|
155
|
+
* formatters.ts#formatDuration` (seconds, `<60s` → `"Ns"` — this IS that shape),
|
|
156
|
+
* `reading/format.ts#formatReadTime` (seconds, no `<60s` branch, floors at `"0m"` — pass a value
|
|
157
|
+
* `>= 60` and the shapes match), `astro-window/formulas.ts#fmtMinutes` (minutes as input — pass
|
|
158
|
+
* `{ unit: 'minutes' }`). The fourth, `walking-pad/formatters.ts#formatDurationClock` (`h:mm:ss`),
|
|
159
|
+
* is {@link durationClock} below, not this.
|
|
160
|
+
*/
|
|
161
|
+
export function duration(value: number, options: DurationOptions = {}): string {
|
|
162
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
163
|
+
const { unit = 'seconds' } = options
|
|
164
|
+
const totalSeconds = Math.max(0, Math.round(unit === 'minutes' ? value * 60 : value))
|
|
165
|
+
if (totalSeconds < 60) return `${totalSeconds}s`
|
|
166
|
+
const totalMinutes = Math.round(totalSeconds / 60)
|
|
167
|
+
const h = Math.floor(totalMinutes / 60)
|
|
168
|
+
const m = totalMinutes % 60
|
|
169
|
+
if (h === 0) return `${m}m`
|
|
170
|
+
return `${h}h ${String(m).padStart(2, '0')}m`
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Clock-style duration — `"1:02:03"`, `"04:12"`. Seeded from `walking-pad/
|
|
174
|
+
* formatters.ts#formatDurationClock`. A non-finite value prints {@link NON_FINITE}. */
|
|
175
|
+
export function durationClock(seconds: number): string {
|
|
176
|
+
if (!Number.isFinite(seconds)) return NON_FINITE
|
|
177
|
+
const total = Math.max(0, Math.round(seconds))
|
|
178
|
+
const h = Math.floor(total / 3600)
|
|
179
|
+
const m = Math.floor((total % 3600) / 60)
|
|
180
|
+
const s = total % 60
|
|
181
|
+
const mm = String(m).padStart(2, '0')
|
|
182
|
+
const ss = String(s).padStart(2, '0')
|
|
183
|
+
return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`
|
|
184
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `basalt-ui/format` — Mantine-free, React-free `Intl`-backed formatting. One implementation for
|
|
3
|
+
* numbers/percentages/money, dates/times/durations and the two measurement units argo needed
|
|
4
|
+
* (`km`/`kcal`) — replacing argo's `features/*\/{formatters,formulas,format}.ts` (~350 of 643
|
|
5
|
+
* lines across four duration spellings) and the chart layer's own `charts/utils/format.ts`, which
|
|
6
|
+
* now re-exports from here.
|
|
7
|
+
*
|
|
8
|
+
* Every formatter takes `(value, opts?)`; `locale` on every `opts` defaults to `undefined` —
|
|
9
|
+
* `Intl`'s own "use the runtime's locale". Every non-finite/unparseable input prints the same
|
|
10
|
+
* {@link NON_FINITE} em dash rather than `"NaN"` or throwing.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { money, percent, duration, weekday, relativeTime } from 'basalt-ui/format'
|
|
14
|
+
*
|
|
15
|
+
* money(1234, { currency: 'USD' }) // "$1,234"
|
|
16
|
+
* percent(0.42) // "42%"
|
|
17
|
+
* duration(3_723) // "1h 02m"
|
|
18
|
+
* weekday('2026-09-02') // "Wed"
|
|
19
|
+
* relativeTime(Date.now() - 3_600_000) // "1 hour ago"
|
|
20
|
+
*/
|
|
21
|
+
export {
|
|
22
|
+
compact,
|
|
23
|
+
deltaPct,
|
|
24
|
+
type DeltaPctOptions,
|
|
25
|
+
fmtCompact,
|
|
26
|
+
type FmtCompactOptions,
|
|
27
|
+
fmtCurrency,
|
|
28
|
+
type FmtCurrencyOptions,
|
|
29
|
+
fmtInt,
|
|
30
|
+
fmtPercent,
|
|
31
|
+
type FmtPercentOptions,
|
|
32
|
+
integer,
|
|
33
|
+
type LocaleOption,
|
|
34
|
+
money,
|
|
35
|
+
NON_FINITE,
|
|
36
|
+
percent,
|
|
37
|
+
} from './number'
|
|
38
|
+
export {
|
|
39
|
+
clock,
|
|
40
|
+
type ClockOptions,
|
|
41
|
+
duration,
|
|
42
|
+
durationClock,
|
|
43
|
+
type DurationOptions,
|
|
44
|
+
type DurationUnit,
|
|
45
|
+
fmtAxisDate,
|
|
46
|
+
fmtTooltipDate,
|
|
47
|
+
relativeTime,
|
|
48
|
+
type RelativeTimeOptions,
|
|
49
|
+
weekday,
|
|
50
|
+
type WeekdayOptions,
|
|
51
|
+
} from './date'
|
|
52
|
+
export { kcal, type KcalOptions, km, type KmOptions } from './measure'
|
|
53
|
+
|
|
54
|
+
import { fmtCompact, fmtCurrency, fmtInt, fmtPercent } from './number'
|
|
55
|
+
import { fmtAxisDate, fmtTooltipDate } from './date'
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Every shipped chart-facing formatter under one name, so a chart can be handed the whole set
|
|
59
|
+
* (`format={formatters.compact}`). Unchanged shape from `charts/utils/format.ts` — this is the
|
|
60
|
+
* SAME object, not a second one, so an existing `formatters.x` call site keeps working verbatim.
|
|
61
|
+
*/
|
|
62
|
+
export const formatters = {
|
|
63
|
+
compact: fmtCompact,
|
|
64
|
+
percent: fmtPercent,
|
|
65
|
+
currency: fmtCurrency,
|
|
66
|
+
int: fmtInt,
|
|
67
|
+
axisDate: fmtAxisDate,
|
|
68
|
+
tooltipDate: fmtTooltipDate,
|
|
69
|
+
} as const
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Distance/energy formatting — seeded from `walking-pad/formatters.ts#formatKm`/`#formatKcal`.
|
|
3
|
+
*/
|
|
4
|
+
import { NON_FINITE } from './shared'
|
|
5
|
+
|
|
6
|
+
export type KmOptions = {
|
|
7
|
+
/** Fraction digits. Default 2 (`"5.30 km"`). */
|
|
8
|
+
digits?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Meters as kilometers — `"5.30 km"`. A non-finite value prints {@link NON_FINITE}. */
|
|
12
|
+
export function km(meters: number, options: KmOptions = {}): string {
|
|
13
|
+
if (!Number.isFinite(meters)) return NON_FINITE
|
|
14
|
+
const { digits = 2 } = options
|
|
15
|
+
return `${(meters / 1000).toFixed(digits)} km`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type KcalOptions = {
|
|
19
|
+
/** Fraction digits once compacted to `k cal`. Default 2. Below 1000 kcal always rounds to a
|
|
20
|
+
* whole number, matching the seed (`formatKcal`). */
|
|
21
|
+
digits?: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Kilocalories, compacting to `"k cal"` above 1000 — `"842 kcal"`, `"1.24 k cal"`. A non-finite
|
|
25
|
+
* value prints {@link NON_FINITE}. */
|
|
26
|
+
export function kcal(value: number, options: KcalOptions = {}): string {
|
|
27
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
28
|
+
const { digits = 2 } = options
|
|
29
|
+
if (value >= 1000) return `${(value / 1000).toFixed(digits)} k cal`
|
|
30
|
+
return `${Math.round(value)} kcal`
|
|
31
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number formatting — `Intl.NumberFormat` with the arguments already decided, so a caller states
|
|
3
|
+
* WHAT a number is rather than how to punctuate it. Moved here from `src/charts/utils/format.ts`
|
|
4
|
+
* (C5 consolidation) — that file now re-exports from this module so charts keep working through
|
|
5
|
+
* one implementation.
|
|
6
|
+
*
|
|
7
|
+
* `money`/`percent`/`integer`/`compact`/`deltaPct` are the argo-facing names (seeded from
|
|
8
|
+
* `features/*\/{formatters,formulas}.ts`); `fmtCurrency`/`fmtPercent`/`fmtInt`/`fmtCompact` are the
|
|
9
|
+
* original chart-formatter names, kept for source compatibility and delegated to by the new names.
|
|
10
|
+
*/
|
|
11
|
+
import { NON_FINITE, numberFormat } from './shared'
|
|
12
|
+
import type { LocaleOption } from './shared'
|
|
13
|
+
|
|
14
|
+
export type { LocaleOption }
|
|
15
|
+
export { NON_FINITE }
|
|
16
|
+
|
|
17
|
+
/** The compact `K` suffix, matched only where it follows the digits it qualifies — so a currency
|
|
18
|
+
* prefix that happens to contain one (`HK$`) survives the lowercasing. */
|
|
19
|
+
const COMPACT_K = /(\d\s?)K/
|
|
20
|
+
|
|
21
|
+
export type FmtCompactOptions = LocaleOption & {
|
|
22
|
+
/** Significant-ish fraction digits on the compacted number. Default 1 (`1.2k`, `3.4M`). */
|
|
23
|
+
digits?: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Compact notation: `1.2k`, `3.4M`, `-820`. The axis formatter for any count whose range spans
|
|
28
|
+
* more than three digits. A non-finite value prints {@link NON_FINITE}.
|
|
29
|
+
*
|
|
30
|
+
* Note `Intl`'s compact notation is LOCALE text, not a suffix table: `en` gives `1.2K`, and this
|
|
31
|
+
* lowercases the `K` to match the package's own numeral style while leaving every other locale's
|
|
32
|
+
* word (`1,2 Mio.`) untouched.
|
|
33
|
+
*/
|
|
34
|
+
export function fmtCompact(value: number, options: FmtCompactOptions = {}): string {
|
|
35
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
36
|
+
const { digits = 1, locale } = options
|
|
37
|
+
return numberFormat(locale, {
|
|
38
|
+
notation: 'compact',
|
|
39
|
+
compactDisplay: 'short',
|
|
40
|
+
maximumFractionDigits: digits,
|
|
41
|
+
})
|
|
42
|
+
.format(value)
|
|
43
|
+
.replace(COMPACT_K, '$1k')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** `compact` — the `basalt-ui/format` name for {@link fmtCompact}. */
|
|
47
|
+
export const compact = fmtCompact
|
|
48
|
+
|
|
49
|
+
export type FmtPercentOptions = LocaleOption & {
|
|
50
|
+
/** Fraction digits. Default 0 (`42%`). */
|
|
51
|
+
digits?: number
|
|
52
|
+
/**
|
|
53
|
+
* What the incoming number MEANS. `'ratio'` (default) is `0.42 → 42%`; `'percent'` is
|
|
54
|
+
* `42 → 42%`. Required as an explicit option rather than guessed from magnitude.
|
|
55
|
+
*/
|
|
56
|
+
input?: 'ratio' | 'percent'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Percentages, from either a ratio or an already-scaled percentage. A non-finite value prints
|
|
60
|
+
* {@link NON_FINITE}. */
|
|
61
|
+
export function fmtPercent(value: number, options: FmtPercentOptions = {}): string {
|
|
62
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
63
|
+
const { digits = 0, input = 'ratio', locale } = options
|
|
64
|
+
return numberFormat(locale, {
|
|
65
|
+
style: 'percent',
|
|
66
|
+
minimumFractionDigits: digits,
|
|
67
|
+
maximumFractionDigits: digits,
|
|
68
|
+
}).format(input === 'percent' ? value / 100 : value)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** `percent` — the `basalt-ui/format` name for {@link fmtPercent}. */
|
|
72
|
+
export const percent = fmtPercent
|
|
73
|
+
|
|
74
|
+
export type DeltaPctOptions = FmtPercentOptions
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* A signed percentage delta: `+4%`, `-12%`, `0%` — argo's `walking-pad/formatters.ts#formatPct`
|
|
78
|
+
* (a `+`-prefixed ratio) generalized onto `Intl`'s own `signDisplay: 'exceptZero'`, which adds the
|
|
79
|
+
* `+`/`-` sign the same way `Intl`'s own minus sign already does, and adds nothing for zero. A
|
|
80
|
+
* non-finite value prints {@link NON_FINITE}.
|
|
81
|
+
*/
|
|
82
|
+
export function deltaPct(value: number, options: DeltaPctOptions = {}): string {
|
|
83
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
84
|
+
const { digits = 0, input = 'ratio', locale } = options
|
|
85
|
+
return numberFormat(locale, {
|
|
86
|
+
style: 'percent',
|
|
87
|
+
signDisplay: 'exceptZero',
|
|
88
|
+
minimumFractionDigits: digits,
|
|
89
|
+
maximumFractionDigits: digits,
|
|
90
|
+
}).format(input === 'percent' ? value / 100 : value)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type FmtCurrencyOptions = LocaleOption & {
|
|
94
|
+
/** ISO 4217 code — required. There is no default currency. */
|
|
95
|
+
currency: string
|
|
96
|
+
/** Compact notation for the amount (`$1.2k`). Default false. */
|
|
97
|
+
compact?: boolean
|
|
98
|
+
/** Fraction digits. Default 0 compacted, else the currency's own default (`undefined`). */
|
|
99
|
+
digits?: number
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Money. `fmtCurrency(1234, { currency: 'USD' })` → `$1,234`. A non-finite amount prints
|
|
103
|
+
* {@link NON_FINITE}. */
|
|
104
|
+
export function fmtCurrency(value: number, options: FmtCurrencyOptions): string {
|
|
105
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
106
|
+
const { currency, compact: isCompact = false, digits, locale } = options
|
|
107
|
+
const fractionDigits = digits ?? (isCompact ? 1 : 0)
|
|
108
|
+
return numberFormat(locale, {
|
|
109
|
+
style: 'currency',
|
|
110
|
+
currency,
|
|
111
|
+
...(isCompact && { notation: 'compact', compactDisplay: 'short' }),
|
|
112
|
+
minimumFractionDigits: isCompact ? 0 : fractionDigits,
|
|
113
|
+
maximumFractionDigits: fractionDigits,
|
|
114
|
+
})
|
|
115
|
+
.format(value)
|
|
116
|
+
.replace(COMPACT_K, '$1k')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** `money` — the `basalt-ui/format` name for {@link fmtCurrency}. */
|
|
120
|
+
export const money = fmtCurrency
|
|
121
|
+
|
|
122
|
+
/** Grouped integer: `12,480`. Rounds. A non-finite value prints {@link NON_FINITE}. */
|
|
123
|
+
export function fmtInt(value: number, options: LocaleOption = {}): string {
|
|
124
|
+
if (!Number.isFinite(value)) return NON_FINITE
|
|
125
|
+
return numberFormat(options.locale, { maximumFractionDigits: 0 }).format(value)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** `integer` — the `basalt-ui/format` name for {@link fmtInt}. */
|
|
129
|
+
export const integer = fmtInt
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared low-level pieces of `basalt-ui/format` — the em-dash sentinel and the memoized
|
|
3
|
+
* `Intl.NumberFormat` cache every number formatter in this module reuses. Moved here (out of
|
|
4
|
+
* `src/charts/utils/format.ts`, C5 consolidation) so the subpath is the single implementation and
|
|
5
|
+
* `charts/utils/format.ts` becomes a thin re-export (`docs/CHARTS-SPEC.md` still calls it home for
|
|
6
|
+
* chart callers, but the code lives here now).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* What EVERY formatter in this module prints for input it cannot represent — `NaN`, `±Infinity`,
|
|
11
|
+
* an `Invalid Date`. An em dash (U+2014). A formatter may say it does not know; it may not print a
|
|
12
|
+
* number that is not one.
|
|
13
|
+
*/
|
|
14
|
+
export const NON_FINITE = '—'
|
|
15
|
+
|
|
16
|
+
/** Options every number formatter here accepts. */
|
|
17
|
+
export type LocaleOption = {
|
|
18
|
+
/** BCP-47 tag. Default `undefined` — the runtime's own locale. */
|
|
19
|
+
locale?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* `Intl.NumberFormat` construction is the expensive half of formatting and a chart formats one
|
|
24
|
+
* label per tick per frame, so instances are memoized on their own arguments. The key is the
|
|
25
|
+
* serialized options because that is exactly what identifies an instance.
|
|
26
|
+
*/
|
|
27
|
+
const formatterCache = new Map<string, Intl.NumberFormat>()
|
|
28
|
+
|
|
29
|
+
export function numberFormat(
|
|
30
|
+
locale: string | undefined,
|
|
31
|
+
options: Intl.NumberFormatOptions,
|
|
32
|
+
): Intl.NumberFormat {
|
|
33
|
+
const key = `${locale ?? ''}|${JSON.stringify(options)}`
|
|
34
|
+
const cached = formatterCache.get(key)
|
|
35
|
+
if (cached !== undefined) return cached
|
|
36
|
+
const created = new Intl.NumberFormat(locale, options)
|
|
37
|
+
formatterCache.set(key, created)
|
|
38
|
+
return created
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const dateFormatterCache = new Map<string, Intl.DateTimeFormat>()
|
|
42
|
+
|
|
43
|
+
export function dateFormat(
|
|
44
|
+
locale: string | undefined,
|
|
45
|
+
options: Intl.DateTimeFormatOptions,
|
|
46
|
+
): Intl.DateTimeFormat {
|
|
47
|
+
const key = `${locale ?? ''}|${JSON.stringify(options)}`
|
|
48
|
+
const cached = dateFormatterCache.get(key)
|
|
49
|
+
if (cached !== undefined) return cached
|
|
50
|
+
const created = new Intl.DateTimeFormat(locale, options)
|
|
51
|
+
dateFormatterCache.set(key, created)
|
|
52
|
+
return created
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Coerces the flexible `Date | number | string` inputs this module's date formatters accept into
|
|
56
|
+
* a `Date`. A `number` is treated as epoch milliseconds (matching `Date`'s own constructor and
|
|
57
|
+
* `agent-chat/relative-time.ts`'s prior contract) — a caller with epoch seconds multiplies by
|
|
58
|
+
* 1000 first. Returns `null` for a non-finite/unparseable input so every caller can share one
|
|
59
|
+
* {@link NON_FINITE} guard instead of re-deriving one. */
|
|
60
|
+
export function toDate(value: Date | number | string): Date | null {
|
|
61
|
+
const date = value instanceof Date ? value : new Date(value)
|
|
62
|
+
return Number.isNaN(date.getTime()) ? null : date
|
|
63
|
+
}
|
package/src/forms/field.ts
CHANGED
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
* three warnings per page. The bundle was basalt's own invention; Mantine's documented idiom is
|
|
11
11
|
* `key={form.key(path)} {...form.getInputProps(path)}`, and this is that idiom with basalt's names.
|
|
12
12
|
*
|
|
13
|
-
* The `@deprecated` `field` alias
|
|
14
|
-
*
|
|
13
|
+
* The `@deprecated` `field` alias that used to live here is removed (1.29.0, C1 consolidation) —
|
|
14
|
+
* `basalt-ui/forms`' `field` collided in name with the `field` store builder on `basalt-ui/state`,
|
|
15
|
+
* which was the whole reason for the rename in the first place. Write
|
|
16
|
+
* `key={fieldKey(form, path)} {...inputProps(form, path)}`.
|
|
15
17
|
*/
|
|
16
18
|
import type { LooseKeys, UseFormReturnType } from '@mantine/form'
|
|
17
19
|
|
|
@@ -55,25 +57,3 @@ export function fieldKey<Values extends Record<string, unknown>, Path extends Lo
|
|
|
55
57
|
): string {
|
|
56
58
|
return form.key(path)
|
|
57
59
|
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @deprecated Renamed to `inputProps` in 1.x — collides with the `field` store builder in
|
|
61
|
-
* `basalt-ui/state`. Kept until the forms surface next changes; removal ships as a plain `feat:`,
|
|
62
|
-
* never a major. Write `key={fieldKey(form, path)} {...inputProps(form, path)}` instead.
|
|
63
|
-
*
|
|
64
|
-
* **It is NOT `export const field = inputProps` any more, and the difference is the whole point.**
|
|
65
|
-
* It returns the 1.27 shape — `getInputProps(path)` PLUS `key` — because an alias of the new
|
|
66
|
-
* `inputProps` would have changed what every existing `<TextInput {...field(form, 'x')} />` does
|
|
67
|
-
* without changing whether it compiles: the element loses its reconciler key, keeps its old text
|
|
68
|
-
* through `form.reset()`, and nothing anywhere says so. A deprecated export is a SCHEDULE, not a
|
|
69
|
-
* behaviour change (`../../CLAUDE.md`, "Deprecation lifecycle"), so this one keeps its behaviour
|
|
70
|
-
* byte for byte — including the React 19 `A props object containing a "key" prop is being spread
|
|
71
|
-
* into JSX` warning it has always produced. That warning is the migration signal; `1.29.0` removing
|
|
72
|
-
* the export is the deadline. `basalt/forms-field-key` reports every remaining call site.
|
|
73
|
-
*/
|
|
74
|
-
export function field<Values extends Record<string, unknown>, Path extends LooseKeys<Values>>(
|
|
75
|
-
form: UseFormReturnType<Values>,
|
|
76
|
-
path: Path,
|
|
77
|
-
): ReturnType<UseFormReturnType<Values>['getInputProps']> & { key: string } {
|
|
78
|
-
return { ...form.getInputProps(path), key: form.key(path) }
|
|
79
|
-
}
|
|
@@ -24,8 +24,8 @@ import type { BasaltProps, SlotStylesProps } from '../common/props'
|
|
|
24
24
|
import { assertRequiredProps } from '../common/validate'
|
|
25
25
|
import { BarActionSlot } from '../controls/actions'
|
|
26
26
|
import type { SlotActions } from '../controls/actions'
|
|
27
|
-
import { WidgetHeader } from '../
|
|
28
|
-
import type { WidgetHeaderTitleProps } from '../
|
|
27
|
+
import { WidgetHeader } from '../dashboard'
|
|
28
|
+
import type { WidgetHeaderTitleProps } from '../dashboard'
|
|
29
29
|
import { useFormState } from './form-state'
|
|
30
30
|
import classes from './form-layout.module.css'
|
|
31
31
|
|
package/src/forms/index.ts
CHANGED
|
@@ -20,8 +20,7 @@ export type { UseBasaltFormOptions } from './create-form'
|
|
|
20
20
|
|
|
21
21
|
// ── inputProps ────────────────────────────────────────────────────────────────
|
|
22
22
|
// Two calls, never one object: `key` inside a spread is a React 19 warning. See field.ts.
|
|
23
|
-
|
|
24
|
-
export { inputProps, fieldKey, field } from './field'
|
|
23
|
+
export { inputProps, fieldKey } from './field'
|
|
25
24
|
|
|
26
25
|
// ── layout — law C1's third home, the form row ────────────────────────────────
|
|
27
26
|
export { FormSection, FormRow, FormGroup, FormActions } from './form-layout'
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { useCallback, useRef, useState } from 'react'
|
|
11
11
|
import type { SyntheticEvent } from 'react'
|
|
12
12
|
import type { UseFormReturnType } from '@mantine/form'
|
|
13
|
-
import { toErrorMessage } from '../
|
|
13
|
+
import { toErrorMessage } from '../common/errors'
|
|
14
14
|
|
|
15
15
|
// ── The server field-error envelope ───────────────────────────────────────────
|
|
16
16
|
|
|
@@ -82,7 +82,7 @@ export type UseFormSubmitOptions<Values extends Record<string, unknown>> = {
|
|
|
82
82
|
onError?: (err: unknown) => void
|
|
83
83
|
/**
|
|
84
84
|
* Turns a thrown value into the message `submitError` carries. Defaults to `toErrorMessage`
|
|
85
|
-
* (
|
|
85
|
+
* (root barrel), which already unwraps `Error`, `{ message }` and Eden's `{ status, value }`.
|
|
86
86
|
* Override it to brand the copy or to branch on `errorStatus(err)`.
|
|
87
87
|
*/
|
|
88
88
|
mapError?: (err: unknown) => string
|