@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
package/src/lib/direction.ts
CHANGED
|
@@ -33,3 +33,42 @@ export function horizontalStep(key: string, el: Element | null | undefined): 1 |
|
|
|
33
33
|
const forward = (key === "ArrowRight") !== isRtl(el);
|
|
34
34
|
return forward ? 1 : -1;
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Where a key moves the focus in a list with ONE tab stop (a roving tabindex): the index
|
|
39
|
+
* to focus from `at` among `count` items, or `undefined` for a key that moves nothing.
|
|
40
|
+
* ←/→ along `dir` — → is next in `ltr` and previous in `rtl` — ↓/↑ as next and previous,
|
|
41
|
+
* Home and End to the ends. With `wrap`, past the last is the first and before the first
|
|
42
|
+
* the last (a ring: PieChart's slices); without, the ends hold (a plane: Treemap's tiles).
|
|
43
|
+
*
|
|
44
|
+
* The charts each mapped the keys themselves, and differently at the ends (0.52 code
|
|
45
|
+
* review of treemap.tsx, finding 9); the end rule is now a word at the call.
|
|
46
|
+
*
|
|
47
|
+
* `dir` and not an element to read it off (0.52 code review, deep, charts, findings 1 and
|
|
48
|
+
* 14): a list in the page's flow takes the page's (`dirOf(el)`), but a plot is laid out
|
|
49
|
+
* left to right in any page (see `ChartContainer`) and passes `"ltr"` — read off the
|
|
50
|
+
* element, Treemap's → walked an RTL page's tiles backwards, against the picture.
|
|
51
|
+
*/
|
|
52
|
+
export function rovingTarget(
|
|
53
|
+
key: string,
|
|
54
|
+
dir: Direction,
|
|
55
|
+
at: number,
|
|
56
|
+
count: number,
|
|
57
|
+
wrap: boolean,
|
|
58
|
+
): number | undefined {
|
|
59
|
+
const physical = key === "ArrowRight" ? 1 : key === "ArrowLeft" ? -1 : 0;
|
|
60
|
+
const step = dir === "rtl" ? -physical : physical;
|
|
61
|
+
const to = step
|
|
62
|
+
? at + step
|
|
63
|
+
: key === "ArrowDown"
|
|
64
|
+
? at + 1
|
|
65
|
+
: key === "ArrowUp"
|
|
66
|
+
? at - 1
|
|
67
|
+
: key === "Home"
|
|
68
|
+
? 0
|
|
69
|
+
: key === "End"
|
|
70
|
+
? count - 1
|
|
71
|
+
: undefined;
|
|
72
|
+
if (to === undefined || count <= 0) return undefined;
|
|
73
|
+
return wrap ? ((to % count) + count) % count : Math.max(0, Math.min(count - 1, to));
|
|
74
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// "The focused node left" (refactor audit 2026-10-30, Wave 1: DSP-4, DSP-15). When the
|
|
2
|
+
// element holding the keyboard's focus is removed — a table row deleted, filtered out or
|
|
3
|
+
// approved; a bulk bar gone with its Clear; a banner its × took away — the browser puts
|
|
4
|
+
// focus on <body> and says nothing: no blur in Chromium, none in jsdom. A keyboard or
|
|
5
|
+
// screen-reader user then starts again from the top of the page. The overlays give focus
|
|
6
|
+
// back to their opener (use-focus-trap); these helpers are for the controls that leave
|
|
7
|
+
// the page while the focus is IN them, and decide where it goes instead.
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Candidate tab stops, in DOM order — narrowed by {@link isTabStop} to the ones a Tab
|
|
11
|
+
* really reaches. The kit's one copy of the rule (0.52 code review, lib/focus-left.ts):
|
|
12
|
+
* this file and use-focus-trap each had it, with an exported `firstTabStop` apiece that
|
|
13
|
+
* disagreed about the element itself, and a combobox panel's toolbar had a third
|
|
14
|
+
* selector. The trap, the focus a removed node drops (below), the tour's field ring and
|
|
15
|
+
* the toolbar all read it here, so "the next stop" is the same stop everywhere.
|
|
16
|
+
*
|
|
17
|
+
* The selector is the one `Modal` used. On its own it was not enough (round 0.34
|
|
18
|
+
* BUG-4): only the bare `[tabindex]` clause excluded `-1`, so a `button`, an `input` or
|
|
19
|
+
* a link taken out of the tab order still counted. A panel that ends in a roving grid —
|
|
20
|
+
* the calendar's days, the month grid, the picker sheet's rows, every one of them `-1`
|
|
21
|
+
* but the active cell — then had a "last" stop no Tab ever reaches: Tab from the real
|
|
22
|
+
* last stop was not wrapped and walked out of the open panel, and Shift+Tab from the
|
|
23
|
+
* first landed on a cell that is not a stop at all.
|
|
24
|
+
*/
|
|
25
|
+
// `input[type=hidden]` is no stop: a Tab never lands on it and focus() on it does nothing,
|
|
26
|
+
// but its tabIndex reads 0, so a trap could take a form's hidden field for its first or last
|
|
27
|
+
// stop and Shift+Tab left the dialog (0.52 code review).
|
|
28
|
+
const TAB_STOP =
|
|
29
|
+
'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]):not([type="hidden"]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A stop the browser's own Tab would land on: in the tab order (`tabIndex` ≥ 0) and not
|
|
33
|
+
* inside something `hidden` or `inert`. Removes only elements a native Tab never
|
|
34
|
+
* reaches, so no correct order changes. `visibility: hidden` and closed `<details>` are
|
|
35
|
+
* still not walked — they need layout, which this rule has never read.
|
|
36
|
+
*/
|
|
37
|
+
function isTabStop(el: HTMLElement): boolean {
|
|
38
|
+
return el.tabIndex >= 0 && !el.closest("[hidden],[inert]");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TabStopOptions {
|
|
42
|
+
/** Whether `el` itself counts, first, when it is a stop. Default true: the tour's
|
|
43
|
+
* target may BE the field (refactor audit 2026-10-30, DUP-9). False for "a control
|
|
44
|
+
* inside it" — a row, a drawer, a trap's container, a toolbar. */
|
|
45
|
+
includeSelf?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The tab stops in `el`, in DOM order — `el` itself first when it is one, unless
|
|
49
|
+
* `includeSelf: false`. */
|
|
50
|
+
export function tabStops(el: Element, { includeSelf = true }: TabStopOptions = {}): HTMLElement[] {
|
|
51
|
+
const inside = Array.from(el.querySelectorAll<HTMLElement>(TAB_STOP)).filter(isTabStop);
|
|
52
|
+
const own = includeSelf && el instanceof HTMLElement && el.matches(TAB_STOP) && isTabStop(el);
|
|
53
|
+
return own ? [el, ...inside] : inside;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The first of {@link tabStops} — the control a person types into or presses — or null. */
|
|
57
|
+
export function firstTabStop(el: Element, options?: TabStopOptions): HTMLElement | null {
|
|
58
|
+
return tabStops(el, options)[0] ?? null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether the focus has fallen out of the page: onto `<body>`, onto nothing, or onto a
|
|
63
|
+
* node no longer in the document. False on the server, where nothing has focus to lose.
|
|
64
|
+
* A recovery asks this first: focus that went somewhere real (a dialog, the next field,
|
|
65
|
+
* an app's own `focus()`) is the user's or the app's, and is left where it is.
|
|
66
|
+
*/
|
|
67
|
+
export function focusIsLost(): boolean {
|
|
68
|
+
if (typeof document === "undefined") return false;
|
|
69
|
+
const el = document.activeElement;
|
|
70
|
+
return !el || el === document.body || !el.isConnected;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Calls `then` when a `blur`/`focusout` was the user's own move to nothing — a click on
|
|
75
|
+
* the page's background, or a click Safari answers by focusing nothing (it focuses no
|
|
76
|
+
* button or link on a click, and blurs what had the focus) — after which a note of where
|
|
77
|
+
* the focus was is stale (0.52 code review: DataTable's row, BulkActionBar's and
|
|
78
|
+
* AlertBanner's notes outlived it, and a removal much later pulled the focus, and the
|
|
79
|
+
* scroll, back to them).
|
|
80
|
+
*
|
|
81
|
+
* No element took the focus, the document holds none, and the node that lost it could
|
|
82
|
+
* still hold it. Not when it was removed, hidden, made inert or disabled: that is the page
|
|
83
|
+
* taking the focus away — the very case those notes are kept for. Not a window losing the
|
|
84
|
+
* focus either, which leaves the node the document's active element. Decided a microtask
|
|
85
|
+
* later, once the event's own work is done: a browser that blurs a node it is removing
|
|
86
|
+
* (or hiding) may do it while the node is still in place, and only after it is gone is
|
|
87
|
+
* that told apart from a click.
|
|
88
|
+
*/
|
|
89
|
+
export function whenFocusLeftForNothing(
|
|
90
|
+
e: { target: EventTarget | null; relatedTarget: EventTarget | null },
|
|
91
|
+
then: () => void,
|
|
92
|
+
): void {
|
|
93
|
+
const el = e.target;
|
|
94
|
+
if (e.relatedTarget !== null || !(el instanceof Element)) return;
|
|
95
|
+
queueMicrotask(() => {
|
|
96
|
+
if (!el.isConnected || el.matches(":disabled") || el.closest("[hidden],[inert]")) return;
|
|
97
|
+
// `display: none`, where the browser can say (jsdom cannot).
|
|
98
|
+
if (typeof el.checkVisibility === "function" && !el.checkVisibility()) return;
|
|
99
|
+
if (focusIsLost()) then();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The tab stops around `frame`, outside it, nearest first on each side — where focus
|
|
105
|
+
* goes once `frame` is gone. Read BEFORE it goes: a removed node has no place in the
|
|
106
|
+
* document to measure from. `limit` per side keeps a long page's stops out of memory.
|
|
107
|
+
*
|
|
108
|
+
* Walked outwards from the frame, `limit` stops each way (0.52 code review,
|
|
109
|
+
* lib/focus-left.ts): read from every stop in the page, a banner under a 2000-row table
|
|
110
|
+
* read all 2000 to keep the nearest five before it.
|
|
111
|
+
*/
|
|
112
|
+
export function tabStopsAround(frame: Element, limit = 5): { after: HTMLElement[]; before: HTMLElement[] } {
|
|
113
|
+
const doc = frame.ownerDocument;
|
|
114
|
+
const walker = doc.createTreeWalker(doc.documentElement, NodeFilter.SHOW_ELEMENT);
|
|
115
|
+
const isStop = (node: Node): node is HTMLElement =>
|
|
116
|
+
node instanceof HTMLElement && node.matches(TAB_STOP) && isTabStop(node);
|
|
117
|
+
// After: from the first element past the frame's own subtree, in document order.
|
|
118
|
+
const after: HTMLElement[] = [];
|
|
119
|
+
walker.currentNode = frame;
|
|
120
|
+
let node: Node | null = walker.nextSibling();
|
|
121
|
+
while (!node && walker.parentNode()) node = walker.nextSibling();
|
|
122
|
+
for (; node && after.length < limit; node = walker.nextNode()) if (isStop(node)) after.push(node);
|
|
123
|
+
// Before: back from the frame, nearest first (its ancestors among them, as before).
|
|
124
|
+
const before: HTMLElement[] = [];
|
|
125
|
+
walker.currentNode = frame;
|
|
126
|
+
for (node = walker.previousNode(); node && before.length < limit; node = walker.previousNode())
|
|
127
|
+
if (isStop(node)) before.push(node);
|
|
128
|
+
return { after, before };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Focus the first of `candidates` that is still in the page and takes the focus; true
|
|
133
|
+
* when one did. Each is tried and checked, since `focus()` on a node that cannot hold it
|
|
134
|
+
* (detached, `display: none`, disabled since) fails without a word.
|
|
135
|
+
*/
|
|
136
|
+
export function focusFirst(candidates: Iterable<Element | null | undefined>): boolean {
|
|
137
|
+
for (const el of candidates) {
|
|
138
|
+
if (!(el instanceof HTMLElement) || !el.isConnected) continue;
|
|
139
|
+
el.focus();
|
|
140
|
+
if (document.activeElement === el) return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Focus a container that is not a tab stop of its own — a table, a list — as the place
|
|
147
|
+
* the user is in, without making it one: `tabindex="-1"` for as long as it holds the
|
|
148
|
+
* focus, unless it already carries a `tabindex` of its own.
|
|
149
|
+
*/
|
|
150
|
+
export function focusContainer(el: HTMLElement): void {
|
|
151
|
+
if (!el.hasAttribute("tabindex")) {
|
|
152
|
+
el.setAttribute("tabindex", "-1");
|
|
153
|
+
el.addEventListener("blur", () => el.removeAttribute("tabindex"), { once: true });
|
|
154
|
+
}
|
|
155
|
+
el.focus();
|
|
156
|
+
}
|
package/src/lib/format.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import { useKitLocale, useKitTimeZone } from "../i18n/kit-labels";
|
|
3
3
|
import { parseIsoDate } from "./dates";
|
|
4
|
+
import { dateFormatter } from "./intl-cache";
|
|
5
|
+
import { isBareIsoDay } from "./iso-day";
|
|
4
6
|
import { formatWithMinus, type MinusStyle } from "./minus";
|
|
5
7
|
import { roundHalfAway } from "./round";
|
|
6
8
|
|
|
@@ -278,9 +280,6 @@ export interface FormatDateOptions extends BaseOptions {
|
|
|
278
280
|
timeZone?: string;
|
|
279
281
|
}
|
|
280
282
|
|
|
281
|
-
/** A bare ISO calendar day, `"YYYY-MM-DD"`. */
|
|
282
|
-
const BARE_ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
|
|
283
|
-
|
|
284
283
|
/** What {@link formatDate} and {@link formatRelativeTime} accept. */
|
|
285
284
|
export type DateInput = string | number | Date | null | undefined;
|
|
286
285
|
|
|
@@ -292,22 +291,11 @@ export type DateInput = string | number | Date | null | undefined;
|
|
|
292
291
|
*/
|
|
293
292
|
export function toDate(value: DateInput): Date | null {
|
|
294
293
|
if (value === null || value === undefined || value === "") return null;
|
|
295
|
-
if (
|
|
294
|
+
if (isBareIsoDay(value)) return parseIsoDate(value);
|
|
296
295
|
const d = value instanceof Date ? value : new Date(value);
|
|
297
296
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
298
297
|
}
|
|
299
298
|
|
|
300
|
-
const dateFormatters = new Map<string, Intl.DateTimeFormat>();
|
|
301
|
-
function dateFormatter(locale: string | undefined, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {
|
|
302
|
-
const key = `${locale ?? ""}|${JSON.stringify(options)}`;
|
|
303
|
-
let f = dateFormatters.get(key);
|
|
304
|
-
if (!f) {
|
|
305
|
-
f = new Intl.DateTimeFormat(locale, options);
|
|
306
|
-
dateFormatters.set(key, f);
|
|
307
|
-
}
|
|
308
|
-
return f;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
299
|
/**
|
|
312
300
|
* A date or timestamp in `locale`. Takes an ISO date ("2026-07-08", read on the local
|
|
313
301
|
* calendar), an ISO timestamp ("2026-07-08T14:05:00Z"), epoch milliseconds or a Date.
|
|
@@ -327,7 +315,7 @@ export function formatDate(
|
|
|
327
315
|
// A bare "YYYY-MM-DD" is a calendar day, which has no zone: it is the day it names
|
|
328
316
|
// wherever it is read, so a `timeZone` (in `options` or the style) never moves it — the
|
|
329
317
|
// device's local midnight shown in a zone west of it was the day before (round 0.45, N9).
|
|
330
|
-
if (
|
|
318
|
+
if (isBareIsoDay(value)) {
|
|
331
319
|
const day = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
|
|
332
320
|
return dateFormatter(options.locale, { ...base, timeZone: "UTC" }).format(day);
|
|
333
321
|
}
|
|
@@ -425,7 +413,7 @@ export function formatRelativeTime(value: DateInput, options: FormatRelativeTime
|
|
|
425
413
|
// tooltip) that said otherwise. Counted in `timeZone`, the zone that date is printed
|
|
426
414
|
// in; a bare "YYYY-MM-DD" is the day it names wherever it is read, as in formatDate.
|
|
427
415
|
// Still the same day (22 hours and more since just after midnight): in hours.
|
|
428
|
-
const bareDay =
|
|
416
|
+
const bareDay = isBareIsoDay(value);
|
|
429
417
|
const days = dayNumber(d, bareDay ? undefined : options.timeZone) - dayNumber(now, options.timeZone);
|
|
430
418
|
return days === 0 ? rtf.format(Math.round(diff / HOUR), "hour") : rtf.format(days, "day");
|
|
431
419
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP status of a thrown error, as one probe (DUP-11, refactor audit 2026-10: the
|
|
3
|
+
* session refresh carried its own copy).
|
|
4
|
+
*
|
|
5
|
+
* It lived in `auth/auth-errors.ts` until 0.52, which made `lib/session-refresh` and
|
|
6
|
+
* `/sync`'s engine import the auth folder — and put auth's English error words in the
|
|
7
|
+
* `/sync` entry's closure — for a three-line reader of `status` (refactor audit
|
|
8
|
+
* 2026-10-30, MOD-6). `auth-errors.ts` re-exports it, so nothing that imported it there
|
|
9
|
+
* moved.
|
|
10
|
+
*
|
|
11
|
+
* @internal Package-private — not re-exported from the barrel.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
type Bag = Record<string, unknown>;
|
|
15
|
+
|
|
16
|
+
const isBag = (value: unknown): value is Bag => typeof value === "object" && value !== null;
|
|
17
|
+
|
|
18
|
+
/** An HTTP status as a number, or undefined. */
|
|
19
|
+
function statusOf(value: unknown): number | undefined {
|
|
20
|
+
return typeof value === "number" && Number.isInteger(value) ? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The HTTP status a thrown error carries, wherever the apps' HTTP clients put it —
|
|
25
|
+
* without depending on any of them:
|
|
26
|
+
*
|
|
27
|
+
* 1. `err.response.status` — axios (all three apps' clients);
|
|
28
|
+
* 2. `err.status` — ofetch's `FetchError`, the generated OpenAPI clients' `ApiError`, and
|
|
29
|
+
* a `Response` thrown as it came;
|
|
30
|
+
* 3. `err.statusCode` — ofetch's alias, and Node-style errors.
|
|
31
|
+
*
|
|
32
|
+
* `undefined` when none is a whole number.
|
|
33
|
+
*/
|
|
34
|
+
export function httpStatusOf(err: unknown): number | undefined {
|
|
35
|
+
if (!isBag(err)) return undefined;
|
|
36
|
+
const response = err.response;
|
|
37
|
+
return (isBag(response) ? statusOf(response.status) : undefined) ?? statusOf(err.status) ?? statusOf(err.statusCode);
|
|
38
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One `Intl.DateTimeFormat` per locale and option set, for every date the kit prints.
|
|
3
|
+
*
|
|
4
|
+
* Constructing the formatter is the expensive part: keksdose measured 392 ms per click on
|
|
5
|
+
* a 270-row register when each cell built its own (dev#632), and kept its own renderer of
|
|
6
|
+
* the date-format preference because the kit's built one per call (refactor audit
|
|
7
|
+
* 2026-10-30, DUP-2). `lib/format` and `lib/date-format` both format through this one.
|
|
8
|
+
*
|
|
9
|
+
* @internal Not part of the barrel — `lib/format` is re-exported whole, so the cache
|
|
10
|
+
* lives apart from it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const dateFormatters = new Map<string, Intl.DateTimeFormat>();
|
|
14
|
+
|
|
15
|
+
/** The cached formatter for `locale` and `options`. Throws as `Intl.DateTimeFormat` does
|
|
16
|
+
* for a tag it refuses — and caches nothing then. */
|
|
17
|
+
export function dateFormatter(locale: string | undefined, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {
|
|
18
|
+
const key = `${locale ?? ""}|${JSON.stringify(options)}`;
|
|
19
|
+
let f = dateFormatters.get(key);
|
|
20
|
+
if (!f) {
|
|
21
|
+
f = new Intl.DateTimeFormat(locale, options);
|
|
22
|
+
dateFormatters.set(key, f);
|
|
23
|
+
}
|
|
24
|
+
return f;
|
|
25
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bare ISO calendar day, `"YYYY-MM-DD"` — no time, no zone: the day it names in every
|
|
3
|
+
* zone. `toDate` and `formatDate` (lib/format), the kit's day ladder (`useDayFormatter`,
|
|
4
|
+
* lib/day-format) and the preference's renderers (lib/date-format) each kept a copy of this
|
|
5
|
+
* pattern (0.52 code review, lib/day-format.ts). Were one to learn a new shape (say
|
|
6
|
+
* `YYYY-MM-DDT00:00`) alone, the ladder would ask the provider for a different day than the
|
|
7
|
+
* kit prints. One rule.
|
|
8
|
+
*
|
|
9
|
+
* @internal Not part of the barrel: `lib/format` is re-exported whole, so it lives apart.
|
|
10
|
+
*/
|
|
11
|
+
export function isBareIsoDay(value: unknown): value is string {
|
|
12
|
+
return typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
13
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createContext, createElement, lazy, useCallback, useContext, useLayoutEffect, useState } from "react";
|
|
2
|
+
import type { ComponentType, LazyExoticComponent } from "react";
|
|
3
|
+
|
|
4
|
+
/** What a {@link lazyWithFallback} fallback is given besides the component's own props. */
|
|
5
|
+
export interface LazyFallbackProps {
|
|
6
|
+
/** What the import rejected with. Already on `console.error`. */
|
|
7
|
+
error: unknown;
|
|
8
|
+
/** Fetch the module again and draw it here if it comes (back online, the deploy's
|
|
9
|
+
* new chunk). Call it on the person's next attempt, never at once: offline, a retry
|
|
10
|
+
* on mount would fetch in a loop. */
|
|
11
|
+
retry: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Attempt<P> {
|
|
15
|
+
Component: LazyExoticComponent<ComponentType<P>>;
|
|
16
|
+
/** Its import failed and its fallback was drawn: the next mount, or `retry`, takes a
|
|
17
|
+
* fresh attempt. Marked when the fallback COMMITS, not when the import rejects: a
|
|
18
|
+
* first mount that suspended is thrown away and rendered anew, and an attempt marked
|
|
19
|
+
* at the rejection would hand that re-render a fresh import — which fails, and so on. */
|
|
20
|
+
spent: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const RetryContext = createContext<() => void>(() => {});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `React.lazy` for a module the kit loads late (refactor audit 2026-10, MOD-1: the tour
|
|
27
|
+
* overlay, the search palette, the feedback dialog), where an import that fails — a tab
|
|
28
|
+
* left open across a deploy, a local-first app offline — draws `Fallback` instead of
|
|
29
|
+
* throwing.
|
|
30
|
+
*
|
|
31
|
+
* `React.lazy` keeps what its import settled to, so one failed fetch threw on every render
|
|
32
|
+
* for as long as the component stayed mounted, to the nearest error boundary. All three
|
|
33
|
+
* sit above the routes — in the provider every app mounts, in the top bar — where that
|
|
34
|
+
* boundary is the whole app's: a white page because somebody pressed ⌘K or "Take the
|
|
35
|
+
* tour" (0.52 code review, tour.tsx: the tour's own fix, written by hand with a module-level
|
|
36
|
+
* `let`, made shared here).
|
|
37
|
+
*
|
|
38
|
+
* A failure is logged and the next mount (or the fallback's `retry`) fetches again; a
|
|
39
|
+
* re-render of a mounted fallback does not. `load` resolves to the component itself,
|
|
40
|
+
* through a LITERAL relative dynamic import of its module (as feedback-submit's), so an
|
|
41
|
+
* app's bundler can split it off. Render the result under a `Suspense`, as a lazy
|
|
42
|
+
* component. (No import call is spelled out here: the build's fix-esm-extensions step
|
|
43
|
+
* reads specifiers in the .d.ts comments too, and would try to resolve an example.)
|
|
44
|
+
*/
|
|
45
|
+
export function lazyWithFallback<P extends object>(
|
|
46
|
+
load: () => Promise<ComponentType<P>>,
|
|
47
|
+
Fallback: ComponentType<P & LazyFallbackProps>,
|
|
48
|
+
): ComponentType<P> {
|
|
49
|
+
const attempt = (): Attempt<P> => {
|
|
50
|
+
const made: Attempt<P> = {
|
|
51
|
+
spent: false,
|
|
52
|
+
Component: lazy<ComponentType<P>>(() =>
|
|
53
|
+
load().then(
|
|
54
|
+
(Loaded) => ({ default: Loaded }),
|
|
55
|
+
(error: unknown) => {
|
|
56
|
+
console.error(error);
|
|
57
|
+
function Failed(props: P) {
|
|
58
|
+
const retry = useContext(RetryContext);
|
|
59
|
+
useLayoutEffect(() => {
|
|
60
|
+
made.spent = true;
|
|
61
|
+
}, []);
|
|
62
|
+
return createElement(Fallback, { ...props, error, retry });
|
|
63
|
+
}
|
|
64
|
+
return { default: Failed };
|
|
65
|
+
},
|
|
66
|
+
),
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
return made;
|
|
70
|
+
};
|
|
71
|
+
let latest = attempt();
|
|
72
|
+
/** The attempt a new mount takes: the latest, or a fresh one once that has failed. */
|
|
73
|
+
const take = () => (latest.spent ? (latest = attempt()) : latest);
|
|
74
|
+
|
|
75
|
+
function LazyWithFallback(props: P) {
|
|
76
|
+
// Fixed per mount, so a re-render of a mounted fallback fetches nothing.
|
|
77
|
+
const [current, setCurrent] = useState(take);
|
|
78
|
+
const retry = useCallback(() => setCurrent(take), []);
|
|
79
|
+
return createElement(RetryContext.Provider, { value: retry }, createElement(current.Component, props));
|
|
80
|
+
}
|
|
81
|
+
return LazyWithFallback;
|
|
82
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a pathname is one a `PathPattern` names — the rule `useLastVisitedPage`'s
|
|
3
|
+
* `exclude` reads (landing/routing.tsx) and the feedback guard's one-shot routes
|
|
4
|
+
* (refactor audit 2026-10-30, FLW-7), here without the router: `landing/routing` imports
|
|
5
|
+
* react-router, which `/feedback` must not drag in. The one copy: routing kept a private
|
|
6
|
+
* one, and the two had drifted within the round (0.52 code review, this file).
|
|
7
|
+
*
|
|
8
|
+
* A string is that path and everything under it (`/join` is `/join` and `/join/abc`, not
|
|
9
|
+
* `/joined`; `/` is only `/`); a RegExp is tested on the pathname (a `g` flag dropped, so
|
|
10
|
+
* `lastIndex` never carries over between calls).
|
|
11
|
+
*
|
|
12
|
+
* Case-INSENSITIVE, as React Router matches a route unless it sets `caseSensitive` (0.52
|
|
13
|
+
* code review, feedback/feedback-page-link.ts): the FLW-7 guard compared case-sensitively,
|
|
14
|
+
* and `/Join?token=` went out as a link the router still rendered as `/join`. A RegExp
|
|
15
|
+
* gets the `i` flag for the same reason. Matching more than a case-sensitive route would
|
|
16
|
+
* only turns a link into text, or leaves a page unrecorded as a resume target: both
|
|
17
|
+
* callers' safe side.
|
|
18
|
+
*
|
|
19
|
+
* @internal Package-private — not re-exported from the barrel.
|
|
20
|
+
*/
|
|
21
|
+
export function matchesPathPattern(pathname: string, pattern: string | RegExp): boolean {
|
|
22
|
+
if (typeof pattern !== "string") {
|
|
23
|
+
return new RegExp(pattern.source, `${pattern.flags.replace(/[gi]/g, "")}i`).test(pathname);
|
|
24
|
+
}
|
|
25
|
+
const path = pathname.toLowerCase();
|
|
26
|
+
if (pattern === "/") return path === "/";
|
|
27
|
+
const base = (pattern.endsWith("/") ? pattern.slice(0, -1) : pattern).toLowerCase();
|
|
28
|
+
return path === base || path.startsWith(`${base}/`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The pathname of `href` (a path with its query and hash) as React Router matches it:
|
|
33
|
+
* each segment percent-decoded, an encoded `/` kept as `%2F` so it never splits a segment
|
|
34
|
+
* (react-router 8, `decodePath` in `matchRoutesImpl`). The 0.52 code review
|
|
35
|
+
* (feedback/feedback-page-link.ts) found `/j%6Fin?token=` passing the FLW-7 guard as a
|
|
36
|
+
* link that the router decoded to `/join`.
|
|
37
|
+
*
|
|
38
|
+
* A malformed escape leaves its own segment as written, where the router gives up on the
|
|
39
|
+
* whole path: the rest still decodes, so the answer is never less than the router's.
|
|
40
|
+
*
|
|
41
|
+
* @internal Package-private — not re-exported from the barrel.
|
|
42
|
+
*/
|
|
43
|
+
export function routerPathname(href: string): string {
|
|
44
|
+
return href
|
|
45
|
+
.replace(/[?#].*$/, "")
|
|
46
|
+
.split("/")
|
|
47
|
+
.map((segment) => {
|
|
48
|
+
try {
|
|
49
|
+
return decodeURIComponent(segment).replace(/\//g, "%2F");
|
|
50
|
+
} catch {
|
|
51
|
+
return segment;
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.join("/");
|
|
55
|
+
}
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* screen only delays its `onClose` — every test suite, the kit's and the apps', reads a
|
|
14
14
|
* closed overlay as gone at once.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
16
|
+
* Public since 0.52 (DUP-8, refactor audit 2026-10-30): Kurvenschmiede's chain animation
|
|
17
|
+
* carried a copy of its own.
|
|
17
18
|
*/
|
|
18
19
|
export function prefersReducedMotion(whereUnknown = false): boolean {
|
|
19
20
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return whereUnknown;
|
package/src/lib/round.ts
CHANGED
|
@@ -25,14 +25,19 @@ function shiftDecimal(value: number, by: number): number {
|
|
|
25
25
|
* `n` rounded to `digits` decimals, half away from zero, in decimal arithmetic:
|
|
26
26
|
* 1.005 → 1.01, -2.5 at 0 → -3. Never NaN for a finite `n`, and never -0 — `formatMoney`
|
|
27
27
|
* prints -0 as "-0,00 €", so a negative that rounds away comes back as 0. A non-finite
|
|
28
|
-
* `n` comes back unchanged.
|
|
28
|
+
* `n` comes back unchanged. `digits` counts in whole decimals, as `toFixed` takes it: 2.5
|
|
29
|
+
* is 2, and NaN is 0.
|
|
29
30
|
*/
|
|
30
31
|
export function roundHalfAway(n: number, digits: number): number {
|
|
31
32
|
if (!Number.isFinite(n)) return n;
|
|
33
|
+
// "1.2345e2.5" is NaN, and `|| 0` below made it 0: MeasuredGrid's `digits`, which went to
|
|
34
|
+
// `toFixed` before DUP-14 brought it here, wrote every computed cell of a table with a
|
|
35
|
+
// fractional one as "0" (0.52 code review, deep, charts, finding 5).
|
|
36
|
+
const places = Math.trunc(digits) || 0;
|
|
32
37
|
const abs = Math.abs(n);
|
|
33
38
|
// Every double from 2^53 up is a whole number already, and scaling one near
|
|
34
39
|
// Number.MAX_VALUE up would overflow to Infinity.
|
|
35
|
-
const magnitude = abs >= 2 ** 53 ? abs : shiftDecimal(Math.round(shiftDecimal(abs,
|
|
40
|
+
const magnitude = abs >= 2 ** 53 ? abs : shiftDecimal(Math.round(shiftDecimal(abs, places)), -places);
|
|
36
41
|
// The sign goes back on after rounding, so `|| 0` must see the signed result.
|
|
37
42
|
return (n < 0 ? -magnitude : magnitude) || 0;
|
|
38
43
|
}
|
package/src/lib/safe-storage.ts
CHANGED
|
@@ -16,8 +16,13 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Since 0.34 (DUP-11, refactor audit 2026-10) it also removes, and guards
|
|
18
18
|
* `sessionStorage` the same way: `landing/routing`, `billing/checkout-processing` and
|
|
19
|
-
* the demo banner each wrote that guard again by hand.
|
|
20
|
-
*
|
|
19
|
+
* the demo banner each wrote that guard again by hand.
|
|
20
|
+
*
|
|
21
|
+
* Since 0.52 all of it but {@link storedKeys} is public, with a JSON pair beside it
|
|
22
|
+
* (DUP-8, refactor audit 2026-10-30). Kept package-private, the guard was written again
|
|
23
|
+
* four times in the kit's showcase and in 16 files across the three apps, some of them
|
|
24
|
+
* without it: a token read that throws where storage is blocked takes every request
|
|
25
|
+
* down with it. (The kit's own DevMenu, 0.51, wrote the session pair a second time too.)
|
|
21
26
|
*/
|
|
22
27
|
|
|
23
28
|
/** `access` on the storage area, or `fallback` where there is none (SSR) or it throws. */
|
|
@@ -37,9 +42,13 @@ export function readStored(key: string): string | null {
|
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
/** Store `value` under `key`, silently doing nothing when storage is unavailable
|
|
40
|
-
* or full. A preference that cannot be persisted is not worth an exception.
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
* or full. A preference that cannot be persisted is not worth an exception. `null`
|
|
46
|
+
* removes the key, as {@link writeSessionStored}'s does (0.52; the two pairs wrote
|
|
47
|
+
* differently, and `null` past the types stored the string "null" — 0.52 code review). */
|
|
48
|
+
export function writeStored(key: string, value: string | null): void {
|
|
49
|
+
guarded("localStorage", undefined, (storage) =>
|
|
50
|
+
value === null ? storage.removeItem(key) : storage.setItem(key, value),
|
|
51
|
+
);
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
/** Forget `key`, silently doing nothing when storage is blocked — there is nothing to
|
|
@@ -60,10 +69,43 @@ export function writeSessionStored(key: string, value: string | null): void {
|
|
|
60
69
|
);
|
|
61
70
|
}
|
|
62
71
|
|
|
72
|
+
/**
|
|
73
|
+
* The JSON stored under `key`, when it parses and `isShape` accepts it; else `null`. Absent,
|
|
74
|
+
* blocked, malformed and another shape all read as "never set" — the caller's default —
|
|
75
|
+
* which is what the six hand-written `JSON.parse(readStored(key))` reads in the kit each
|
|
76
|
+
* answered in their own try/catch (DUP-8). `isShape` gets the parsed value as `unknown`:
|
|
77
|
+
* the shape check is the one part that is the caller's. A shape check that throws reads
|
|
78
|
+
* as another shape: null.
|
|
79
|
+
*/
|
|
80
|
+
export function readStoredJson<T>(key: string, isShape: (value: unknown) => value is T): T | null {
|
|
81
|
+
const text = readStored(key);
|
|
82
|
+
if (text === null) return null;
|
|
83
|
+
try {
|
|
84
|
+
const value: unknown = JSON.parse(text);
|
|
85
|
+
return isShape(value) ? value : null;
|
|
86
|
+
} catch {
|
|
87
|
+
// not JSON, or a shape check that read past what is there
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Store `value` as JSON under `key`, silently doing nothing when storage is unavailable
|
|
93
|
+
* or full, or the value has no JSON form (a cycle, a BigInt) — {@link writeStored}'s rule. */
|
|
94
|
+
export function writeStoredJson(key: string, value: unknown): void {
|
|
95
|
+
let text: string | undefined;
|
|
96
|
+
try {
|
|
97
|
+
text = JSON.stringify(value);
|
|
98
|
+
} catch {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// `undefined` (and a function) has no JSON form either: stringify answers undefined.
|
|
102
|
+
if (text !== undefined) writeStored(key, text);
|
|
103
|
+
}
|
|
104
|
+
|
|
63
105
|
/**
|
|
64
106
|
* Every key in `area` that starts with `prefix`, or `[]` where storage refuses — for the
|
|
65
107
|
* reset of this device's settings, which removes a table's views by their prefix
|
|
66
|
-
* (docs/settings-harmonization.md §11.4). Package-private
|
|
108
|
+
* (docs/settings-harmonization.md §11.4). Package-private: the reset's alone.
|
|
67
109
|
*/
|
|
68
110
|
export function storedKeys(prefix: string, area: "local" | "session" = "local"): string[] {
|
|
69
111
|
return guarded(area === "session" ? "sessionStorage" : "localStorage", [], (storage) => {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
38
|
import type { AxiosLikeInstance, ServerWakeAxiosConfig } from "./server-wake";
|
|
39
|
-
import { httpStatusOf } from "
|
|
39
|
+
import { httpStatusOf } from "./http-status";
|
|
40
40
|
|
|
41
41
|
/** What the app's `refresh` resolves: the new access token, plus whatever its `store`
|
|
42
42
|
* needs (its refresh token, the user). */
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a chart says a share of its whole — PieChart's slices and centre, Treemap's tiles
|
|
3
|
+
* and groups. Internal, not exported from the barrel.
|
|
4
|
+
*
|
|
5
|
+
* Each chart held a copy of the same two `Intl` formats and the same switch between them
|
|
6
|
+
* (0.52 code review of treemap.tsx, finding 9); one copy is the rule both say.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A formatter for shares (0–1) in `locale`, as a percent: one decimal under 10 %, where
|
|
11
|
+
* rounding to whole percents starts to lie — a slice of 0.4 % said as "0%" says it is not
|
|
12
|
+
* there — and whole percents above. Make it once per locale (it builds two `Intl` formats).
|
|
13
|
+
*/
|
|
14
|
+
export function shareFormat(locale: string | undefined): (share: number) => string {
|
|
15
|
+
const whole = new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 0 });
|
|
16
|
+
const fine = new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 1 });
|
|
17
|
+
return (share) => (share < 0.1 ? fine : whole).format(share);
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upper-case the first letter only, in `locale` — the one copy of the rule (refactor audit
|
|
3
|
+
* 2026-10-30, DUP-11: MonthPicker and JumpCalendar each had it, and MiniCalendar's caption
|
|
4
|
+
* still used the CSS `capitalize` it was written against).
|
|
5
|
+
*
|
|
6
|
+
* Several locales (fr, es, it, pt, …) write month names in lower case, which at the head
|
|
7
|
+
* of a field or a panel reads as a typo. But only the FIRST letter is ours to change:
|
|
8
|
+
* `capitalize` upper-cases every word, so Spanish read "Septiembre De 2026", and a
|
|
9
|
+
* caption that does not start with the month kept its wrong capital — Hungarian read
|
|
10
|
+
* "2026. Szeptember". This gives "Septiembre de 2026" and "2026. szeptember".
|
|
11
|
+
*
|
|
12
|
+
* In `locale` because the case mapping is the language's: Turkish upper-cases "i" to "İ".
|
|
13
|
+
* `undefined` is the runtime's default, as everywhere `Intl` takes one.
|
|
14
|
+
*
|
|
15
|
+
* Package-private — not re-exported from the barrel.
|
|
16
|
+
*/
|
|
17
|
+
export function upperFirst(text: string, locale: string | undefined): string {
|
|
18
|
+
return text.charAt(0).toLocaleUpperCase(locale) + text.slice(1);
|
|
19
|
+
}
|