@eifi1/ui-kit 0.51.6 → 0.52.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 +146 -26
- 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 +3 -7
- 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.d.ts +3 -2
- package/dist/auth/complete-name-dialog.js +3 -5
- 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-request.d.ts +11 -2
- package/dist/billing/cancel-request.js +15 -3
- package/dist/billing/cancel-request.js.map +1 -1
- package/dist/billing/cancel-subscription.d.ts +22 -10
- package/dist/billing/cancel-subscription.js +8 -11
- package/dist/billing/cancel-subscription.js.map +1 -1
- package/dist/billing/dialog-close.d.ts +29 -0
- package/dist/billing/dialog-close.js +8 -0
- package/dist/billing/dialog-close.js.map +1 -0
- package/dist/billing/plan-card.d.ts +14 -4
- package/dist/billing/plan-card.js +15 -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 +21 -9
- package/dist/billing/plan-switch.js +10 -8
- package/dist/billing/plan-switch.js.map +1 -1
- package/dist/chart.d.ts +12 -0
- package/dist/chart.js +16 -1
- 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 +22 -4
- package/dist/components/amount-input.js +3 -3
- 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/calculator.d.ts +21 -2
- package/dist/components/calculator.js +53 -14
- package/dist/components/calculator.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 +119 -7
- package/dist/components/combobox-core.js +151 -46
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +1 -1
- package/dist/components/combobox.js +142 -131
- 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 +36 -7
- package/dist/components/data-table-mobile.js +63 -31
- 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 +25 -8
- package/dist/components/data-table.js +154 -25
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-field.d.ts +24 -2
- package/dist/components/date-field.js +33 -8
- package/dist/components/date-field.js.map +1 -1
- package/dist/components/date-picker.d.ts +15 -2
- 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 +47 -20
- package/dist/components/date-range-picker.js +23 -7
- package/dist/components/date-range-picker.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +2 -1
- package/dist/components/dialog-frame.js.map +1 -1
- package/dist/components/entity-combobox.js +4 -2
- package/dist/components/entity-combobox.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/field-sync-labels.d.ts +1 -0
- package/dist/components/field-sync-labels.js.map +1 -1
- package/dist/components/field-sync.d.ts +23 -1
- package/dist/components/field-sync.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 +150 -141
- 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 +4 -3
- 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.d.ts +24 -0
- package/dist/components/modal.js +12 -4
- package/dist/components/modal.js.map +1 -1
- package/dist/components/money-draft.js +7 -5
- package/dist/components/money-draft.js.map +1 -1
- package/dist/components/money-field.d.ts +2 -2
- package/dist/components/money-field.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-entity-combobox.js +2 -1
- package/dist/components/multi-entity-combobox.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/number-field.d.ts +6 -4
- package/dist/components/number-field.js +3 -2
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +4 -1
- package/dist/components/number-input.js +3 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.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/reauth-dialog.d.ts +4 -5
- package/dist/components/reauth-dialog.js +1 -4
- package/dist/components/reauth-dialog.js.map +1 -1
- package/dist/components/row-actions.js +2 -1
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/section-nav.d.ts +7 -0
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/series-chart-axes.d.ts +10 -3
- package/dist/components/series-chart-axes.js +1 -0
- 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 +31 -7
- package/dist/components/series-chart.js +72 -29
- 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/sign-chip-labels.d.ts +1 -0
- package/dist/components/sign-chip-labels.js.map +1 -1
- package/dist/components/sign-chip.d.ts +10 -1
- package/dist/components/sign-chip.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 +13 -11
- 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 +26 -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 +140 -17
- package/dist/components/treemap.js +324 -97
- 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 +6 -2
- package/dist/data-table.js +7 -2
- package/dist/data-table.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 +30 -0
- package/dist/feedback/feedback-mutation.js +16 -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 +12 -3
- package/dist/feedback/feedback-public-list.js +2 -1
- 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 +5 -0
- package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
- package/dist/feedback/feedback-row-detail.d.ts +64 -28
- package/dist/feedback/feedback-row-detail.js +30 -28
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback/feedback-status-undo.d.ts +30 -11
- 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 +24 -23
- package/dist/feedback/feedback-thread.js +2 -1
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/feedback.d.ts +34 -0
- package/dist/feedback.js +42 -0
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-dismiss.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.d.ts +7 -1
- package/dist/hooks/use-overlay-history.js +35 -5
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/i18n/defaults.js +4 -5
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +7 -1
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +9 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +20 -41
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +7 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +20 -10
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +9 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +7 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +8 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +5 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +64 -17
- package/dist/index.js +77 -12
- 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/calc.d.ts +7 -1
- package/dist/lib/calc.js +3 -1
- package/dist/lib/calc.js.map +1 -1
- package/dist/lib/clipboard-minus.d.ts +2 -2
- package/dist/lib/clipboard-minus.js +1 -1
- package/dist/lib/clipboard-minus.js.map +1 -1
- 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/decimal-marks.d.ts +7 -1
- package/dist/lib/decimal-marks.js +5 -2
- package/dist/lib/decimal-marks.js.map +1 -1
- 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.d.ts +5 -3
- package/dist/lib/format.js +16 -13
- 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/isin.d.ts +10 -0
- package/dist/lib/isin.js.map +1 -1
- 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 +38 -0
- package/dist/lib/text-match.js +24 -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/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 +7 -0
- package/dist/search.js +10 -0
- 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 +21 -5
- package/dist/shell/account-menu-language.js +3 -1
- package/dist/shell/account-menu-language.js.map +1 -1
- package/dist/shell/account-menu-text-size.d.ts +26 -9
- package/dist/shell/account-menu-text-size.js +3 -2
- package/dist/shell/account-menu-text-size.js.map +1 -1
- package/dist/shell/dev-menu.js +5 -18
- package/dist/shell/dev-menu.js.map +1 -1
- package/dist/shell/role-switcher.d.ts +7 -0
- package/dist/shell/role-switcher.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 +0 -2
- 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 +27 -8
- package/dist/shell.js +28 -7
- 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 +6 -2
- package/dist/sync.js +6 -1
- 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/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 +16 -4
- package/dist/tour/tour.js +62 -778
- 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/tour/use-tour-url-sync.js +2 -2
- package/dist/tour/use-tour-url-sync.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 +12 -8
- 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 +5 -10
- 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 +9 -8
- 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-request.ts +28 -6
- package/src/billing/cancel-subscription.tsx +29 -22
- package/src/billing/dialog-close.ts +34 -0
- package/src/billing/plan-card.tsx +36 -11
- package/src/billing/plan-change-labels.ts +66 -0
- package/src/billing/plan-switch.tsx +31 -20
- package/src/chart.ts +21 -3
- package/src/components/account-chips.tsx +20 -23
- package/src/components/alert-banner.tsx +82 -5
- package/src/components/amount-input.tsx +32 -14
- 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/calculator.tsx +98 -17
- 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 +303 -69
- package/src/components/combobox.tsx +184 -155
- 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 +110 -27
- 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 +267 -41
- package/src/components/date-field.tsx +65 -8
- package/src/components/date-picker.tsx +125 -48
- package/src/components/date-range-picker.tsx +82 -31
- package/src/components/dialog-frame.tsx +2 -1
- package/src/components/entity-combobox.tsx +4 -1
- 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/field-sync-labels.ts +1 -0
- package/src/components/field-sync.tsx +23 -1
- 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 +198 -152
- 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 +14 -5
- package/src/components/measured-grid.tsx +8 -4
- package/src/components/mini-calendar.tsx +6 -5
- package/src/components/modal.tsx +45 -4
- package/src/components/money-draft.ts +16 -5
- package/src/components/money-field.tsx +2 -2
- package/src/components/month-grid.tsx +229 -0
- package/src/components/month-picker.tsx +192 -200
- package/src/components/multi-entity-combobox.tsx +1 -0
- package/src/components/multi-select.tsx +21 -9
- package/src/components/number-field.tsx +14 -7
- package/src/components/number-input.tsx +9 -3
- package/src/components/numpad-sheet.tsx +4 -3
- 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/reauth-dialog.tsx +6 -10
- package/src/components/row-actions.tsx +2 -1
- package/src/components/section-nav.tsx +7 -0
- package/src/components/series-chart-axes.ts +11 -3
- package/src/components/series-chart-labels.ts +7 -4
- package/src/components/series-chart.tsx +178 -69
- package/src/components/share-card.tsx +6 -6
- package/src/components/sign-chip-labels.ts +1 -0
- package/src/components/sign-chip.tsx +10 -1
- package/src/components/tabs-labels.ts +13 -0
- package/src/components/tabs.tsx +35 -19
- 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 +39 -0
- package/src/components/treemap-layout.ts +210 -0
- package/src/components/treemap.tsx +722 -133
- package/src/components/use-table-state.ts +12 -9
- package/src/data-table.ts +9 -2
- package/src/eslint/role-utilities.ts +97 -19
- package/src/feedback/feedback-dialog.tsx +30 -19
- package/src/feedback/feedback-mutation.ts +41 -0
- package/src/feedback/feedback-page-link.ts +90 -0
- package/src/feedback/feedback-public-list.tsx +17 -5
- package/src/feedback/feedback-replies.tsx +13 -6
- package/src/feedback/feedback-row-detail-labels.ts +5 -0
- package/src/feedback/feedback-row-detail.tsx +103 -67
- package/src/feedback/feedback-status-undo.ts +41 -27
- 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 +27 -25
- package/src/feedback.ts +42 -0
- package/src/hooks/use-dismiss.ts +3 -2
- 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 +70 -5
- package/src/i18n/defaults.ts +4 -5
- package/src/i18n/german.ts +8 -2
- package/src/i18n/kit-labels.tsx +9 -2
- package/src/i18n/locales/es.ts +8 -2
- package/src/i18n/locales/fr.ts +21 -13
- package/src/i18n/locales/hu.ts +10 -2
- package/src/i18n/locales/it.ts +8 -2
- package/src/i18n/locales/zh.ts +9 -2
- package/src/i18n/review.ts +5 -0
- package/src/index.ts +103 -13
- package/src/landing/routing.tsx +14 -10
- package/src/lib/broadcast.ts +52 -0
- package/src/lib/calc.ts +13 -2
- package/src/lib/clipboard-minus.ts +14 -4
- package/src/lib/date-format.ts +89 -13
- package/src/lib/day-format.ts +96 -0
- package/src/lib/decimal-marks.ts +12 -3
- 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 +34 -19
- package/src/lib/http-status.ts +38 -0
- package/src/lib/intl-cache.ts +25 -0
- package/src/lib/isin.ts +10 -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 +69 -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/use-command-key.ts +13 -12
- package/src/search.ts +9 -0
- package/src/settings/use-settings-focus.ts +3 -0
- package/src/shell/account-menu-language.tsx +27 -5
- package/src/shell/account-menu-text-size.tsx +32 -10
- package/src/shell/dev-menu.tsx +7 -21
- package/src/shell/role-switcher.tsx +7 -0
- package/src/shell/top-bar-actions.tsx +1 -1
- package/src/shell/top-bar-parts-labels.ts +0 -3
- package/src/shell.ts +40 -9
- package/src/sync/engine.ts +339 -61
- package/src/sync.ts +8 -1
- package/src/testing/css-variables.ts +8 -9
- 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 +116 -1193
- package/src/tour/use-tour-progress.ts +1 -1
- package/src/tour/use-tour-url-sync.ts +7 -3
- 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 +2 -1
- package/dist/components/ui-labels.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/src/components/ui-labels.ts +0 -24
- package/src/shell/dev-switchers.tsx +0 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/chart.tsx"],"sourcesContent":["// shadcn/ui chart kit — vendored (MIT) and adapted for this app:\n// - Recharts 3 instead of 2\n// - the original keys its styling off shadcn theme tokens (--muted-foreground,\n// --border, bg-background …). This app has no shadcn token layer, so those\n// are mapped onto this kit's own tokens (--text-muted, --border, --bg-surface\n// …), which follow the active palette preset and flip with the theme by\n// themselves — hence no `dark:` variants anywhere below.\n// The value on show is the *structure*: config-driven colors injected as CSS\n// vars, muted axis/grid styling, and a polished tooltip/legend — the things\n// that make shadcn charts read as \"designed\" rather than \"default Recharts\".\nimport { createContext, useContext, useEffect, useId, useLayoutEffect, useRef, useState } from \"react\";\nimport type { ComponentProps, CSSProperties, ReactNode, RefObject } from \"react\";\nimport { Legend as RechartsLegend, ResponsiveContainer, Tooltip as RechartsTooltip } from \"recharts\";\nimport { cn } from \"../lib/cn\";\nimport { isRtl } from \"../lib/direction\";\nimport { TYPOGRAPHIC_MINUS } from \"../lib/minus\";\n\nexport interface ChartSeriesConfig {\n label: ReactNode;\n color: string;\n}\n\nexport type ChartConfig = Record<string, ChartSeriesConfig>;\n\nconst ChartContext = createContext<ChartConfig | null>(null);\n\nexport function useChart(): ChartConfig {\n const ctx = useContext(ChartContext);\n if (!ctx) throw new Error(\"useChart must be used within a <ChartContainer />\");\n return ctx;\n}\n\ninterface ChartContainerProps extends ComponentProps<\"div\"> {\n config: ChartConfig;\n children: ComponentProps<typeof ResponsiveContainer>[\"children\"];\n}\n\n/**\n * The shell every chart in the kit is drawn inside.\n *\n * **The plot is not mirrored in RTL.** An x axis is a number line or a timeline, and\n * both run left-to-right in Hebrew and Arabic charts as much as in English ones; a\n * time axis flipped to run right-to-left is the surprise, not the courtesy. So no\n * `reversed` on any axis, and the SVG is pinned to `direction: ltr` — without that it\n * inherits the page's `rtl`, which flips what `text-anchor: end` means, and the tick\n * labels of a left-hand y axis are drawn INTO the plot instead of beside it. Text\n * inside the SVG still shapes by its own script; only the anchoring is physical.\n * The HTML around the plot — legend, tooltip, the zoom reset button — follows the\n * page's direction like any other markup. A consumer who does want a mirrored\n * category axis passes `reversed` on its own `XAxis`.\n */\nexport function ChartContainer({ id, className, children, config, ...props }: ChartContainerProps) {\n const uid = useId();\n // The id is pasted into an UNQUOTED `[data-chart=…]` selector below, so it has to be\n // a CSS identifier: one stray space or `#` and the browser discards the whole rule,\n // taking every series colour with it — silently, because an invalid rule is not an\n // error anywhere. `useId()` gives `:r0:`, and a consumer-supplied id gives whatever\n // they typed, so both are filtered rather than just the colons.\n const chartId = `chart-${(id ?? uid).replace(/[^A-Za-z0-9_-]/g, \"\")}`;\n return (\n <ChartContext.Provider value={config}>\n <div\n data-chart={chartId}\n className={cn(\n // touch-pan-y lets a vertical finger drag still scroll the page while\n // a horizontal drag is handed to recharts for tooltip scrubbing,\n // instead of the page scrolling underneath the gesture (feedback #292).\n \"flex w-full touch-pan-y justify-center text-xs\",\n // physical anchoring for the unmirrored plot (see the doc comment above)\n \"[&_.recharts-surface]:[direction:ltr]\",\n // muted axis labels + gridlines; one class each, since the tokens\n // already carry the light/dark values\n \"[&_.recharts-cartesian-axis-tick_text]:fill-muted\",\n \"[&_.recharts-cartesian-grid_line]:stroke-subtle/70\",\n // hairline hover cursor instead of the heavy default\n \"[&_.recharts-curve.recharts-tooltip-cursor]:stroke-strong\",\n \"[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-[var(--bg-hover)]\",\n // kill the focus outlines & white sector strokes Recharts adds\n \"[&_.recharts-sector]:outline-none [&_.recharts-sector[stroke='#fff']]:stroke-transparent\",\n \"[&_.recharts-layer]:outline-none [&_.recharts-surface]:outline-none [&_.recharts-dot[stroke='#fff']]:stroke-transparent\",\n className,\n )}\n {...props}\n >\n <ChartStyle id={chartId} config={config} />\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {children}\n </ResponsiveContainer>\n </div>\n </ChartContext.Provider>\n );\n}\n\n/**\n * A config key, which becomes the tail of a custom-property NAME.\n *\n * `dangerouslySetInnerHTML` means what it says: nothing below is escaped by React, and\n * a `ChartConfig` is not necessarily written by hand — kastlan builds one from category\n * rows in its database, so these keys are user data. A key containing `}` closes this\n * rule, and whatever follows it in that key becomes a NEW rule — one that applies to\n * the whole document rather than to this one chart.\n */\nconst CSS_IDENT = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\n/**\n * A config colour, which becomes a declaration VALUE.\n *\n * Every form a palette actually produces: both hex spellings, the functional notations\n * — with the argument list restricted to the characters those functions can legally\n * contain, so no `;`, no `}` and no nested `url(` — and `var(--token)`, which is what\n * this kit's own `paletteFor`/`CHART_COLORS` return. `var()`'s optional FALLBACK is\n * deliberately not accepted: its second argument is an arbitrary value, i.e. the same\n * hole in a shape that looks safe.\n */\nconst CSS_COLOR = /^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6}|(?:rgba?|hsla?)\\([0-9A-Za-z.,%/ -]*\\)|var\\(--[A-Za-z0-9_-]+\\))$/;\n\n/**\n * The colour an entry is painted with, or `undefined` when the style block refuses it.\n *\n * The one gate both sides go through: {@link ChartStyle} emits exactly these, and the\n * legend and tooltip swatches show exactly these. They used to read `config[key].color`\n * directly, so an entry the style block had skipped still showed its configured colour\n * in the key — a swatch promising a colour the series was never painted in.\n */\nfunction paintedColor(key: string, entry: ChartSeriesConfig | undefined): string | undefined {\n if (!entry?.color) return undefined;\n return CSS_IDENT.test(key) && CSS_COLOR.test(entry.color) ? entry.color : undefined;\n}\n\n// Inject each series colour as a `--color-<key>` custom property scoped to this\n// chart, so chart JSX can refer to `var(--color-total)` and the legend/tooltip\n// stay in sync from one source of truth.\nfunction ChartStyle({ id, config }: { id: string; config: ChartConfig }) {\n const entries = Object.entries(config).filter(([key, c]) => {\n if (!c.color) return false;\n // Skipping the entry costs that one series its `var(--color-…)` and leaves the rest\n // of the chart painted; emitting it unchecked costs the page. The warning is so the\n // consumer finds out from their console rather than from a colourless bar.\n if (!CSS_IDENT.test(key)) {\n console.warn(`[ui-kit] chart config key is not a CSS identifier, skipped: ${key}`);\n return false;\n }\n if (!CSS_COLOR.test(c.color)) {\n console.warn(`[ui-kit] chart config colour for \"${key}\" is not a colour, skipped: ${c.color}`);\n return false;\n }\n return true;\n });\n if (!entries.length) return null;\n const css = `[data-chart=${id}] {\\n${entries.map(([key, c]) => ` --color-${key}: ${c.color};`).join(\"\\n\")}\\n}`;\n return <style dangerouslySetInnerHTML={{ __html: css }} />;\n}\n\n/**\n * recharts' `Tooltip`, with `filterNull` defaulting to **false**.\n *\n * recharts drops every payload entry whose value is `null`/`undefined` before the\n * content sees it, so the missing-value path {@link ChartTooltipContent} documents —\n * the em dash, `formatValue(undefined)` — never ran under the default: a series with a\n * gap at the cursor simply vanished from the tooltip, which reads as \"this series does\n * not exist here\" rather than \"it has no value here\". Kept, the entry prints \"—\".\n *\n * The other half of that filter — dropping series drawn with `hide` — is redone by\n * `ChartTooltipContent` itself (honouring `includeHidden`), so a legend toggle still\n * takes a series out of the tooltip. Pass `filterNull` to get recharts' behaviour back.\n */\nexport function ChartTooltip(props: ComponentProps<typeof RechartsTooltip>) {\n return <RechartsTooltip filterNull={false} {...props} />;\n}\nexport const ChartLegend = RechartsLegend;\n\ninterface TooltipPayloadItem {\n dataKey?: string | number;\n name?: string | number;\n value?: number | string;\n color?: string;\n /** A series drawn with `hide` — dropped here, see {@link ChartTooltip}. */\n hide?: boolean;\n payload?: Record<string, unknown>;\n}\n\n/**\n * What a series has no value for at this point: a gap in a history, a projection that\n * has not happened yet, a field that did not exist at an earlier capture.\n *\n * An em dash rather than an empty cell, so the row still lines up with its siblings and\n * the reader can see that the series WAS asked and had nothing, rather than wondering\n * whether it was dropped. It is a mark, not a word, so there is nothing here to\n * translate and no label prop to resolve; a caller who wants words passes\n * `formatValue`, which is handed the `undefined` and decides.\n */\nconst NO_VALUE = \"—\";\n\n/**\n * The number this payload item carries, or `undefined` when it carries none.\n *\n * This was `item.value ?? 0`, so every one of the absences above printed a confident\n * \"0\" that appears nowhere in the data — a fabricated figure in a tooltip, which is the\n * one place a reader goes to check an exact number. keksdose found it and wrapped the\n * component rather than trust it. `\"\"` and a non-numeric string are the same absence\n * spelled differently (recharts hands a string value straight through from the data),\n * and are answered the same way.\n */\nfunction tooltipValue(value: number | string | undefined): number | undefined {\n if (value == null || value === \"\") return undefined;\n const n = typeof value === \"string\" ? Number(value) : value;\n return Number.isFinite(n) ? n : undefined;\n}\n\ninterface ChartTooltipContentProps {\n active?: boolean;\n payload?: TooltipPayloadItem[];\n label?: string | number;\n indicator?: \"dot\" | \"line\";\n hideLabel?: boolean;\n labelFormatter?: (label: string | number) => ReactNode;\n /** Formats one series' value for display. NOT called when the series has no value at\n * this point — {@link ChartTooltipContentProps.formatValue} is the one that is. */\n valueFormatter?: (value: number) => ReactNode;\n /** Formats one series' value INCLUDING its absence: handed `undefined` for a gap, a\n * projection that has not happened yet, or a field that did not exist at an earlier\n * capture, and whatever it returns is rendered as-is. Only the caller knows whether\n * that absence reads as a dash, a \"not yet\" or a genuine zero, so this is how it\n * says. Supplying it wins over `valueFormatter` for every value.\n *\n * It sits NEXT to `valueFormatter` rather than widening it because that prop's\n * parameter is contextually typed at every call site there is — `(v) => money(v)` —\n * so widening it to `number | undefined` is a compile error in all three consuming\n * apps, which is a breaking change this module does not get to make by itself. */\n formatValue?: (value: number | undefined) => ReactNode;\n /** Cursor position within the chart, injected by recharts. Used with\n * {@link boundaryRef} to decide whether to flip the tooltip. */\n coordinate?: { x?: number; y?: number };\n /** When set, the tooltip flips to the LEFT of the cursor if it would otherwise\n * spill past the right edge of this (usually horizontally-scrolling) container\n * — e.g. a wide chart whose rightmost bars sat off-screen (feedback #77). */\n boundaryRef?: RefObject<HTMLElement | null>;\n /** Injected by recharts: keep series drawn with `hide` in the tooltip. */\n includeHidden?: boolean;\n /**\n * `\"floating\"` (the default): the box recharts moves with the cursor. `\"readout\"`: the\n * same heading and values as a flat row for a FIXED place outside the plot — a\n * `SeriesChart` whose `tooltip.placement` is `\"above\"`/`\"below\"` portals it there, so\n * a finger on a phone no longer sits on the box it is trying to read (keksdose #359).\n * No border, shadow, width cap or viewport nudge; the values flow in a grid whose\n * column count depends on the row's width alone, so the row is the same height\n * whatever is being pointed at — and, idle, it shows {@link idleKeys} with \"—\".\n */\n variant?: \"floating\" | \"readout\";\n /** Readout only: the series to list while nothing is active, so the row keeps the\n * height it will have once something is. */\n idleKeys?: readonly string[];\n /** Readout only: the heading while nothing is active. Default \"—\". */\n idleLabel?: ReactNode;\n}\n\nexport function ChartTooltipContent({\n active,\n payload,\n label,\n indicator = \"dot\",\n hideLabel = false,\n labelFormatter,\n valueFormatter,\n formatValue,\n coordinate,\n boundaryRef,\n includeHidden = false,\n variant = \"floating\",\n idleKeys,\n idleLabel,\n}: ChartTooltipContentProps) {\n const config = useChart();\n const tipRef = useRef<HTMLDivElement>(null);\n const readout = variant === \"readout\";\n // `ChartTooltip` keeps null values (so they can print \"—\"), which also keeps the\n // series a legend toggle switched off with `hide`; those go here instead.\n const items = includeHidden ? payload : payload?.filter((item) => item.hide !== true);\n const shown = Boolean(active && items?.length);\n // Everything below that measures and moves the box is about a FLOATING one.\n const visible = shown && !readout;\n // Recharts anchors the tooltip at coordinate.x inside the full (scrolled) chart\n // width; subtract the container's scrollLeft to get its on-screen x, then flip\n // left if the tooltip's own width would run past the visible right edge.\n //\n // Both the cursor and the flip are PHYSICAL (the plot is never mirrored, see\n // `ChartContainer`), but an RTL scroller counts `scrollLeft` from its right edge —\n // 0 at the start, negative going left — so there the content's left edge sits\n // `scrollWidth - clientWidth + scrollLeft` px to the left of the visible one.\n //\n // Measured in a layout effect (the DOM is not readable during render), so it commits\n // before paint. `payload` and `label` are deps because they change the tooltip's own\n // width, and recharts hands a fresh `payload` on every tooltip render, so a scroll\n // followed by any re-render is re-measured just as it was when this ran in render.\n const [flip, setFlip] = useState(false);\n const x = coordinate?.x;\n useLayoutEffect(() => {\n const boundary = boundaryRef?.current;\n if (!visible || !boundary || x == null) {\n setFlip(false);\n return;\n }\n const offset = isRtl(boundary)\n ? boundary.scrollWidth - boundary.clientWidth + boundary.scrollLeft\n : boundary.scrollLeft;\n const visibleX = x - offset;\n const tipWidth = tipRef.current?.offsetWidth ?? 160;\n setFlip(visibleX + tipWidth + 12 > boundary.clientWidth);\n }, [visible, x, boundaryRef, payload, label]);\n\n // Whatever placed it — recharts' own rule or the boundary flip above — the box then\n // clamps itself into the viewport, 8px off each edge. The width cap alone did not\n // keep it on screen: on a 390px phone recharts opened a near-full-width tooltip to\n // the RIGHT of a pointer 35% into the plot, and the values ran off the edge\n // (lenkbank, on 0.15.2). Measured after placement, minus the shift it already has,\n // so a re-measure never compounds; an unmeasured box (no layout) is left alone.\n const [nudge, setNudge] = useState(0);\n const nudgeRef = useRef(0);\n // recharts slides its wrapper to each new position with a CSS transition, so the box\n // measured on commit is still where it WAS; measure again once the slide ends.\n const [settled, setSettled] = useState(0);\n useEffect(() => {\n const wrapper = tipRef.current?.closest<HTMLElement>(\".recharts-tooltip-wrapper\");\n if (!visible || !wrapper) return;\n const onEnd = () => setSettled((n) => n + 1);\n wrapper.addEventListener(\"transitionend\", onEnd);\n return () => wrapper.removeEventListener(\"transitionend\", onEnd);\n }, [visible]);\n useLayoutEffect(() => {\n const el = tipRef.current;\n if (!visible || !el) {\n nudgeRef.current = 0;\n setNudge(0);\n return;\n }\n const rect = el.getBoundingClientRect();\n if (rect.width === 0) return;\n const margin = 8;\n const viewport = document.documentElement.clientWidth || window.innerWidth;\n const left = rect.left - nudgeRef.current;\n const right = left + rect.width;\n let next = 0;\n if (right > viewport - margin) next = viewport - margin - right;\n if (left + next < margin) next = margin - left;\n if (next !== nudgeRef.current) {\n nudgeRef.current = next;\n setNudge(next);\n }\n }, [visible, x, payload, label, flip, settled]);\n // One series' line — the floating box stacks them; the readout lays them in a grid,\n // where a long name is cut rather than wrapped so every cell stays one line high.\n const tooltipRow = (item: TooltipPayloadItem, i: number, inRow: boolean) => {\n // Bars/lines/areas identify a series by dataKey; pies/treemaps key off\n // `name` (the nameKey value). Prefer whichever the config knows.\n const dk = item.dataKey != null ? String(item.dataKey) : undefined;\n const nm = item.name != null ? String(item.name) : undefined;\n const cfgKey = dk && config[dk] ? dk : nm && config[nm] ? nm : (dk ?? nm ?? String(i));\n const series = config[cfgKey];\n const fill = typeof item.payload?.fill === \"string\" ? item.payload.fill : undefined;\n // The data point's own `fill` before recharts' `item.color`: a treemap tile,\n // or a bar with a per-cell colour, IS the colour the reader is pointing at,\n // while `item.color` is the series-wide one — every tile's swatch came out\n // `--chart-1` whatever colour the tile was.\n const color = paintedColor(cfgKey, series) ?? fill ?? item.color ?? \"var(--text-muted)\";\n const name = series?.label ?? nm ?? cfgKey;\n const raw = tooltipValue(item.value);\n let text: ReactNode;\n // An idle readout cell has asked nothing yet, so there is no absence for\n // `formatValue` to word — it is the bare mark.\n if (inRow && !shown) text = NO_VALUE;\n else if (formatValue) text = formatValue(raw);\n else if (raw == null) text = NO_VALUE;\n // The bare number's minus is U+2212, as every kit figure's (0.49, KIT-1).\n else text = valueFormatter ? valueFormatter(raw) : String(raw).replace(/-/g, TYPOGRAPHIC_MINUS);\n return (\n <div\n key={`${cfgKey}-${i}`}\n // A readout cell is capped so a wide row does not push its value a column away\n // from its name.\n className={cn(\"flex items-center\", inRow ? \"min-w-0 max-w-[12rem] gap-1.5\" : \"gap-2\")}\n >\n <span\n className={cn(\"shrink-0 rounded-[2px]\", indicator === \"dot\" ? \"h-2.5 w-2.5 rounded-full\" : \"h-2.5 w-1\")}\n style={{ backgroundColor: color }}\n />\n <span className={cn(\"min-w-0 text-muted\", inRow ? \"truncate\" : \"break-words\")}>{name}</span>\n <span\n data-private\n // Isolated, so an RTL page does not turn \"-26.6\" into \"26.6-\": a figure has no\n // strong direction of its own, and one formatted with an RTL mark keeps it.\n dir=\"auto\"\n className=\"ms-auto shrink-0 whitespace-nowrap font-mono font-medium tabular-nums text-primary\"\n >\n {text}\n </span>\n </div>\n );\n };\n\n if (readout) {\n // Idle, the row lists every series with no value — the same cells it will have, so\n // nothing under it moves on the first tap.\n const rows: TooltipPayloadItem[] = shown\n ? items!\n : (idleKeys ?? Object.keys(config)).map((key) => ({ dataKey: key }));\n return (\n <div className=\"text-xs\" data-chart-readout={shown ? \"active\" : \"idle\"}>\n {/* A full 24 px line: the zoom's reset button sits at the top corner of a chart\n whose row is above it, and has to fit beside the heading, not over a value. */}\n <div data-readout-heading className=\"truncate font-medium leading-6 text-primary\">\n {shown && !hideLabel && label != null\n ? labelFormatter\n ? labelFormatter(label)\n : label\n : (idleLabel ?? NO_VALUE)}\n </div>\n <div className=\"grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-x-4 gap-y-1\">\n {rows.map((item, i) => tooltipRow(item, i, true))}\n </div>\n </div>\n );\n }\n if (!visible || !items) return null;\n return (\n <div\n ref={tipRef}\n style={\n boundaryRef || nudge\n ? {\n transform: [\n boundaryRef ? (flip ? \"translateX(calc(-100% - 12px))\" : \"translateX(12px)\") : \"\",\n nudge ? `translateX(${nudge}px)` : \"\",\n ]\n .filter(Boolean)\n .join(\" \"),\n }\n : undefined\n }\n // Capped at the viewport: six long series names (lenkbank's German motion\n // labels) made the box wider than a 390px chart, and recharts, which keeps a box\n // inside the plot only when it fits, let it run off the edge with the values cut.\n // Names wrap inside the cap; the values never do.\n className=\"min-w-[9rem] max-w-[min(20rem,calc(100vw-2rem))] rounded-lg border border-subtle bg-surface px-2.5 py-2 text-xs shadow-xl\"\n >\n {!hideLabel && label != null && (\n <div className=\"mb-1.5 font-medium text-primary\">{labelFormatter ? labelFormatter(label) : label}</div>\n )}\n <div className=\"grid gap-1.5\">{items.map((item, i) => tooltipRow(item, i, false))}</div>\n </div>\n );\n}\n\ninterface LegendPayloadItem {\n value?: string;\n dataKey?: string | number;\n color?: string;\n}\n\ninterface ChartLegendContentProps {\n payload?: LegendPayloadItem[];\n onItemClick?: (key: string) => void;\n /**\n * TOGGLE mode (recommended): the series currently switched off. Each entry is a\n * toggle button — pressed while its series is shown — and a hidden one is drawn\n * faded with its label struck through. Several can be off at once; pair with\n * `toggleHidden(hidden, key)` in `onItemClick` and `hide` on the chart's series.\n */\n hiddenKeys?: ReadonlySet<string> | readonly string[];\n /**\n * ISOLATE mode: the one series shown in full, every other entry dimmed. Kept for\n * the charts built on it; a legend where each entry switches its own series is\n * what readers expect from a legend, so prefer `hiddenKeys`.\n */\n activeKey?: string | null;\n}\n\nexport function ChartLegendContent({ payload, onItemClick, hiddenKeys, activeKey }: ChartLegendContentProps) {\n const hidden = hiddenKeys ? new Set(hiddenKeys) : null;\n const config = useChart();\n if (!payload?.length) return null;\n return (\n <div className=\"flex flex-wrap items-center justify-center gap-x-4 gap-y-1 pt-3\">\n {payload.map((item, i) => {\n const dk = item.dataKey != null ? String(item.dataKey) : undefined;\n const vv = item.value != null ? String(item.value) : undefined;\n const cfgKey = dk && config[dk] ? dk : vv && config[vv] ? vv : (dk ?? vv ?? String(i));\n const series = config[cfgKey];\n const color = paintedColor(cfgKey, series) ?? item.color ?? \"var(--text-muted)\";\n const label = series?.label ?? vv ?? cfgKey;\n const off = hidden?.has(cfgKey) ?? false;\n const dimmed = off || (activeKey != null && activeKey !== cfgKey);\n const className = cn(\n \"flex items-center gap-1.5 text-secondary transition-opacity\",\n onItemClick ? \"cursor-pointer hover:opacity-80\" : \"cursor-default\",\n dimmed && \"opacity-40\",\n );\n const swatch = (\n <span\n aria-hidden\n className=\"h-2.5 w-2.5 shrink-0 rounded-[3px]\"\n style={{ backgroundColor: color } as CSSProperties}\n />\n );\n // A legend with no `onItemClick` is a KEY, not a control — and that is every\n // legend in kastlan, where this rendered N focusable buttons per chart that did\n // nothing when activated. A keyboard user paid the tab stops for all of them,\n // and a screen reader announced each one as a button, promising an action there\n // was none of. The interactive branch is unchanged.\n return onItemClick ? (\n <button\n key={`${cfgKey}-${i}`}\n type=\"button\"\n onClick={() => onItemClick(cfgKey)}\n // Toggles report their state: in toggle mode pressed = the series is shown;\n // in isolate mode pressed = the isolated one. With neither supplied the\n // legend knows nothing about state, and a pressed=\"false\" it cannot vouch\n // for would lie — so it says nothing.\n aria-pressed={hidden ? !off : activeKey === undefined ? undefined : activeKey === cfgKey}\n className={className}\n >\n {swatch}\n <span className={cn(off && \"line-through\")}>{label}</span>\n </button>\n ) : (\n <span key={`${cfgKey}-${i}`} className={className}>\n {swatch}\n {label}\n </span>\n );\n })}\n </div>\n );\n}\n"],"mappings":";AA6DM,SAuBE,KAvBF;AAnDN,SAAS,eAAe,YAAY,WAAW,OAAO,iBAAiB,QAAQ,gBAAgB;AAE/F,SAAS,UAAU,gBAAgB,qBAAqB,WAAW,uBAAuB;AAC1F,SAAS,UAAU;AACnB,SAAS,aAAa;AACtB,SAAS,yBAAyB;AASlC,MAAM,eAAe,cAAkC,IAAI;AAEpD,SAAS,WAAwB;AACtC,QAAM,MAAM,WAAW,YAAY;AACnC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,mDAAmD;AAC7E,SAAO;AACT;AAqBO,SAAS,eAAe,EAAE,IAAI,WAAW,UAAU,QAAQ,GAAG,MAAM,GAAwB;AACjG,QAAM,MAAM,MAAM;AAMlB,QAAM,UAAU,UAAU,MAAM,KAAK,QAAQ,mBAAmB,EAAE,CAAC;AACnE,SACE,oBAAC,aAAa,UAAb,EAAsB,OAAO,QAC5B;AAAA,IAAC;AAAA;AAAA,MACC,cAAY;AAAA,MACZ,WAAW;AAAA;AAAA;AAAA;AAAA,QAIT;AAAA;AAAA,QAEA;AAAA;AAAA;AAAA,QAGA;AAAA,QACA;AAAA;AAAA,QAEA;AAAA,QACA;AAAA;AAAA,QAEA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,4BAAC,cAAW,IAAI,SAAS,QAAgB;AAAA,QACzC,oBAAC,uBAAoB,OAAM,QAAO,QAAO,QACtC,UACH;AAAA;AAAA;AAAA,EACF,GACF;AAEJ;AAWA,MAAM,YAAY;AAYlB,MAAM,YAAY;AAUlB,SAAS,aAAa,KAAa,OAA0D;AAC3F,MAAI,CAAC,OAAO,MAAO,QAAO;AAC1B,SAAO,UAAU,KAAK,GAAG,KAAK,UAAU,KAAK,MAAM,KAAK,IAAI,MAAM,QAAQ;AAC5E;AAKA,SAAS,WAAW,EAAE,IAAI,OAAO,GAAwC;AACvE,QAAM,UAAU,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM;AAC1D,QAAI,CAAC,EAAE,MAAO,QAAO;AAIrB,QAAI,CAAC,UAAU,KAAK,GAAG,GAAG;AACxB,cAAQ,KAAK,+DAA+D,GAAG,EAAE;AACjF,aAAO;AAAA,IACT;AACA,QAAI,CAAC,UAAU,KAAK,EAAE,KAAK,GAAG;AAC5B,cAAQ,KAAK,qCAAqC,GAAG,+BAA+B,EAAE,KAAK,EAAE;AAC7F,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,CAAC;AACD,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,QAAM,MAAM,eAAe,EAAE;AAAA,EAAQ,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,KAAK,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA;AAC1G,SAAO,oBAAC,WAAM,yBAAyB,EAAE,QAAQ,IAAI,GAAG;AAC1D;AAeO,SAAS,aAAa,OAA+C;AAC1E,SAAO,oBAAC,mBAAgB,YAAY,OAAQ,GAAG,OAAO;AACxD;AACO,MAAM,cAAc;AAsB3B,MAAM,WAAW;AAYjB,SAAS,aAAa,OAAwD;AAC5E,MAAI,SAAS,QAAQ,UAAU,GAAI,QAAO;AAC1C,QAAM,IAAI,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI;AACtD,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAiDO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,SAAS,SAAS;AACxB,QAAM,SAAS,OAAuB,IAAI;AAC1C,QAAM,UAAU,YAAY;AAG5B,QAAM,QAAQ,gBAAgB,UAAU,SAAS,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI;AACpF,QAAM,QAAQ,QAAQ,UAAU,OAAO,MAAM;AAE7C,QAAM,UAAU,SAAS,CAAC;AAc1B,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,IAAI,YAAY;AACtB,kBAAgB,MAAM;AACpB,UAAM,WAAW,aAAa;AAC9B,QAAI,CAAC,WAAW,CAAC,YAAY,KAAK,MAAM;AACtC,cAAQ,KAAK;AACb;AAAA,IACF;AACA,UAAM,SAAS,MAAM,QAAQ,IACzB,SAAS,cAAc,SAAS,cAAc,SAAS,aACvD,SAAS;AACb,UAAM,WAAW,IAAI;AACrB,UAAM,WAAW,OAAO,SAAS,eAAe;AAChD,YAAQ,WAAW,WAAW,KAAK,SAAS,WAAW;AAAA,EACzD,GAAG,CAAC,SAAS,GAAG,aAAa,SAAS,KAAK,CAAC;AAQ5C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,CAAC;AACpC,QAAM,WAAW,OAAO,CAAC;AAGzB,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,CAAC;AACxC,YAAU,MAAM;AACd,UAAM,UAAU,OAAO,SAAS,QAAqB,2BAA2B;AAChF,QAAI,CAAC,WAAW,CAAC,QAAS;AAC1B,UAAM,QAAQ,MAAM,WAAW,CAAC,MAAM,IAAI,CAAC;AAC3C,YAAQ,iBAAiB,iBAAiB,KAAK;AAC/C,WAAO,MAAM,QAAQ,oBAAoB,iBAAiB,KAAK;AAAA,EACjE,GAAG,CAAC,OAAO,CAAC;AACZ,kBAAgB,MAAM;AACpB,UAAM,KAAK,OAAO;AAClB,QAAI,CAAC,WAAW,CAAC,IAAI;AACnB,eAAS,UAAU;AACnB,eAAS,CAAC;AACV;AAAA,IACF;AACA,UAAM,OAAO,GAAG,sBAAsB;AACtC,QAAI,KAAK,UAAU,EAAG;AACtB,UAAM,SAAS;AACf,UAAM,WAAW,SAAS,gBAAgB,eAAe,OAAO;AAChE,UAAM,OAAO,KAAK,OAAO,SAAS;AAClC,UAAM,QAAQ,OAAO,KAAK;AAC1B,QAAI,OAAO;AACX,QAAI,QAAQ,WAAW,OAAQ,QAAO,WAAW,SAAS;AAC1D,QAAI,OAAO,OAAO,OAAQ,QAAO,SAAS;AAC1C,QAAI,SAAS,SAAS,SAAS;AAC7B,eAAS,UAAU;AACnB,eAAS,IAAI;AAAA,IACf;AAAA,EACF,GAAG,CAAC,SAAS,GAAG,SAAS,OAAO,MAAM,OAAO,CAAC;AAG9C,QAAM,aAAa,CAAC,MAA0B,GAAW,UAAmB;AAG1E,UAAM,KAAK,KAAK,WAAW,OAAO,OAAO,KAAK,OAAO,IAAI;AACzD,UAAM,KAAK,KAAK,QAAQ,OAAO,OAAO,KAAK,IAAI,IAAI;AACnD,UAAM,SAAS,MAAM,OAAO,EAAE,IAAI,KAAK,MAAM,OAAO,EAAE,IAAI,KAAM,MAAM,MAAM,OAAO,CAAC;AACpF,UAAM,SAAS,OAAO,MAAM;AAC5B,UAAM,OAAO,OAAO,KAAK,SAAS,SAAS,WAAW,KAAK,QAAQ,OAAO;AAK1E,UAAM,QAAQ,aAAa,QAAQ,MAAM,KAAK,QAAQ,KAAK,SAAS;AACpE,UAAM,OAAO,QAAQ,SAAS,MAAM;AACpC,UAAM,MAAM,aAAa,KAAK,KAAK;AACnC,QAAI;AAGJ,QAAI,SAAS,CAAC,MAAO,QAAO;AAAA,aACnB,YAAa,QAAO,YAAY,GAAG;AAAA,aACnC,OAAO,KAAM,QAAO;AAAA,QAExB,QAAO,iBAAiB,eAAe,GAAG,IAAI,OAAO,GAAG,EAAE,QAAQ,MAAM,iBAAiB;AAC9F,WACE;AAAA,MAAC;AAAA;AAAA,QAIC,WAAW,GAAG,qBAAqB,QAAQ,kCAAkC,OAAO;AAAA,QAEpF;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,GAAG,0BAA0B,cAAc,QAAQ,6BAA6B,WAAW;AAAA,cACtG,OAAO,EAAE,iBAAiB,MAAM;AAAA;AAAA,UAClC;AAAA,UACA,oBAAC,UAAK,WAAW,GAAG,sBAAsB,QAAQ,aAAa,aAAa,GAAI,gBAAK;AAAA,UACrF;AAAA,YAAC;AAAA;AAAA,cACC,gBAAY;AAAA,cAGZ,KAAI;AAAA,cACJ,WAAU;AAAA,cAET;AAAA;AAAA,UACH;AAAA;AAAA;AAAA,MAlBK,GAAG,MAAM,IAAI,CAAC;AAAA,IAmBrB;AAAA,EAEJ;AAEA,MAAI,SAAS;AAGX,UAAM,OAA6B,QAC/B,SACC,YAAY,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,IAAI,EAAE;AACrE,WACE,qBAAC,SAAI,WAAU,WAAU,sBAAoB,QAAQ,WAAW,QAG9D;AAAA,0BAAC,SAAI,wBAAoB,MAAC,WAAU,+CACjC,mBAAS,CAAC,aAAa,SAAS,OAC7B,iBACE,eAAe,KAAK,IACpB,QACD,aAAa,UACpB;AAAA,MACA,oBAAC,SAAI,WAAU,uEACZ,eAAK,IAAI,CAAC,MAAM,MAAM,WAAW,MAAM,GAAG,IAAI,CAAC,GAClD;AAAA,OACF;AAAA,EAEJ;AACA,MAAI,CAAC,WAAW,CAAC,MAAO,QAAO;AAC/B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,OACE,eAAe,QACX;AAAA,QACE,WAAW;AAAA,UACT,cAAe,OAAO,mCAAmC,qBAAsB;AAAA,UAC/E,QAAQ,cAAc,KAAK,QAAQ;AAAA,QACrC,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MACb,IACA;AAAA,MAMN,WAAU;AAAA,MAET;AAAA,SAAC,aAAa,SAAS,QACtB,oBAAC,SAAI,WAAU,mCAAmC,2BAAiB,eAAe,KAAK,IAAI,OAAM;AAAA,QAEnG,oBAAC,SAAI,WAAU,gBAAgB,gBAAM,IAAI,CAAC,MAAM,MAAM,WAAW,MAAM,GAAG,KAAK,CAAC,GAAE;AAAA;AAAA;AAAA,EACpF;AAEJ;AA0BO,SAAS,mBAAmB,EAAE,SAAS,aAAa,YAAY,UAAU,GAA4B;AAC3G,QAAM,SAAS,aAAa,IAAI,IAAI,UAAU,IAAI;AAClD,QAAM,SAAS,SAAS;AACxB,MAAI,CAAC,SAAS,OAAQ,QAAO;AAC7B,SACE,oBAAC,SAAI,WAAU,mEACZ,kBAAQ,IAAI,CAAC,MAAM,MAAM;AACxB,UAAM,KAAK,KAAK,WAAW,OAAO,OAAO,KAAK,OAAO,IAAI;AACzD,UAAM,KAAK,KAAK,SAAS,OAAO,OAAO,KAAK,KAAK,IAAI;AACrD,UAAM,SAAS,MAAM,OAAO,EAAE,IAAI,KAAK,MAAM,OAAO,EAAE,IAAI,KAAM,MAAM,MAAM,OAAO,CAAC;AACpF,UAAM,SAAS,OAAO,MAAM;AAC5B,UAAM,QAAQ,aAAa,QAAQ,MAAM,KAAK,KAAK,SAAS;AAC5D,UAAM,QAAQ,QAAQ,SAAS,MAAM;AACrC,UAAM,MAAM,QAAQ,IAAI,MAAM,KAAK;AACnC,UAAM,SAAS,OAAQ,aAAa,QAAQ,cAAc;AAC1D,UAAM,YAAY;AAAA,MAChB;AAAA,MACA,cAAc,oCAAoC;AAAA,MAClD,UAAU;AAAA,IACZ;AACA,UAAM,SACJ;AAAA,MAAC;AAAA;AAAA,QACC,eAAW;AAAA,QACX,WAAU;AAAA,QACV,OAAO,EAAE,iBAAiB,MAAM;AAAA;AAAA,IAClC;AAOF,WAAO,cACL;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,SAAS,MAAM,YAAY,MAAM;AAAA,QAKjC,gBAAc,SAAS,CAAC,MAAM,cAAc,SAAY,SAAY,cAAc;AAAA,QAClF;AAAA,QAEC;AAAA;AAAA,UACD,oBAAC,UAAK,WAAW,GAAG,OAAO,cAAc,GAAI,iBAAM;AAAA;AAAA;AAAA,MAX9C,GAAG,MAAM,IAAI,CAAC;AAAA,IAYrB,IAEA,qBAAC,UAA4B,WAC1B;AAAA;AAAA,MACA;AAAA,SAFQ,GAAG,MAAM,IAAI,CAAC,EAGzB;AAAA,EAEJ,CAAC,GACH;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/chart.tsx"],"sourcesContent":["// shadcn/ui chart kit — vendored (MIT) and adapted for this app:\n// - Recharts 3 instead of 2\n// - the original keys its styling off shadcn theme tokens (--muted-foreground,\n// --border, bg-background …). This app has no shadcn token layer, so those\n// are mapped onto this kit's own tokens (--text-muted, --border, --bg-surface\n// …), which follow the active palette preset and flip with the theme by\n// themselves — hence no `dark:` variants anywhere below.\n// The value on show is the *structure*: config-driven colors injected as CSS\n// vars, muted axis/grid styling, and a polished tooltip/legend — the things\n// that make shadcn charts read as \"designed\" rather than \"default Recharts\".\nimport { createContext, useContext, useEffect, useId, useLayoutEffect, useRef, useState } from \"react\";\nimport type { ComponentProps, CSSProperties, ReactNode, RefObject } from \"react\";\nimport { Legend as RechartsLegend, ResponsiveContainer, Tooltip as RechartsTooltip } from \"recharts\";\nimport { cn } from \"../lib/cn\";\nimport { isRtl } from \"../lib/direction\";\nimport { useFigureDescription } from \"./chart-figure\";\nimport { TYPOGRAPHIC_MINUS } from \"../lib/minus\";\n\nexport interface ChartSeriesConfig {\n label: ReactNode;\n color: string;\n}\n\nexport type ChartConfig = Record<string, ChartSeriesConfig>;\n\nconst ChartContext = createContext<ChartConfig | null>(null);\n\nexport function useChart(): ChartConfig {\n const ctx = useContext(ChartContext);\n if (!ctx) throw new Error(\"useChart must be used within a <ChartContainer />\");\n return ctx;\n}\n\ninterface ChartContainerProps extends ComponentProps<\"div\"> {\n config: ChartConfig;\n children: ComponentProps<typeof ResponsiveContainer>[\"children\"];\n /**\n * What the chart SHOWS, in a sentence, as its accessible description (0.52): read through\n * `aria-describedby`, after a caller's own ids, and drawn nowhere. Inside the root, so\n * the chart stays the one element its parent lays out. Pair it with `role=\"figure\"` and\n * an `aria-label`, as `Treemap` and `SeriesChart` do.\n */\n description?: string;\n}\n\n/**\n * The shell every chart in the kit is drawn inside.\n *\n * **The plot is not mirrored in RTL.** An x axis is a number line or a timeline, and\n * both run left-to-right in Hebrew and Arabic charts as much as in English ones; a\n * time axis flipped to run right-to-left is the surprise, not the courtesy. So no\n * `reversed` on any axis, and the SVG is pinned to `direction: ltr` — without that it\n * inherits the page's `rtl`, which flips what `text-anchor: end` means, and the tick\n * labels of a left-hand y axis are drawn INTO the plot instead of beside it. Text\n * inside the SVG still shapes by its own script; only the anchoring is physical.\n * The HTML around the plot — legend, tooltip, the zoom reset button — follows the\n * page's direction like any other markup. A consumer who does want a mirrored\n * category axis passes `reversed` on its own `XAxis`.\n */\nexport function ChartContainer({ id, className, children, config, description, ...props }: ChartContainerProps) {\n const uid = useId();\n const about = useFigureDescription(description, props[\"aria-describedby\"]);\n // The id is pasted into an UNQUOTED `[data-chart=…]` selector below, so it has to be\n // a CSS identifier: one stray space or `#` and the browser discards the whole rule,\n // taking every series colour with it — silently, because an invalid rule is not an\n // error anywhere. `useId()` gives `:r0:`, and a consumer-supplied id gives whatever\n // they typed, so both are filtered rather than just the colons.\n const chartId = `chart-${(id ?? uid).replace(/[^A-Za-z0-9_-]/g, \"\")}`;\n return (\n <ChartContext.Provider value={config}>\n <div\n data-chart={chartId}\n className={cn(\n // touch-pan-y lets a vertical finger drag still scroll the page while\n // a horizontal drag is handed to recharts for tooltip scrubbing,\n // instead of the page scrolling underneath the gesture (feedback #292).\n \"flex w-full touch-pan-y justify-center text-xs\",\n // physical anchoring for the unmirrored plot (see the doc comment above)\n \"[&_.recharts-surface]:[direction:ltr]\",\n // muted axis labels + gridlines; one class each, since the tokens\n // already carry the light/dark values\n \"[&_.recharts-cartesian-axis-tick_text]:fill-muted\",\n \"[&_.recharts-cartesian-grid_line]:stroke-subtle/70\",\n // hairline hover cursor instead of the heavy default\n \"[&_.recharts-curve.recharts-tooltip-cursor]:stroke-strong\",\n \"[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-[var(--bg-hover)]\",\n // kill the focus outlines & white sector strokes Recharts adds\n \"[&_.recharts-sector]:outline-none [&_.recharts-sector[stroke='#fff']]:stroke-transparent\",\n \"[&_.recharts-layer]:outline-none [&_.recharts-surface]:outline-none [&_.recharts-dot[stroke='#fff']]:stroke-transparent\",\n className,\n )}\n {...props}\n aria-describedby={about.describedBy}\n >\n <ChartStyle id={chartId} config={config} />\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {children}\n </ResponsiveContainer>\n {about.node}\n </div>\n </ChartContext.Provider>\n );\n}\n\n/**\n * A config key, which becomes the tail of a custom-property NAME.\n *\n * `dangerouslySetInnerHTML` means what it says: nothing below is escaped by React, and\n * a `ChartConfig` is not necessarily written by hand — kastlan builds one from category\n * rows in its database, so these keys are user data. A key containing `}` closes this\n * rule, and whatever follows it in that key becomes a NEW rule — one that applies to\n * the whole document rather than to this one chart.\n */\nconst CSS_IDENT = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\n/**\n * A config colour, which becomes a declaration VALUE.\n *\n * Every form a palette actually produces: both hex spellings, the functional notations\n * — with the argument list restricted to the characters those functions can legally\n * contain, so no `;`, no `}` and no nested `url(` — and `var(--token)`, which is what\n * this kit's own `paletteFor`/`CHART_COLORS` return. `var()`'s optional FALLBACK is\n * deliberately not accepted: its second argument is an arbitrary value, i.e. the same\n * hole in a shape that looks safe.\n */\nconst CSS_COLOR = /^(?:#[0-9a-fA-F]{3}|#[0-9a-fA-F]{6}|(?:rgba?|hsla?)\\([0-9A-Za-z.,%/ -]*\\)|var\\(--[A-Za-z0-9_-]+\\))$/;\n\n/**\n * The colour an entry is painted with, or `undefined` when the style block refuses it.\n *\n * The one gate both sides go through: {@link ChartStyle} emits exactly these, and the\n * legend and tooltip swatches show exactly these. They used to read `config[key].color`\n * directly, so an entry the style block had skipped still showed its configured colour\n * in the key — a swatch promising a colour the series was never painted in.\n */\nfunction paintedColor(key: string, entry: ChartSeriesConfig | undefined): string | undefined {\n if (!entry?.color) return undefined;\n return CSS_IDENT.test(key) && CSS_COLOR.test(entry.color) ? entry.color : undefined;\n}\n\n// Inject each series colour as a `--color-<key>` custom property scoped to this\n// chart, so chart JSX can refer to `var(--color-total)` and the legend/tooltip\n// stay in sync from one source of truth.\nfunction ChartStyle({ id, config }: { id: string; config: ChartConfig }) {\n const entries = Object.entries(config).filter(([key, c]) => {\n if (!c.color) return false;\n // Skipping the entry costs that one series its `var(--color-…)` and leaves the rest\n // of the chart painted; emitting it unchecked costs the page. The warning is so the\n // consumer finds out from their console rather than from a colourless bar.\n if (!CSS_IDENT.test(key)) {\n console.warn(`[ui-kit] chart config key is not a CSS identifier, skipped: ${key}`);\n return false;\n }\n if (!CSS_COLOR.test(c.color)) {\n console.warn(`[ui-kit] chart config colour for \"${key}\" is not a colour, skipped: ${c.color}`);\n return false;\n }\n return true;\n });\n if (!entries.length) return null;\n const css = `[data-chart=${id}] {\\n${entries.map(([key, c]) => ` --color-${key}: ${c.color};`).join(\"\\n\")}\\n}`;\n return <style dangerouslySetInnerHTML={{ __html: css }} />;\n}\n\n/**\n * recharts' `Tooltip`, with `filterNull` defaulting to **false**.\n *\n * recharts drops every payload entry whose value is `null`/`undefined` before the\n * content sees it, so the missing-value path {@link ChartTooltipContent} documents —\n * the em dash, `formatValue(undefined)` — never ran under the default: a series with a\n * gap at the cursor simply vanished from the tooltip, which reads as \"this series does\n * not exist here\" rather than \"it has no value here\". Kept, the entry prints \"—\".\n *\n * The other half of that filter — dropping series drawn with `hide` — is redone by\n * `ChartTooltipContent` itself (honouring `includeHidden`), so a legend toggle still\n * takes a series out of the tooltip. Pass `filterNull` to get recharts' behaviour back.\n */\nexport function ChartTooltip(props: ComponentProps<typeof RechartsTooltip>) {\n return <RechartsTooltip filterNull={false} {...props} />;\n}\nexport const ChartLegend = RechartsLegend;\n\ninterface TooltipPayloadItem {\n dataKey?: string | number;\n name?: string | number;\n value?: number | string;\n color?: string;\n /** A series drawn with `hide` — dropped here, see {@link ChartTooltip}. */\n hide?: boolean;\n payload?: Record<string, unknown>;\n}\n\n/**\n * What a series has no value for at this point: a gap in a history, a projection that\n * has not happened yet, a field that did not exist at an earlier capture.\n *\n * An em dash rather than an empty cell, so the row still lines up with its siblings and\n * the reader can see that the series WAS asked and had nothing, rather than wondering\n * whether it was dropped. It is a mark, not a word, so there is nothing here to\n * translate and no label prop to resolve; a caller who wants words passes\n * `formatValue`, which is handed the `undefined` and decides.\n */\nconst NO_VALUE = \"—\";\n\n/**\n * The number this payload item carries, or `undefined` when it carries none.\n *\n * This was `item.value ?? 0`, so every one of the absences above printed a confident\n * \"0\" that appears nowhere in the data — a fabricated figure in a tooltip, which is the\n * one place a reader goes to check an exact number. keksdose found it and wrapped the\n * component rather than trust it. `\"\"` and a non-numeric string are the same absence\n * spelled differently (recharts hands a string value straight through from the data),\n * and are answered the same way.\n */\nfunction tooltipValue(value: number | string | undefined): number | undefined {\n if (value == null || value === \"\") return undefined;\n const n = typeof value === \"string\" ? Number(value) : value;\n return Number.isFinite(n) ? n : undefined;\n}\n\ninterface ChartTooltipContentProps {\n active?: boolean;\n payload?: TooltipPayloadItem[];\n label?: string | number;\n indicator?: \"dot\" | \"line\";\n hideLabel?: boolean;\n labelFormatter?: (label: string | number) => ReactNode;\n /** Formats one series' value for display. NOT called when the series has no value at\n * this point — {@link ChartTooltipContentProps.formatValue} is the one that is. */\n valueFormatter?: (value: number) => ReactNode;\n /** Formats one series' value INCLUDING its absence: handed `undefined` for a gap, a\n * projection that has not happened yet, or a field that did not exist at an earlier\n * capture, and whatever it returns is rendered as-is. Only the caller knows whether\n * that absence reads as a dash, a \"not yet\" or a genuine zero, so this is how it\n * says. Supplying it wins over `valueFormatter` for every value.\n *\n * It sits NEXT to `valueFormatter` rather than widening it because that prop's\n * parameter is contextually typed at every call site there is — `(v) => money(v)` —\n * so widening it to `number | undefined` is a compile error in all three consuming\n * apps, which is a breaking change this module does not get to make by itself. */\n formatValue?: (value: number | undefined) => ReactNode;\n /** Cursor position within the chart, injected by recharts. Used with\n * {@link boundaryRef} to decide whether to flip the tooltip. */\n coordinate?: { x?: number; y?: number };\n /** When set, the tooltip flips to the LEFT of the cursor if it would otherwise\n * spill past the right edge of this (usually horizontally-scrolling) container\n * — e.g. a wide chart whose rightmost bars sat off-screen (feedback #77). */\n boundaryRef?: RefObject<HTMLElement | null>;\n /** Injected by recharts: keep series drawn with `hide` in the tooltip. */\n includeHidden?: boolean;\n /**\n * `\"floating\"` (the default): the box recharts moves with the cursor. `\"readout\"`: the\n * same heading and values as a flat row for a FIXED place outside the plot — a\n * `SeriesChart` whose `tooltip.placement` is `\"above\"`/`\"below\"` portals it there, so\n * a finger on a phone no longer sits on the box it is trying to read (keksdose #359).\n * No border, shadow, width cap or viewport nudge; the values flow in a grid whose\n * column count depends on the row's width alone, so the row is the same height\n * whatever is being pointed at — and, idle, it shows {@link idleKeys} with \"—\".\n */\n variant?: \"floating\" | \"readout\";\n /** Readout only: the series to list while nothing is active, so the row keeps the\n * height it will have once something is. */\n idleKeys?: readonly string[];\n /** Readout only: the heading while nothing is active. Default \"—\". */\n idleLabel?: ReactNode;\n}\n\nexport function ChartTooltipContent({\n active,\n payload,\n label,\n indicator = \"dot\",\n hideLabel = false,\n labelFormatter,\n valueFormatter,\n formatValue,\n coordinate,\n boundaryRef,\n includeHidden = false,\n variant = \"floating\",\n idleKeys,\n idleLabel,\n}: ChartTooltipContentProps) {\n const config = useChart();\n const tipRef = useRef<HTMLDivElement>(null);\n const readout = variant === \"readout\";\n // `ChartTooltip` keeps null values (so they can print \"—\"), which also keeps the\n // series a legend toggle switched off with `hide`; those go here instead.\n const items = includeHidden ? payload : payload?.filter((item) => item.hide !== true);\n const shown = Boolean(active && items?.length);\n // Everything below that measures and moves the box is about a FLOATING one.\n const visible = shown && !readout;\n // Recharts anchors the tooltip at coordinate.x inside the full (scrolled) chart\n // width; subtract the container's scrollLeft to get its on-screen x, then flip\n // left if the tooltip's own width would run past the visible right edge.\n //\n // Both the cursor and the flip are PHYSICAL (the plot is never mirrored, see\n // `ChartContainer`), but an RTL scroller counts `scrollLeft` from its right edge —\n // 0 at the start, negative going left — so there the content's left edge sits\n // `scrollWidth - clientWidth + scrollLeft` px to the left of the visible one.\n //\n // Measured in a layout effect (the DOM is not readable during render), so it commits\n // before paint. `payload` and `label` are deps because they change the tooltip's own\n // width, and recharts hands a fresh `payload` on every tooltip render, so a scroll\n // followed by any re-render is re-measured just as it was when this ran in render.\n const [flip, setFlip] = useState(false);\n const x = coordinate?.x;\n useLayoutEffect(() => {\n const boundary = boundaryRef?.current;\n if (!visible || !boundary || x == null) {\n setFlip(false);\n return;\n }\n const offset = isRtl(boundary)\n ? boundary.scrollWidth - boundary.clientWidth + boundary.scrollLeft\n : boundary.scrollLeft;\n const visibleX = x - offset;\n const tipWidth = tipRef.current?.offsetWidth ?? 160;\n setFlip(visibleX + tipWidth + 12 > boundary.clientWidth);\n }, [visible, x, boundaryRef, payload, label]);\n\n // Whatever placed it — recharts' own rule or the boundary flip above — the box then\n // clamps itself into the viewport, 8px off each edge. The width cap alone did not\n // keep it on screen: on a 390px phone recharts opened a near-full-width tooltip to\n // the RIGHT of a pointer 35% into the plot, and the values ran off the edge\n // (lenkbank, on 0.15.2). Measured after placement, minus the shift it already has,\n // so a re-measure never compounds; an unmeasured box (no layout) is left alone.\n const [nudge, setNudge] = useState(0);\n const nudgeRef = useRef(0);\n // recharts slides its wrapper to each new position with a CSS transition, so the box\n // measured on commit is still where it WAS; measure again once the slide ends.\n const [settled, setSettled] = useState(0);\n useEffect(() => {\n const wrapper = tipRef.current?.closest<HTMLElement>(\".recharts-tooltip-wrapper\");\n if (!visible || !wrapper) return;\n const onEnd = () => setSettled((n) => n + 1);\n wrapper.addEventListener(\"transitionend\", onEnd);\n return () => wrapper.removeEventListener(\"transitionend\", onEnd);\n }, [visible]);\n useLayoutEffect(() => {\n const el = tipRef.current;\n if (!visible || !el) {\n nudgeRef.current = 0;\n setNudge(0);\n return;\n }\n const rect = el.getBoundingClientRect();\n if (rect.width === 0) return;\n const margin = 8;\n const viewport = document.documentElement.clientWidth || window.innerWidth;\n const left = rect.left - nudgeRef.current;\n const right = left + rect.width;\n let next = 0;\n if (right > viewport - margin) next = viewport - margin - right;\n if (left + next < margin) next = margin - left;\n if (next !== nudgeRef.current) {\n nudgeRef.current = next;\n setNudge(next);\n }\n }, [visible, x, payload, label, flip, settled]);\n // One series' line — the floating box stacks them; the readout lays them in a grid,\n // where a long name is cut rather than wrapped so every cell stays one line high.\n const tooltipRow = (item: TooltipPayloadItem, i: number, inRow: boolean) => {\n // Bars/lines/areas identify a series by dataKey; pies/treemaps key off\n // `name` (the nameKey value). Prefer whichever the config knows.\n const dk = item.dataKey != null ? String(item.dataKey) : undefined;\n const nm = item.name != null ? String(item.name) : undefined;\n const cfgKey = dk && config[dk] ? dk : nm && config[nm] ? nm : (dk ?? nm ?? String(i));\n const series = config[cfgKey];\n const fill = typeof item.payload?.fill === \"string\" ? item.payload.fill : undefined;\n // The data point's own `fill` before recharts' `item.color`: a treemap tile,\n // or a bar with a per-cell colour, IS the colour the reader is pointing at,\n // while `item.color` is the series-wide one — every tile's swatch came out\n // `--chart-1` whatever colour the tile was.\n const color = paintedColor(cfgKey, series) ?? fill ?? item.color ?? \"var(--text-muted)\";\n const name = series?.label ?? nm ?? cfgKey;\n const raw = tooltipValue(item.value);\n let text: ReactNode;\n // An idle readout cell has asked nothing yet, so there is no absence for\n // `formatValue` to word — it is the bare mark.\n if (inRow && !shown) text = NO_VALUE;\n else if (formatValue) text = formatValue(raw);\n else if (raw == null) text = NO_VALUE;\n // The bare number's minus is U+2212, as every kit figure's (0.49, KIT-1).\n else text = valueFormatter ? valueFormatter(raw) : String(raw).replace(/-/g, TYPOGRAPHIC_MINUS);\n return (\n <div\n key={`${cfgKey}-${i}`}\n // A readout cell is capped so a wide row does not push its value a column away\n // from its name.\n className={cn(\"flex items-center\", inRow ? \"min-w-0 max-w-[12rem] gap-1.5\" : \"gap-2\")}\n >\n <span\n className={cn(\"shrink-0 rounded-[2px]\", indicator === \"dot\" ? \"h-2.5 w-2.5 rounded-full\" : \"h-2.5 w-1\")}\n style={{ backgroundColor: color }}\n />\n <span className={cn(\"min-w-0 text-muted\", inRow ? \"truncate\" : \"break-words\")}>{name}</span>\n <span\n data-private\n // Isolated, so an RTL page does not turn \"-26.6\" into \"26.6-\": a figure has no\n // strong direction of its own, and one formatted with an RTL mark keeps it.\n dir=\"auto\"\n className=\"ms-auto shrink-0 whitespace-nowrap font-mono font-medium tabular-nums text-primary\"\n >\n {text}\n </span>\n </div>\n );\n };\n\n if (readout) {\n // Idle, the row lists every series with no value — the same cells it will have, so\n // nothing under it moves on the first tap.\n const rows: TooltipPayloadItem[] = shown\n ? items!\n : (idleKeys ?? Object.keys(config)).map((key) => ({ dataKey: key }));\n return (\n <div className=\"text-xs\" data-chart-readout={shown ? \"active\" : \"idle\"}>\n {/* A full 24 px line: the zoom's reset button sits at the top corner of a chart\n whose row is above it, and has to fit beside the heading, not over a value. */}\n <div data-readout-heading className=\"truncate font-medium leading-6 text-primary\">\n {shown && !hideLabel && label != null\n ? labelFormatter\n ? labelFormatter(label)\n : label\n : (idleLabel ?? NO_VALUE)}\n </div>\n <div className=\"grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-x-4 gap-y-1\">\n {rows.map((item, i) => tooltipRow(item, i, true))}\n </div>\n </div>\n );\n }\n if (!visible || !items) return null;\n return (\n <div\n ref={tipRef}\n style={\n boundaryRef || nudge\n ? {\n transform: [\n boundaryRef ? (flip ? \"translateX(calc(-100% - 12px))\" : \"translateX(12px)\") : \"\",\n nudge ? `translateX(${nudge}px)` : \"\",\n ]\n .filter(Boolean)\n .join(\" \"),\n }\n : undefined\n }\n // Capped at the viewport: six long series names (lenkbank's German motion\n // labels) made the box wider than a 390px chart, and recharts, which keeps a box\n // inside the plot only when it fits, let it run off the edge with the values cut.\n // Names wrap inside the cap; the values never do.\n className=\"min-w-[9rem] max-w-[min(20rem,calc(100vw-2rem))] rounded-lg border border-subtle bg-surface px-2.5 py-2 text-xs shadow-xl\"\n >\n {!hideLabel && label != null && (\n <div className=\"mb-1.5 font-medium text-primary\">{labelFormatter ? labelFormatter(label) : label}</div>\n )}\n <div className=\"grid gap-1.5\">{items.map((item, i) => tooltipRow(item, i, false))}</div>\n </div>\n );\n}\n\ninterface LegendPayloadItem {\n value?: string;\n dataKey?: string | number;\n color?: string;\n}\n\ninterface ChartLegendContentProps {\n payload?: LegendPayloadItem[];\n onItemClick?: (key: string) => void;\n /**\n * TOGGLE mode (recommended): the series currently switched off. Each entry is a\n * toggle button — pressed while its series is shown — and a hidden one is drawn\n * faded with its label struck through. Several can be off at once; pair with\n * `toggleHidden(hidden, key)` in `onItemClick` and `hide` on the chart's series.\n */\n hiddenKeys?: ReadonlySet<string> | readonly string[];\n /**\n * ISOLATE mode: the one series shown in full, every other entry dimmed. Kept for\n * the charts built on it; a legend where each entry switches its own series is\n * what readers expect from a legend, so prefer `hiddenKeys`.\n */\n activeKey?: string | null;\n}\n\nexport function ChartLegendContent({ payload, onItemClick, hiddenKeys, activeKey }: ChartLegendContentProps) {\n const hidden = hiddenKeys ? new Set(hiddenKeys) : null;\n const config = useChart();\n if (!payload?.length) return null;\n return (\n <div className=\"flex flex-wrap items-center justify-center gap-x-4 gap-y-1 pt-3\">\n {payload.map((item, i) => {\n const dk = item.dataKey != null ? String(item.dataKey) : undefined;\n const vv = item.value != null ? String(item.value) : undefined;\n const cfgKey = dk && config[dk] ? dk : vv && config[vv] ? vv : (dk ?? vv ?? String(i));\n const series = config[cfgKey];\n const color = paintedColor(cfgKey, series) ?? item.color ?? \"var(--text-muted)\";\n const label = series?.label ?? vv ?? cfgKey;\n const off = hidden?.has(cfgKey) ?? false;\n const dimmed = off || (activeKey != null && activeKey !== cfgKey);\n // The label's cue, in a key and on a button alike: a hidden series struck through,\n // and an entry an isolate legend dims in the muted ink — drawn still, so not struck.\n // Only a toggle button had one, and a key or an isolate legend told an entry off by\n // its 10px swatch at 40 % alone (0.52 code review, deep, charts, finding 9).\n const cue = off ? \"line-through\" : dimmed ? \"text-muted\" : undefined;\n const className = cn(\n \"flex items-center gap-1.5 text-secondary transition-opacity\",\n onItemClick ? \"cursor-pointer hover:opacity-80\" : \"cursor-default\",\n );\n // Only the swatch fades; the label keeps an ink of AA contrast (0.52, refactor audit\n // 2026-10-30, DSP-9). The whole entry at 40 % put a switched-off label under 2:1,\n // on a button that brings its series back and is named by nothing else.\n const swatch = (\n <span\n aria-hidden\n className={cn(\"h-2.5 w-2.5 shrink-0 rounded-[3px]\", dimmed && \"opacity-40\")}\n style={{ backgroundColor: color } as CSSProperties}\n />\n );\n // A legend with no `onItemClick` is a KEY, not a control — and that is every\n // legend in kastlan, where this rendered N focusable buttons per chart that did\n // nothing when activated. A keyboard user paid the tab stops for all of them,\n // and a screen reader announced each one as a button, promising an action there\n // was none of. The interactive branch is unchanged.\n return onItemClick ? (\n <button\n key={`${cfgKey}-${i}`}\n type=\"button\"\n onClick={() => onItemClick(cfgKey)}\n // Toggles report their state: in toggle mode pressed = the series is shown;\n // in isolate mode pressed = the isolated one. With neither supplied the\n // legend knows nothing about state, and a pressed=\"false\" it cannot vouch\n // for would lie — so it says nothing.\n aria-pressed={hidden ? !off : activeKey === undefined ? undefined : activeKey === cfgKey}\n className={className}\n >\n {swatch}\n <span className={cue}>{label}</span>\n </button>\n ) : (\n // On the entry, not round the label: the text is its own, so the key's DOM is the\n // same with a cue as without. A line-through reaches the text only (the swatch\n // holds none), and the ink colours no swatch.\n <span key={`${cfgKey}-${i}`} className={cn(className, cue)}>\n {swatch}\n {label}\n </span>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -71,16 +71,19 @@ export declare const DISABLED_ROW_CLASS = "cursor-not-allowed opacity-50 hover:b
|
|
|
71
71
|
* Whether an entity picker offers its create row — one rule for
|
|
72
72
|
* {@link EntityCombobox}, {@link MultiEntityCombobox} and {@link InlineEntityCombobox}.
|
|
73
73
|
*
|
|
74
|
-
* With a query: while the trimmed query names no option exactly
|
|
75
|
-
*
|
|
76
|
-
*
|
|
74
|
+
* With a query: while the trimmed query names no option exactly — an exact hit is a
|
|
75
|
+
* record the list is already showing, and a second way to "make" it would mint a
|
|
76
|
+
* duplicate. "Exactly" as the list matches, folded by `locale` ({@link sameName}): with
|
|
77
|
+
* plain `toLowerCase`, "cafe" listed Café and offered "Create “cafe”" beside it (refactor
|
|
78
|
+
* audit 2026-10-30, DUP-5). With NO query: only when the caller named the row for that
|
|
77
79
|
* case (`createEmptyLabel`) — a row reading `Create “”` says nothing, and the kit cannot
|
|
78
80
|
* know the noun ("Create cash account") that would make it say something (keksdose G9).
|
|
79
81
|
*
|
|
80
82
|
* `names` is what the query is compared against: the loaded rows for a panel picker
|
|
81
|
-
* (its async list is all it knows), every option for the inline one.
|
|
83
|
+
* (its async list is all it knows), every option for the inline one. `locale`: the
|
|
84
|
+
* provider's, which the list's match folds by; left out, the runtime's.
|
|
82
85
|
*/
|
|
83
|
-
export declare function offersCreate(onCreate: unknown, query: string, names: readonly string[], createEmptyLabel: string | undefined): boolean;
|
|
86
|
+
export declare function offersCreate(onCreate: unknown, query: string, names: readonly string[], createEmptyLabel: string | undefined, locale?: string): boolean;
|
|
84
87
|
/**
|
|
85
88
|
* {@link EndHintRow}'s `hint` and `className` for a combobox-family field, keeping the
|
|
86
89
|
* 0.22 rule (keksdose): a field that was passed `hint` at all — even `undefined` — keeps
|
|
@@ -219,9 +222,75 @@ export declare function useComboboxCore<V extends string | number>({ options, mi
|
|
|
219
222
|
* heading is the one thing that says where in the alphabet the thumb is. Opaque — the
|
|
220
223
|
* list's own `bg-surface` — so the rows pass beneath it rather than through it. Shared
|
|
221
224
|
* by every list that heads groups (the panel and the sheet, `Combobox`, `Autocomplete`,
|
|
222
|
-
* `InlineEntityCombobox`).
|
|
225
|
+
* `InlineEntityCombobox`). Inside its {@link OptionGroup} since 0.52, it sticks while its
|
|
226
|
+
* own group is in view and the next group's heading pushes it out.
|
|
223
227
|
*/
|
|
224
228
|
export declare const GROUP_HEADING_STICKY = "sticky top-0 z-10 bg-surface";
|
|
229
|
+
/** One run of rows that share a group, as {@link groupRuns} finds them: `group` is
|
|
230
|
+
* `null` for rows filed under none, which stand bare in the list; `start` is the index
|
|
231
|
+
* of the run's first row in the whole list (the option ids and the arrows count by it). */
|
|
232
|
+
export interface GroupRun<T> {
|
|
233
|
+
group: string | null;
|
|
234
|
+
start: number;
|
|
235
|
+
rows: T[];
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* A list's rows, cut where the group changes — the runs {@link OptionGroup} wraps. The
|
|
239
|
+
* lists keep their rows group-contiguous (the panel's source order, or the ranked list
|
|
240
|
+
* regrouped by its best member), so a run is "the rows up to the next change of group".
|
|
241
|
+
* `groupOf` is asked once per row. An empty group name is no group: a heading of nothing
|
|
242
|
+
* names nothing.
|
|
243
|
+
*/
|
|
244
|
+
export declare function groupRuns<T>(rows: readonly T[], groupOf: (row: T) => string | null | undefined): GroupRun<T>[];
|
|
245
|
+
/**
|
|
246
|
+
* A group of options under its heading, inside a `role="listbox"`: a `role="group"`
|
|
247
|
+
* NAMED by the heading, so a reader moving through the list hears which group a row is
|
|
248
|
+
* in (refactor audit 2026-10-30, INP-8). The heading was loose text among the options,
|
|
249
|
+
* and focus stays in the search box with the row announced through
|
|
250
|
+
* `aria-activedescendant` — keksdose's "Groceries" under Recent and again under its own
|
|
251
|
+
* group were two identical names, and an account filed by its type never said the type.
|
|
252
|
+
* The heading stays a visible first item of the group (sticky within it); the columns of
|
|
253
|
+
* {@link ComboboxPanel} carry the same `role="group"`.
|
|
254
|
+
*
|
|
255
|
+
* `id` must be unique in the document (the list's id plus the run's start); `className`
|
|
256
|
+
* places the heading's text in its list.
|
|
257
|
+
*/
|
|
258
|
+
export declare function OptionGroup({ id, heading, className, children, }: {
|
|
259
|
+
id: string;
|
|
260
|
+
heading: ReactNode;
|
|
261
|
+
className?: string;
|
|
262
|
+
children: ReactNode;
|
|
263
|
+
}): import("react").JSX.Element;
|
|
264
|
+
/**
|
|
265
|
+
* A list's rows in their {@link groupRuns}: a run filed under no group stands bare, any
|
|
266
|
+
* other is an {@link OptionGroup} under its heading. The one drawing of it for
|
|
267
|
+
* {@link ComboboxPanel}, {@link Combobox}, {@link InlineEntityCombobox} and
|
|
268
|
+
* {@link Autocomplete}, which had a copy each (0.52 code review, combobox-core.tsx).
|
|
269
|
+
*
|
|
270
|
+
* A run is keyed by its group and how many runs of that group came before it — NOT by
|
|
271
|
+
* the index of its first row, which every keystroke that drops a row from an earlier
|
|
272
|
+
* group moves: each later group was re-keyed and React rebuilt it, every row of it, per
|
|
273
|
+
* keystroke. A row is keyed by `rowKey` among the rows of its run; an option may stand
|
|
274
|
+
* in two groups (keksdose's "Recent", live #203), never twice in one.
|
|
275
|
+
*
|
|
276
|
+
* `row(o, i, grouped)`: `i` is the row's index in the whole list (the option ids and the
|
|
277
|
+
* arrows count by it), `grouped` whether it stands under a heading. `headingClassName`
|
|
278
|
+
* places a run's heading text in its list. The group's id is the list's id and the run's
|
|
279
|
+
* first row, unique in the document.
|
|
280
|
+
*/
|
|
281
|
+
export declare function GroupedOptions<T>({ runs, listboxId, rowKey, row, headingClassName, }: {
|
|
282
|
+
runs: readonly GroupRun<T>[];
|
|
283
|
+
listboxId: string;
|
|
284
|
+
rowKey: (o: T) => string;
|
|
285
|
+
row: (o: T, index: number, grouped: boolean) => ReactNode;
|
|
286
|
+
headingClassName: (run: GroupRun<T>) => string;
|
|
287
|
+
}): import("react").JSX.Element[];
|
|
288
|
+
/** The anchored list's heading text: the first group's sits closer to the list's top
|
|
289
|
+
* edge, unless a row stands above it (`first: false`, Combobox's create row). */
|
|
290
|
+
export declare const anchoredHeadingClass: (run: GroupRun<unknown>, first?: boolean) => string;
|
|
291
|
+
/** The typed comboboxes' phone sheet's heading text ({@link Combobox},
|
|
292
|
+
* {@link InlineEntityCombobox}). */
|
|
293
|
+
export declare const SHEET_HEADING_CLASS = "px-4 pb-0.5 pt-3 text-caption";
|
|
225
294
|
export declare const SUGGESTION_LIST_CLASS = "max-h-64 overflow-y-auto rounded-md border border-subtle bg-surface py-1 shadow-lg";
|
|
226
295
|
export declare const suggestionRowClass: (isActive: boolean) => string;
|
|
227
296
|
/**
|
|
@@ -237,6 +306,47 @@ export declare const suggestionRowClass: (isActive: boolean) => string;
|
|
|
237
306
|
* trade than a list that does not scroll in a test.
|
|
238
307
|
*/
|
|
239
308
|
export declare function useActiveOptionScroll(activeId: string | undefined): void;
|
|
309
|
+
/**
|
|
310
|
+
* What a TYPED combobox's list says — {@link Combobox} and {@link InlineEntityCombobox},
|
|
311
|
+
* which had a copy each, the second saying "see the twin" (0.52 code review,
|
|
312
|
+
* inline-entity-combobox.tsx; the rules are refactor audit 2026-10-30, INP-6):
|
|
313
|
+
*
|
|
314
|
+
* - `showing`: the anchored list is on screen — open, not inert, with a row to show (an
|
|
315
|
+
* option, or `extraRow`, the create row). The list is then a layer on the Escape stack
|
|
316
|
+
* (Wave 2, DUP-1). Never on a phone, where the sheet is the list and the layer.
|
|
317
|
+
* - `expanded`, the field's `aria-expanded`: a list on screen. An open list with nothing
|
|
318
|
+
* to show said "expanded" with no listbox in the document.
|
|
319
|
+
* - `activeOption`: the id of the row the keyboard is on — `active`, a TAKEABLE row's
|
|
320
|
+
* index, else -1 — in whichever list shows, kept scrolled into view.
|
|
321
|
+
* - `statusRegion`: a polite live region with the row count, or "No results" for a query
|
|
322
|
+
* that left nothing to show or do. Focus stays in the field, or in the sheet's search
|
|
323
|
+
* box, so a list changing under it was silent. A list opened on no query asked nothing.
|
|
324
|
+
* Render it outside the list, so the list going cannot take it along: on a phone inside
|
|
325
|
+
* the sheet, which hides from a reader what lies outside it. There it mounts with the
|
|
326
|
+
* sheet, so the count the sheet opens with is not announced — as a ComboboxPanel's is
|
|
327
|
+
* not, in either shell — and every change after it is.
|
|
328
|
+
*/
|
|
329
|
+
export declare function useTypedListStatus({ isPhone, open, inert, rowCount, extraRow, query, active, optionId, labels, }: {
|
|
330
|
+
isPhone: boolean;
|
|
331
|
+
open: boolean;
|
|
332
|
+
inert: boolean;
|
|
333
|
+
/** The options the list shows. */
|
|
334
|
+
rowCount: number;
|
|
335
|
+
/** A create row shows: the list is not empty, it offers to fill itself. */
|
|
336
|
+
extraRow: boolean;
|
|
337
|
+
query: string;
|
|
338
|
+
active: number;
|
|
339
|
+
optionId: (index: number) => string;
|
|
340
|
+
labels: {
|
|
341
|
+
resultCount: (count: number) => string;
|
|
342
|
+
noResults: string;
|
|
343
|
+
};
|
|
344
|
+
}): {
|
|
345
|
+
showing: boolean;
|
|
346
|
+
expanded: boolean;
|
|
347
|
+
activeOption: string | undefined;
|
|
348
|
+
statusRegion: ReactNode;
|
|
349
|
+
};
|
|
240
350
|
/**
|
|
241
351
|
* How wide the anchored panel is. The phone sheet is the screen's width whatever this
|
|
242
352
|
* says.
|
|
@@ -294,7 +404,9 @@ export interface ComboboxPanelProps<V extends string | number> extends Component
|
|
|
294
404
|
*/
|
|
295
405
|
renderOption?: (o: ComboOption<V>) => ReactNode;
|
|
296
406
|
/** Controls between the search box and the list, in the anchored panel and the phone
|
|
297
|
-
* sheet alike — MultiSelect's "Select all" and "Clear".
|
|
407
|
+
* sheet alike — MultiSelect's "Select all" and "Clear". In the anchored panel Tab from
|
|
408
|
+
* the search box walks them before it leaves (0.52); keep them focusable when they
|
|
409
|
+
* have nothing to do (`aria-disabled`), or a press drops their focus to <body>. */
|
|
298
410
|
toolbar?: ReactNode;
|
|
299
411
|
/** See {@link ComboboxPanelWidth}. */
|
|
300
412
|
width?: ComboboxPanelWidth;
|
|
@@ -5,6 +5,7 @@ import { createPortal } from "react-dom";
|
|
|
5
5
|
import { Check, Plus } from "lucide-react";
|
|
6
6
|
import { cn } from "../lib/cn.js";
|
|
7
7
|
import { CLIPS_ATTRIBUTE } from "../lib/clipping.js";
|
|
8
|
+
import { tabStops } from "../lib/focus-left.js";
|
|
8
9
|
import { DropdownSearchHeader } from "./dropdown.js";
|
|
9
10
|
import { PickerSheet, SHEET_ROW_CLASS } from "./picker-sheet.js";
|
|
10
11
|
import { usePhoneLayout, useRemPx } from "../hooks/use-breakpoint.js";
|
|
@@ -15,7 +16,7 @@ import { anchoredInline, useAnchoredPanel, viewportSize } from "../hooks/use-anc
|
|
|
15
16
|
import { pressInside, takeEscape, useDismissLayer, useOutsideClick } from "../hooks/use-dismiss.js";
|
|
16
17
|
import { useOverlayHistory } from "../hooks/use-overlay-history.js";
|
|
17
18
|
import { isImeEnter } from "../lib/ime.js";
|
|
18
|
-
import { matchesSearch, searchWords } from "../lib/text-match.js";
|
|
19
|
+
import { matchesSearch, sameName, searchWords } from "../lib/text-match.js";
|
|
19
20
|
import { DEFAULT_COMBOBOX_LABELS, useKitLabels, useKitLocale } from "../i18n/kit-labels.js";
|
|
20
21
|
import { hasContent } from "../lib/presence.js";
|
|
21
22
|
const isOptionEnabled = (o) => o !== void 0 && !o.disabled;
|
|
@@ -34,11 +35,10 @@ function settleEnabled(rows, i) {
|
|
|
34
35
|
return before !== i ? before : -1;
|
|
35
36
|
}
|
|
36
37
|
const DISABLED_ROW_CLASS = "cursor-not-allowed opacity-50 hover:bg-transparent";
|
|
37
|
-
function offersCreate(onCreate, query, names, createEmptyLabel) {
|
|
38
|
+
function offersCreate(onCreate, query, names, createEmptyLabel, locale) {
|
|
38
39
|
if (!onCreate) return false;
|
|
39
40
|
if (!query) return Boolean(createEmptyLabel);
|
|
40
|
-
|
|
41
|
-
return !names.some((n) => n.toLowerCase() === q);
|
|
41
|
+
return !names.some((n) => sameName(n, query, locale));
|
|
42
42
|
}
|
|
43
43
|
function endHintRowProps(hintPassed, hasLabel, labelHint) {
|
|
44
44
|
if (hasLabel) return { hint: void 0 };
|
|
@@ -180,6 +180,69 @@ function useComboboxCore({
|
|
|
180
180
|
};
|
|
181
181
|
}
|
|
182
182
|
const GROUP_HEADING_STICKY = "sticky top-0 z-10 bg-surface";
|
|
183
|
+
function groupRuns(rows, groupOf) {
|
|
184
|
+
const runs = [];
|
|
185
|
+
rows.forEach((row, i) => {
|
|
186
|
+
const raw = groupOf(row);
|
|
187
|
+
const group = raw === void 0 || raw === null || raw === "" ? null : raw;
|
|
188
|
+
const last = runs[runs.length - 1];
|
|
189
|
+
if (last && last.group === group) last.rows.push(row);
|
|
190
|
+
else runs.push({ group, start: i, rows: [row] });
|
|
191
|
+
});
|
|
192
|
+
return runs;
|
|
193
|
+
}
|
|
194
|
+
function OptionGroup({
|
|
195
|
+
id,
|
|
196
|
+
heading,
|
|
197
|
+
className,
|
|
198
|
+
children
|
|
199
|
+
}) {
|
|
200
|
+
return /* @__PURE__ */ jsx("li", { role: "presentation", children: /* @__PURE__ */ jsxs("ul", { role: "group", "aria-labelledby": id, children: [
|
|
201
|
+
/* @__PURE__ */ jsx(
|
|
202
|
+
"li",
|
|
203
|
+
{
|
|
204
|
+
role: "presentation",
|
|
205
|
+
id,
|
|
206
|
+
className: cn(GROUP_HEADING_STICKY, "font-semibold uppercase tracking-wide text-muted", className),
|
|
207
|
+
children: heading
|
|
208
|
+
}
|
|
209
|
+
),
|
|
210
|
+
children
|
|
211
|
+
] }) });
|
|
212
|
+
}
|
|
213
|
+
function GroupedOptions({
|
|
214
|
+
runs,
|
|
215
|
+
listboxId,
|
|
216
|
+
rowKey,
|
|
217
|
+
row,
|
|
218
|
+
headingClassName
|
|
219
|
+
}) {
|
|
220
|
+
return keyRuns(runs).map(({ run, key }) => {
|
|
221
|
+
const grouped = run.group !== null;
|
|
222
|
+
const rows = run.rows.map((o, k) => /* @__PURE__ */ jsx(Fragment2, { children: row(o, run.start + k, grouped) }, rowKey(o)));
|
|
223
|
+
if (!grouped) return /* @__PURE__ */ jsx(Fragment2, { children: rows }, key);
|
|
224
|
+
return /* @__PURE__ */ jsx(
|
|
225
|
+
OptionGroup,
|
|
226
|
+
{
|
|
227
|
+
id: `${listboxId}-group-${run.start}`,
|
|
228
|
+
heading: run.group,
|
|
229
|
+
className: headingClassName(run),
|
|
230
|
+
children: rows
|
|
231
|
+
},
|
|
232
|
+
key
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function keyRuns(runs) {
|
|
237
|
+
const seen = /* @__PURE__ */ new Map();
|
|
238
|
+
return runs.map((run) => {
|
|
239
|
+
const nth = seen.get(run.group) ?? 0;
|
|
240
|
+
seen.set(run.group, nth + 1);
|
|
241
|
+
return { run, key: run.group === null ? String(nth) : `${nth}:${run.group}` };
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const anchoredHeadingClass = (run, first = true) => cn("px-3 pb-0.5 text-caption", run.start === 0 && first ? "pt-1" : "pt-2");
|
|
245
|
+
const SHEET_HEADING_CLASS = "px-4 pb-0.5 pt-3 text-caption";
|
|
183
246
|
const SUGGESTION_LIST_CLASS = "max-h-64 overflow-y-auto rounded-md border border-subtle bg-surface py-1 shadow-lg";
|
|
184
247
|
const suggestionRowClass = (isActive) => cn(
|
|
185
248
|
"block w-full truncate-until-large px-3 py-1.5 text-start text-sm text-primary",
|
|
@@ -191,6 +254,25 @@ function useActiveOptionScroll(activeId) {
|
|
|
191
254
|
document.getElementById(activeId)?.scrollIntoView?.({ block: "nearest" });
|
|
192
255
|
}, [activeId]);
|
|
193
256
|
}
|
|
257
|
+
function useTypedListStatus({
|
|
258
|
+
isPhone,
|
|
259
|
+
open,
|
|
260
|
+
inert,
|
|
261
|
+
rowCount,
|
|
262
|
+
extraRow,
|
|
263
|
+
query,
|
|
264
|
+
active,
|
|
265
|
+
optionId,
|
|
266
|
+
labels
|
|
267
|
+
}) {
|
|
268
|
+
const showing = !isPhone && open && !inert && (rowCount > 0 || extraRow);
|
|
269
|
+
const expanded = isPhone ? open && !inert : showing;
|
|
270
|
+
const activeOption = expanded && active >= 0 ? optionId(active) : void 0;
|
|
271
|
+
useActiveOptionScroll(activeOption);
|
|
272
|
+
const status = !open || inert ? null : rowCount > 0 ? labels.resultCount(rowCount) : query && !extraRow ? labels.noResults : null;
|
|
273
|
+
const statusRegion = /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "sr-only", children: status });
|
|
274
|
+
return { showing, expanded, activeOption, statusRegion };
|
|
275
|
+
}
|
|
194
276
|
const PANEL_MIN_REM = 13.75;
|
|
195
277
|
const PANEL_EDGE = 8;
|
|
196
278
|
function ComboboxPanel({
|
|
@@ -256,8 +338,36 @@ function ComboboxPanel({
|
|
|
256
338
|
observer.observe(el);
|
|
257
339
|
return () => observer.disconnect();
|
|
258
340
|
}, [fit, placed, panelRef]);
|
|
341
|
+
const toolbarRef = useRef(null);
|
|
342
|
+
const toolbarStops = () => toolbarRef.current ? tabStops(toolbarRef.current, { includeSelf: false }) : [];
|
|
343
|
+
const toolbarBox = hasContent(toolbar) && // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- never focused itself: it only keeps a pointer press on its buttons from taking the focus out of the search box.
|
|
344
|
+
/* @__PURE__ */ jsx(
|
|
345
|
+
"div",
|
|
346
|
+
{
|
|
347
|
+
ref: toolbarRef,
|
|
348
|
+
onMouseDown: (e) => {
|
|
349
|
+
if (!e.target.closest("input, select, textarea, [contenteditable]")) e.preventDefault();
|
|
350
|
+
},
|
|
351
|
+
children: toolbar
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
const tabInToolbar = (e) => {
|
|
355
|
+
const stops = toolbarStops();
|
|
356
|
+
const at2 = stops.indexOf(e.target);
|
|
357
|
+
if (e.shiftKey && at2 === 0) {
|
|
358
|
+
e.preventDefault();
|
|
359
|
+
inputRef.current?.focus();
|
|
360
|
+
} else if (!e.shiftKey && at2 === stops.length - 1) {
|
|
361
|
+
e.preventDefault();
|
|
362
|
+
closeToTrigger();
|
|
363
|
+
}
|
|
364
|
+
};
|
|
259
365
|
const onKeyDown = (e) => {
|
|
260
366
|
if (takeEscape(e)) return;
|
|
367
|
+
if (toolbarRef.current?.contains(e.target)) {
|
|
368
|
+
if (e.key === "Tab" && !isPhone) tabInToolbar(e);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
261
371
|
if (isImeEnter(e)) return;
|
|
262
372
|
if (e.key === "ArrowDown") {
|
|
263
373
|
e.preventDefault();
|
|
@@ -278,7 +388,9 @@ function ComboboxPanel({
|
|
|
278
388
|
else if (showCreate && !createLocked) onCreate();
|
|
279
389
|
} else if (e.key === "Tab" && !isPhone) {
|
|
280
390
|
e.preventDefault();
|
|
281
|
-
|
|
391
|
+
const first = e.shiftKey ? void 0 : toolbarStops()[0];
|
|
392
|
+
if (first) first.focus();
|
|
393
|
+
else closeToTrigger();
|
|
282
394
|
}
|
|
283
395
|
};
|
|
284
396
|
const message = busy && results.length === 0 ? (
|
|
@@ -399,15 +511,7 @@ function ComboboxPanel({
|
|
|
399
511
|
]
|
|
400
512
|
}
|
|
401
513
|
) });
|
|
402
|
-
const
|
|
403
|
-
if (columns && !isPhone) {
|
|
404
|
-
results.forEach((o, i) => {
|
|
405
|
-
const group = o.group ?? "";
|
|
406
|
-
const last = blocks[blocks.length - 1];
|
|
407
|
-
if (last && last.group === group) last.rows.push({ o, i });
|
|
408
|
-
else blocks.push({ group, rows: [{ o, i }] });
|
|
409
|
-
});
|
|
410
|
-
}
|
|
514
|
+
const runs = groupRuns(results, (o) => o.group);
|
|
411
515
|
const list = /* @__PURE__ */ jsxs(
|
|
412
516
|
"ul",
|
|
413
517
|
{
|
|
@@ -422,37 +526,32 @@ function ComboboxPanel({
|
|
|
422
526
|
isPhone && "flex-none"
|
|
423
527
|
),
|
|
424
528
|
children: [
|
|
425
|
-
columns && !isPhone ?
|
|
529
|
+
columns && !isPhone ? keyRuns(runs).map(({ run, key }) => (
|
|
426
530
|
// A column block: its heading is a picture of the group's name, which the
|
|
427
531
|
// `role="group"` carries for a reader — the listbox may own options and
|
|
428
|
-
// groups, and a heading of loose text in it is neither.
|
|
532
|
+
// groups, and a heading of loose text in it is neither. Keyed as
|
|
533
|
+
// GroupedOptions keys a run, so a filter does not rebuild the later columns.
|
|
429
534
|
/* @__PURE__ */ jsxs("li", { role: "presentation", className: "mb-3 break-inside-avoid", children: [
|
|
430
|
-
|
|
431
|
-
/* @__PURE__ */ jsx("ul", { role: "group", "aria-label":
|
|
432
|
-
] },
|
|
433
|
-
)) :
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
),
|
|
452
|
-
optionRow(o, i)
|
|
453
|
-
] }, `${o.group ?? ""}|${String(o.value)}`)
|
|
454
|
-
);
|
|
455
|
-
}),
|
|
535
|
+
run.group !== null && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted", children: run.group }),
|
|
536
|
+
/* @__PURE__ */ jsx("ul", { role: "group", "aria-label": run.group ?? void 0, className: "space-y-0.5", children: run.rows.map((o, k) => /* @__PURE__ */ jsx(Fragment2, { children: optionRow(o, run.start + k) }, String(o.value))) })
|
|
537
|
+
] }, key)
|
|
538
|
+
)) : (
|
|
539
|
+
// One heading per group rather than a grey suffix on every row (#136), and the
|
|
540
|
+
// thing the native <select>'s <optgroup> used to give the account picker for
|
|
541
|
+
// free (live #200) — a `role="group"` it names (INP-8). Keksdose repeats
|
|
542
|
+
// recently-used categories in a "Recent" group at the top (live #203): a value
|
|
543
|
+
// is a row's key within its group alone.
|
|
544
|
+
/* @__PURE__ */ jsx(
|
|
545
|
+
GroupedOptions,
|
|
546
|
+
{
|
|
547
|
+
runs,
|
|
548
|
+
listboxId,
|
|
549
|
+
rowKey: (o) => String(o.value),
|
|
550
|
+
row: optionRow,
|
|
551
|
+
headingClassName: (run) => isPhone ? "px-4 pb-1 pt-3 text-xs" : anchoredHeadingClass(run)
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
),
|
|
456
555
|
createRow
|
|
457
556
|
]
|
|
458
557
|
}
|
|
@@ -466,7 +565,7 @@ function ComboboxPanel({
|
|
|
466
565
|
className,
|
|
467
566
|
style,
|
|
468
567
|
open: true,
|
|
469
|
-
onClose:
|
|
568
|
+
onClose: closeToTrigger,
|
|
470
569
|
title: sheetTitle,
|
|
471
570
|
query,
|
|
472
571
|
onQueryChange: setQuery,
|
|
@@ -479,7 +578,7 @@ function ComboboxPanel({
|
|
|
479
578
|
activeId,
|
|
480
579
|
done: multi,
|
|
481
580
|
children: [
|
|
482
|
-
|
|
581
|
+
toolbarBox,
|
|
483
582
|
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
484
583
|
list,
|
|
485
584
|
announcement
|
|
@@ -536,7 +635,7 @@ function ComboboxPanel({
|
|
|
536
635
|
activeId
|
|
537
636
|
}
|
|
538
637
|
),
|
|
539
|
-
|
|
638
|
+
toolbarBox,
|
|
540
639
|
list,
|
|
541
640
|
announcement
|
|
542
641
|
]
|
|
@@ -550,8 +649,13 @@ export {
|
|
|
550
649
|
ComboboxPanel,
|
|
551
650
|
DISABLED_ROW_CLASS,
|
|
552
651
|
GROUP_HEADING_STICKY,
|
|
652
|
+
GroupedOptions,
|
|
653
|
+
OptionGroup,
|
|
654
|
+
SHEET_HEADING_CLASS,
|
|
553
655
|
SUGGESTION_LIST_CLASS,
|
|
656
|
+
anchoredHeadingClass,
|
|
554
657
|
endHintRowProps,
|
|
658
|
+
groupRuns,
|
|
555
659
|
isOptionEnabled,
|
|
556
660
|
offersCreate,
|
|
557
661
|
settleEnabled,
|
|
@@ -559,6 +663,7 @@ export {
|
|
|
559
663
|
suggestionRowClass,
|
|
560
664
|
useActiveOptionScroll,
|
|
561
665
|
useComboboxCore,
|
|
562
|
-
useOptionSource
|
|
666
|
+
useOptionSource,
|
|
667
|
+
useTypedListStatus
|
|
563
668
|
};
|
|
564
669
|
//# sourceMappingURL=combobox-core.js.map
|