@eifi1/ui-kit 0.31.1 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -28
- package/dist/account/delete-account-setting.d.ts +8 -3
- package/dist/admin/admin-action-confirm.d.ts +8 -3
- package/dist/admin/admin-action-log.d.ts +1 -1
- package/dist/admin/admin-action-log.js +4 -2
- package/dist/admin/admin-action-log.js.map +1 -1
- package/dist/admin/invitations-panel.d.ts +8 -3
- package/dist/admin/invitations-panel.js +29 -26
- package/dist/admin/invitations-panel.js.map +1 -1
- package/dist/admin/reviewer-scope-editor.d.ts +8 -3
- package/dist/admin/role-select.d.ts +10 -5
- package/dist/admin/transfer-ownership-dialog.d.ts +8 -3
- package/dist/admin/user-roster.d.ts +10 -5
- package/dist/admin/user-roster.js +8 -47
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/auth/auth-errors.d.ts +43 -8
- package/dist/auth/auth-errors.js +24 -1
- package/dist/auth/auth-errors.js.map +1 -1
- package/dist/auth/register-form.d.ts +8 -3
- package/dist/billing/billing-banner.d.ts +184 -0
- package/dist/billing/billing-banner.js +98 -0
- package/dist/billing/billing-banner.js.map +1 -0
- package/dist/billing/billing-labels.d.ts +100 -0
- package/dist/billing/billing-labels.js +76 -0
- package/dist/billing/billing-labels.js.map +1 -0
- package/dist/billing/billing-lock.d.ts +158 -0
- package/dist/billing/billing-lock.js +17 -0
- package/dist/billing/billing-lock.js.map +1 -0
- package/dist/billing/billing-standing.d.ts +149 -0
- package/dist/billing/billing-standing.js +28 -0
- package/dist/billing/billing-standing.js.map +1 -0
- package/dist/billing/plan-card.d.ts +248 -0
- package/dist/billing/plan-card.js +248 -0
- package/dist/billing/plan-card.js.map +1 -0
- package/dist/billing/plan-limit-notice.d.ts +156 -0
- package/dist/billing/plan-limit-notice.js +76 -0
- package/dist/billing/plan-limit-notice.js.map +1 -0
- package/dist/billing/plan-limit.d.ts +34 -0
- package/dist/billing/plan-limit.js +20 -0
- package/dist/billing/plan-limit.js.map +1 -0
- package/dist/billing/plan-price.d.ts +61 -0
- package/dist/billing/plan-price.js +45 -0
- package/dist/billing/plan-price.js.map +1 -0
- package/dist/billing/subscription-actions.d.ts +133 -0
- package/dist/billing/subscription-actions.js +28 -0
- package/dist/billing/subscription-actions.js.map +1 -0
- package/dist/billing/subscription-status-chip.d.ts +149 -0
- package/dist/billing/subscription-status-chip.js +48 -0
- package/dist/billing/subscription-status-chip.js.map +1 -0
- package/dist/chart.d.ts +9 -3
- package/dist/chart.js.map +1 -1
- package/dist/components/account-chips.d.ts +16 -4
- package/dist/components/account-chips.js +21 -3
- package/dist/components/account-chips.js.map +1 -1
- package/dist/components/alert-banner.d.ts +6 -1
- package/dist/components/alert-banner.js +5 -4
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +8 -3
- package/dist/components/amount-input.js +5 -5
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/appearance-labels.d.ts +38 -0
- package/dist/components/appearance-labels.js +19 -0
- package/dist/components/appearance-labels.js.map +1 -0
- package/dist/components/autocomplete.js +2 -2
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/breadcrumbs.js +3 -2
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/bulk-action-bar.d.ts +2 -2
- package/dist/components/bulk-action-bar.js +7 -12
- package/dist/components/bulk-action-bar.js.map +1 -1
- package/dist/components/button-group.d.ts +8 -3
- package/dist/components/button-group.js +7 -1
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/calculator.d.ts +8 -3
- package/dist/components/calculator.js +4 -3
- package/dist/components/calculator.js.map +1 -1
- package/dist/components/calendar-heatmap.js +5 -5
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/chart-height.d.ts +23 -0
- package/dist/components/chart-height.js +15 -0
- package/dist/components/chart-height.js.map +1 -0
- package/dist/components/chart-zoom.js +6 -1
- package/dist/components/chart-zoom.js.map +1 -1
- package/dist/components/checkbox-group.js +2 -2
- package/dist/components/checkbox-group.js.map +1 -1
- package/dist/components/checkbox.d.ts +10 -0
- package/dist/components/checkbox.js +25 -10
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/chip.d.ts +19 -3
- package/dist/components/chip.js +54 -23
- package/dist/components/chip.js.map +1 -1
- package/dist/components/choice-card.d.ts +1 -1
- package/dist/components/choice-card.js +9 -8
- package/dist/components/choice-card.js.map +1 -1
- package/dist/components/column-mapper.js +1 -1
- package/dist/components/column-mapper.js.map +1 -1
- package/dist/components/combobox-core.js +8 -7
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.js +8 -8
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +8 -3
- package/dist/components/copy-button.d.ts +10 -5
- package/dist/components/country-select.js +1 -1
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/currency-select.js +3 -3
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +8 -3
- package/dist/components/data-table-cells.d.ts +1 -1
- package/dist/components/data-table-filter-popover.d.ts +1 -1
- package/dist/components/data-table-filter-popover.js +4 -1
- package/dist/components/data-table-filter-popover.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-labels.d.ts +1 -1
- package/dist/components/data-table-pagination.d.ts +3 -2
- package/dist/components/data-table-pagination.js +15 -6
- package/dist/components/data-table-pagination.js.map +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +123 -52
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +1 -1
- package/dist/components/date-picker.js +18 -6
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/description-list.js +2 -2
- package/dist/components/description-list.js.map +1 -1
- package/dist/components/dialog-frame.js +2 -1
- package/dist/components/dialog-frame.js.map +1 -1
- package/dist/components/disclosure.js +3 -2
- package/dist/components/disclosure.js.map +1 -1
- package/dist/components/dropdown.d.ts +1 -1
- package/dist/components/dropdown.js +7 -1
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/facing-pair.d.ts +19 -6
- package/dist/components/facing-pair.js +3 -2
- package/dist/components/facing-pair.js.map +1 -1
- package/dist/components/field-parts.d.ts +102 -6
- package/dist/components/field-parts.js +81 -6
- package/dist/components/field-parts.js.map +1 -1
- package/dist/components/field-sync.d.ts +4 -1
- package/dist/components/field-sync.js +12 -2
- package/dist/components/field-sync.js.map +1 -1
- package/dist/components/field.js +2 -2
- package/dist/components/field.js.map +1 -1
- package/dist/components/file-button.d.ts +8 -3
- package/dist/components/file-dropzone.d.ts +8 -3
- package/dist/components/floating-panel.js +7 -4
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/focus-ring.d.ts +12 -0
- package/dist/components/focus-ring.js +8 -0
- package/dist/components/focus-ring.js.map +1 -0
- package/dist/components/form-actions.d.ts +8 -3
- package/dist/components/form-actions.js +33 -19
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/full-bleed-dialog.d.ts +11 -1
- package/dist/components/full-bleed-dialog.js +3 -1
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/iban-input.d.ts +8 -3
- package/dist/components/image-grid.js +1 -1
- package/dist/components/image-grid.js.map +1 -1
- package/dist/components/inline-edit-field.js +10 -4
- package/dist/components/inline-edit-field.js.map +1 -1
- package/dist/components/language-select.d.ts +8 -3
- package/dist/components/legal-page.d.ts +26 -5
- package/dist/components/legal-page.js +3 -1
- package/dist/components/legal-page.js.map +1 -1
- package/dist/components/legal.d.ts +8 -3
- package/dist/components/legal.js +2 -1
- package/dist/components/legal.js.map +1 -1
- package/dist/components/lightbox.js +24 -2
- package/dist/components/lightbox.js.map +1 -1
- package/dist/components/line-items.js +2 -1
- package/dist/components/line-items.js.map +1 -1
- package/dist/components/list.d.ts +5 -0
- package/dist/components/list.js +13 -9
- package/dist/components/list.js.map +1 -1
- package/dist/components/measured-grid.d.ts +3 -1
- package/dist/components/measured-grid.js +38 -20
- package/dist/components/measured-grid.js.map +1 -1
- package/dist/components/menu-item.js +2 -1
- package/dist/components/menu-item.js.map +1 -1
- package/dist/components/mini-calendar.js +8 -4
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/money-field.d.ts +8 -3
- package/dist/components/month-picker.js +50 -39
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/nav-pills.js +1 -1
- package/dist/components/nav-pills.js.map +1 -1
- package/dist/components/number-field.d.ts +8 -3
- package/dist/components/number-input.d.ts +11 -4
- package/dist/components/number-input.js +8 -6
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +8 -3
- package/dist/components/numpad-sheet.js +2 -1
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/one-time-code-input.js +2 -2
- package/dist/components/one-time-code-input.js.map +1 -1
- package/dist/components/page-contents.js +2 -2
- package/dist/components/page-contents.js.map +1 -1
- package/dist/components/phone-input.d.ts +8 -3
- package/dist/components/phone-input.js +4 -4
- package/dist/components/phone-input.js.map +1 -1
- package/dist/components/pie-chart.d.ts +8 -2
- package/dist/components/pie-chart.js +11 -7
- package/dist/components/pie-chart.js.map +1 -1
- package/dist/components/popover.d.ts +9 -1
- package/dist/components/popover.js +8 -4
- package/dist/components/popover.js.map +1 -1
- package/dist/components/row-actions.d.ts +100 -0
- package/dist/components/row-actions.js +231 -0
- package/dist/components/row-actions.js.map +1 -0
- package/dist/components/scroll-area.js +1 -1
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/search-field.js +3 -2
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/series-chart-budget.d.ts +30 -7
- package/dist/components/series-chart-budget.js +12 -4
- package/dist/components/series-chart-budget.js.map +1 -1
- package/dist/components/series-chart-ticks.d.ts +7 -1
- package/dist/components/series-chart-ticks.js +6 -0
- package/dist/components/series-chart-ticks.js.map +1 -1
- package/dist/components/series-chart.d.ts +58 -28
- package/dist/components/series-chart.js +42 -32
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/settings-fields.d.ts +57 -8
- package/dist/components/settings-fields.js +39 -1
- package/dist/components/settings-fields.js.map +1 -1
- package/dist/components/share-card.d.ts +11 -6
- package/dist/components/share-card.js +8 -2
- package/dist/components/share-card.js.map +1 -1
- package/dist/components/sign-chip.d.ts +4 -0
- package/dist/components/sign-chip.js +17 -4
- package/dist/components/sign-chip.js.map +1 -1
- package/dist/components/signature-pad.d.ts +3 -1
- package/dist/components/signature-pad.js +8 -5
- package/dist/components/signature-pad.js.map +1 -1
- package/dist/components/slider.js +7 -6
- package/dist/components/slider.js.map +1 -1
- package/dist/components/sparkline.d.ts +5 -1
- package/dist/components/sparkline.js +4 -2
- package/dist/components/sparkline.js.map +1 -1
- package/dist/components/stat-tile.d.ts +17 -1
- package/dist/components/stat-tile.js +48 -20
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/status-dot.d.ts +4 -0
- package/dist/components/status-dot.js +9 -2
- package/dist/components/status-dot.js.map +1 -1
- package/dist/components/swipeable-row.js +10 -2
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/switch.d.ts +21 -7
- package/dist/components/switch.js +24 -11
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.js +2 -2
- package/dist/components/table.js.map +1 -1
- package/dist/components/text-link.d.ts +8 -3
- package/dist/components/text-link.js +1 -1
- package/dist/components/text-link.js.map +1 -1
- package/dist/components/text.d.ts +7 -2
- package/dist/components/text.js +5 -5
- package/dist/components/text.js.map +1 -1
- package/dist/components/tile-radio.js +2 -1
- package/dist/components/tile-radio.js.map +1 -1
- package/dist/components/time-input.d.ts +8 -3
- package/dist/components/toast.d.ts +18 -2
- package/dist/components/toast.js +8 -5
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toggle-group.d.ts +25 -3
- package/dist/components/toggle-group.js +40 -11
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +10 -1
- package/dist/components/toggle-legend.js +21 -7
- package/dist/components/toggle-legend.js.map +1 -1
- package/dist/components/translation-review-editor.js +1 -1
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review-parts.d.ts +4 -0
- package/dist/components/translation-review.d.ts +4 -0
- package/dist/components/translation-review.js +3 -3
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/tree-view.js +3 -2
- package/dist/components/tree-view.js.map +1 -1
- package/dist/components/treemap.d.ts +5 -2
- package/dist/components/treemap.js +33 -16
- package/dist/components/treemap.js.map +1 -1
- package/dist/components/ui.d.ts +8 -3
- package/dist/components/ui.js +207 -119
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/components/user-avatar.d.ts +5 -1
- package/dist/components/user-avatar.js +1 -1
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/components/wizard-stepper.js +20 -14
- package/dist/components/wizard-stepper.js.map +1 -1
- package/dist/{data-table-labels-DUELeXpa.d.ts → data-table-labels-kpAAVi6g.d.ts} +26 -2
- package/dist/data-table.d.ts +1 -1
- package/dist/{delete-account-setting-CpIhzxxZ.d.ts → delete-account-setting-FLyJf-kZ.d.ts} +642 -60
- package/dist/demo/demo-banner.js +1 -0
- package/dist/demo/demo-banner.js.map +1 -1
- package/dist/feedback/feedback-attachment.d.ts +8 -3
- package/dist/feedback/feedback-context.d.ts +8 -3
- package/dist/feedback/feedback-crash.d.ts +8 -3
- package/dist/feedback/feedback-dialog.d.ts +8 -3
- package/dist/feedback/feedback-dialog.js +3 -3
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +8 -3
- package/dist/feedback/feedback-inbox.js +2 -2
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-labels.d.ts +8 -3
- package/dist/feedback/feedback-menu.d.ts +9 -4
- package/dist/feedback/feedback-record.d.ts +8 -3
- package/dist/feedback/feedback-row-detail.d.ts +8 -3
- package/dist/feedback/feedback-status-undo.d.ts +8 -3
- package/dist/feedback/feedback-submit.d.ts +8 -3
- package/dist/feedback/feedback-swipe.d.ts +8 -3
- package/dist/feedback/feedback-table.d.ts +8 -3
- package/dist/feedback/feedback-thread.d.ts +8 -3
- package/dist/feedback/feedback-thread.js +3 -3
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/feedback.d.ts +8 -3
- package/dist/hooks/use-anchored-panel.d.ts +32 -3
- package/dist/hooks/use-anchored-panel.js +21 -3
- package/dist/hooks/use-anchored-panel.js.map +1 -1
- package/dist/hooks/use-breakpoint.d.ts +88 -0
- package/dist/hooks/use-breakpoint.js +50 -0
- package/dist/hooks/use-breakpoint.js.map +1 -0
- package/dist/hooks/use-file-drop.d.ts +8 -3
- package/dist/hooks/use-large-text.d.ts +29 -0
- package/dist/hooks/use-large-text.js +20 -0
- package/dist/hooks/use-large-text.js.map +1 -0
- package/dist/hooks/use-windowed-rows.d.ts +52 -8
- package/dist/hooks/use-windowed-rows.js +94 -5
- package/dist/hooks/use-windowed-rows.js.map +1 -1
- package/dist/i18n/defaults.d.ts +8 -3
- package/dist/i18n/defaults.js +8 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +8 -3
- package/dist/i18n/german.js +94 -3
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +8 -3
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +10 -5
- package/dist/i18n/locales/de-CH.d.ts +8 -3
- package/dist/i18n/locales/en.d.ts +8 -3
- package/dist/i18n/locales/es.d.ts +8 -3
- package/dist/i18n/locales/es.js +89 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +8 -3
- package/dist/i18n/locales/fr.js +91 -3
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +8 -3
- package/dist/i18n/locales/hu.js +91 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +8 -3
- package/dist/i18n/locales/it.js +92 -3
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +8 -3
- package/dist/i18n/locales/zh.js +89 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +8 -3
- package/dist/i18n/review.js +15 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +27 -11
- package/dist/index.js +77 -4
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.d.ts +8 -3
- package/dist/landing/landing-sections.d.ts +8 -3
- package/dist/landing/landing-sections.js +7 -3
- package/dist/landing/landing-sections.js.map +1 -1
- package/dist/landing/public-header.d.ts +9 -4
- package/dist/landing/public-header.js +3 -3
- package/dist/landing/public-header.js.map +1 -1
- package/dist/landing/routing.d.ts +8 -3
- package/dist/lib/cn.js +3 -2
- package/dist/lib/cn.js.map +1 -1
- package/dist/rhf/fields.d.ts +8 -3
- package/dist/rhf/fields.js +1 -1
- package/dist/rhf/fields.js.map +1 -1
- package/dist/rhf/form.d.ts +8 -3
- package/dist/rhf/form.js +2 -2
- package/dist/rhf/form.js.map +1 -1
- package/dist/rhf.d.ts +8 -3
- package/dist/search/command-palette.d.ts +1 -1
- package/dist/search/command-palette.js +9 -8
- package/dist/search/command-palette.js.map +1 -1
- package/dist/settings/settings-layout.js +2 -1
- package/dist/settings/settings-layout.js.map +1 -1
- package/dist/settings/settings-section.d.ts +8 -3
- package/dist/settings/use-account-appearance.d.ts +92 -0
- package/dist/settings/use-account-appearance.js +63 -0
- package/dist/settings/use-account-appearance.js.map +1 -0
- package/dist/settings/use-settings-route.d.ts +1 -1
- package/dist/settings/use-settings-route.js +2 -3
- package/dist/settings/use-settings-route.js.map +1 -1
- package/dist/shell/app-shell.d.ts +12 -171
- package/dist/shell/app-shell.js +144 -28
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/auth-layout.js +1 -1
- package/dist/shell/auth-layout.js.map +1 -1
- package/dist/shell/option-switcher-menu.js +1 -1
- package/dist/shell/option-switcher-menu.js.map +1 -1
- package/dist/shell/top-bar-brand.d.ts +8 -3
- package/dist/shell/top-bar-brand.js +3 -1
- package/dist/shell/top-bar-brand.js.map +1 -1
- package/dist/shell/topbar-action-menu.d.ts +4 -0
- package/dist/shell/topbar-action-menu.js +3 -3
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell/topbar-controls.js +4 -4
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/shell.d.ts +9 -5
- package/dist/theme/contrast-tokens.d.ts +41 -0
- package/dist/theme/contrast-tokens.js +59 -0
- package/dist/theme/contrast-tokens.js.map +1 -0
- package/dist/theme/contrast.d.ts +110 -0
- package/dist/theme/contrast.js +128 -0
- package/dist/theme/contrast.js.map +1 -0
- package/dist/theme/palette-presets.d.ts +12 -1
- package/dist/theme/palette-presets.js +9 -4
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/theme/palette-store.js.map +1 -1
- package/dist/theme/text-size.d.ts +173 -0
- package/dist/theme/text-size.js +121 -0
- package/dist/theme/text-size.js.map +1 -0
- package/dist/wizard/stepper-nav.d.ts +8 -3
- package/dist/wizard/wizard-summary.js +1 -1
- package/dist/wizard/wizard-summary.js.map +1 -1
- package/dist/wizard.d.ts +8 -3
- package/package.json +1 -1
- package/src/admin/admin-action-log.tsx +6 -1
- package/src/admin/invitations-panel.tsx +36 -29
- package/src/admin/user-roster.tsx +17 -72
- package/src/auth/auth-errors.ts +68 -7
- package/src/billing/billing-banner.tsx +205 -0
- package/src/billing/billing-labels.ts +240 -0
- package/src/billing/billing-lock.ts +75 -0
- package/src/billing/billing-standing.ts +72 -0
- package/src/billing/plan-card.tsx +458 -0
- package/src/billing/plan-limit-notice.tsx +145 -0
- package/src/billing/plan-limit.ts +53 -0
- package/src/billing/plan-price.ts +101 -0
- package/src/billing/subscription-actions.tsx +76 -0
- package/src/billing/subscription-status-chip.tsx +95 -0
- package/src/chart.ts +2 -0
- package/src/components/account-chips.tsx +36 -10
- package/src/components/alert-banner.tsx +16 -7
- package/src/components/amount-input.tsx +5 -5
- package/src/components/appearance-labels.ts +47 -0
- package/src/components/autocomplete.tsx +2 -2
- package/src/components/breadcrumbs.tsx +3 -3
- package/src/components/bulk-action-bar.tsx +11 -14
- package/src/components/button-group.tsx +21 -0
- package/src/components/calculator.tsx +4 -3
- package/src/components/calendar-heatmap.tsx +5 -5
- package/src/components/chart-height.ts +30 -0
- package/src/components/chart-zoom.tsx +6 -1
- package/src/components/checkbox-group.tsx +2 -2
- package/src/components/checkbox.tsx +62 -20
- package/src/components/chip.tsx +139 -30
- package/src/components/choice-card.tsx +16 -11
- package/src/components/column-mapper.tsx +1 -1
- package/src/components/combobox-core.tsx +8 -7
- package/src/components/combobox.tsx +8 -8
- package/src/components/country-select.tsx +1 -1
- package/src/components/currency-select.tsx +3 -3
- package/src/components/data-table-filter-popover.tsx +7 -1
- package/src/components/data-table-pagination.tsx +60 -31
- package/src/components/data-table.tsx +164 -43
- package/src/components/date-picker.tsx +44 -10
- package/src/components/description-list.tsx +2 -2
- package/src/components/dialog-frame.tsx +2 -1
- package/src/components/disclosure.tsx +3 -2
- package/src/components/dropdown.tsx +10 -2
- package/src/components/facing-pair.ts +12 -5
- package/src/components/field-parts.tsx +190 -7
- package/src/components/field-sync.tsx +17 -3
- package/src/components/field.tsx +2 -2
- package/src/components/floating-panel.tsx +7 -4
- package/src/components/focus-ring.ts +12 -0
- package/src/components/form-actions.tsx +55 -18
- package/src/components/full-bleed-dialog.tsx +18 -4
- package/src/components/image-grid.tsx +1 -1
- package/src/components/inline-edit-field.tsx +26 -4
- package/src/components/legal-page.tsx +28 -2
- package/src/components/legal.tsx +19 -1
- package/src/components/lightbox.tsx +24 -2
- package/src/components/line-items.tsx +7 -1
- package/src/components/list.tsx +36 -10
- package/src/components/measured-grid.tsx +49 -24
- package/src/components/menu-item.tsx +2 -2
- package/src/components/mini-calendar.tsx +8 -4
- package/src/components/month-picker.tsx +15 -6
- package/src/components/nav-pills.tsx +1 -1
- package/src/components/number-input.tsx +14 -7
- package/src/components/numpad-sheet.tsx +2 -2
- package/src/components/one-time-code-input.tsx +2 -2
- package/src/components/page-contents.tsx +2 -2
- package/src/components/phone-input.tsx +9 -4
- package/src/components/pie-chart.tsx +19 -9
- package/src/components/popover.tsx +22 -5
- package/src/components/row-actions.tsx +450 -0
- package/src/components/scroll-area.tsx +1 -1
- package/src/components/search-field.tsx +4 -2
- package/src/components/series-chart-budget.ts +40 -8
- package/src/components/series-chart-ticks.ts +10 -0
- package/src/components/series-chart.tsx +123 -72
- package/src/components/settings-fields.tsx +89 -5
- package/src/components/share-card.tsx +31 -2
- package/src/components/sign-chip.tsx +28 -4
- package/src/components/signature-pad.tsx +13 -7
- package/src/components/slider.tsx +22 -9
- package/src/components/sparkline.tsx +11 -3
- package/src/components/stat-tile.tsx +70 -14
- package/src/components/status-dot.tsx +25 -2
- package/src/components/swipeable-row.tsx +10 -2
- package/src/components/switch.tsx +57 -23
- package/src/components/table.tsx +2 -2
- package/src/components/text-link.tsx +1 -1
- package/src/components/text.tsx +11 -6
- package/src/components/tile-radio.tsx +4 -1
- package/src/components/toast.tsx +27 -7
- package/src/components/toggle-group.tsx +115 -14
- package/src/components/toggle-legend.tsx +53 -8
- package/src/components/translation-review-editor.tsx +1 -1
- package/src/components/translation-review.tsx +11 -5
- package/src/components/tree-view.tsx +3 -2
- package/src/components/treemap.tsx +27 -10
- package/src/components/ui.tsx +353 -153
- package/src/components/user-avatar.tsx +1 -1
- package/src/components/wizard-stepper.tsx +5 -1
- package/src/demo/demo-banner.tsx +5 -0
- package/src/feedback/feedback-dialog.tsx +3 -3
- package/src/feedback/feedback-inbox.tsx +2 -2
- package/src/feedback/feedback-thread.tsx +3 -3
- package/src/hooks/use-anchored-panel.ts +67 -5
- package/src/hooks/use-breakpoint.ts +132 -0
- package/src/hooks/use-large-text.ts +41 -0
- package/src/hooks/use-windowed-rows.ts +168 -12
- package/src/i18n/defaults.ts +8 -0
- package/src/i18n/german.ts +107 -0
- package/src/i18n/kit-labels.tsx +12 -0
- package/src/i18n/locales/es.ts +98 -0
- package/src/i18n/locales/fr.ts +102 -0
- package/src/i18n/locales/hu.ts +100 -0
- package/src/i18n/locales/it.ts +103 -0
- package/src/i18n/locales/zh.ts +94 -0
- package/src/i18n/review.ts +14 -0
- package/src/index.ts +76 -6
- package/src/landing/landing-sections.tsx +7 -3
- package/src/landing/public-header.tsx +4 -4
- package/src/lib/cn.ts +13 -2
- package/src/rhf/fields.tsx +1 -1
- package/src/rhf/form.tsx +2 -2
- package/src/search/command-palette.tsx +10 -9
- package/src/settings/settings-layout.tsx +2 -1
- package/src/settings/use-account-appearance.ts +148 -0
- package/src/settings/use-settings-route.ts +3 -4
- package/src/shell/app-shell.tsx +278 -54
- package/src/shell/auth-layout.tsx +1 -1
- package/src/shell/option-switcher-menu.tsx +1 -1
- package/src/shell/top-bar-brand.tsx +3 -1
- package/src/shell/topbar-action-menu.tsx +3 -3
- package/src/shell/topbar-controls.tsx +4 -4
- package/src/theme/contrast-tokens.ts +112 -0
- package/src/theme/contrast.ts +225 -0
- package/src/theme/palette-presets.ts +24 -4
- package/src/theme/palette-store.ts +6 -1
- package/src/theme/text-size.ts +283 -0
- package/src/wizard/wizard-summary.tsx +4 -2
- package/tokens.css +158 -8
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
import { isTextSize, resolveTextSize } from "../theme/text-size";
|
|
3
|
+
import type { TextSize } from "../theme/text-size";
|
|
4
|
+
import { isContrastMode, resolveContrastMode } from "../theme/contrast";
|
|
5
|
+
import type { ContrastMode } from "../theme/contrast";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The text size and contrast follow the account like the language does
|
|
9
|
+
* (docs/text-size-harmonization.md §2.2, §2.6, §6; settings contract §6.2) — the same
|
|
10
|
+
* rule as {@link useAccountLanguage}, for two values at once.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Where a value in force came from, in the order they are asked. */
|
|
14
|
+
export type AppearanceSource = "device" | "account" | "default";
|
|
15
|
+
|
|
16
|
+
/** The two fields of an `/auth/me` answer this reads — server-kit 0.6's `UserResponse`,
|
|
17
|
+
* both nullable (null = never chosen). */
|
|
18
|
+
export interface AccountAppearanceFields {
|
|
19
|
+
text_size?: string | null;
|
|
20
|
+
contrast?: string | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** This device's own choices — the text-size and contrast stores' values, `null` where
|
|
24
|
+
* this device never chose. */
|
|
25
|
+
export interface DeviceAppearance {
|
|
26
|
+
textSize?: string | null;
|
|
27
|
+
contrast?: string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The body of `PATCH /auth/me` for a pick: one field, as server-kit's `ProfileUpdate`
|
|
31
|
+
* spells it. */
|
|
32
|
+
export type AccountAppearancePatch = { text_size: TextSize } | { contrast: ContrastMode };
|
|
33
|
+
|
|
34
|
+
export interface ResolvedAccountAppearance {
|
|
35
|
+
textSize: TextSize;
|
|
36
|
+
/** The stored MODE — "system" included — not whether more contrast is on now. */
|
|
37
|
+
contrast: ContrastMode;
|
|
38
|
+
sources: { textSize: AppearanceSource; contrast: AppearanceSource };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function source(device: boolean, account: boolean): AppearanceSource {
|
|
42
|
+
return device ? "device" : account ? "account" : "default";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The values on a device (§2.6): **its own choice → the account's → the default**
|
|
47
|
+
* (Normal; "system" for contrast), each taken only when it is a known value. Pure.
|
|
48
|
+
*/
|
|
49
|
+
export function resolveAccountAppearance(
|
|
50
|
+
device: DeviceAppearance,
|
|
51
|
+
account: AccountAppearanceFields | null | undefined,
|
|
52
|
+
): ResolvedAccountAppearance {
|
|
53
|
+
return {
|
|
54
|
+
textSize: resolveTextSize(device.textSize, account?.text_size),
|
|
55
|
+
contrast: resolveContrastMode(device.contrast, account?.contrast),
|
|
56
|
+
sources: {
|
|
57
|
+
textSize: source(isTextSize(device.textSize), isTextSize(account?.text_size)),
|
|
58
|
+
contrast: source(isContrastMode(device.contrast), isContrastMode(account?.contrast)),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface UseAccountAppearanceOptions {
|
|
64
|
+
/** The signed-in account (`/auth/me`), or `null` / `undefined` while signed out or not
|
|
65
|
+
* loaded yet — then nothing is saved to it. */
|
|
66
|
+
account: AccountAppearanceFields | null | undefined;
|
|
67
|
+
/** This device's own choices: `useTextSizeStore((s) => s.size)` and
|
|
68
|
+
* `useContrastStore((s) => s.contrast)`. */
|
|
69
|
+
device: DeviceAppearance;
|
|
70
|
+
/** Store a device choice: `(patch) => { if (patch.textSize) setSize(patch.textSize);
|
|
71
|
+
* if (patch.contrast) setContrast(patch.contrast); }`. */
|
|
72
|
+
setDevice: (patch: { textSize?: TextSize; contrast?: ContrastMode }) => void;
|
|
73
|
+
/** Write the account: `PATCH /auth/me` with the patch as its body. Return its promise. */
|
|
74
|
+
save: (patch: AccountAppearancePatch) => unknown;
|
|
75
|
+
/** A demo session: a pick stays on the device, the account is never written (§6). */
|
|
76
|
+
isDemo?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface AccountAppearance extends ResolvedAccountAppearance {
|
|
80
|
+
/** The user picked a size: it becomes the device's choice and — signed in, and not a
|
|
81
|
+
* demo — the account's. The promise is `save`'s; the device keeps the pick if it
|
|
82
|
+
* fails, and the app says so (`appearance.saveFailed`, 0.32.1). */
|
|
83
|
+
pickTextSize: (size: TextSize) => Promise<unknown>;
|
|
84
|
+
/** As {@link pickTextSize}, for the contrast mode. */
|
|
85
|
+
pickContrast: (mode: ContrastMode) => Promise<unknown>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The account's text size and contrast on this device (§6): the order of
|
|
90
|
+
* {@link resolveAccountAppearance}, and the writes.
|
|
91
|
+
*
|
|
92
|
+
* - **Nothing writes the account on its own.** Rendering, signing in and adopting the
|
|
93
|
+
* account's values call neither `save` nor `setDevice`, so a new device adopts the
|
|
94
|
+
* account instead of overwriting it.
|
|
95
|
+
* - **A pick** writes the device's choice, then `save` — except in a demo session or
|
|
96
|
+
* signed out.
|
|
97
|
+
*
|
|
98
|
+
* The hook only answers. The values are put in force by the stores' apply hooks, which
|
|
99
|
+
* resolve the same order from the same inputs:
|
|
100
|
+
*
|
|
101
|
+
* ```tsx
|
|
102
|
+
* useApplyTextSize({ account: me?.text_size });
|
|
103
|
+
* useApplyContrast({ account: me?.contrast });
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export function useAccountAppearance(options: UseAccountAppearanceOptions): AccountAppearance {
|
|
107
|
+
const { account, device, setDevice, save, isDemo = false } = options;
|
|
108
|
+
const deviceTextSize = device.textSize;
|
|
109
|
+
const deviceContrast = device.contrast;
|
|
110
|
+
const accountTextSize = account?.text_size;
|
|
111
|
+
const accountContrast = account?.contrast;
|
|
112
|
+
const signedIn = account !== null && account !== undefined;
|
|
113
|
+
// By value, so a fresh `/auth/me` object with the same two fields changes nothing.
|
|
114
|
+
const resolved = useMemo(
|
|
115
|
+
() =>
|
|
116
|
+
resolveAccountAppearance(
|
|
117
|
+
{ textSize: deviceTextSize, contrast: deviceContrast },
|
|
118
|
+
signedIn ? { text_size: accountTextSize, contrast: accountContrast } : undefined,
|
|
119
|
+
),
|
|
120
|
+
[deviceTextSize, deviceContrast, signedIn, accountTextSize, accountContrast],
|
|
121
|
+
);
|
|
122
|
+
const write = useCallback(
|
|
123
|
+
(patch: AccountAppearancePatch) => {
|
|
124
|
+
if (isDemo || !signedIn) return Promise.resolve();
|
|
125
|
+
try {
|
|
126
|
+
return Promise.resolve(save(patch));
|
|
127
|
+
} catch (error) {
|
|
128
|
+
return Promise.reject(error);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
[save, isDemo, signedIn],
|
|
132
|
+
);
|
|
133
|
+
const pickTextSize = useCallback(
|
|
134
|
+
(size: TextSize) => {
|
|
135
|
+
setDevice({ textSize: size });
|
|
136
|
+
return write({ text_size: size });
|
|
137
|
+
},
|
|
138
|
+
[setDevice, write],
|
|
139
|
+
);
|
|
140
|
+
const pickContrast = useCallback(
|
|
141
|
+
(mode: ContrastMode) => {
|
|
142
|
+
setDevice({ contrast: mode });
|
|
143
|
+
return write({ contrast: mode });
|
|
144
|
+
},
|
|
145
|
+
[setDevice, write],
|
|
146
|
+
);
|
|
147
|
+
return { ...resolved, pickTextSize, pickContrast };
|
|
148
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo } from "react";
|
|
2
2
|
import { useLocation, useNavigate } from "react-router";
|
|
3
|
-
import {
|
|
4
|
-
import { PHONE_QUERY } from "../components/ui";
|
|
3
|
+
import { usePhoneLayout } from "../hooks/use-breakpoint";
|
|
5
4
|
import { normalizeSettingsBase, visibleSettingsGroups } from "./settings-catalogue";
|
|
6
5
|
import type { SettingsEntry, SettingsGroup } from "./settings-catalogue";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
|
-
* Which layout the settings page is in. `"auto"` follows the screen (
|
|
8
|
+
* Which layout the settings page is in. `"auto"` follows the screen (`usePhoneLayout()`,
|
|
10
9
|
* as `Tabs` does); `"desktop"` / `"phone"` pin one — for a preview of both side by side,
|
|
11
10
|
* where the window is one width.
|
|
12
11
|
*/
|
|
@@ -187,7 +186,7 @@ export function useSettingsRoute<G extends string>(options: UseSettingsRouteOpti
|
|
|
187
186
|
const { groups, entries, basePath, defaultGroup, aliases, layout = "auto" } = options;
|
|
188
187
|
const location = useLocation();
|
|
189
188
|
const navigate = useNavigate();
|
|
190
|
-
const narrow =
|
|
189
|
+
const narrow = usePhoneLayout();
|
|
191
190
|
const phone = layout === "phone" || (layout === "auto" && narrow);
|
|
192
191
|
const base = normalizeSettingsBase(basePath);
|
|
193
192
|
|
package/src/shell/app-shell.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
|
-
import { Link, NavLink, matchPath, useLocation } from "react-router";
|
|
5
|
-
import { ChevronRight, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
4
|
+
import { Link, NavLink, matchPath, useHref, useLocation } from "react-router";
|
|
5
|
+
import { ChevronRight, MoreHorizontal, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
6
6
|
import type { LucideIcon } from "lucide-react";
|
|
7
7
|
import { cn } from "../lib/cn";
|
|
8
8
|
import { scrollIntoStrip, useStripFade } from "../lib/strip-fade";
|
|
@@ -10,9 +10,13 @@ import { dirOf, type Direction } from "../lib/direction";
|
|
|
10
10
|
import { readStored, writeStored } from "../lib/safe-storage";
|
|
11
11
|
import { Tooltip } from "../components/tooltip";
|
|
12
12
|
import { pickLinkRenderer } from "../components/text-link";
|
|
13
|
+
import { DialogFrame } from "../components/dialog-frame";
|
|
14
|
+
import { CompactControls } from "../components/ui";
|
|
13
15
|
import { useAnchoredRect } from "../hooks/use-anchored-rect";
|
|
14
16
|
import { useEscapeKey } from "../hooks/use-dismiss";
|
|
15
|
-
import {
|
|
17
|
+
import { useBreakpoint } from "../hooks/use-breakpoint";
|
|
18
|
+
import { useTextSize } from "../theme/text-size";
|
|
19
|
+
import type { TextSize } from "../theme/text-size";
|
|
16
20
|
import { DEFAULT_APP_SHELL_LABELS, useKitLabels, useKitLink } from "../i18n/kit-labels";
|
|
17
21
|
import type { KitLinkComponent, KitLinkProps } from "../i18n/kit-labels";
|
|
18
22
|
|
|
@@ -95,6 +99,46 @@ export type AppShellSidebarFooterItem =
|
|
|
95
99
|
links: { to: string; label: string; external?: boolean }[];
|
|
96
100
|
};
|
|
97
101
|
|
|
102
|
+
/**
|
|
103
|
+
* The words of the phone bar's "More" cell and its sheet (0.32,
|
|
104
|
+
* docs/text-size-harmonization.md §4 Navigation, §10.7) — the `appShellMore` namespace of
|
|
105
|
+
* `<UiKitProvider labels>`, overridable per shell through `moreLabels`.
|
|
106
|
+
*/
|
|
107
|
+
export interface AppShellMoreLabels {
|
|
108
|
+
/** The cell's visible label, under its icon — one short word. */
|
|
109
|
+
more: string;
|
|
110
|
+
/** The sheet's heading, and so its accessible name. */
|
|
111
|
+
moreTitle: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const DEFAULT_APP_SHELL_MORE_LABELS: AppShellMoreLabels = {
|
|
115
|
+
more: "More",
|
|
116
|
+
moreTitle: "More pages",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The most entries the phone bar shows before the rest go under "More", per text size
|
|
121
|
+
* (§10.7): every entry at Normal, four at Large, three at Extra large. A fifth of a
|
|
122
|
+
* 360 px phone at 150 % is 48 px of Normal-sized room, and the cells hold a word each.
|
|
123
|
+
*/
|
|
124
|
+
export const DEFAULT_MOBILE_BAR_MAX: Readonly<Record<TextSize, number>> = {
|
|
125
|
+
normal: Number.POSITIVE_INFINITY,
|
|
126
|
+
large: 4,
|
|
127
|
+
xlarge: 3,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Split the bar's entries into the cells shown and the ones under "More", in the app's
|
|
132
|
+
* order (§10.7: the app puts its four most used first, or orders by role). One hidden
|
|
133
|
+
* entry is no saving — the More cell would take its place — so the bar then shows them
|
|
134
|
+
* all: the cell count is the same either way, and a real entry beats a detour.
|
|
135
|
+
*/
|
|
136
|
+
export function splitMobileBar<T>(entries: readonly T[], max: number): { shown: T[]; hidden: T[] } {
|
|
137
|
+
const limit = Math.max(1, Math.floor(max));
|
|
138
|
+
if (entries.length <= limit + 1) return { shown: [...entries], hidden: [] };
|
|
139
|
+
return { shown: entries.slice(0, limit), hidden: entries.slice(limit) };
|
|
140
|
+
}
|
|
141
|
+
|
|
98
142
|
/**
|
|
99
143
|
* Exported and `<div>`-shaped. This is the outermost element of every consuming app, so
|
|
100
144
|
* there is nothing above it to hang an id, a landmark label or a `data-tour` anchor on —
|
|
@@ -156,6 +200,22 @@ export interface AppShellProps extends ComponentPropsWithoutRef<"div"> {
|
|
|
156
200
|
* price of pages hidden past the edge, so it is the app's call, per shell.
|
|
157
201
|
*/
|
|
158
202
|
mobileSubNavLayout?: "wrap" | "scroll";
|
|
203
|
+
/**
|
|
204
|
+
* The most entries the phone bar shows; the rest move into a "More" cell that opens a
|
|
205
|
+
* sheet listing them, each group with its pages (0.32, docs/text-size-harmonization.md
|
|
206
|
+
* §4, §10.7). The bar takes `nav` in the app's order — after `mobileHidden` — so the
|
|
207
|
+
* app puts its most used entries first (or orders by role: kastlan's manager and
|
|
208
|
+
* accountant see different firsts).
|
|
209
|
+
*
|
|
210
|
+
* Default {@link DEFAULT_MOBILE_BAR_MAX}: unlimited at Normal, 4 at Large, 3 at Extra
|
|
211
|
+
* large, because the cells' words grow with the text and the phone does not. A number
|
|
212
|
+
* replaces the default at every size; `{ large: 5 }` replaces it at one size only.
|
|
213
|
+
* Never fewer than one entry; and one entry over the limit shows in full rather than
|
|
214
|
+
* behind a More that would take the same cell.
|
|
215
|
+
*/
|
|
216
|
+
mobileBarMax?: number | Partial<Record<TextSize, number>>;
|
|
217
|
+
/** The More cell's words over the provider's `appShellMore`. */
|
|
218
|
+
moreLabels?: Partial<AppShellMoreLabels>;
|
|
159
219
|
/**
|
|
160
220
|
* Lay the shell out inside its PARENT'S box instead of the viewport: `h-full` rather
|
|
161
221
|
* than `h-dvh`, its own scroll container at every width, a bottom bar pinned to the
|
|
@@ -201,9 +261,10 @@ function useNavHeightVar(
|
|
|
201
261
|
// Re-run when the breakpoint flips. A ResizeObserver SKIPS an element that is not
|
|
202
262
|
// being rendered, so the nav going `display:none` above `md` fires no callback and
|
|
203
263
|
// would leave the last phone height published — a desktop footer would then sit 56px
|
|
204
|
-
// off the bottom after a live resize.
|
|
205
|
-
//
|
|
206
|
-
|
|
264
|
+
// off the bottom after a live resize. `useBreakpoint("md")` is the nav's own `md:hidden`
|
|
265
|
+
// at the text size in force (0.32), so the two cannot disagree about where the
|
|
266
|
+
// boundary is — at Extra large both move to 72rem.
|
|
267
|
+
const isMdUp = useBreakpoint("md", false);
|
|
207
268
|
useEffect(() => {
|
|
208
269
|
const node = ref.current;
|
|
209
270
|
const shell = shellRef.current;
|
|
@@ -272,14 +333,26 @@ export function AppShell({
|
|
|
272
333
|
subNav = "flyout",
|
|
273
334
|
mobileSubNav = true,
|
|
274
335
|
mobileSubNavLayout = "wrap",
|
|
336
|
+
mobileBarMax,
|
|
337
|
+
moreLabels,
|
|
275
338
|
toggleGroupLabel,
|
|
276
339
|
embedded: embeddedProp,
|
|
277
340
|
className,
|
|
278
341
|
...rest
|
|
279
342
|
}: AppShellProps) {
|
|
280
343
|
const embedded = useContext(AppShellNesting) || !!embeddedProp;
|
|
281
|
-
|
|
282
|
-
const
|
|
344
|
+
const { size: textSize } = useTextSize();
|
|
345
|
+
const large = textSize !== "normal";
|
|
346
|
+
// The bottom bar's cells: every entry the phone does not reach another way, up to the
|
|
347
|
+
// text size's limit; the rest go under More (§10.7).
|
|
348
|
+
const barMax =
|
|
349
|
+
typeof mobileBarMax === "number"
|
|
350
|
+
? mobileBarMax
|
|
351
|
+
: (mobileBarMax?.[textSize] ?? DEFAULT_MOBILE_BAR_MAX[textSize]);
|
|
352
|
+
const { shown: barNav, hidden: moreNav } = splitMobileBar(
|
|
353
|
+
nav.filter((item) => !item.mobileHidden),
|
|
354
|
+
barMax,
|
|
355
|
+
);
|
|
283
356
|
// An embedded shell sharing the default key would overwrite the outer app's sidebar
|
|
284
357
|
// preference with its own — so it persists only when given a key of its own.
|
|
285
358
|
const collapseStorageKey =
|
|
@@ -310,6 +383,11 @@ export function AppShell({
|
|
|
310
383
|
useEffect(() => {
|
|
311
384
|
if (collapseStorageKey !== null) writeStored(collapseStorageKey, collapsed ? "1" : "0");
|
|
312
385
|
}, [collapsed, collapseStorageKey]);
|
|
386
|
+
// §4: at Large and Extra large the sidebar never collapses to icons only — a glyph
|
|
387
|
+
// with its name in a tooltip is the very thing a reader who asked for bigger type
|
|
388
|
+
// cannot use. The persisted preference is left alone, so Normal gets it back, and the
|
|
389
|
+
// toggle goes while it could not do anything.
|
|
390
|
+
const iconOnly = collapsed && !large;
|
|
313
391
|
|
|
314
392
|
return (
|
|
315
393
|
// The caller's attributes go on the shell's own root, never on the `data-tour="nav"`
|
|
@@ -331,7 +409,10 @@ export function AppShell({
|
|
|
331
409
|
className,
|
|
332
410
|
)}
|
|
333
411
|
>
|
|
334
|
-
{
|
|
412
|
+
{/* §4: "the top bar keeps its essential icons" — its IconButtons stay icons at
|
|
413
|
+
Large (their labels the names and tooltips) and a locked one keeps its reason
|
|
414
|
+
in the tooltip: a bar 3rem tall has no room for words under every glyph. */}
|
|
415
|
+
<CompactControls>{topBar}</CompactControls>
|
|
335
416
|
<div className="flex flex-1 min-h-0">
|
|
336
417
|
<aside
|
|
337
418
|
className={cn(
|
|
@@ -340,7 +421,7 @@ export function AppShell({
|
|
|
340
421
|
// Embedded, the row it sits in is already the shell's height; the sticky
|
|
341
422
|
// offsets below are measured against the window's top bar.
|
|
342
423
|
embedded ? "md:self-stretch" : "md:sticky md:top-12 md:self-start md:h-[calc(100vh-3rem)]",
|
|
343
|
-
|
|
424
|
+
iconOnly ? "md:w-14" : "md:w-60",
|
|
344
425
|
)}
|
|
345
426
|
>
|
|
346
427
|
{/* The `<nav>` is flex-1 so it fills the sidebar's height (with empty
|
|
@@ -355,7 +436,7 @@ export function AppShell({
|
|
|
355
436
|
<SidebarNavItem
|
|
356
437
|
key={item.to}
|
|
357
438
|
item={item}
|
|
358
|
-
collapsed={
|
|
439
|
+
collapsed={iconOnly}
|
|
359
440
|
inline={subNav === "inline"}
|
|
360
441
|
toggleGroupLabel={labels.toggleGroup}
|
|
361
442
|
openGroup={openGroup}
|
|
@@ -365,33 +446,36 @@ export function AppShell({
|
|
|
365
446
|
</div>
|
|
366
447
|
</nav>
|
|
367
448
|
{!!sidebarFooterItems?.length && (
|
|
368
|
-
<SidebarFooterItems items={sidebarFooterItems} collapsed={
|
|
449
|
+
<SidebarFooterItems items={sidebarFooterItems} collapsed={iconOnly} renderLink={renderLink} />
|
|
369
450
|
)}
|
|
370
|
-
{sidebarFooter?.(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
451
|
+
{sidebarFooter?.(iconOnly)}
|
|
452
|
+
{/* No toggle at Large: the sidebar cannot collapse there (see `iconOnly`). */}
|
|
453
|
+
{!large && (
|
|
454
|
+
<div className="border-t border-[var(--border)] p-2">
|
|
455
|
+
{collapsed ? (
|
|
456
|
+
<Tooltip label={labels.expand} side="end" portal className="block">
|
|
457
|
+
<button
|
|
458
|
+
type="button"
|
|
459
|
+
onClick={() => setCollapsed(false)}
|
|
460
|
+
aria-label={labels.expand}
|
|
461
|
+
className="flex w-full items-center justify-center min-h-9 rounded-md text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]"
|
|
462
|
+
>
|
|
463
|
+
<PanelLeftOpen className="size-4 shrink-0 rtl:-scale-x-100" />
|
|
464
|
+
</button>
|
|
465
|
+
</Tooltip>
|
|
466
|
+
) : (
|
|
374
467
|
<button
|
|
375
468
|
type="button"
|
|
376
|
-
onClick={() => setCollapsed(
|
|
377
|
-
aria-label={labels.
|
|
378
|
-
className="flex w-full items-center
|
|
469
|
+
onClick={() => setCollapsed(true)}
|
|
470
|
+
aria-label={labels.collapse}
|
|
471
|
+
className="flex w-full items-center gap-3 min-h-9 px-3 py-2 rounded-md text-sm text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]"
|
|
379
472
|
>
|
|
380
|
-
<
|
|
473
|
+
<PanelLeftClose className="size-4 shrink-0 rtl:-scale-x-100" />
|
|
474
|
+
<span className="truncate">{labels.collapse}</span>
|
|
381
475
|
</button>
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
type="button"
|
|
386
|
-
onClick={() => setCollapsed(true)}
|
|
387
|
-
aria-label={labels.collapse}
|
|
388
|
-
className="flex w-full items-center gap-3 min-h-9 px-3 py-2 rounded-md text-sm text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]"
|
|
389
|
-
>
|
|
390
|
-
<PanelLeftClose className="size-4 shrink-0 rtl:-scale-x-100" />
|
|
391
|
-
<span className="truncate">{labels.collapse}</span>
|
|
392
|
-
</button>
|
|
393
|
-
)}
|
|
394
|
-
</div>
|
|
476
|
+
)}
|
|
477
|
+
</div>
|
|
478
|
+
)}
|
|
395
479
|
</aside>
|
|
396
480
|
|
|
397
481
|
{/* Content column beside the sidebar. main grows to fill so the footer
|
|
@@ -456,11 +540,12 @@ export function AppShell({
|
|
|
456
540
|
<nav
|
|
457
541
|
data-tour="nav"
|
|
458
542
|
className="grid"
|
|
459
|
-
style={{ gridTemplateColumns: `repeat(${barNav.length}, minmax(0, 1fr))` }}
|
|
543
|
+
style={{ gridTemplateColumns: `repeat(${barNav.length + (moreNav.length ? 1 : 0)}, minmax(0, 1fr))` }}
|
|
460
544
|
>
|
|
461
545
|
{barNav.map((item) => (
|
|
462
546
|
<MobileNavItem key={item.to} item={item} />
|
|
463
547
|
))}
|
|
548
|
+
{moreNav.length > 0 && <MobileMoreCell hidden={moreNav} labels={moreLabels} />}
|
|
464
549
|
</nav>
|
|
465
550
|
</div>
|
|
466
551
|
</div>
|
|
@@ -527,7 +612,7 @@ function SidebarFooterItems({
|
|
|
527
612
|
<nav
|
|
528
613
|
key={item.key}
|
|
529
614
|
aria-label={item.label}
|
|
530
|
-
className="flex flex-wrap justify-center gap-x-3 gap-y-0.5 text-
|
|
615
|
+
className="flex flex-wrap justify-center gap-x-3 gap-y-0.5 text-caption text-[var(--text-muted)]"
|
|
531
616
|
>
|
|
532
617
|
{item.links.map((l) => (
|
|
533
618
|
<span key={l.to}>
|
|
@@ -561,6 +646,37 @@ function SidebarFooterItems({
|
|
|
561
646
|
);
|
|
562
647
|
}
|
|
563
648
|
|
|
649
|
+
/**
|
|
650
|
+
* A bar cell's look, shared by the entries and the More cell. The active page needs a
|
|
651
|
+
* clear marker, not just a subtle text shade (feedback #327): brand-accent icon + label,
|
|
652
|
+
* bolder weight, and a top accent bar spanning the cell so it reads at a glance.
|
|
653
|
+
*/
|
|
654
|
+
const mobileCellClass = (active: boolean) =>
|
|
655
|
+
cn(
|
|
656
|
+
// `h-full`: the grid row stretches every cell to the tallest one's height, so a
|
|
657
|
+
// label on two lines never leaves its neighbours a different shape.
|
|
658
|
+
"relative flex h-full w-full flex-col items-center justify-start py-2 text-caption gap-0.5 transition-colors",
|
|
659
|
+
"before:absolute before:content-[''] before:inset-x-4 before:top-0 before:h-0.5 before:rounded-full before:transition-colors",
|
|
660
|
+
active
|
|
661
|
+
? "font-semibold text-[var(--brand)] before:bg-[var(--brand)]"
|
|
662
|
+
: "text-[var(--text-muted)] before:bg-transparent",
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* A bar cell's label (0.32, §10.7): up to TWO lines, never an ellipsis on one. At Large
|
|
667
|
+
* a cell is a fifth of a phone holding type a quarter bigger, and one line truncated
|
|
668
|
+
* "Transactions" to "Trans…" in every app (Kurvenschmiede). Two lines and then a clamp,
|
|
669
|
+
* centred, a long word broken (and hyphenated where the page's `lang` allows) rather
|
|
670
|
+
* than overflowing into the next cell. `shortLabel` still wins where the app has one.
|
|
671
|
+
*/
|
|
672
|
+
function MobileCellLabel({ children }: { children: ReactNode }) {
|
|
673
|
+
return (
|
|
674
|
+
<span className="line-clamp-2 max-w-full break-words px-0.5 text-center leading-tight hyphens-auto">
|
|
675
|
+
{children}
|
|
676
|
+
</span>
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
564
680
|
/** One cell of the phone's group bar. Marked while the group's own page OR any of its
|
|
565
681
|
* pages is current — NavLink alone only knows its own `to`, which left the bar with
|
|
566
682
|
* nothing marked on every sub-page (the same defect the sidebar had). */
|
|
@@ -571,31 +687,139 @@ function MobileNavItem({ item }: { item: AppShellNavItem }) {
|
|
|
571
687
|
to={item.to}
|
|
572
688
|
end={item.end ?? true}
|
|
573
689
|
data-tour={item.dataTour}
|
|
574
|
-
className={({ isActive }) =>
|
|
575
|
-
// The active page needs a clear marker, not just a subtle text
|
|
576
|
-
// shade (feedback #327): brand-accent icon + label, bolder weight,
|
|
577
|
-
// and a top accent bar spanning the cell so it reads at a glance.
|
|
578
|
-
cn(
|
|
579
|
-
"relative flex flex-col items-center justify-center py-2 text-[11px] gap-0.5 transition-colors",
|
|
580
|
-
"before:absolute before:content-[''] before:inset-x-4 before:top-0 before:h-0.5 before:rounded-full before:transition-colors",
|
|
581
|
-
isActive || subActive
|
|
582
|
-
? "font-semibold text-[var(--brand)] before:bg-[var(--brand)]"
|
|
583
|
-
: "text-[var(--text-muted)] before:bg-transparent",
|
|
584
|
-
)
|
|
585
|
-
}
|
|
690
|
+
className={({ isActive }) => mobileCellClass(isActive || subActive)}
|
|
586
691
|
>
|
|
587
692
|
<span className="relative">
|
|
588
693
|
<item.icon className="size-5" />
|
|
589
694
|
</span>
|
|
590
|
-
{
|
|
591
|
-
`shortLabel` was not supplied (or a translation is longer than its
|
|
592
|
-
author expected): an ellipsis in one cell is a far smaller problem
|
|
593
|
-
than a bar whose rows are different heights. */}
|
|
594
|
-
<span className="max-w-full truncate px-0.5">{item.shortLabel ?? item.label}</span>
|
|
695
|
+
<MobileCellLabel>{item.shortLabel ?? item.label}</MobileCellLabel>
|
|
595
696
|
</NavLink>
|
|
596
697
|
);
|
|
597
698
|
}
|
|
598
699
|
|
|
700
|
+
/** Whether the current route is `item`'s page or one of its pages. */
|
|
701
|
+
function entryIsCurrent(item: AppShellNavItem, pathname: string): boolean {
|
|
702
|
+
return (
|
|
703
|
+
!!matchPath({ path: item.to, end: item.end ?? true }, pathname) ||
|
|
704
|
+
!!item.items?.some((sub) => matchPath({ path: sub.to, end: sub.end ?? true }, pathname))
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** A hidden entry's link, resolved as the router would write it (basename included). */
|
|
709
|
+
function TourAnchor({ to, dataTour }: { to: string; dataTour?: string }) {
|
|
710
|
+
const href = useHref(to);
|
|
711
|
+
return (
|
|
712
|
+
// eslint-disable-next-line jsx-a11y/anchor-has-content -- a tour anchor, aria-hidden and inert; the More button under it is the control (see MobileMoreCell)
|
|
713
|
+
<a
|
|
714
|
+
href={href}
|
|
715
|
+
data-tour={dataTour}
|
|
716
|
+
// Not a control: no name, no tab stop, no pointer — the More button under it
|
|
717
|
+
// takes the tap. It exists only to be FOUND (see MobileMoreCell).
|
|
718
|
+
aria-hidden
|
|
719
|
+
tabIndex={-1}
|
|
720
|
+
className="pointer-events-none absolute inset-0"
|
|
721
|
+
/>
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* The bar's "More" cell (0.32, docs/text-size-harmonization.md §4, §10.7): the entries
|
|
727
|
+
* past `mobileBarMax`, in a sheet — each entry as a link, and under a group its pages,
|
|
728
|
+
* since a group the bar no longer shows has no sub-row to reach them by either. Marked
|
|
729
|
+
* active while the current page is one of them, as the cell it replaced would be.
|
|
730
|
+
*
|
|
731
|
+
* **Tour anchors.** A guided tour finds a nav entry by `[data-tour="nav"] a[href="/x"]`
|
|
732
|
+
* (keksdose's tours) or by the entry's own `dataTour`. An entry under More has no link
|
|
733
|
+
* in the bar, and the sidebar's copy is `display:none` on a phone, so such a step would
|
|
734
|
+
* spotlight nothing. So the More cell carries the hidden entries' links — one `<a>` per
|
|
735
|
+
* entry and per page of a hidden group, with the entry's `href` and `dataTour` — laid
|
|
736
|
+
* over the cell (`absolute inset-0`): both selectors resolve to a visible box, and that
|
|
737
|
+
* box is the More cell, which is where the reader has to go next. They are
|
|
738
|
+
* `aria-hidden`, out of the tab order and pointer-transparent, so neither a screen
|
|
739
|
+
* reader nor a tap ever meets them; the button under them is the one control.
|
|
740
|
+
*/
|
|
741
|
+
function MobileMoreCell({
|
|
742
|
+
hidden,
|
|
743
|
+
labels: labelsProp,
|
|
744
|
+
}: {
|
|
745
|
+
hidden: AppShellNavItem[];
|
|
746
|
+
labels?: Partial<AppShellMoreLabels>;
|
|
747
|
+
}) {
|
|
748
|
+
const labels: AppShellMoreLabels = useKitLabels("appShellMore", DEFAULT_APP_SHELL_MORE_LABELS, labelsProp);
|
|
749
|
+
const { pathname } = useLocation();
|
|
750
|
+
const [open, setOpen] = useState(false);
|
|
751
|
+
const active = hidden.some((item) => entryIsCurrent(item, pathname));
|
|
752
|
+
const close = () => setOpen(false);
|
|
753
|
+
return (
|
|
754
|
+
<div data-slot="app-shell-more" className="relative min-w-0">
|
|
755
|
+
<button
|
|
756
|
+
type="button"
|
|
757
|
+
aria-haspopup="dialog"
|
|
758
|
+
aria-expanded={open}
|
|
759
|
+
onClick={() => setOpen(true)}
|
|
760
|
+
className={mobileCellClass(active)}
|
|
761
|
+
>
|
|
762
|
+
<span className="relative">
|
|
763
|
+
<MoreHorizontal aria-hidden className="size-5" />
|
|
764
|
+
</span>
|
|
765
|
+
<MobileCellLabel>{labels.more}</MobileCellLabel>
|
|
766
|
+
</button>
|
|
767
|
+
{hidden.flatMap((item) => [
|
|
768
|
+
<TourAnchor key={item.to} to={item.to} dataTour={item.dataTour} />,
|
|
769
|
+
...(item.items ?? []).map((sub) => <TourAnchor key={`${item.to} ${sub.to}`} to={sub.to} />),
|
|
770
|
+
])}
|
|
771
|
+
<DialogFrame open={open} onClose={close} title={labels.moreTitle} closeButton bodyClassName="space-y-1">
|
|
772
|
+
<ul className="space-y-1">
|
|
773
|
+
{hidden.map((item) => (
|
|
774
|
+
<li key={item.to}>
|
|
775
|
+
<NavLink
|
|
776
|
+
to={item.to}
|
|
777
|
+
end={item.end ?? true}
|
|
778
|
+
onClick={close}
|
|
779
|
+
className={({ isActive }) =>
|
|
780
|
+
cn(
|
|
781
|
+
"flex min-h-11 items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors",
|
|
782
|
+
isActive
|
|
783
|
+
? "bg-[var(--bg-inverse)] text-[var(--text-inverse)]"
|
|
784
|
+
: "text-[var(--text-primary)] hover:bg-[var(--bg-hover)]",
|
|
785
|
+
)
|
|
786
|
+
}
|
|
787
|
+
>
|
|
788
|
+
<item.icon aria-hidden className="size-5 shrink-0" />
|
|
789
|
+
<span className="min-w-0 break-words leading-snug">{item.label}</span>
|
|
790
|
+
</NavLink>
|
|
791
|
+
{!!item.items?.length && (
|
|
792
|
+
<ul aria-label={item.label} className="ms-[1.3rem] mt-0.5 space-y-0.5 border-s border-[var(--border)] ps-2">
|
|
793
|
+
{item.items.map((sub) => (
|
|
794
|
+
<li key={sub.to}>
|
|
795
|
+
<NavLink
|
|
796
|
+
to={sub.to}
|
|
797
|
+
end={sub.end ?? true}
|
|
798
|
+
onClick={close}
|
|
799
|
+
className={({ isActive }) =>
|
|
800
|
+
cn(
|
|
801
|
+
"flex min-h-11 items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm transition-colors",
|
|
802
|
+
isActive
|
|
803
|
+
? "bg-[var(--bg-inverse)] text-[var(--text-inverse)]"
|
|
804
|
+
: "text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]",
|
|
805
|
+
)
|
|
806
|
+
}
|
|
807
|
+
>
|
|
808
|
+
<sub.icon aria-hidden className="size-4 shrink-0 opacity-70" />
|
|
809
|
+
<span className="min-w-0 break-words leading-snug">{sub.label}</span>
|
|
810
|
+
</NavLink>
|
|
811
|
+
</li>
|
|
812
|
+
))}
|
|
813
|
+
</ul>
|
|
814
|
+
)}
|
|
815
|
+
</li>
|
|
816
|
+
))}
|
|
817
|
+
</ul>
|
|
818
|
+
</DialogFrame>
|
|
819
|
+
</div>
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
|
|
599
823
|
/**
|
|
600
824
|
* The upper row of the phone navigation: the pages of the group the reader is in, as
|
|
601
825
|
* a sideways-scrolling row of pills. Rendered only while such a group is current —
|
|
@@ -741,7 +965,7 @@ function SidebarNavItem({
|
|
|
741
965
|
</span>
|
|
742
966
|
{/* Wraps rather than truncates: in the sidebar a label cut to "Checkbox, switch
|
|
743
967
|
& sl…" hides the one word that tells two pages apart, and there is height
|
|
744
|
-
to spare. (The phone bar
|
|
968
|
+
to spare. (The phone bar clamps at two lines instead, since 0.32 §10.7.) */}
|
|
745
969
|
{!collapsed && <span className="min-w-0 flex-1 break-words leading-snug">{item.label}</span>}
|
|
746
970
|
{!collapsed && hasSub && <ChevronRight className="size-3.5 shrink-0 opacity-60 rtl:-scale-x-100" />}
|
|
747
971
|
</NavLink>
|
|
@@ -995,7 +1219,7 @@ function SidebarFlyout({ item, children }: { item: AppShellNavItem; children: Re
|
|
|
995
1219
|
aria-label={item.label}
|
|
996
1220
|
className="animate-flyout min-w-52 overflow-hidden rounded-md border border-[var(--border)] bg-[var(--bg-surface)] shadow-lg"
|
|
997
1221
|
>
|
|
998
|
-
<div className="border-b border-[var(--border)] px-3 py-2 text-
|
|
1222
|
+
<div className="border-b border-[var(--border)] px-3 py-2 text-caption font-semibold uppercase tracking-wide text-[var(--text-muted)]">
|
|
999
1223
|
{item.label}
|
|
1000
1224
|
</div>
|
|
1001
1225
|
<ul className="py-1">
|
|
@@ -67,7 +67,7 @@ const SAFE_AREA: CSSProperties = {
|
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
const FOOTER_CLASS =
|
|
70
|
-
"flex flex-wrap justify-center gap-x-4 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a:hover]:underline [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-
|
|
70
|
+
"flex flex-wrap justify-center gap-x-4 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a:hover]:underline [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-[length:var(--focus-ring-width)] [&_a:focus-visible]:ring-[var(--brand)]";
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* The public page frame outside the app shell: sign-in, registration, email
|
|
@@ -46,7 +46,7 @@ export function OptionSwitcherMenu<T extends string>({
|
|
|
46
46
|
{(close) => (
|
|
47
47
|
<ul className="py-1">
|
|
48
48
|
{heading && (
|
|
49
|
-
<li className="px-3 py-1 text-
|
|
49
|
+
<li className="px-3 py-1 text-micro font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
|
|
50
50
|
{heading}
|
|
51
51
|
</li>
|
|
52
52
|
)}
|