@eifi1/ui-kit 0.51.7 → 0.53.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 +178 -67
- package/dist/account/account-parts.d.ts +10 -8
- package/dist/account/account-parts.js +1 -18
- package/dist/account/account-parts.js.map +1 -1
- package/dist/account/data-export-setting-labels.js +1 -1
- package/dist/account/data-export-setting-labels.js.map +1 -1
- package/dist/account/delete-account-setting-labels.js +1 -1
- package/dist/account/delete-account-setting-labels.js.map +1 -1
- package/dist/account/email-change-setting-labels.js +1 -1
- package/dist/account/email-change-setting-labels.js.map +1 -1
- package/dist/account/passkeys-setting.d.ts +9 -2
- package/dist/account/passkeys-setting.js +41 -29
- package/dist/account/passkeys-setting.js.map +1 -1
- package/dist/account/sessions-setting.d.ts +3 -2
- package/dist/account/sessions-setting.js +10 -8
- package/dist/account/sessions-setting.js.map +1 -1
- package/dist/admin/admin-action-confirm.js +1 -3
- package/dist/admin/admin-action-confirm.js.map +1 -1
- package/dist/admin/admin-parts.d.ts +6 -3
- package/dist/admin/admin-parts.js +6 -21
- package/dist/admin/admin-parts.js.map +1 -1
- package/dist/admin/mail-test-card-labels.js +1 -1
- package/dist/admin/mail-test-card-labels.js.map +1 -1
- package/dist/admin/plan-change-labels.d.ts +2 -2
- package/dist/admin/plan-change-labels.js +1 -17
- package/dist/admin/plan-change-labels.js.map +1 -1
- package/dist/admin/plan-change.d.ts +3 -42
- package/dist/admin/plan-change.js +1 -1
- package/dist/admin/plan-change.js.map +1 -1
- package/dist/admin/reviewer-scope-editor.js +3 -3
- package/dist/admin/reviewer-scope-editor.js.map +1 -1
- package/dist/auth/auth-errors-labels.d.ts +11 -0
- package/dist/auth/auth-errors-labels.js +13 -0
- package/dist/auth/auth-errors-labels.js.map +1 -0
- package/dist/auth/auth-errors.d.ts +7 -23
- package/dist/auth/auth-errors.js +2 -15
- package/dist/auth/auth-errors.js.map +1 -1
- package/dist/auth/complete-name-dialog.js +2 -1
- package/dist/auth/complete-name-dialog.js.map +1 -1
- package/dist/auth/forgot-password-form-labels.js +1 -1
- package/dist/auth/forgot-password-form-labels.js.map +1 -1
- package/dist/auth/forgot-password-form.js +7 -2
- package/dist/auth/forgot-password-form.js.map +1 -1
- package/dist/auth/register-form-labels.js +1 -1
- package/dist/auth/register-form-labels.js.map +1 -1
- package/dist/auth/reset-password-form-labels.js +1 -1
- package/dist/auth/reset-password-form-labels.js.map +1 -1
- package/dist/auth/reset-password-form.js +5 -1
- package/dist/auth/reset-password-form.js.map +1 -1
- package/dist/auth/sign-in-form-labels.js +1 -1
- package/dist/auth/sign-in-form-labels.js.map +1 -1
- package/dist/auth/status-parts.d.ts +0 -3
- package/dist/auth/status-parts.js +0 -4
- package/dist/auth/status-parts.js.map +1 -1
- package/dist/auth/verify-email-labels.js +1 -1
- package/dist/auth/verify-email-labels.js.map +1 -1
- package/dist/auth/verify-email.js +7 -2
- package/dist/auth/verify-email.js.map +1 -1
- package/dist/billing/cancel-subscription.d.ts +10 -7
- package/dist/billing/cancel-subscription.js +6 -6
- package/dist/billing/cancel-subscription.js.map +1 -1
- package/dist/billing/plan-card.d.ts +5 -3
- package/dist/billing/plan-card.js +7 -8
- package/dist/billing/plan-card.js.map +1 -1
- package/dist/billing/plan-change-labels.d.ts +42 -0
- package/dist/billing/plan-change-labels.js +22 -0
- package/dist/billing/plan-change-labels.js.map +1 -0
- package/dist/billing/plan-switch.d.ts +9 -6
- package/dist/billing/plan-switch.js +7 -5
- package/dist/billing/plan-switch.js.map +1 -1
- package/dist/chart.d.ts +4 -3
- package/dist/chart.js +72 -4
- package/dist/chart.js.map +1 -1
- package/dist/components/account-chips.d.ts +5 -5
- package/dist/components/account-chips.js +11 -13
- package/dist/components/account-chips.js.map +1 -1
- package/dist/components/alert-banner.d.ts +16 -5
- package/dist/components/alert-banner.js +46 -1
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +13 -0
- package/dist/components/amount-input.js +1 -0
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.js +57 -65
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/bulk-action-bar.d.ts +12 -2
- package/dist/components/bulk-action-bar.js +65 -21
- package/dist/components/bulk-action-bar.js.map +1 -1
- package/dist/components/button-look.d.ts +5 -5
- package/dist/components/button-look.js.map +1 -1
- package/dist/components/button.d.ts +2 -2
- package/dist/components/button.js.map +1 -1
- package/dist/components/chart-edge.d.ts +16 -0
- package/dist/components/chart-edge.js +14 -1
- package/dist/components/chart-edge.js.map +1 -1
- package/dist/components/chart-figure.d.ts +13 -0
- package/dist/components/chart-figure.js +15 -0
- package/dist/components/chart-figure.js.map +1 -0
- package/dist/components/chart-zoom-core.js +1 -4
- package/dist/components/chart-zoom-core.js.map +1 -1
- package/dist/components/chart.d.ts +8 -1
- package/dist/components/chart.js +19 -10
- package/dist/components/chart.js.map +1 -1
- package/dist/components/combobox-core.d.ts +111 -2
- package/dist/components/combobox-core.js +148 -42
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +1 -1
- package/dist/components/combobox.js +141 -130
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/copy-button.d.ts +6 -5
- package/dist/components/copy-button.js +5 -3
- package/dist/components/copy-button.js.map +1 -1
- package/dist/components/country-select.js +2 -2
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/data-table-column-settings.d.ts +1 -1
- package/dist/components/data-table-column-settings.js +33 -7
- package/dist/components/data-table-column-settings.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +4 -2
- package/dist/components/data-table-filters.js +2 -2
- package/dist/components/data-table-filters.js.map +1 -1
- package/dist/components/data-table-fit.d.ts +6 -4
- package/dist/components/data-table-fit.js +4 -1
- package/dist/components/data-table-fit.js.map +1 -1
- package/dist/components/data-table-groups.d.ts +1 -1
- package/dist/components/data-table-groups.js.map +1 -1
- package/dist/components/data-table-head.d.ts +1 -1
- package/dist/components/data-table-head.js +15 -12
- package/dist/components/data-table-head.js.map +1 -1
- package/dist/components/data-table-mobile.d.ts +12 -7
- package/dist/components/data-table-mobile.js +43 -30
- package/dist/components/data-table-mobile.js.map +1 -1
- package/dist/components/data-table-pagination.js +11 -6
- package/dist/components/data-table-pagination.js.map +1 -1
- package/dist/components/data-table-resize.d.ts +1 -1
- package/dist/components/data-table-resize.js.map +1 -1
- package/dist/components/data-table-row-actions.d.ts +2 -2
- package/dist/components/data-table-row-actions.js.map +1 -1
- package/dist/components/data-table-row.d.ts +6 -2
- package/dist/components/data-table-row.js +17 -10
- package/dist/components/data-table-row.js.map +1 -1
- package/dist/components/data-table-selection.d.ts +1 -1
- package/dist/components/data-table-selection.js.map +1 -1
- package/dist/components/data-table-shared.d.ts +10 -2
- package/dist/components/data-table-shared.js +18 -0
- package/dist/components/data-table-shared.js.map +1 -1
- package/dist/components/data-table-sort.d.ts +21 -2
- package/dist/components/data-table-sort.js +24 -0
- package/dist/components/data-table-sort.js.map +1 -1
- package/dist/components/data-table-virtual.d.ts +1 -1
- package/dist/components/data-table-virtual.js +18 -6
- package/dist/components/data-table-virtual.js.map +1 -1
- package/dist/components/data-table.d.ts +13 -4
- package/dist/components/data-table.js +139 -36
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-field.d.ts +25 -3
- package/dist/components/date-field.js +33 -8
- package/dist/components/date-field.js.map +1 -1
- package/dist/components/date-picker.d.ts +16 -3
- package/dist/components/date-picker.js +55 -26
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/date-range-picker.d.ts +38 -24
- package/dist/components/date-range-picker.js +23 -7
- package/dist/components/date-range-picker.js.map +1 -1
- package/dist/components/error-boundary.js +37 -30
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/{ui-labels.d.ts → field-base-labels.d.ts} +0 -2
- package/dist/components/{ui-labels.js → field-base-labels.js} +2 -9
- package/dist/components/field-base-labels.js.map +1 -0
- package/dist/components/field-base.d.ts +4 -3
- package/dist/components/field-base.js +3 -3
- package/dist/components/field-base.js.map +1 -1
- package/dist/components/form-actions.js +11 -5
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/hover-menu.d.ts +3 -0
- package/dist/components/hover-menu.js +5 -3
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/icon-button.js +14 -9
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/inline-confirm.d.ts +43 -0
- package/dist/components/inline-confirm.js +67 -0
- package/dist/components/inline-confirm.js.map +1 -0
- package/dist/components/inline-entity-combobox.d.ts +4 -3
- package/dist/components/inline-entity-combobox.js +148 -140
- package/dist/components/inline-entity-combobox.js.map +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/input.js.map +1 -1
- package/dist/components/jump-calendar.js +28 -112
- package/dist/components/jump-calendar.js.map +1 -1
- package/dist/components/legal-page.d.ts +3 -1
- package/dist/components/legal-page.js +3 -2
- package/dist/components/legal-page.js.map +1 -1
- package/dist/components/legal.d.ts +4 -2
- package/dist/components/legal.js.map +1 -1
- package/dist/components/lightbox.d.ts +2 -2
- package/dist/components/lightbox.js +17 -6
- package/dist/components/lightbox.js.map +1 -1
- package/dist/components/list.d.ts +6 -2
- package/dist/components/list.js +3 -2
- package/dist/components/list.js.map +1 -1
- package/dist/components/measured-grid.d.ts +1 -1
- package/dist/components/measured-grid.js +2 -1
- package/dist/components/measured-grid.js.map +1 -1
- package/dist/components/mini-calendar.js +3 -5
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.js +3 -1
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-grid.d.ts +55 -0
- package/dist/components/month-grid.js +148 -0
- package/dist/components/month-grid.js.map +1 -0
- package/dist/components/month-picker.d.ts +36 -4
- package/dist/components/month-picker.js +123 -146
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-select.d.ts +2 -2
- package/dist/components/multi-select.js +11 -6
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/nav-pills.d.ts +1 -1
- package/dist/components/nav-pills.js.map +1 -1
- package/dist/components/picker-sheet.js +2 -1
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/pie-chart-labels.d.ts +7 -0
- package/dist/components/pie-chart-labels.js +1 -0
- package/dist/components/pie-chart-labels.js.map +1 -1
- package/dist/components/pie-chart.d.ts +5 -3
- package/dist/components/pie-chart.js +7 -22
- package/dist/components/pie-chart.js.map +1 -1
- package/dist/components/popover.d.ts +3 -0
- package/dist/components/popover.js +7 -1
- package/dist/components/popover.js.map +1 -1
- package/dist/components/row-actions.js +2 -1
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/series-chart-axes.d.ts +4 -4
- package/dist/components/series-chart-axes.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +7 -4
- package/dist/components/series-chart-labels.js.map +1 -1
- package/dist/components/series-chart.d.ts +92 -23
- package/dist/components/series-chart.js +74 -30
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/share-card.js +3 -3
- package/dist/components/share-card.js.map +1 -1
- package/dist/components/tabs-labels.d.ts +2 -0
- package/dist/components/tabs-labels.js +11 -0
- package/dist/components/tabs-labels.js.map +1 -0
- package/dist/components/tabs.d.ts +1 -1
- package/dist/components/tabs.js +1 -1
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +2 -2
- package/dist/components/toggle-legend.js +6 -6
- package/dist/components/toggle-legend.js.map +1 -1
- package/dist/components/translation-review-editor.d.ts +4 -2
- package/dist/components/translation-review-editor.js +2 -3
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review.d.ts +2 -1
- package/dist/components/translation-review.js +2 -2
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/treemap-labels.d.ts +31 -0
- package/dist/components/treemap-labels.js +10 -0
- package/dist/components/treemap-labels.js.map +1 -0
- package/dist/components/treemap-layout.d.ts +62 -0
- package/dist/components/treemap-layout.js +126 -0
- package/dist/components/treemap-layout.js.map +1 -0
- package/dist/components/treemap.d.ts +167 -19
- package/dist/components/treemap.js +340 -100
- package/dist/components/treemap.js.map +1 -1
- package/dist/components/use-table-state.d.ts +5 -5
- package/dist/components/use-table-state.js +4 -1
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/data-table.d.ts +1 -2
- package/dist/data-table.js +1 -4
- package/dist/data-table.js.map +1 -1
- package/dist/demo/demo-countdown.d.ts +0 -5
- package/dist/demo/demo-countdown.js +0 -4
- package/dist/demo/demo-countdown.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/eslint/role-utilities.d.ts +36 -7
- package/dist/eslint/role-utilities.js +42 -9
- package/dist/eslint/role-utilities.js.map +1 -1
- package/dist/feedback/feedback-dialog.d.ts +25 -16
- package/dist/feedback/feedback-dialog.js +6 -2
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-mutation.d.ts +18 -0
- package/dist/feedback/feedback-mutation.js +8 -0
- package/dist/feedback/feedback-mutation.js.map +1 -0
- package/dist/feedback/feedback-page-link.d.ts +53 -0
- package/dist/feedback/feedback-page-link.js +37 -0
- package/dist/feedback/feedback-page-link.js.map +1 -0
- package/dist/feedback/feedback-public-list.d.ts +8 -4
- package/dist/feedback/feedback-public-list.js +2 -2
- package/dist/feedback/feedback-public-list.js.map +1 -1
- package/dist/feedback/feedback-replies.d.ts +3 -1
- package/dist/feedback/feedback-replies.js +5 -4
- package/dist/feedback/feedback-replies.js.map +1 -1
- package/dist/feedback/feedback-row-detail-labels.js +0 -3
- package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
- package/dist/feedback/feedback-row-detail.d.ts +33 -44
- package/dist/feedback/feedback-row-detail.js +28 -34
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback/feedback-status-undo.d.ts +17 -14
- package/dist/feedback/feedback-status-undo.js +10 -15
- package/dist/feedback/feedback-status-undo.js.map +1 -1
- package/dist/feedback/feedback-submit-dialog.d.ts +1 -1
- package/dist/feedback/feedback-submit-dialog.js +1 -1
- package/dist/feedback/feedback-submit-dialog.js.map +1 -1
- package/dist/feedback/feedback-submit.js +9 -3
- package/dist/feedback/feedback-submit.js.map +1 -1
- package/dist/feedback/feedback-swipe.d.ts +1 -1
- package/dist/feedback/feedback-swipe.js.map +1 -1
- package/dist/feedback/feedback-table.d.ts +21 -9
- package/dist/feedback/feedback-table.js +45 -25
- package/dist/feedback/feedback-table.js.map +1 -1
- package/dist/feedback/feedback-thread-labels.d.ts +2 -2
- package/dist/feedback/feedback-thread-labels.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +15 -23
- package/dist/feedback/feedback-thread.js +2 -1
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/feedback.d.ts +6 -6
- package/dist/feedback.js +90 -6
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-focus-trap.d.ts +35 -0
- package/dist/hooks/use-focus-trap.js +6 -4
- package/dist/hooks/use-focus-trap.js.map +1 -1
- package/dist/hooks/use-hotkey.d.ts +5 -2
- package/dist/hooks/use-hotkey.js +5 -4
- package/dist/hooks/use-hotkey.js.map +1 -1
- package/dist/hooks/use-long-press.d.ts +7 -4
- package/dist/hooks/use-long-press.js +3 -0
- package/dist/hooks/use-long-press.js.map +1 -1
- package/dist/hooks/use-media-query.js +12 -4
- package/dist/hooks/use-media-query.js.map +1 -1
- package/dist/hooks/use-mounted.d.ts +22 -0
- package/dist/hooks/use-mounted.js +24 -0
- package/dist/hooks/use-mounted.js.map +1 -0
- package/dist/hooks/use-overlay-history.js +19 -3
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/i18n/defaults.js +4 -9
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +7 -18
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +9 -11
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +15 -64
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +7 -18
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +20 -27
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +9 -18
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +7 -19
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +8 -18
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +12 -2
- package/dist/i18n/review.js.map +1 -1
- package/dist/i18n/shared-labels.js +0 -1
- package/dist/i18n/shared-labels.js.map +1 -1
- package/dist/index.d.ts +21 -23
- package/dist/index.js +50 -65
- package/dist/index.js.map +1 -1
- package/dist/landing/routing.d.ts +3 -1
- package/dist/landing/routing.js +4 -7
- package/dist/landing/routing.js.map +1 -1
- package/dist/lib/broadcast.d.ts +17 -0
- package/dist/lib/broadcast.js +30 -0
- package/dist/lib/broadcast.js.map +1 -0
- package/dist/lib/date-format.d.ts +37 -2
- package/dist/lib/date-format.js +34 -8
- package/dist/lib/date-format.js.map +1 -1
- package/dist/lib/day-format.d.ts +46 -0
- package/dist/lib/day-format.js +46 -0
- package/dist/lib/day-format.js.map +1 -0
- package/dist/lib/describe-failure.d.ts +14 -0
- package/dist/lib/describe-failure.js +9 -0
- package/dist/lib/describe-failure.js.map +1 -0
- package/dist/lib/direction.d.ts +16 -0
- package/dist/lib/direction.js +9 -1
- package/dist/lib/direction.js.map +1 -1
- package/dist/lib/focus-left.d.ts +63 -0
- package/dist/lib/focus-left.js +67 -0
- package/dist/lib/focus-left.js.map +1 -0
- package/dist/lib/format.js +5 -14
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/http-status.d.ts +24 -0
- package/dist/lib/http-status.js +14 -0
- package/dist/lib/http-status.js.map +1 -0
- package/dist/lib/intl-cache.d.ts +14 -0
- package/dist/lib/intl-cache.js +15 -0
- package/dist/lib/intl-cache.js.map +1 -0
- package/dist/lib/iso-day.d.ts +11 -0
- package/dist/lib/iso-day.js +8 -0
- package/dist/lib/iso-day.js.map +1 -0
- package/dist/lib/lazy-with-fallback.d.ts +31 -0
- package/dist/lib/lazy-with-fallback.js +40 -0
- package/dist/lib/lazy-with-fallback.js.map +1 -0
- package/dist/lib/path-pattern.d.ts +34 -0
- package/dist/lib/path-pattern.js +24 -0
- package/dist/lib/path-pattern.js.map +1 -0
- package/dist/lib/reduced-motion.d.ts +2 -1
- package/dist/lib/reduced-motion.js.map +1 -1
- package/dist/lib/round.d.ts +2 -1
- package/dist/lib/round.js +2 -1
- package/dist/lib/round.js.map +1 -1
- package/dist/lib/safe-storage.d.ts +24 -5
- package/dist/lib/safe-storage.js +27 -2
- package/dist/lib/safe-storage.js.map +1 -1
- package/dist/lib/session-refresh.js +1 -1
- package/dist/lib/session-refresh.js.map +1 -1
- package/dist/lib/share-format.d.ts +13 -0
- package/dist/lib/share-format.js +10 -0
- package/dist/lib/share-format.js.map +1 -0
- package/dist/lib/text-case.d.ts +17 -0
- package/dist/lib/text-case.js +8 -0
- package/dist/lib/text-case.js.map +1 -0
- package/dist/lib/text-match.d.ts +29 -0
- package/dist/lib/text-match.js +20 -2
- package/dist/lib/text-match.js.map +1 -1
- package/dist/lib/thenable.d.ts +13 -0
- package/dist/lib/thenable.js +8 -0
- package/dist/lib/thenable.js.map +1 -0
- package/dist/lib/translation-review.d.ts +7 -2
- package/dist/lib/translation-review.js +4 -4
- package/dist/lib/translation-review.js.map +1 -1
- package/dist/notifications/device-zone-report.js +1 -1
- package/dist/notifications/device-zone-report.js.map +1 -1
- package/dist/notifications/notification-bell.d.ts +2 -1
- package/dist/notifications/notification-bell.js +3 -3
- package/dist/notifications/notification-bell.js.map +1 -1
- package/dist/notifications/notification-inbox.d.ts +10 -6
- package/dist/notifications/notification-inbox.js +26 -48
- package/dist/notifications/notification-inbox.js.map +1 -1
- package/dist/notifications/notification-parts.d.ts +3 -2
- package/dist/notifications/notification-parts.js +1 -3
- package/dist/notifications/notification-parts.js.map +1 -1
- package/dist/notifications/notification-types-setting.js +1 -1
- package/dist/notifications/notification-types-setting.js.map +1 -1
- package/dist/notifications/notifications-labels.d.ts +6 -0
- package/dist/notifications/notifications-labels.js +1 -0
- package/dist/notifications/notifications-labels.js.map +1 -1
- package/dist/notifications/push-device-setting.js +1 -1
- package/dist/notifications/push-device-setting.js.map +1 -1
- package/dist/notifications/push-devices-setting.d.ts +2 -1
- package/dist/notifications/push-devices-setting.js +27 -49
- package/dist/notifications/push-devices-setting.js.map +1 -1
- package/dist/notifications/quiet-hours-setting.d.ts +10 -3
- package/dist/notifications/quiet-hours-setting.js +65 -26
- package/dist/notifications/quiet-hours-setting.js.map +1 -1
- package/dist/notifications/unsubscribe-panel.js +4 -7
- package/dist/notifications/unsubscribe-panel.js.map +1 -1
- package/dist/notifications/use-push-device.d.ts +39 -1
- package/dist/notifications/use-push-device.js +28 -7
- package/dist/notifications/use-push-device.js.map +1 -1
- package/dist/offline/query-persistence.d.ts +3 -1
- package/dist/offline/query-persistence.js +19 -2
- package/dist/offline/query-persistence.js.map +1 -1
- package/dist/rhf/fields.d.ts +11 -4
- package/dist/rhf/fields.js.map +1 -1
- package/dist/search/command-palette.js +1 -1
- package/dist/search/command-palette.js.map +1 -1
- package/dist/search/global-search.js +19 -9
- package/dist/search/global-search.js.map +1 -1
- package/dist/search/search-index.d.ts +0 -6
- package/dist/search/search-index.js +0 -4
- package/dist/search/search-index.js.map +1 -1
- package/dist/search/use-command-key.d.ts +12 -1
- package/dist/search/use-command-key.js +3 -12
- package/dist/search/use-command-key.js.map +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +6 -1
- package/dist/search.js.map +1 -1
- package/dist/settings/use-settings-focus.js +1 -0
- package/dist/settings/use-settings-focus.js.map +1 -1
- package/dist/shell/account-menu-language.d.ts +5 -3
- package/dist/shell/account-menu-language.js +2 -2
- package/dist/shell/account-menu-language.js.map +1 -1
- package/dist/shell/account-menu-text-size.d.ts +9 -6
- package/dist/shell/account-menu-text-size.js +2 -2
- package/dist/shell/account-menu-text-size.js.map +1 -1
- package/dist/shell/app-shell.d.ts +0 -10
- package/dist/shell/app-shell.js +1 -7
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/company-switcher.d.ts +1 -1
- package/dist/shell/company-switcher.js.map +1 -1
- package/dist/shell/dev-menu.d.ts +1 -1
- package/dist/shell/dev-menu.js +5 -18
- package/dist/shell/dev-menu.js.map +1 -1
- package/dist/shell/top-bar-actions.d.ts +1 -1
- package/dist/shell/top-bar-actions.js.map +1 -1
- package/dist/shell/top-bar-parts-labels.d.ts +1 -3
- package/dist/shell/top-bar-parts-labels.js +0 -1
- package/dist/shell/top-bar-parts-labels.js.map +1 -1
- package/dist/shell.d.ts +7 -10
- package/dist/shell.js +14 -23
- package/dist/shell.js.map +1 -1
- package/dist/sync/engine.d.ts +30 -4
- package/dist/sync/engine.js +172 -52
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync.d.ts +2 -2
- package/dist/sync.js +2 -10
- package/dist/sync.js.map +1 -1
- package/dist/testing/css-variables.js +3 -8
- package/dist/testing/css-variables.js.map +1 -1
- package/dist/theme/chart-palette.d.ts +0 -5
- package/dist/theme/chart-palette.js +0 -2
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/contrast.d.ts +2 -1
- package/dist/theme/contrast.js +3 -8
- package/dist/theme/contrast.js.map +1 -1
- package/dist/theme/palette-store.js +6 -6
- package/dist/theme/palette-store.js.map +1 -1
- package/dist/theme/text-size.js +3 -8
- package/dist/theme/text-size.js.map +1 -1
- package/dist/theme/theme-store.js +3 -6
- package/dist/theme/theme-store.js.map +1 -1
- package/dist/tour/tour-catalogue.d.ts +7 -4
- package/dist/tour/tour-catalogue.js +12 -10
- package/dist/tour/tour-catalogue.js.map +1 -1
- package/dist/tour/tour-dock.d.ts +0 -2
- package/dist/tour/tour-dock.js +0 -2
- package/dist/tour/tour-dock.js.map +1 -1
- package/dist/tour/tour-gates.d.ts +4 -0
- package/dist/tour/tour-gates.js +14 -0
- package/dist/tour/tour-gates.js.map +1 -1
- package/dist/tour/tour-overlay.d.ts +27 -0
- package/dist/tour/tour-overlay.js +741 -0
- package/dist/tour/tour-overlay.js.map +1 -0
- package/dist/tour/tour.d.ts +13 -3
- package/dist/tour/tour.js +59 -781
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour/use-tour-progress.js +1 -1
- package/dist/tour/use-tour-progress.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +5 -1
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +6 -2
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.d.ts +3 -1
- package/dist/wizard/use-wizard.js.map +1 -1
- package/dist/wizard/validation.d.ts +3 -1
- package/dist/wizard/validation.js.map +1 -1
- package/package.json +10 -6
- package/src/account/account-parts.ts +5 -25
- package/src/account/data-export-setting-labels.ts +1 -1
- package/src/account/delete-account-setting-labels.ts +1 -1
- package/src/account/email-change-setting-labels.ts +1 -1
- package/src/account/passkeys-setting.tsx +60 -38
- package/src/account/sessions-setting.tsx +18 -12
- package/src/admin/admin-action-confirm.tsx +1 -5
- package/src/admin/admin-parts.ts +11 -25
- package/src/admin/mail-test-card-labels.ts +1 -1
- package/src/admin/plan-change-labels.ts +5 -23
- package/src/admin/plan-change.tsx +7 -44
- package/src/admin/reviewer-scope-editor.tsx +3 -3
- package/src/auth/auth-errors-labels.ts +23 -0
- package/src/auth/auth-errors.ts +11 -38
- package/src/auth/complete-name-dialog.tsx +4 -1
- package/src/auth/forgot-password-form-labels.ts +1 -1
- package/src/auth/forgot-password-form.tsx +11 -6
- package/src/auth/register-form-labels.ts +1 -1
- package/src/auth/reset-password-form-labels.ts +1 -1
- package/src/auth/reset-password-form.tsx +8 -4
- package/src/auth/sign-in-form-labels.ts +1 -1
- package/src/auth/status-parts.tsx +0 -6
- package/src/auth/verify-email-labels.ts +1 -1
- package/src/auth/verify-email.tsx +11 -6
- package/src/billing/cancel-subscription.tsx +15 -12
- package/src/billing/plan-card.tsx +18 -10
- package/src/billing/plan-change-labels.ts +66 -0
- package/src/billing/plan-switch.tsx +15 -12
- package/src/chart.ts +73 -6
- package/src/components/account-chips.tsx +20 -23
- package/src/components/alert-banner.tsx +82 -5
- package/src/components/amount-input.tsx +18 -7
- package/src/components/autocomplete.tsx +64 -70
- package/src/components/bulk-action-bar.tsx +70 -2
- package/src/components/button-look.ts +5 -5
- package/src/components/button.tsx +2 -2
- package/src/components/chart-edge.ts +34 -1
- package/src/components/chart-figure.tsx +35 -0
- package/src/components/chart-zoom-core.tsx +6 -5
- package/src/components/chart.tsx +26 -5
- package/src/components/combobox-core.tsx +293 -62
- package/src/components/combobox.tsx +183 -153
- package/src/components/copy-button.tsx +34 -23
- package/src/components/country-select.tsx +16 -11
- package/src/components/data-table-column-settings.tsx +52 -10
- package/src/components/data-table-filters.ts +6 -4
- package/src/components/data-table-fit.ts +19 -5
- package/src/components/data-table-groups.tsx +1 -1
- package/src/components/data-table-head.tsx +32 -22
- package/src/components/data-table-mobile.tsx +61 -26
- package/src/components/data-table-pagination.tsx +21 -6
- package/src/components/data-table-resize.ts +1 -1
- package/src/components/data-table-row-actions.tsx +3 -3
- package/src/components/data-table-row.tsx +25 -14
- package/src/components/data-table-selection.ts +1 -1
- package/src/components/data-table-shared.tsx +33 -1
- package/src/components/data-table-sort.ts +57 -2
- package/src/components/data-table-virtual.ts +32 -8
- package/src/components/data-table.tsx +239 -68
- package/src/components/date-field.tsx +66 -9
- package/src/components/date-picker.tsx +125 -49
- package/src/components/date-range-picker.tsx +71 -36
- package/src/components/error-boundary.tsx +53 -39
- package/src/components/field-base-labels.ts +20 -0
- package/src/components/field-base.tsx +13 -7
- package/src/components/form-actions.tsx +20 -5
- package/src/components/hover-menu.tsx +18 -8
- package/src/components/icon-button.tsx +14 -9
- package/src/components/inline-confirm.tsx +116 -0
- package/src/components/inline-entity-combobox.tsx +197 -153
- package/src/components/input.tsx +1 -1
- package/src/components/jump-calendar.tsx +30 -146
- package/src/components/legal-page.tsx +10 -7
- package/src/components/legal.tsx +4 -2
- package/src/components/lightbox.tsx +21 -8
- package/src/components/list.tsx +10 -4
- package/src/components/measured-grid.tsx +8 -4
- package/src/components/mini-calendar.tsx +6 -5
- package/src/components/modal.tsx +6 -1
- package/src/components/month-grid.tsx +229 -0
- package/src/components/month-picker.tsx +192 -200
- package/src/components/multi-select.tsx +21 -9
- package/src/components/nav-pills.tsx +1 -1
- package/src/components/picker-sheet.tsx +12 -9
- package/src/components/pie-chart-labels.ts +8 -0
- package/src/components/pie-chart.tsx +22 -28
- package/src/components/popover.tsx +18 -1
- package/src/components/row-actions.tsx +2 -1
- package/src/components/series-chart-axes.ts +9 -4
- package/src/components/series-chart-labels.ts +7 -4
- package/src/components/series-chart.tsx +253 -87
- package/src/components/share-card.tsx +6 -6
- package/src/components/tabs-labels.ts +13 -0
- package/src/components/tabs.tsx +2 -2
- package/src/components/toggle-legend.tsx +16 -11
- package/src/components/translation-review-editor.tsx +6 -5
- package/src/components/translation-review.tsx +4 -3
- package/src/components/treemap-labels.ts +47 -0
- package/src/components/treemap-layout.ts +210 -0
- package/src/components/treemap.tsx +780 -138
- package/src/components/use-table-state.ts +12 -9
- package/src/data-table.ts +1 -4
- package/src/demo/demo-countdown.ts +3 -8
- package/src/eslint/role-utilities.ts +97 -19
- package/src/feedback/feedback-dialog.tsx +30 -19
- package/src/feedback/feedback-mutation.ts +20 -0
- package/src/feedback/feedback-page-link.ts +90 -0
- package/src/feedback/feedback-public-list.tsx +13 -7
- package/src/feedback/feedback-replies.tsx +13 -6
- package/src/feedback/feedback-row-detail-labels.ts +0 -3
- package/src/feedback/feedback-row-detail.tsx +70 -84
- package/src/feedback/feedback-status-undo.ts +28 -28
- package/src/feedback/feedback-submit-dialog.tsx +2 -2
- package/src/feedback/feedback-submit.tsx +21 -7
- package/src/feedback/feedback-swipe.tsx +1 -1
- package/src/feedback/feedback-table.tsx +86 -38
- package/src/feedback/feedback-thread-labels.ts +2 -2
- package/src/feedback/feedback-thread.tsx +18 -26
- package/src/feedback.ts +89 -6
- package/src/hooks/use-focus-trap.ts +8 -24
- package/src/hooks/use-hotkey.ts +24 -7
- package/src/hooks/use-long-press.ts +16 -2
- package/src/hooks/use-media-query.ts +20 -4
- package/src/hooks/use-mounted.ts +39 -0
- package/src/hooks/use-overlay-history.ts +32 -3
- package/src/i18n/defaults.ts +4 -9
- package/src/i18n/german.ts +8 -19
- package/src/i18n/kit-labels.tsx +9 -11
- package/src/i18n/locales/es.ts +8 -19
- package/src/i18n/locales/fr.ts +21 -30
- package/src/i18n/locales/hu.ts +10 -19
- package/src/i18n/locales/it.ts +8 -20
- package/src/i18n/locales/zh.ts +9 -19
- package/src/i18n/review.ts +12 -2
- package/src/i18n/shared-labels.ts +0 -1
- package/src/index.ts +57 -49
- package/src/landing/routing.tsx +14 -10
- package/src/lib/broadcast.ts +52 -0
- package/src/lib/date-format.ts +89 -13
- package/src/lib/day-format.ts +96 -0
- package/src/lib/describe-failure.ts +18 -0
- package/src/lib/direction.ts +39 -0
- package/src/lib/focus-left.ts +156 -0
- package/src/lib/format.ts +5 -17
- package/src/lib/http-status.ts +38 -0
- package/src/lib/intl-cache.ts +25 -0
- package/src/lib/iso-day.ts +13 -0
- package/src/lib/lazy-with-fallback.ts +82 -0
- package/src/lib/path-pattern.ts +55 -0
- package/src/lib/reduced-motion.ts +2 -1
- package/src/lib/round.ts +7 -2
- package/src/lib/safe-storage.ts +48 -6
- package/src/lib/session-refresh.ts +1 -1
- package/src/lib/share-format.ts +18 -0
- package/src/lib/text-case.ts +19 -0
- package/src/lib/text-match.ts +57 -2
- package/src/lib/thenable.ts +19 -0
- package/src/lib/translation-review.ts +13 -5
- package/src/notifications/device-zone-report.ts +1 -1
- package/src/notifications/notification-bell.tsx +7 -4
- package/src/notifications/notification-inbox.tsx +40 -58
- package/src/notifications/notification-parts.tsx +3 -4
- package/src/notifications/notification-types-setting.tsx +1 -1
- package/src/notifications/notifications-labels.ts +7 -0
- package/src/notifications/push-device-setting.tsx +1 -1
- package/src/notifications/push-devices-setting.tsx +30 -55
- package/src/notifications/quiet-hours-setting.tsx +93 -31
- package/src/notifications/unsubscribe-panel.tsx +4 -7
- package/src/notifications/use-push-device.ts +76 -10
- package/src/offline/query-persistence.ts +35 -3
- package/src/rhf/fields.tsx +11 -4
- package/src/search/command-palette.tsx +2 -4
- package/src/search/global-search.tsx +36 -15
- package/src/search/search-index.ts +0 -15
- package/src/search/use-command-key.ts +13 -12
- package/src/search.ts +10 -1
- package/src/settings/use-settings-focus.ts +3 -0
- package/src/shell/account-menu-language.tsx +6 -4
- package/src/shell/account-menu-text-size.tsx +10 -7
- package/src/shell/app-shell.tsx +0 -13
- package/src/shell/company-switcher.tsx +1 -1
- package/src/shell/dev-menu.tsx +8 -22
- package/src/shell/top-bar-actions.tsx +1 -1
- package/src/shell/top-bar-parts-labels.ts +1 -4
- package/src/shell.ts +21 -17
- package/src/sync/engine.ts +339 -61
- package/src/sync.ts +2 -8
- package/src/testing/css-variables.ts +8 -9
- package/src/theme/chart-palette.ts +0 -2
- package/src/theme/contrast.ts +8 -9
- package/src/theme/palette-store.ts +9 -9
- package/src/theme/text-size.ts +6 -8
- package/src/theme/theme-store.ts +7 -9
- package/src/tour/tour-catalogue.tsx +26 -15
- package/src/tour/tour-dock.ts +0 -2
- package/src/tour/tour-gates.ts +23 -0
- package/src/tour/tour-overlay.tsx +1170 -0
- package/src/tour/tour.tsx +108 -1202
- package/src/tour/use-tour-progress.ts +1 -1
- package/src/wizard/stepper-nav.tsx +3 -1
- package/src/wizard/types.ts +11 -2
- package/src/wizard/use-wizard.ts +2 -1
- package/src/wizard/validation.ts +8 -2
- package/tokens.css +6 -5
- package/dist/components/field-sync-labels.d.ts +0 -2
- package/dist/components/field-sync-labels.js +0 -12
- package/dist/components/field-sync-labels.js.map +0 -1
- package/dist/components/field-sync.d.ts +0 -164
- package/dist/components/field-sync.js +0 -269
- package/dist/components/field-sync.js.map +0 -1
- package/dist/components/section-nav.d.ts +0 -37
- package/dist/components/section-nav.js +0 -33
- package/dist/components/section-nav.js.map +0 -1
- package/dist/components/sign-chip-labels.d.ts +0 -2
- package/dist/components/sign-chip-labels.js +0 -11
- package/dist/components/sign-chip-labels.js.map +0 -1
- package/dist/components/sign-chip.d.ts +0 -68
- package/dist/components/sign-chip.js +0 -47
- package/dist/components/sign-chip.js.map +0 -1
- package/dist/components/ui-labels.js.map +0 -1
- package/dist/lib/isin.d.ts +0 -28
- package/dist/lib/isin.js +0 -36
- package/dist/lib/isin.js.map +0 -1
- package/dist/shell/dev-switchers.d.ts +0 -6
- package/dist/shell/dev-switchers.js +0 -12
- package/dist/shell/dev-switchers.js.map +0 -1
- package/dist/shell/role-switcher.d.ts +0 -21
- package/dist/shell/role-switcher.js +0 -31
- package/dist/shell/role-switcher.js.map +0 -1
- package/src/components/field-sync-labels.ts +0 -13
- package/src/components/field-sync.tsx +0 -482
- package/src/components/section-nav.tsx +0 -65
- package/src/components/sign-chip-labels.ts +0 -12
- package/src/components/sign-chip.tsx +0 -124
- package/src/components/ui-labels.ts +0 -24
- package/src/lib/isin.ts +0 -62
- package/src/shell/dev-switchers.tsx +0 -18
- package/src/shell/role-switcher.tsx +0 -46
|
@@ -24,8 +24,12 @@ import type { CommitScope } from "../components/write-lock.js";
|
|
|
24
24
|
* `onDone` — are all set on `useWizard`. What is set HERE is how the Finish button
|
|
25
25
|
* looks and what wraps it, because that is presentation and usually depends on
|
|
26
26
|
* state the app holds (a "replace everything" toggle, a write lock — `finishCommit`).
|
|
27
|
+
*
|
|
28
|
+
* `TData`'s constraint is `UseWizardOptions`' (its mapped half is for `Parameters<>`).
|
|
27
29
|
*/
|
|
28
|
-
export declare function StepperNav<TData extends
|
|
30
|
+
export declare function StepperNav<TData extends object & {
|
|
31
|
+
[K in keyof TData]: unknown;
|
|
32
|
+
}>({ wizard, children, title, description, className, labels, finishVariant, finishDisabled, finishCommit, finishDisabledReason, doneDisabled, renderFinish, }: {
|
|
29
33
|
wizard: UseWizardReturn<TData>;
|
|
30
34
|
children: ReactNode;
|
|
31
35
|
title?: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/wizard/stepper-nav.tsx"],"sourcesContent":["import { useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { Check } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { Button } from \"../components/button\";\nimport { Spinner } from \"../components/spinner\";\nimport type { ButtonVariant } from \"../components/button-look\";\nimport { Modal } from \"../components/modal\";\nimport { alertFrameClass } from \"../components/alert-banner\";\nimport { WizardContextProvider } from \"./wizard-context\";\nimport { DEFAULT_WIZARD_LABELS } from \"./types\";\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport type { UseWizardReturn, WizardFinishResult, WizardLabels } from \"./types\";\nimport type { CommitScope } from \"../components/write-lock\";\nimport { useTourGateRelease } from \"../tour/tour-gates\";\n\n/**\n * The chrome around a wizard: the step indicator, the step content, and the\n * Cancel / Back / Skip / Next / Finish bar — plus the cancel-confirmation\n * dialog, because \"are you sure you want to lose this\" is part of the wizard's\n * contract rather than something each app should re-decide.\n *\n * It is a component and not a set of parts for one reason: which button shows,\n * and whether it is enabled, is a function of `wizard` state that was getting\n * re-derived (and re-derived slightly differently) at every call site. Finish is\n * gated on `canFinish`, Next and Skip on `nextBlocked`, and Skip only appears on\n * an `optional` step that is not the last — that policy lives here now.\n *\n * The `<StepperNav>` is also what publishes {@link WizardContextProvider}, which\n * is how a mounted step registers its own validator. A step's\n * `useRhfWizardStep` / `useWizardStepValidate` therefore only works inside one.\n *\n * Which buttons show is read off `wizard` (`canCancel`, `canGoBack`, `canDone`,\n * `isCommitStep`), so the policy options — `cancellable`, `onExit`, `commits`,\n * `onDone` — are all set on `useWizard`. What is set HERE is how the Finish button\n * looks and what wraps it, because that is presentation and usually depends on\n * state the app holds (a \"replace everything\" toggle, a write lock — `finishCommit`).\n */\nexport function StepperNav<TData extends Record<string, unknown>>({\n wizard,\n children,\n title,\n description,\n className,\n labels,\n finishVariant = \"accent\",\n finishDisabled = false,\n finishCommit,\n finishDisabledReason,\n doneDisabled = false,\n renderFinish,\n}: {\n wizard: UseWizardReturn<TData>;\n children: ReactNode;\n title?: ReactNode;\n description?: ReactNode;\n className?: string;\n labels?: LabelOverride<WizardLabels>;\n /** The Finish button's variant — `\"danger\"` for a destructive commit (an import\n * that replaces what is there). Default `\"accent\"`. Next keeps `\"accent\"`. */\n finishVariant?: ButtonVariant;\n /** Disable Finish for a reason of the app's own, on top of the kit's gates\n * (`canFinish`, submitting, validating). */\n finishDisabled?: boolean;\n /**\n * Finish COMMITS — {@link Button}'s `commit`, handed to the Finish button. Under a\n * locked {@link WriteLockProvider} it is `aria-disabled` but focusable, with the lock's\n * reason in the kit Tooltip, and a click (or Enter) does not run `wizard.finish`.\n * Every other button of the bar stays live: the steps before the commit write nothing,\n * so the whole wizard can still be walked through on a read-only page.\n *\n * keksdose G1: its YNAB import runs the mapping wizard on the read-only demo (dev#497)\n * and locks only the confirm step's Finish — by cloning `commit` into the element\n * `renderFinish` hands it, because StepperNav took no `commit` of its own. That clone\n * still works; this is the same thing without one. Named `finishCommit` beside\n * `finishVariant` / `finishDisabled` rather than a bare `commit`, which on a component\n * with five buttons would not say which one saves — and would read like `useWizard`'s\n * `commits`, which is a step's policy, not a lock.\n */\n finishCommit?: CommitScope;\n /**\n * Why Finish is not available — {@link Button}'s `disabledReason` on the Finish button:\n * `aria-disabled` and focusable, the reason in the kit Tooltip and on the button's\n * `aria-describedby`, `wizard.finish` not run. The explained form of `finishDisabled`\n * (a native `disabled` takes the button out of the tab order, so its reason never\n * reaches a keyboard). Under a lock with `finishCommit`, the lock's reason wins.\n */\n finishDisabledReason?: ReactNode;\n /**\n * Disable Done — the button of the last step after a commit — while the step still\n * has work of its own running. keksdose's YNAB import ends on a recurring step whose\n * convert loop writes one rule at a time (ynab-import-panel.tsx): leaving mid-loop\n * abandons the rest. Until now the panel dropped `onDone` for the duration, which\n * takes the button away instead of saying \"not yet\": disabled, it stays where the\n * eye left it and comes back to life when the loop ends.\n */\n doneDisabled?: boolean;\n /**\n * Wrap the Finish button: receives the kit's button element — already labelled,\n * gated and wired to `wizard.finish` — and returns what to render in its place.\n *\n * ```tsx\n * renderFinish={(button) => <SaveGuard lock={lock}>{button}</SaveGuard>}\n * ```\n *\n * A wrapper and not a replacement (`finishButton: (props) => …`) or a hook\n * (`onBeforeFinish`): the app's need is to put something AROUND the commit — a\n * write-lock tooltip, which has to sit on a wrapping element because a disabled\n * button receives no pointer events — while the kit keeps owning what the button\n * says and when it is enabled. A replacement would hand every app the gating to\n * re-derive (the problem this component exists to end); `onBeforeFinish` can veto\n * a click but cannot explain a disabled button. The element is a plain `<Button>`,\n * so a wrapper that clones it with `disabled` works as expected.\n *\n * For the write lock itself there is no need to wrap any more: `finishCommit` (and\n * `finishDisabledReason`) put the kit Button's own Tooltip on Finish. An element\n * cloned with `commit` here keeps working the same way.\n */\n renderFinish?: (button: ReactElement) => ReactNode;\n}) {\n const l = useKitLabels(\"wizard\", DEFAULT_WIZARD_LABELS, labels);\n const cancelTitleId = useId();\n\n // The step area never shrinks below the tallest step shown so far, so the button\n // bar does not jump up and down as Next and Back move between a long step and a\n // short one. This replaces a fixed `min-h-[300px]` (carried over from Kastlan,\n // whose steps are all long forms), which did the same for steps under 300px but\n // left a short wizard — two fields a step — with a ~200px hole above its buttons.\n // Measured rather than guessed: the floor is whatever the app's own steps need.\n const contentRef = useRef<HTMLDivElement>(null);\n const [contentFloor, setContentFloor] = useState(0);\n useLayoutEffect(() => {\n const el = contentRef.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => {\n const h = el.offsetHeight;\n setContentFloor((floor) => (h > floor ? h : floor));\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n const contextValue = useMemo(\n () => ({\n registerStepValidate: wizard.registerStepValidate,\n setNextBlocked: wizard.setNextBlocked,\n }),\n [wizard.registerStepValidate, wizard.setNextBlocked],\n );\n\n // Finish is only valid once every step before the last has been completed or\n // skipped (derived once in useWizard). Prevents a restored last-step index\n // (empty data after a reload) from firing an empty submit.\n const canFinish = wizard.canFinish;\n\n // A running tour's `awaitGate` steps (0.50, docs/tours-harmonization.md §11.9): Next\n // releases \"wizard-next\" when the wizard moved and refuses it when a validator kept it\n // in place; Finish releases \"wizard-finish\" once `onComplete` resolved and refuses it,\n // with the error's message, when it threw. No-ops without a TourProvider or a tour.\n const tourGates = useTourGateRelease();\n // `useWizard`'s goNext/finish resolve their outcome at run time (see their types); a\n // wizard object built some other way resolves nothing, and nothing is signalled.\n const goNext = async () => {\n const moved = (await wizard.goNext()) as unknown as boolean | undefined;\n if (moved === true) tourGates.release(\"wizard-next\");\n else if (moved === false) tourGates.refuse(\"wizard-next\");\n };\n const finish = async () => {\n const result = (await wizard.finish()) as unknown as WizardFinishResult | undefined;\n if (!result) return;\n if (result.ok) tourGates.release(\"wizard-finish\");\n else tourGates.refuse(\"wizard-finish\", result.error);\n };\n\n // Pending while the step's validators run (an async server check): a spinner and\n // `aria-busy`, and disabled — the hook's own re-entry guard still refuses a second\n // press, this just stops it looking like the first one did nothing.\n const pending = wizard.isValidating;\n const stepNextLabel = wizard.currentStep.nextLabel;\n\n // Every branch below assigns it; no initial value to overwrite.\n let forwardButton: ReactNode;\n if (wizard.isCommitStep) {\n const finishButton = (\n <Button\n variant={finishVariant}\n data-tour=\"wizard-finish\"\n onClick={() => void finish()}\n // The kit Button's lock: a reason (its own or, with `commit`, the provider's)\n // wins over `disabled` and keeps the button reachable, saying why.\n commit={finishCommit}\n disabledReason={finishDisabledReason}\n disabled={wizard.isSubmitting || pending || !canFinish || finishDisabled}\n aria-busy={wizard.isSubmitting || pending || undefined}\n >\n {/* Decorative: the button's text is what a reader hears. */}\n {pending && <Spinner label={null} className=\"size-4\" />}\n {wizard.isSubmitting ? l.submitting : (stepNextLabel ?? l.finish)}\n </Button>\n );\n forwardButton = renderFinish ? renderFinish(finishButton) : finishButton;\n } else if (wizard.isLastStep) {\n // The last step after a commit. Nothing to finish; Done only when the app gave\n // an `onDone` — otherwise the step carries its own way out.\n forwardButton = wizard.canDone ? (\n <Button variant=\"accent\" data-tour=\"wizard-done\" onClick={wizard.done} disabled={doneDisabled}>\n {stepNextLabel ?? l.done ?? DEFAULT_WIZARD_LABELS.done}\n </Button>\n ) : null;\n } else {\n forwardButton = (\n <Button\n variant=\"accent\"\n data-tour=\"wizard-next\"\n onClick={() => void goNext()}\n disabled={wizard.nextBlocked || pending}\n aria-busy={pending || undefined}\n >\n {pending && <Spinner label={null} className=\"size-4\" />}\n {stepNextLabel ?? l.next}\n </Button>\n );\n }\n\n return (\n <div className={cn(\"flex flex-col gap-6\", className)}>\n {(title || description) && (\n <div>\n {title && <h1 className=\"text-2xl font-bold tracking-tight text-primary\">{title}</h1>}\n {description && <p className=\"mt-1 text-sm text-muted\">{description}</p>}\n </div>\n )}\n\n {/* Step indicator */}\n <nav aria-label={l.steps}>\n <ol className=\"flex w-full items-center\">\n {wizard.steps.map((step, index) => {\n const status = wizard.stepStatus(index);\n const isLast = index === wizard.steps.length - 1;\n\n return (\n <li key={step.id} className={cn(\"flex items-center\", !isLast && \"flex-1\")}>\n <button\n type=\"button\"\n onClick={() => wizard.goToStep(index)}\n // `canGoToStep` adds the commit lock: once past a committing step,\n // the steps up to it are done with and cannot be reopened.\n disabled={status === \"upcoming\" || !wizard.canGoToStep(index)}\n aria-current={status === \"active\" ? \"step\" : undefined}\n className={cn(\n \"group flex flex-col items-center gap-1.5\",\n status === \"upcoming\" ? \"cursor-default\" : \"cursor-pointer\",\n )}\n >\n {/* Circle */}\n <span\n className={cn(\n \"flex size-8 shrink-0 items-center justify-center rounded-full border-2 text-xs font-semibold transition-colors\",\n status === \"completed\" && \"border-brand bg-brand text-brand-contrast\",\n status === \"active\" && \"border-brand bg-surface text-brand\",\n status === \"upcoming\" && \"border-subtle bg-surface text-placeholder\",\n status === \"skipped\" && \"border-subtle bg-surface-2 text-muted\",\n )}\n >\n {status === \"completed\" ? <Check className=\"size-4\" /> : index + 1}\n </span>\n {/* Label — hidden on mobile, where the line below carries it */}\n <span\n className={cn(\n \"hidden text-xs font-medium md:block\",\n status === \"active\" || status === \"completed\" ? \"text-primary\" : \"text-muted\",\n )}\n >\n {step.label}\n </span>\n </button>\n\n {/* Connector line */}\n {!isLast && (\n <div\n className={cn(\"mx-2 h-0.5 flex-1\", status === \"completed\" ? \"bg-brand\" : \"bg-[var(--border)]\")}\n />\n )}\n </li>\n );\n })}\n </ol>\n {/* Mobile: the labels above are hidden, so name the current step here */}\n <p className=\"mt-2 text-center text-sm font-medium text-primary md:hidden\">\n {l.step(wizard.currentStepIndex + 1, wizard.steps.length)}\n {\" — \"}\n {wizard.currentStep.label}\n </p>\n </nav>\n\n {/* Step content */}\n <WizardContextProvider value={contextValue}>\n <div style={contentFloor > 0 ? { minHeight: contentFloor } : undefined}>\n <div ref={contentRef}>{children}</div>\n </div>\n </WizardContextProvider>\n\n {/* Submit error */}\n {wizard.error && (\n <div role=\"alert\" className={cn(\"text-sm text-danger\", alertFrameClass(\"danger\"))}>\n {wizard.error}\n </div>\n )}\n\n {/* Navigation bar */}\n <div className=\"flex items-center justify-between border-t border-subtle pt-4\">\n {/* The empty span keeps the right-hand group on the right when Cancel is off. */}\n {wizard.canCancel ? (\n <Button variant=\"secondary\" onClick={wizard.cancel}>\n {l.cancel}\n </Button>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-2\">\n {wizard.canGoBack && (\n // Disabled, not hidden, while the commit runs: it comes back if the commit\n // fails, and a button that vanishes mid-click is its own kind of surprise.\n <Button variant=\"secondary\" onClick={wizard.goBack} disabled={wizard.isSubmitting}>\n {l.back}\n </Button>\n )}\n {wizard.currentStep.optional && !wizard.isLastStep && !wizard.isCommitStep && (\n <Button variant=\"ghost\" onClick={wizard.skip} disabled={wizard.nextBlocked || wizard.isValidating}>\n {l.skip}\n </Button>\n )}\n {forwardButton}\n </div>\n </div>\n\n {/* Cancel confirmation */}\n {wizard.showCancelDialog && (\n <Modal onClose={wizard.dismissCancel} labelledBy={cancelTitleId} className=\"space-y-4\">\n <div className=\"space-y-1.5\">\n <h2 id={cancelTitleId} className=\"text-lg font-semibold text-primary\">\n {l.cancelTitle}\n </h2>\n <p className=\"text-sm text-muted\">{l.confirmCancel}</p>\n </div>\n <div className=\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\">\n <Button variant=\"secondary\" onClick={wizard.dismissCancel}>\n {l.cancelDismissLabel}\n </Button>\n <Button variant=\"danger\" onClick={wizard.confirmCancel}>\n {l.cancelConfirmLabel}\n </Button>\n </div>\n </Modal>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/stepper-nav.tsx"],"sourcesContent":["import { useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { Check } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { Button } from \"../components/button\";\nimport { Spinner } from \"../components/spinner\";\nimport type { ButtonVariant } from \"../components/button-look\";\nimport { Modal } from \"../components/modal\";\nimport { alertFrameClass } from \"../components/alert-banner\";\nimport { WizardContextProvider } from \"./wizard-context\";\nimport { DEFAULT_WIZARD_LABELS } from \"./types\";\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport type { UseWizardReturn, WizardFinishResult, WizardLabels } from \"./types\";\nimport type { CommitScope } from \"../components/write-lock\";\nimport { useTourGateRelease } from \"../tour/tour-gates\";\n\n/**\n * The chrome around a wizard: the step indicator, the step content, and the\n * Cancel / Back / Skip / Next / Finish bar — plus the cancel-confirmation\n * dialog, because \"are you sure you want to lose this\" is part of the wizard's\n * contract rather than something each app should re-decide.\n *\n * It is a component and not a set of parts for one reason: which button shows,\n * and whether it is enabled, is a function of `wizard` state that was getting\n * re-derived (and re-derived slightly differently) at every call site. Finish is\n * gated on `canFinish`, Next and Skip on `nextBlocked`, and Skip only appears on\n * an `optional` step that is not the last — that policy lives here now.\n *\n * The `<StepperNav>` is also what publishes {@link WizardContextProvider}, which\n * is how a mounted step registers its own validator. A step's\n * `useRhfWizardStep` / `useWizardStepValidate` therefore only works inside one.\n *\n * Which buttons show is read off `wizard` (`canCancel`, `canGoBack`, `canDone`,\n * `isCommitStep`), so the policy options — `cancellable`, `onExit`, `commits`,\n * `onDone` — are all set on `useWizard`. What is set HERE is how the Finish button\n * looks and what wraps it, because that is presentation and usually depends on\n * state the app holds (a \"replace everything\" toggle, a write lock — `finishCommit`).\n *\n * `TData`'s constraint is `UseWizardOptions`' (its mapped half is for `Parameters<>`).\n */\nexport function StepperNav<TData extends object & { [K in keyof TData]: unknown }>({\n wizard,\n children,\n title,\n description,\n className,\n labels,\n finishVariant = \"accent\",\n finishDisabled = false,\n finishCommit,\n finishDisabledReason,\n doneDisabled = false,\n renderFinish,\n}: {\n wizard: UseWizardReturn<TData>;\n children: ReactNode;\n title?: ReactNode;\n description?: ReactNode;\n className?: string;\n labels?: LabelOverride<WizardLabels>;\n /** The Finish button's variant — `\"danger\"` for a destructive commit (an import\n * that replaces what is there). Default `\"accent\"`. Next keeps `\"accent\"`. */\n finishVariant?: ButtonVariant;\n /** Disable Finish for a reason of the app's own, on top of the kit's gates\n * (`canFinish`, submitting, validating). */\n finishDisabled?: boolean;\n /**\n * Finish COMMITS — {@link Button}'s `commit`, handed to the Finish button. Under a\n * locked {@link WriteLockProvider} it is `aria-disabled` but focusable, with the lock's\n * reason in the kit Tooltip, and a click (or Enter) does not run `wizard.finish`.\n * Every other button of the bar stays live: the steps before the commit write nothing,\n * so the whole wizard can still be walked through on a read-only page.\n *\n * keksdose G1: its YNAB import runs the mapping wizard on the read-only demo (dev#497)\n * and locks only the confirm step's Finish — by cloning `commit` into the element\n * `renderFinish` hands it, because StepperNav took no `commit` of its own. That clone\n * still works; this is the same thing without one. Named `finishCommit` beside\n * `finishVariant` / `finishDisabled` rather than a bare `commit`, which on a component\n * with five buttons would not say which one saves — and would read like `useWizard`'s\n * `commits`, which is a step's policy, not a lock.\n */\n finishCommit?: CommitScope;\n /**\n * Why Finish is not available — {@link Button}'s `disabledReason` on the Finish button:\n * `aria-disabled` and focusable, the reason in the kit Tooltip and on the button's\n * `aria-describedby`, `wizard.finish` not run. The explained form of `finishDisabled`\n * (a native `disabled` takes the button out of the tab order, so its reason never\n * reaches a keyboard). Under a lock with `finishCommit`, the lock's reason wins.\n */\n finishDisabledReason?: ReactNode;\n /**\n * Disable Done — the button of the last step after a commit — while the step still\n * has work of its own running. keksdose's YNAB import ends on a recurring step whose\n * convert loop writes one rule at a time (ynab-import-panel.tsx): leaving mid-loop\n * abandons the rest. Until now the panel dropped `onDone` for the duration, which\n * takes the button away instead of saying \"not yet\": disabled, it stays where the\n * eye left it and comes back to life when the loop ends.\n */\n doneDisabled?: boolean;\n /**\n * Wrap the Finish button: receives the kit's button element — already labelled,\n * gated and wired to `wizard.finish` — and returns what to render in its place.\n *\n * ```tsx\n * renderFinish={(button) => <SaveGuard lock={lock}>{button}</SaveGuard>}\n * ```\n *\n * A wrapper and not a replacement (`finishButton: (props) => …`) or a hook\n * (`onBeforeFinish`): the app's need is to put something AROUND the commit — a\n * write-lock tooltip, which has to sit on a wrapping element because a disabled\n * button receives no pointer events — while the kit keeps owning what the button\n * says and when it is enabled. A replacement would hand every app the gating to\n * re-derive (the problem this component exists to end); `onBeforeFinish` can veto\n * a click but cannot explain a disabled button. The element is a plain `<Button>`,\n * so a wrapper that clones it with `disabled` works as expected.\n *\n * For the write lock itself there is no need to wrap any more: `finishCommit` (and\n * `finishDisabledReason`) put the kit Button's own Tooltip on Finish. An element\n * cloned with `commit` here keeps working the same way.\n */\n renderFinish?: (button: ReactElement) => ReactNode;\n}) {\n const l = useKitLabels(\"wizard\", DEFAULT_WIZARD_LABELS, labels);\n const cancelTitleId = useId();\n\n // The step area never shrinks below the tallest step shown so far, so the button\n // bar does not jump up and down as Next and Back move between a long step and a\n // short one. This replaces a fixed `min-h-[300px]` (carried over from Kastlan,\n // whose steps are all long forms), which did the same for steps under 300px but\n // left a short wizard — two fields a step — with a ~200px hole above its buttons.\n // Measured rather than guessed: the floor is whatever the app's own steps need.\n const contentRef = useRef<HTMLDivElement>(null);\n const [contentFloor, setContentFloor] = useState(0);\n useLayoutEffect(() => {\n const el = contentRef.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => {\n const h = el.offsetHeight;\n setContentFloor((floor) => (h > floor ? h : floor));\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n const contextValue = useMemo(\n () => ({\n registerStepValidate: wizard.registerStepValidate,\n setNextBlocked: wizard.setNextBlocked,\n }),\n [wizard.registerStepValidate, wizard.setNextBlocked],\n );\n\n // Finish is only valid once every step before the last has been completed or\n // skipped (derived once in useWizard). Prevents a restored last-step index\n // (empty data after a reload) from firing an empty submit.\n const canFinish = wizard.canFinish;\n\n // A running tour's `awaitGate` steps (0.50, docs/tours-harmonization.md §11.9): Next\n // releases \"wizard-next\" when the wizard moved and refuses it when a validator kept it\n // in place; Finish releases \"wizard-finish\" once `onComplete` resolved and refuses it,\n // with the error's message, when it threw. No-ops without a TourProvider or a tour.\n const tourGates = useTourGateRelease();\n // `useWizard`'s goNext/finish resolve their outcome at run time (see their types); a\n // wizard object built some other way resolves nothing, and nothing is signalled.\n const goNext = async () => {\n const moved = (await wizard.goNext()) as unknown as boolean | undefined;\n if (moved === true) tourGates.release(\"wizard-next\");\n else if (moved === false) tourGates.refuse(\"wizard-next\");\n };\n const finish = async () => {\n const result = (await wizard.finish()) as unknown as WizardFinishResult | undefined;\n if (!result) return;\n if (result.ok) tourGates.release(\"wizard-finish\");\n else tourGates.refuse(\"wizard-finish\", result.error);\n };\n\n // Pending while the step's validators run (an async server check): a spinner and\n // `aria-busy`, and disabled — the hook's own re-entry guard still refuses a second\n // press, this just stops it looking like the first one did nothing.\n const pending = wizard.isValidating;\n const stepNextLabel = wizard.currentStep.nextLabel;\n\n // Every branch below assigns it; no initial value to overwrite.\n let forwardButton: ReactNode;\n if (wizard.isCommitStep) {\n const finishButton = (\n <Button\n variant={finishVariant}\n data-tour=\"wizard-finish\"\n onClick={() => void finish()}\n // The kit Button's lock: a reason (its own or, with `commit`, the provider's)\n // wins over `disabled` and keeps the button reachable, saying why.\n commit={finishCommit}\n disabledReason={finishDisabledReason}\n disabled={wizard.isSubmitting || pending || !canFinish || finishDisabled}\n aria-busy={wizard.isSubmitting || pending || undefined}\n >\n {/* Decorative: the button's text is what a reader hears. */}\n {pending && <Spinner label={null} className=\"size-4\" />}\n {wizard.isSubmitting ? l.submitting : (stepNextLabel ?? l.finish)}\n </Button>\n );\n forwardButton = renderFinish ? renderFinish(finishButton) : finishButton;\n } else if (wizard.isLastStep) {\n // The last step after a commit. Nothing to finish; Done only when the app gave\n // an `onDone` — otherwise the step carries its own way out.\n forwardButton = wizard.canDone ? (\n <Button variant=\"accent\" data-tour=\"wizard-done\" onClick={wizard.done} disabled={doneDisabled}>\n {stepNextLabel ?? l.done ?? DEFAULT_WIZARD_LABELS.done}\n </Button>\n ) : null;\n } else {\n forwardButton = (\n <Button\n variant=\"accent\"\n data-tour=\"wizard-next\"\n onClick={() => void goNext()}\n disabled={wizard.nextBlocked || pending}\n aria-busy={pending || undefined}\n >\n {pending && <Spinner label={null} className=\"size-4\" />}\n {stepNextLabel ?? l.next}\n </Button>\n );\n }\n\n return (\n <div className={cn(\"flex flex-col gap-6\", className)}>\n {(title || description) && (\n <div>\n {title && <h1 className=\"text-2xl font-bold tracking-tight text-primary\">{title}</h1>}\n {description && <p className=\"mt-1 text-sm text-muted\">{description}</p>}\n </div>\n )}\n\n {/* Step indicator */}\n <nav aria-label={l.steps}>\n <ol className=\"flex w-full items-center\">\n {wizard.steps.map((step, index) => {\n const status = wizard.stepStatus(index);\n const isLast = index === wizard.steps.length - 1;\n\n return (\n <li key={step.id} className={cn(\"flex items-center\", !isLast && \"flex-1\")}>\n <button\n type=\"button\"\n onClick={() => wizard.goToStep(index)}\n // `canGoToStep` adds the commit lock: once past a committing step,\n // the steps up to it are done with and cannot be reopened.\n disabled={status === \"upcoming\" || !wizard.canGoToStep(index)}\n aria-current={status === \"active\" ? \"step\" : undefined}\n className={cn(\n \"group flex flex-col items-center gap-1.5\",\n status === \"upcoming\" ? \"cursor-default\" : \"cursor-pointer\",\n )}\n >\n {/* Circle */}\n <span\n className={cn(\n \"flex size-8 shrink-0 items-center justify-center rounded-full border-2 text-xs font-semibold transition-colors\",\n status === \"completed\" && \"border-brand bg-brand text-brand-contrast\",\n status === \"active\" && \"border-brand bg-surface text-brand\",\n status === \"upcoming\" && \"border-subtle bg-surface text-placeholder\",\n status === \"skipped\" && \"border-subtle bg-surface-2 text-muted\",\n )}\n >\n {status === \"completed\" ? <Check className=\"size-4\" /> : index + 1}\n </span>\n {/* Label — hidden on mobile, where the line below carries it */}\n <span\n className={cn(\n \"hidden text-xs font-medium md:block\",\n status === \"active\" || status === \"completed\" ? \"text-primary\" : \"text-muted\",\n )}\n >\n {step.label}\n </span>\n </button>\n\n {/* Connector line */}\n {!isLast && (\n <div\n className={cn(\"mx-2 h-0.5 flex-1\", status === \"completed\" ? \"bg-brand\" : \"bg-[var(--border)]\")}\n />\n )}\n </li>\n );\n })}\n </ol>\n {/* Mobile: the labels above are hidden, so name the current step here */}\n <p className=\"mt-2 text-center text-sm font-medium text-primary md:hidden\">\n {l.step(wizard.currentStepIndex + 1, wizard.steps.length)}\n {\" — \"}\n {wizard.currentStep.label}\n </p>\n </nav>\n\n {/* Step content */}\n <WizardContextProvider value={contextValue}>\n <div style={contentFloor > 0 ? { minHeight: contentFloor } : undefined}>\n <div ref={contentRef}>{children}</div>\n </div>\n </WizardContextProvider>\n\n {/* Submit error */}\n {wizard.error && (\n <div role=\"alert\" className={cn(\"text-sm text-danger\", alertFrameClass(\"danger\"))}>\n {wizard.error}\n </div>\n )}\n\n {/* Navigation bar */}\n <div className=\"flex items-center justify-between border-t border-subtle pt-4\">\n {/* The empty span keeps the right-hand group on the right when Cancel is off. */}\n {wizard.canCancel ? (\n <Button variant=\"secondary\" onClick={wizard.cancel}>\n {l.cancel}\n </Button>\n ) : (\n <span />\n )}\n <div className=\"flex items-center gap-2\">\n {wizard.canGoBack && (\n // Disabled, not hidden, while the commit runs: it comes back if the commit\n // fails, and a button that vanishes mid-click is its own kind of surprise.\n <Button variant=\"secondary\" onClick={wizard.goBack} disabled={wizard.isSubmitting}>\n {l.back}\n </Button>\n )}\n {wizard.currentStep.optional && !wizard.isLastStep && !wizard.isCommitStep && (\n <Button variant=\"ghost\" onClick={wizard.skip} disabled={wizard.nextBlocked || wizard.isValidating}>\n {l.skip}\n </Button>\n )}\n {forwardButton}\n </div>\n </div>\n\n {/* Cancel confirmation */}\n {wizard.showCancelDialog && (\n <Modal onClose={wizard.dismissCancel} labelledBy={cancelTitleId} className=\"space-y-4\">\n <div className=\"space-y-1.5\">\n <h2 id={cancelTitleId} className=\"text-lg font-semibold text-primary\">\n {l.cancelTitle}\n </h2>\n <p className=\"text-sm text-muted\">{l.confirmCancel}</p>\n </div>\n <div className=\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\">\n <Button variant=\"secondary\" onClick={wizard.dismissCancel}>\n {l.cancelDismissLabel}\n </Button>\n <Button variant=\"danger\" onClick={wizard.confirmCancel}>\n {l.cancelConfirmLabel}\n </Button>\n </div>\n </Modal>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/wizard/types.d.ts
CHANGED
|
@@ -61,7 +61,9 @@ export interface WizardStepConfig {
|
|
|
61
61
|
export type WizardUrlSync = boolean | {
|
|
62
62
|
param?: string;
|
|
63
63
|
};
|
|
64
|
-
export interface UseWizardOptions<TData extends
|
|
64
|
+
export interface UseWizardOptions<TData extends object & {
|
|
65
|
+
[K in keyof TData]: unknown;
|
|
66
|
+
}> {
|
|
65
67
|
steps: WizardStepConfig[];
|
|
66
68
|
initialData?: Partial<TData>;
|
|
67
69
|
onComplete?: (data: TData) => void | Promise<void>;
|
|
@@ -123,7 +125,9 @@ export interface WizardFinishResult {
|
|
|
123
125
|
ok: boolean;
|
|
124
126
|
error?: string;
|
|
125
127
|
}
|
|
126
|
-
export interface UseWizardReturn<TData extends
|
|
128
|
+
export interface UseWizardReturn<TData extends object & {
|
|
129
|
+
[K in keyof TData]: unknown;
|
|
130
|
+
}> {
|
|
127
131
|
currentStepIndex: number;
|
|
128
132
|
currentStep: WizardStepConfig;
|
|
129
133
|
steps: WizardStepConfig[];
|
package/dist/wizard/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/wizard/types.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { DEFAULT_WIZARD_LABELS } from \"./wizard-labels\";\n\nexport type FieldErrors = Record<string, string>;\n\nexport type ValidateResult = boolean | { ok: boolean; errors?: FieldErrors };\n\nexport interface WizardStepConfig {\n /** Unique step identifier. */\n id: string;\n /**\n * What the step is called in the indicator.\n *\n * A `ReactNode`, not a translation key: the engine never resolves strings.\n * An app with i18n maps its own keys at the `useWizard` call — the same split\n * `TourStep` and `DataTableLabels` already use.\n */\n label: ReactNode;\n /** Whether the step can be skipped. */\n optional?: boolean;\n /**\n * Validation gate before advancing.\n * - Return `true` / `false` to just block.\n * - Return `{ ok: false, errors: { fieldName: \"message\" } }` to also surface\n * per-field error messages via `wizard.fieldErrors`.\n */\n validate?: () => ValidateResult | Promise<ValidateResult>;\n /**\n * The forward button's label on THIS step, in place of `labels.next` — \"Analyse\",\n * \"Import\", \"Send code\". On the committing step (see {@link commits}) it labels\n * Finish instead of `labels.finish`; `labels.submitting` still replaces it while\n * the commit runs. Like `label`, a node the app has already translated.\n */\n nextLabel?: ReactNode;\n /**\n * This step's forward button COMMITS: it runs `onComplete` (via `finish`) instead\n * of just advancing. Without the flag the last step commits, as it always has.\n *\n * Flag a step before the last and every step after it becomes a post-commit step —\n * an import's \"here is what happened, set up recurring rules?\" page:\n * - a successful `onComplete` advances onto the next step (a failed one stays, with\n * `wizard.error`, exactly like a last-step Finish);\n * - from there Back, the indicator and `goBack`/`goToStep` cannot cross back over\n * the commit, and Cancel is hidden — there is nothing left to discard;\n * - the last post-commit step's button is Done, which calls `onDone` (and is not\n * shown when there is no `onDone`: the step then carries its own way out).\n *\n * Only the FIRST flagged step counts. Steps after it may still validate, skip and\n * use `nextLabel` like any other.\n */\n commits?: boolean;\n}\n\n/**\n * Where the active step lives. `true` (the default) mirrors it to `?step=N` through\n * react-router, as every wizard before 0.8 did; `{ param }` renames that param (two\n * wizards on one page, or a page that already uses `step`); `false` keeps it in\n * memory only and needs no router at all.\n *\n * Read once, at mount: switching it on a mounted wizard has no effect.\n */\nexport type WizardUrlSync = boolean | { param?: string };\n\nexport interface UseWizardOptions<TData extends Record<string, unknown>> {\n steps: WizardStepConfig[];\n initialData?: Partial<TData>;\n onComplete?: (data: TData) => void | Promise<void>;\n onCancel?: () => void;\n /**\n * Leave the wizard BACKWARDS: when given, Back is shown on the first step too and\n * calls this — keksdose's import panel, whose first step's Back returns to the\n * dropzone the file came from. No confirm dialog: going back is not discarding.\n */\n onExit?: () => void;\n /**\n * The Done button on the last step after a committing step (see\n * {@link WizardStepConfig.commits}). Without it no Done button is rendered, and the\n * post-commit step is expected to carry its own way out.\n */\n onDone?: () => void;\n /**\n * Whether the chrome offers Cancel at all. Default `true`. `false` hides the button\n * (`wizard.canCancel` is false); `wizard.cancel()` still works if the app calls it.\n */\n cancellable?: boolean;\n /**\n * Whether Cancel asks first (\"Discard this form?\"). Default `true`. `false` makes\n * `cancel()` call `onCancel` straight away — for a wizard that has nothing to lose\n * yet, or one whose data is kept elsewhere.\n */\n confirmCancel?: boolean;\n /**\n * Mirror the active step to the URL. Default `true` (`?step=N`, needs a router).\n * See {@link WizardUrlSync}.\n *\n * The param is WRITTEN only, never read back (see the note in `use-wizard.ts`). It\n * is removed again when the wizard is left through the kit: a confirmed Cancel,\n * `onExit` from the first step's Back, and Done — each before the app's callback\n * runs, so an app that navigates away navigates from a clean URL. It is NOT removed\n * after `onComplete` on a last step: an app navigating away there would otherwise\n * be pulled back by the kit's own URL write. An app that stays on the page after a\n * final Finish drops the param itself, or uses `urlSync: false`.\n */\n urlSync?: WizardUrlSync;\n /**\n * Shown when a step blocks with no per-field detail — an RHF step surfacing\n * its own inline messages, say — so the user learns why the wizard did not\n * advance. Falls back to `wizard.missingRequired` from the {@link UiKitProvider},\n * then to English.\n */\n missingRequiredMessage?: string;\n /**\n * How to surface {@link missingRequiredMessage}. Defaults to the kit's `toast.error`\n * (sonner, an optional peer, is loaded only when a toast is shown). Pass your own to\n * route it somewhere else, or to a no-op to silence it.\n */\n onValidationFailed?: (message: string) => void;\n}\n\nexport type StepStatus = \"upcoming\" | \"active\" | \"completed\" | \"skipped\";\n\n/** What `finish` resolves to at run time (0.50): whether the commit went through, and the\n * error's message when `onComplete` threw. `undefined` when there was nothing to run. */\nexport interface WizardFinishResult {\n ok: boolean;\n error?: string;\n}\nexport interface UseWizardReturn<TData extends Record<string, unknown>> {\n // Navigation\n currentStepIndex: number;\n currentStep: WizardStepConfig;\n steps: WizardStepConfig[];\n isFirstStep: boolean;\n isLastStep: boolean;\n /** True once every step before the committing one (the last, unless a step is\n * flagged `commits`) has been completed or skipped. */\n canFinish: boolean;\n /** The step whose forward button commits: the first `commits` step, else the last. */\n commitStepIndex: number;\n /** The active step is {@link commitStepIndex}: the chrome shows Finish, not Next. */\n isCommitStep: boolean;\n /** `onComplete` has resolved. Once the active step is past the committing one, Back\n * cannot cross it and Cancel is hidden. */\n committed: boolean;\n /** Whether Back is offered here: not on the first step unless `onExit` was given,\n * and never back across a commit. (Disabled, not hidden, while `isSubmitting`.) */\n canGoBack: boolean;\n /** Whether Cancel is offered: `cancellable`, and not after a commit. */\n canCancel: boolean;\n /** Whether Done is offered: the last step, past a commit, with an `onDone`. */\n canDone: boolean;\n /** Whether the indicator may jump to `index` — a completed/skipped/active step on\n * the right side of any commit. `goToStep` refuses anything this rejects. */\n canGoToStep: (index: number) => boolean;\n /** Validates the step and moves on. (0.50: at run time it resolves `true` when it\n * moved, `false` when a validator kept it in place — what StepperNav tells a running\n * tour, docs/tours-harmonization.md §11.9. The declared type stays `Promise<void>`.) */\n goNext: () => Promise<void>;\n /** One step back — or, on the first step, `onExit`. Refused while submitting. */\n goBack: () => void;\n goToStep: (index: number) => void;\n skip: () => void;\n cancel: () => void;\n /** Validates and commits (`onComplete`). (0.50: at run time it resolves `{ ok, error? }`\n * — see {@link WizardFinishResult}; the declared type stays `Promise<void>`.) */\n finish: () => Promise<void>;\n /** Leave after the post-commit steps: drops the URL param, then calls `onDone`. */\n done: () => void;\n /** Register a validator for the mounted step (used by useRhfWizardStep). */\n registerStepValidate: (fn: () => ValidateResult | Promise<ValidateResult>) => () => void;\n /** True when the mounted step has blocked forward navigation (Next/Skip disabled). */\n nextBlocked: boolean;\n /** Set by the active step (via useWizardNextGate) to disable/enable Next+Skip. */\n setNextBlocked: (blocked: boolean) => void;\n\n // Step state\n stepStatus: (index: number) => StepStatus;\n completedSteps: Set<number>;\n\n // Data\n data: TData;\n updateData: (partial: Partial<TData>) => void;\n\n // Loading / error\n isSubmitting: boolean;\n /** A `goNext`/`finish` is running the step's validators (an async server check,\n * say). The chrome shows Next pending — spinner, `aria-busy`, disabled. */\n isValidating: boolean;\n error: string | null;\n clearError: () => void;\n\n // Per-field validation errors (populated by the last failed goNext)\n fieldErrors: FieldErrors;\n clearFieldErrors: () => void;\n\n // Cancel dialog\n showCancelDialog: boolean;\n confirmCancel: () => void;\n dismissCancel: () => void;\n}\n\nexport interface SummaryItem {\n label: ReactNode;\n value: ReactNode;\n}\n\nexport interface SummarySection {\n label: ReactNode;\n /**\n * Which step the section's edit button jumps back to. Leave it out for a section\n * that belongs to no step — file-level counts, a total — and the card has no edit\n * button, instead of borrowing one from whichever step it was parked under.\n */\n stepIndex?: number;\n items: SummaryItem[];\n}\n\n/**\n * Every string the wizard chrome shows. All optional with English defaults, so a\n * monolingual app can mount `StepperNav` bare; a translated app passes the whole\n * object once (typically from a thin app-side wrapper).\n */\nexport interface WizardLabels {\n cancel: string;\n back: string;\n next: string;\n skip: string;\n finish: string;\n /** Shown on Finish while `isSubmitting`. */\n submitting: string;\n /** `aria-label` for the step indicator's `<nav>`. */\n steps: string;\n /** The mobile \"Step 2 of 5\" line. */\n step: (current: number, total: number) => string;\n /** Heading of the cancel-confirmation dialog. */\n cancelTitle: string;\n /** Body of the cancel-confirmation dialog. */\n confirmCancel: string;\n /** Confirm button in the cancel dialog. */\n cancelConfirmLabel: string;\n /** Dismiss button in the cancel dialog (\"keep editing\"). */\n cancelDismissLabel: string;\n /** Heading above a {@link WizardSummary}. */\n reviewTitle: string;\n /** `aria-label` on a summary section's edit button. */\n edit: string;\n /**\n * Toasted (or handed to `onValidationFailed`) when a step blocks Next without\n * saying which field is at fault. `useWizard`'s `missingRequiredMessage` wins.\n *\n * This and `genericError` are OPTIONAL, unlike every key above, for the reason\n * `TwoFactorSettingLabels.qrAlt` is: apps annotate a full translation as\n * `const DE: WizardLabels = {…}`, and a new required key would be a compile error\n * in each of them on the next update. The defaults below always carry both.\n */\n missingRequired?: string;\n /** `wizard.error` when `onComplete` rejects with nothing readable — a thrown\n * non-Error, or an Error whose message is empty. Optional: see above. */\n genericError?: string;\n /** The button on the last step after a committing step (`onDone`). Optional: see\n * `missingRequired`. */\n done?: string;\n}\n\n// The English words live in ./wizard-labels, a module of words (round 0.34).\nexport { DEFAULT_WIZARD_LABELS };\n"],"mappings":";AACA,SAAS,6BAA6B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/types.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { DEFAULT_WIZARD_LABELS } from \"./wizard-labels\";\n\nexport type FieldErrors = Record<string, string>;\n\nexport type ValidateResult = boolean | { ok: boolean; errors?: FieldErrors };\n\nexport interface WizardStepConfig {\n /** Unique step identifier. */\n id: string;\n /**\n * What the step is called in the indicator.\n *\n * A `ReactNode`, not a translation key: the engine never resolves strings.\n * An app with i18n maps its own keys at the `useWizard` call — the same split\n * `TourStep` and `DataTableLabels` already use.\n */\n label: ReactNode;\n /** Whether the step can be skipped. */\n optional?: boolean;\n /**\n * Validation gate before advancing.\n * - Return `true` / `false` to just block.\n * - Return `{ ok: false, errors: { fieldName: \"message\" } }` to also surface\n * per-field error messages via `wizard.fieldErrors`.\n */\n validate?: () => ValidateResult | Promise<ValidateResult>;\n /**\n * The forward button's label on THIS step, in place of `labels.next` — \"Analyse\",\n * \"Import\", \"Send code\". On the committing step (see {@link commits}) it labels\n * Finish instead of `labels.finish`; `labels.submitting` still replaces it while\n * the commit runs. Like `label`, a node the app has already translated.\n */\n nextLabel?: ReactNode;\n /**\n * This step's forward button COMMITS: it runs `onComplete` (via `finish`) instead\n * of just advancing. Without the flag the last step commits, as it always has.\n *\n * Flag a step before the last and every step after it becomes a post-commit step —\n * an import's \"here is what happened, set up recurring rules?\" page:\n * - a successful `onComplete` advances onto the next step (a failed one stays, with\n * `wizard.error`, exactly like a last-step Finish);\n * - from there Back, the indicator and `goBack`/`goToStep` cannot cross back over\n * the commit, and Cancel is hidden — there is nothing left to discard;\n * - the last post-commit step's button is Done, which calls `onDone` (and is not\n * shown when there is no `onDone`: the step then carries its own way out).\n *\n * Only the FIRST flagged step counts. Steps after it may still validate, skip and\n * use `nextLabel` like any other.\n */\n commits?: boolean;\n}\n\n/**\n * Where the active step lives. `true` (the default) mirrors it to `?step=N` through\n * react-router, as every wizard before 0.8 did; `{ param }` renames that param (two\n * wizards on one page, or a page that already uses `step`); `false` keeps it in\n * memory only and needs no router at all.\n *\n * Read once, at mount: switching it on a mounted wizard has no effect.\n */\nexport type WizardUrlSync = boolean | { param?: string };\n\n// `TData extends object`, not `Record<string, unknown>` (0.52, API-2, refactor audit\n// 2026-10-30): an `interface` has no index signature and failed the old constraint, so\n// kastlan gave 13 wizard data interfaces one — which made every key legal, a misspelt one\n// in `updateData` included. The wizard reads only `keyof TData`.\n// The mapped half constrains nothing (every object type satisfies it): it is there for\n// `Parameters<typeof useWizard>` / `ReturnType<typeof useWizard>`, which read the\n// constraint, and `object` has no keys — a wrapper's `data.amount` stopped type-checking\n// (0.52 code review). With it they read an indexable record, as under 0.51. The same on\n// `UseWizardReturn`, `useWizard`, `StepperNav` and `requiredFieldsValidator`.\nexport interface UseWizardOptions<TData extends object & { [K in keyof TData]: unknown }> {\n steps: WizardStepConfig[];\n initialData?: Partial<TData>;\n onComplete?: (data: TData) => void | Promise<void>;\n onCancel?: () => void;\n /**\n * Leave the wizard BACKWARDS: when given, Back is shown on the first step too and\n * calls this — keksdose's import panel, whose first step's Back returns to the\n * dropzone the file came from. No confirm dialog: going back is not discarding.\n */\n onExit?: () => void;\n /**\n * The Done button on the last step after a committing step (see\n * {@link WizardStepConfig.commits}). Without it no Done button is rendered, and the\n * post-commit step is expected to carry its own way out.\n */\n onDone?: () => void;\n /**\n * Whether the chrome offers Cancel at all. Default `true`. `false` hides the button\n * (`wizard.canCancel` is false); `wizard.cancel()` still works if the app calls it.\n */\n cancellable?: boolean;\n /**\n * Whether Cancel asks first (\"Discard this form?\"). Default `true`. `false` makes\n * `cancel()` call `onCancel` straight away — for a wizard that has nothing to lose\n * yet, or one whose data is kept elsewhere.\n */\n confirmCancel?: boolean;\n /**\n * Mirror the active step to the URL. Default `true` (`?step=N`, needs a router).\n * See {@link WizardUrlSync}.\n *\n * The param is WRITTEN only, never read back (see the note in `use-wizard.ts`). It\n * is removed again when the wizard is left through the kit: a confirmed Cancel,\n * `onExit` from the first step's Back, and Done — each before the app's callback\n * runs, so an app that navigates away navigates from a clean URL. It is NOT removed\n * after `onComplete` on a last step: an app navigating away there would otherwise\n * be pulled back by the kit's own URL write. An app that stays on the page after a\n * final Finish drops the param itself, or uses `urlSync: false`.\n */\n urlSync?: WizardUrlSync;\n /**\n * Shown when a step blocks with no per-field detail — an RHF step surfacing\n * its own inline messages, say — so the user learns why the wizard did not\n * advance. Falls back to `wizard.missingRequired` from the {@link UiKitProvider},\n * then to English.\n */\n missingRequiredMessage?: string;\n /**\n * How to surface {@link missingRequiredMessage}. Defaults to the kit's `toast.error`\n * (sonner, an optional peer, is loaded only when a toast is shown). Pass your own to\n * route it somewhere else, or to a no-op to silence it.\n */\n onValidationFailed?: (message: string) => void;\n}\n\nexport type StepStatus = \"upcoming\" | \"active\" | \"completed\" | \"skipped\";\n\n/** What `finish` resolves to at run time (0.50): whether the commit went through, and the\n * error's message when `onComplete` threw. `undefined` when there was nothing to run. */\nexport interface WizardFinishResult {\n ok: boolean;\n error?: string;\n}\nexport interface UseWizardReturn<TData extends object & { [K in keyof TData]: unknown }> {\n // Navigation\n currentStepIndex: number;\n currentStep: WizardStepConfig;\n steps: WizardStepConfig[];\n isFirstStep: boolean;\n isLastStep: boolean;\n /** True once every step before the committing one (the last, unless a step is\n * flagged `commits`) has been completed or skipped. */\n canFinish: boolean;\n /** The step whose forward button commits: the first `commits` step, else the last. */\n commitStepIndex: number;\n /** The active step is {@link commitStepIndex}: the chrome shows Finish, not Next. */\n isCommitStep: boolean;\n /** `onComplete` has resolved. Once the active step is past the committing one, Back\n * cannot cross it and Cancel is hidden. */\n committed: boolean;\n /** Whether Back is offered here: not on the first step unless `onExit` was given,\n * and never back across a commit. (Disabled, not hidden, while `isSubmitting`.) */\n canGoBack: boolean;\n /** Whether Cancel is offered: `cancellable`, and not after a commit. */\n canCancel: boolean;\n /** Whether Done is offered: the last step, past a commit, with an `onDone`. */\n canDone: boolean;\n /** Whether the indicator may jump to `index` — a completed/skipped/active step on\n * the right side of any commit. `goToStep` refuses anything this rejects. */\n canGoToStep: (index: number) => boolean;\n /** Validates the step and moves on. (0.50: at run time it resolves `true` when it\n * moved, `false` when a validator kept it in place — what StepperNav tells a running\n * tour, docs/tours-harmonization.md §11.9. The declared type stays `Promise<void>`.) */\n goNext: () => Promise<void>;\n /** One step back — or, on the first step, `onExit`. Refused while submitting. */\n goBack: () => void;\n goToStep: (index: number) => void;\n skip: () => void;\n cancel: () => void;\n /** Validates and commits (`onComplete`). (0.50: at run time it resolves `{ ok, error? }`\n * — see {@link WizardFinishResult}; the declared type stays `Promise<void>`.) */\n finish: () => Promise<void>;\n /** Leave after the post-commit steps: drops the URL param, then calls `onDone`. */\n done: () => void;\n /** Register a validator for the mounted step (used by useRhfWizardStep). */\n registerStepValidate: (fn: () => ValidateResult | Promise<ValidateResult>) => () => void;\n /** True when the mounted step has blocked forward navigation (Next/Skip disabled). */\n nextBlocked: boolean;\n /** Set by the active step (via useWizardNextGate) to disable/enable Next+Skip. */\n setNextBlocked: (blocked: boolean) => void;\n\n // Step state\n stepStatus: (index: number) => StepStatus;\n completedSteps: Set<number>;\n\n // Data\n data: TData;\n updateData: (partial: Partial<TData>) => void;\n\n // Loading / error\n isSubmitting: boolean;\n /** A `goNext`/`finish` is running the step's validators (an async server check,\n * say). The chrome shows Next pending — spinner, `aria-busy`, disabled. */\n isValidating: boolean;\n error: string | null;\n clearError: () => void;\n\n // Per-field validation errors (populated by the last failed goNext)\n fieldErrors: FieldErrors;\n clearFieldErrors: () => void;\n\n // Cancel dialog\n showCancelDialog: boolean;\n confirmCancel: () => void;\n dismissCancel: () => void;\n}\n\nexport interface SummaryItem {\n label: ReactNode;\n value: ReactNode;\n}\n\nexport interface SummarySection {\n label: ReactNode;\n /**\n * Which step the section's edit button jumps back to. Leave it out for a section\n * that belongs to no step — file-level counts, a total — and the card has no edit\n * button, instead of borrowing one from whichever step it was parked under.\n */\n stepIndex?: number;\n items: SummaryItem[];\n}\n\n/**\n * Every string the wizard chrome shows. All optional with English defaults, so a\n * monolingual app can mount `StepperNav` bare; a translated app passes the whole\n * object once (typically from a thin app-side wrapper).\n */\nexport interface WizardLabels {\n cancel: string;\n back: string;\n next: string;\n skip: string;\n finish: string;\n /** Shown on Finish while `isSubmitting`. */\n submitting: string;\n /** `aria-label` for the step indicator's `<nav>`. */\n steps: string;\n /** The mobile \"Step 2 of 5\" line. */\n step: (current: number, total: number) => string;\n /** Heading of the cancel-confirmation dialog. */\n cancelTitle: string;\n /** Body of the cancel-confirmation dialog. */\n confirmCancel: string;\n /** Confirm button in the cancel dialog. */\n cancelConfirmLabel: string;\n /** Dismiss button in the cancel dialog (\"keep editing\"). */\n cancelDismissLabel: string;\n /** Heading above a {@link WizardSummary}. */\n reviewTitle: string;\n /** `aria-label` on a summary section's edit button. */\n edit: string;\n /**\n * Toasted (or handed to `onValidationFailed`) when a step blocks Next without\n * saying which field is at fault. `useWizard`'s `missingRequiredMessage` wins.\n *\n * This and `genericError` are OPTIONAL, unlike every key above, for the reason\n * `TwoFactorSettingLabels.qrAlt` is: apps annotate a full translation as\n * `const DE: WizardLabels = {…}`, and a new required key would be a compile error\n * in each of them on the next update. The defaults below always carry both.\n */\n missingRequired?: string;\n /** `wizard.error` when `onComplete` rejects with nothing readable — a thrown\n * non-Error, or an Error whose message is empty. Optional: see above. */\n genericError?: string;\n /** The button on the last step after a committing step (`onDone`). Optional: see\n * `missingRequired`. */\n done?: string;\n}\n\n// The English words live in ./wizard-labels, a module of words (round 0.34).\nexport { DEFAULT_WIZARD_LABELS };\n"],"mappings":";AACA,SAAS,6BAA6B;","names":[]}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { UseWizardOptions, UseWizardReturn } from "./types.js";
|
|
2
|
-
export declare function useWizard<TData extends
|
|
2
|
+
export declare function useWizard<TData extends object & {
|
|
3
|
+
[K in keyof TData]: unknown;
|
|
4
|
+
}>(options: UseWizardOptions<TData>): UseWizardReturn<TData>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/wizard/use-wizard.ts"],"sourcesContent":["/**\n * The multi-step wizard state machine: step index, completed/skipped sets,\n * per-step async validators, collected data, field errors, submit and the\n * cancel-confirm gate.\n *\n * Domain-free, and router-free on request. By default it mirrors the active step\n * to `?step=N`, so it must be mounted inside a react-router context — the same\n * requirement `DataTable` already carries. `urlSync: false` keeps the step in\n * memory and needs no router; `urlSync: { param }` renames the param. It only ever\n * WRITES that param (see the note on `initialStep` below for why it deliberately\n * does not read it back), and removes it when the wizard is left through the kit\n * (see `UseWizardOptions.urlSync`).\n *\n * The committing step is the last one unless a step says `commits: true`; the\n * steps after that one are post-commit steps (see `WizardStepConfig.commits`).\n */\nimport { useCallback, useEffect, useLayoutEffect, useReducer, useRef, useState } from \"react\";\nimport { useSearchParams } from \"react-router\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport { DEFAULT_WIZARD_LABELS } from \"./types\";\nimport { toast } from \"../components/toast\";\nimport type {\n WizardFinishResult,\n FieldErrors,\n StepStatus,\n UseWizardOptions,\n UseWizardReturn,\n ValidateResult,\n WizardStepConfig,\n} from \"./types\";\n\ninterface WizardState<TData> {\n currentStepIndex: number;\n completedSteps: Set<number>;\n skippedSteps: Set<number>;\n data: TData;\n isSubmitting: boolean;\n isValidating: boolean;\n error: string | null;\n fieldErrors: FieldErrors;\n showCancelDialog: boolean;\n /** The step whose `onComplete` resolved, or null before any commit. */\n committedStepIndex: number | null;\n}\n\n/** Past the commit: the active step comes after the step that committed. Moves to\n * that step or any before it are refused from here. */\nfunction isAfterCommit(state: { currentStepIndex: number; committedStepIndex: number | null }) {\n return state.committedStepIndex !== null && state.currentStepIndex > state.committedStepIndex;\n}\n\n/** Whether `index` is on the far side of a commit the wizard has already moved past. */\nfunction crossesCommit(state: { currentStepIndex: number; committedStepIndex: number | null }, index: number) {\n return isAfterCommit(state) && index <= (state.committedStepIndex as number);\n}\n\ntype WizardAction<TData> =\n // `from` is the step the move was decided ON. A move whose step is no longer the\n // current one is stale — see `goNext` — and is dropped rather than applied relative\n // to wherever the wizard has got to since.\n | { type: \"GO_NEXT\"; from: number }\n | { type: \"GO_BACK\" }\n | { type: \"GO_TO_STEP\"; index: number }\n | { type: \"SKIP\"; from: number }\n | { type: \"UPDATE_DATA\"; partial: Partial<TData> }\n | { type: \"COMMITTED\"; from: number }\n | { type: \"SET_SUBMITTING\"; value: boolean }\n | { type: \"SET_VALIDATING\"; value: boolean }\n | { type: \"SET_ERROR\"; error: string | null }\n | { type: \"SET_FIELD_ERRORS\"; errors: FieldErrors }\n | { type: \"SHOW_CANCEL_DIALOG\"; show: boolean };\n\nfunction createReducer<TData>(stepCount: number) {\n return function reducer(state: WizardState<TData>, action: WizardAction<TData>): WizardState<TData> {\n switch (action.type) {\n case \"GO_NEXT\": {\n if (action.from !== state.currentStepIndex) return state;\n const completed = new Set(state.completedSteps);\n completed.add(state.currentStepIndex);\n const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);\n return { ...state, currentStepIndex: nextIndex, completedSteps: completed, fieldErrors: {} };\n }\n case \"GO_BACK\": {\n const prevIndex = Math.max(state.currentStepIndex - 1, 0);\n if (crossesCommit(state, prevIndex)) return state;\n return { ...state, currentStepIndex: prevIndex, fieldErrors: {} };\n }\n case \"GO_TO_STEP\": {\n if (action.index < 0 || action.index >= stepCount) return state;\n if (crossesCommit(state, action.index)) return state;\n if (!state.completedSteps.has(action.index) && action.index > state.currentStepIndex) {\n return state;\n }\n return { ...state, currentStepIndex: action.index };\n }\n case \"SKIP\": {\n if (action.from !== state.currentStepIndex) return state;\n const skipped = new Set(state.skippedSteps);\n skipped.add(state.currentStepIndex);\n const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);\n return { ...state, currentStepIndex: nextIndex, skippedSteps: skipped };\n }\n case \"UPDATE_DATA\": {\n // Clear error for any field that is now being edited (so typing makes\n // the red message disappear).\n const nextErrors = { ...state.fieldErrors };\n for (const key of Object.keys(action.partial)) {\n delete nextErrors[key];\n }\n return { ...state, data: { ...state.data, ...action.partial }, fieldErrors: nextErrors };\n }\n case \"COMMITTED\": {\n // Recorded whatever step committed, so `wizard.committed` reads true after any\n // successful Finish. Only a commit with steps after it MOVES: the default\n // last-step commit stays where it is and looks exactly as it did before 0.8.\n const committed = { ...state, committedStepIndex: action.from };\n if (action.from !== state.currentStepIndex || action.from >= stepCount - 1) {\n return committed;\n }\n const completed = new Set(state.completedSteps);\n completed.add(action.from);\n return {\n ...committed,\n currentStepIndex: action.from + 1,\n completedSteps: completed,\n fieldErrors: {},\n };\n }\n case \"SET_SUBMITTING\":\n return { ...state, isSubmitting: action.value };\n case \"SET_VALIDATING\":\n return { ...state, isValidating: action.value };\n case \"SET_ERROR\":\n return { ...state, error: action.error };\n case \"SET_FIELD_ERRORS\":\n return { ...state, fieldErrors: action.errors };\n case \"SHOW_CANCEL_DIALOG\":\n return { ...state, showCancelDialog: action.show };\n default:\n return state;\n }\n };\n}\n\nexport function useWizard<TData extends Record<string, unknown>>(\n options: UseWizardOptions<TData>,\n): UseWizardReturn<TData> {\n const {\n steps,\n initialData,\n onComplete,\n onCancel,\n onExit,\n onDone,\n cancellable = true,\n confirmCancel: askBeforeCancel = true,\n urlSync = true,\n missingRequiredMessage,\n onValidationFailed,\n } = options;\n // The latest `steps`, for the stable callbacks below. Synced after commit (before\n // paint, and before any handler can run) rather than written during render.\n const stepsRef = useRef<WizardStepConfig[]>(steps);\n useLayoutEffect(() => {\n stepsRef.current = steps;\n }, [steps]);\n // A hook, not a component, but it produces two sentences of its own — the\n // \"fill in the required fields\" toast and the fallback submit error — and both\n // reach the user. So it reads the provider like any component does; only the two\n // strings are depended on below, so the callbacks stay stable across renders.\n // The `??` is for the type only: both keys are optional on `WizardLabels` (see\n // there), and the merge never replaces a default with `undefined`.\n const wizardLabels = useKitLabels(\"wizard\", DEFAULT_WIZARD_LABELS, {\n missingRequired: missingRequiredMessage,\n });\n const missingRequired = wizardLabels.missingRequired ?? DEFAULT_WIZARD_LABELS.missingRequired;\n const genericError = wizardLabels.genericError ?? DEFAULT_WIZARD_LABELS.genericError;\n\n // Validators registered by the CURRENT step's mounted components (RHF steps\n // register via `useRhfWizardStep` through the wizard context). Only the active\n // step's validators are present — each unregisters on unmount. `goNext` and\n // `finish` run them (via `runStepValidators`) alongside the step config's own\n // `validate`.\n const validatorsRef = useRef(new Map<symbol, () => ValidateResult | Promise<ValidateResult>>());\n const registerStepValidate = useCallback((fn: () => ValidateResult | Promise<ValidateResult>) => {\n const token = Symbol();\n validatorsRef.current.set(token, fn);\n return () => {\n validatorsRef.current.delete(token);\n };\n }, []);\n\n // Chosen once, at mount: a hook may not be called conditionally, but it may be\n // chosen once and then called unconditionally on every render — which is what\n // makes `urlSync: false` work with no router above the wizard at all.\n const [useStepUrl] = useState(() => (urlSync === false ? useNoStepUrl : useSearchParamStepUrl));\n const [urlParam] = useState(() => (typeof urlSync === \"object\" && urlSync.param ? urlSync.param : \"step\"));\n // The wizard's entered `data` and `completedSteps` are NOT persisted across a\n // reload / deep-link (the reducer re-inits to empty defaults on every mount).\n // Restoring a later step index from `?step=N` would therefore leave the wizard\n // on (or past) the last step with an EMPTY data set, letting Finish submit an\n // empty payload. Always start at step 0 so a reload re-runs the wizard from a\n // valid state instead of losing the entered data silently.\n const initialStep = 0;\n\n const reducer = createReducer<TData>(steps.length);\n const [state, dispatch] = useReducer(reducer, {\n currentStepIndex: initialStep,\n completedSteps: new Set<number>(),\n skippedSteps: new Set<number>(),\n data: (initialData ?? {}) as TData,\n isSubmitting: false,\n isValidating: false,\n error: null,\n fieldErrors: {},\n showCancelDialog: false,\n committedStepIndex: null,\n });\n\n // A mounted step can DISABLE forward navigation (Next/Skip) while its own\n // state is invalid (e.g. tenant shares != 100%). Defaults to false, so\n // wizards that never call setNextBlocked behave exactly as before.\n const [nextBlocked, setNextBlockedState] = useState(false);\n const setNextBlocked = useCallback((blocked: boolean) => setNextBlockedState(blocked), []);\n\n // Sync step index to URL (a no-op under `urlSync: false`). `clearStepUrl` drops\n // the param when the wizard is left through the kit, and stops the sync writing\n // it back.\n const clearStepUrl = useStepUrl(urlParam, state.currentStepIndex);\n\n // `steps`, not `stepsRef.current`: reading a ref in render is what the hooks\n // linter (rightly) objects to, and the ref only catches up after the commit.\n const currentStep = steps[state.currentStepIndex];\n const isFirstStep = state.currentStepIndex === 0;\n const isLastStep = state.currentStepIndex === steps.length - 1;\n // The step whose forward button commits: the first one flagged `commits`, else the\n // last — which is every wizard written before the flag existed.\n const flagged = steps.findIndex((s) => s.commits);\n const commitStepIndex = flagged === -1 ? steps.length - 1 : flagged;\n const isCommitStep = state.currentStepIndex === commitStepIndex;\n const afterCommit = isAfterCommit(state);\n // Finish is only valid once every step before the committing one has actually\n // been walked through (completed or skipped). Guards against a restored/last-step\n // index submitting a payload built from empty defaults.\n const canFinish = steps.every(\n (_, index) => index >= commitStepIndex || state.completedSteps.has(index) || state.skippedSteps.has(index),\n );\n const canGoBack = (!isFirstStep || onExit !== undefined) && !crossesCommit(state, state.currentStepIndex - 1);\n const canCancel = cancellable && !afterCommit;\n const canDone = isLastStep && afterCommit && onDone !== undefined;\n\n // Runs the active step's config `validate` plus every validator its mounted\n // components registered (RHF steps via useRhfWizardStep), AND-combining the\n // results and merging any per-field errors. Returns `true` when the step\n // passes (or registered no validators). Shared by `goNext` and `finish` so a\n // form-bearing last step is validated (and its values flushed) on Finish too.\n const runStepValidators = useCallback(async (): Promise<boolean> => {\n const step = stepsRef.current[state.currentStepIndex];\n const validators = [...validatorsRef.current.values()];\n if (step.validate) validators.push(step.validate);\n if (validators.length === 0) return true;\n let ok = true;\n let errors: FieldErrors = {};\n try {\n for (const validate of validators) {\n const result = await validate();\n const vOk = typeof result === \"boolean\" ? result : result.ok;\n if (!vOk) {\n ok = false;\n if (typeof result !== \"boolean\" && result.errors) {\n errors = { ...errors, ...result.errors };\n }\n }\n }\n } catch (err) {\n // FALL THROUGH, do not return. A bare `return false` here blocked Next while\n // skipping the `if (!ok)` block below — the only thing that shows a message,\n // and whose own comment is \"say something anyway, so the user is not left with\n // a Next button that silently does nothing\". So a validator that threw (an\n // async one whose request rejected, a null dereference in a field rule)\n // produced exactly the outcome that block was written to prevent: no field\n // errors, no toast, `onValidationFailed` never called, nothing in the console.\n //\n // The step still must not advance; `ok = false` is what says so. Whatever\n // partial `errors` the loop had collected before the throw are kept — they are\n // real, and an empty set is what routes this to the generic message.\n console.error(\"[wizard] a step validator threw\", err);\n ok = false;\n }\n if (!ok) {\n dispatch({ type: \"SET_FIELD_ERRORS\", errors });\n if (Object.keys(errors).length === 0) {\n // No per-field detail (e.g. an RHF step surfacing its own inline\n // message) — say something anyway, so the user is not left with a Next\n // button that silently does nothing.\n const message = missingRequired;\n if (onValidationFailed) {\n onValidationFailed(message);\n } else {\n // The kit's `toast` loads sonner (an optional peer) lazily, so an app\n // that never trips this never has to install it.\n toast.error(message);\n }\n }\n return false;\n }\n return true;\n }, [state.currentStepIndex, missingRequired, onValidationFailed]);\n\n /**\n * One forward move at a time. A double-click on Next used to validate the CURRENT\n * step twice and then advance twice — the second GO_NEXT landing on a step whose\n * own validators never ran, so a required field two steps in could be walked past.\n * Two layers, because either alone leaves a gap:\n *\n * - this ref refuses a second `goNext`/`skip`/`finish` while one is in flight,\n * which covers an async validator (a server check) of any length;\n * - the `from` on each action drops a move decided on a step that is no longer\n * current, which covers the click that arrives after the first move resolved\n * but before React re-rendered — its closure still holds the old step index.\n */\n const advancingRef = useRef(false);\n\n const goNext = useCallback(async (): Promise<boolean | undefined> => {\n if (advancingRef.current) return undefined;\n advancingRef.current = true;\n const from = state.currentStepIndex;\n // Pending state for the chrome (spinner, aria-busy). The ref above is still what\n // refuses the second click: state would only be seen after a re-render.\n dispatch({ type: \"SET_VALIDATING\", value: true });\n try {\n if (await runStepValidators()) {\n dispatch({ type: \"GO_NEXT\", from });\n return true;\n }\n return false;\n } finally {\n dispatch({ type: \"SET_VALIDATING\", value: false });\n advancingRef.current = false;\n }\n }, [runStepValidators, state.currentStepIndex]);\n\n // Read by `goBack`, which must not move while `onComplete` runs: the commit was\n // decided on the data of the step the user is on, and Back mid-commit would show a\n // step whose edits can no longer reach it. The chrome disables the button too; this\n // covers an app calling `goBack` itself.\n const submittingRef = useRef(false);\n\n const goBack = useCallback(() => {\n if (submittingRef.current) return;\n if (state.currentStepIndex === 0 && onExit) {\n clearStepUrl();\n onExit();\n return;\n }\n // On step 0 with no `onExit` this stays the no-op move (clearing field errors) it\n // always was.\n dispatch({ type: \"GO_BACK\" });\n }, [state.currentStepIndex, onExit, clearStepUrl]);\n\n const goToStep = useCallback((index: number) => {\n dispatch({ type: \"GO_TO_STEP\", index });\n }, []);\n\n const canGoToStep = useCallback(\n (index: number) =>\n index >= 0 &&\n index < steps.length &&\n (index <= state.currentStepIndex || state.completedSteps.has(index)) &&\n !crossesCommit(state, index),\n [steps.length, state],\n );\n\n const skip = useCallback(() => {\n // Not while Next is validating: the skip would land first and the pending GO_NEXT\n // would then be stale anyway — but the user asked for one move, not a race.\n if (advancingRef.current) return;\n dispatch({ type: \"SKIP\", from: state.currentStepIndex });\n }, [state.currentStepIndex]);\n\n const confirmCancel = useCallback(() => {\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: false });\n clearStepUrl();\n onCancel?.();\n }, [onCancel, clearStepUrl]);\n\n const cancel = useCallback(() => {\n if (!askBeforeCancel) {\n confirmCancel();\n return;\n }\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: true });\n }, [askBeforeCancel, confirmCancel]);\n\n const done = useCallback(() => {\n clearStepUrl();\n onDone?.();\n }, [onDone, clearStepUrl]);\n\n const dismissCancel = useCallback(() => {\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: false });\n }, []);\n\n const updateData = useCallback((partial: Partial<TData>) => {\n dispatch({ type: \"UPDATE_DATA\", partial });\n }, []);\n\n const clearError = useCallback(() => {\n dispatch({ type: \"SET_ERROR\", error: null });\n }, []);\n\n const clearFieldErrors = useCallback(() => {\n dispatch({ type: \"SET_FIELD_ERRORS\", errors: {} });\n }, []);\n\n const finish = useCallback(async (): Promise<WizardFinishResult | undefined> => {\n if (!onComplete || !canFinish) return undefined;\n // A commit with steps after it happens once: those steps exist because the\n // data has been written. (A last-step commit keeps its old behaviour — the app\n // may retry Finish after `onComplete` resolved, as it always could.)\n if (commitStepIndex < steps.length - 1 && state.committedStepIndex !== null) return undefined;\n // The same re-entry guard as `goNext`: a double-click on Finish would otherwise\n // validate twice and call `onComplete` twice — `isSubmitting` is only set once\n // validation has passed, too late to stop the second click.\n if (advancingRef.current) return undefined;\n advancingRef.current = true;\n const from = state.currentStepIndex;\n try {\n // Validate the (possibly form-bearing) committing step before submitting,\n // matching the Next gate — review-only steps register no validators and pass\n // through.\n dispatch({ type: \"SET_VALIDATING\", value: true });\n let valid: boolean;\n try {\n valid = await runStepValidators();\n } finally {\n dispatch({ type: \"SET_VALIDATING\", value: false });\n }\n if (!valid) return { ok: false };\n submittingRef.current = true;\n dispatch({ type: \"SET_SUBMITTING\", value: true });\n dispatch({ type: \"SET_ERROR\", error: null });\n try {\n await onComplete(state.data);\n dispatch({ type: \"COMMITTED\", from });\n return { ok: true };\n } catch (err) {\n const message = err instanceof Error && err.message ? err.message : genericError;\n dispatch({ type: \"SET_ERROR\", error: message });\n return { ok: false, error: err instanceof Error && err.message ? err.message : undefined };\n } finally {\n submittingRef.current = false;\n dispatch({ type: \"SET_SUBMITTING\", value: false });\n }\n } finally {\n advancingRef.current = false;\n }\n }, [\n onComplete,\n canFinish,\n commitStepIndex,\n steps.length,\n state.committedStepIndex,\n state.currentStepIndex,\n state.data,\n runStepValidators,\n genericError,\n ]);\n\n const stepStatus = useCallback(\n (index: number): StepStatus => {\n if (index === state.currentStepIndex) return \"active\";\n if (state.skippedSteps.has(index)) return \"skipped\";\n if (state.completedSteps.has(index)) return \"completed\";\n return \"upcoming\";\n },\n [state.currentStepIndex, state.completedSteps, state.skippedSteps],\n );\n\n return {\n currentStepIndex: state.currentStepIndex,\n currentStep,\n steps,\n isFirstStep,\n isLastStep,\n canFinish,\n commitStepIndex,\n isCommitStep,\n committed: state.committedStepIndex !== null,\n canGoBack,\n canCancel,\n canDone,\n canGoToStep,\n // Their outcomes are what StepperNav tells a running tour (0.50); the declared types stay\n // `Promise<void>`, so an app's `let p: Promise<void> = wizard.goNext()` still compiles.\n goNext: goNext as () => Promise<void>,\n goBack,\n goToStep,\n skip,\n cancel,\n finish: finish as () => Promise<void>,\n done,\n registerStepValidate,\n nextBlocked,\n setNextBlocked,\n stepStatus,\n completedSteps: state.completedSteps,\n data: state.data,\n updateData,\n isSubmitting: state.isSubmitting,\n isValidating: state.isValidating,\n error: state.error,\n clearError,\n fieldErrors: state.fieldErrors,\n clearFieldErrors,\n showCancelDialog: state.showCancelDialog,\n confirmCancel,\n dismissCancel,\n };\n}\n\n/**\n * `urlSync: true` / `{ param }`: mirror the active step to `?<param>=N`, and hand back\n * the function that removes it again. Once removed it stays removed — the sync effect\n * re-runs whenever react-router hands out a new setter (every search change), and\n * without the latch it would write the param straight back after the exit cleared it.\n */\nfunction useSearchParamStepUrl(param: string, index: number): () => void {\n const [, setUrlParams] = useSearchParams();\n const leftRef = useRef(false);\n useEffect(() => {\n if (leftRef.current) return;\n setUrlParams(\n (prev) => {\n prev.set(param, String(index));\n return prev;\n },\n { replace: true },\n );\n }, [param, index, setUrlParams]);\n return useCallback(() => {\n if (leftRef.current) return;\n leftRef.current = true;\n setUrlParams(\n (prev) => {\n prev.delete(param);\n return prev;\n },\n { replace: true },\n );\n }, [param, setUrlParams]);\n}\n\n/** `urlSync: false`: the step lives in the reducer only. Touches no router context. */\nfunction useNoStepUrl(_param: string, _index: number): () => void {\n return noop;\n}\n\nfunction noop() {}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/use-wizard.ts"],"sourcesContent":["/**\n * The multi-step wizard state machine: step index, completed/skipped sets,\n * per-step async validators, collected data, field errors, submit and the\n * cancel-confirm gate.\n *\n * Domain-free, and router-free on request. By default it mirrors the active step\n * to `?step=N`, so it must be mounted inside a react-router context — the same\n * requirement `DataTable` already carries. `urlSync: false` keeps the step in\n * memory and needs no router; `urlSync: { param }` renames the param. It only ever\n * WRITES that param (see the note on `initialStep` below for why it deliberately\n * does not read it back), and removes it when the wizard is left through the kit\n * (see `UseWizardOptions.urlSync`).\n *\n * The committing step is the last one unless a step says `commits: true`; the\n * steps after that one are post-commit steps (see `WizardStepConfig.commits`).\n */\nimport { useCallback, useEffect, useLayoutEffect, useReducer, useRef, useState } from \"react\";\nimport { useSearchParams } from \"react-router\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport { DEFAULT_WIZARD_LABELS } from \"./types\";\nimport { toast } from \"../components/toast\";\nimport type {\n WizardFinishResult,\n FieldErrors,\n StepStatus,\n UseWizardOptions,\n UseWizardReturn,\n ValidateResult,\n WizardStepConfig,\n} from \"./types\";\n\ninterface WizardState<TData> {\n currentStepIndex: number;\n completedSteps: Set<number>;\n skippedSteps: Set<number>;\n data: TData;\n isSubmitting: boolean;\n isValidating: boolean;\n error: string | null;\n fieldErrors: FieldErrors;\n showCancelDialog: boolean;\n /** The step whose `onComplete` resolved, or null before any commit. */\n committedStepIndex: number | null;\n}\n\n/** Past the commit: the active step comes after the step that committed. Moves to\n * that step or any before it are refused from here. */\nfunction isAfterCommit(state: { currentStepIndex: number; committedStepIndex: number | null }) {\n return state.committedStepIndex !== null && state.currentStepIndex > state.committedStepIndex;\n}\n\n/** Whether `index` is on the far side of a commit the wizard has already moved past. */\nfunction crossesCommit(state: { currentStepIndex: number; committedStepIndex: number | null }, index: number) {\n return isAfterCommit(state) && index <= (state.committedStepIndex as number);\n}\n\ntype WizardAction<TData> =\n // `from` is the step the move was decided ON. A move whose step is no longer the\n // current one is stale — see `goNext` — and is dropped rather than applied relative\n // to wherever the wizard has got to since.\n | { type: \"GO_NEXT\"; from: number }\n | { type: \"GO_BACK\" }\n | { type: \"GO_TO_STEP\"; index: number }\n | { type: \"SKIP\"; from: number }\n | { type: \"UPDATE_DATA\"; partial: Partial<TData> }\n | { type: \"COMMITTED\"; from: number }\n | { type: \"SET_SUBMITTING\"; value: boolean }\n | { type: \"SET_VALIDATING\"; value: boolean }\n | { type: \"SET_ERROR\"; error: string | null }\n | { type: \"SET_FIELD_ERRORS\"; errors: FieldErrors }\n | { type: \"SHOW_CANCEL_DIALOG\"; show: boolean };\n\nfunction createReducer<TData>(stepCount: number) {\n return function reducer(state: WizardState<TData>, action: WizardAction<TData>): WizardState<TData> {\n switch (action.type) {\n case \"GO_NEXT\": {\n if (action.from !== state.currentStepIndex) return state;\n const completed = new Set(state.completedSteps);\n completed.add(state.currentStepIndex);\n const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);\n return { ...state, currentStepIndex: nextIndex, completedSteps: completed, fieldErrors: {} };\n }\n case \"GO_BACK\": {\n const prevIndex = Math.max(state.currentStepIndex - 1, 0);\n if (crossesCommit(state, prevIndex)) return state;\n return { ...state, currentStepIndex: prevIndex, fieldErrors: {} };\n }\n case \"GO_TO_STEP\": {\n if (action.index < 0 || action.index >= stepCount) return state;\n if (crossesCommit(state, action.index)) return state;\n if (!state.completedSteps.has(action.index) && action.index > state.currentStepIndex) {\n return state;\n }\n return { ...state, currentStepIndex: action.index };\n }\n case \"SKIP\": {\n if (action.from !== state.currentStepIndex) return state;\n const skipped = new Set(state.skippedSteps);\n skipped.add(state.currentStepIndex);\n const nextIndex = Math.min(state.currentStepIndex + 1, stepCount - 1);\n return { ...state, currentStepIndex: nextIndex, skippedSteps: skipped };\n }\n case \"UPDATE_DATA\": {\n // Clear error for any field that is now being edited (so typing makes\n // the red message disappear).\n const nextErrors = { ...state.fieldErrors };\n for (const key of Object.keys(action.partial)) {\n delete nextErrors[key];\n }\n return { ...state, data: { ...state.data, ...action.partial }, fieldErrors: nextErrors };\n }\n case \"COMMITTED\": {\n // Recorded whatever step committed, so `wizard.committed` reads true after any\n // successful Finish. Only a commit with steps after it MOVES: the default\n // last-step commit stays where it is and looks exactly as it did before 0.8.\n const committed = { ...state, committedStepIndex: action.from };\n if (action.from !== state.currentStepIndex || action.from >= stepCount - 1) {\n return committed;\n }\n const completed = new Set(state.completedSteps);\n completed.add(action.from);\n return {\n ...committed,\n currentStepIndex: action.from + 1,\n completedSteps: completed,\n fieldErrors: {},\n };\n }\n case \"SET_SUBMITTING\":\n return { ...state, isSubmitting: action.value };\n case \"SET_VALIDATING\":\n return { ...state, isValidating: action.value };\n case \"SET_ERROR\":\n return { ...state, error: action.error };\n case \"SET_FIELD_ERRORS\":\n return { ...state, fieldErrors: action.errors };\n case \"SHOW_CANCEL_DIALOG\":\n return { ...state, showCancelDialog: action.show };\n default:\n return state;\n }\n };\n}\n\n// The constraint's mapped half: see `UseWizardOptions` (Parameters<> and ReturnType<> read it).\nexport function useWizard<TData extends object & { [K in keyof TData]: unknown }>(\n options: UseWizardOptions<TData>,\n): UseWizardReturn<TData> {\n const {\n steps,\n initialData,\n onComplete,\n onCancel,\n onExit,\n onDone,\n cancellable = true,\n confirmCancel: askBeforeCancel = true,\n urlSync = true,\n missingRequiredMessage,\n onValidationFailed,\n } = options;\n // The latest `steps`, for the stable callbacks below. Synced after commit (before\n // paint, and before any handler can run) rather than written during render.\n const stepsRef = useRef<WizardStepConfig[]>(steps);\n useLayoutEffect(() => {\n stepsRef.current = steps;\n }, [steps]);\n // A hook, not a component, but it produces two sentences of its own — the\n // \"fill in the required fields\" toast and the fallback submit error — and both\n // reach the user. So it reads the provider like any component does; only the two\n // strings are depended on below, so the callbacks stay stable across renders.\n // The `??` is for the type only: both keys are optional on `WizardLabels` (see\n // there), and the merge never replaces a default with `undefined`.\n const wizardLabels = useKitLabels(\"wizard\", DEFAULT_WIZARD_LABELS, {\n missingRequired: missingRequiredMessage,\n });\n const missingRequired = wizardLabels.missingRequired ?? DEFAULT_WIZARD_LABELS.missingRequired;\n const genericError = wizardLabels.genericError ?? DEFAULT_WIZARD_LABELS.genericError;\n\n // Validators registered by the CURRENT step's mounted components (RHF steps\n // register via `useRhfWizardStep` through the wizard context). Only the active\n // step's validators are present — each unregisters on unmount. `goNext` and\n // `finish` run them (via `runStepValidators`) alongside the step config's own\n // `validate`.\n const validatorsRef = useRef(new Map<symbol, () => ValidateResult | Promise<ValidateResult>>());\n const registerStepValidate = useCallback((fn: () => ValidateResult | Promise<ValidateResult>) => {\n const token = Symbol();\n validatorsRef.current.set(token, fn);\n return () => {\n validatorsRef.current.delete(token);\n };\n }, []);\n\n // Chosen once, at mount: a hook may not be called conditionally, but it may be\n // chosen once and then called unconditionally on every render — which is what\n // makes `urlSync: false` work with no router above the wizard at all.\n const [useStepUrl] = useState(() => (urlSync === false ? useNoStepUrl : useSearchParamStepUrl));\n const [urlParam] = useState(() => (typeof urlSync === \"object\" && urlSync.param ? urlSync.param : \"step\"));\n // The wizard's entered `data` and `completedSteps` are NOT persisted across a\n // reload / deep-link (the reducer re-inits to empty defaults on every mount).\n // Restoring a later step index from `?step=N` would therefore leave the wizard\n // on (or past) the last step with an EMPTY data set, letting Finish submit an\n // empty payload. Always start at step 0 so a reload re-runs the wizard from a\n // valid state instead of losing the entered data silently.\n const initialStep = 0;\n\n const reducer = createReducer<TData>(steps.length);\n const [state, dispatch] = useReducer(reducer, {\n currentStepIndex: initialStep,\n completedSteps: new Set<number>(),\n skippedSteps: new Set<number>(),\n data: (initialData ?? {}) as TData,\n isSubmitting: false,\n isValidating: false,\n error: null,\n fieldErrors: {},\n showCancelDialog: false,\n committedStepIndex: null,\n });\n\n // A mounted step can DISABLE forward navigation (Next/Skip) while its own\n // state is invalid (e.g. tenant shares != 100%). Defaults to false, so\n // wizards that never call setNextBlocked behave exactly as before.\n const [nextBlocked, setNextBlockedState] = useState(false);\n const setNextBlocked = useCallback((blocked: boolean) => setNextBlockedState(blocked), []);\n\n // Sync step index to URL (a no-op under `urlSync: false`). `clearStepUrl` drops\n // the param when the wizard is left through the kit, and stops the sync writing\n // it back.\n const clearStepUrl = useStepUrl(urlParam, state.currentStepIndex);\n\n // `steps`, not `stepsRef.current`: reading a ref in render is what the hooks\n // linter (rightly) objects to, and the ref only catches up after the commit.\n const currentStep = steps[state.currentStepIndex];\n const isFirstStep = state.currentStepIndex === 0;\n const isLastStep = state.currentStepIndex === steps.length - 1;\n // The step whose forward button commits: the first one flagged `commits`, else the\n // last — which is every wizard written before the flag existed.\n const flagged = steps.findIndex((s) => s.commits);\n const commitStepIndex = flagged === -1 ? steps.length - 1 : flagged;\n const isCommitStep = state.currentStepIndex === commitStepIndex;\n const afterCommit = isAfterCommit(state);\n // Finish is only valid once every step before the committing one has actually\n // been walked through (completed or skipped). Guards against a restored/last-step\n // index submitting a payload built from empty defaults.\n const canFinish = steps.every(\n (_, index) => index >= commitStepIndex || state.completedSteps.has(index) || state.skippedSteps.has(index),\n );\n const canGoBack = (!isFirstStep || onExit !== undefined) && !crossesCommit(state, state.currentStepIndex - 1);\n const canCancel = cancellable && !afterCommit;\n const canDone = isLastStep && afterCommit && onDone !== undefined;\n\n // Runs the active step's config `validate` plus every validator its mounted\n // components registered (RHF steps via useRhfWizardStep), AND-combining the\n // results and merging any per-field errors. Returns `true` when the step\n // passes (or registered no validators). Shared by `goNext` and `finish` so a\n // form-bearing last step is validated (and its values flushed) on Finish too.\n const runStepValidators = useCallback(async (): Promise<boolean> => {\n const step = stepsRef.current[state.currentStepIndex];\n const validators = [...validatorsRef.current.values()];\n if (step.validate) validators.push(step.validate);\n if (validators.length === 0) return true;\n let ok = true;\n let errors: FieldErrors = {};\n try {\n for (const validate of validators) {\n const result = await validate();\n const vOk = typeof result === \"boolean\" ? result : result.ok;\n if (!vOk) {\n ok = false;\n if (typeof result !== \"boolean\" && result.errors) {\n errors = { ...errors, ...result.errors };\n }\n }\n }\n } catch (err) {\n // FALL THROUGH, do not return. A bare `return false` here blocked Next while\n // skipping the `if (!ok)` block below — the only thing that shows a message,\n // and whose own comment is \"say something anyway, so the user is not left with\n // a Next button that silently does nothing\". So a validator that threw (an\n // async one whose request rejected, a null dereference in a field rule)\n // produced exactly the outcome that block was written to prevent: no field\n // errors, no toast, `onValidationFailed` never called, nothing in the console.\n //\n // The step still must not advance; `ok = false` is what says so. Whatever\n // partial `errors` the loop had collected before the throw are kept — they are\n // real, and an empty set is what routes this to the generic message.\n console.error(\"[wizard] a step validator threw\", err);\n ok = false;\n }\n if (!ok) {\n dispatch({ type: \"SET_FIELD_ERRORS\", errors });\n if (Object.keys(errors).length === 0) {\n // No per-field detail (e.g. an RHF step surfacing its own inline\n // message) — say something anyway, so the user is not left with a Next\n // button that silently does nothing.\n const message = missingRequired;\n if (onValidationFailed) {\n onValidationFailed(message);\n } else {\n // The kit's `toast` loads sonner (an optional peer) lazily, so an app\n // that never trips this never has to install it.\n toast.error(message);\n }\n }\n return false;\n }\n return true;\n }, [state.currentStepIndex, missingRequired, onValidationFailed]);\n\n /**\n * One forward move at a time. A double-click on Next used to validate the CURRENT\n * step twice and then advance twice — the second GO_NEXT landing on a step whose\n * own validators never ran, so a required field two steps in could be walked past.\n * Two layers, because either alone leaves a gap:\n *\n * - this ref refuses a second `goNext`/`skip`/`finish` while one is in flight,\n * which covers an async validator (a server check) of any length;\n * - the `from` on each action drops a move decided on a step that is no longer\n * current, which covers the click that arrives after the first move resolved\n * but before React re-rendered — its closure still holds the old step index.\n */\n const advancingRef = useRef(false);\n\n const goNext = useCallback(async (): Promise<boolean | undefined> => {\n if (advancingRef.current) return undefined;\n advancingRef.current = true;\n const from = state.currentStepIndex;\n // Pending state for the chrome (spinner, aria-busy). The ref above is still what\n // refuses the second click: state would only be seen after a re-render.\n dispatch({ type: \"SET_VALIDATING\", value: true });\n try {\n if (await runStepValidators()) {\n dispatch({ type: \"GO_NEXT\", from });\n return true;\n }\n return false;\n } finally {\n dispatch({ type: \"SET_VALIDATING\", value: false });\n advancingRef.current = false;\n }\n }, [runStepValidators, state.currentStepIndex]);\n\n // Read by `goBack`, which must not move while `onComplete` runs: the commit was\n // decided on the data of the step the user is on, and Back mid-commit would show a\n // step whose edits can no longer reach it. The chrome disables the button too; this\n // covers an app calling `goBack` itself.\n const submittingRef = useRef(false);\n\n const goBack = useCallback(() => {\n if (submittingRef.current) return;\n if (state.currentStepIndex === 0 && onExit) {\n clearStepUrl();\n onExit();\n return;\n }\n // On step 0 with no `onExit` this stays the no-op move (clearing field errors) it\n // always was.\n dispatch({ type: \"GO_BACK\" });\n }, [state.currentStepIndex, onExit, clearStepUrl]);\n\n const goToStep = useCallback((index: number) => {\n dispatch({ type: \"GO_TO_STEP\", index });\n }, []);\n\n const canGoToStep = useCallback(\n (index: number) =>\n index >= 0 &&\n index < steps.length &&\n (index <= state.currentStepIndex || state.completedSteps.has(index)) &&\n !crossesCommit(state, index),\n [steps.length, state],\n );\n\n const skip = useCallback(() => {\n // Not while Next is validating: the skip would land first and the pending GO_NEXT\n // would then be stale anyway — but the user asked for one move, not a race.\n if (advancingRef.current) return;\n dispatch({ type: \"SKIP\", from: state.currentStepIndex });\n }, [state.currentStepIndex]);\n\n const confirmCancel = useCallback(() => {\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: false });\n clearStepUrl();\n onCancel?.();\n }, [onCancel, clearStepUrl]);\n\n const cancel = useCallback(() => {\n if (!askBeforeCancel) {\n confirmCancel();\n return;\n }\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: true });\n }, [askBeforeCancel, confirmCancel]);\n\n const done = useCallback(() => {\n clearStepUrl();\n onDone?.();\n }, [onDone, clearStepUrl]);\n\n const dismissCancel = useCallback(() => {\n dispatch({ type: \"SHOW_CANCEL_DIALOG\", show: false });\n }, []);\n\n const updateData = useCallback((partial: Partial<TData>) => {\n dispatch({ type: \"UPDATE_DATA\", partial });\n }, []);\n\n const clearError = useCallback(() => {\n dispatch({ type: \"SET_ERROR\", error: null });\n }, []);\n\n const clearFieldErrors = useCallback(() => {\n dispatch({ type: \"SET_FIELD_ERRORS\", errors: {} });\n }, []);\n\n const finish = useCallback(async (): Promise<WizardFinishResult | undefined> => {\n if (!onComplete || !canFinish) return undefined;\n // A commit with steps after it happens once: those steps exist because the\n // data has been written. (A last-step commit keeps its old behaviour — the app\n // may retry Finish after `onComplete` resolved, as it always could.)\n if (commitStepIndex < steps.length - 1 && state.committedStepIndex !== null) return undefined;\n // The same re-entry guard as `goNext`: a double-click on Finish would otherwise\n // validate twice and call `onComplete` twice — `isSubmitting` is only set once\n // validation has passed, too late to stop the second click.\n if (advancingRef.current) return undefined;\n advancingRef.current = true;\n const from = state.currentStepIndex;\n try {\n // Validate the (possibly form-bearing) committing step before submitting,\n // matching the Next gate — review-only steps register no validators and pass\n // through.\n dispatch({ type: \"SET_VALIDATING\", value: true });\n let valid: boolean;\n try {\n valid = await runStepValidators();\n } finally {\n dispatch({ type: \"SET_VALIDATING\", value: false });\n }\n if (!valid) return { ok: false };\n submittingRef.current = true;\n dispatch({ type: \"SET_SUBMITTING\", value: true });\n dispatch({ type: \"SET_ERROR\", error: null });\n try {\n await onComplete(state.data);\n dispatch({ type: \"COMMITTED\", from });\n return { ok: true };\n } catch (err) {\n const message = err instanceof Error && err.message ? err.message : genericError;\n dispatch({ type: \"SET_ERROR\", error: message });\n return { ok: false, error: err instanceof Error && err.message ? err.message : undefined };\n } finally {\n submittingRef.current = false;\n dispatch({ type: \"SET_SUBMITTING\", value: false });\n }\n } finally {\n advancingRef.current = false;\n }\n }, [\n onComplete,\n canFinish,\n commitStepIndex,\n steps.length,\n state.committedStepIndex,\n state.currentStepIndex,\n state.data,\n runStepValidators,\n genericError,\n ]);\n\n const stepStatus = useCallback(\n (index: number): StepStatus => {\n if (index === state.currentStepIndex) return \"active\";\n if (state.skippedSteps.has(index)) return \"skipped\";\n if (state.completedSteps.has(index)) return \"completed\";\n return \"upcoming\";\n },\n [state.currentStepIndex, state.completedSteps, state.skippedSteps],\n );\n\n return {\n currentStepIndex: state.currentStepIndex,\n currentStep,\n steps,\n isFirstStep,\n isLastStep,\n canFinish,\n commitStepIndex,\n isCommitStep,\n committed: state.committedStepIndex !== null,\n canGoBack,\n canCancel,\n canDone,\n canGoToStep,\n // Their outcomes are what StepperNav tells a running tour (0.50); the declared types stay\n // `Promise<void>`, so an app's `let p: Promise<void> = wizard.goNext()` still compiles.\n goNext: goNext as () => Promise<void>,\n goBack,\n goToStep,\n skip,\n cancel,\n finish: finish as () => Promise<void>,\n done,\n registerStepValidate,\n nextBlocked,\n setNextBlocked,\n stepStatus,\n completedSteps: state.completedSteps,\n data: state.data,\n updateData,\n isSubmitting: state.isSubmitting,\n isValidating: state.isValidating,\n error: state.error,\n clearError,\n fieldErrors: state.fieldErrors,\n clearFieldErrors,\n showCancelDialog: state.showCancelDialog,\n confirmCancel,\n dismissCancel,\n };\n}\n\n/**\n * `urlSync: true` / `{ param }`: mirror the active step to `?<param>=N`, and hand back\n * the function that removes it again. Once removed it stays removed — the sync effect\n * re-runs whenever react-router hands out a new setter (every search change), and\n * without the latch it would write the param straight back after the exit cleared it.\n */\nfunction useSearchParamStepUrl(param: string, index: number): () => void {\n const [, setUrlParams] = useSearchParams();\n const leftRef = useRef(false);\n useEffect(() => {\n if (leftRef.current) return;\n setUrlParams(\n (prev) => {\n prev.set(param, String(index));\n return prev;\n },\n { replace: true },\n );\n }, [param, index, setUrlParams]);\n return useCallback(() => {\n if (leftRef.current) return;\n leftRef.current = true;\n setUrlParams(\n (prev) => {\n prev.delete(param);\n return prev;\n },\n { replace: true },\n );\n }, [param, setUrlParams]);\n}\n\n/** `urlSync: false`: the step lives in the reducer only. Touches no router context. */\nfunction useNoStepUrl(_param: string, _index: number): () => void {\n return noop;\n}\n\nfunction noop() {}\n"],"mappings":"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","names":[]}
|
|
@@ -16,4 +16,6 @@ export interface RequiredFieldSpec<T> {
|
|
|
16
16
|
* rate in range) pass a `valid` predicate. The message is caller-supplied so
|
|
17
17
|
* each page keeps its own namespace (e.g. `t("common:validation.required")`).
|
|
18
18
|
*/
|
|
19
|
-
export declare function requiredFieldsValidator<T extends
|
|
19
|
+
export declare function requiredFieldsValidator<T extends object & {
|
|
20
|
+
[K in keyof T]: unknown;
|
|
21
|
+
}>(data: T, fields: readonly RequiredFieldSpec<T>[], defaultMessage: string): ValidateResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/wizard/validation.ts"],"sourcesContent":["import type { ValidateResult, FieldErrors } from \"./types\";\n\nexport interface RequiredFieldSpec<T> {\n key: keyof T;\n /**\n * Custom predicate (e.g. numeric `> 0` or range checks). Defaults to a\n * \"present\" check: a non-empty trimmed string, or a non-null id/enum.\n */\n valid?: (value: unknown, data: T) => boolean;\n /** Per-field message override (defaults to the shared `defaultMessage`). */\n message?: string;\n}\n\n/**\n * Folds the copy-pasted `const errors = {}; if (!data.x) errors.x = required; …`\n * step-validate block reimplemented across the single-file entity wizards.\n * Pure-required fields need only `{ key }`; numeric constraints (amount `> 0`,\n * rate in range) pass a `valid` predicate. The message is caller-supplied so\n * each page keeps its own namespace (e.g. `t(\"common:validation.required\")`).\n */\nexport function requiredFieldsValidator<T extends
|
|
1
|
+
{"version":3,"sources":["../../src/wizard/validation.ts"],"sourcesContent":["import type { ValidateResult, FieldErrors } from \"./types\";\n\nexport interface RequiredFieldSpec<T> {\n key: keyof T;\n /**\n * Custom predicate (e.g. numeric `> 0` or range checks). Defaults to a\n * \"present\" check: a non-empty trimmed string, or a non-null id/enum.\n */\n valid?: (value: unknown, data: T) => boolean;\n /** Per-field message override (defaults to the shared `defaultMessage`). */\n message?: string;\n}\n\n/**\n * Folds the copy-pasted `const errors = {}; if (!data.x) errors.x = required; …`\n * step-validate block reimplemented across the single-file entity wizards.\n * Pure-required fields need only `{ key }`; numeric constraints (amount `> 0`,\n * rate in range) pass a `valid` predicate. The message is caller-supplied so\n * each page keeps its own namespace (e.g. `t(\"common:validation.required\")`).\n */\n// `T extends object` (0.52, API-2) so a wizard's data may be an interface. The mapped half\n// constrains nothing (every object type satisfies it): it is there for\n// `Parameters<typeof requiredFieldsValidator>`, which reads the constraint — `keyof object`\n// is `never`, so a wrapper typed that way refused every `{ key }` (0.52 code review). With\n// it the constraint reads as an indexable record, as 0.51's `Record<string, unknown>` did.\nexport function requiredFieldsValidator<T extends object & { [K in keyof T]: unknown }>(\n data: T,\n // `readonly` (0.52, API-7): a field list held `as const` passes without a copy.\n fields: readonly RequiredFieldSpec<T>[],\n defaultMessage: string,\n): ValidateResult {\n const errors: FieldErrors = {};\n for (const f of fields) {\n const v = data[f.key];\n const ok = f.valid ? f.valid(v, data) : typeof v === \"string\" ? v.trim() !== \"\" : v != null;\n if (!ok) errors[String(f.key)] = f.message ?? defaultMessage;\n }\n return { ok: Object.keys(errors).length === 0, errors };\n}\n"],"mappings":";AAyBO,SAAS,wBACd,MAEA,QACA,gBACgB;AAChB,QAAM,SAAsB,CAAC;AAC7B,aAAW,KAAK,QAAQ;AACtB,UAAM,IAAI,KAAK,EAAE,GAAG;AACpB,UAAM,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,IAAI,OAAO,MAAM,WAAW,EAAE,KAAK,MAAM,KAAK,KAAK;AACvF,QAAI,CAAC,GAAI,QAAO,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,WAAW;AAAA,EAChD;AACA,SAAO,EAAE,IAAI,OAAO,KAAK,MAAM,EAAE,WAAW,GAAG,OAAO;AACxD;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eifi1/ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "App-agnostic shared design system: domain-free React components, theme and tokens.",
|
|
6
6
|
"_sideEffects": "The barrel (src/index.ts) re-exports EVERY component, including components/chart, which statically imports recharts. Without this field a bundler must assume each of those modules has side effects, so it cannot drop the unused ones: importing a Button from '@eifi1/ui-kit' dragged all 433KB of recharts into the consumer's ENTRY chunk, and Keksdose was preloading it on every page load, login included. Nothing in src/ imports for side effects (no bare imports, no CSS imports from JS, no module-level global writes) — the token stylesheet is its own './tokens.css' export — so this is safe. CSS is still listed so any future stylesheet survives. Re-checked when `flag-icons` left `dependencies` and tsup started emitting a `\"use client\"` banner: neither touches this. The flag stylesheet was never imported from JS in the first place (that was the finding), and a directive prologue is a string literal every bundler lifts out before it decides whether a module is inert.",
|
|
@@ -106,13 +106,14 @@
|
|
|
106
106
|
"test:coverage": "vitest run --coverage",
|
|
107
107
|
"check:graph": "node scripts/check-module-graph.mjs",
|
|
108
108
|
"check:package": "node scripts/verify-package.mjs --quiet",
|
|
109
|
+
"check:api": "node scripts/check-api-compat.mjs",
|
|
109
110
|
"check:tailwind": "node scripts/check-tailwind-scan.mjs",
|
|
110
111
|
"check:security": "npm audit --audit-level=high",
|
|
111
112
|
"check:commits": "node scripts/check-commit-range.cjs",
|
|
112
113
|
"check:commit-rules": "node scripts/check-commit-rules.mjs",
|
|
113
114
|
"sync:commit-rules": "node scripts/sync-commit-rules.mjs",
|
|
114
|
-
"format": "prettier --write .",
|
|
115
|
-
"format:check": "prettier --check .",
|
|
115
|
+
"format": "prettier --write . --cache --cache-location node_modules/.cache/prettier/.prettier-cache --cache-strategy content",
|
|
116
|
+
"format:check": "prettier --check . --cache --cache-location node_modules/.cache/prettier/.prettier-cache --cache-strategy content",
|
|
116
117
|
"check:static": "npm run format:check && npm run typecheck && npm run lint && npm run check:tokens && npm run check:security && npm run check:commit-rules && npm run check:commits",
|
|
117
118
|
"check:test": "npm run test:coverage",
|
|
118
119
|
"check:build": "npm run build && npm run check:graph && npm run check:package && node scripts/gen-export-inventory.mjs --check && node scripts/gen-styling-inventory.mjs --check && npm run build:showcase && npm run check:tailwind",
|
|
@@ -121,7 +122,7 @@
|
|
|
121
122
|
"test:watch": "vitest",
|
|
122
123
|
"build": "node scripts/tsup-quiet.mjs && node scripts/build-dts.mjs && node scripts/fix-esm-extensions.mjs && node scripts/flatten-en.mjs && node scripts/build-pay-page.mjs",
|
|
123
124
|
"verify:package": "npm run build && node scripts/verify-package.mjs",
|
|
124
|
-
"dev": "tsup --watch --onSuccess \"node scripts/build-dts.mjs && node scripts/fix-esm-extensions.mjs\"",
|
|
125
|
+
"dev": "tsup --watch --onSuccess \"node scripts/build-dts.mjs && node scripts/fix-esm-extensions.mjs && node scripts/flatten-en.mjs && node scripts/build-pay-page.mjs\"",
|
|
125
126
|
"dev:showcase": "vite --config showcase/vite.config.ts",
|
|
126
127
|
"build:showcase": "vite build --config showcase/vite.config.ts --logLevel warn",
|
|
127
128
|
"preview:showcase": "vite preview --config showcase/vite.config.ts",
|
|
@@ -186,6 +187,7 @@
|
|
|
186
187
|
},
|
|
187
188
|
"devDependencies": {
|
|
188
189
|
"@eslint/js": "^10.0.1",
|
|
190
|
+
"@tailwindcss/oxide": "^4.3.3",
|
|
189
191
|
"@tailwindcss/vite": "^4.3.3",
|
|
190
192
|
"@tanstack/query-async-storage-persister": "^5.104.1",
|
|
191
193
|
"@tanstack/react-query": "^5.104.1",
|
|
@@ -201,6 +203,7 @@
|
|
|
201
203
|
"axe-core": "4.13.0",
|
|
202
204
|
"commit-and-tag-version": "^13.2.1",
|
|
203
205
|
"dexie": "^4.4.6",
|
|
206
|
+
"esbuild": "^0.28.2",
|
|
204
207
|
"eslint": "^10.11.0",
|
|
205
208
|
"eslint-import-resolver-typescript": "^4.4.5",
|
|
206
209
|
"eslint-plugin-import-x": "^4.17.1",
|
|
@@ -219,6 +222,7 @@
|
|
|
219
222
|
"react-hook-form": "^7.88.0",
|
|
220
223
|
"react-router": "^8.3.0",
|
|
221
224
|
"recharts": "^3.8.1",
|
|
225
|
+
"rollup": "^4.63.5",
|
|
222
226
|
"sonner": "^2.0.7",
|
|
223
227
|
"tailwindcss": "^4.3.3",
|
|
224
228
|
"tsup": "^8.5.0",
|
|
@@ -249,9 +253,9 @@
|
|
|
249
253
|
"engines": {
|
|
250
254
|
"node": ">=20.18"
|
|
251
255
|
},
|
|
252
|
-
"_overrides": "esbuild is forced to ^0.28.2 for GHSA-g7r4-m6w7-qqqr (dev-server file read on Windows, fixed in 0.28.1). vite already allows ^0.28; tsup 8.5.1 still pins ^0.27, and `npm run check` (build + package verification) proves it works on 0.28. Drop
|
|
256
|
+
"_overrides": "esbuild is forced to ^0.28.2 for GHSA-g7r4-m6w7-qqqr (dev-server file read on Windows, fixed in 0.28.1). vite already allows ^0.28; tsup 8.5.1 still pins ^0.27, and `npm run check` (build + package verification) proves it works on 0.28. The override is `$esbuild`, the devDependency's own range: npm refuses an override of a direct dependency that names a different spec. Drop it once tsup allows 0.28 itself; the devDependency stays. esbuild and rollup are devDependencies because scripts import them (flatten-en and check-module-graph; refactor audit 2026-10-30, TST-9): they came only through tsup and vite, so a move off tsup would have broken check:graph. eslint-plugin-jsx-a11y 6.10.2 (its latest) declares eslint peers up to ^9; it runs unchanged on eslint 10 — every a11y rule still fires, same counts — so its eslint peer is pointed at ours. Drop that entry once jsx-a11y declares ^10.",
|
|
253
257
|
"overrides": {
|
|
254
|
-
"esbuild": "
|
|
258
|
+
"esbuild": "$esbuild",
|
|
255
259
|
"eslint-plugin-jsx-a11y": {
|
|
256
260
|
"eslint": "$eslint"
|
|
257
261
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* What the account's own settings parts share (docs/user-admin-harmonization.md §6):
|
|
5
|
-
* the companies a refusal names
|
|
3
|
+
* the companies a refusal names and the card's type (the mounted guard re-exported).
|
|
6
4
|
*
|
|
7
5
|
* @internal Not part of the barrel; the parts are.
|
|
8
6
|
*/
|
|
@@ -39,28 +37,10 @@ export function refusalCompanies(err: unknown): string[] | undefined {
|
|
|
39
37
|
);
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
mounted.current = true;
|
|
48
|
-
return () => {
|
|
49
|
-
mounted.current = false;
|
|
50
|
-
};
|
|
51
|
-
}, []);
|
|
52
|
-
return mounted;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Call `call` and hand back a promise, a throw included — a callback may return
|
|
56
|
-
* nothing (done at once), a promise, or throw before it returns one. */
|
|
57
|
-
export function settle(call: () => unknown): Promise<unknown> {
|
|
58
|
-
try {
|
|
59
|
-
return Promise.resolve(call());
|
|
60
|
-
} catch (error) {
|
|
61
|
-
return Promise.reject(error);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
40
|
+
/** The mounted guard and the callback-to-promise bridge, in `hooks/use-mounted.ts` since
|
|
41
|
+
* 0.52: seven notifications modules imported them from here (refactor audit
|
|
42
|
+
* 2026-10-30, MOD-6). Re-exported for the account parts that still do. */
|
|
43
|
+
export { settle, useMounted } from "../hooks/use-mounted";
|
|
64
44
|
|
|
65
45
|
/** The settings cards' type scale, PasskeysSetting's: a small title and a muted line.
|
|
66
46
|
* Defined with the card look in settings/ since 0.34 (MOD-8, refactor audit 2026-10). */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// loads as one chunk like the other languages (0.34, KS U8). ./data-export-setting
|
|
4
4
|
// re-exports them.
|
|
5
5
|
|
|
6
|
-
import { englishWait } from "../auth/auth-errors";
|
|
6
|
+
import { englishWait } from "../auth/auth-errors-labels";
|
|
7
7
|
import type { DataExportLabels } from "./data-export-setting";
|
|
8
8
|
|
|
9
9
|
export const DEFAULT_DATA_EXPORT_LABELS: DataExportLabels = {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// loads as one chunk like the other languages (0.34, KS U8). ./delete-account-setting
|
|
4
4
|
// re-exports them.
|
|
5
5
|
|
|
6
|
-
import { englishRateLimited } from "../auth/auth-errors";
|
|
6
|
+
import { englishRateLimited } from "../auth/auth-errors-labels";
|
|
7
7
|
import type { DeleteAccountLabels } from "./delete-account-setting";
|
|
8
8
|
|
|
9
9
|
export const DEFAULT_DELETE_ACCOUNT_LABELS: DeleteAccountLabels = {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// loads as one chunk like the other languages (0.34, KS U8). ./email-change-setting
|
|
4
4
|
// re-exports them.
|
|
5
5
|
|
|
6
|
-
import { englishRateLimited } from "../auth/auth-errors";
|
|
6
|
+
import { englishRateLimited } from "../auth/auth-errors-labels";
|
|
7
7
|
import type { EmailChangeLabels } from "./email-change-setting";
|
|
8
8
|
|
|
9
9
|
export const DEFAULT_EMAIL_CHANGE_LABELS: EmailChangeLabels = {
|