@eifi1/ui-kit 0.32.2 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -9
- package/dist/account/delete-account-setting.d.ts +11 -10
- package/dist/account/delete-account-setting.js +2 -1
- package/dist/account/delete-account-setting.js.map +1 -1
- package/dist/account/email-change-setting.d.ts +5 -0
- package/dist/account/email-change-setting.js +4 -3
- package/dist/account/email-change-setting.js.map +1 -1
- package/dist/account/sessions-setting.d.ts +4 -0
- package/dist/account/sessions-setting.js +3 -2
- package/dist/account/sessions-setting.js.map +1 -1
- package/dist/admin/admin-action-confirm.d.ts +11 -10
- package/dist/admin/admin-action-confirm.js.map +1 -1
- package/dist/admin/invitations-panel.d.ts +11 -10
- package/dist/admin/invitations-panel.js +4 -3
- package/dist/admin/invitations-panel.js.map +1 -1
- package/dist/admin/plan-change.d.ts +101 -0
- package/dist/admin/plan-change.js +210 -0
- package/dist/admin/plan-change.js.map +1 -0
- package/dist/admin/reviewer-scope-editor.d.ts +11 -10
- package/dist/admin/reviewer-scope-editor.js.map +1 -1
- package/dist/admin/role-select.d.ts +8 -7
- package/dist/admin/role-select.js.map +1 -1
- package/dist/admin/transfer-ownership-dialog.d.ts +11 -10
- package/dist/admin/user-roster.d.ts +8 -7
- package/dist/admin/user-roster.js +2 -2
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/auth/auth-errors.d.ts +13 -2
- package/dist/auth/auth-errors.js +5 -1
- package/dist/auth/auth-errors.js.map +1 -1
- package/dist/auth/complete-name-dialog.d.ts +4 -1
- package/dist/auth/complete-name-dialog.js +2 -1
- package/dist/auth/complete-name-dialog.js.map +1 -1
- package/dist/auth/register-form.d.ts +11 -10
- package/dist/billing/billing-banner.d.ts +14 -11
- package/dist/billing/billing-banner.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +11 -10
- package/dist/billing/billing-labels.js +10 -1
- package/dist/billing/billing-labels.js.map +1 -1
- package/dist/billing/billing-lock.d.ts +40 -12
- package/dist/billing/billing-lock.js +9 -3
- package/dist/billing/billing-lock.js.map +1 -1
- package/dist/billing/billing-overview.d.ts +155 -0
- package/dist/billing/billing-overview.js +2 -0
- package/dist/billing/billing-overview.js.map +1 -0
- package/dist/billing/billing-standing.d.ts +11 -10
- package/dist/billing/checkout-processing.d.ts +218 -0
- package/dist/billing/checkout-processing.js +198 -0
- package/dist/billing/checkout-processing.js.map +1 -0
- package/dist/billing/checkout-return.d.ts +51 -0
- package/dist/billing/checkout-return.js +43 -0
- package/dist/billing/checkout-return.js.map +1 -0
- package/dist/billing/pay-page.d.ts +215 -0
- package/dist/billing/pay-page.js +113 -0
- package/dist/billing/pay-page.js.map +1 -0
- package/dist/billing/plan-card.d.ts +11 -10
- package/dist/billing/plan-limit-notice.d.ts +11 -10
- package/dist/billing/plan-limit-toast.d.ts +144 -0
- package/dist/billing/plan-limit-toast.js +40 -0
- package/dist/billing/plan-limit-toast.js.map +1 -0
- package/dist/billing/subscription-actions.d.ts +39 -15
- package/dist/billing/subscription-actions.js +9 -2
- package/dist/billing/subscription-actions.js.map +1 -1
- package/dist/billing/subscription-status-chip.d.ts +10 -9
- package/dist/bin/eifi1-pay-page.mjs +166 -0
- package/dist/chart.d.ts +11 -10
- package/dist/components/account-chips.d.ts +6 -1
- package/dist/components/account-settings.d.ts +4 -1
- package/dist/components/account-settings.js +2 -1
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/amount-input.d.ts +11 -10
- package/dist/components/autocomplete.d.ts +2 -1
- package/dist/components/autocomplete.js +5 -3
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/button-group.d.ts +11 -10
- package/dist/components/button-group.js +1 -1
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/calculator.d.ts +11 -10
- package/dist/components/calculator.js +3 -3
- package/dist/components/calculator.js.map +1 -1
- package/dist/components/checkbox-group.d.ts +2 -1
- package/dist/components/checkbox-group.js.map +1 -1
- package/dist/components/checkbox.d.ts +2 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/chip.d.ts +2 -1
- package/dist/components/chip.js +19 -17
- package/dist/components/chip.js.map +1 -1
- package/dist/components/choice-card.d.ts +3 -1
- package/dist/components/choice-card.js +1 -1
- package/dist/components/choice-card.js.map +1 -1
- package/dist/components/combobox-core.js +2 -1
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +4 -3
- package/dist/components/combobox.js +2 -2
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +11 -10
- package/dist/components/confirm-dialog.js +6 -3
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/copy-button.d.ts +12 -11
- package/dist/components/country-select.d.ts +2 -1
- package/dist/components/country-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +11 -10
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-cells.d.ts +12 -1
- package/dist/components/data-table-filter-popover.d.ts +12 -1
- package/dist/components/data-table-filters.d.ts +12 -1
- package/dist/components/data-table-labels.d.ts +12 -1
- package/dist/components/data-table-pagination.d.ts +12 -1
- package/dist/components/data-table.d.ts +12 -1
- package/dist/components/data-table.js +3 -3
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.js +6 -4
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/dropdown.js +9 -1
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +3 -2
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +11 -10
- package/dist/components/field-parts.d.ts +32 -4
- package/dist/components/field-parts.js +54 -19
- package/dist/components/field-parts.js.map +1 -1
- package/dist/components/file-button.d.ts +11 -10
- package/dist/components/file-button.js +3 -2
- package/dist/components/file-button.js.map +1 -1
- package/dist/components/file-dropzone.d.ts +12 -11
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/floating-panel.js +5 -5
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +10 -9
- package/dist/components/form-actions.js +3 -3
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/iban-input.d.ts +11 -10
- package/dist/components/inline-edit-field.d.ts +2 -1
- package/dist/components/inline-edit-field.js +6 -6
- package/dist/components/inline-edit-field.js.map +1 -1
- package/dist/components/language-select.d.ts +11 -10
- package/dist/components/legal-page.d.ts +11 -10
- package/dist/components/legal.d.ts +11 -10
- package/dist/components/list.d.ts +2 -1
- package/dist/components/list.js +4 -5
- package/dist/components/list.js.map +1 -1
- package/dist/components/modal.js.map +1 -1
- package/dist/components/money-field.d.ts +11 -10
- package/dist/components/month-picker.js +54 -44
- package/dist/components/month-picker.js.map +1 -1
- package/dist/components/multi-entity-combobox.d.ts +3 -2
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/number-field.d.ts +18 -10
- package/dist/components/number-field.js.map +1 -1
- package/dist/components/number-input.d.ts +37 -10
- package/dist/components/number-input.js +27 -12
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +11 -10
- package/dist/components/numpad-sheet.js +2 -2
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/phone-input.d.ts +11 -10
- package/dist/components/popover.js +6 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/row-actions.d.ts +6 -5
- package/dist/components/row-actions.js +130 -32
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/series-chart.d.ts +11 -10
- package/dist/components/settings-fields.d.ts +11 -10
- package/dist/components/share-card.d.ts +8 -7
- package/dist/components/share-card.js +26 -10
- package/dist/components/share-card.js.map +1 -1
- package/dist/components/skeleton.d.ts +10 -2
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/status-dot.d.ts +1 -0
- package/dist/components/status-dot.js +2 -2
- package/dist/components/status-dot.js.map +1 -1
- package/dist/components/swatch-picker.d.ts +2 -1
- package/dist/components/swatch-picker.js.map +1 -1
- package/dist/components/swipeable-row.d.ts +88 -17
- package/dist/components/swipeable-row.js +46 -9
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/switch.d.ts +2 -1
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.d.ts +2 -1
- package/dist/components/table.js +6 -0
- package/dist/components/table.js.map +1 -1
- package/dist/components/text-link.d.ts +10 -9
- package/dist/components/time-input.d.ts +11 -10
- package/dist/components/toggle-group.d.ts +2 -1
- package/dist/components/toggle-group.js +1 -1
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/translation-review-editor.d.ts +4 -0
- package/dist/components/translation-review-editor.js +4 -3
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review-parts.d.ts +1 -0
- package/dist/components/translation-review.d.ts +1 -0
- package/dist/components/translation-review.js +15 -18
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/ui.d.ts +10 -9
- package/dist/components/ui.js +47 -27
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-table-state.d.ts +12 -1
- package/dist/components/user-avatar.d.ts +1 -0
- package/dist/components/write-lock.d.ts +101 -13
- package/dist/components/write-lock.js +37 -13
- package/dist/components/write-lock.js.map +1 -1
- package/dist/{data-table-labels-kpAAVi6g.d.ts → data-table-labels-BJ5JE_X2.d.ts} +17 -0
- package/dist/data-table.d.ts +12 -1
- package/dist/{delete-account-setting-Dy4kJgUm.d.ts → delete-account-setting-DAgGBrbn.d.ts} +393 -42
- package/dist/feedback/feedback-attachment.d.ts +11 -10
- package/dist/feedback/feedback-attachment.js +7 -7
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-context.d.ts +11 -10
- package/dist/feedback/feedback-crash.d.ts +11 -10
- package/dist/feedback/feedback-dialog.d.ts +11 -10
- package/dist/feedback/feedback-inbox.d.ts +10 -9
- package/dist/feedback/feedback-inbox.js +7 -3
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback/feedback-labels.d.ts +11 -10
- package/dist/feedback/feedback-menu.d.ts +4 -3
- package/dist/feedback/feedback-record.d.ts +11 -10
- package/dist/feedback/feedback-row-detail.d.ts +11 -10
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback/feedback-status-undo.d.ts +11 -10
- package/dist/feedback/feedback-submit.d.ts +11 -10
- package/dist/feedback/feedback-swipe.d.ts +11 -10
- package/dist/feedback/feedback-swipe.js +3 -6
- package/dist/feedback/feedback-swipe.js.map +1 -1
- package/dist/feedback/feedback-table.d.ts +11 -10
- package/dist/feedback/feedback-table.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +11 -10
- package/dist/feedback/feedback-thread.js +5 -1
- package/dist/feedback/feedback-thread.js.map +1 -1
- package/dist/feedback.d.ts +11 -10
- package/dist/hooks/use-anchored-panel.d.ts +37 -7
- package/dist/hooks/use-anchored-panel.js +6 -7
- package/dist/hooks/use-anchored-panel.js.map +1 -1
- package/dist/hooks/use-file-drop.d.ts +11 -10
- package/dist/hooks/use-focus-trap.d.ts +8 -0
- package/dist/hooks/use-focus-trap.js +17 -11
- package/dist/hooks/use-focus-trap.js.map +1 -1
- package/dist/i18n/defaults.d.ts +11 -10
- package/dist/i18n/defaults.js +2 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +11 -10
- package/dist/i18n/german.js +37 -1
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +5 -4
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +11 -10
- package/dist/i18n/locales/de-CH.d.ts +11 -10
- package/dist/i18n/locales/en.d.ts +11 -10
- package/dist/i18n/locales/es.d.ts +11 -10
- package/dist/i18n/locales/es.js +35 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +11 -10
- package/dist/i18n/locales/fr.js +37 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +11 -10
- package/dist/i18n/locales/hu.js +38 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +11 -10
- package/dist/i18n/locales/it.js +35 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +11 -10
- package/dist/i18n/locales/zh.js +35 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/plurals.d.ts +20 -1
- package/dist/i18n/plurals.js +11 -0
- package/dist/i18n/plurals.js.map +1 -1
- package/dist/i18n/review.d.ts +11 -10
- package/dist/i18n/review.js +8 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +40 -2
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.d.ts +11 -10
- package/dist/landing/landing-sections.d.ts +11 -10
- package/dist/landing/public-header.d.ts +11 -10
- package/dist/landing/routing.d.ts +11 -10
- package/dist/pay/.well-known/apple-developer-merchantid-domain-association +1 -0
- package/dist/pay/index.html +30 -0
- package/dist/pay/pay.css +126 -0
- package/dist/pay/pay.js +198 -0
- package/dist/rhf/fields.d.ts +11 -10
- package/dist/rhf/form.d.ts +11 -10
- package/dist/rhf.d.ts +11 -10
- package/dist/settings/settings-section.d.ts +11 -10
- package/dist/shell/app-shell.d.ts +10 -9
- package/dist/shell/top-bar-brand.d.ts +11 -10
- package/dist/shell/topbar-action-menu.d.ts +1 -0
- package/dist/shell.d.ts +11 -10
- package/dist/testing/css-variables.d.ts +74 -0
- package/dist/testing/css-variables.generated.d.ts +7 -0
- package/dist/testing/css-variables.generated.js +8 -0
- package/dist/testing/css-variables.generated.js.map +1 -0
- package/dist/testing/css-variables.js +93 -0
- package/dist/testing/css-variables.js.map +1 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +12 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/theme/contrast-tokens.d.ts +44 -1
- package/dist/theme/contrast-tokens.js +21 -0
- package/dist/theme/contrast-tokens.js.map +1 -1
- package/dist/theme/contrast.d.ts +2 -2
- package/dist/theme/palette-derive.js +60 -11
- package/dist/theme/palette-derive.js.map +1 -1
- package/dist/theme/palette-presets.js +45 -43
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +12 -11
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard.d.ts +11 -10
- package/package.json +10 -2
- package/src/account/delete-account-setting.tsx +6 -1
- package/src/account/email-change-setting.tsx +9 -3
- package/src/account/sessions-setting.tsx +7 -2
- package/src/admin/admin-action-confirm.tsx +2 -1
- package/src/admin/invitations-panel.tsx +9 -3
- package/src/admin/plan-change.tsx +487 -0
- package/src/admin/reviewer-scope-editor.tsx +2 -1
- package/src/admin/role-select.tsx +2 -1
- package/src/admin/user-roster.tsx +5 -3
- package/src/auth/auth-errors.ts +25 -2
- package/src/auth/complete-name-dialog.tsx +6 -2
- package/src/billing/billing-banner.tsx +3 -1
- package/src/billing/billing-labels.ts +45 -0
- package/src/billing/billing-lock.ts +36 -3
- package/src/billing/billing-overview.ts +58 -0
- package/src/billing/checkout-processing.ts +391 -0
- package/src/billing/checkout-return.ts +90 -0
- package/src/billing/pay-page.ts +246 -0
- package/src/billing/plan-limit-toast.tsx +91 -0
- package/src/billing/subscription-actions.tsx +41 -8
- package/src/components/account-settings.tsx +6 -2
- package/src/components/autocomplete.tsx +10 -4
- package/src/components/button-group.tsx +4 -1
- package/src/components/calculator.tsx +10 -6
- package/src/components/checkbox-group.tsx +2 -2
- package/src/components/checkbox.tsx +2 -1
- package/src/components/chip.tsx +37 -32
- package/src/components/choice-card.tsx +4 -2
- package/src/components/combobox-core.tsx +5 -2
- package/src/components/combobox.tsx +8 -6
- package/src/components/confirm-dialog.tsx +13 -6
- package/src/components/country-select.tsx +2 -2
- package/src/components/danger-confirm.tsx +2 -2
- package/src/components/data-table.tsx +20 -9
- package/src/components/date-picker.tsx +10 -7
- package/src/components/dropdown.tsx +12 -2
- package/src/components/entity-combobox.tsx +3 -3
- package/src/components/field-parts.tsx +61 -14
- package/src/components/file-button.tsx +5 -4
- package/src/components/file-dropzone.tsx +2 -2
- package/src/components/floating-panel.tsx +7 -5
- package/src/components/form-actions.tsx +12 -5
- package/src/components/full-bleed-dialog.tsx +6 -0
- package/src/components/inline-edit-field.tsx +13 -13
- package/src/components/list.tsx +10 -9
- package/src/components/modal.tsx +5 -0
- package/src/components/month-picker.tsx +8 -2
- package/src/components/multi-entity-combobox.tsx +3 -3
- package/src/components/number-field.tsx +7 -0
- package/src/components/number-input.tsx +71 -11
- package/src/components/numpad-sheet.tsx +8 -4
- package/src/components/popover.tsx +6 -2
- package/src/components/row-actions.tsx +208 -58
- package/src/components/share-card.tsx +67 -13
- package/src/components/skeleton.tsx +10 -2
- package/src/components/status-dot.tsx +7 -7
- package/src/components/swatch-picker.tsx +2 -2
- package/src/components/swipeable-row.tsx +138 -27
- package/src/components/switch.tsx +2 -1
- package/src/components/table.tsx +8 -1
- package/src/components/toggle-group.tsx +5 -3
- package/src/components/translation-review-editor.tsx +8 -3
- package/src/components/translation-review.tsx +18 -22
- package/src/components/ui.tsx +92 -43
- package/src/components/write-lock.tsx +150 -24
- package/src/feedback/feedback-attachment.tsx +13 -9
- package/src/feedback/feedback-inbox.tsx +10 -6
- package/src/feedback/feedback-row-detail.tsx +3 -2
- package/src/feedback/feedback-swipe.tsx +6 -11
- package/src/feedback/feedback-table.tsx +2 -1
- package/src/feedback/feedback-thread.tsx +20 -1
- package/src/hooks/use-anchored-panel.ts +60 -14
- package/src/hooks/use-focus-trap.ts +55 -12
- package/src/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +41 -0
- package/src/i18n/kit-labels.tsx +4 -0
- package/src/i18n/locales/es.ts +35 -0
- package/src/i18n/locales/fr.ts +41 -0
- package/src/i18n/locales/hu.ts +39 -0
- package/src/i18n/locales/it.ts +36 -0
- package/src/i18n/locales/zh.ts +34 -0
- package/src/i18n/plurals.ts +29 -0
- package/src/i18n/review.ts +7 -0
- package/src/index.ts +51 -5
- package/src/testing/css-variables.generated.ts +112 -0
- package/src/testing/css-variables.ts +191 -0
- package/src/testing/index.ts +13 -0
- package/src/theme/contrast-tokens.ts +53 -2
- package/src/theme/palette-derive.ts +87 -10
- package/src/theme/palette-presets.ts +45 -43
- package/src/wizard/stepper-nav.tsx +2 -1
- package/tokens.css +324 -46
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
import type { FeedbackContext, FeedbackRecord } from "./feedback-record";
|
|
38
38
|
import { useFeedbackStatusUndo } from "./feedback-status-undo";
|
|
39
39
|
import type { FeedbackStatusChange } from "./feedback-status-undo";
|
|
40
|
+
import type { CommitScope } from "../components/write-lock";
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* One report, opened — the panel a feedback table unfolds under its row (§4.4 of
|
|
@@ -410,7 +411,7 @@ export interface FeedbackReworkSectionProps {
|
|
|
410
411
|
* `feedbackToast` refusals, the `feedbackAttachment` words. */
|
|
411
412
|
attachment?: Partial<FeedbackNoteAttachment>;
|
|
412
413
|
/** The send COMMITS — see `FeedbackNoteEditor`'s `commit`. */
|
|
413
|
-
commit?:
|
|
414
|
+
commit?: CommitScope;
|
|
414
415
|
labels?: Partial<FeedbackDetailLabels>;
|
|
415
416
|
}
|
|
416
417
|
|
|
@@ -569,7 +570,7 @@ export interface FeedbackRowDetailProps {
|
|
|
569
570
|
* reaches `onUpdate`. Off by default — keksdose's shell lock is a read-only demo
|
|
570
571
|
* BUDGET, and a person viewing one still owns their own reports.
|
|
571
572
|
*/
|
|
572
|
-
commit?:
|
|
573
|
+
commit?: CommitScope;
|
|
573
574
|
/**
|
|
574
575
|
* Open with the subject: the category badge and the title (feedback contract §8.1,
|
|
575
576
|
* keksdose live #395). On by default; `false` for a page that already shows the title
|
|
@@ -79,11 +79,9 @@ function targetOf(id: string, status: FeedbackStatus): FeedbackStatus | null {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/** One action's panel
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* mode, the dark page on the pale sky in dark (the translation list's reset does the
|
|
86
|
-
* same on its grey). */
|
|
82
|
+
/** One action's panel, by tone (0.33): SwipeableRow paints the tone's soft wash idle,
|
|
83
|
+
* and its fill under that fill's own foreground once armed — the same pairs in light
|
|
84
|
+
* and dark, where the fills turn pastel. */
|
|
87
85
|
function panel(
|
|
88
86
|
id: FeedbackSwipeAction,
|
|
89
87
|
target: FeedbackStatus,
|
|
@@ -97,24 +95,21 @@ function panel(
|
|
|
97
95
|
onCommit,
|
|
98
96
|
label: labels[target],
|
|
99
97
|
icon: <ChevronsRight className="size-4" aria-hidden />,
|
|
100
|
-
|
|
101
|
-
armedClassName: "bg-[var(--info)] text-[var(--info-contrast)]",
|
|
98
|
+
tone: "info",
|
|
102
99
|
};
|
|
103
100
|
case "done":
|
|
104
101
|
return {
|
|
105
102
|
onCommit,
|
|
106
103
|
label: labels.DONE,
|
|
107
104
|
icon: <Check className="size-4" aria-hidden />,
|
|
108
|
-
|
|
109
|
-
armedClassName: "bg-[var(--success)] text-[var(--success-contrast)]",
|
|
105
|
+
tone: "success",
|
|
110
106
|
};
|
|
111
107
|
case "wont_do":
|
|
112
108
|
return {
|
|
113
109
|
onCommit,
|
|
114
110
|
label: labels.WONT_DO,
|
|
115
111
|
icon: <Ban className="size-4" aria-hidden />,
|
|
116
|
-
|
|
117
|
-
armedClassName: "bg-[var(--danger-hover)] text-[var(--danger-contrast)]",
|
|
112
|
+
tone: "danger",
|
|
118
113
|
};
|
|
119
114
|
}
|
|
120
115
|
}
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import { FEEDBACK_AWAITING_STATUSES, reworkCount, type FeedbackContext, type FeedbackRecord } from "./feedback-record";
|
|
32
32
|
import type { FeedbackStatusChange } from "./feedback-status-undo";
|
|
33
33
|
import { feedbackPageHref, feedbackPagePath } from "./feedback-row-detail";
|
|
34
|
+
import type { CommitScope } from "../components/write-lock";
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* The two feedback pages' TABLE — `/feedback` (the admin inbox) and `/my-feedback` (the
|
|
@@ -399,7 +400,7 @@ export interface FeedbackColumnsOptions {
|
|
|
399
400
|
/** The status buttons SAVE when pressed: pass `true` to put them under the page's
|
|
400
401
|
* `WriteLockProvider` (FeedbackStatusTransitions' `commit`). Off by default — a
|
|
401
402
|
* feedback inbox is rarely what an app's lock is about. */
|
|
402
|
-
commit?:
|
|
403
|
+
commit?: CommitScope;
|
|
403
404
|
labels: FeedbackPageLabels;
|
|
404
405
|
statusLabels: FeedbackStatusLabels;
|
|
405
406
|
categoryLabels: FeedbackCategoryLabels;
|
|
@@ -7,6 +7,8 @@ import { Button, EmptyState, Spinner, Textarea } from "../components/ui";
|
|
|
7
7
|
import type { CharacterCountLabels } from "../components/ui";
|
|
8
8
|
import { Skeleton } from "../components/skeleton";
|
|
9
9
|
import { Tooltip } from "../components/tooltip";
|
|
10
|
+
import { useWriteLock } from "../components/write-lock";
|
|
11
|
+
import type { CommitScope } from "../components/write-lock";
|
|
10
12
|
import { useKitFileLabels, useKitLabels, useKitLocale } from "../i18n/kit-labels";
|
|
11
13
|
import { FeedbackAttachmentField } from "./feedback-attachment";
|
|
12
14
|
import type { FeedbackAttachmentFieldSingleProps } from "./feedback-attachment";
|
|
@@ -565,6 +567,18 @@ export interface FeedbackComposerProps {
|
|
|
565
567
|
/** Rendered INSTEAD of the composer — the thread is closed, or read-only for this
|
|
566
568
|
* reader. */
|
|
567
569
|
disabledReason?: ReactNode;
|
|
570
|
+
/**
|
|
571
|
+
* Send COMMITS (0.33.1, kastlan's assistant): under a locked {@link WriteLockProvider}
|
|
572
|
+
* Send is disabled the `disabledReason` way — focusable, the lock's reason in its
|
|
573
|
+
* tooltip and description, as on {@link Button}'s `commit` — and neither Enter nor
|
|
574
|
+
* Ctrl/⌘+Enter sends. The box stays editable and the draft stays: nothing in it
|
|
575
|
+
* reaches the server until Send, and the lock may lift. No provider, or an unlocked
|
|
576
|
+
* one: no effect. A {@link CommitScope}: kastlan's assistant answers under the demo
|
|
577
|
+
* and not under a lapsed plan (its backend's 402), so it passes
|
|
578
|
+
* `commit={{ except: ["demo"] }}` where it read `useWriteLock` and fed `canSend` and a
|
|
579
|
+
* reason by hand. Not the prop above: that one replaces the box, this one keeps it.
|
|
580
|
+
*/
|
|
581
|
+
commit?: CommitScope;
|
|
568
582
|
rows?: number;
|
|
569
583
|
/**
|
|
570
584
|
* The longest draft the box takes, in UTF-16 code units as the browser counts — the
|
|
@@ -671,6 +685,7 @@ export function FeedbackComposer({
|
|
|
671
685
|
canSend: canSendProp,
|
|
672
686
|
sendOn = "mod-enter",
|
|
673
687
|
disabledReason,
|
|
688
|
+
commit,
|
|
674
689
|
rows = 3,
|
|
675
690
|
maxLength,
|
|
676
691
|
showCount,
|
|
@@ -686,6 +701,9 @@ export function FeedbackComposer({
|
|
|
686
701
|
...rest
|
|
687
702
|
}: FeedbackComposerProps) {
|
|
688
703
|
const labels = useKitLabels("feedbackComposer", DEFAULT_FEEDBACK_COMPOSER_LABELS, labelsProp);
|
|
704
|
+
// The lock as Send sees it, with its scope (§12.36). Send's Button reads the same
|
|
705
|
+
// through its own `commit`; this holds the keyboard's send back with it.
|
|
706
|
+
const lock = useWriteLock(commit ?? false);
|
|
689
707
|
const rootAttributes = { id, ...dataAttributes(rest) };
|
|
690
708
|
const hintId = useId();
|
|
691
709
|
const [ownDraft, setOwnDraft] = useState("");
|
|
@@ -752,7 +770,7 @@ export function FeedbackComposer({
|
|
|
752
770
|
// Longer than `maxLength` only through `insertText` or a controlled value (typing
|
|
753
771
|
// stops at it): the server would refuse it, so it does not go.
|
|
754
772
|
const tooLong = maxLength !== undefined && draft.length > maxLength;
|
|
755
|
-
const canSend = (canSendProp ?? draft.trim().length > 0) && !pending && !tooLong;
|
|
773
|
+
const canSend = (canSendProp ?? draft.trim().length > 0) && !pending && !tooLong && !lock.locked;
|
|
756
774
|
const send = () => {
|
|
757
775
|
if (!canSend) return;
|
|
758
776
|
const sentDraft = draft;
|
|
@@ -844,6 +862,7 @@ export function FeedbackComposer({
|
|
|
844
862
|
<Button
|
|
845
863
|
onClick={send}
|
|
846
864
|
disabled={!canSend}
|
|
865
|
+
commit={commit}
|
|
847
866
|
aria-busy={pending || undefined}
|
|
848
867
|
size={size === "sm" ? "sm" : undefined}
|
|
849
868
|
>
|
|
@@ -21,6 +21,13 @@ import { useTextSize } from "../theme/text-size";
|
|
|
21
21
|
export interface ViewportBox {
|
|
22
22
|
top: number;
|
|
23
23
|
height: number;
|
|
24
|
+
/**
|
|
25
|
+
* The height of the LAYOUT viewport itself, which `position: fixed`'s `bottom` is
|
|
26
|
+
* measured from. Only {@link anchoredPanelPlacement} reads it, for a flipped panel's
|
|
27
|
+
* `bottom`. Defaults to `top + height`: the two are the same box whenever no keyboard
|
|
28
|
+
* and no zoom shrink the visible region.
|
|
29
|
+
*/
|
|
30
|
+
layoutHeight?: number;
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
/**
|
|
@@ -71,9 +78,21 @@ export function readKeyboardInset({ minimum = MIN_KEYBOARD_PX }: KeyboardInsetOp
|
|
|
71
78
|
function readViewport(): ViewportBox {
|
|
72
79
|
if (typeof window === "undefined") return { top: 0, height: 0 };
|
|
73
80
|
const vv = window.visualViewport;
|
|
81
|
+
// The layout viewport's height is `innerHeight`, the reference `readKeyboardInset`
|
|
82
|
+
// measures the keyboard against. The keyboard leaves it alone (it shrinks only the
|
|
83
|
+
// VISUAL viewport; under `interactive-widget=resizes-content` it shrinks both, the
|
|
84
|
+
// fixed box with them), and it follows the mobile URL bar as it slides away, as a
|
|
85
|
+
// fixed box's `bottom` does. Not `documentElement.clientHeight`: on mobile Chrome and
|
|
86
|
+
// Safari that stays at the small, URL-bar-shown height, so with the bar scrolled away
|
|
87
|
+
// a flipped panel would sit that much too low, over its own field. `innerHeight`'s one
|
|
88
|
+
// error is a desktop page with a horizontal scrollbar, which it counts and the fixed
|
|
89
|
+
// box does not: the gap widens by the bar's thickness, and never closes.
|
|
90
|
+
const layoutHeight = window.innerHeight;
|
|
74
91
|
// offsetTop, not 0: with the keyboard up the user can also pan the visual viewport
|
|
75
92
|
// within the layout viewport, which shifts where "visible" starts.
|
|
76
|
-
return vv
|
|
93
|
+
return vv
|
|
94
|
+
? { top: vv.offsetTop, height: vv.height, layoutHeight }
|
|
95
|
+
: { top: 0, height: window.innerHeight, layoutHeight };
|
|
77
96
|
}
|
|
78
97
|
|
|
79
98
|
export interface AnchoredPanelOptions {
|
|
@@ -90,11 +109,24 @@ export interface AnchoredPanelOptions {
|
|
|
90
109
|
minHeight?: number;
|
|
91
110
|
}
|
|
92
111
|
|
|
112
|
+
/**
|
|
113
|
+
* `top` and `bottom` are the two edges of a panel exactly `maxHeight` tall. A panel is
|
|
114
|
+
* sized by its content and `maxHeight` only caps it, so apply the edge on the trigger's
|
|
115
|
+
* side, the one that holds whatever the height: `top` while the panel is below,
|
|
116
|
+
* `bottom` (and no `top`) once it is flipped `above`.
|
|
117
|
+
*/
|
|
93
118
|
export interface AnchoredPanel {
|
|
94
119
|
/** The trigger's rect, or null while closed/unmeasured. */
|
|
95
120
|
rect: AnchorRect | null;
|
|
96
|
-
/** Viewport-space `top` for the panel.
|
|
121
|
+
/** Viewport-space `top` for the panel — the one to apply while it is below. Flipped,
|
|
122
|
+
* it is where a FULL-height panel's top would be, and a three-row list placed by it
|
|
123
|
+
* floated far above its field (keksdose K1, live #401); place that one by `bottom`. */
|
|
97
124
|
top: number;
|
|
125
|
+
/** CSS `bottom` for a `position: fixed` panel: the distance from the layout
|
|
126
|
+
* viewport's bottom edge up to the panel's. The one to apply once it is flipped
|
|
127
|
+
* `above`: it keeps the panel's foot `gap` above the trigger however short its
|
|
128
|
+
* content, and while a filtered list shrinks or grows under the user's typing. */
|
|
129
|
+
bottom: number;
|
|
98
130
|
/** Cap that keeps the panel inside the VISIBLE viewport — apply as `maxHeight`. */
|
|
99
131
|
maxHeight: number;
|
|
100
132
|
/** Whether the panel was flipped above the trigger. */
|
|
@@ -122,6 +154,14 @@ const FLIP_GAIN = 48;
|
|
|
122
154
|
* returned `maxHeight` is what makes the flip sufficient rather than merely different:
|
|
123
155
|
* without it a tall panel placed above just runs off the top instead of the bottom.
|
|
124
156
|
*
|
|
157
|
+
* A flipped panel hangs from its BOTTOM edge, `gap` above the trigger, and is placed by
|
|
158
|
+
* `bottom` (keksdose K1, live #401). It used to be placed by a `top` worked out as
|
|
159
|
+
* though the panel were `maxHeight` tall, and a panel is only as tall as its content:
|
|
160
|
+
* a three-row address list under a 256 px cap sat ~176 px above its field, on a phone
|
|
161
|
+
* with the keyboard up. Only when even the {@link FLOOR} does not fit above does that
|
|
162
|
+
* edge give way, as `top` always did, so the panel's head (a search box, often) stays
|
|
163
|
+
* on screen rather than above it.
|
|
164
|
+
*
|
|
125
165
|
* Split out so the geometry is testable without a DOM and a fake keyboard — @hb/ui has
|
|
126
166
|
* no test runner of its own, so this is exercised from the consuming app.
|
|
127
167
|
*/
|
|
@@ -140,19 +180,17 @@ export function anchoredPanelPlacement(
|
|
|
140
180
|
(spaceBelow < preferredHeight && (spaceAbove >= preferredHeight || spaceAbove - spaceBelow >= FLIP_GAIN)));
|
|
141
181
|
const space = Math.max(FLOOR, above ? spaceAbove : spaceBelow);
|
|
142
182
|
const maxHeight = Math.min(preferredHeight, space);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
183
|
+
// Unchanged, for the apps that call the hook and apply it: the edge to use below, and
|
|
184
|
+
// a full-height panel's top above. In layout-viewport space, like the rect.
|
|
185
|
+
const top = above ? Math.max(viewTop, rect.top - gap - maxHeight) : rect.bottom + gap;
|
|
186
|
+
// `bottom` counts up from the LAYOUT viewport's bottom edge, not the visible region's:
|
|
187
|
+
// `position: fixed` is laid out in the layout viewport, which runs on behind the
|
|
188
|
+
// keyboard, exactly as the rect is measured in it. The keyboard, the pan and the zoom
|
|
189
|
+
// are already in `maxHeight`, which keeps the panel's top inside what is on screen.
|
|
190
|
+
const layoutHeight = viewport.layoutHeight ?? viewport.top + viewport.height;
|
|
191
|
+
return { top, bottom: layoutHeight - (top + maxHeight), maxHeight, above };
|
|
148
192
|
}
|
|
149
193
|
|
|
150
|
-
/**
|
|
151
|
-
* Where to put a panel anchored under `ref`, tracking the visible viewport.
|
|
152
|
-
*
|
|
153
|
-
* Callers own horizontal placement — the pickers align to the trigger's left edge, the
|
|
154
|
-
* popovers to its right — and must apply `maxHeight` with an internal `overflow-y`.
|
|
155
|
-
*/
|
|
156
194
|
/**
|
|
157
195
|
* The region actually on screen, tracked while `active` (Keksdose live #328).
|
|
158
196
|
*
|
|
@@ -204,6 +242,14 @@ export function useVisualViewport(active: boolean): ViewportBox | null {
|
|
|
204
242
|
return box;
|
|
205
243
|
}
|
|
206
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Where to put a panel anchored under `ref`, tracking the visible viewport.
|
|
247
|
+
*
|
|
248
|
+
* Callers own horizontal placement — the pickers align to the trigger's left edge, the
|
|
249
|
+
* popovers to its right — and must apply `maxHeight` with an internal `overflow-y`.
|
|
250
|
+
* Vertically, apply `top` while the panel is below and `bottom` (with no `top`) once it
|
|
251
|
+
* is `above`; see {@link AnchoredPanel}.
|
|
252
|
+
*/
|
|
207
253
|
export function useAnchoredPanel<T extends HTMLElement>(
|
|
208
254
|
ref: RefObject<T | null>,
|
|
209
255
|
open: boolean,
|
|
@@ -236,7 +282,7 @@ export function useAnchoredPanel<T extends HTMLElement>(
|
|
|
236
282
|
}, [open]);
|
|
237
283
|
|
|
238
284
|
if (!rect) {
|
|
239
|
-
return { rect: null, top: 0, maxHeight: preferredHeight, above: false };
|
|
285
|
+
return { rect: null, top: 0, bottom: 0, maxHeight: preferredHeight, above: false };
|
|
240
286
|
}
|
|
241
287
|
return { rect, ...anchoredPanelPlacement(rect, viewport, { ...options, preferredHeight, minHeight }) };
|
|
242
288
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Keep keyboard focus inside an open overlay, and give it back when it closes.
|
|
@@ -23,6 +23,10 @@ import { useEffect, useRef } from "react";
|
|
|
23
23
|
* `<body>`, which for a keyboard user means losing their place in the page
|
|
24
24
|
* entirely. When the target is gone, focus falls back to the nearest thing that
|
|
25
25
|
* still exists rather than to nothing.
|
|
26
|
+
* - **Leaves an app's own autofocus alone** (0.33.1). React honours a child's
|
|
27
|
+
* `autoFocus` while it commits, and this hook's effect runs AFTER that commit, so
|
|
28
|
+
* focusing the container there took focus straight back off the field the app had
|
|
29
|
+
* asked for. See `initialFocus`.
|
|
26
30
|
* - **Nests.** An overlay can open another — a picker inside a dialog. Only the
|
|
27
31
|
* innermost trap handles a keystroke, and closing it hands control back to the one
|
|
28
32
|
* underneath. Innermost is decided by DOM containment rather than by mount order;
|
|
@@ -66,29 +70,61 @@ export interface FocusTrapOptions {
|
|
|
66
70
|
* what `Modal` does and why: focusing the first FIELD pops the software keyboard on
|
|
67
71
|
* a phone the moment a dialog opens, before the user has asked to type.
|
|
68
72
|
* The container needs `tabIndex={-1}` for this to work.
|
|
73
|
+
*
|
|
74
|
+
* `"container"` and `"first"` both step aside when focus is ALREADY inside the
|
|
75
|
+
* container, on something other than the container itself. That is a child the app
|
|
76
|
+
* focused on purpose — an `autoFocus`, which React honours before this hook's effect
|
|
77
|
+
* runs — and the keyboard it brings up is the one the user was promised. Moving focus
|
|
78
|
+
* off it lost keksdose's create card its amount field the instant it got it, and on a
|
|
79
|
+
* phone the numpad sheet with it, which shows only while the field is focused (live
|
|
80
|
+
* #402). A function is the caller's explicit choice and is always followed.
|
|
69
81
|
*/
|
|
70
82
|
initialFocus?: "container" | "first" | (() => HTMLElement | null);
|
|
71
83
|
}
|
|
72
84
|
|
|
85
|
+
/** Whatever has focus right now; nothing on the server, where there is no document. */
|
|
86
|
+
function focusedElement(): Element | null {
|
|
87
|
+
return typeof document === "undefined" ? null : document.activeElement;
|
|
88
|
+
}
|
|
89
|
+
|
|
73
90
|
export function useFocusTrap(
|
|
74
91
|
containerRef: React.RefObject<HTMLElement | null>,
|
|
75
92
|
{ active = true, restoreFocus = true, initialFocus = "container" }: FocusTrapOptions = {},
|
|
76
93
|
): void {
|
|
77
94
|
// Latest-ref so a caller passing an inline `initialFocus` thunk — which every caller
|
|
78
95
|
// will — does not tear the trap down and re-engage it on each render, stealing focus
|
|
79
|
-
// back to the top of the panel mid-interaction.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
96
|
+
// back to the top of the panel mid-interaction. Written in a LAYOUT effect so that
|
|
97
|
+
// the trap's cleanup, which runs after the layout effects of the same commit, reads
|
|
98
|
+
// this render's `active` rather than the last one's — see the cleanup for why.
|
|
99
|
+
const optsRef = useRef({ active, restoreFocus, initialFocus });
|
|
100
|
+
useLayoutEffect(() => {
|
|
101
|
+
optsRef.current = { active, restoreFocus, initialFocus };
|
|
83
102
|
});
|
|
84
103
|
|
|
104
|
+
// Where focus was BEFORE the trap engaged: the element it is given back to on close.
|
|
105
|
+
//
|
|
106
|
+
// Read DURING the render that turns the trap on, and that timing is the point. React
|
|
107
|
+
// honours a child's `autoFocus` while it commits that render, before any effect runs,
|
|
108
|
+
// so the effect below asking `document.activeElement` would get the child, inside the
|
|
109
|
+
// overlay. Restoring to that on close aimed focus at the dialog that was closing —
|
|
110
|
+
// already out of the DOM, so focus fell to <body> rather than going back to the
|
|
111
|
+
// button that opened it.
|
|
112
|
+
//
|
|
113
|
+
// State adjusted during render (the pattern `Modal` uses for `open`) rather than a
|
|
114
|
+
// ref written there: set once per opening and cleared on release, so a re-render
|
|
115
|
+
// while open cannot replace it with something inside, and StrictMode's second render
|
|
116
|
+
// and second effect both see the same element. `undefined` means "not captured";
|
|
117
|
+
// `null` is a capture that found nothing.
|
|
118
|
+
const [opener, setOpener] = useState<Element | null | undefined>(undefined);
|
|
119
|
+
if (active && opener === undefined) setOpener(focusedElement());
|
|
120
|
+
else if (!active && opener !== undefined) setOpener(undefined);
|
|
121
|
+
|
|
85
122
|
useEffect(() => {
|
|
86
123
|
if (!active) return;
|
|
87
124
|
const container = containerRef.current;
|
|
88
125
|
if (!container) return;
|
|
89
126
|
|
|
90
127
|
container.setAttribute(TRAP_ATTR, "");
|
|
91
|
-
const previouslyFocused = document.activeElement as HTMLElement | null;
|
|
92
128
|
|
|
93
129
|
/** Is `node` inside a trap nested within this one? */
|
|
94
130
|
const inNestedTrap = (node: Element | null) => {
|
|
@@ -106,12 +142,13 @@ export function useFocusTrap(
|
|
|
106
142
|
);
|
|
107
143
|
|
|
108
144
|
const { initialFocus: how } = optsRef.current;
|
|
145
|
+
const focused = document.activeElement;
|
|
146
|
+
// A child that already has focus was given it on purpose — see `initialFocus`.
|
|
147
|
+
const childHasFocus = focused !== container && container.contains(focused);
|
|
109
148
|
if (typeof how === "function") {
|
|
110
149
|
(how() ?? container).focus();
|
|
111
|
-
} else if (
|
|
112
|
-
(tabbables()[0] ?? container).focus();
|
|
113
|
-
} else {
|
|
114
|
-
container.focus();
|
|
150
|
+
} else if (!childHasFocus) {
|
|
151
|
+
(how === "first" ? (tabbables()[0] ?? container) : container).focus();
|
|
115
152
|
}
|
|
116
153
|
|
|
117
154
|
const onKeyDown = (e: KeyboardEvent) => {
|
|
@@ -149,12 +186,18 @@ export function useFocusTrap(
|
|
|
149
186
|
container.removeEventListener("keydown", onKeyDown);
|
|
150
187
|
container.removeAttribute(TRAP_ATTR);
|
|
151
188
|
|
|
189
|
+
// Only when the trap is really being released: the container has left the page,
|
|
190
|
+
// or `active` has turned false. StrictMode runs this cleanup once more right after
|
|
191
|
+
// mounting, with the overlay still up, and then the effect again — and React does
|
|
192
|
+
// not re-apply `autoFocus` for that second run. Restoring here would pull focus
|
|
193
|
+
// out of the autofocused field for good: live #402 again, in development.
|
|
194
|
+
if (container.isConnected && optsRef.current.active) return;
|
|
152
195
|
if (!optsRef.current.restoreFocus) return;
|
|
153
196
|
// `isConnected` is the whole point: restoring to a node that has been removed
|
|
154
197
|
// sends focus to <body> and the user loses their place entirely. When the
|
|
155
198
|
// original is gone, leave focus where the browser put it rather than moving it
|
|
156
199
|
// somewhere arbitrary — a wrong guess is worse than no move.
|
|
157
|
-
if (
|
|
200
|
+
if (opener?.isConnected) (opener as HTMLElement).focus?.();
|
|
158
201
|
};
|
|
159
|
-
}, [active, containerRef]);
|
|
202
|
+
}, [active, containerRef, opener]);
|
|
160
203
|
}
|
package/src/i18n/defaults.ts
CHANGED
|
@@ -85,6 +85,7 @@ import { DEFAULT_SETTINGS_LABELS } from "../settings/settings-labels";
|
|
|
85
85
|
import { DEFAULT_LANDING_LABELS } from "../landing/landing-labels";
|
|
86
86
|
import { DEFAULT_DEMO_LABELS } from "../demo/demo-labels";
|
|
87
87
|
import { DEFAULT_BILLING_LABELS } from "../billing/billing-labels";
|
|
88
|
+
import { DEFAULT_PLAN_CHANGE_LABELS } from "../admin/plan-change";
|
|
88
89
|
import { DEFAULT_APPEARANCE_LABELS } from "../components/appearance-labels";
|
|
89
90
|
import { DEFAULT_ROW_ACTIONS_LABELS } from "../components/row-actions";
|
|
90
91
|
import { DEFAULT_APP_SHELL_MORE_LABELS } from "../shell/app-shell";
|
|
@@ -219,6 +220,7 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
|
|
|
219
220
|
landing: DEFAULT_LANDING_LABELS,
|
|
220
221
|
demo: DEFAULT_DEMO_LABELS,
|
|
221
222
|
billing: DEFAULT_BILLING_LABELS,
|
|
223
|
+
planChange: DEFAULT_PLAN_CHANGE_LABELS,
|
|
222
224
|
appearance: DEFAULT_APPEARANCE_LABELS,
|
|
223
225
|
rowActions: DEFAULT_ROW_ACTIONS_LABELS,
|
|
224
226
|
appShellMore: DEFAULT_APP_SHELL_MORE_LABELS,
|
package/src/i18n/german.ts
CHANGED
|
@@ -1525,9 +1525,22 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1525
1525
|
// Both figures arrive formatted.
|
|
1526
1526
|
usage: (used, limit) => `${used} von ${limit}`,
|
|
1527
1527
|
contactSubject: (dimension) => `Tariflimit: ${dimension}`,
|
|
1528
|
+
// The toast's figure line (§14.11) and the plan change's over-limit lines (§14.12).
|
|
1529
|
+
limitUsageLine: (dimension, used, limit) => `${dimension}: ${used} von ${limit}`,
|
|
1528
1530
|
manage: "Zahlung und Rechnungen",
|
|
1529
1531
|
// §312k BGB: the button that leads to the cancellation says exactly this (§12.26).
|
|
1530
1532
|
cancel: "Verträge hier kündigen",
|
|
1533
|
+
// §14.5: no customer at the provider yet, so no portal to open.
|
|
1534
|
+
notAtProvider:
|
|
1535
|
+
"Noch nichts zu verwalten. Zahlungen und Rechnungen erscheinen hier, sobald Sie einen kostenpflichtigen Tarif haben.",
|
|
1536
|
+
// §14.4: the pay page's lines, built into its bundle. "Bezahlvorgang", Paddle's own
|
|
1537
|
+
// German for its checkout.
|
|
1538
|
+
payOpening: "Der sichere Bezahlvorgang wird geöffnet…",
|
|
1539
|
+
payNothing: "Hier ist nichts zu bezahlen. Um einen Tarif zu wählen, kehren Sie zur App zurück.",
|
|
1540
|
+
payFailed:
|
|
1541
|
+
"Der Bezahlvorgang konnte nicht geöffnet werden. Bitte kehren Sie zurück und versuchen Sie es gleich noch einmal.",
|
|
1542
|
+
payBack:
|
|
1543
|
+
"Der Bezahlvorgang wurde geschlossen. Sie können zurückkehren oder diese Seite neu laden, um ihn wieder zu öffnen.",
|
|
1531
1544
|
waitingChanges: (count) =>
|
|
1532
1545
|
count === 1
|
|
1533
1546
|
? `${n(count)} Änderung wartet auf einen Tarif`
|
|
@@ -1536,6 +1549,34 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1536
1549
|
discardWaiting: "Wartende Änderungen verwerfen",
|
|
1537
1550
|
notConfigured: "Zahlungen sind noch nicht eingerichtet. Bitte versuchen Sie es später noch einmal.",
|
|
1538
1551
|
disabled: "Abonnements sind hier nicht verfügbar.",
|
|
1552
|
+
// §14.2: server-kit 0.7's refusals. "Zahlung und Rechnungen", as `manage`.
|
|
1553
|
+
providerUnavailable:
|
|
1554
|
+
"Der Zahlungsanbieter ist gerade nicht erreichbar. Bitte versuchen Sie es gleich noch einmal.",
|
|
1555
|
+
alreadySubscribed: "Sie haben bereits ein Abonnement. Ihren Tarif ändern Sie unter „Zahlung und Rechnungen“.",
|
|
1556
|
+
planNotSold:
|
|
1557
|
+
"Dieser Tarif ist für diesen Abrechnungszeitraum oder diese Währung nicht erhältlich. Bitte wählen Sie einen anderen.",
|
|
1558
|
+
},
|
|
1559
|
+
// 0.33.0 — docs/billing-harmonization.md §14.12: the operator's plan parts, "Tarif" as
|
|
1560
|
+
// in `billing`; keksdose's reviewed admin wording where it had one.
|
|
1561
|
+
planChange: {
|
|
1562
|
+
column: "Tarif",
|
|
1563
|
+
title: "Tarif ändern",
|
|
1564
|
+
confirm: "Tarif ändern",
|
|
1565
|
+
plan: "Tarif",
|
|
1566
|
+
current: (plan) => `Aktueller Tarif: ${plan}`,
|
|
1567
|
+
keepsItems: "Ein kleinerer Tarif sperrt nur das Anlegen weiterer Einträge; gelöscht wird nichts.",
|
|
1568
|
+
until: "Kostenlos bis",
|
|
1569
|
+
untilHint: "Leer lassen für kein Ende. Eine laufende Beta behält ihr eigenes Ende.",
|
|
1570
|
+
needsChange: "Wählen Sie einen anderen Tarif oder ein Enddatum.",
|
|
1571
|
+
changed: (from, to) => `Tarif von ${from} auf ${to} geändert.`,
|
|
1572
|
+
set: (to) => `Tarif auf ${to} gesetzt.`,
|
|
1573
|
+
keptBeta: "Die Beta behält ihr Ende; nur der Tarif wurde geändert.",
|
|
1574
|
+
untilDone: (date) => `Kostenlos bis ${date}.`,
|
|
1575
|
+
overLimit: "Über dem Limit des neuen Tarifs: Nichts wird entfernt, neue Einträge sind gesperrt.",
|
|
1576
|
+
// 0.33.1: the app's word heads the line. After "von" it would want the dative
|
|
1577
|
+
// plural ("von 5 Objekten"), which no app's `one`/`other` form carries; as a
|
|
1578
|
+
// heading it is the nominative, in the form for the limit ("Objekt: 1 von 1").
|
|
1579
|
+
usageOf: (used, limit, dimension) => `${dimension}: ${used} von ${limit}`,
|
|
1539
1580
|
},
|
|
1540
1581
|
// 0.32.0 — docs/text-size-harmonization.md §6: the text-size and contrast settings in
|
|
1541
1582
|
// the "Darstellung" group.
|
package/src/i18n/kit-labels.tsx
CHANGED
|
@@ -81,6 +81,7 @@ import type { SettingsLabels } from "../settings/settings-labels";
|
|
|
81
81
|
import type { LandingLabels } from "../landing/landing-labels";
|
|
82
82
|
import type { DemoLabels } from "../demo/demo-labels";
|
|
83
83
|
import type { BillingLabels } from "../billing/billing-labels";
|
|
84
|
+
import type { PlanChangeLabels } from "../admin/plan-change";
|
|
84
85
|
import type { AppearanceLabels } from "../components/appearance-labels";
|
|
85
86
|
import type { RowActionsLabels } from "../components/row-actions";
|
|
86
87
|
import type { AppShellMoreLabels } from "../shell/app-shell";
|
|
@@ -398,6 +399,9 @@ export interface UiKitLabels {
|
|
|
398
399
|
demo: DemoLabels;
|
|
399
400
|
/** 0.32.0: plans, standing, banners and the limit notice (docs/billing-harmonization.md §7). */
|
|
400
401
|
billing: BillingLabels;
|
|
402
|
+
/** 0.33.0: the operator's plan parts — `planColumn`, `PlanChangeConfirm` and the
|
|
403
|
+
* lines of `usePlanChangeResult` (docs/billing-harmonization.md §14.12). */
|
|
404
|
+
planChange: PlanChangeLabels;
|
|
401
405
|
/** 0.32.0: the text-size and contrast settings (docs/text-size-harmonization.md §6). */
|
|
402
406
|
appearance: AppearanceLabels;
|
|
403
407
|
/** 0.32.0: a row's actions folded into a "⋯" menu at Large (text size §10.8). */
|
package/src/i18n/locales/es.ts
CHANGED
|
@@ -1485,13 +1485,48 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
1485
1485
|
// Both figures arrive formatted; "de", as `characterCount.count`.
|
|
1486
1486
|
usage: (used, limit) => `${used} de ${limit}`,
|
|
1487
1487
|
contactSubject: (dimension) => `Límite del plan: ${dimension}`,
|
|
1488
|
+
// The toast's figure line (§14.11) and the plan change's over-limit lines (§14.12).
|
|
1489
|
+
limitUsageLine: (dimension, used, limit) => `${dimension}: ${used} de ${limit}`,
|
|
1488
1490
|
manage: "Pagos y facturas",
|
|
1489
1491
|
cancel: "Cancelar la suscripción",
|
|
1492
|
+
// §14.5: no customer at the provider yet, so no portal to open.
|
|
1493
|
+
notAtProvider:
|
|
1494
|
+
"Todavía no hay nada que gestionar. Los pagos y las facturas aparecerán aquí cuando tenga un plan de pago.",
|
|
1495
|
+
// §14.4: the pay page's lines, built into its bundle. "Usted".
|
|
1496
|
+
payOpening: "Abriendo el pago seguro…",
|
|
1497
|
+
payNothing: "Aquí no hay nada que pagar. Para elegir un plan, vuelva a la aplicación.",
|
|
1498
|
+
payFailed: "No se ha podido abrir el pago. Vuelva atrás e inténtelo de nuevo en un momento.",
|
|
1499
|
+
payBack: "El pago se ha cerrado. Puede volver atrás o recargar esta página para abrirlo de nuevo.",
|
|
1490
1500
|
waitingChanges: (count) => `${n(count)} ${plural(count, "cambio", "cambios")} a la espera de un plan`,
|
|
1491
1501
|
// "Descartar", as `wizard.cancelConfirmLabel`.
|
|
1492
1502
|
discardWaiting: "Descartar los cambios en espera",
|
|
1493
1503
|
notConfigured: "Los pagos aún no están configurados. Inténtelo de nuevo más tarde.",
|
|
1494
1504
|
disabled: "Las suscripciones no están disponibles aquí.",
|
|
1505
|
+
// §14.2: server-kit 0.7's refusals. "Pagos y facturas", as `manage`. "Usted".
|
|
1506
|
+
providerUnavailable: "El proveedor de pagos no está disponible en este momento. Inténtelo de nuevo en un momento.",
|
|
1507
|
+
alreadySubscribed: "Ya tiene una suscripción. Para cambiar de plan, vaya a «Pagos y facturas».",
|
|
1508
|
+
planNotSold: "Este plan no está disponible para ese periodo de facturación o esa moneda. Elija otro.",
|
|
1509
|
+
},
|
|
1510
|
+
// 0.33.0 — docs/billing-harmonization.md §14.12: the operator's plan parts, "plan" as
|
|
1511
|
+
// in `billing`. "Usted".
|
|
1512
|
+
planChange: {
|
|
1513
|
+
column: "Plan",
|
|
1514
|
+
title: "Cambiar de plan",
|
|
1515
|
+
confirm: "Cambiar de plan",
|
|
1516
|
+
plan: "Plan",
|
|
1517
|
+
current: (plan) => `Plan actual: ${plan}`,
|
|
1518
|
+
keepsItems: "Un plan más pequeño solo impide crear más; no se elimina nada.",
|
|
1519
|
+
until: "Gratis hasta el",
|
|
1520
|
+
untilHint: "Déjelo vacío para que no tenga fin. Una beta en curso conserva su propio fin.",
|
|
1521
|
+
needsChange: "Elija otro plan o una fecha de fin.",
|
|
1522
|
+
changed: (from, to) => `Plan cambiado de ${from} a ${to}.`,
|
|
1523
|
+
set: (to) => `Plan establecido en ${to}.`,
|
|
1524
|
+
keptBeta: "La beta conserva su fin; solo ha cambiado el plan.",
|
|
1525
|
+
untilDone: (date) => `Gratis hasta el ${date}.`,
|
|
1526
|
+
overLimit: "Por encima del límite del nuevo plan: no se elimina nada, los elementos nuevos quedan bloqueados.",
|
|
1527
|
+
// Figures formatted; "de", as `billing.usage`. The app's word follows the limit,
|
|
1528
|
+
// in its form (0.33.1): "1 de 1 presupuesto", "1 de 5 presupuestos".
|
|
1529
|
+
usageOf: (used, limit, dimension) => `${used} de ${limit} ${dimension}`,
|
|
1495
1530
|
},
|
|
1496
1531
|
// 0.32.0 — docs/text-size-harmonization.md §6: the text-size and contrast settings in
|
|
1497
1532
|
// the "Apariencia" group.
|
package/src/i18n/locales/fr.ts
CHANGED
|
@@ -1516,14 +1516,55 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
1516
1516
|
// Both figures arrive formatted; "sur", as `characterCount.count`.
|
|
1517
1517
|
usage: (used, limit) => `${used} sur ${limit}`,
|
|
1518
1518
|
contactSubject: (dimension) => `Limite de la formule\u00a0: ${dimension}`,
|
|
1519
|
+
// The toast's figure line (§14.11) and the plan change's over-limit lines (§14.12):
|
|
1520
|
+
// a key of its own for the space before the colon.
|
|
1521
|
+
limitUsageLine: (dimension, used, limit) => `${dimension}\u00a0: ${used} sur ${limit}`,
|
|
1519
1522
|
manage: "Paiement et factures",
|
|
1520
1523
|
cancel: "Résilier l’abonnement",
|
|
1524
|
+
// §14.5: no customer at the provider yet, so no portal to open.
|
|
1525
|
+
notAtProvider:
|
|
1526
|
+
"Rien à gérer pour l’instant. Les paiements et les factures apparaîtront ici dès que vous aurez une formule payante.",
|
|
1527
|
+
// §14.4: the pay page's lines, built into its bundle.
|
|
1528
|
+
payOpening: "Ouverture du paiement sécurisé…",
|
|
1529
|
+
payNothing: "Il n’y a rien à payer ici. Pour choisir une formule, revenez à l’application.",
|
|
1530
|
+
payFailed: "Le paiement n’a pas pu s’ouvrir. Veuillez revenir en arrière et réessayer dans un instant.",
|
|
1531
|
+
payBack: "Le paiement a été fermé. Vous pouvez revenir en arrière, ou recharger cette page pour le rouvrir.",
|
|
1521
1532
|
waitingChanges: (count) =>
|
|
1522
1533
|
`${n(count)} ${plural(count, "modification en attente", "modifications en attente")} d’une formule`,
|
|
1523
1534
|
// "Abandonner", as `wizard.cancelConfirmLabel`.
|
|
1524
1535
|
discardWaiting: "Abandonner les modifications en attente",
|
|
1525
1536
|
notConfigured: "Les paiements ne sont pas encore configurés. Veuillez réessayer plus tard.",
|
|
1526
1537
|
disabled: "Les abonnements ne sont pas disponibles ici.",
|
|
1538
|
+
// §14.2: server-kit 0.7's refusals. "Paiement et factures", as `manage`.
|
|
1539
|
+
providerUnavailable:
|
|
1540
|
+
"Le prestataire de paiement est injoignable pour le moment. Veuillez réessayer dans un instant.",
|
|
1541
|
+
alreadySubscribed:
|
|
1542
|
+
"Vous avez déjà un abonnement. Pour changer de formule, allez dans «\u202fPaiement et factures\u202f».",
|
|
1543
|
+
planNotSold:
|
|
1544
|
+
"Cette formule n’est pas proposée pour cette période de facturation ou cette devise. Veuillez en choisir une autre.",
|
|
1545
|
+
},
|
|
1546
|
+
// 0.33.0 — docs/billing-harmonization.md §14.12: the operator's plan parts, "formule"
|
|
1547
|
+
// as in `billing`. "Vous".
|
|
1548
|
+
planChange: {
|
|
1549
|
+
column: "Formule",
|
|
1550
|
+
title: "Changer de formule",
|
|
1551
|
+
confirm: "Changer de formule",
|
|
1552
|
+
plan: "Formule",
|
|
1553
|
+
current: (plan) => `Formule actuelle\u00a0: ${plan}`,
|
|
1554
|
+
keepsItems: "Une formule plus petite bloque seulement les nouvelles créations\u202f; rien n’est supprimé.",
|
|
1555
|
+
until: "Gratuit jusqu’au",
|
|
1556
|
+
untilHint: "Laissez vide pour aucune fin. Une bêta en cours garde sa propre fin.",
|
|
1557
|
+
needsChange: "Choisissez une autre formule ou une date de fin.",
|
|
1558
|
+
changed: (from, to) => `Formule changée de ${from} à ${to}.`,
|
|
1559
|
+
set: (to) => `Formule définie sur ${to}.`,
|
|
1560
|
+
keptBeta: "La bêta garde sa fin\u202f; seule la formule a changé.",
|
|
1561
|
+
untilDone: (date) => `Gratuit jusqu’au ${date}.`,
|
|
1562
|
+
overLimit:
|
|
1563
|
+
"Au-delà de la limite de la nouvelle formule\u00a0: rien n’est supprimé, les nouveaux éléments sont bloqués.",
|
|
1564
|
+
// Figures formatted; "sur", as `billing.usage`. The app's word follows the limit,
|
|
1565
|
+
// in its form (0.33.1): "1 sur 1 budget", "1 sur 5 budgets" — and French reads 0
|
|
1566
|
+
// as singular, which the forms' `one` covers.
|
|
1567
|
+
usageOf: (used, limit, dimension) => `${used} sur ${limit} ${dimension}`,
|
|
1527
1568
|
},
|
|
1528
1569
|
// 0.32.0 — docs/text-size-harmonization.md §6: the text-size and contrast settings in
|
|
1529
1570
|
// the "Apparence" group. The steps agree with "taille", the modes with "contraste".
|