@eifi1/ui-kit 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -76
- package/dist/admin/plan-change.js +2 -2
- package/dist/admin/plan-change.js.map +1 -1
- package/dist/admin/user-roster.d.ts +6 -0
- package/dist/admin/user-roster.js +4 -0
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +26 -0
- package/dist/billing/billing-labels.js +9 -0
- package/dist/billing/billing-labels.js.map +1 -1
- package/dist/billing/subscription-standing.d.ts +61 -0
- package/dist/billing/subscription-standing.js +51 -0
- package/dist/billing/subscription-standing.js.map +1 -0
- package/dist/billing/usage-meter.d.ts +52 -0
- package/dist/billing/usage-meter.js +52 -0
- package/dist/billing/usage-meter.js.map +1 -0
- package/dist/chart.d.ts +1 -0
- package/dist/chart.js +4 -0
- package/dist/chart.js.map +1 -1
- package/dist/components/alert-banner.js +2 -3
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.js +3 -15
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.js +2 -2
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/bulk-action-bar.js +2 -1
- package/dist/components/bulk-action-bar.js.map +1 -1
- package/dist/components/button.d.ts +35 -1
- package/dist/components/button.js +5 -9
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar-heatmap.js +2 -2
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/camera-button.d.ts +89 -0
- package/dist/components/camera-button.js +122 -0
- package/dist/components/camera-button.js.map +1 -0
- package/dist/components/camera-capture.d.ts +66 -0
- package/dist/components/camera-capture.js +190 -0
- package/dist/components/camera-capture.js.map +1 -0
- package/dist/components/camera-labels.d.ts +20 -0
- package/dist/components/camera-labels.js +15 -0
- package/dist/components/camera-labels.js.map +1 -0
- package/dist/components/chart-edge.d.ts +9 -0
- package/dist/components/chart-edge.js +14 -3
- package/dist/components/chart-edge.js.map +1 -1
- package/dist/components/chart-zoom-core.js +34 -29
- package/dist/components/chart-zoom-core.js.map +1 -1
- package/dist/components/checkbox-group.js +1 -1
- package/dist/components/checkbox-group.js.map +1 -1
- package/dist/components/combobox-core.js +3 -2
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.js +2 -2
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/consent-again-notice-labels.d.ts +25 -0
- package/dist/components/consent-again-notice-labels.js +15 -0
- package/dist/components/consent-again-notice-labels.js.map +1 -0
- package/dist/components/consent-again-notice.d.ts +85 -0
- package/dist/components/consent-again-notice.js +94 -0
- package/dist/components/consent-again-notice.js.map +1 -0
- package/dist/components/country-select.js +2 -7
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/currency-select.js +2 -2
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.js +2 -2
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-column-settings.js +2 -2
- package/dist/components/data-table-column-settings.js.map +1 -1
- package/dist/components/data-table-fit.js.map +1 -1
- package/dist/components/data-table-virtual.js +13 -3
- package/dist/components/data-table-virtual.js.map +1 -1
- package/dist/components/date-field.js +2 -2
- package/dist/components/date-field.js.map +1 -1
- package/dist/components/date-picker.js +2 -2
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/date-range-picker.js +2 -6
- package/dist/components/date-range-picker.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +3 -1
- package/dist/components/dialog-frame.js.map +1 -1
- package/dist/components/dropdown.js +7 -3
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/entity-combobox.js +2 -2
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/field-parts.d.ts +108 -6
- package/dist/components/field-parts.js +59 -6
- package/dist/components/field-parts.js.map +1 -1
- package/dist/components/field-suggestion-labels.d.ts +2 -0
- package/dist/components/field-suggestion-labels.js +10 -0
- package/dist/components/field-suggestion-labels.js.map +1 -0
- package/dist/components/field.js +1 -1
- package/dist/components/field.js.map +1 -1
- package/dist/components/file-button.d.ts +1 -1
- package/dist/components/file-button.js.map +1 -1
- package/dist/components/floating-panel.d.ts +7 -6
- package/dist/components/floating-panel.js +3 -17
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +3 -2
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/hover-menu.js +3 -2
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/icon-button.d.ts +23 -5
- package/dist/components/icon-button.js +6 -8
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/inline-confirm.js +2 -1
- package/dist/components/inline-confirm.js.map +1 -1
- package/dist/components/inline-entity-combobox.js +2 -2
- package/dist/components/inline-entity-combobox.js.map +1 -1
- package/dist/components/input.js +2 -2
- package/dist/components/input.js.map +1 -1
- package/dist/components/jump-calendar.js +3 -3
- package/dist/components/jump-calendar.js.map +1 -1
- package/dist/components/legal.d.ts +13 -1
- package/dist/components/legal.js +12 -0
- package/dist/components/legal.js.map +1 -1
- package/dist/components/legended-series-chart.d.ts +80 -0
- package/dist/components/legended-series-chart.js +70 -0
- package/dist/components/legended-series-chart.js.map +1 -0
- package/dist/components/lightbox.js +2 -1
- package/dist/components/lightbox.js.map +1 -1
- package/dist/components/mini-calendar.js +13 -5
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +23 -2
- package/dist/components/modal.js +24 -6
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +1 -1
- package/dist/components/month-picker.js +6 -6
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.js +2 -2
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.js +2 -6
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-input.js +2 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/page-header-layout.d.ts +18 -0
- package/dist/components/page-header-layout.js +23 -0
- package/dist/components/page-header-layout.js.map +1 -0
- package/dist/components/page-header-skeleton.d.ts +41 -0
- package/dist/components/page-header-skeleton.js +52 -0
- package/dist/components/page-header-skeleton.js.map +1 -0
- package/dist/components/page-header.js +4 -17
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/popover.js +7 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/release-notes-labels.d.ts +16 -0
- package/dist/components/release-notes-labels.js +18 -0
- package/dist/components/release-notes-labels.js.map +1 -0
- package/dist/components/release-notes.d.ts +65 -0
- package/dist/components/release-notes.js +65 -0
- package/dist/components/release-notes.js.map +1 -0
- package/dist/components/select.js +2 -2
- package/dist/components/select.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +9 -0
- package/dist/components/series-chart-labels.js +3 -1
- package/dist/components/series-chart-labels.js.map +1 -1
- package/dist/components/series-chart.d.ts +5 -0
- package/dist/components/series-chart.js +89 -36
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/toggle-group.js +3 -2
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/treemap-layout.js.map +1 -1
- package/dist/components/treemap.d.ts +17 -15
- package/dist/components/treemap.js +167 -85
- package/dist/components/treemap.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/eslint/role-utilities.d.ts +2 -2
- package/dist/eslint/role-utilities.js.map +1 -1
- package/dist/feedback/feedback-inbox-page.d.ts +260 -0
- package/dist/feedback/feedback-inbox-page.js +207 -0
- package/dist/feedback/feedback-inbox-page.js.map +1 -0
- package/dist/feedback/feedback-vocabulary.js +2 -2
- package/dist/feedback/feedback-vocabulary.js.map +1 -1
- package/dist/feedback.d.ts +1 -0
- package/dist/feedback.js +6 -0
- package/dist/feedback.js.map +1 -1
- package/dist/hooks/use-debounce.d.ts +32 -5
- package/dist/hooks/use-debounce.js +6 -5
- package/dist/hooks/use-debounce.js.map +1 -1
- package/dist/hooks/use-file-picker.d.ts +3 -1
- package/dist/hooks/use-file-picker.js.map +1 -1
- package/dist/hooks/use-focus-trap.d.ts +19 -3
- package/dist/hooks/use-focus-trap.js +15 -5
- package/dist/hooks/use-focus-trap.js.map +1 -1
- package/dist/hooks/use-has-camera.d.ts +36 -0
- package/dist/hooks/use-has-camera.js +53 -0
- package/dist/hooks/use-has-camera.js.map +1 -0
- package/dist/hooks/use-hotkey.js +1 -10
- package/dist/hooks/use-hotkey.js.map +1 -1
- package/dist/hooks/use-overlay-history.d.ts +6 -1
- package/dist/hooks/use-overlay-history.js +10 -2
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/i18n/defaults.js +11 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +75 -3
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +20 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +92 -4
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +71 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +72 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +74 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +74 -3
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +71 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +22 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +24 -5
- package/dist/index.js +35 -1
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-labels.d.ts +8 -0
- package/dist/landing/landing-labels.js +2 -1
- package/dist/landing/landing-labels.js.map +1 -1
- package/dist/lib/changelog.d.ts +130 -0
- package/dist/lib/changelog.js +137 -0
- package/dist/lib/changelog.js.map +1 -0
- package/dist/lib/clipboard-minus.js +2 -2
- package/dist/lib/clipboard-minus.js.map +1 -1
- package/dist/lib/dates.d.ts +29 -3
- package/dist/lib/dates.js +41 -12
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/editable-content.d.ts +33 -0
- package/dist/lib/editable-content.js +15 -0
- package/dist/lib/editable-content.js.map +1 -0
- package/dist/lib/focus-left.d.ts +47 -1
- package/dist/lib/focus-left.js +11 -2
- package/dist/lib/focus-left.js.map +1 -1
- package/dist/lib/server-wake.d.ts +49 -9
- package/dist/lib/server-wake.js +25 -14
- package/dist/lib/server-wake.js.map +1 -1
- package/dist/lib/session-refresh.d.ts +53 -0
- package/dist/lib/session-refresh.js +27 -1
- package/dist/lib/session-refresh.js.map +1 -1
- package/dist/rhf/fields.js +3 -3
- package/dist/rhf/fields.js.map +1 -1
- package/dist/shell/app-shell.d.ts +25 -1
- package/dist/shell/app-shell.js +3 -1
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/dev-menu.d.ts +3 -0
- package/dist/shell/dev-menu.js +4 -2
- package/dist/shell/dev-menu.js.map +1 -1
- package/dist/shell/held-changes-banner-labels.d.ts +23 -0
- package/dist/shell/held-changes-banner-labels.js +14 -0
- package/dist/shell/held-changes-banner-labels.js.map +1 -0
- package/dist/shell/held-changes-banner.d.ts +75 -0
- package/dist/shell/held-changes-banner.js +106 -0
- package/dist/shell/held-changes-banner.js.map +1 -0
- package/dist/shell/sync-status-indicator.d.ts +27 -12
- package/dist/shell/sync-status-indicator.js +13 -4
- package/dist/shell/sync-status-indicator.js.map +1 -1
- package/dist/shell/top-bar-parts-labels.d.ts +5 -0
- package/dist/shell/top-bar-parts-labels.js +3 -1
- package/dist/shell/top-bar-parts-labels.js.map +1 -1
- package/dist/shell.d.ts +2 -0
- package/dist/shell.js +3 -0
- package/dist/shell.js.map +1 -1
- package/dist/sync/engine.d.ts +11 -2
- package/dist/sync/engine.js +13 -5
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/hooks.d.ts +3 -2
- package/dist/sync/hooks.js +4 -2
- package/dist/sync/hooks.js.map +1 -1
- package/dist/testing/css-variables.generated.d.ts +2 -2
- package/dist/testing/css-variables.generated.js +1 -1
- package/dist/testing/css-variables.generated.js.map +1 -1
- package/dist/theme/chart-palette.d.ts +18 -0
- package/dist/theme/chart-palette.js +17 -1
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/contrast-tokens.d.ts +3 -3
- package/dist/theme/contrast-tokens.js.map +1 -1
- package/dist/theme/contrast.d.ts +4 -4
- package/dist/theme/contrast.js.map +1 -1
- package/dist/tour/tour-overlay.js +2 -1
- package/dist/tour/tour-overlay.js.map +1 -1
- package/package.json +5 -9
- package/tokens.css +19 -54
- package/src/account/account-parts.ts +0 -47
- package/src/account/account-settings-labels.ts +0 -240
- package/src/account/account-settings.tsx +0 -698
- package/src/account/data-export-setting-labels.ts +0 -20
- package/src/account/data-export-setting.tsx +0 -200
- package/src/account/delete-account-setting-labels.ts +0 -33
- package/src/account/delete-account-setting.tsx +0 -342
- package/src/account/email-change-setting-labels.ts +0 -31
- package/src/account/email-change-setting.tsx +0 -443
- package/src/account/passkeys-setting.tsx +0 -445
- package/src/account/sessions-setting-labels.ts +0 -24
- package/src/account/sessions-setting.tsx +0 -283
- package/src/admin/admin-action-confirm-labels.ts +0 -19
- package/src/admin/admin-action-confirm.tsx +0 -509
- package/src/admin/admin-action-log-labels.ts +0 -83
- package/src/admin/admin-action-log.tsx +0 -464
- package/src/admin/admin-area-labels.ts +0 -114
- package/src/admin/admin-forbidden.tsx +0 -75
- package/src/admin/admin-overview.tsx +0 -258
- package/src/admin/admin-parts.ts +0 -68
- package/src/admin/admin-sections.tsx +0 -208
- package/src/admin/admin-tool-links.tsx +0 -90
- package/src/admin/invitations-panel-labels.ts +0 -31
- package/src/admin/invitations-panel.tsx +0 -516
- package/src/admin/mail-test-card-labels.ts +0 -19
- package/src/admin/mail-test-card.tsx +0 -189
- package/src/admin/plan-change-labels.ts +0 -5
- package/src/admin/plan-change.tsx +0 -474
- package/src/admin/reviewer-scope-editor-labels.ts +0 -16
- package/src/admin/reviewer-scope-editor.tsx +0 -224
- package/src/admin/role-select-labels.ts +0 -16
- package/src/admin/role-select.tsx +0 -282
- package/src/admin/transfer-ownership-dialog-labels.ts +0 -20
- package/src/admin/transfer-ownership-dialog.tsx +0 -162
- package/src/admin/user-roster-labels.ts +0 -17
- package/src/admin/user-roster.tsx +0 -559
- package/src/auth/accept-invitation-labels.ts +0 -21
- package/src/auth/accept-invitation.tsx +0 -210
- package/src/auth/auth-errors-labels.ts +0 -23
- package/src/auth/auth-errors.ts +0 -439
- package/src/auth/complete-name-dialog-labels.ts +0 -16
- package/src/auth/complete-name-dialog.tsx +0 -253
- package/src/auth/email-tag.ts +0 -75
- package/src/auth/forgot-password-form-labels.ts +0 -20
- package/src/auth/forgot-password-form.tsx +0 -221
- package/src/auth/form-rules.ts +0 -40
- package/src/auth/new-password-fields.tsx +0 -85
- package/src/auth/not-found-page-labels.ts +0 -12
- package/src/auth/not-found-page.tsx +0 -95
- package/src/auth/register-form-labels.ts +0 -31
- package/src/auth/register-form.tsx +0 -456
- package/src/auth/reset-password-form-labels.ts +0 -27
- package/src/auth/reset-password-form.tsx +0 -341
- package/src/auth/sign-in-form-labels.ts +0 -44
- package/src/auth/sign-in-form.tsx +0 -858
- package/src/auth/status-parts.tsx +0 -132
- package/src/auth/verify-email-labels.ts +0 -26
- package/src/auth/verify-email.tsx +0 -445
- package/src/billing/billing-banner.tsx +0 -258
- package/src/billing/billing-labels.ts +0 -381
- package/src/billing/billing-lock.ts +0 -108
- package/src/billing/billing-overview.ts +0 -157
- package/src/billing/billing-standing.ts +0 -80
- package/src/billing/cancel-request.ts +0 -97
- package/src/billing/cancel-subscription.tsx +0 -98
- package/src/billing/checkout-processing.ts +0 -394
- package/src/billing/checkout-return.ts +0 -90
- package/src/billing/pay-page.ts +0 -330
- package/src/billing/plan-card.tsx +0 -562
- package/src/billing/plan-change-labels.ts +0 -66
- package/src/billing/plan-limit-notice.tsx +0 -148
- package/src/billing/plan-limit-toast.tsx +0 -90
- package/src/billing/plan-limit.ts +0 -52
- package/src/billing/plan-price.ts +0 -98
- package/src/billing/plan-switch.tsx +0 -216
- package/src/billing/provider-page.ts +0 -84
- package/src/billing/subscription-actions.tsx +0 -247
- package/src/billing/subscription-status-chip.tsx +0 -95
- package/src/chart.ts +0 -103
- package/src/components/account-chips-labels.ts +0 -16
- package/src/components/account-chips.tsx +0 -474
- package/src/components/alert-banner.tsx +0 -670
- package/src/components/amount-input.tsx +0 -816
- package/src/components/appearance-labels.ts +0 -63
- package/src/components/authed-image-labels.ts +0 -11
- package/src/components/authed-image.tsx +0 -209
- package/src/components/autocomplete.tsx +0 -645
- package/src/components/breadcrumbs-labels.ts +0 -10
- package/src/components/breadcrumbs.tsx +0 -205
- package/src/components/bulk-action-bar-labels.ts +0 -11
- package/src/components/bulk-action-bar.tsx +0 -443
- package/src/components/button-group.tsx +0 -223
- package/src/components/button-look.ts +0 -256
- package/src/components/button.tsx +0 -339
- package/src/components/calculator.tsx +0 -341
- package/src/components/calendar-heatmap-labels.ts +0 -15
- package/src/components/calendar-heatmap.tsx +0 -600
- package/src/components/card.tsx +0 -377
- package/src/components/chart-edge.ts +0 -106
- package/src/components/chart-figure.tsx +0 -35
- package/src/components/chart-height.ts +0 -30
- package/src/components/chart-zoom-core.tsx +0 -627
- package/src/components/chart-zoom.tsx +0 -22
- package/src/components/chart.tsx +0 -554
- package/src/components/checkbox-group.tsx +0 -247
- package/src/components/checkbox.tsx +0 -346
- package/src/components/chip-input.tsx +0 -370
- package/src/components/chip-labels.ts +0 -13
- package/src/components/chip.tsx +0 -843
- package/src/components/choice-card.tsx +0 -544
- package/src/components/column-mapper-labels.ts +0 -38
- package/src/components/column-mapper.tsx +0 -655
- package/src/components/combobox-core.tsx +0 -1321
- package/src/components/combobox-suggestions.tsx +0 -131
- package/src/components/combobox.tsx +0 -716
- package/src/components/confirm-context.ts +0 -7
- package/src/components/confirm-dialog-labels.ts +0 -13
- package/src/components/confirm-dialog.tsx +0 -341
- package/src/components/copy-button-labels.ts +0 -13
- package/src/components/copy-button.tsx +0 -192
- package/src/components/country-select-labels.ts +0 -11
- package/src/components/country-select.tsx +0 -539
- package/src/components/currency-option-list.tsx +0 -188
- package/src/components/currency-select.tsx +0 -343
- package/src/components/danger-confirm-labels.ts +0 -20
- package/src/components/danger-confirm.tsx +0 -987
- package/src/components/data-table-cells.tsx +0 -118
- package/src/components/data-table-column-settings.tsx +0 -198
- package/src/components/data-table-filter-popover.tsx +0 -289
- package/src/components/data-table-filters.ts +0 -315
- package/src/components/data-table-fit.ts +0 -301
- package/src/components/data-table-groups.tsx +0 -288
- package/src/components/data-table-head.tsx +0 -280
- package/src/components/data-table-labels.ts +0 -198
- package/src/components/data-table-leading-row.tsx +0 -58
- package/src/components/data-table-mobile.tsx +0 -1117
- package/src/components/data-table-pagination.tsx +0 -202
- package/src/components/data-table-resize.ts +0 -188
- package/src/components/data-table-row-actions.tsx +0 -302
- package/src/components/data-table-row.tsx +0 -496
- package/src/components/data-table-selection.ts +0 -71
- package/src/components/data-table-shared.tsx +0 -287
- package/src/components/data-table-sort.ts +0 -163
- package/src/components/data-table-virtual.ts +0 -576
- package/src/components/data-table.tsx +0 -2598
- package/src/components/date-field.tsx +0 -684
- package/src/components/date-format-setting.tsx +0 -64
- package/src/components/date-picker.tsx +0 -404
- package/src/components/date-range-picker.tsx +0 -518
- package/src/components/description-list-labels.ts +0 -9
- package/src/components/description-list.tsx +0 -386
- package/src/components/dialog-frame-labels.ts +0 -7
- package/src/components/dialog-frame.tsx +0 -284
- package/src/components/disclosure.tsx +0 -476
- package/src/components/dropdown.tsx +0 -461
- package/src/components/empty-state.tsx +0 -136
- package/src/components/entity-combobox.tsx +0 -422
- package/src/components/error-boundary-labels.ts +0 -20
- package/src/components/error-boundary.tsx +0 -675
- package/src/components/facing-pair.ts +0 -93
- package/src/components/field-base-labels.ts +0 -20
- package/src/components/field-base.tsx +0 -656
- package/src/components/field-parts.tsx +0 -825
- package/src/components/field-strip.tsx +0 -129
- package/src/components/field.tsx +0 -206
- package/src/components/file-button.tsx +0 -295
- package/src/components/file-dropzone.tsx +0 -559
- package/src/components/floating-panel-labels.ts +0 -10
- package/src/components/floating-panel.tsx +0 -828
- package/src/components/focus-ring.ts +0 -16
- package/src/components/form-actions-labels.ts +0 -11
- package/src/components/form-actions.tsx +0 -578
- package/src/components/full-bleed-dialog.tsx +0 -294
- package/src/components/grouped-picker.tsx +0 -184
- package/src/components/help-popover.tsx +0 -117
- package/src/components/hover-menu.tsx +0 -343
- package/src/components/iban-input-labels.ts +0 -14
- package/src/components/iban-input.tsx +0 -236
- package/src/components/icon-button.tsx +0 -959
- package/src/components/icon-picker-labels.ts +0 -13
- package/src/components/icon-picker.tsx +0 -232
- package/src/components/image-grid-labels.ts +0 -11
- package/src/components/image-grid.tsx +0 -202
- package/src/components/inline-confirm.tsx +0 -116
- package/src/components/inline-edit-field-labels.ts +0 -11
- package/src/components/inline-edit-field.tsx +0 -552
- package/src/components/inline-entity-combobox.tsx +0 -849
- package/src/components/input.tsx +0 -394
- package/src/components/jump-calendar.tsx +0 -265
- package/src/components/language-select.tsx +0 -93
- package/src/components/legal-labels.ts +0 -105
- package/src/components/legal-page.tsx +0 -711
- package/src/components/legal.tsx +0 -668
- package/src/components/lightbox-labels.ts +0 -18
- package/src/components/lightbox.tsx +0 -399
- package/src/components/line-items-labels.ts +0 -14
- package/src/components/line-items.tsx +0 -602
- package/src/components/list-labels.ts +0 -12
- package/src/components/list.tsx +0 -870
- package/src/components/loading-state.tsx +0 -78
- package/src/components/measured-grid-labels.ts +0 -25
- package/src/components/measured-grid.tsx +0 -837
- package/src/components/menu-item.tsx +0 -241
- package/src/components/mini-calendar-labels.ts +0 -17
- package/src/components/mini-calendar.tsx +0 -792
- package/src/components/modal.tsx +0 -534
- package/src/components/money-draft.ts +0 -83
- package/src/components/money-field.tsx +0 -76
- package/src/components/month-grid.tsx +0 -229
- package/src/components/month-picker-labels.ts +0 -22
- package/src/components/month-picker.tsx +0 -1087
- package/src/components/multi-entity-combobox.tsx +0 -369
- package/src/components/multi-select.tsx +0 -376
- package/src/components/nav-pills.tsx +0 -198
- package/src/components/number-field.tsx +0 -327
- package/src/components/number-input.tsx +0 -559
- package/src/components/numeric-field.ts +0 -169
- package/src/components/numpad-sheet.tsx +0 -285
- package/src/components/one-time-code-input.tsx +0 -270
- package/src/components/page-contents-labels.ts +0 -9
- package/src/components/page-contents.tsx +0 -282
- package/src/components/page-header-context.ts +0 -8
- package/src/components/page-header.tsx +0 -298
- package/src/components/password-strength-labels.ts +0 -22
- package/src/components/password-strength.tsx +0 -241
- package/src/components/phone-input-labels.ts +0 -10
- package/src/components/phone-input.tsx +0 -282
- package/src/components/picker-sheet.tsx +0 -330
- package/src/components/pie-chart-labels.ts +0 -40
- package/src/components/pie-chart.tsx +0 -480
- package/src/components/popover-labels.ts +0 -14
- package/src/components/popover.tsx +0 -268
- package/src/components/progress-bar-labels.ts +0 -10
- package/src/components/progress-bar.tsx +0 -479
- package/src/components/qr-code.tsx +0 -76
- package/src/components/reauth-dialog-labels.ts +0 -13
- package/src/components/reauth-dialog.tsx +0 -265
- package/src/components/row-actions-labels.ts +0 -12
- package/src/components/row-actions.tsx +0 -795
- package/src/components/scroll-area.tsx +0 -123
- package/src/components/search-field.tsx +0 -192
- package/src/components/select.tsx +0 -294
- package/src/components/separator.tsx +0 -33
- package/src/components/series-chart-axes.ts +0 -342
- package/src/components/series-chart-budget.ts +0 -150
- package/src/components/series-chart-labels.ts +0 -33
- package/src/components/series-chart-thin.ts +0 -180
- package/src/components/series-chart-ticks.ts +0 -194
- package/src/components/series-chart.tsx +0 -1906
- package/src/components/server-wake-labels.ts +0 -11
- package/src/components/server-wake.tsx +0 -129
- package/src/components/settings-fields.tsx +0 -237
- package/src/components/share-card-labels.ts +0 -30
- package/src/components/share-card.tsx +0 -675
- package/src/components/signature-pad-labels.ts +0 -24
- package/src/components/signature-pad.tsx +0 -747
- package/src/components/signed-amount-labels.ts +0 -10
- package/src/components/signed-amount.tsx +0 -336
- package/src/components/skeleton.tsx +0 -80
- package/src/components/slider.tsx +0 -338
- package/src/components/sparkline-labels.ts +0 -14
- package/src/components/sparkline.tsx +0 -322
- package/src/components/spinner.tsx +0 -81
- package/src/components/stat-tile-labels.ts +0 -15
- package/src/components/stat-tile.tsx +0 -754
- package/src/components/status-dot.tsx +0 -170
- package/src/components/swatch-picker-labels.ts +0 -10
- package/src/components/swatch-picker.tsx +0 -283
- package/src/components/swipeable-row.tsx +0 -386
- package/src/components/switch.tsx +0 -273
- package/src/components/table-skeleton.tsx +0 -136
- package/src/components/table.tsx +0 -647
- package/src/components/tabs-labels.ts +0 -13
- package/src/components/tabs.tsx +0 -693
- package/src/components/text-link.tsx +0 -359
- package/src/components/text.tsx +0 -150
- package/src/components/textarea.tsx +0 -330
- package/src/components/tile-radio.tsx +0 -332
- package/src/components/time-input.tsx +0 -121
- package/src/components/toast-labels.ts +0 -12
- package/src/components/toast.tsx +0 -519
- package/src/components/toggle-group.tsx +0 -752
- package/src/components/toggle-legend.tsx +0 -394
- package/src/components/tooltip.tsx +0 -1149
- package/src/components/translation-review-editor.tsx +0 -290
- package/src/components/translation-review-labels.ts +0 -181
- package/src/components/translation-review-parts.tsx +0 -272
- package/src/components/translation-review.tsx +0 -1240
- package/src/components/tree-view.tsx +0 -589
- package/src/components/treemap-labels.ts +0 -47
- package/src/components/treemap-layout.ts +0 -210
- package/src/components/treemap.tsx +0 -1184
- package/src/components/trigger-aria.ts +0 -44
- package/src/components/use-anchor-dir.ts +0 -20
- package/src/components/use-mobile-reveal.ts +0 -62
- package/src/components/use-table-state.ts +0 -753
- package/src/components/user-avatar.tsx +0 -116
- package/src/components/wizard-stepper.tsx +0 -42
- package/src/components/write-lock-labels.ts +0 -10
- package/src/components/write-lock.tsx +0 -253
- package/src/currencies.ts +0 -90
- package/src/data-table.ts +0 -42
- package/src/demo/demo-banner.tsx +0 -153
- package/src/demo/demo-countdown.ts +0 -136
- package/src/demo/demo-ended.tsx +0 -90
- package/src/demo/demo-labels.ts +0 -78
- package/src/demo/demo-session.ts +0 -32
- package/src/demo/demo-start.tsx +0 -156
- package/src/eslint/index.ts +0 -161
- package/src/eslint/role-utilities.generated.ts +0 -146
- package/src/eslint/role-utilities.ts +0 -233
- package/src/feedback/feedback-attachment-download.tsx +0 -77
- package/src/feedback/feedback-attachment-labels.ts +0 -37
- package/src/feedback/feedback-attachment.tsx +0 -1239
- package/src/feedback/feedback-capture.ts +0 -287
- package/src/feedback/feedback-comment-thread.tsx +0 -353
- package/src/feedback/feedback-context-labels.ts +0 -11
- package/src/feedback/feedback-context.tsx +0 -245
- package/src/feedback/feedback-crash.ts +0 -547
- package/src/feedback/feedback-diagnostics.tsx +0 -41
- package/src/feedback/feedback-dialog-labels.ts +0 -29
- package/src/feedback/feedback-dialog.tsx +0 -452
- package/src/feedback/feedback-entry-file.tsx +0 -150
- package/src/feedback/feedback-inbox.tsx +0 -552
- package/src/feedback/feedback-labels.ts +0 -144
- package/src/feedback/feedback-menu-labels.ts +0 -12
- package/src/feedback/feedback-menu.tsx +0 -177
- package/src/feedback/feedback-mutation.ts +0 -20
- package/src/feedback/feedback-page-link.ts +0 -90
- package/src/feedback/feedback-public-labels.ts +0 -81
- package/src/feedback/feedback-public-list.tsx +0 -524
- package/src/feedback/feedback-public-switch.tsx +0 -102
- package/src/feedback/feedback-public-types.ts +0 -101
- package/src/feedback/feedback-record.ts +0 -428
- package/src/feedback/feedback-replies-context.ts +0 -9
- package/src/feedback/feedback-replies.tsx +0 -343
- package/src/feedback/feedback-rework-entry-chip.tsx +0 -18
- package/src/feedback/feedback-row-detail-labels.ts +0 -31
- package/src/feedback/feedback-row-detail.tsx +0 -871
- package/src/feedback/feedback-status-undo.ts +0 -109
- package/src/feedback/feedback-submit-dialog.tsx +0 -131
- package/src/feedback/feedback-submit.tsx +0 -270
- package/src/feedback/feedback-swipe.tsx +0 -192
- package/src/feedback/feedback-table-labels.ts +0 -27
- package/src/feedback/feedback-table.tsx +0 -912
- package/src/feedback/feedback-thread-labels.ts +0 -22
- package/src/feedback/feedback-thread-parts-labels.ts +0 -41
- package/src/feedback/feedback-thread.tsx +0 -905
- package/src/feedback/feedback-vocabulary.ts +0 -263
- package/src/feedback/feedback-vote-button.tsx +0 -124
- package/src/feedback.ts +0 -116
- package/src/hooks/use-anchored-panel.ts +0 -396
- package/src/hooks/use-anchored-rect.ts +0 -77
- package/src/hooks/use-announce.ts +0 -85
- package/src/hooks/use-authed-src.ts +0 -167
- package/src/hooks/use-body-scroll-lock.ts +0 -119
- package/src/hooks/use-breakpoint.ts +0 -133
- package/src/hooks/use-close-transition.ts +0 -107
- package/src/hooks/use-copy-to-clipboard.ts +0 -122
- package/src/hooks/use-debounce.ts +0 -114
- package/src/hooks/use-dismiss.ts +0 -371
- package/src/hooks/use-document-title.ts +0 -33
- package/src/hooks/use-file-drop.ts +0 -121
- package/src/hooks/use-file-picker-labels.ts +0 -28
- package/src/hooks/use-file-picker.tsx +0 -359
- package/src/hooks/use-focus-trap.ts +0 -199
- package/src/hooks/use-hotkey.ts +0 -212
- package/src/hooks/use-large-text.ts +0 -41
- package/src/hooks/use-long-press.ts +0 -140
- package/src/hooks/use-media-query.ts +0 -39
- package/src/hooks/use-mounted.ts +0 -39
- package/src/hooks/use-noindex.ts +0 -37
- package/src/hooks/use-overlay-history.ts +0 -714
- package/src/hooks/use-row-swipe.ts +0 -207
- package/src/hooks/use-search-param-state.ts +0 -434
- package/src/hooks/use-windowed-rows.ts +0 -225
- package/src/i18n/defaults.ts +0 -267
- package/src/i18n/german.ts +0 -2059
- package/src/i18n/kit-labels.tsx +0 -1003
- package/src/i18n/languages.ts +0 -349
- package/src/i18n/locales/de-CH.ts +0 -24
- package/src/i18n/locales/en.ts +0 -267
- package/src/i18n/locales/es.ts +0 -1999
- package/src/i18n/locales/fr.ts +0 -2037
- package/src/i18n/locales/hu.ts +0 -2004
- package/src/i18n/locales/it.ts +0 -2014
- package/src/i18n/locales/zh.ts +0 -1912
- package/src/i18n/plurals.ts +0 -86
- package/src/i18n/review.ts +0 -475
- package/src/i18n/shared-labels.ts +0 -138
- package/src/i18n/swiss.ts +0 -29
- package/src/index.ts +0 -1191
- package/src/landing/access.ts +0 -93
- package/src/landing/landing-actions.tsx +0 -97
- package/src/landing/landing-labels.ts +0 -59
- package/src/landing/landing-sections.tsx +0 -341
- package/src/landing/page-seo.ts +0 -261
- package/src/landing/public-header.tsx +0 -121
- package/src/landing/public-page.tsx +0 -106
- package/src/landing/routing.tsx +0 -225
- package/src/lib/broadcast.ts +0 -52
- package/src/lib/calc.ts +0 -323
- package/src/lib/chip-edge-snap.ts +0 -212
- package/src/lib/clipboard-minus.ts +0 -107
- package/src/lib/clipping.ts +0 -34
- package/src/lib/cn.ts +0 -28
- package/src/lib/column-mapping.ts +0 -234
- package/src/lib/countries.ts +0 -91
- package/src/lib/data-attributes.ts +0 -14
- package/src/lib/date-format.ts +0 -227
- package/src/lib/dates.ts +0 -288
- package/src/lib/day-format.ts +0 -96
- package/src/lib/decimal-marks.ts +0 -108
- package/src/lib/describe-failure.ts +0 -18
- package/src/lib/device-settings.ts +0 -147
- package/src/lib/direction.ts +0 -74
- package/src/lib/document-navigation.ts +0 -17
- package/src/lib/document-title.ts +0 -41
- package/src/lib/download.ts +0 -172
- package/src/lib/float-clearance.ts +0 -70
- package/src/lib/floating-stack.ts +0 -70
- package/src/lib/focus-left.ts +0 -156
- package/src/lib/format.ts +0 -585
- package/src/lib/http-status.ts +0 -38
- package/src/lib/iban.ts +0 -251
- package/src/lib/ime.ts +0 -24
- package/src/lib/intl-cache.ts +0 -27
- package/src/lib/iso-day.ts +0 -13
- package/src/lib/lazy-with-fallback.ts +0 -82
- package/src/lib/logger.ts +0 -69
- package/src/lib/minus.ts +0 -50
- package/src/lib/path-pattern.ts +0 -55
- package/src/lib/person-name.ts +0 -155
- package/src/lib/phone.ts +0 -208
- package/src/lib/plain-text.ts +0 -22
- package/src/lib/presence.ts +0 -18
- package/src/lib/qr-encode.ts +0 -444
- package/src/lib/quiet-focus.ts +0 -32
- package/src/lib/reduced-motion.ts +0 -22
- package/src/lib/refs.ts +0 -22
- package/src/lib/round.ts +0 -43
- package/src/lib/safe-storage.ts +0 -119
- package/src/lib/server-wake.ts +0 -315
- package/src/lib/session-refresh.ts +0 -472
- package/src/lib/share-format.ts +0 -18
- package/src/lib/status-tone.ts +0 -43
- package/src/lib/strip-fade.ts +0 -287
- package/src/lib/table-text.ts +0 -554
- package/src/lib/text-case.ts +0 -19
- package/src/lib/text-match.ts +0 -169
- package/src/lib/thenable.ts +0 -19
- package/src/lib/translation-review.ts +0 -769
- package/src/notifications/device-zone-report.ts +0 -67
- package/src/notifications/feedback-notification-types-labels.ts +0 -33
- package/src/notifications/feedback-notification-types.ts +0 -71
- package/src/notifications/notification-bell.tsx +0 -119
- package/src/notifications/notification-inbox.tsx +0 -309
- package/src/notifications/notification-parts.tsx +0 -81
- package/src/notifications/notification-types-setting.tsx +0 -406
- package/src/notifications/notification-types.ts +0 -89
- package/src/notifications/notifications-labels.ts +0 -287
- package/src/notifications/push-device-setting.tsx +0 -230
- package/src/notifications/push-devices-setting.tsx +0 -374
- package/src/notifications/push-worker.ts +0 -168
- package/src/notifications/quiet-hours-setting.tsx +0 -238
- package/src/notifications/unsubscribe-panel.tsx +0 -200
- package/src/notifications/use-push-device.ts +0 -448
- package/src/offline/local-first.ts +0 -100
- package/src/offline/pwa-update-prompt-labels.ts +0 -9
- package/src/offline/pwa-update-prompt.tsx +0 -78
- package/src/offline/query-persistence.ts +0 -391
- package/src/offline/service-worker-updates.ts +0 -81
- package/src/offline/shared-computer-labels.ts +0 -8
- package/src/offline/shared-computer.tsx +0 -87
- package/src/offline/sign-out-pending-dialog-labels.ts +0 -21
- package/src/offline/sign-out-pending-dialog.tsx +0 -135
- package/src/offline.ts +0 -37
- package/src/push-worker.ts +0 -25
- package/src/rhf/fields.tsx +0 -2187
- package/src/rhf/form-core.tsx +0 -324
- package/src/rhf/form.tsx +0 -12
- package/src/rhf/line-items.tsx +0 -135
- package/src/rhf/use-rhf-wizard-step.ts +0 -104
- package/src/rhf.ts +0 -21
- package/src/search/command-palette-labels.ts +0 -16
- package/src/search/command-palette.tsx +0 -624
- package/src/search/global-search-labels.ts +0 -13
- package/src/search/global-search.tsx +0 -660
- package/src/search/search-index.ts +0 -283
- package/src/search/use-command-key.ts +0 -23
- package/src/search.ts +0 -14
- package/src/settings/device-reset-setting-labels.ts +0 -20
- package/src/settings/device-reset-setting.tsx +0 -116
- package/src/settings/settings-card-classes.ts +0 -11
- package/src/settings/settings-catalogue.ts +0 -150
- package/src/settings/settings-context.ts +0 -33
- package/src/settings/settings-heading.tsx +0 -67
- package/src/settings/settings-labels.ts +0 -87
- package/src/settings/settings-layout.tsx +0 -471
- package/src/settings/settings-section.tsx +0 -108
- package/src/settings/use-account-appearance.ts +0 -148
- package/src/settings/use-account-language.ts +0 -146
- package/src/settings/use-settings-focus.ts +0 -128
- package/src/settings/use-settings-route.ts +0 -258
- package/src/shell/account-menu-language.tsx +0 -130
- package/src/shell/account-menu-text-size.tsx +0 -80
- package/src/shell/admin-menu.tsx +0 -183
- package/src/shell/app-shell-labels.ts +0 -11
- package/src/shell/app-shell.tsx +0 -1560
- package/src/shell/auth-layout.tsx +0 -186
- package/src/shell/company-switcher-labels.ts +0 -13
- package/src/shell/company-switcher.tsx +0 -136
- package/src/shell/dev-menu.tsx +0 -257
- package/src/shell/fill-height.tsx +0 -45
- package/src/shell/language-code.ts +0 -5
- package/src/shell/option-switcher-menu.tsx +0 -84
- package/src/shell/sync-status-indicator.tsx +0 -303
- package/src/shell/top-bar-actions.tsx +0 -87
- package/src/shell/top-bar-brand.tsx +0 -78
- package/src/shell/top-bar-parts-labels.ts +0 -148
- package/src/shell/top-bar.tsx +0 -40
- package/src/shell/topbar-action-menu.tsx +0 -567
- package/src/shell/topbar-controls.tsx +0 -269
- package/src/shell/tour-menu.tsx +0 -187
- package/src/shell.ts +0 -108
- package/src/sync/db.ts +0 -129
- package/src/sync/engine.ts +0 -1011
- package/src/sync/hooks.ts +0 -98
- package/src/sync/testing.ts +0 -73
- package/src/sync/wire.ts +0 -61
- package/src/sync.ts +0 -37
- package/src/table-text.ts +0 -8
- package/src/testing/css-variables.generated.ts +0 -112
- package/src/testing/css-variables.ts +0 -189
- package/src/testing/index.ts +0 -29
- package/src/testing/react.ts +0 -58
- package/src/testing/tour.ts +0 -41
- package/src/testing/viewport.ts +0 -357
- package/src/theme/chart-palette.ts +0 -232
- package/src/theme/color.ts +0 -272
- package/src/theme/contrast-tokens.ts +0 -268
- package/src/theme/contrast.ts +0 -250
- package/src/theme/palette-derive.ts +0 -825
- package/src/theme/palette-presets.ts +0 -544
- package/src/theme/palette-store.ts +0 -108
- package/src/theme/text-size.ts +0 -289
- package/src/theme/theme-store.ts +0 -157
- package/src/tour/tour-catalogue-labels.ts +0 -75
- package/src/tour/tour-catalogue.tsx +0 -335
- package/src/tour/tour-dock.ts +0 -134
- package/src/tour/tour-engine.ts +0 -37
- package/src/tour/tour-gates.ts +0 -218
- package/src/tour/tour-guide-button.tsx +0 -64
- package/src/tour/tour-guide-labels.ts +0 -15
- package/src/tour/tour-info.ts +0 -144
- package/src/tour/tour-labels.ts +0 -23
- package/src/tour/tour-overlay.tsx +0 -1170
- package/src/tour/tour.tsx +0 -750
- package/src/tour/use-tour-progress.ts +0 -671
- package/src/tour/use-tour-url-sync.ts +0 -266
- package/src/tour.ts +0 -21
- package/src/vite-env.d.ts +0 -23
- package/src/wizard/stepper-nav.tsx +0 -360
- package/src/wizard/types.ts +0 -275
- package/src/wizard/use-wizard.ts +0 -560
- package/src/wizard/validation.ts +0 -39
- package/src/wizard/wizard-context.tsx +0 -74
- package/src/wizard/wizard-labels.ts +0 -29
- package/src/wizard/wizard-step.tsx +0 -76
- package/src/wizard/wizard-summary.tsx +0 -84
- package/src/wizard.ts +0 -15
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
// Chip's edge snapping: the module-level engine (one ResizeObserver, one `resize`
|
|
2
|
-
// listener and one resolution query for every snapped chip on the page, and the state
|
|
3
|
-
// they share) and the hook a chip hangs it on. Moved out of components/chip.tsx as it
|
|
4
|
-
// was (MOD-9, refactor audit 2026-10); chip.tsx still exports `refreshChipEdges`.
|
|
5
|
-
import { useCallback, useLayoutEffect, useRef } from "react";
|
|
6
|
-
|
|
7
|
-
/* ── Edge snapping (fractional device-pixel ratios) ──────────────────────────
|
|
8
|
-
*
|
|
9
|
-
* WHY. At a device-pixel ratio of 1.25 (a 125 % Windows display, or 125 % browser
|
|
10
|
-
* zoom) Chrome snaps a box to whole CSS pixels and only then scales it, so a 1px
|
|
11
|
-
* border is 1.25 device pixels wide and starts at a quarter-pixel phase that depends
|
|
12
|
-
* on where the edge lands. A chip's left edge follows whatever stands before it, its
|
|
13
|
-
* right edge follows its own text, so the two land on unrelated phases: one rasterises
|
|
14
|
-
* as a crisp pixel, the other as a two-pixel smear at half strength — and a 60 %-alpha
|
|
15
|
-
* border (`--danger-border`) makes the smeared side read as a THINNER line (keksdose
|
|
16
|
-
* dev #576).
|
|
17
|
-
*
|
|
18
|
-
* WHAT FIXES IT. The two edges rasterise as mirror images exactly when their device
|
|
19
|
-
* positions sum to a whole number: `(2·left + width) · dpr` integral, `left` being the
|
|
20
|
-
* edge's snapped CSS x. Rounding the WIDTH alone to the ratio's grid (4px at 1.25)
|
|
21
|
-
* makes the phases EQUAL, which is not the same thing: measured with keksdose's
|
|
22
|
-
* playwright harness across every left phase at 1.25, width-only rounding mirrored the
|
|
23
|
-
* edges at two of the four phases and left the other two as lopsided as before (mean
|
|
24
|
-
* peak asymmetry 16.5, worse than the unsnapped 10.9 a random width gets by luck); the
|
|
25
|
-
* position-aware width below took it to 0 at every phase. Inset rings, whole-pixel
|
|
26
|
-
* starts, compositing hints and fractional border widths were measured by keksdose
|
|
27
|
-
* and do not help — Chrome rounds a sub-pixel border back up to 1px.
|
|
28
|
-
*
|
|
29
|
-
* So the fix needs the chip's position, which is why it is opt-in (`snapEdges`): it
|
|
30
|
-
* reads layout. Cheaply — every snapped chip on the page shares one ResizeObserver,
|
|
31
|
-
* one `resize` listener and one resolution media query, and a pass is batched into a
|
|
32
|
-
* frame as read-all / write-all, not a forced layout per chip. At an integer ratio,
|
|
33
|
-
* and at a half one (1.5, 2.5: grid 2 — measured symmetric already, both edges a
|
|
34
|
-
* pixel and a half on the same pattern), there is nothing to fix and a pass
|
|
35
|
-
* writes nothing.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
/** The smallest whole CSS-pixel count `n` with `n × dpr` whole — 4 at 1.25, 2 at 1.5,
|
|
39
|
-
* 1 at an integer ratio. 0 for a ratio no step up to 4px fits (110 % zoom wants 10px;
|
|
40
|
-
* a chip growing by up to 9px is a layout change, not a hairline fix). */
|
|
41
|
-
function chipEdgeGrid(dpr: number): number {
|
|
42
|
-
for (let n = 1; n <= 4; n++) if (Math.abs(n * dpr - Math.round(n * dpr)) < 1e-3) return n;
|
|
43
|
-
return 0;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* The width (whole CSS px, at least `natural`) that mirrors a box's two edges at
|
|
48
|
-
* `dpr`, for a box whose left edge sits at CSS x `left`. `natural` back when the ratio
|
|
49
|
-
* needs nothing.
|
|
50
|
-
*/
|
|
51
|
-
function snappedChipEdgeWidth(natural: number, left: number, dpr: number): number {
|
|
52
|
-
const n = chipEdgeGrid(dpr);
|
|
53
|
-
if (n <= 2) return natural;
|
|
54
|
-
const l = Math.round(left);
|
|
55
|
-
// A hair under a whole pixel is that pixel: 59.999 is a 60px chip, not a 61px one.
|
|
56
|
-
let w = Math.ceil(natural - 0.01);
|
|
57
|
-
while ((((2 * l + w) % n) + n) % n !== 0) w++;
|
|
58
|
-
return w;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const snapped = new Set<HTMLElement>();
|
|
62
|
-
/** The min-width each chip was given by a pass — so a pass only ever clears its own. */
|
|
63
|
-
const written = new WeakMap<HTMLElement, string>();
|
|
64
|
-
let snapFrame = 0;
|
|
65
|
-
let snapObserver: ResizeObserver | null = null;
|
|
66
|
-
let ratioQuery: MediaQueryList | null = null;
|
|
67
|
-
|
|
68
|
-
function clearOwn(el: HTMLElement) {
|
|
69
|
-
const own = written.get(el);
|
|
70
|
-
if (own !== undefined && el.style.minWidth === own) el.style.minWidth = "";
|
|
71
|
-
written.delete(el);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Re-snap every chip rendered with `snapEdges`, now. The kit already re-snaps when a
|
|
76
|
-
* chip renders, resizes, or the window or the ratio changes; call this after moving
|
|
77
|
-
* chips some other way (a side panel sliding in beside a table of status chips),
|
|
78
|
-
* since a move is not something an observer can see.
|
|
79
|
-
*/
|
|
80
|
-
export function refreshChipEdges(): void {
|
|
81
|
-
if (snapFrame) {
|
|
82
|
-
cancelAnimationFrame(snapFrame);
|
|
83
|
-
snapFrame = 0;
|
|
84
|
-
}
|
|
85
|
-
if (typeof window === "undefined" || !snapped.size) return;
|
|
86
|
-
const dpr = window.devicePixelRatio || 1;
|
|
87
|
-
const els = [...snapped];
|
|
88
|
-
// Write: back to the natural width, so what is read below is the chip, not our pin.
|
|
89
|
-
for (const el of els) clearOwn(el);
|
|
90
|
-
const n = chipEdgeGrid(dpr);
|
|
91
|
-
if (n <= 2) return;
|
|
92
|
-
// A chip whose own end decides where it starts (RTL, `justify-end`) moves when it
|
|
93
|
-
// grows, so a pass re-reads what it wrote and steps on by a pixel until the edges
|
|
94
|
-
// agree — `n` steps cover every residue. Each pass is ONE layout for every chip
|
|
95
|
-
// together. A chip that never agrees (a centred one moves both edges at once) is
|
|
96
|
-
// given its natural width back rather than kept wider for nothing.
|
|
97
|
-
let pending = els;
|
|
98
|
-
for (let pass = 0; pass <= n && pending.length; pass++) {
|
|
99
|
-
const reads = pending.map((el) => {
|
|
100
|
-
const r = el.getBoundingClientRect();
|
|
101
|
-
// The text label (chip.tsx's CHIP_LABEL_TRUNCATE; `.truncate` before 0.33).
|
|
102
|
-
const label = el.querySelector<HTMLElement>(".truncate-until-large");
|
|
103
|
-
// Not laid out (a hidden ancestor, jsdom), or truncated at its container's edge,
|
|
104
|
-
// where a wider chip would overflow: leave it alone. So too a label wrapped onto
|
|
105
|
-
// a second line at Large (0.32.1): the chip is already as wide as its row lets it
|
|
106
|
-
// be, and a min-width past that would push it over the edge just the same.
|
|
107
|
-
if (!r.width || (label && (label.scrollWidth > label.clientWidth || labelWraps(label)))) return null;
|
|
108
|
-
const width = Math.round(r.width * 64) / 64;
|
|
109
|
-
const target = snappedChipEdgeWidth(width, r.left + window.scrollX, dpr);
|
|
110
|
-
return target === width ? null : target;
|
|
111
|
-
});
|
|
112
|
-
const next: HTMLElement[] = [];
|
|
113
|
-
pending.forEach((el, i) => {
|
|
114
|
-
const target = reads[i];
|
|
115
|
-
if (target == null) return;
|
|
116
|
-
if (pass === n) {
|
|
117
|
-
clearOwn(el);
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
const own = written.get(el);
|
|
121
|
-
const value = `${pass > 0 && own !== undefined ? parseFloat(own) + 1 : target}px`;
|
|
122
|
-
el.style.minWidth = value;
|
|
123
|
-
written.set(el, value);
|
|
124
|
-
next.push(el);
|
|
125
|
-
});
|
|
126
|
-
pending = next;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Whether a chip's label runs to more than one line — taller than one and a half of
|
|
131
|
-
* its line height. A line height of `normal` (no number to compare) counts as one line. */
|
|
132
|
-
function labelWraps(label: HTMLElement): boolean {
|
|
133
|
-
const line = Number.parseFloat(getComputedStyle(label).lineHeight);
|
|
134
|
-
return line > 0 && label.clientHeight > 1.5 * line;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function scheduleChipEdges() {
|
|
138
|
-
if (snapFrame || typeof requestAnimationFrame === "undefined") return;
|
|
139
|
-
snapFrame = requestAnimationFrame(() => {
|
|
140
|
-
snapFrame = 0;
|
|
141
|
-
refreshChipEdges();
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** The resolution query for the CURRENT ratio: it stops matching when the ratio
|
|
146
|
-
* changes (a window dragged to another display), which a `resize` need not fire. */
|
|
147
|
-
function watchRatio() {
|
|
148
|
-
ratioQuery?.removeEventListener("change", onRatioChange);
|
|
149
|
-
ratioQuery =
|
|
150
|
-
typeof window.matchMedia === "function"
|
|
151
|
-
? window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`)
|
|
152
|
-
: null;
|
|
153
|
-
ratioQuery?.addEventListener("change", onRatioChange);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function onRatioChange() {
|
|
157
|
-
watchRatio();
|
|
158
|
-
scheduleChipEdges();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function registerSnap(el: HTMLElement) {
|
|
162
|
-
if (snapped.has(el)) return;
|
|
163
|
-
if (!snapped.size) {
|
|
164
|
-
if (typeof ResizeObserver !== "undefined") snapObserver = new ResizeObserver(scheduleChipEdges);
|
|
165
|
-
window.addEventListener("resize", scheduleChipEdges);
|
|
166
|
-
watchRatio();
|
|
167
|
-
}
|
|
168
|
-
snapped.add(el);
|
|
169
|
-
snapObserver?.observe(el);
|
|
170
|
-
scheduleChipEdges();
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function unregisterSnap(el: HTMLElement) {
|
|
174
|
-
if (!snapped.delete(el)) return;
|
|
175
|
-
snapObserver?.unobserve(el);
|
|
176
|
-
clearOwn(el);
|
|
177
|
-
if (snapped.size) return;
|
|
178
|
-
snapObserver?.disconnect();
|
|
179
|
-
snapObserver = null;
|
|
180
|
-
window.removeEventListener("resize", scheduleChipEdges);
|
|
181
|
-
ratioQuery?.removeEventListener("change", onRatioChange);
|
|
182
|
-
ratioQuery = null;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/** The ref a chip hangs on its visible pill, plus a re-snap after every render (its
|
|
186
|
-
* label or its neighbours may have changed). */
|
|
187
|
-
export function useSnapEdges(on: boolean) {
|
|
188
|
-
const node = useRef<HTMLElement | null>(null);
|
|
189
|
-
const ref = useCallback(
|
|
190
|
-
(el: HTMLElement | null) => {
|
|
191
|
-
if (el === node.current) return;
|
|
192
|
-
const prev = node.current;
|
|
193
|
-
node.current = el;
|
|
194
|
-
// React detaches and re-attaches a ref whose callback changed identity (a
|
|
195
|
-
// consumer's inline ref) — the same element, a moment apart. Unregistering on
|
|
196
|
-
// the detach would clear the pin and paint one frame unsnapped, so the drop
|
|
197
|
-
// waits a microtask and is skipped if the element came straight back.
|
|
198
|
-
if (prev) queueMicrotask(() => node.current !== prev && unregisterSnap(prev));
|
|
199
|
-
if (el && on) registerSnap(el);
|
|
200
|
-
},
|
|
201
|
-
[on],
|
|
202
|
-
);
|
|
203
|
-
useLayoutEffect(() => {
|
|
204
|
-
const el = node.current;
|
|
205
|
-
if (!el) return;
|
|
206
|
-
if (on) {
|
|
207
|
-
registerSnap(el);
|
|
208
|
-
scheduleChipEdges();
|
|
209
|
-
} else unregisterSnap(el);
|
|
210
|
-
});
|
|
211
|
-
return ref;
|
|
212
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The copy handler for the one minus sign (0.49 KIT-1b, Marcel's follow-up to KIT-1).
|
|
3
|
-
*
|
|
4
|
-
* The kit prints a negative with U+2212 (lib/minus.ts). A spreadsheet does not read that
|
|
5
|
-
* as a number: a figure copied out of a kit table and pasted into Excel landed as text.
|
|
6
|
-
* So while a selection inside the page is copied, every U+2212 standing directly before a
|
|
7
|
-
* number — optionally after a currency code or symbol and spaces: "−CHF 1,250.50",
|
|
8
|
-
* "−€2,000", "−US$12", "−12%" — becomes the ASCII "-" in the clipboard, in `text/plain` AND
|
|
9
|
-
* in `text/html` (Excel pastes the HTML flavour, as cells). The page keeps "−".
|
|
10
|
-
*
|
|
11
|
-
* A copy with no such minus is left to the browser untouched, and so is any copy out of an
|
|
12
|
-
* input, a textarea or an editable element: what the person typed is theirs.
|
|
13
|
-
*
|
|
14
|
-
* Installed once per document by `UiKitProvider` (`copyAsciiMinus`, default `true`) —
|
|
15
|
-
* the one part every app mounts, the signed-out pages included.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* U+2212, then up to five letters, currency symbols or dots, then spaces, then a digit.
|
|
20
|
-
*
|
|
21
|
-
* Five of them, not "a code or one symbol": English and Chinese write a currency with
|
|
22
|
-
* letters before the symbol — "−US$", "−CA$", "−A$", "−NZ$", "−HK$", "−R$", "−MX$",
|
|
23
|
-
* "−JP¥", "−CN¥" — and `narrowSymbol` with letters alone ("−kr", "−zł", "−Kč"). Those kept
|
|
24
|
-
* U+2212 in the clipboard, and Excel pasted them as text: 35 of 351 locale × currency
|
|
25
|
-
* pairs at the default display, every one English or Chinese, and 136 of 1428 with
|
|
26
|
-
* `narrowSymbol` (refactor audit 2026-10-30, DSP-11). A minus with no digit after it
|
|
27
|
-
* ("x−y", "a − b") is still left alone.
|
|
28
|
-
*/
|
|
29
|
-
const MINUS_BEFORE_NUMBER = /\u2212(?=[\p{L}\p{Sc}.]{0,5}[\s\u00a0\u202f]*\d)/gu;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* `text` with each U+2212 that stands before a number (in the sense above) as the ASCII
|
|
33
|
-
* hyphen-minus, every other character as it was — for text an app hands a machine that
|
|
34
|
-
* did not come from a formatter's `minus: "ascii"`.
|
|
35
|
-
*/
|
|
36
|
-
export function asciiMinusInNumbers(text: string): string {
|
|
37
|
-
return text.replace(MINUS_BEFORE_NUMBER, "-");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function hasMinusBeforeNumber(text: string): boolean {
|
|
41
|
-
MINUS_BEFORE_NUMBER.lastIndex = 0;
|
|
42
|
-
const found = MINUS_BEFORE_NUMBER.test(text);
|
|
43
|
-
MINUS_BEFORE_NUMBER.lastIndex = 0;
|
|
44
|
-
return found;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** An input, a textarea or an editable element: a copy there is the person's own text. */
|
|
48
|
-
function isEditable(node: Node | null | undefined): boolean {
|
|
49
|
-
const el = node instanceof Element ? node : node?.parentElement;
|
|
50
|
-
if (!el) return false;
|
|
51
|
-
if (el.closest("input, textarea, select")) return true;
|
|
52
|
-
return (
|
|
53
|
-
(el as HTMLElement).isContentEditable === true ||
|
|
54
|
-
el.closest('[contenteditable=""], [contenteditable="true"]') !== null
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Table parts copied alone are wrapped back into a table, which Excel reads as cells. */
|
|
59
|
-
function wrapTableParts(container: HTMLElement): string {
|
|
60
|
-
const first = container.firstElementChild?.tagName;
|
|
61
|
-
const html = container.innerHTML;
|
|
62
|
-
if (first === "TD" || first === "TH") return `<table><tbody><tr>${html}</tr></tbody></table>`;
|
|
63
|
-
if (first === "TR") return `<table><tbody>${html}</tbody></table>`;
|
|
64
|
-
if (first === "TBODY" || first === "THEAD" || first === "TFOOT") return `<table>${html}</table>`;
|
|
65
|
-
return html;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function onCopy(event: ClipboardEvent): void {
|
|
69
|
-
if (event.defaultPrevented || !event.clipboardData) return;
|
|
70
|
-
const doc = (event.target as Node | null)?.ownerDocument ?? document;
|
|
71
|
-
if (isEditable(event.target as Node | null) || isEditable(doc.activeElement)) return;
|
|
72
|
-
const selection = doc.getSelection();
|
|
73
|
-
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
|
|
74
|
-
if (isEditable(selection.anchorNode) || isEditable(selection.focusNode)) return;
|
|
75
|
-
const text = selection.toString();
|
|
76
|
-
if (!hasMinusBeforeNumber(text)) return;
|
|
77
|
-
|
|
78
|
-
const container = doc.createElement("div");
|
|
79
|
-
for (let i = 0; i < selection.rangeCount; i++) container.appendChild(selection.getRangeAt(i).cloneContents());
|
|
80
|
-
const walker = doc.createTreeWalker(container, NodeFilter.SHOW_TEXT);
|
|
81
|
-
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
82
|
-
node.nodeValue = asciiMinusInNumbers(node.nodeValue ?? "");
|
|
83
|
-
}
|
|
84
|
-
event.clipboardData.setData("text/plain", asciiMinusInNumbers(text));
|
|
85
|
-
event.clipboardData.setData("text/html", wrapTableParts(container));
|
|
86
|
-
event.preventDefault();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const installs = new WeakMap<Document, number>();
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Listen for copies on `doc` until the returned function is called. Counted: nested
|
|
93
|
-
* providers install it once, and the last one to go removes it.
|
|
94
|
-
*/
|
|
95
|
-
export function installClipboardMinus(doc: Document = document): () => void {
|
|
96
|
-
const count = installs.get(doc) ?? 0;
|
|
97
|
-
if (count === 0) doc.addEventListener("copy", onCopy);
|
|
98
|
-
installs.set(doc, count + 1);
|
|
99
|
-
let done = false;
|
|
100
|
-
return () => {
|
|
101
|
-
if (done) return;
|
|
102
|
-
done = true;
|
|
103
|
-
const left = (installs.get(doc) ?? 1) - 1;
|
|
104
|
-
installs.set(doc, left);
|
|
105
|
-
if (left === 0) doc.removeEventListener("copy", onCopy);
|
|
106
|
-
};
|
|
107
|
-
}
|
package/src/lib/clipping.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/** Internal (not exported from the package): the Tooltip's auto-portal check. The
|
|
2
|
-
* attribute name itself IS exported, as `CLIPS_ATTRIBUTE` from the tooltip module. */
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The explicit "this element clips or scrolls" marker. A container carrying it counts
|
|
6
|
-
* as clipping whatever its computed `overflow` says.
|
|
7
|
-
*
|
|
8
|
-
* Why a marker as well as the computed style: the kit's containers get their
|
|
9
|
-
* `overflow` from Tailwind classes, and jsdom computes no stylesheet — so under test
|
|
10
|
-
* every scroller reads `overflow: visible`, the Tooltip stays in place, and its always-
|
|
11
|
-
* mounted bubble shows up in the cell's text (keksdose 0.10: "CheckingChecking"). The
|
|
12
|
-
* browser and the test then disagreed about where the bubble lives, and apps pinned
|
|
13
|
-
* `portal` on every tooltip in a table to make them agree. The kit's own scrollers
|
|
14
|
-
* (DataTable's body, Table's wrapper) carry the marker, so the answer is the same in
|
|
15
|
-
* both; an app marks its own with `{ [CLIPS_ATTRIBUTE]: "" }` or a literal `data-clips`.
|
|
16
|
-
*/
|
|
17
|
-
export const CLIPS_ATTRIBUTE = "data-clips";
|
|
18
|
-
|
|
19
|
-
/** Whether anything between `el` and `<body>` clips or scrolls its content — the
|
|
20
|
-
* containers an absolutely positioned bubble would be cut off by, or would widen.
|
|
21
|
-
* Either the computed `overflow` says so or the element carries
|
|
22
|
-
* {@link CLIPS_ATTRIBUTE}. `<body>` and `<html>` are not counted: their `overflow`
|
|
23
|
-
* belongs to the viewport (a dialog's scroll lock sets it), and the viewport clips a
|
|
24
|
-
* portalled bubble just the same. */
|
|
25
|
-
export function hasClippingAncestor(el: Element | null): boolean {
|
|
26
|
-
if (typeof window === "undefined" || !el) return false;
|
|
27
|
-
for (let node = el.parentElement; node; node = node.parentElement) {
|
|
28
|
-
if (node === document.body || node === document.documentElement) return false;
|
|
29
|
-
if (node.hasAttribute(CLIPS_ATTRIBUTE)) return true;
|
|
30
|
-
const style = window.getComputedStyle(node);
|
|
31
|
-
if (style.overflowX !== "visible" || style.overflowY !== "visible") return true;
|
|
32
|
-
}
|
|
33
|
-
return false;
|
|
34
|
-
}
|
package/src/lib/cn.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { clsx } from "clsx";
|
|
2
|
-
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* tailwind-merge, told about the kit's own font sizes (docs/text-size-harmonization.md
|
|
6
|
-
* §3.2): `text-micro` and `text-caption` are `@theme` sizes in tokens.css, which
|
|
7
|
-
* tailwind-merge cannot see. Unknown, a `text-<word>` is taken for a COLOUR, so
|
|
8
|
-
* `cn("text-caption text-[var(--text-muted)]")` dropped the size — the later colour
|
|
9
|
-
* "won" over it — and `cn("text-xs", "text-caption")` kept both sizes. Registered as
|
|
10
|
-
* sizes, they merge like `text-xs`: a later size replaces an earlier one, a colour sits
|
|
11
|
-
* beside them.
|
|
12
|
-
*
|
|
13
|
-
* The same for the focus frame's width (0.34, refactor audit DOC-7): `ring-focus` and
|
|
14
|
-
* `outline-focus` are `@theme inline` WIDTHS, and unknown, a `ring-<word>` is taken for a
|
|
15
|
-
* colour — so `cn(FOCUS_RING, "focus-visible:ring-strong")` dropped the width and the
|
|
16
|
-
* ring was gone. Registered as widths, they merge like `ring-2` / `outline-2`, as the
|
|
17
|
-
* arbitrary `length:` widths they replaced did. (No class is spelt out here: an app's
|
|
18
|
-
* Tailwind scans the built source maps too, and would generate it.)
|
|
19
|
-
*/
|
|
20
|
-
const merge = extendTailwindMerge({
|
|
21
|
-
extend: {
|
|
22
|
-
theme: { text: ["micro", "caption"] },
|
|
23
|
-
classGroups: { "ring-w": [{ ring: ["focus"] }], "outline-w": [{ outline: ["focus"] }] },
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
/** Merge class lists with Tailwind conflict resolution (later wins). */
|
|
28
|
-
export const cn = (...inputs: Parameters<typeof clsx>) => merge(clsx(inputs));
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Which column of a table plays which part — the headless half of {@link ColumnMapper}.
|
|
3
|
-
*
|
|
4
|
-
* Two apps built this by hand, the same way. Kurvenschmiede's columns input
|
|
5
|
-
* (`features/control/columns-input.tsx`) reads a recorder's export and asks which
|
|
6
|
-
* column is the time, the setpoint and the actual; keksdose's bank-file import
|
|
7
|
-
* (`import-map-step.tsx`, `use-file-import.ts`) shows a bank's CSV and asks which
|
|
8
|
-
* column is the booking date, the amount, the payee. Both then have the same four
|
|
9
|
-
* rules, and keksdose wrote them down after getting each one wrong once:
|
|
10
|
-
*
|
|
11
|
-
* * **A role sits on one column at most, and a column carries one role.** Picking a
|
|
12
|
-
* role for a column MOVES it there from wherever it was ({@link assignColumnRole});
|
|
13
|
-
* the column it left falls back to "ignore" in the same step. There is no error
|
|
14
|
-
* state, because there is nothing wrong to report.
|
|
15
|
-
* * **What is still missing is named**, not left to a disabled button
|
|
16
|
-
* ({@link missingRoles}). A required role can be one of a group: a bank export that
|
|
17
|
-
* splits debit and credit has no amount column and needs none, so `required` may
|
|
18
|
-
* name a group, satisfied by any one of its members (keksdose's "money").
|
|
19
|
-
* * **A mapping is indexes into ONE table.** When the column count changes, the
|
|
20
|
-
* indexes mean nothing any more and are guessed afresh; when it does not (the
|
|
21
|
-
* header line toggled), they still point where they did — keksdose's `keepRoles`.
|
|
22
|
-
* * **The rows a role cannot read are reported by line**, not dropped in silence
|
|
23
|
-
* ({@link readMappedTable}): a footer under a recording, a total under a statement.
|
|
24
|
-
*
|
|
25
|
-
* Pure functions over plain data, so a two-step wizard can hold the mapping in its own
|
|
26
|
-
* state between steps, and a table the server sniffed (keksdose's) can be mapped with
|
|
27
|
-
* the same rules as one parsed here.
|
|
28
|
-
*/
|
|
29
|
-
import { tableNumber } from "./table-text";
|
|
30
|
-
import type { TableLine, TextTable } from "./table-text";
|
|
31
|
-
|
|
32
|
-
/** One part a column can play, defined by the caller. */
|
|
33
|
-
export interface ColumnRole<R extends string = string> {
|
|
34
|
-
/** The key the mapping is stored under. */
|
|
35
|
-
value: R;
|
|
36
|
-
/** What the role select shows — translated by the caller. */
|
|
37
|
-
label: string;
|
|
38
|
-
/**
|
|
39
|
-
* `true`: the table cannot be used until a column plays this role. A string names a
|
|
40
|
-
* GROUP: the table needs any one of the roles that share it — keksdose's amount,
|
|
41
|
-
* debit and credit, any of which carries the money.
|
|
42
|
-
*/
|
|
43
|
-
required?: boolean | string;
|
|
44
|
-
/**
|
|
45
|
-
* Every cell this role reads must be a number in the table's decimal convention
|
|
46
|
-
* ({@link tableNumber}). A row where one is not — a footer, a units line, a total
|
|
47
|
-
* with a grouping mark — is moved to `unread` with its line number instead of being
|
|
48
|
-
* handed over. Kurvenschmiede's time, setpoint and actual. The cells stay strings:
|
|
49
|
-
* converting is the caller's, with `tableNumber(cell, result.decimalComma)`.
|
|
50
|
-
*/
|
|
51
|
-
numeric?: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Role → column index. A role that is absent or `null` sits on no column — so a
|
|
55
|
-
* mapping with nullable fields (keksdose's `CsvMapping`) is one as it stands. */
|
|
56
|
-
export type ColumnMapping<R extends string = string> = Partial<Record<R, number | null>>;
|
|
57
|
-
|
|
58
|
-
/** What the table is, read through a mapping. */
|
|
59
|
-
export interface ColumnMapperResult<R extends string = string> extends TextTable {
|
|
60
|
-
mapping: ColumnMapping<R>;
|
|
61
|
-
/** What the table still needs before it can be used: one entry per gap, each the
|
|
62
|
-
* roles any of which would fill it (one role, or a required group). */
|
|
63
|
-
missing: R[][];
|
|
64
|
-
/** Every required role placed, and at least one row read. */
|
|
65
|
-
complete: boolean;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const placedAt = <R extends string>(mapping: ColumnMapping<R>, role: R, width?: number): number | null => {
|
|
69
|
-
const column = mapping[role];
|
|
70
|
-
if (typeof column !== "number") return null;
|
|
71
|
-
return width === undefined || column < width ? column : null;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
/** The role on this column, or `null` for an ignored one. */
|
|
75
|
-
export function roleOfColumn<R extends string>(
|
|
76
|
-
roles: readonly ColumnRole<R>[],
|
|
77
|
-
mapping: ColumnMapping<R>,
|
|
78
|
-
column: number,
|
|
79
|
-
): R | null {
|
|
80
|
-
return roles.find((role) => mapping[role.value] === column)?.value ?? null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Put `role` on `column` — or, with `null`, take the column's role off it.
|
|
85
|
-
*
|
|
86
|
-
* Whatever this column played before stops, and the role moves here from any other
|
|
87
|
-
* column, in one new mapping (keksdose's `assignColumn`: "both halves are patches to
|
|
88
|
-
* the same object"). Other fields of the mapping are kept as they are.
|
|
89
|
-
*/
|
|
90
|
-
export function assignColumnRole<R extends string, M extends ColumnMapping<R>>(
|
|
91
|
-
roles: readonly ColumnRole<R>[],
|
|
92
|
-
mapping: M,
|
|
93
|
-
column: number,
|
|
94
|
-
role: R | null,
|
|
95
|
-
): M {
|
|
96
|
-
const next = { ...mapping } as Record<string, unknown>;
|
|
97
|
-
for (const { value } of roles) {
|
|
98
|
-
if (mapping[value] === column) next[value] = null;
|
|
99
|
-
}
|
|
100
|
-
if (role !== null) next[role] = column;
|
|
101
|
-
return next as M;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* The gaps: each required role with no column, and each required group none of whose
|
|
106
|
-
* roles has one. In the order of `roles`. A column index past `width` is no column.
|
|
107
|
-
*/
|
|
108
|
-
export function missingRoles<R extends string>(
|
|
109
|
-
roles: readonly ColumnRole<R>[],
|
|
110
|
-
mapping: ColumnMapping<R>,
|
|
111
|
-
width?: number,
|
|
112
|
-
): R[][] {
|
|
113
|
-
const gaps: R[][] = [];
|
|
114
|
-
const groups = new Set<string>();
|
|
115
|
-
for (const role of roles) {
|
|
116
|
-
if (role.required === true) {
|
|
117
|
-
if (placedAt(mapping, role.value, width) === null) gaps.push([role.value]);
|
|
118
|
-
} else if (typeof role.required === "string" && !groups.has(role.required)) {
|
|
119
|
-
groups.add(role.required);
|
|
120
|
-
const members = roles.filter((other) => other.required === role.required);
|
|
121
|
-
if (members.every((member) => placedAt(mapping, member.value, width) === null)) {
|
|
122
|
-
gaps.push(members.map((member) => member.value));
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return gaps;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const normalised = (text: string) => text.trim().toLowerCase();
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* A first mapping for a table nobody has mapped yet.
|
|
133
|
-
*
|
|
134
|
-
* A column whose header IS a role's label or key gets that role. The required roles
|
|
135
|
-
* left over then take the free columns in order — Kurvenschmiede's "time, setpoint,
|
|
136
|
-
* actual are the first three columns", which is how a recorder writes them. Optional
|
|
137
|
-
* roles are not guessed by position: in a bank
|
|
138
|
-
* export of twelve columns, "the fourth role is the fourth column" is a wrong answer
|
|
139
|
-
* presented as a suggestion, and an "ignore" is the honest one.
|
|
140
|
-
*/
|
|
141
|
-
export function guessMapping<R extends string>(
|
|
142
|
-
roles: readonly ColumnRole<R>[],
|
|
143
|
-
header: readonly string[] | null,
|
|
144
|
-
width: number,
|
|
145
|
-
): ColumnMapping<R> {
|
|
146
|
-
const mapping: ColumnMapping<R> = {};
|
|
147
|
-
const taken = new Set<number>();
|
|
148
|
-
if (header) {
|
|
149
|
-
for (const role of roles) {
|
|
150
|
-
const names = [normalised(role.label), normalised(role.value)];
|
|
151
|
-
const column = header.findIndex((name, index) => !taken.has(index) && names.includes(normalised(name)));
|
|
152
|
-
if (column >= 0) {
|
|
153
|
-
mapping[role.value] = column;
|
|
154
|
-
taken.add(column);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
const free = Array.from({ length: width }, (_, index) => index).filter((index) => !taken.has(index));
|
|
159
|
-
for (const role of roles) {
|
|
160
|
-
if (role.required !== true || typeof mapping[role.value] === "number") continue;
|
|
161
|
-
const column = free.shift();
|
|
162
|
-
if (column === undefined) break;
|
|
163
|
-
mapping[role.value] = column;
|
|
164
|
-
}
|
|
165
|
-
return mapping;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* The table read through the mapping: the rows every `numeric` role can read, the
|
|
170
|
-
* others moved to `unread` (in line order, with the parse's own), what is missing,
|
|
171
|
-
* and whether the table can be used. Column indexes past the table's width count as
|
|
172
|
-
* unplaced.
|
|
173
|
-
*/
|
|
174
|
-
export function readMappedTable<R extends string>(
|
|
175
|
-
table: TextTable,
|
|
176
|
-
roles: readonly ColumnRole<R>[],
|
|
177
|
-
mapping: ColumnMapping<R>,
|
|
178
|
-
): ColumnMapperResult<R> {
|
|
179
|
-
const numeric = roles
|
|
180
|
-
.filter((role) => role.numeric)
|
|
181
|
-
.map((role) => placedAt(mapping, role.value, table.width))
|
|
182
|
-
.filter((column): column is number => column !== null);
|
|
183
|
-
const rows: string[][] = [];
|
|
184
|
-
const lines: TableLine[] = [];
|
|
185
|
-
const refused: TableLine[] = [];
|
|
186
|
-
table.rows.forEach((row, index) => {
|
|
187
|
-
if (numeric.every((column) => Number.isFinite(tableNumber(row[column] ?? "", table.decimalComma)))) {
|
|
188
|
-
rows.push(row);
|
|
189
|
-
lines.push(table.lines[index]);
|
|
190
|
-
} else {
|
|
191
|
-
refused.push(table.lines[index]);
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
const unread = refused.length ? [...table.unread, ...refused].sort((a, b) => a.line - b.line) : table.unread;
|
|
195
|
-
const missing = missingRoles(roles, mapping, table.width);
|
|
196
|
-
return {
|
|
197
|
-
...table,
|
|
198
|
-
rows,
|
|
199
|
-
lines,
|
|
200
|
-
unread,
|
|
201
|
-
mapping,
|
|
202
|
-
missing,
|
|
203
|
-
complete: missing.length === 0 && rows.length > 0,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* A dropped or chosen file's text — UTF-8 when it is UTF-8, else Windows-1252.
|
|
209
|
-
*
|
|
210
|
-
* A German bank or bench export is very often written in Windows-1252, and reading it
|
|
211
|
-
* as UTF-8 turns every "ä" in a column name into "�" — no error, just a header nobody
|
|
212
|
-
* recognises. So the bytes are tried as strict UTF-8 first and read as Windows-1252
|
|
213
|
-
* when that fails (keksdose's server sniffs the encoding for the same reason; a file
|
|
214
|
-
* read in the browser had nothing doing it). Falls back to `FileReader` where a `File`
|
|
215
|
-
* has no `arrayBuffer` (Kurvenschmiede's `readText`).
|
|
216
|
-
*/
|
|
217
|
-
export async function readTextFile(file: Blob): Promise<string> {
|
|
218
|
-
const bytes = await bytesOf(file);
|
|
219
|
-
try {
|
|
220
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
221
|
-
} catch {
|
|
222
|
-
return new TextDecoder("windows-1252").decode(bytes);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function bytesOf(file: Blob): Promise<ArrayBuffer> {
|
|
227
|
-
if (typeof file.arrayBuffer === "function") return file.arrayBuffer();
|
|
228
|
-
return new Promise((resolve, reject) => {
|
|
229
|
-
const reader = new FileReader();
|
|
230
|
-
reader.onload = () => resolve(reader.result as ArrayBuffer);
|
|
231
|
-
reader.onerror = () => reject(reader.error ?? new Error("read failed"));
|
|
232
|
-
reader.readAsArrayBuffer(file);
|
|
233
|
-
});
|
|
234
|
-
}
|