@eifi1/ui-kit 0.54.0 → 0.56.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 +90 -76
- package/dist/admin/plan-change.js +2 -2
- package/dist/admin/plan-change.js.map +1 -1
- package/dist/admin/user-roster.d.ts +6 -0
- package/dist/admin/user-roster.js +4 -0
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +26 -0
- package/dist/billing/billing-labels.js +9 -0
- package/dist/billing/billing-labels.js.map +1 -1
- package/dist/billing/subscription-standing.d.ts +61 -0
- package/dist/billing/subscription-standing.js +51 -0
- package/dist/billing/subscription-standing.js.map +1 -0
- package/dist/billing/usage-meter.d.ts +52 -0
- package/dist/billing/usage-meter.js +52 -0
- package/dist/billing/usage-meter.js.map +1 -0
- package/dist/chart.d.ts +1 -0
- package/dist/chart.js +4 -0
- package/dist/chart.js.map +1 -1
- package/dist/components/alert-banner.js +2 -3
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.js +3 -15
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.js +2 -2
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/bulk-action-bar.js +2 -1
- package/dist/components/bulk-action-bar.js.map +1 -1
- package/dist/components/button.d.ts +35 -1
- package/dist/components/button.js +6 -9
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar-heatmap.js +2 -2
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/camera-button.d.ts +89 -0
- package/dist/components/camera-button.js +122 -0
- package/dist/components/camera-button.js.map +1 -0
- package/dist/components/camera-capture.d.ts +66 -0
- package/dist/components/camera-capture.js +190 -0
- package/dist/components/camera-capture.js.map +1 -0
- package/dist/components/camera-labels.d.ts +20 -0
- package/dist/components/camera-labels.js +15 -0
- package/dist/components/camera-labels.js.map +1 -0
- package/dist/components/chart-edge.d.ts +9 -0
- package/dist/components/chart-edge.js +14 -3
- package/dist/components/chart-edge.js.map +1 -1
- package/dist/components/chart-zoom-core.js +34 -29
- package/dist/components/chart-zoom-core.js.map +1 -1
- package/dist/components/checkbox-group.js +1 -1
- package/dist/components/checkbox-group.js.map +1 -1
- package/dist/components/combobox-core.js +3 -2
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.js +2 -2
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/consent-again-notice-labels.d.ts +25 -0
- package/dist/components/consent-again-notice-labels.js +15 -0
- package/dist/components/consent-again-notice-labels.js.map +1 -0
- package/dist/components/consent-again-notice.d.ts +85 -0
- package/dist/components/consent-again-notice.js +94 -0
- package/dist/components/consent-again-notice.js.map +1 -0
- package/dist/components/country-select.js +2 -7
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/currency-select.js +2 -2
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.js +2 -2
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-column-settings.js +2 -2
- package/dist/components/data-table-column-settings.js.map +1 -1
- package/dist/components/data-table-fit.js.map +1 -1
- package/dist/components/data-table-virtual.js +13 -3
- package/dist/components/data-table-virtual.js.map +1 -1
- package/dist/components/date-field.js +2 -2
- package/dist/components/date-field.js.map +1 -1
- package/dist/components/date-picker.js +2 -2
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/date-range-picker.js +2 -6
- package/dist/components/date-range-picker.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +3 -1
- package/dist/components/dialog-frame.js.map +1 -1
- package/dist/components/dropdown.js +7 -3
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/entity-combobox.js +2 -2
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/field-parts.d.ts +108 -6
- package/dist/components/field-parts.js +59 -6
- package/dist/components/field-parts.js.map +1 -1
- package/dist/components/field-suggestion-labels.d.ts +2 -0
- package/dist/components/field-suggestion-labels.js +10 -0
- package/dist/components/field-suggestion-labels.js.map +1 -0
- package/dist/components/field.js +1 -1
- package/dist/components/field.js.map +1 -1
- package/dist/components/file-button.d.ts +1 -1
- package/dist/components/file-button.js.map +1 -1
- package/dist/components/floating-panel.d.ts +7 -6
- package/dist/components/floating-panel.js +3 -17
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +3 -2
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/hover-menu.js +3 -2
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/icon-button.d.ts +23 -5
- package/dist/components/icon-button.js +7 -8
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/inline-confirm.js +2 -1
- package/dist/components/inline-confirm.js.map +1 -1
- package/dist/components/inline-entity-combobox.js +2 -2
- package/dist/components/inline-entity-combobox.js.map +1 -1
- package/dist/components/input.js +2 -2
- package/dist/components/input.js.map +1 -1
- package/dist/components/jump-calendar.js +3 -3
- package/dist/components/jump-calendar.js.map +1 -1
- package/dist/components/legal.d.ts +13 -1
- package/dist/components/legal.js +12 -0
- package/dist/components/legal.js.map +1 -1
- package/dist/components/legended-series-chart.d.ts +83 -0
- package/dist/components/legended-series-chart.js +77 -0
- package/dist/components/legended-series-chart.js.map +1 -0
- package/dist/components/lightbox.js +2 -1
- package/dist/components/lightbox.js.map +1 -1
- package/dist/components/mini-calendar.js +13 -5
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +23 -2
- package/dist/components/modal.js +24 -6
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +1 -1
- package/dist/components/month-picker.js +6 -6
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.js +2 -2
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.js +2 -6
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-input.js +2 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/page-header-layout.d.ts +18 -0
- package/dist/components/page-header-layout.js +23 -0
- package/dist/components/page-header-layout.js.map +1 -0
- package/dist/components/page-header-skeleton.d.ts +41 -0
- package/dist/components/page-header-skeleton.js +52 -0
- package/dist/components/page-header-skeleton.js.map +1 -0
- package/dist/components/page-header.js +4 -17
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/popover.js +7 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/release-notes-labels.d.ts +16 -0
- package/dist/components/release-notes-labels.js +18 -0
- package/dist/components/release-notes-labels.js.map +1 -0
- package/dist/components/release-notes.d.ts +65 -0
- package/dist/components/release-notes.js +65 -0
- package/dist/components/release-notes.js.map +1 -0
- package/dist/components/select.js +2 -2
- package/dist/components/select.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +9 -0
- package/dist/components/series-chart-labels.js +3 -1
- package/dist/components/series-chart-labels.js.map +1 -1
- package/dist/components/series-chart.d.ts +5 -0
- package/dist/components/series-chart.js +89 -36
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/toggle-group.js +3 -2
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/treemap-layout.js.map +1 -1
- package/dist/components/treemap.d.ts +17 -15
- package/dist/components/treemap.js +167 -85
- package/dist/components/treemap.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/eslint/role-utilities.d.ts +2 -2
- package/dist/eslint/role-utilities.js.map +1 -1
- package/dist/feedback/feedback-inbox-page.d.ts +260 -0
- package/dist/feedback/feedback-inbox-page.js +207 -0
- package/dist/feedback/feedback-inbox-page.js.map +1 -0
- package/dist/feedback/feedback-vocabulary.js +2 -2
- package/dist/feedback/feedback-vocabulary.js.map +1 -1
- package/dist/feedback.d.ts +1 -0
- package/dist/feedback.js +6 -0
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-debounce.d.ts +32 -5
- package/dist/hooks/use-debounce.js +6 -5
- package/dist/hooks/use-debounce.js.map +1 -1
- package/dist/hooks/use-file-picker.d.ts +3 -1
- package/dist/hooks/use-file-picker.js.map +1 -1
- package/dist/hooks/use-focus-trap.d.ts +19 -3
- package/dist/hooks/use-focus-trap.js +15 -5
- package/dist/hooks/use-focus-trap.js.map +1 -1
- package/dist/hooks/use-has-camera.d.ts +36 -0
- package/dist/hooks/use-has-camera.js +53 -0
- package/dist/hooks/use-has-camera.js.map +1 -0
- package/dist/hooks/use-hotkey.js +1 -10
- package/dist/hooks/use-hotkey.js.map +1 -1
- package/dist/hooks/use-overlay-history.d.ts +6 -1
- package/dist/hooks/use-overlay-history.js +10 -2
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/i18n/defaults.js +11 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +75 -3
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +20 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +92 -4
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +71 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +72 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +74 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +74 -3
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +71 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +22 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +24 -5
- package/dist/index.js +35 -1
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-labels.d.ts +8 -0
- package/dist/landing/landing-labels.js +2 -1
- package/dist/landing/landing-labels.js.map +1 -1
- package/dist/lib/changelog.d.ts +130 -0
- package/dist/lib/changelog.js +137 -0
- package/dist/lib/changelog.js.map +1 -0
- package/dist/lib/clipboard-minus.js +2 -2
- package/dist/lib/clipboard-minus.js.map +1 -1
- package/dist/lib/dates.d.ts +29 -3
- package/dist/lib/dates.js +41 -12
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/editable-content.d.ts +33 -0
- package/dist/lib/editable-content.js +15 -0
- package/dist/lib/editable-content.js.map +1 -0
- package/dist/lib/focus-left.d.ts +47 -1
- package/dist/lib/focus-left.js +11 -2
- package/dist/lib/focus-left.js.map +1 -1
- package/dist/lib/server-wake.d.ts +49 -9
- package/dist/lib/server-wake.js +25 -14
- package/dist/lib/server-wake.js.map +1 -1
- package/dist/lib/session-refresh.d.ts +53 -0
- package/dist/lib/session-refresh.js +27 -1
- package/dist/lib/session-refresh.js.map +1 -1
- package/dist/rhf/fields.js +3 -3
- package/dist/rhf/fields.js.map +1 -1
- package/dist/shell/app-shell.d.ts +25 -1
- package/dist/shell/app-shell.js +3 -1
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/dev-menu.d.ts +3 -0
- package/dist/shell/dev-menu.js +4 -2
- package/dist/shell/dev-menu.js.map +1 -1
- package/dist/shell/held-changes-banner-labels.d.ts +23 -0
- package/dist/shell/held-changes-banner-labels.js +14 -0
- package/dist/shell/held-changes-banner-labels.js.map +1 -0
- package/dist/shell/held-changes-banner.d.ts +75 -0
- package/dist/shell/held-changes-banner.js +106 -0
- package/dist/shell/held-changes-banner.js.map +1 -0
- package/dist/shell/sync-status-indicator.d.ts +27 -12
- package/dist/shell/sync-status-indicator.js +13 -4
- package/dist/shell/sync-status-indicator.js.map +1 -1
- package/dist/shell/top-bar-parts-labels.d.ts +5 -0
- package/dist/shell/top-bar-parts-labels.js +3 -1
- package/dist/shell/top-bar-parts-labels.js.map +1 -1
- package/dist/shell.d.ts +2 -0
- package/dist/shell.js +3 -0
- package/dist/shell.js.map +1 -1
- package/dist/sync/engine.d.ts +11 -2
- package/dist/sync/engine.js +13 -5
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/hooks.d.ts +3 -2
- package/dist/sync/hooks.js +4 -2
- package/dist/sync/hooks.js.map +1 -1
- package/dist/testing/css-variables.generated.d.ts +2 -2
- package/dist/testing/css-variables.generated.js +1 -1
- package/dist/testing/css-variables.generated.js.map +1 -1
- package/dist/theme/chart-palette.d.ts +18 -0
- package/dist/theme/chart-palette.js +17 -1
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/contrast-tokens.d.ts +3 -3
- package/dist/theme/contrast-tokens.js.map +1 -1
- package/dist/theme/contrast.d.ts +4 -4
- package/dist/theme/contrast.js.map +1 -1
- package/dist/tour/tour-overlay.js +2 -1
- package/dist/tour/tour-overlay.js.map +1 -1
- package/package.json +9 -12
- package/tokens.css +19 -54
- package/src/account/account-parts.ts +0 -47
- package/src/account/account-settings-labels.ts +0 -240
- package/src/account/account-settings.tsx +0 -698
- package/src/account/data-export-setting-labels.ts +0 -20
- package/src/account/data-export-setting.tsx +0 -200
- package/src/account/delete-account-setting-labels.ts +0 -33
- package/src/account/delete-account-setting.tsx +0 -342
- package/src/account/email-change-setting-labels.ts +0 -31
- package/src/account/email-change-setting.tsx +0 -443
- package/src/account/passkeys-setting.tsx +0 -445
- package/src/account/sessions-setting-labels.ts +0 -24
- package/src/account/sessions-setting.tsx +0 -283
- package/src/admin/admin-action-confirm-labels.ts +0 -19
- package/src/admin/admin-action-confirm.tsx +0 -509
- package/src/admin/admin-action-log-labels.ts +0 -83
- package/src/admin/admin-action-log.tsx +0 -464
- package/src/admin/admin-area-labels.ts +0 -114
- package/src/admin/admin-forbidden.tsx +0 -75
- package/src/admin/admin-overview.tsx +0 -258
- package/src/admin/admin-parts.ts +0 -68
- package/src/admin/admin-sections.tsx +0 -208
- package/src/admin/admin-tool-links.tsx +0 -90
- package/src/admin/invitations-panel-labels.ts +0 -31
- package/src/admin/invitations-panel.tsx +0 -516
- package/src/admin/mail-test-card-labels.ts +0 -19
- package/src/admin/mail-test-card.tsx +0 -189
- package/src/admin/plan-change-labels.ts +0 -5
- package/src/admin/plan-change.tsx +0 -474
- package/src/admin/reviewer-scope-editor-labels.ts +0 -16
- package/src/admin/reviewer-scope-editor.tsx +0 -224
- package/src/admin/role-select-labels.ts +0 -16
- package/src/admin/role-select.tsx +0 -282
- package/src/admin/transfer-ownership-dialog-labels.ts +0 -20
- package/src/admin/transfer-ownership-dialog.tsx +0 -162
- package/src/admin/user-roster-labels.ts +0 -17
- package/src/admin/user-roster.tsx +0 -559
- package/src/auth/accept-invitation-labels.ts +0 -21
- package/src/auth/accept-invitation.tsx +0 -210
- package/src/auth/auth-errors-labels.ts +0 -23
- package/src/auth/auth-errors.ts +0 -439
- package/src/auth/complete-name-dialog-labels.ts +0 -16
- package/src/auth/complete-name-dialog.tsx +0 -253
- package/src/auth/email-tag.ts +0 -75
- package/src/auth/forgot-password-form-labels.ts +0 -20
- package/src/auth/forgot-password-form.tsx +0 -221
- package/src/auth/form-rules.ts +0 -40
- package/src/auth/new-password-fields.tsx +0 -85
- package/src/auth/not-found-page-labels.ts +0 -12
- package/src/auth/not-found-page.tsx +0 -95
- package/src/auth/register-form-labels.ts +0 -31
- package/src/auth/register-form.tsx +0 -456
- package/src/auth/reset-password-form-labels.ts +0 -27
- package/src/auth/reset-password-form.tsx +0 -341
- package/src/auth/sign-in-form-labels.ts +0 -44
- package/src/auth/sign-in-form.tsx +0 -858
- package/src/auth/status-parts.tsx +0 -132
- package/src/auth/verify-email-labels.ts +0 -26
- package/src/auth/verify-email.tsx +0 -445
- package/src/billing/billing-banner.tsx +0 -258
- package/src/billing/billing-labels.ts +0 -381
- package/src/billing/billing-lock.ts +0 -108
- package/src/billing/billing-overview.ts +0 -157
- package/src/billing/billing-standing.ts +0 -80
- package/src/billing/cancel-request.ts +0 -97
- package/src/billing/cancel-subscription.tsx +0 -98
- package/src/billing/checkout-processing.ts +0 -394
- package/src/billing/checkout-return.ts +0 -90
- package/src/billing/pay-page.ts +0 -330
- package/src/billing/plan-card.tsx +0 -562
- package/src/billing/plan-change-labels.ts +0 -66
- package/src/billing/plan-limit-notice.tsx +0 -148
- package/src/billing/plan-limit-toast.tsx +0 -90
- package/src/billing/plan-limit.ts +0 -52
- package/src/billing/plan-price.ts +0 -98
- package/src/billing/plan-switch.tsx +0 -216
- package/src/billing/provider-page.ts +0 -84
- package/src/billing/subscription-actions.tsx +0 -247
- package/src/billing/subscription-status-chip.tsx +0 -95
- package/src/chart.ts +0 -103
- package/src/components/account-chips-labels.ts +0 -16
- package/src/components/account-chips.tsx +0 -474
- package/src/components/alert-banner.tsx +0 -670
- package/src/components/amount-input.tsx +0 -816
- package/src/components/appearance-labels.ts +0 -63
- package/src/components/authed-image-labels.ts +0 -11
- package/src/components/authed-image.tsx +0 -209
- package/src/components/autocomplete.tsx +0 -645
- package/src/components/breadcrumbs-labels.ts +0 -10
- package/src/components/breadcrumbs.tsx +0 -205
- package/src/components/bulk-action-bar-labels.ts +0 -11
- package/src/components/bulk-action-bar.tsx +0 -443
- package/src/components/button-group.tsx +0 -223
- package/src/components/button-look.ts +0 -256
- package/src/components/button.tsx +0 -339
- package/src/components/calculator.tsx +0 -341
- package/src/components/calendar-heatmap-labels.ts +0 -15
- package/src/components/calendar-heatmap.tsx +0 -600
- package/src/components/card.tsx +0 -377
- package/src/components/chart-edge.ts +0 -106
- package/src/components/chart-figure.tsx +0 -35
- package/src/components/chart-height.ts +0 -30
- package/src/components/chart-zoom-core.tsx +0 -627
- package/src/components/chart-zoom.tsx +0 -22
- package/src/components/chart.tsx +0 -554
- package/src/components/checkbox-group.tsx +0 -247
- package/src/components/checkbox.tsx +0 -346
- package/src/components/chip-input.tsx +0 -370
- package/src/components/chip-labels.ts +0 -13
- package/src/components/chip.tsx +0 -843
- package/src/components/choice-card.tsx +0 -544
- package/src/components/column-mapper-labels.ts +0 -38
- package/src/components/column-mapper.tsx +0 -655
- package/src/components/combobox-core.tsx +0 -1321
- package/src/components/combobox-suggestions.tsx +0 -131
- package/src/components/combobox.tsx +0 -716
- package/src/components/confirm-context.ts +0 -7
- package/src/components/confirm-dialog-labels.ts +0 -13
- package/src/components/confirm-dialog.tsx +0 -341
- package/src/components/copy-button-labels.ts +0 -13
- package/src/components/copy-button.tsx +0 -192
- package/src/components/country-select-labels.ts +0 -11
- package/src/components/country-select.tsx +0 -539
- package/src/components/currency-option-list.tsx +0 -188
- package/src/components/currency-select.tsx +0 -343
- package/src/components/danger-confirm-labels.ts +0 -20
- package/src/components/danger-confirm.tsx +0 -987
- package/src/components/data-table-cells.tsx +0 -118
- package/src/components/data-table-column-settings.tsx +0 -198
- package/src/components/data-table-filter-popover.tsx +0 -289
- package/src/components/data-table-filters.ts +0 -315
- package/src/components/data-table-fit.ts +0 -301
- package/src/components/data-table-groups.tsx +0 -288
- package/src/components/data-table-head.tsx +0 -280
- package/src/components/data-table-labels.ts +0 -198
- package/src/components/data-table-leading-row.tsx +0 -58
- package/src/components/data-table-mobile.tsx +0 -1117
- package/src/components/data-table-pagination.tsx +0 -202
- package/src/components/data-table-resize.ts +0 -188
- package/src/components/data-table-row-actions.tsx +0 -302
- package/src/components/data-table-row.tsx +0 -496
- package/src/components/data-table-selection.ts +0 -71
- package/src/components/data-table-shared.tsx +0 -287
- package/src/components/data-table-sort.ts +0 -163
- package/src/components/data-table-virtual.ts +0 -576
- package/src/components/data-table.tsx +0 -2598
- package/src/components/date-field.tsx +0 -684
- package/src/components/date-format-setting.tsx +0 -64
- package/src/components/date-picker.tsx +0 -404
- package/src/components/date-range-picker.tsx +0 -518
- package/src/components/description-list-labels.ts +0 -9
- package/src/components/description-list.tsx +0 -386
- package/src/components/dialog-frame-labels.ts +0 -7
- package/src/components/dialog-frame.tsx +0 -284
- package/src/components/disclosure.tsx +0 -476
- package/src/components/dropdown.tsx +0 -461
- package/src/components/empty-state.tsx +0 -136
- package/src/components/entity-combobox.tsx +0 -422
- package/src/components/error-boundary-labels.ts +0 -20
- package/src/components/error-boundary.tsx +0 -675
- package/src/components/facing-pair.ts +0 -93
- package/src/components/field-base-labels.ts +0 -20
- package/src/components/field-base.tsx +0 -656
- package/src/components/field-parts.tsx +0 -825
- package/src/components/field-strip.tsx +0 -129
- package/src/components/field.tsx +0 -206
- package/src/components/file-button.tsx +0 -295
- package/src/components/file-dropzone.tsx +0 -559
- package/src/components/floating-panel-labels.ts +0 -10
- package/src/components/floating-panel.tsx +0 -828
- package/src/components/focus-ring.ts +0 -16
- package/src/components/form-actions-labels.ts +0 -11
- package/src/components/form-actions.tsx +0 -578
- package/src/components/full-bleed-dialog.tsx +0 -294
- package/src/components/grouped-picker.tsx +0 -184
- package/src/components/help-popover.tsx +0 -117
- package/src/components/hover-menu.tsx +0 -343
- package/src/components/iban-input-labels.ts +0 -14
- package/src/components/iban-input.tsx +0 -236
- package/src/components/icon-button.tsx +0 -959
- package/src/components/icon-picker-labels.ts +0 -13
- package/src/components/icon-picker.tsx +0 -232
- package/src/components/image-grid-labels.ts +0 -11
- package/src/components/image-grid.tsx +0 -202
- package/src/components/inline-confirm.tsx +0 -116
- package/src/components/inline-edit-field-labels.ts +0 -11
- package/src/components/inline-edit-field.tsx +0 -552
- package/src/components/inline-entity-combobox.tsx +0 -849
- package/src/components/input.tsx +0 -394
- package/src/components/jump-calendar.tsx +0 -265
- package/src/components/language-select.tsx +0 -93
- package/src/components/legal-labels.ts +0 -105
- package/src/components/legal-page.tsx +0 -711
- package/src/components/legal.tsx +0 -668
- package/src/components/lightbox-labels.ts +0 -18
- package/src/components/lightbox.tsx +0 -399
- package/src/components/line-items-labels.ts +0 -14
- package/src/components/line-items.tsx +0 -602
- package/src/components/list-labels.ts +0 -12
- package/src/components/list.tsx +0 -870
- package/src/components/loading-state.tsx +0 -78
- package/src/components/measured-grid-labels.ts +0 -25
- package/src/components/measured-grid.tsx +0 -837
- package/src/components/menu-item.tsx +0 -241
- package/src/components/mini-calendar-labels.ts +0 -17
- package/src/components/mini-calendar.tsx +0 -792
- package/src/components/modal.tsx +0 -534
- package/src/components/money-draft.ts +0 -83
- package/src/components/money-field.tsx +0 -76
- package/src/components/month-grid.tsx +0 -229
- package/src/components/month-picker-labels.ts +0 -22
- package/src/components/month-picker.tsx +0 -1087
- package/src/components/multi-entity-combobox.tsx +0 -369
- package/src/components/multi-select.tsx +0 -376
- package/src/components/nav-pills.tsx +0 -198
- package/src/components/number-field.tsx +0 -327
- package/src/components/number-input.tsx +0 -559
- package/src/components/numeric-field.ts +0 -169
- package/src/components/numpad-sheet.tsx +0 -285
- package/src/components/one-time-code-input.tsx +0 -270
- package/src/components/page-contents-labels.ts +0 -9
- package/src/components/page-contents.tsx +0 -282
- package/src/components/page-header-context.ts +0 -8
- package/src/components/page-header.tsx +0 -298
- package/src/components/password-strength-labels.ts +0 -22
- package/src/components/password-strength.tsx +0 -241
- package/src/components/phone-input-labels.ts +0 -10
- package/src/components/phone-input.tsx +0 -282
- package/src/components/picker-sheet.tsx +0 -330
- package/src/components/pie-chart-labels.ts +0 -40
- package/src/components/pie-chart.tsx +0 -480
- package/src/components/popover-labels.ts +0 -14
- package/src/components/popover.tsx +0 -268
- package/src/components/progress-bar-labels.ts +0 -10
- package/src/components/progress-bar.tsx +0 -479
- package/src/components/qr-code.tsx +0 -76
- package/src/components/reauth-dialog-labels.ts +0 -13
- package/src/components/reauth-dialog.tsx +0 -265
- package/src/components/row-actions-labels.ts +0 -12
- package/src/components/row-actions.tsx +0 -795
- package/src/components/scroll-area.tsx +0 -123
- package/src/components/search-field.tsx +0 -192
- package/src/components/select.tsx +0 -294
- package/src/components/separator.tsx +0 -33
- package/src/components/series-chart-axes.ts +0 -342
- package/src/components/series-chart-budget.ts +0 -150
- package/src/components/series-chart-labels.ts +0 -33
- package/src/components/series-chart-thin.ts +0 -180
- package/src/components/series-chart-ticks.ts +0 -194
- package/src/components/series-chart.tsx +0 -1906
- package/src/components/server-wake-labels.ts +0 -11
- package/src/components/server-wake.tsx +0 -129
- package/src/components/settings-fields.tsx +0 -237
- package/src/components/share-card-labels.ts +0 -30
- package/src/components/share-card.tsx +0 -675
- package/src/components/signature-pad-labels.ts +0 -24
- package/src/components/signature-pad.tsx +0 -747
- package/src/components/signed-amount-labels.ts +0 -10
- package/src/components/signed-amount.tsx +0 -336
- package/src/components/skeleton.tsx +0 -80
- package/src/components/slider.tsx +0 -338
- package/src/components/sparkline-labels.ts +0 -14
- package/src/components/sparkline.tsx +0 -322
- package/src/components/spinner.tsx +0 -81
- package/src/components/stat-tile-labels.ts +0 -15
- package/src/components/stat-tile.tsx +0 -754
- package/src/components/status-dot.tsx +0 -170
- package/src/components/swatch-picker-labels.ts +0 -10
- package/src/components/swatch-picker.tsx +0 -283
- package/src/components/swipeable-row.tsx +0 -386
- package/src/components/switch.tsx +0 -273
- package/src/components/table-skeleton.tsx +0 -136
- package/src/components/table.tsx +0 -647
- package/src/components/tabs-labels.ts +0 -13
- package/src/components/tabs.tsx +0 -693
- package/src/components/text-link.tsx +0 -359
- package/src/components/text.tsx +0 -150
- package/src/components/textarea.tsx +0 -330
- package/src/components/tile-radio.tsx +0 -332
- package/src/components/time-input.tsx +0 -121
- package/src/components/toast-labels.ts +0 -12
- package/src/components/toast.tsx +0 -519
- package/src/components/toggle-group.tsx +0 -752
- package/src/components/toggle-legend.tsx +0 -394
- package/src/components/tooltip.tsx +0 -1149
- package/src/components/translation-review-editor.tsx +0 -290
- package/src/components/translation-review-labels.ts +0 -181
- package/src/components/translation-review-parts.tsx +0 -272
- package/src/components/translation-review.tsx +0 -1240
- package/src/components/tree-view.tsx +0 -589
- package/src/components/treemap-labels.ts +0 -47
- package/src/components/treemap-layout.ts +0 -210
- package/src/components/treemap.tsx +0 -1184
- package/src/components/trigger-aria.ts +0 -44
- package/src/components/use-anchor-dir.ts +0 -20
- package/src/components/use-mobile-reveal.ts +0 -62
- package/src/components/use-table-state.ts +0 -753
- package/src/components/user-avatar.tsx +0 -116
- package/src/components/wizard-stepper.tsx +0 -42
- package/src/components/write-lock-labels.ts +0 -10
- package/src/components/write-lock.tsx +0 -253
- package/src/currencies.ts +0 -90
- package/src/data-table.ts +0 -42
- package/src/demo/demo-banner.tsx +0 -153
- package/src/demo/demo-countdown.ts +0 -136
- package/src/demo/demo-ended.tsx +0 -90
- package/src/demo/demo-labels.ts +0 -78
- package/src/demo/demo-session.ts +0 -32
- package/src/demo/demo-start.tsx +0 -156
- package/src/eslint/index.ts +0 -161
- package/src/eslint/role-utilities.generated.ts +0 -146
- package/src/eslint/role-utilities.ts +0 -233
- package/src/feedback/feedback-attachment-download.tsx +0 -77
- package/src/feedback/feedback-attachment-labels.ts +0 -37
- package/src/feedback/feedback-attachment.tsx +0 -1239
- package/src/feedback/feedback-capture.ts +0 -287
- package/src/feedback/feedback-comment-thread.tsx +0 -353
- package/src/feedback/feedback-context-labels.ts +0 -11
- package/src/feedback/feedback-context.tsx +0 -245
- package/src/feedback/feedback-crash.ts +0 -547
- package/src/feedback/feedback-diagnostics.tsx +0 -41
- package/src/feedback/feedback-dialog-labels.ts +0 -29
- package/src/feedback/feedback-dialog.tsx +0 -452
- package/src/feedback/feedback-entry-file.tsx +0 -150
- package/src/feedback/feedback-inbox.tsx +0 -552
- package/src/feedback/feedback-labels.ts +0 -144
- package/src/feedback/feedback-menu-labels.ts +0 -12
- package/src/feedback/feedback-menu.tsx +0 -177
- package/src/feedback/feedback-mutation.ts +0 -20
- package/src/feedback/feedback-page-link.ts +0 -90
- package/src/feedback/feedback-public-labels.ts +0 -81
- package/src/feedback/feedback-public-list.tsx +0 -524
- package/src/feedback/feedback-public-switch.tsx +0 -102
- package/src/feedback/feedback-public-types.ts +0 -101
- package/src/feedback/feedback-record.ts +0 -428
- package/src/feedback/feedback-replies-context.ts +0 -9
- package/src/feedback/feedback-replies.tsx +0 -343
- package/src/feedback/feedback-rework-entry-chip.tsx +0 -18
- package/src/feedback/feedback-row-detail-labels.ts +0 -31
- package/src/feedback/feedback-row-detail.tsx +0 -871
- package/src/feedback/feedback-status-undo.ts +0 -109
- package/src/feedback/feedback-submit-dialog.tsx +0 -131
- package/src/feedback/feedback-submit.tsx +0 -270
- package/src/feedback/feedback-swipe.tsx +0 -192
- package/src/feedback/feedback-table-labels.ts +0 -27
- package/src/feedback/feedback-table.tsx +0 -912
- package/src/feedback/feedback-thread-labels.ts +0 -22
- package/src/feedback/feedback-thread-parts-labels.ts +0 -41
- package/src/feedback/feedback-thread.tsx +0 -905
- package/src/feedback/feedback-vocabulary.ts +0 -263
- package/src/feedback/feedback-vote-button.tsx +0 -124
- package/src/feedback.ts +0 -116
- package/src/hooks/use-anchored-panel.ts +0 -396
- package/src/hooks/use-anchored-rect.ts +0 -77
- package/src/hooks/use-announce.ts +0 -85
- package/src/hooks/use-authed-src.ts +0 -167
- package/src/hooks/use-body-scroll-lock.ts +0 -119
- package/src/hooks/use-breakpoint.ts +0 -133
- package/src/hooks/use-close-transition.ts +0 -107
- package/src/hooks/use-copy-to-clipboard.ts +0 -122
- package/src/hooks/use-debounce.ts +0 -114
- package/src/hooks/use-dismiss.ts +0 -371
- package/src/hooks/use-document-title.ts +0 -33
- package/src/hooks/use-file-drop.ts +0 -121
- package/src/hooks/use-file-picker-labels.ts +0 -28
- package/src/hooks/use-file-picker.tsx +0 -359
- package/src/hooks/use-focus-trap.ts +0 -199
- package/src/hooks/use-hotkey.ts +0 -212
- package/src/hooks/use-large-text.ts +0 -41
- package/src/hooks/use-long-press.ts +0 -140
- package/src/hooks/use-media-query.ts +0 -39
- package/src/hooks/use-mounted.ts +0 -39
- package/src/hooks/use-noindex.ts +0 -37
- package/src/hooks/use-overlay-history.ts +0 -714
- package/src/hooks/use-row-swipe.ts +0 -207
- package/src/hooks/use-search-param-state.ts +0 -434
- package/src/hooks/use-windowed-rows.ts +0 -225
- package/src/i18n/defaults.ts +0 -267
- package/src/i18n/german.ts +0 -2059
- package/src/i18n/kit-labels.tsx +0 -1003
- package/src/i18n/languages.ts +0 -349
- package/src/i18n/locales/de-CH.ts +0 -24
- package/src/i18n/locales/en.ts +0 -267
- package/src/i18n/locales/es.ts +0 -1999
- package/src/i18n/locales/fr.ts +0 -2037
- package/src/i18n/locales/hu.ts +0 -2004
- package/src/i18n/locales/it.ts +0 -2014
- package/src/i18n/locales/zh.ts +0 -1912
- package/src/i18n/plurals.ts +0 -86
- package/src/i18n/review.ts +0 -475
- package/src/i18n/shared-labels.ts +0 -138
- package/src/i18n/swiss.ts +0 -29
- package/src/index.ts +0 -1191
- package/src/landing/access.ts +0 -93
- package/src/landing/landing-actions.tsx +0 -97
- package/src/landing/landing-labels.ts +0 -59
- package/src/landing/landing-sections.tsx +0 -341
- package/src/landing/page-seo.ts +0 -261
- package/src/landing/public-header.tsx +0 -121
- package/src/landing/public-page.tsx +0 -106
- package/src/landing/routing.tsx +0 -225
- package/src/lib/broadcast.ts +0 -52
- package/src/lib/calc.ts +0 -323
- package/src/lib/chip-edge-snap.ts +0 -212
- package/src/lib/clipboard-minus.ts +0 -107
- package/src/lib/clipping.ts +0 -34
- package/src/lib/cn.ts +0 -28
- package/src/lib/column-mapping.ts +0 -234
- package/src/lib/countries.ts +0 -91
- package/src/lib/data-attributes.ts +0 -14
- package/src/lib/date-format.ts +0 -227
- package/src/lib/dates.ts +0 -288
- package/src/lib/day-format.ts +0 -96
- package/src/lib/decimal-marks.ts +0 -108
- package/src/lib/describe-failure.ts +0 -18
- package/src/lib/device-settings.ts +0 -147
- package/src/lib/direction.ts +0 -74
- package/src/lib/document-navigation.ts +0 -17
- package/src/lib/document-title.ts +0 -41
- package/src/lib/download.ts +0 -172
- package/src/lib/float-clearance.ts +0 -70
- package/src/lib/floating-stack.ts +0 -70
- package/src/lib/focus-left.ts +0 -156
- package/src/lib/format.ts +0 -585
- package/src/lib/http-status.ts +0 -38
- package/src/lib/iban.ts +0 -251
- package/src/lib/ime.ts +0 -24
- package/src/lib/intl-cache.ts +0 -27
- package/src/lib/iso-day.ts +0 -13
- package/src/lib/lazy-with-fallback.ts +0 -82
- package/src/lib/logger.ts +0 -69
- package/src/lib/minus.ts +0 -50
- package/src/lib/path-pattern.ts +0 -55
- package/src/lib/person-name.ts +0 -155
- package/src/lib/phone.ts +0 -208
- package/src/lib/plain-text.ts +0 -22
- package/src/lib/presence.ts +0 -18
- package/src/lib/qr-encode.ts +0 -444
- package/src/lib/quiet-focus.ts +0 -32
- package/src/lib/reduced-motion.ts +0 -22
- package/src/lib/refs.ts +0 -22
- package/src/lib/round.ts +0 -43
- package/src/lib/safe-storage.ts +0 -119
- package/src/lib/server-wake.ts +0 -315
- package/src/lib/session-refresh.ts +0 -472
- package/src/lib/share-format.ts +0 -18
- package/src/lib/status-tone.ts +0 -43
- package/src/lib/strip-fade.ts +0 -287
- package/src/lib/table-text.ts +0 -554
- package/src/lib/text-case.ts +0 -19
- package/src/lib/text-match.ts +0 -169
- package/src/lib/thenable.ts +0 -19
- package/src/lib/translation-review.ts +0 -769
- package/src/notifications/device-zone-report.ts +0 -67
- package/src/notifications/feedback-notification-types-labels.ts +0 -33
- package/src/notifications/feedback-notification-types.ts +0 -71
- package/src/notifications/notification-bell.tsx +0 -119
- package/src/notifications/notification-inbox.tsx +0 -309
- package/src/notifications/notification-parts.tsx +0 -81
- package/src/notifications/notification-types-setting.tsx +0 -406
- package/src/notifications/notification-types.ts +0 -89
- package/src/notifications/notifications-labels.ts +0 -287
- package/src/notifications/push-device-setting.tsx +0 -230
- package/src/notifications/push-devices-setting.tsx +0 -374
- package/src/notifications/push-worker.ts +0 -168
- package/src/notifications/quiet-hours-setting.tsx +0 -238
- package/src/notifications/unsubscribe-panel.tsx +0 -200
- package/src/notifications/use-push-device.ts +0 -448
- package/src/offline/local-first.ts +0 -100
- package/src/offline/pwa-update-prompt-labels.ts +0 -9
- package/src/offline/pwa-update-prompt.tsx +0 -78
- package/src/offline/query-persistence.ts +0 -391
- package/src/offline/service-worker-updates.ts +0 -81
- package/src/offline/shared-computer-labels.ts +0 -8
- package/src/offline/shared-computer.tsx +0 -87
- package/src/offline/sign-out-pending-dialog-labels.ts +0 -21
- package/src/offline/sign-out-pending-dialog.tsx +0 -135
- package/src/offline.ts +0 -37
- package/src/push-worker.ts +0 -25
- package/src/rhf/fields.tsx +0 -2187
- package/src/rhf/form-core.tsx +0 -324
- package/src/rhf/form.tsx +0 -12
- package/src/rhf/line-items.tsx +0 -135
- package/src/rhf/use-rhf-wizard-step.ts +0 -104
- package/src/rhf.ts +0 -21
- package/src/search/command-palette-labels.ts +0 -16
- package/src/search/command-palette.tsx +0 -624
- package/src/search/global-search-labels.ts +0 -13
- package/src/search/global-search.tsx +0 -660
- package/src/search/search-index.ts +0 -283
- package/src/search/use-command-key.ts +0 -23
- package/src/search.ts +0 -14
- package/src/settings/device-reset-setting-labels.ts +0 -20
- package/src/settings/device-reset-setting.tsx +0 -116
- package/src/settings/settings-card-classes.ts +0 -11
- package/src/settings/settings-catalogue.ts +0 -150
- package/src/settings/settings-context.ts +0 -33
- package/src/settings/settings-heading.tsx +0 -67
- package/src/settings/settings-labels.ts +0 -87
- package/src/settings/settings-layout.tsx +0 -471
- package/src/settings/settings-section.tsx +0 -108
- package/src/settings/use-account-appearance.ts +0 -148
- package/src/settings/use-account-language.ts +0 -146
- package/src/settings/use-settings-focus.ts +0 -128
- package/src/settings/use-settings-route.ts +0 -258
- package/src/shell/account-menu-language.tsx +0 -130
- package/src/shell/account-menu-text-size.tsx +0 -80
- package/src/shell/admin-menu.tsx +0 -183
- package/src/shell/app-shell-labels.ts +0 -11
- package/src/shell/app-shell.tsx +0 -1560
- package/src/shell/auth-layout.tsx +0 -186
- package/src/shell/company-switcher-labels.ts +0 -13
- package/src/shell/company-switcher.tsx +0 -136
- package/src/shell/dev-menu.tsx +0 -257
- package/src/shell/fill-height.tsx +0 -45
- package/src/shell/language-code.ts +0 -5
- package/src/shell/option-switcher-menu.tsx +0 -84
- package/src/shell/sync-status-indicator.tsx +0 -303
- package/src/shell/top-bar-actions.tsx +0 -87
- package/src/shell/top-bar-brand.tsx +0 -78
- package/src/shell/top-bar-parts-labels.ts +0 -148
- package/src/shell/top-bar.tsx +0 -40
- package/src/shell/topbar-action-menu.tsx +0 -567
- package/src/shell/topbar-controls.tsx +0 -269
- package/src/shell/tour-menu.tsx +0 -187
- package/src/shell.ts +0 -108
- package/src/sync/db.ts +0 -129
- package/src/sync/engine.ts +0 -1011
- package/src/sync/hooks.ts +0 -98
- package/src/sync/testing.ts +0 -73
- package/src/sync/wire.ts +0 -61
- package/src/sync.ts +0 -37
- package/src/table-text.ts +0 -8
- package/src/testing/css-variables.generated.ts +0 -112
- package/src/testing/css-variables.ts +0 -189
- package/src/testing/index.ts +0 -29
- package/src/testing/react.ts +0 -58
- package/src/testing/tour.ts +0 -41
- package/src/testing/viewport.ts +0 -357
- package/src/theme/chart-palette.ts +0 -232
- package/src/theme/color.ts +0 -272
- package/src/theme/contrast-tokens.ts +0 -268
- package/src/theme/contrast.ts +0 -250
- package/src/theme/palette-derive.ts +0 -825
- package/src/theme/palette-presets.ts +0 -544
- package/src/theme/palette-store.ts +0 -108
- package/src/theme/text-size.ts +0 -289
- package/src/theme/theme-store.ts +0 -157
- package/src/tour/tour-catalogue-labels.ts +0 -75
- package/src/tour/tour-catalogue.tsx +0 -335
- package/src/tour/tour-dock.ts +0 -134
- package/src/tour/tour-engine.ts +0 -37
- package/src/tour/tour-gates.ts +0 -218
- package/src/tour/tour-guide-button.tsx +0 -64
- package/src/tour/tour-guide-labels.ts +0 -15
- package/src/tour/tour-info.ts +0 -144
- package/src/tour/tour-labels.ts +0 -23
- package/src/tour/tour-overlay.tsx +0 -1170
- package/src/tour/tour.tsx +0 -750
- package/src/tour/use-tour-progress.ts +0 -671
- package/src/tour/use-tour-url-sync.ts +0 -266
- package/src/tour.ts +0 -21
- package/src/vite-env.d.ts +0 -23
- package/src/wizard/stepper-nav.tsx +0 -360
- package/src/wizard/types.ts +0 -275
- package/src/wizard/use-wizard.ts +0 -560
- package/src/wizard/validation.ts +0 -39
- package/src/wizard/wizard-context.tsx +0 -74
- package/src/wizard/wizard-labels.ts +0 -29
- package/src/wizard/wizard-step.tsx +0 -76
- package/src/wizard/wizard-summary.tsx +0 -84
- package/src/wizard.ts +0 -15
package/src/components/modal.tsx
DELETED
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
import { createContext, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { createPortal } from "react-dom";
|
|
3
|
-
import type {
|
|
4
|
-
ComponentPropsWithoutRef,
|
|
5
|
-
KeyboardEvent,
|
|
6
|
-
MouseEvent as ReactMouseEvent,
|
|
7
|
-
PointerEvent as ReactPointerEvent,
|
|
8
|
-
ReactNode,
|
|
9
|
-
Ref,
|
|
10
|
-
} from "react";
|
|
11
|
-
import { cn } from "../lib/cn";
|
|
12
|
-
import { mergeRefs } from "./field-parts";
|
|
13
|
-
import { useFocusTrap } from "../hooks/use-focus-trap";
|
|
14
|
-
import { useBreakpoint } from "../hooks/use-breakpoint";
|
|
15
|
-
import { useBodyScrollLock } from "../hooks/use-body-scroll-lock";
|
|
16
|
-
import { useOverlayHistory } from "../hooks/use-overlay-history";
|
|
17
|
-
import { useDialogParam, type DialogParam } from "../hooks/use-search-param-state";
|
|
18
|
-
import { OVERLAY_EXIT_MS, skipsExitAnimation, useCloseTransition } from "../hooks/use-close-transition";
|
|
19
|
-
import { runOwnKeyDown, takeEscape, useDismissLayer } from "../hooks/use-dismiss";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Drag the panel by its top strip, in pointer space (dev#460).
|
|
23
|
-
*
|
|
24
|
-
* Deliberately not a library and not persisted: the offset is component state that
|
|
25
|
-
* dies with the dialog. It starts only on a press that lands on the panel's own
|
|
26
|
-
* chrome — never on a field, a button or a link, which is what keeps "drag to select
|
|
27
|
-
* text in the textarea" working. Above `md` only: below it the panel is a
|
|
28
|
-
* full-width bottom sheet with nothing beside it to uncover.
|
|
29
|
-
*
|
|
30
|
-
* The whole gesture stays on the PANEL — captured pointer, React handlers — rather than
|
|
31
|
-
* on `window`. The first shape of this added `pointermove`/`pointerup` to `window` from
|
|
32
|
-
* inside the `pointerdown` handler and took them off again from the `pointerup` handler,
|
|
33
|
-
* so the teardown ran only if the gesture ended: React never sees a listener added from
|
|
34
|
-
* an event handler, and an unmount mid-drag left a live `pointermove` on `window` for
|
|
35
|
-
* the life of the page, pinning this hook's state with it. The one caller that enables
|
|
36
|
-
* dragging is the feedback composer, which closes ITSELF on submit — a Ctrl+Enter sent
|
|
37
|
-
* with the pointer still down is exactly that order of events, once per drag.
|
|
38
|
-
*/
|
|
39
|
-
function useDragOffset(enabled: boolean) {
|
|
40
|
-
const [offset, setOffset] = useState<{ x: number; y: number } | null>(null);
|
|
41
|
-
const from = useRef<{ x: number; y: number; ox: number; oy: number } | null>(null);
|
|
42
|
-
// `md:` as the panel's own CSS reads it, at the text size in force: 768 px at Normal,
|
|
43
|
-
// 960 at Large. `innerWidth >= 768` dragged the bottom sheet of an 800 px window at
|
|
44
|
-
// Large (refactor audit 2026-10-30, DUP-9). Without `matchMedia` (jsdom), the desktop.
|
|
45
|
-
const wide = useBreakpoint("md", true);
|
|
46
|
-
|
|
47
|
-
const endDrag = () => {
|
|
48
|
-
from.current = null;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
/** Back to where the panel opens. A `Modal` with an `open` prop outlives its own
|
|
53
|
-
* closes, so "the position resets when the dialog closes" needs saying. */
|
|
54
|
-
reset: () => setOffset(null),
|
|
55
|
-
style: offset ? { transform: `translate(${offset.x}px, ${offset.y}px)` } : undefined,
|
|
56
|
-
handlers: !enabled
|
|
57
|
-
? undefined
|
|
58
|
-
: {
|
|
59
|
-
onPointerDown: (e: ReactPointerEvent<HTMLDivElement>) => {
|
|
60
|
-
if (e.button !== 0 || !wide) return;
|
|
61
|
-
const target = e.target as HTMLElement;
|
|
62
|
-
// Only the panel itself and its non-interactive chrome start a drag; a
|
|
63
|
-
// press inside any control belongs to that control.
|
|
64
|
-
if (target.closest("input,textarea,select,button,a,[role='button']")) return;
|
|
65
|
-
from.current = { x: e.clientX, y: e.clientY, ox: offset?.x ?? 0, oy: offset?.y ?? 0 };
|
|
66
|
-
try {
|
|
67
|
-
// Capture is what lets the handlers live on the panel: every later
|
|
68
|
-
// pointer event for this pointer retargets here, so the drag keeps
|
|
69
|
-
// working once the pointer outruns the panel's own box — which it always
|
|
70
|
-
// does, since the panel moves out from under it.
|
|
71
|
-
e.currentTarget.setPointerCapture(e.pointerId);
|
|
72
|
-
} catch {
|
|
73
|
-
// jsdom implements no pointer capture, and a browser throws
|
|
74
|
-
// NotFoundError for a pointerId that is already gone. Neither is worth
|
|
75
|
-
// refusing the drag: uncaptured, the panel still gets the moves that
|
|
76
|
-
// land on it.
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
onPointerMove: (e: ReactPointerEvent<HTMLDivElement>) => {
|
|
80
|
-
const start = from.current;
|
|
81
|
-
if (!start) return;
|
|
82
|
-
setOffset({ x: start.ox + e.clientX - start.x, y: start.oy + e.clientY - start.y });
|
|
83
|
-
},
|
|
84
|
-
onPointerUp: endDrag,
|
|
85
|
-
// A cancel (the browser taking the pointer for a scroll or a gesture) and a
|
|
86
|
-
// lost capture both mean no `pointerup` is coming. Without them the drag would
|
|
87
|
-
// stay "live" and the next move over the panel would jump it.
|
|
88
|
-
onPointerCancel: endDrag,
|
|
89
|
-
onLostPointerCapture: endDrag,
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* The panel's own ANIMATED dismissal, for chrome rendered inside it. `DialogFrame`'s X
|
|
96
|
-
* and its `actions(close)` read this, so a Cancel or an X lowers the panel exactly the
|
|
97
|
-
* way Escape does — calling `onClose` from inside unmounts with no exit, which is the
|
|
98
|
-
* documented limit of {@link useCloseTransition}. `null` outside a `Modal`.
|
|
99
|
-
*/
|
|
100
|
-
export const ModalCloseContext = createContext<(() => void) | null>(null);
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Backdrop handlers that close only when a press starts AND ends on the
|
|
104
|
-
* backdrop itself. A plain onClick fires on mouseup, so dragging a text
|
|
105
|
-
* selection that starts inside the panel and releases over the backdrop would
|
|
106
|
-
* dismiss the dialog (feedback #254) — and the reverse drag would too, since
|
|
107
|
-
* click targets the common ancestor of the down/up elements.
|
|
108
|
-
*/
|
|
109
|
-
export function useBackdropClose(onClose: () => void) {
|
|
110
|
-
const pressStartedOnBackdrop = useRef(false);
|
|
111
|
-
return {
|
|
112
|
-
onMouseDown: (e: ReactMouseEvent<HTMLDivElement>) => {
|
|
113
|
-
pressStartedOnBackdrop.current = e.target === e.currentTarget;
|
|
114
|
-
},
|
|
115
|
-
onMouseUp: (e: ReactMouseEvent<HTMLDivElement>) => {
|
|
116
|
-
const started = pressStartedOnBackdrop.current;
|
|
117
|
-
pressStartedOnBackdrop.current = false;
|
|
118
|
-
if (started && e.target === e.currentTarget) onClose();
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Tab-cycle target set: the interactive descendants the focus trap rotates
|
|
124
|
-
// through. Querying live (per keystroke) means dynamically rendered fields are
|
|
125
|
-
// included automatically.
|
|
126
|
-
/**
|
|
127
|
-
* `extends ComponentPropsWithoutRef<"div">` so anything a `<div>` takes reaches the
|
|
128
|
-
* PANEL — the element with `role="dialog"`, which is the one a consumer means. That is
|
|
129
|
-
* where a `data-tour` anchor, a test id, an `aria-describedby` or an `aria-label` has
|
|
130
|
-
* to land; before this, a closed prop list dropped all four silently (audit §api-design).
|
|
131
|
-
*/
|
|
132
|
-
export interface ModalProps extends Omit<ComponentPropsWithoutRef<"div">, "role"> {
|
|
133
|
-
/** Invoked on backdrop click and on Escape. */
|
|
134
|
-
onClose: () => void;
|
|
135
|
-
/**
|
|
136
|
-
* Who decides whether the dialog is up, when the caller wants to keep it MOUNTED.
|
|
137
|
-
*
|
|
138
|
-
* Left out, the dialog is open for as long as it is mounted — `{open && <Modal/>}`,
|
|
139
|
-
* which is every caller before this prop and stays exactly as it was. That idiom has
|
|
140
|
-
* one blind spot: a close the CALLER decides on (a save that succeeded, a
|
|
141
|
-
* `setOpen(false)` from outside) unmounts the panel on the spot, with no exit,
|
|
142
|
-
* because there is nothing left to animate. kastlan's `FormModal` was nothing but an
|
|
143
|
-
* `if (!open) return null` gate in front of this.
|
|
144
|
-
*
|
|
145
|
-
* Passed, the component stays mounted while closed and renders nothing, and a flip
|
|
146
|
-
* to `false` plays the same exit a dismissal does before the panel goes. The focus
|
|
147
|
-
* trap, the scroll lock and the Back-gesture history entry are held only while
|
|
148
|
-
* `open` is true — they are released the moment it turns false, so focus is back on
|
|
149
|
-
* the trigger while the panel is still lowering. A dismissal (Escape, the backdrop,
|
|
150
|
-
* Back, the frame's X) animates first and then calls `onClose`; the `false` that
|
|
151
|
-
* comes back from it is not animated a second time.
|
|
152
|
-
*
|
|
153
|
-
* The body is unmounted once the exit has finished, as it is today, so a form inside
|
|
154
|
-
* still starts fresh on the next open.
|
|
155
|
-
*/
|
|
156
|
-
open?: boolean;
|
|
157
|
-
/** `"alertdialog"` for a dialog that interrupts to ask a question the user must
|
|
158
|
-
* answer (a confirmation) — screen readers announce it with more urgency. Only the
|
|
159
|
-
* two dialog roles are allowed: the panel stays modal either way. */
|
|
160
|
-
role?: "dialog" | "alertdialog";
|
|
161
|
-
children: ReactNode;
|
|
162
|
-
/**
|
|
163
|
-
* Panel max-width: `md` (28rem), `lg` (32rem), `xl` (48rem), `2xl` (the display less
|
|
164
|
-
* the backdrop's margin, capped at 110rem).
|
|
165
|
-
*
|
|
166
|
-
* `xl` exists because two callers had already written `className="max-w-3xl"` to
|
|
167
|
-
* reach it — the payee backfill's preview table and the price-merge dialog, both
|
|
168
|
-
* of which hold a list and several fields rather than one question. A caller
|
|
169
|
-
* patching a missing size IS the signal that the size is missing, and a
|
|
170
|
-
* hand-written width in a `className` is a width nothing else can line up with.
|
|
171
|
-
*
|
|
172
|
-
* `2xl` is the same signal one size up (round 0.34 C3, KS U1): a dialog whose
|
|
173
|
-
* content is a TABLE to be read. Kurvenschmiede's five part dialogs hold a measured
|
|
174
|
-
* table pasted from a spreadsheet and had patched themselves to
|
|
175
|
-
* `w-[min(110rem,94vw)] max-w-none`; Keksdose's report drilldown passed `size="lg"`
|
|
176
|
-
* and then `max-w-5xl`. The cap is the generous one of the two, because a table
|
|
177
|
-
* squeezed into a box in the middle of a wide screen is a table scrolled sideways
|
|
178
|
-
* (KS feedback #16); below the cap the panel is as wide as the backdrop lets it be.
|
|
179
|
-
* In rem, so it grows with the text size until the screen stops it.
|
|
180
|
-
*
|
|
181
|
-
* The panel is `w-full` under every one of them, so the cap only bites once the
|
|
182
|
-
* viewport is wider than it: on a phone all four are the same full-width bottom
|
|
183
|
-
* sheet. Nothing here may introduce a `min-w-*`, which is what would break that.
|
|
184
|
-
*/
|
|
185
|
-
size?: "md" | "lg" | "xl" | "2xl";
|
|
186
|
-
/** Extra classes for the panel (e.g. `space-y-3` for form content spacing). */
|
|
187
|
-
className?: string;
|
|
188
|
-
/** id of the heading element, wired to `aria-labelledby`. */
|
|
189
|
-
labelledBy?: string;
|
|
190
|
-
/**
|
|
191
|
-
* Extra key handling on the panel (e.g. Ctrl/Cmd+Enter to submit). Runs first; a
|
|
192
|
-
* `preventDefault()` on Escape HERE keeps the dialog open — the owner's claim (see
|
|
193
|
-
* `use-dismiss.ts`). One a field inside makes does not, since 0.34: the rule
|
|
194
|
-
* `FullBleedDialog` already had, so a search box keeping Chromium from clearing itself
|
|
195
|
-
* no longer holds the dialog open (Wave 2, DUP-1).
|
|
196
|
-
*/
|
|
197
|
-
onKeyDown?: (e: KeyboardEvent<HTMLDivElement>) => void;
|
|
198
|
-
/**
|
|
199
|
-
* Drop the backdrop's mobile margin so the panel can reach the screen edges
|
|
200
|
-
* (a full-screen sheet on phones). The panel still needs its own
|
|
201
|
-
* `h-[100dvh] max-w-full rounded-none` classes via `className` to fill it; the
|
|
202
|
-
* desktop `md:p-4` margin is kept. Defaults to false (padded on all sizes).
|
|
203
|
-
*/
|
|
204
|
-
fullBleed?: boolean;
|
|
205
|
-
/**
|
|
206
|
-
* Let the user drag the panel out of the way by its top edge (Keksdose feedback
|
|
207
|
-
* dev#460: *"Make the feedback dialog draggable so I can see behind it if it
|
|
208
|
-
* blocks something."*).
|
|
209
|
-
*
|
|
210
|
-
* Opt-in, because it only makes sense for a dialog whose content is ABOUT the page
|
|
211
|
-
* behind it — the feedback composer is the case, an ordinary form is not, and a
|
|
212
|
-
* draggable panel that has nothing to reveal is a control that can only get lost.
|
|
213
|
-
*
|
|
214
|
-
* Pointer-only by design: it is a "move this out of the way" gesture, not a layout
|
|
215
|
-
* choice worth persisting, and the position resets when the dialog closes. On a
|
|
216
|
-
* phone the panel is a bottom sheet the width of the screen, so dragging is
|
|
217
|
-
* suppressed there — there is nothing beside it to see.
|
|
218
|
-
*/
|
|
219
|
-
draggable?: boolean;
|
|
220
|
-
/**
|
|
221
|
-
* Bind the open state to a URL search param (0.12.0): `"edit"` is open while
|
|
222
|
-
* `?edit` is in the query, `{ key: "dialog", value: "create-lease" }` while
|
|
223
|
-
* `?dialog=create-lease` is. The URL is then the only state — `open` is ignored —
|
|
224
|
-
* and the dialog opens from anywhere that sets the param: {@link useDialogParam}'s
|
|
225
|
-
* setter, a `<Link>`, a shared link, a reload.
|
|
226
|
-
*
|
|
227
|
-
* kastlan's `useDialogParam` did this beside the dialog, so its open state could be
|
|
228
|
-
* deep-linked and captured in a feedback report. Here it also owns the history:
|
|
229
|
-
* opening through `useDialogParam` pushes an entry, so the Back gesture closes the
|
|
230
|
-
* dialog by leaving it, and a close (Escape, the backdrop, the X) goes back over
|
|
231
|
-
* that entry rather than stacking a second one. The dialog's own Back handling
|
|
232
|
-
* ({@link useOverlayHistory}'s throwaway entry) stays off in this mode — the param's
|
|
233
|
-
* entry already is one, and two would take two Back presses to close one dialog.
|
|
234
|
-
*
|
|
235
|
-
* `onClose` still runs, after the param is cleared. Requires a react-router Router.
|
|
236
|
-
*/
|
|
237
|
-
urlParam?: DialogParam;
|
|
238
|
-
/**
|
|
239
|
-
* A request the dialog started is in flight: it stays until the caller says otherwise
|
|
240
|
-
* (refactor audit 2026-10-30, DUP-3). A request that has left cannot be called back by
|
|
241
|
-
* closing the dialog, and the dialog should not pretend it can.
|
|
242
|
-
*
|
|
243
|
-
* While true the panel is `aria-busy`, and every dismissal is refused before the exit
|
|
244
|
-
* starts — the backdrop, Escape, Back, the frame's X and an `actions(close)` button —
|
|
245
|
-
* so nothing lowers and snaps back. Five dialogs used to hold Escape by hand, in an
|
|
246
|
-
* `onKeyDown`, and nothing else: a backdrop press or Android's Back closed four of them
|
|
247
|
-
* mid-request, and CancelSubscriptionConfirm, which guarded its `onClose`, lowered its
|
|
248
|
-
* panel and popped it back up.
|
|
249
|
-
*
|
|
250
|
-
* Escape still goes to whatever is open over the dialog first (a list, a tooltip); the
|
|
251
|
-
* press that reaches the dialog's own layer is taken and dropped. A refused Back puts
|
|
252
|
-
* the dialog's history entry back, so the Back after the request closes the dialog
|
|
253
|
-
* rather than leaving the page.
|
|
254
|
-
*
|
|
255
|
-
* Not held: a close the caller decides on (`open={false}`, unmounting), and — with
|
|
256
|
-
* `urlParam` — Back, which leaves the param's entry like any other navigation. A close
|
|
257
|
-
* the dialog asks for itself through `ModalCloseContext` while busy is refused too; one
|
|
258
|
-
* made from an effect that depends on that close (ReauthDialog's on a resolved submit)
|
|
259
|
-
* runs again once `busy` turns false, because the close changes with it.
|
|
260
|
-
*
|
|
261
|
-
* A dialog that must not be dismissed while NOTHING runs — a code shown once — wants
|
|
262
|
-
* {@link ModalProps.held} instead: the same hold without the request.
|
|
263
|
-
*/
|
|
264
|
-
busy?: boolean;
|
|
265
|
-
/**
|
|
266
|
-
* Hold the dialog open: every dismissal `busy` refuses is refused — the backdrop,
|
|
267
|
-
* Escape, Back, the frame's X and an `actions(close)` button — but no request is
|
|
268
|
-
* claimed: the panel is not `aria-busy`. For a dialog the user must not lose by
|
|
269
|
-
* accident while nothing is in flight (keksdose audit follow-up, 2026-11-01): keksdose's
|
|
270
|
-
* one-time recovery code (auth/recovery-code-pane.tsx) refused in its own `onClose`,
|
|
271
|
-
* which comes after the exit has started and cannot hold Back — the first Back spent
|
|
272
|
-
* the dialog's history entry, and the second left the page, and the code with it.
|
|
273
|
-
* Held, a refused Back puts the entry back, so every Back is held, not only the first.
|
|
274
|
-
* The refusal is silent, as under `busy` — `onClose` is not called — so what the
|
|
275
|
-
* dialog is waiting for is said up front, not after a refused press.
|
|
276
|
-
*
|
|
277
|
-
* The way out is a close the CALLER decides on, which is never held: `open={false}`
|
|
278
|
-
* (the exit still plays) or unmounting, from the dialog's own "I have saved it" button.
|
|
279
|
-
* Not the `close` that `actions` and `headerActions` receive — that is the dialog's
|
|
280
|
-
* own dismissal, and held like the rest.
|
|
281
|
-
*
|
|
282
|
-
* `DialogFrame`'s X is DISABLED while held, where under `busy` it stays enabled and its
|
|
283
|
-
* press is dropped: a request is over in a moment, a hold lasts until the user acts,
|
|
284
|
-
* and an X that does nothing for that long reads as broken. Disabled rather than
|
|
285
|
-
* hidden, so the header does not reflow the moment the hold lifts; a caller that wants
|
|
286
|
-
* it gone passes `closeButton={!held}`. Independent of `busy`: either one holds.
|
|
287
|
-
*/
|
|
288
|
-
held?: boolean;
|
|
289
|
-
/**
|
|
290
|
-
* The PANEL — the `role="dialog"` element the rest of the props go on, not the
|
|
291
|
-
* backdrop around it (0.35, round 0.35 R4, the follow-up to API-2; as on
|
|
292
|
-
* `FullBleedDialog`). Merged with the panel's own ref, which the focus
|
|
293
|
-
* trap holds. Set while the panel is in the document: `null` while a dialog with
|
|
294
|
-
* `open={false}` is closed, and after a `urlParam` dialog's param is cleared.
|
|
295
|
-
*/
|
|
296
|
-
ref?: Ref<HTMLDivElement>;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* The URL-bound dialog: the param is the open state, and closing clears it. A
|
|
301
|
-
* component of its own so the router hook is only called by a `Modal` that asked for
|
|
302
|
-
* it — a plain `Modal` must keep working with no Router above it.
|
|
303
|
-
*/
|
|
304
|
-
function UrlParamModal({ urlParam, onClose, ...rest }: ModalProps & { urlParam: DialogParam }) {
|
|
305
|
-
const [open, setOpen] = useDialogParam(urlParam);
|
|
306
|
-
// Closed from here the moment the dismissal is decided. Going back is a traversal
|
|
307
|
-
// the browser lands a task later, and between the two the param still says "open":
|
|
308
|
-
// without this the panel, already lowered, would pop back up for that task.
|
|
309
|
-
const [leaving, setLeaving] = useState(false);
|
|
310
|
-
if (leaving && !open) setLeaving(false);
|
|
311
|
-
return (
|
|
312
|
-
<ModalPanel
|
|
313
|
-
{...rest}
|
|
314
|
-
open={open && !leaving}
|
|
315
|
-
ownHistoryEntry={false}
|
|
316
|
-
onClose={() => {
|
|
317
|
-
setLeaving(true);
|
|
318
|
-
setOpen(false);
|
|
319
|
-
onClose();
|
|
320
|
-
}}
|
|
321
|
-
/>
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Centered (bottom-sheet on mobile) modal dialog: dimmed backdrop, single
|
|
327
|
-
* rounded panel. Owns the overlay mechanics that the hand-rolled dialogs each
|
|
328
|
-
* re-implemented or lacked — backdrop-click + Escape to close, the platform Back
|
|
329
|
-
* gesture (Keksdose feedback #172), background scroll lock (feedback #204), focus
|
|
330
|
-
* into the panel on open and back to the trigger on close, a Tab focus trap, and
|
|
331
|
-
* `role="dialog"`/`aria-modal`.
|
|
332
|
-
*/
|
|
333
|
-
export function Modal(props: ModalProps) {
|
|
334
|
-
return props.urlParam !== undefined ? (
|
|
335
|
-
<UrlParamModal {...props} urlParam={props.urlParam} />
|
|
336
|
-
) : (
|
|
337
|
-
<ModalPanel {...props} />
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function ModalPanel({
|
|
342
|
-
onClose,
|
|
343
|
-
open,
|
|
344
|
-
children,
|
|
345
|
-
size = "md",
|
|
346
|
-
className,
|
|
347
|
-
labelledBy,
|
|
348
|
-
onKeyDown,
|
|
349
|
-
fullBleed,
|
|
350
|
-
draggable,
|
|
351
|
-
style,
|
|
352
|
-
role = "dialog",
|
|
353
|
-
urlParam: _urlParam,
|
|
354
|
-
busy = false,
|
|
355
|
-
held = false,
|
|
356
|
-
"aria-busy": ariaBusy,
|
|
357
|
-
ownHistoryEntry = true,
|
|
358
|
-
ref,
|
|
359
|
-
...rest
|
|
360
|
-
}: ModalProps & {
|
|
361
|
-
/** False when the URL already holds an entry for this dialog — see `urlParam`. */
|
|
362
|
-
ownHistoryEntry?: boolean;
|
|
363
|
-
}) {
|
|
364
|
-
const panelRef = useRef<HTMLDivElement>(null);
|
|
365
|
-
// The caller's `ref` beside the trap's (round 0.35 R4): memoised, so the panel is not
|
|
366
|
-
// detached and re-attached on every render.
|
|
367
|
-
const setPanel = useMemo(() => mergeRefs(panelRef, ref), [ref]);
|
|
368
|
-
const drag = useDragOffset(Boolean(draggable));
|
|
369
|
-
// Without `open`, mounted means open — the constant `true` every hook below used to
|
|
370
|
-
// be given. With it, `open` itself.
|
|
371
|
-
const active = open ?? true;
|
|
372
|
-
|
|
373
|
-
// Whether the last close came through `requestClose`, i.e. has ALREADY been
|
|
374
|
-
// animated. Set in the same batch as the caller's `onClose`, so the render that sees
|
|
375
|
-
// `open` turn false also sees this and does not lower the panel a second time.
|
|
376
|
-
const [dismissed, setDismissed] = useState(false);
|
|
377
|
-
// The exit of a close the caller decided on (only reachable with `open`). Adjusted
|
|
378
|
-
// during render when `open` flips — `Collapse`'s pattern — so the very render that
|
|
379
|
-
// closes it already paints the `-out` classes rather than a frame of nothing.
|
|
380
|
-
const [exiting, setExiting] = useState(false);
|
|
381
|
-
const [prevOpen, setPrevOpen] = useState(open);
|
|
382
|
-
if (open !== prevOpen) {
|
|
383
|
-
setPrevOpen(open);
|
|
384
|
-
setDismissed(false);
|
|
385
|
-
setExiting(open === false && prevOpen === true && !dismissed && !skipsExitAnimation());
|
|
386
|
-
if (open) drag.reset();
|
|
387
|
-
}
|
|
388
|
-
useEffect(() => {
|
|
389
|
-
if (!exiting) return;
|
|
390
|
-
const timer = setTimeout(() => setExiting(false), OVERLAY_EXIT_MS);
|
|
391
|
-
return () => clearTimeout(timer);
|
|
392
|
-
}, [exiting]);
|
|
393
|
-
|
|
394
|
-
// The panel lowers itself before the caller unmounts it (live #320 rework). Every
|
|
395
|
-
// dismissal below goes through `requestClose`; `onClose` still does the closing, one
|
|
396
|
-
// animation later. A caller that closes the dialog ITSELF — after a save, say —
|
|
397
|
-
// unmounts with no exit unless it passes `open`, which is what that prop is for.
|
|
398
|
-
const { closing, requestClose: lower } = useCloseTransition(() => {
|
|
399
|
-
setDismissed(true);
|
|
400
|
-
onClose();
|
|
401
|
-
});
|
|
402
|
-
// `busy` (DUP-3) and `held` refuse HERE, before `lower` starts the exit: a refusal in
|
|
403
|
-
// the caller's `onClose` comes 220ms too late, after the panel has already gone down.
|
|
404
|
-
// A new function when the hold flips, so a close asked for from an effect while busy
|
|
405
|
-
// (a resolved action whose caller's `isPending` trails its promise) runs again.
|
|
406
|
-
const holding = busy || held;
|
|
407
|
-
const requestClose = useCallback(() => {
|
|
408
|
-
if (!holding) lower();
|
|
409
|
-
}, [holding, lower]);
|
|
410
|
-
// A panel on its way out after `open` turned false is already closed: a press on
|
|
411
|
-
// its fading backdrop must not report a second close.
|
|
412
|
-
const backdropClose = useBackdropClose(active ? requestClose : () => {});
|
|
413
|
-
// Back means the same as Escape here. On a phone Escape doesn't exist, so without
|
|
414
|
-
// this the only way out of a dialog is finding its close button — and Back, the
|
|
415
|
-
// gesture everyone reaches for, navigated the page underneath instead (#172).
|
|
416
|
-
// Held only while open: a closed `open={false}` dialog must not own a history entry.
|
|
417
|
-
// `held`: a Back while busy or held is refused, and the entry it took is put back.
|
|
418
|
-
useOverlayHistory(active && ownHistoryEntry, requestClose, { held: holding });
|
|
419
|
-
|
|
420
|
-
// Through the shared hook and not by hand: this component's own save/restore copy
|
|
421
|
-
// was one half of the pair that left the page permanently unscrollable when a dialog
|
|
422
|
-
// containing an open sheet was closed — see the note in use-body-scroll-lock.ts.
|
|
423
|
-
useBodyScrollLock(active);
|
|
424
|
-
|
|
425
|
-
// Focus the panel itself (not the first field) so opening doesn't pop the mobile
|
|
426
|
-
// keyboard, while still moving focus into the dialog for keyboard and screen-reader
|
|
427
|
-
// users. Through the shared hook since this component is where it came from — the
|
|
428
|
-
// hook adds per-keystroke recomputation, a guard on restoring to a detached node,
|
|
429
|
-
// and nesting, none of which this copy had. Released when `open` turns false, which
|
|
430
|
-
// hands focus back to the trigger while the panel is still on its way out.
|
|
431
|
-
//
|
|
432
|
-
// "The panel itself" only when nothing inside asked for focus: a field the app
|
|
433
|
-
// autofocuses on purpose keeps it (0.33.1, keksdose live #402 — the same trap took
|
|
434
|
-
// an autofocused amount straight back in `FullBleedDialog`). Its keyboard is then the
|
|
435
|
-
// one the app chose to show; the trigger still gets focus back on close.
|
|
436
|
-
useFocusTrap(panelRef, { active, initialFocus: "container" });
|
|
437
|
-
const leaving = closing || exiting;
|
|
438
|
-
|
|
439
|
-
// One layer on the Escape stack while open (Wave 2, DUP-1): a list, a popover or a
|
|
440
|
-
// tooltip opened inside takes the key before the dialog does, wherever focus is —
|
|
441
|
-
// and the dialog gets it even when focus is not in the panel.
|
|
442
|
-
const layer = useDismissLayer(active, requestClose);
|
|
443
|
-
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
|
444
|
-
// The caller's handler sees Escape only while this dialog is the layer on top: a
|
|
445
|
-
// tooltip or a list open over it takes that press first (0.35, KS's gear wizard).
|
|
446
|
-
runOwnKeyDown(e, onKeyDown, layer);
|
|
447
|
-
// To whatever is on top — this dialog, unless something opened over it — and
|
|
448
|
-
// stopped here. Tab containment lives in useFocusTrap, which listens on the panel.
|
|
449
|
-
takeEscape(e);
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
// Portal to <body> so the `fixed inset-0 z-50` overlay escapes whatever stacking
|
|
453
|
-
// context its caller sits in. Rendered inline, a modal opened from e.g. the top
|
|
454
|
-
// bar (a z-30 context) could paint BELOW a sibling z-30 sticky element like the
|
|
455
|
-
// expanded transaction editor (feedback #320). The tour/command-palette overlays
|
|
456
|
-
// already portal for the same reason.
|
|
457
|
-
if (!active && !exiting) return null;
|
|
458
|
-
return createPortal(
|
|
459
|
-
<div
|
|
460
|
-
// Out of the accessibility tree for the length of a caller-driven exit: focus is
|
|
461
|
-
// already back on the page, and an `aria-modal` panel that is leaving would hide
|
|
462
|
-
// that page from a screen reader for 220ms. Not `inert` — that would let a tap
|
|
463
|
-
// fall through the fading backdrop to the row underneath.
|
|
464
|
-
aria-hidden={exiting || undefined}
|
|
465
|
-
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6):
|
|
466
|
-
// a backdrop, so the tour measures the panel inside it to tell a full-screen sheet.
|
|
467
|
-
data-kit-layer="backdrop"
|
|
468
|
-
className={cn(
|
|
469
|
-
// Live #320. The panel below rises only where it is bottom-anchored: from md up
|
|
470
|
-
// it is centred, and a centred box sliding up from off-screen reads as a
|
|
471
|
-
// different component arriving rather than as the same one settling.
|
|
472
|
-
leaving ? "animate-overlay-out" : "animate-overlay",
|
|
473
|
-
"fixed inset-0 z-50 flex items-end justify-center bg-black/40 md:items-center",
|
|
474
|
-
fullBleed ? "p-0 md:p-4" : "p-4",
|
|
475
|
-
)}
|
|
476
|
-
{...backdropClose}
|
|
477
|
-
>
|
|
478
|
-
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- always
|
|
479
|
-
role="dialog" or "alertdialog" (the ternary hides that from the rule): `onKeyDown`
|
|
480
|
-
is Escape-to-close per the APG dialog pattern, and the drag handlers are
|
|
481
|
-
swipe-to-dismiss, whose keyboard path is that same Escape. */}
|
|
482
|
-
<div
|
|
483
|
-
// `...rest` FIRST, everything the dialog needs to be a dialog after it. A
|
|
484
|
-
// wrapper that spreads its own props through to this one cannot then take away
|
|
485
|
-
// the role, the modality or the `tabIndex={-1}` that `useFocusTrap` needs —
|
|
486
|
-
// losing any of the three is silent, and the symptom (a dialog a screen reader
|
|
487
|
-
// walks straight out of) shows up nowhere near the call site that caused it.
|
|
488
|
-
{...rest}
|
|
489
|
-
ref={setPanel}
|
|
490
|
-
// Normalised at runtime too: an untyped caller's `role="presentation"` must not
|
|
491
|
-
// take the dialog role away (props-passthrough pins that).
|
|
492
|
-
role={role === "alertdialog" ? "alertdialog" : "dialog"}
|
|
493
|
-
aria-modal="true"
|
|
494
|
-
// A caller's own `aria-busy` (a preview still loading) is kept beside `busy`.
|
|
495
|
-
aria-busy={busy ? true : ariaBusy}
|
|
496
|
-
aria-labelledby={labelledBy}
|
|
497
|
-
tabIndex={-1}
|
|
498
|
-
onKeyDown={handleKeyDown}
|
|
499
|
-
{...drag.handlers}
|
|
500
|
-
// MERGED, not replaced: `drag.style` is undefined until a drag starts, so
|
|
501
|
-
// assigning it outright would drop a caller's own `style` on every render but
|
|
502
|
-
// the ones where the panel is being dragged.
|
|
503
|
-
style={{ ...style, ...drag.style }}
|
|
504
|
-
className={cn(
|
|
505
|
-
leaving ? "animate-sheet-out md:animate-none" : "animate-sheet md:animate-none",
|
|
506
|
-
"w-full rounded-lg border border-subtle bg-surface shadow-sm outline-none",
|
|
507
|
-
{ md: "max-w-md", lg: "max-w-lg", xl: "max-w-3xl", "2xl": "max-w-[110rem]" }[size],
|
|
508
|
-
// A panel taller than the screen has to scroll ITSELF. The backdrop is
|
|
509
|
-
// `fixed inset-0` and the body is scroll-locked while a dialog is open, so
|
|
510
|
-
// without this the overflow simply had nowhere to go: on a phone the panel
|
|
511
|
-
// is bottom-anchored, so it grew UPWARD past the top edge and the part that
|
|
512
|
-
// went past it could not be reached by any gesture (Keksdose live #254, on
|
|
513
|
-
// the price-merge dialog — but it was every dialog with more than a screen
|
|
514
|
-
// of content). `max-h-full` resolves against the backdrop's content box,
|
|
515
|
-
// which is the viewport minus its own padding, so the panel stops exactly
|
|
516
|
-
// where the screen does; `overscroll-contain` keeps the fling inside it.
|
|
517
|
-
//
|
|
518
|
-
// Three callers had already patched themselves with `max-h-[90vh]` +
|
|
519
|
-
// an inner scroller. Those still win — `cn` is tailwind-merge — and this is
|
|
520
|
-
// the default they should not have needed.
|
|
521
|
-
"max-h-full overflow-y-auto overscroll-contain",
|
|
522
|
-
"p-4",
|
|
523
|
-
// The grab affordance sits on the panel's own top strip: a drag handle of
|
|
524
|
-
// its own would be one more control in a dialog that is mostly one field.
|
|
525
|
-
draggable && "md:[&>*:first-child]:cursor-grab",
|
|
526
|
-
className,
|
|
527
|
-
)}
|
|
528
|
-
>
|
|
529
|
-
<ModalCloseContext.Provider value={requestClose}>{children}</ModalCloseContext.Provider>
|
|
530
|
-
</div>
|
|
531
|
-
</div>,
|
|
532
|
-
document.body,
|
|
533
|
-
);
|
|
534
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { formatResult } from "../lib/calc";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The draft-and-settle loop of a money field whose store is not AmountInput's text.
|
|
6
|
-
* Package-private — a module of its own so `money-field.tsx`'s `export *` in the barrel
|
|
7
|
-
* does not make it API.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/** The number an amount's text reads as — AmountInput reports dot-decimal text — `null`
|
|
11
|
-
* for empty, `undefined` for no number yet (a lone "-" or ".", a sum still being typed:
|
|
12
|
-
* "1200+"). */
|
|
13
|
-
export function readAmount(text: string): number | null | undefined {
|
|
14
|
-
const t = text.trim();
|
|
15
|
-
if (t === "") return null;
|
|
16
|
-
const n = Number(t);
|
|
17
|
-
return Number.isFinite(n) ? n : undefined;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** A stored amount as a number: a number as it is, a stored text read as an amount's
|
|
21
|
-
* text is, nothing as `null`. */
|
|
22
|
-
function amountOf(value: unknown): number | null | undefined {
|
|
23
|
-
if (value === null || value === undefined) return null;
|
|
24
|
-
return typeof value === "number" ? (Number.isFinite(value) ? value : undefined) : readAmount(String(value));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** {@link useMoneyDraft}'s answer. */
|
|
28
|
-
export interface MoneyDraft {
|
|
29
|
-
/** The text the field shows — AmountInput's `value`. */
|
|
30
|
-
draft: string;
|
|
31
|
-
/** AmountInput's `onChange`: keeps the typed text, and answers what it reads as — a
|
|
32
|
-
* number, `null` for empty, `undefined` for no number yet. */
|
|
33
|
-
change: (text: string) => number | null | undefined;
|
|
34
|
-
/** AmountInput's `onCommit`: what the settled text reads as. With no number in it (a
|
|
35
|
-
* lone "-") the draft goes back to the stored amount — rather than a guess, and rather
|
|
36
|
-
* than leaving "-" on screen while the store keeps the old figure — and the answer is
|
|
37
|
-
* `undefined`. A `"string"` store keeps what was typed: it holds that text too. */
|
|
38
|
-
settle: (text: string) => number | null | undefined;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The draft-and-settle loop of a money field whose store is a number, or text the store
|
|
43
|
-
* holds itself: `MoneyField`'s, and `RhfMoneyField`'s (`valueAs`). One copy since 0.34
|
|
44
|
-
* (DUP-9, refactor audit 2026-10): the twin's own kept a different parse and no
|
|
45
|
-
* snap-back, so a lone "-" stayed on screen while the form kept the old number.
|
|
46
|
-
*
|
|
47
|
-
* The text being typed lives here — a stored number cannot hold "12." or "12+5" — and is
|
|
48
|
-
* re-read from `value` only when `value` moves away from what the draft already says (a
|
|
49
|
-
* reset, another row's save, a `setValue`), so a commit's own echo keeps the typist's
|
|
50
|
-
* spelling ("12.50" stays "12.50" when 12.5 comes back).
|
|
51
|
-
*/
|
|
52
|
-
export function useMoneyDraft(value: unknown, valueAs: "number" | "string" = "number"): MoneyDraft {
|
|
53
|
-
// A number that is not finite is no amount, as on NumberField. NaN never equals itself,
|
|
54
|
-
// and the re-read below took it for a new value on every render until React threw "Too
|
|
55
|
-
// many re-renders" — MoneyField and RhfMoneyField both (refactor audit 2026-10-30,
|
|
56
|
-
// INP-11).
|
|
57
|
-
const stored = typeof value === "number" && !Number.isFinite(value) ? null : value;
|
|
58
|
-
// `formatResult`, not `String`: `String` writes exponent form below 1e-6 and from 1e21,
|
|
59
|
-
// and the commit's digits-only cleanup strips the "e" — one click into the field and
|
|
60
|
-
// away committed a float leftover of 5.1e-15 as 5.12, 1e-7 as 17 and 1e21 as 121
|
|
61
|
-
// (INP-12, BUG-11's class).
|
|
62
|
-
const external =
|
|
63
|
-
stored === null || stored === undefined ? "" : typeof stored === "number" ? formatResult(stored) : String(stored);
|
|
64
|
-
const [draft, setDraft] = useState(external);
|
|
65
|
-
const [seen, setSeen] = useState<unknown>(stored);
|
|
66
|
-
if (!Object.is(seen, stored)) {
|
|
67
|
-
setSeen(stored);
|
|
68
|
-
const same = valueAs === "string" ? draft === external : readAmount(draft) === amountOf(stored);
|
|
69
|
-
if (!same) setDraft(external);
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
draft,
|
|
73
|
-
change: (text) => {
|
|
74
|
-
setDraft(text);
|
|
75
|
-
return readAmount(text);
|
|
76
|
-
},
|
|
77
|
-
settle: (text) => {
|
|
78
|
-
const next = readAmount(text);
|
|
79
|
-
if (next === undefined && valueAs === "number") setDraft(external);
|
|
80
|
-
return next;
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
}
|