@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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useId, useState } from "react";
|
|
2
|
-
import type { ComponentPropsWithRef, ReactNode } from "react";
|
|
1
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
2
|
+
import type { ComponentPropsWithRef, KeyboardEvent, ReactNode } from "react";
|
|
3
3
|
|
|
4
4
|
import type { LabelOverride } from "../i18n/kit-labels";
|
|
5
5
|
import { cn } from "../lib/cn";
|
|
@@ -8,7 +8,7 @@ import { TimeInput } from "../components/time-input";
|
|
|
8
8
|
import { COMMIT_EXCEPT_BILLING } from "../components/write-lock";
|
|
9
9
|
import { CARD_DESCRIPTION_CLASS } from "../settings/settings-card-classes";
|
|
10
10
|
import { SettingsCardTitle } from "../settings/settings-heading";
|
|
11
|
-
import { settle, useMounted } from "../
|
|
11
|
+
import { settle, useMounted } from "../hooks/use-mounted";
|
|
12
12
|
import { describeFailure, FailureLine, useNotificationsLabels, useWriteOverlay } from "./notification-parts";
|
|
13
13
|
import type { NotificationRegionLabels, NotificationsLabels } from "./notifications-labels";
|
|
14
14
|
import type { NotificationSettingsPatch } from "./notification-types";
|
|
@@ -49,7 +49,9 @@ export interface QuietHoursSettingProps extends Omit<ComponentPropsWithRef<"div"
|
|
|
49
49
|
held?: boolean;
|
|
50
50
|
/**
|
|
51
51
|
* Save one change — `PATCH /notifications/settings`: a time, or `null` when cleared.
|
|
52
|
-
* Never a zone.
|
|
52
|
+
* Never a zone. Called when a time is ENTERED — the field left, or Enter, or the card
|
|
53
|
+
* unmounted with a time still typed — never per keystroke, and one call per field at a
|
|
54
|
+
* time (0.52). The field shows the new value while the call runs and goes back if it
|
|
53
55
|
* rejects.
|
|
54
56
|
*/
|
|
55
57
|
onChange: (patch: NotificationSettingsPatch) => Promise<unknown> | void;
|
|
@@ -77,8 +79,13 @@ type QuietField = "quiet_hours_start" | "quiet_hours_end";
|
|
|
77
79
|
* only** — the zone of the device last used, else `Europe/Zurich`. The card names it
|
|
78
80
|
* read-only and never sends one; there is no picker and no "Use it".
|
|
79
81
|
*
|
|
80
|
-
* - **Two times**, each saved
|
|
81
|
-
*
|
|
82
|
+
* - **Two times**, each saved when it is entered — on leaving the field or on Enter, or
|
|
83
|
+
* when the card goes while it is still typed — and a cleared time sends `null`. While
|
|
84
|
+
* only one is set the card says so: the window fails open until both are.
|
|
85
|
+
* - **One request per field at a time** (0.52): a time entered while that field's save
|
|
86
|
+
* runs waits for it, and only the latest waiting one is sent. Each input event used to
|
|
87
|
+
* PATCH — typing "30" over 22:00's minutes sent 22:03 and 22:30, and whichever answer
|
|
88
|
+
* landed last stayed on the server (refactor audit 2026-10-30, FLW-4).
|
|
82
89
|
* - **The zone** shows while a time is set: `zone`, else `fallbackZone`.
|
|
83
90
|
*
|
|
84
91
|
* Under a write lock the fields are `commit` controls exempt from billing.
|
|
@@ -103,32 +110,83 @@ export function QuietHoursSetting({
|
|
|
103
110
|
const mounted = useMounted();
|
|
104
111
|
const id = useId();
|
|
105
112
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
// What is typed and not yet entered, per field: the field shows it, nothing is sent.
|
|
114
|
+
const [drafts, setDrafts] = useState<Partial<Record<QuietField, string>>>({});
|
|
115
|
+
// Per field: whether a save runs, and the latest time entered while it did (own key =
|
|
116
|
+
// one is waiting). Refs: a save's answer reads them after renders it did not see.
|
|
117
|
+
const running = useRef<Partial<Record<QuietField, boolean>>>({});
|
|
118
|
+
const waiting = useRef<Partial<Record<QuietField, string | null>>>({});
|
|
119
|
+
|
|
120
|
+
const server = (field: QuietField) => (field === "quiet_hours_start" ? start : end);
|
|
121
|
+
const saved = (field: QuietField) => (overlay.has(field) ? (overlay.get(field) ?? null) : server(field));
|
|
122
|
+
const shown = (field: QuietField) => drafts[field] ?? saved(field) ?? "";
|
|
123
|
+
const startShown = shown("quiet_hours_start");
|
|
124
|
+
const endShown = shown("quiet_hours_end");
|
|
110
125
|
const accountZone = zone ?? fallbackZone;
|
|
111
126
|
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
setFailure(null);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
() =>
|
|
118
|
-
if (mounted.current) for (const field of fields) overlay.settle(field, true);
|
|
119
|
-
},
|
|
120
|
-
(error: unknown) => {
|
|
121
|
-
if (!mounted.current) return;
|
|
122
|
-
for (const field of fields) overlay.settle(field, false);
|
|
123
|
-
setFailure(describeFailure(describeError?.(error), labels.failed));
|
|
124
|
-
},
|
|
127
|
+
const send = (field: QuietField, value: string | null) => {
|
|
128
|
+
running.current[field] = true;
|
|
129
|
+
if (mounted.current) setFailure(null);
|
|
130
|
+
settle(() => onChange({ [field]: value })).then(
|
|
131
|
+
() => done(field, true),
|
|
132
|
+
(error: unknown) => done(field, false, error),
|
|
125
133
|
);
|
|
126
134
|
};
|
|
127
135
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
136
|
+
/** A save answered: the waiting time goes next — even after an unmount, it is what the
|
|
137
|
+
* person entered last — and supersedes this answer, a failure included. */
|
|
138
|
+
const done = (field: QuietField, ok: boolean, error?: unknown) => {
|
|
139
|
+
running.current[field] = false;
|
|
140
|
+
if (Object.prototype.hasOwnProperty.call(waiting.current, field)) {
|
|
141
|
+
const next = waiting.current[field] ?? null;
|
|
142
|
+
delete waiting.current[field];
|
|
143
|
+
send(field, next);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (!mounted.current) return;
|
|
147
|
+
overlay.settle(field, ok);
|
|
148
|
+
if (!ok) setFailure(describeFailure(describeError?.(error), labels.failed));
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/** `draft` entered: the time it names, or `undefined` when that is what the field
|
|
152
|
+
* already holds and nothing is to be sent. Queued behind the field's running save. */
|
|
153
|
+
const save = (field: QuietField, draft: string): string | null | undefined => {
|
|
154
|
+
const next = draft || null;
|
|
155
|
+
if (next === saved(field)) return undefined;
|
|
156
|
+
if (running.current[field]) waiting.current[field] = next;
|
|
157
|
+
else send(field, next);
|
|
158
|
+
return next;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** The typed time, entered: saved unless it is what the field already holds. */
|
|
162
|
+
const enter = (field: QuietField) => {
|
|
163
|
+
const draft = drafts[field];
|
|
164
|
+
if (draft === undefined) return;
|
|
165
|
+
setDrafts((current) => {
|
|
166
|
+
const rest = { ...current };
|
|
167
|
+
delete rest[field];
|
|
168
|
+
return rest;
|
|
169
|
+
});
|
|
170
|
+
const next = save(field, draft);
|
|
171
|
+
if (next !== undefined) overlay.set(field, next);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Leaving the card is leaving the field. A focused input removed with its card fires no
|
|
175
|
+
// blur — Escape on the settings sheet, a shortcut that navigates — and the time it showed
|
|
176
|
+
// as typed was dropped (0.52 code review, quiet-hours-setting.tsx; save-on-blur is FLW-4's
|
|
177
|
+
// decision). So whatever is still typed is entered as the card goes. A ref to this
|
|
178
|
+
// render's drafts and saved values, assigned in an effect, never during render.
|
|
179
|
+
const leave = useRef<() => void>(() => {});
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
leave.current = () => {
|
|
182
|
+
for (const [field, draft] of Object.entries(drafts) as [QuietField, string][]) save(field, draft);
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
useEffect(() => () => leave.current(), []);
|
|
186
|
+
|
|
187
|
+
const type = (field: QuietField, value: string) => setDrafts((current) => ({ ...current, [field]: value }));
|
|
188
|
+
const enterOnEnter = (field: QuietField, event: KeyboardEvent<HTMLInputElement>) => {
|
|
189
|
+
if (event.key === "Enter") enter(field);
|
|
132
190
|
};
|
|
133
191
|
|
|
134
192
|
const anySet = Boolean(startShown || endShown);
|
|
@@ -144,19 +202,23 @@ export function QuietHoursSetting({
|
|
|
144
202
|
<TimeInput
|
|
145
203
|
id={`${id}-start`}
|
|
146
204
|
label={labels.quietFrom}
|
|
147
|
-
value={startShown
|
|
205
|
+
value={startShown}
|
|
206
|
+
onValueChange={(value) => type("quiet_hours_start", value)}
|
|
207
|
+
onBlur={() => enter("quiet_hours_start")}
|
|
208
|
+
onKeyDown={(event) => enterOnEnter("quiet_hours_start", event)}
|
|
148
209
|
disabled={disabled}
|
|
149
210
|
commit={COMMIT_EXCEPT_BILLING}
|
|
150
|
-
onValueChange={(value) => saveTime("quiet_hours_start", value)}
|
|
151
211
|
className="w-36"
|
|
152
212
|
/>
|
|
153
213
|
<TimeInput
|
|
154
214
|
id={`${id}-end`}
|
|
155
215
|
label={labels.quietUntil}
|
|
156
|
-
value={endShown
|
|
216
|
+
value={endShown}
|
|
217
|
+
onValueChange={(value) => type("quiet_hours_end", value)}
|
|
218
|
+
onBlur={() => enter("quiet_hours_end")}
|
|
219
|
+
onKeyDown={(event) => enterOnEnter("quiet_hours_end", event)}
|
|
157
220
|
disabled={disabled}
|
|
158
221
|
commit={COMMIT_EXCEPT_BILLING}
|
|
159
|
-
onValueChange={(value) => saveTime("quiet_hours_end", value)}
|
|
160
222
|
className="w-36"
|
|
161
223
|
/>
|
|
162
224
|
</div>
|
|
@@ -4,12 +4,13 @@ import { CheckCircle2, Link2Off } from "lucide-react";
|
|
|
4
4
|
|
|
5
5
|
import type { LabelOverride } from "../i18n/kit-labels";
|
|
6
6
|
import { cn } from "../lib/cn";
|
|
7
|
-
import { errorBodyWithCode
|
|
7
|
+
import { errorBodyWithCode } from "../auth/auth-errors";
|
|
8
|
+
import { httpStatusOf } from "../lib/http-status";
|
|
8
9
|
import { INTRO_CLASS, OUTCOME_CLASS, STATUS_HEADING_CLASS, useFocusWhen } from "../auth/status-parts";
|
|
9
10
|
import type { AuthHeadingLevel } from "../auth/status-parts";
|
|
10
11
|
import { Button } from "../components/button";
|
|
11
12
|
import { EmptyState } from "../components/empty-state";
|
|
12
|
-
import { useMounted } from "../
|
|
13
|
+
import { useMounted } from "../hooks/use-mounted";
|
|
13
14
|
import { FailureLine, useNotificationsLabels } from "./notification-parts";
|
|
14
15
|
import type { NotificationsLabels } from "./notifications-labels";
|
|
15
16
|
|
|
@@ -154,11 +155,7 @@ export function UnsubscribePanel({
|
|
|
154
155
|
tone="success"
|
|
155
156
|
icon={<CheckCircle2 />}
|
|
156
157
|
title={shown === "stopped-these" ? labels.stoppedThese : labels.stoppedAll(appName)}
|
|
157
|
-
hint={
|
|
158
|
-
<>
|
|
159
|
-
{labels.accountMail} {labels.turnBackOn}
|
|
160
|
-
</>
|
|
161
|
-
}
|
|
158
|
+
hint={labels.stoppedHint(labels.accountMail, labels.turnBackOn)}
|
|
162
159
|
action={settingsLink}
|
|
163
160
|
className={OUTCOME_CLASS}
|
|
164
161
|
/>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import { useMediaQuery } from "../hooks/use-media-query";
|
|
4
|
-
import { httpStatusOf } from "../
|
|
4
|
+
import { httpStatusOf } from "../lib/http-status";
|
|
5
5
|
import { PUSH_SUBSCRIPTION_CHANGED, keyBytes, pushSubscriptionInput as inputOf } from "./push-worker";
|
|
6
6
|
import type { PushSubscriptionInput } from "./push-worker";
|
|
7
7
|
|
|
@@ -85,7 +85,8 @@ export interface PushDevice {
|
|
|
85
85
|
* the permission was not granted or push is off on the server; rejects with a
|
|
86
86
|
* {@link PushDeviceError} or the app's request's error. */
|
|
87
87
|
enable: () => Promise<boolean>;
|
|
88
|
-
/** Tell the server first, then the browser, so a dropped device stops at once.
|
|
88
|
+
/** Tell the server first, then the browser, so a dropped device stops at once. At a
|
|
89
|
+
* sign-out, with no card mounted: {@link releaseThisDevicePush}. */
|
|
89
90
|
disable: () => Promise<void>;
|
|
90
91
|
sendTest: () => Promise<{ sent: number }>;
|
|
91
92
|
}
|
|
@@ -152,6 +153,78 @@ async function activeRegistration(): Promise<ServiceWorkerRegistration> {
|
|
|
152
153
|
|
|
153
154
|
const defaultEndpointTaken = (error: unknown) => httpStatusOf(error) === 409;
|
|
154
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Drop this browser's subscription when the person turns push off: the server first, so a
|
|
158
|
+
* dropped device stops receiving even if the browser's unsubscribe fails, then the
|
|
159
|
+
* browser, cut at 15 s. Whether there was one. A refused server keeps the browser's
|
|
160
|
+
* subscription: the card says the turn-off failed, still reads on, and the person tries
|
|
161
|
+
* again. {@link usePushDevice}'s `disable`; a sign-out has its own
|
|
162
|
+
* ({@link releaseThisDevicePush}).
|
|
163
|
+
*/
|
|
164
|
+
async function releaseSubscription(unregister: (endpoint: string) => Promise<unknown>): Promise<boolean> {
|
|
165
|
+
const registration = await activeRegistration();
|
|
166
|
+
const subscription = await registration.pushManager.getSubscription();
|
|
167
|
+
if (!subscription) return false;
|
|
168
|
+
await unregister(subscription.endpoint);
|
|
169
|
+
await within(subscription.unsubscribe(), PUSH_TIMEOUT_MS, "push_timeout");
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Release this browser's push at an EXPLICIT sign-out (0.52, docs/notifications-
|
|
175
|
+
* harmonization.md §10.6; refactor audit 2026-10-30, FLW-2) — without the settings card
|
|
176
|
+
* mounted, which `usePushDevice().disable` needs.
|
|
177
|
+
*
|
|
178
|
+
* A sign-out left the subscription registered to the account, so the device went on
|
|
179
|
+
* showing that account's notifications, title and body (keksdose: invoices, bookings,
|
|
180
|
+
* shares), to whoever used it next — while the same sign-out wiped the mirror, the outbox
|
|
181
|
+
* and the query cache "for the next person on this device".
|
|
182
|
+
*
|
|
183
|
+
* Call it with the app's `unregister` (`POST /notifications/devices/unregister
|
|
184
|
+
* {endpoint}`) BEFORE the session is cleared, while the token is still valid:
|
|
185
|
+
*
|
|
186
|
+
* async function logOut() {
|
|
187
|
+
* await releaseThisDevicePush(api.unregisterDevice).catch(() => undefined);
|
|
188
|
+
* clear();
|
|
189
|
+
* }
|
|
190
|
+
*
|
|
191
|
+
* The server first, then the browser — and the browser EVEN WHEN THE SERVER REFUSED (0.52
|
|
192
|
+
* code review, this file): `disable`'s order left a sign-out on a flaky network, or one
|
|
193
|
+
* that met a 5xx, with the device still subscribed. Unsubscribed, the push service answers
|
|
194
|
+
* the server's next send with 410 and the server prunes the row. The browser's unsubscribe
|
|
195
|
+
* is cut at 15 s (`PushDeviceError("push_timeout")`). Then it tells every mounted
|
|
196
|
+
* {@link usePushDevice} ({@link PUSH_SUBSCRIPTION_CHANGED}).
|
|
197
|
+
*
|
|
198
|
+
* Resolves whether this browser held a subscription; `false` at once where push is
|
|
199
|
+
* unsupported or was never set up — no service worker registration, or none ever active
|
|
200
|
+
* (0.52 code review: most devices, which used to reject with `no_service_worker`, after
|
|
201
|
+
* 10 s for a worker stuck installing). It asks the registration, not `ready`: a
|
|
202
|
+
* subscription belongs to the registration, and a sign-out waits for no worker. Rejects
|
|
203
|
+
* as the app's request did — once the browser has let go — or with `push_timeout`. Best
|
|
204
|
+
* effort at a sign-out: catch it, a sign-out must not stop on push.
|
|
205
|
+
*
|
|
206
|
+
* Only an explicit sign-out (Marcel's decision 2 of the 2026-10-30 audit): a session that
|
|
207
|
+
* merely expired keeps its device, and whoever signs back in turns push on again.
|
|
208
|
+
*/
|
|
209
|
+
export async function releaseThisDevicePush(unregister: (endpoint: string) => Promise<unknown>): Promise<boolean> {
|
|
210
|
+
if (!pushSupported()) return false;
|
|
211
|
+
try {
|
|
212
|
+
const registration = await navigator.serviceWorker.getRegistration();
|
|
213
|
+
// A worker never active holds no subscription: `subscribe` needs an active one.
|
|
214
|
+
if (!registration?.active) return false;
|
|
215
|
+
const subscription = await registration.pushManager.getSubscription();
|
|
216
|
+
if (!subscription) return false;
|
|
217
|
+
try {
|
|
218
|
+
await unregister(subscription.endpoint);
|
|
219
|
+
} finally {
|
|
220
|
+
await within(subscription.unsubscribe(), PUSH_TIMEOUT_MS, "push_timeout");
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
} finally {
|
|
224
|
+
notifyPushSubscriptionChanged();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
155
228
|
/**
|
|
156
229
|
* This browser's push for the signed-in account. Hand it one function per endpoint
|
|
157
230
|
* (§3.8); {@link PushDeviceSetting} draws it, and an app's own card may use it too.
|
|
@@ -344,14 +417,7 @@ export function usePushDevice(options: UsePushDeviceOptions): PushDevice {
|
|
|
344
417
|
if (!supported) return;
|
|
345
418
|
setBusy(true);
|
|
346
419
|
try {
|
|
347
|
-
|
|
348
|
-
const subscription = await registration.pushManager.getSubscription();
|
|
349
|
-
if (subscription) {
|
|
350
|
-
// The server first: a dropped device stops receiving even if the browser's
|
|
351
|
-
// unsubscribe below fails.
|
|
352
|
-
await latest.current.unregister(subscription.endpoint);
|
|
353
|
-
await within(subscription.unsubscribe(), PUSH_TIMEOUT_MS, "push_timeout");
|
|
354
|
-
}
|
|
420
|
+
await releaseSubscription((endpoint) => latest.current.unregister(endpoint));
|
|
355
421
|
if (mounted.current) {
|
|
356
422
|
setSubscribed(false);
|
|
357
423
|
setForeignSubscription(false);
|
|
@@ -2,6 +2,7 @@ import { createAsyncStoragePersister } from "@tanstack/query-async-storage-persi
|
|
|
2
2
|
import type { QueryClient } from "@tanstack/react-query";
|
|
3
3
|
import type { PersistedClient, PersistQueryClientOptions } from "@tanstack/react-query-persist-client";
|
|
4
4
|
import { del, get, keys, set } from "idb-keyval";
|
|
5
|
+
import { listenOn, postOnce } from "../lib/broadcast";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* The persisted TanStack Query cache, keksdose's policy as a kit helper
|
|
@@ -101,7 +102,9 @@ export interface QueryPersistence {
|
|
|
101
102
|
/** Everything `<PersistQueryClientProvider persistOptions>` needs. */
|
|
102
103
|
persistOptions: Omit<PersistQueryClientOptions, "queryClient">;
|
|
103
104
|
/** Sign-out: stops writing under this key, empties the in-memory cache and deletes this
|
|
104
|
-
* key's persisted copy.
|
|
105
|
+
* key's persisted copy. In every tab (0.52): the persistences of this key in the other tabs
|
|
106
|
+
* stop writing too, and delete the key once more after any save of theirs under way.
|
|
107
|
+
* Best-effort; never throws. */
|
|
105
108
|
clear: (queryClient: QueryClient) => Promise<void>;
|
|
106
109
|
/** The company switch (or any change of key without a sign-out): stops writing under this
|
|
107
110
|
* key — a save already scheduled is dropped too — and empties the in-memory cache, so
|
|
@@ -223,6 +226,31 @@ export function createQueryPersistence({
|
|
|
223
226
|
// throttled save of the emptied cache) is dropped. A restore re-arms it.
|
|
224
227
|
let retired = false;
|
|
225
228
|
|
|
229
|
+
// 0.52 (refactor audit 2026-10-30, FLW-1): `retired` lived in this tab alone, so a sign-out
|
|
230
|
+
// in one tab deleted the key and another tab's persister wrote its in-memory cache straight
|
|
231
|
+
// back under it. While armed, a persistence listens on a channel named after its key; a
|
|
232
|
+
// `clear` there retires it, and it deletes the key once more — IndexedDB runs a save of its
|
|
233
|
+
// own already under way first. A `leave` posts nothing: another tab may still be on that
|
|
234
|
+
// company. Without BroadcastChannel, as before 0.52.
|
|
235
|
+
const across = `eifi1-query-cache:${key}`;
|
|
236
|
+
let unlisten: (() => void) | null = null;
|
|
237
|
+
|
|
238
|
+
const retire = () => {
|
|
239
|
+
retired = true;
|
|
240
|
+
unlisten?.();
|
|
241
|
+
unlisten = null;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const listen = () => {
|
|
245
|
+
unlisten ??= listenOn(across, (data) => {
|
|
246
|
+
if (data !== "clear") return;
|
|
247
|
+
retire();
|
|
248
|
+
Promise.resolve()
|
|
249
|
+
.then(() => storage.removeItem(key))
|
|
250
|
+
.catch(() => undefined);
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
|
|
226
254
|
const keyAllowed = (queryKey: readonly unknown[]) =>
|
|
227
255
|
!denied.has(String(queryKey[0])) && (shouldPersistKey ? shouldPersistKey(queryKey) : true);
|
|
228
256
|
|
|
@@ -313,6 +341,7 @@ export function createQueryPersistence({
|
|
|
313
341
|
// Only the provider's restore reads: mounting this persistence again re-arms it.
|
|
314
342
|
getItem: async (k) => {
|
|
315
343
|
retired = false;
|
|
344
|
+
listen();
|
|
316
345
|
return (await storage.getItem(k)) ?? null;
|
|
317
346
|
},
|
|
318
347
|
setItem: async (k, v) => {
|
|
@@ -329,7 +358,7 @@ export function createQueryPersistence({
|
|
|
329
358
|
});
|
|
330
359
|
|
|
331
360
|
const leave = async (queryClient: QueryClient) => {
|
|
332
|
-
|
|
361
|
+
retire();
|
|
333
362
|
try {
|
|
334
363
|
queryClient.clear();
|
|
335
364
|
} catch {
|
|
@@ -338,7 +367,10 @@ export function createQueryPersistence({
|
|
|
338
367
|
};
|
|
339
368
|
|
|
340
369
|
const clear = async (queryClient: QueryClient) => {
|
|
341
|
-
|
|
370
|
+
// Retired first, so this tab's own channel is closed and does not hear its `clear`.
|
|
371
|
+
retire();
|
|
372
|
+
// Before this tab's delete: the other tabs stop writing as early as they can.
|
|
373
|
+
postOnce(across, "clear");
|
|
342
374
|
try {
|
|
343
375
|
queryClient.clear();
|
|
344
376
|
await persister.removeClient();
|
package/src/rhf/fields.tsx
CHANGED
|
@@ -791,7 +791,11 @@ function DateControl({
|
|
|
791
791
|
);
|
|
792
792
|
}
|
|
793
793
|
|
|
794
|
-
/**
|
|
794
|
+
/**
|
|
795
|
+
* The kit's {@link DatePicker}, holding an ISO `"YYYY-MM-DD"` string. `commit` and
|
|
796
|
+
* `disabledReason` are the picker's own (0.52, refactor audit 2026-10-30, API-3): a date
|
|
797
|
+
* that saves itself stays focusable under a lock and says why.
|
|
798
|
+
*/
|
|
795
799
|
export function RhfDateField<
|
|
796
800
|
TFieldValues extends FieldValues = FieldValues,
|
|
797
801
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
@@ -893,9 +897,11 @@ export type RhfDateRangePickerProps<
|
|
|
893
897
|
* pick, which would mark (and, with `mode: "onTouched"`, validate) an end the user
|
|
894
898
|
* is still choosing.
|
|
895
899
|
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
898
|
-
* `commit="apply"`
|
|
900
|
+
* Without `apply` (the picker's default) the first calendar click is reported on its
|
|
901
|
+
* own, so the form holds `(from, emptyValue)` until the second; with `apply` nothing
|
|
902
|
+
* reaches the form until Apply. `apply` was `commit="apply"` until 0.52 (gone in 0.53):
|
|
903
|
+
* `commit` is now the write lock's, as on every other binding — with
|
|
904
|
+
* `disabledReason`, the picker's own (refactor audit 2026-10-30, API-3).
|
|
899
905
|
*
|
|
900
906
|
* The common props (`label`, `hint`, `required`, `disabled`, `excludeWhenDisabled`,
|
|
901
907
|
* `control`, `className`) apply to the pair; `rules` are per field.
|
|
@@ -1460,6 +1466,7 @@ function MonthControl({
|
|
|
1460
1466
|
* lookup until 0.34, API-3), so focus-on-error lands there; the form's error paints the
|
|
1461
1467
|
* trigger and is the shell's message; the hint is the shell's description; neither is
|
|
1462
1468
|
* handed to the picker, so its box never changes. Picking marks the field touched.
|
|
1469
|
+
* `commit` and `disabledReason` are the picker's own (0.52, API-3).
|
|
1463
1470
|
*
|
|
1464
1471
|
* ```tsx
|
|
1465
1472
|
* <RhfMonthPicker name="fiscalYear" label="Fiscal year" mode="year" valueAsNumber
|
|
@@ -10,6 +10,8 @@ import { useFocusTrap } from "../hooks/use-focus-trap";
|
|
|
10
10
|
import { useBodyScrollLock } from "../hooks/use-body-scroll-lock";
|
|
11
11
|
import { takeEscape, useDismissLayer } from "../hooks/use-dismiss";
|
|
12
12
|
import { isImeEnter } from "../lib/ime";
|
|
13
|
+
// A provider's answer is a promise: the only case with anything to wait for.
|
|
14
|
+
import { isThenable } from "../lib/thenable";
|
|
13
15
|
import { useKitLabels, type LabelOverride } from "../i18n/kit-labels";
|
|
14
16
|
import { DEFAULT_COMMAND_PALETTE_LABELS } from "./command-palette-labels";
|
|
15
17
|
|
|
@@ -106,10 +108,6 @@ export { DEFAULT_COMMAND_PALETTE_LABELS };
|
|
|
106
108
|
*/
|
|
107
109
|
export type CommandPaletteDensity = "compact" | "comfortable";
|
|
108
110
|
|
|
109
|
-
/** A provider's answer is a promise — the only case with anything to wait for. */
|
|
110
|
-
const isThenable = (value: unknown): value is PromiseLike<unknown> =>
|
|
111
|
-
typeof (value as PromiseLike<unknown> | null)?.then === "function";
|
|
112
|
-
|
|
113
111
|
/** The defaults minus `dialog`, so a resolved `dialog` means someone SUPPLIED one —
|
|
114
112
|
* see the dialog's `aria-label` for why that distinction matters. */
|
|
115
113
|
const { dialog: DEFAULT_DIALOG_NAME, ...DEFAULTS_WITHOUT_DIALOG } = DEFAULT_COMMAND_PALETTE_LABELS;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import { Suspense, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
3
3
|
import { useHref, useInRouterContext, useNavigate } from "react-router";
|
|
4
4
|
import { AlertCircle, Loader2, Search } from "lucide-react";
|
|
5
5
|
import { Tooltip } from "../components/tooltip";
|
|
6
6
|
import { TOPBAR_TRIGGER_CLASS } from "../shell/topbar-controls";
|
|
7
7
|
import { cn } from "../lib/cn";
|
|
8
|
+
import { lazyWithFallback, type LazyFallbackProps } from "../lib/lazy-with-fallback";
|
|
9
|
+
import { isApplePlatform } from "../hooks/use-hotkey";
|
|
8
10
|
import { useKitLabels, type LabelOverride } from "../i18n/kit-labels";
|
|
9
11
|
// Types only from ./command-palette, gone from the built module: the palette loads late.
|
|
10
|
-
import type { CommandItem, CommandPaletteDensity, CommandPaletteLabels } from "./command-palette";
|
|
12
|
+
import type { CommandItem, CommandPalette, CommandPaletteDensity, CommandPaletteLabels } from "./command-palette";
|
|
11
13
|
import { DEFAULT_COMMAND_PALETTE_LABELS } from "./command-palette-labels";
|
|
12
14
|
import { useCommandKey } from "./use-command-key";
|
|
13
15
|
import { createSearchIndex } from "./search-index";
|
|
@@ -20,11 +22,32 @@ import { DEFAULT_GLOBAL_SEARCH_LABELS } from "./global-search-labels";
|
|
|
20
22
|
* entry chunks, for a dialog drawn only once somebody searches. A LITERAL relative
|
|
21
23
|
* `import()`, as the catalogue loaders are (i18n/languages.ts), so the library build keeps
|
|
22
24
|
* the module its own and an app's bundler splits it off.
|
|
25
|
+
*
|
|
26
|
+
* A fetch that fails — a tab left open across a deploy, offline — closes the search rather
|
|
27
|
+
* than throwing (0.52 code review, tour.tsx): the top bar sits above the routes' error
|
|
28
|
+
* boundaries in these apps, so the rejected import took the whole shell down on ⌘K.
|
|
23
29
|
*/
|
|
24
|
-
const LazyCommandPalette =
|
|
25
|
-
import("./command-palette").then((module) =>
|
|
30
|
+
const LazyCommandPalette = lazyWithFallback(
|
|
31
|
+
() => import("./command-palette").then((module) => module.CommandPalette),
|
|
32
|
+
PaletteUnavailable,
|
|
26
33
|
);
|
|
27
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Stands in for a palette whose chunk did not arrive. Mounted open — this attempt's fetch
|
|
37
|
+
* failed — it closes the search, so the trigger and ⌘K work again; opened after that, it
|
|
38
|
+
* fetches again, and the palette appears if it comes. Never both in one open: offline, a
|
|
39
|
+
* retry on the open it closes would fetch in a loop.
|
|
40
|
+
*/
|
|
41
|
+
function PaletteUnavailable({ open, onClose, retry }: ComponentProps<typeof CommandPalette> & LazyFallbackProps) {
|
|
42
|
+
const failedOpen = useRef(open);
|
|
43
|
+
useLayoutEffect(() => {
|
|
44
|
+
if (!open) failedOpen.current = false;
|
|
45
|
+
else if (failedOpen.current) onClose();
|
|
46
|
+
else retry();
|
|
47
|
+
}, [open, onClose, retry]);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
28
51
|
export interface GlobalSearchLabels {
|
|
29
52
|
/** The trigger's accessible name, and the dialog's. */
|
|
30
53
|
trigger: string;
|
|
@@ -193,12 +216,6 @@ export interface GlobalSearchProps {
|
|
|
193
216
|
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
194
217
|
}
|
|
195
218
|
|
|
196
|
-
function isApplePlatform(): boolean {
|
|
197
|
-
if (typeof navigator === "undefined") return false;
|
|
198
|
-
const nav = navigator as Navigator & { userAgentData?: { platform?: string } };
|
|
199
|
-
return /mac|iphone|ipad|ipod/i.test(nav.userAgentData?.platform || nav.platform || nav.userAgent);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
219
|
const pageLoad = (href: string) => window.location.assign(href);
|
|
203
220
|
const identity = (href: string) => href;
|
|
204
221
|
|
|
@@ -347,7 +364,8 @@ function GlobalSearchImpl({
|
|
|
347
364
|
|
|
348
365
|
const openSearch = useCallback(() => setOpen(true), [setOpen]);
|
|
349
366
|
const noop = useCallback(() => {}, []);
|
|
350
|
-
|
|
367
|
+
// Off, not a no-op: a no-op still cancelled every Mod+K (0.52 code review, global-search.tsx).
|
|
368
|
+
useCommandKey(openSearch, { enabled: shortcut });
|
|
351
369
|
|
|
352
370
|
/* ── the static index ─────────────────────────────────────────────────── */
|
|
353
371
|
|
|
@@ -572,6 +590,7 @@ function GlobalSearchImpl({
|
|
|
572
590
|
[l.trigger, l.placeholder, paletteLabels],
|
|
573
591
|
);
|
|
574
592
|
|
|
593
|
+
// `useHotkey`'s own platform check, so the label and the chord it answers never disagree.
|
|
575
594
|
const apple = useMemo(() => isApplePlatform(), []);
|
|
576
595
|
const keys = apple ? "⌘K" : "Ctrl K";
|
|
577
596
|
const triggerLabel = triggerName === "withShortcut" ? l.shortcut(keys) : l.trigger;
|
|
@@ -580,7 +599,10 @@ function GlobalSearchImpl({
|
|
|
580
599
|
label: triggerLabel,
|
|
581
600
|
tooltip: l.shortcut(keys),
|
|
582
601
|
keys,
|
|
583
|
-
|
|
602
|
+
// The one chord that opens it here: Mod+K since 0.52 (DUP-12). Both were announced,
|
|
603
|
+
// and on every platform one of them did nothing — Control+K is a Mac text field's
|
|
604
|
+
// kill-line (0.52 code review, global-search.tsx).
|
|
605
|
+
ariaKeyShortcuts: apple ? "Meta+K" : "Control+K",
|
|
584
606
|
};
|
|
585
607
|
|
|
586
608
|
return (
|
|
@@ -613,8 +635,7 @@ function GlobalSearchImpl({
|
|
|
613
635
|
</Tooltip>
|
|
614
636
|
)}
|
|
615
637
|
{/* Nothing is drawn while the palette's module is on its way (a few milliseconds,
|
|
616
|
-
once per page load). A failed load
|
|
617
|
-
boundary, as a lazy route's is. */}
|
|
638
|
+
once per page load). A failed load closes the search (`PaletteUnavailable`). */}
|
|
618
639
|
{paletteMounted && (
|
|
619
640
|
<Suspense fallback={null}>
|
|
620
641
|
<LazyCommandPalette
|
|
@@ -281,18 +281,3 @@ function capByGroup<E extends SearchEntry>(hits: SearchHit<E>[], groupLimit: num
|
|
|
281
281
|
}
|
|
282
282
|
return out;
|
|
283
283
|
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* One-shot form of {@link createSearchIndex}: the matching entries, best first. For a
|
|
287
|
-
* list that changes on every call (a server response, a store selector); build an index
|
|
288
|
-
* once when the entries are stable.
|
|
289
|
-
*/
|
|
290
|
-
export function matchEntries<E extends SearchEntry>(
|
|
291
|
-
entries: readonly E[],
|
|
292
|
-
query: string,
|
|
293
|
-
options?: SearchIndexOptions,
|
|
294
|
-
): E[] {
|
|
295
|
-
return createSearchIndex(entries, options)
|
|
296
|
-
.search(query)
|
|
297
|
-
.map((hit) => hit.entry);
|
|
298
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useHotkey, type UseHotkeyOptions } from "../hooks/use-hotkey";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Registers a global ⌘K / Ctrl-K shortcut that calls `onOpen`.
|
|
@@ -7,16 +7,17 @@ import { useEffect } from "react";
|
|
|
7
7
|
* the shortcut from the moment the top bar mounts but loads the palette only when it is
|
|
8
8
|
* first opened, and a hook imported from command-palette.tsx would have brought the
|
|
9
9
|
* palette along with it. Still exported from command-palette.tsx, where it always was.
|
|
10
|
+
*
|
|
11
|
+
* `useHotkey("Mod+K")` since 0.52 (refactor audit 2026-10-30, DUP-12): Cmd+K on a Mac,
|
|
12
|
+
* Ctrl+K elsewhere, with no other modifier, once per press. Hand-rolled, it took Ctrl+K
|
|
13
|
+
* on a Mac as well — a text field's kill-line there — and Ctrl+Shift+K, Cmd+Alt+K and
|
|
14
|
+
* every repeat of a held key. It still fires from inside a text field: the palette is
|
|
15
|
+
* where a search starts, whatever has focus.
|
|
16
|
+
*
|
|
17
|
+
* `enabled: false` lets Mod+K go (0.52 code review, global-search.tsx): `GlobalSearch
|
|
18
|
+
* shortcut={false}` passed a no-op, and the listener still cancelled every Mod+K — the
|
|
19
|
+
* browser's own Ctrl+K and any other handler on the page that checks `defaultPrevented`.
|
|
10
20
|
*/
|
|
11
|
-
export function useCommandKey(onOpen: () => void): void {
|
|
12
|
-
|
|
13
|
-
const handler = (e: KeyboardEvent) => {
|
|
14
|
-
if ((e.metaKey || e.ctrlKey) && (e.key === "k" || e.key === "K")) {
|
|
15
|
-
e.preventDefault();
|
|
16
|
-
onOpen();
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
document.addEventListener("keydown", handler);
|
|
20
|
-
return () => document.removeEventListener("keydown", handler);
|
|
21
|
-
}, [onOpen]);
|
|
21
|
+
export function useCommandKey(onOpen: () => void, options: Pick<UseHotkeyOptions, "enabled"> = {}): void {
|
|
22
|
+
useHotkey("Mod+K", () => onOpen(), { allowInInputs: true, enabled: options.enabled });
|
|
22
23
|
}
|
package/src/search.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
// `@eifi1/ui-kit/search` — the command palette / global search.
|
|
2
2
|
// A re-slicing of the main barrel, not a new API.
|
|
3
3
|
export * from "./search/command-palette";
|
|
4
|
-
|
|
4
|
+
// Named, not `*`: 0.53 took `SEARCH_TIER_POINTS` and `normalizeSearchText` off the surface
|
|
5
|
+
// (deprecated in 0.52, refactor audit 2026-10-30, API-4); the index keeps them.
|
|
6
|
+
export {
|
|
7
|
+
createSearchIndex,
|
|
8
|
+
type SearchEntry,
|
|
9
|
+
type SearchMatchField,
|
|
10
|
+
type SearchHit,
|
|
11
|
+
type SearchIndexOptions,
|
|
12
|
+
type SearchIndex,
|
|
13
|
+
} from "./search/search-index";
|
|
5
14
|
export * from "./search/global-search";
|
|
@@ -59,6 +59,9 @@ export function useSettingsFocus(anchor?: string | null): void {
|
|
|
59
59
|
if (ring.current) {
|
|
60
60
|
window.clearTimeout(ring.current.timer);
|
|
61
61
|
ring.current.el.classList.remove(...SETTINGS_FOCUS_RING);
|
|
62
|
+
// The mark goes with the ring, as the timer's own end takes both: left behind, the
|
|
63
|
+
// first card stayed "focused" for good (keksdose's matching-tab test reads it).
|
|
64
|
+
ring.current.el.removeAttribute("data-settings-focus");
|
|
62
65
|
}
|
|
63
66
|
el.classList.add(...SETTINGS_FOCUS_RING);
|
|
64
67
|
el.setAttribute("data-settings-focus", "");
|