@eifi1/ui-kit 0.32.2 → 0.33.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 +16 -8
- package/dist/account/delete-account-setting.d.ts +10 -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 +10 -10
- package/dist/admin/admin-action-confirm.js.map +1 -1
- package/dist/admin/invitations-panel.d.ts +10 -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 +100 -0
- package/dist/admin/plan-change.js +208 -0
- package/dist/admin/plan-change.js.map +1 -0
- package/dist/admin/reviewer-scope-editor.d.ts +10 -10
- package/dist/admin/reviewer-scope-editor.js.map +1 -1
- package/dist/admin/role-select.d.ts +7 -7
- package/dist/admin/role-select.js.map +1 -1
- package/dist/admin/transfer-ownership-dialog.d.ts +10 -10
- package/dist/admin/user-roster.d.ts +7 -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 +10 -10
- package/dist/billing/billing-banner.d.ts +13 -11
- package/dist/billing/billing-banner.js.map +1 -1
- package/dist/billing/billing-labels.d.ts +10 -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 +39 -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 +154 -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 +10 -10
- package/dist/billing/checkout-processing.d.ts +217 -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 +210 -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 +10 -10
- package/dist/billing/plan-limit-notice.d.ts +10 -10
- package/dist/billing/plan-limit-toast.d.ts +143 -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 +38 -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 +9 -9
- package/dist/bin/eifi1-pay-page.mjs +166 -0
- package/dist/chart.d.ts +10 -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 +10 -10
- package/dist/components/autocomplete.d.ts +2 -1
- package/dist/components/autocomplete.js +3 -1
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/button-group.d.ts +10 -10
- package/dist/components/button-group.js +1 -1
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/calculator.d.ts +10 -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.d.ts +4 -3
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-dialog.d.ts +10 -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 +11 -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 +10 -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/entity-combobox.d.ts +3 -2
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +10 -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 +10 -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 +11 -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 +9 -9
- package/dist/components/form-actions.js +3 -3
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/iban-input.d.ts +10 -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 +10 -10
- package/dist/components/legal-page.d.ts +10 -10
- package/dist/components/legal.d.ts +10 -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/money-field.d.ts +10 -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 +10 -10
- package/dist/components/number-input.d.ts +10 -10
- package/dist/components/numpad-sheet.d.ts +10 -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 +10 -10
- package/dist/components/row-actions.d.ts +5 -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 +10 -10
- package/dist/components/settings-fields.d.ts +10 -10
- package/dist/components/share-card.d.ts +7 -7
- package/dist/components/share-card.js +26 -10
- package/dist/components/share-card.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 +9 -9
- package/dist/components/time-input.d.ts +10 -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 +9 -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-DiPX4Vue.d.ts} +355 -41
- package/dist/feedback/feedback-attachment.d.ts +10 -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 +10 -10
- package/dist/feedback/feedback-crash.d.ts +10 -10
- package/dist/feedback/feedback-dialog.d.ts +10 -10
- package/dist/feedback/feedback-inbox.d.ts +9 -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 +10 -10
- package/dist/feedback/feedback-menu.d.ts +3 -3
- package/dist/feedback/feedback-record.d.ts +10 -10
- package/dist/feedback/feedback-row-detail.d.ts +10 -10
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback/feedback-status-undo.d.ts +10 -10
- package/dist/feedback/feedback-submit.d.ts +10 -10
- package/dist/feedback/feedback-swipe.d.ts +10 -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 +10 -10
- package/dist/feedback/feedback-table.js.map +1 -1
- package/dist/feedback/feedback-thread.d.ts +10 -10
- package/dist/feedback.d.ts +10 -10
- package/dist/hooks/use-file-drop.d.ts +10 -10
- package/dist/i18n/defaults.d.ts +10 -10
- package/dist/i18n/defaults.js +2 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +10 -10
- package/dist/i18n/german.js +34 -1
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +4 -4
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +10 -10
- package/dist/i18n/locales/de-CH.d.ts +10 -10
- package/dist/i18n/locales/en.d.ts +10 -10
- package/dist/i18n/locales/es.d.ts +10 -10
- package/dist/i18n/locales/es.js +34 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +10 -10
- package/dist/i18n/locales/fr.js +35 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +10 -10
- package/dist/i18n/locales/hu.js +36 -1
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +10 -10
- package/dist/i18n/locales/it.js +34 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +10 -10
- package/dist/i18n/locales/zh.js +34 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +10 -10
- package/dist/i18n/review.js +8 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +40 -2
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.d.ts +10 -10
- package/dist/landing/landing-sections.d.ts +10 -10
- package/dist/landing/public-header.d.ts +10 -10
- package/dist/landing/routing.d.ts +10 -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 +10 -10
- package/dist/rhf/form.d.ts +10 -10
- package/dist/rhf.d.ts +10 -10
- package/dist/settings/settings-section.d.ts +10 -10
- package/dist/shell/app-shell.d.ts +9 -9
- package/dist/shell/top-bar-brand.d.ts +10 -10
- package/dist/shell/topbar-action-menu.d.ts +1 -0
- package/dist/shell.d.ts +10 -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 +11 -11
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard.d.ts +10 -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 +459 -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 +242 -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 +5 -2
- 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.tsx +4 -4
- 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/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/inline-edit-field.tsx +13 -13
- package/src/components/list.tsx +10 -9
- package/src/components/month-picker.tsx +8 -2
- package/src/components/multi-entity-combobox.tsx +3 -3
- package/src/components/numpad-sheet.tsx +8 -4
- package/src/components/row-actions.tsx +208 -58
- package/src/components/share-card.tsx +67 -13
- 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/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +38 -0
- package/src/i18n/kit-labels.tsx +4 -0
- package/src/i18n/locales/es.ts +34 -0
- package/src/i18n/locales/fr.ts +39 -0
- package/src/i18n/locales/hu.ts +37 -0
- package/src/i18n/locales/it.ts +35 -0
- package/src/i18n/locales/zh.ts +33 -0
- package/src/i18n/review.ts +7 -0
- package/src/index.ts +48 -4
- 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 +306 -46
package/src/components/chip.tsx
CHANGED
|
@@ -10,7 +10,7 @@ import { DisabledReasonLine, useDisabledReasonLine, type DisabledReasonDisplay }
|
|
|
10
10
|
import { DEFAULT_COMMON_LABELS, useKitLabels, useKitLink, useKitLocale } from "../i18n/kit-labels";
|
|
11
11
|
import { pickLinkRenderer } from "./text-link";
|
|
12
12
|
import { Tooltip } from "./tooltip";
|
|
13
|
-
import { useCommitReason } from "./write-lock";
|
|
13
|
+
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* A chip: a compact pill carrying one value.
|
|
@@ -137,7 +137,9 @@ const TONE: Record<ChipTone, { idle: string; selected: string }> = {
|
|
|
137
137
|
// outline toggle that stays an outline when on has no visible state.
|
|
138
138
|
const OUTLINE: Record<ChipTone, string> = {
|
|
139
139
|
neutral: "border-[var(--border-strong)] bg-transparent text-[var(--text-secondary)]",
|
|
140
|
-
brand
|
|
140
|
+
// The brand AS TEXT is `--brand-muted` (0.33): `--brand` itself is solved to 3:1, a
|
|
141
|
+
// fill's boundary, and as text it fell to 3.0:1 in the derived dark presets.
|
|
142
|
+
brand: "border-[var(--brand)] bg-transparent text-brand-muted",
|
|
141
143
|
danger: "border-[var(--danger-border)] bg-transparent text-[var(--danger)]",
|
|
142
144
|
warning: "border-[var(--warning-border)] bg-transparent text-[var(--warning)]",
|
|
143
145
|
success: "border-[var(--success-border)] bg-transparent text-[var(--success)]",
|
|
@@ -151,25 +153,27 @@ const OUTLINE: Record<ChipTone, string> = {
|
|
|
151
153
|
orange: "border-[var(--hue-orange-border)] bg-transparent text-[var(--hue-orange)]",
|
|
152
154
|
};
|
|
153
155
|
|
|
154
|
-
// `solid`: the fill under its contrast
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
156
|
+
// `solid`: the fill under its own foreground, `<fill>-contrast` (0.33, colour-roles
|
|
157
|
+
// §5.1): white in light, the tone's 950 (the page, for money) on the dark theme's
|
|
158
|
+
// pastels. Until 0.33 only brand and danger had one and the rest borrowed
|
|
159
|
+
// `--text-inverse`, the surface colour — which left light warning at 4.24:1 and orange
|
|
160
|
+
// at 4.38:1. Neutral keeps the inverse pair: a count pill, louder than the neutral
|
|
161
|
+
// fill. The border goes transparent rather than away, so a solid chip is exactly as
|
|
162
|
+
// tall as a soft one.
|
|
159
163
|
const SOLID: Record<ChipTone, string> = {
|
|
160
|
-
neutral: "border-transparent bg-
|
|
161
|
-
brand: "border-transparent bg-
|
|
162
|
-
danger: "border-transparent bg-
|
|
163
|
-
warning: "border-transparent bg-
|
|
164
|
-
success: "border-transparent bg-
|
|
165
|
-
info: "border-transparent bg-
|
|
166
|
-
income: "border-transparent bg-
|
|
167
|
-
expense: "border-transparent bg-
|
|
168
|
-
blue: "border-transparent bg-
|
|
169
|
-
indigo: "border-transparent bg-
|
|
170
|
-
purple: "border-transparent bg-
|
|
171
|
-
teal: "border-transparent bg-
|
|
172
|
-
orange: "border-transparent bg-
|
|
164
|
+
neutral: "border-transparent bg-inverse text-inverse",
|
|
165
|
+
brand: "border-transparent bg-brand text-brand-contrast",
|
|
166
|
+
danger: "border-transparent bg-danger text-danger-contrast",
|
|
167
|
+
warning: "border-transparent bg-warning text-warning-contrast",
|
|
168
|
+
success: "border-transparent bg-success text-success-contrast",
|
|
169
|
+
info: "border-transparent bg-info text-info-contrast",
|
|
170
|
+
income: "border-transparent bg-money-pos text-money-income-contrast",
|
|
171
|
+
expense: "border-transparent bg-money-neg text-money-expense-contrast",
|
|
172
|
+
blue: "border-transparent bg-hue-blue text-hue-blue-contrast",
|
|
173
|
+
indigo: "border-transparent bg-hue-indigo text-hue-indigo-contrast",
|
|
174
|
+
purple: "border-transparent bg-hue-purple text-hue-purple-contrast",
|
|
175
|
+
teal: "border-transparent bg-hue-teal text-hue-teal-contrast",
|
|
176
|
+
orange: "border-transparent bg-hue-orange text-hue-orange-contrast",
|
|
173
177
|
};
|
|
174
178
|
|
|
175
179
|
/** `dot`: the fill of the dot itself — the tone's strongest colour, which is the one
|
|
@@ -380,7 +384,8 @@ export function refreshChipEdges(): void {
|
|
|
380
384
|
for (let pass = 0; pass <= n && pending.length; pass++) {
|
|
381
385
|
const reads = pending.map((el) => {
|
|
382
386
|
const r = el.getBoundingClientRect();
|
|
383
|
-
|
|
387
|
+
// The text label (CHIP_LABEL_TRUNCATE; `.truncate` before 0.33).
|
|
388
|
+
const label = el.querySelector<HTMLElement>(".truncate-until-large");
|
|
384
389
|
// Not laid out (a hidden ancestor, jsdom), or truncated at its container's edge,
|
|
385
390
|
// where a wider chip would overflow: leave it alone. So too a label wrapped onto
|
|
386
391
|
// a second line at Large (0.32.1): the chip is already as wide as its row lets it
|
|
@@ -511,16 +516,16 @@ const CHIP_BASE = `${CHIP_PILL} ${CHIP_RING}`;
|
|
|
511
516
|
* "…". keksdose's "Zur Prüfung zurücklegen" read "Zur Prüfung zu…" on a phone at Extra
|
|
512
517
|
* large, which is a different instruction. No size has a fixed height (padding and
|
|
513
518
|
* `min-h-*` only), so the pill grows with its lines; the mark before the text stays
|
|
514
|
-
* centred on them, as in a pill. A `large:`
|
|
519
|
+
* centred on them, as in a pill. A `large:` rule, so Normal keeps its one-line chip.
|
|
515
520
|
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
* the
|
|
521
|
+
* 0.33 (§10.17): the kit's one `truncate-until-large` (tokens.css) in place of this
|
|
522
|
+
* file's own `truncate` + `large:whitespace-normal large:break-words`, as StatusDot's
|
|
523
|
+
* label took it. The utility breaks with `overflow-wrap: anywhere`, which lowers the
|
|
524
|
+
* label's min-content width to one letter: a status chip in an auto-sized table column
|
|
525
|
+
* wants the column given a width at Large, or it is squeezed to a letter per line
|
|
526
|
+
* (StatusDot's label was, in the showcase's tone table at 360 px, Extra large).
|
|
522
527
|
*/
|
|
523
|
-
const
|
|
528
|
+
const CHIP_LABEL_TRUNCATE = "truncate-until-large";
|
|
524
529
|
|
|
525
530
|
/**
|
|
526
531
|
* An interactive chip's touch target at Large and Extra large (docs/text-size-harmonization.md
|
|
@@ -720,7 +725,7 @@ export type ChipProps = ChipBaseProps &
|
|
|
720
725
|
/** This chip COMMITS — pressing it saves. Under a locked
|
|
721
726
|
* {@link WriteLockProvider} it takes the `disabledReason` path with the lock's
|
|
722
727
|
* reason, as {@link Button}'s `commit` does. */
|
|
723
|
-
commit?:
|
|
728
|
+
commit?: CommitScope;
|
|
724
729
|
}
|
|
725
730
|
);
|
|
726
731
|
|
|
@@ -841,7 +846,7 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
841
846
|
) : (
|
|
842
847
|
<Icon className={cn(s.icon, "shrink-0")} aria-hidden />
|
|
843
848
|
))}
|
|
844
|
-
{/* Text truncates at Normal and wraps at Large (
|
|
849
|
+
{/* Text truncates at Normal and wraps at Large (CHIP_LABEL_TRUNCATE). Mixed
|
|
845
850
|
children (an icon and a word) sit in a row instead: as a plain span, the svg —
|
|
846
851
|
a block under Tailwind's preflight — stacked above the text (keksdose live
|
|
847
852
|
#358). */}
|
|
@@ -849,7 +854,7 @@ export const Chip = forwardRef<HTMLElement, ChipProps>(function Chip(
|
|
|
849
854
|
className={cn(
|
|
850
855
|
"min-w-0",
|
|
851
856
|
typeof children === "string" || typeof children === "number"
|
|
852
|
-
?
|
|
857
|
+
? CHIP_LABEL_TRUNCATE
|
|
853
858
|
: "inline-flex items-center gap-1 [&>svg]:size-[1em] [&>svg]:shrink-0",
|
|
854
859
|
)}
|
|
855
860
|
>
|
|
@@ -247,13 +247,15 @@ const META_TONE: Record<ActionCardMetaTone, string> = {
|
|
|
247
247
|
};
|
|
248
248
|
|
|
249
249
|
/** The colour of {@link ActionCardProps.icon}. `default` is the secondary text colour
|
|
250
|
-
* the icon has always had; `brand` tints it (keksdose's privacy enrolment)
|
|
250
|
+
* the icon has always had; `brand` tints it (keksdose's privacy enrolment) with the
|
|
251
|
+
* brand as text, `--brand-muted` (0.33, docs/colour-roles-harmonization.md §5.5):
|
|
252
|
+
* `--brand` read 3.03:1 on the card's hover in the derived dark presets. */
|
|
251
253
|
export type ActionCardIconTone = "default" | "muted" | "brand" | "warning" | "danger" | "info" | "success";
|
|
252
254
|
|
|
253
255
|
const ICON_TONE: Record<ActionCardIconTone, string> = {
|
|
254
256
|
default: "text-[var(--text-secondary)]",
|
|
255
257
|
...META_TONE,
|
|
256
|
-
brand: "text-
|
|
258
|
+
brand: "text-brand-muted",
|
|
257
259
|
};
|
|
258
260
|
|
|
259
261
|
/** What {@link ActionCardProps.renderLink} (and the provider's `linkComponent`) is
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
useLockReason,
|
|
35
35
|
} from "./field-parts";
|
|
36
36
|
import { hasMessage, mergeDescribedBy } from "./choice-parts";
|
|
37
|
-
import { useCommitReason } from "./write-lock";
|
|
37
|
+
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
38
38
|
|
|
39
39
|
// One look for both combobox flavors below — the suggestion list and its rows
|
|
40
40
|
// must stay pixel-identical between the free-text and the id-keyed variant (and
|
|
@@ -207,7 +207,7 @@ export interface ComboboxProps
|
|
|
207
207
|
/** This field COMMITS. Under a locked {@link WriteLockProvider} it is locked the
|
|
208
208
|
* `disabledReason` way with the lock's reason; inside a form with its own Save, leave
|
|
209
209
|
* it off and put `commit` on the Save. No provider, or an unlocked one: no effect. */
|
|
210
|
-
commit?:
|
|
210
|
+
commit?: CommitScope;
|
|
211
211
|
/** Heading an option belongs under. Supplying it makes this list read exactly
|
|
212
212
|
* like {@link InlineEntityCombobox}'s — one heading per group with its rows
|
|
213
213
|
* indented beneath — instead of a flat list (feedback #136 rework: the payee
|
|
@@ -849,7 +849,7 @@ export interface InlineEntityComboboxProps<V extends string | number, C extends
|
|
|
849
849
|
* A picker inside a form with its own Save stays editable under the lock — leave this
|
|
850
850
|
* off there and put `commit` on the Save. No provider, or an unlocked one: no effect.
|
|
851
851
|
*/
|
|
852
|
-
commit?:
|
|
852
|
+
commit?: CommitScope;
|
|
853
853
|
/**
|
|
854
854
|
* A last row that makes a new record from what was typed — keksdose G9, whose "Create
|
|
855
855
|
* cash account" had to become a separate button because this picker had no such row.
|
|
@@ -894,7 +894,7 @@ export interface InlineEntityComboboxProps<V extends string | number, C extends
|
|
|
894
894
|
* one explanation a phone cannot show), and `onCreate` is not called. Not needed with
|
|
895
895
|
* `commit`: a locked field opens no list at all.
|
|
896
896
|
*/
|
|
897
|
-
createCommit?:
|
|
897
|
+
createCommit?: CommitScope;
|
|
898
898
|
/**
|
|
899
899
|
* The `<input>` — the focusable control, the one a reader types in — as a React 19
|
|
900
900
|
* ref prop (0.24, kastlan). Its account picker is this component inside an
|
|
@@ -6,7 +6,7 @@ import { TypedConfirmField, typedMatches } from "./danger-confirm";
|
|
|
6
6
|
import type { TypedMatch } from "./danger-confirm";
|
|
7
7
|
import { DialogFrame } from "./dialog-frame";
|
|
8
8
|
import { Button } from "./ui";
|
|
9
|
-
import { useWriteLock } from "./write-lock";
|
|
9
|
+
import { useWriteLock, writeLockFor, type CommitScope } from "./write-lock";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* The `confirmDialog` namespace of `<UiKitProvider labels>`: the two buttons' fallback
|
|
@@ -83,8 +83,12 @@ export interface ConfirmOptions {
|
|
|
83
83
|
* host sits — it is `aria-disabled` with the lock's reason, so a read-only page (a
|
|
84
84
|
* demo, a viewer's budget) can't confirm a delete through a dialog. One prop for the
|
|
85
85
|
* many confirm sites an app has (kastlan's 0.31 adoption counted ~23).
|
|
86
|
+
*
|
|
87
|
+
* 0.33: a {@link CommitScope} — `confirm({ commit: COMMIT_EXCEPT_BILLING })` for a
|
|
88
|
+
* confirm a lapsed plan still allows (removing access, the account's own settings;
|
|
89
|
+
* docs/billing-harmonization.md §12.36).
|
|
86
90
|
*/
|
|
87
|
-
commit?:
|
|
91
|
+
commit?: CommitScope;
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
export type ConfirmFn = (options: ConfirmOptions) => Promise<boolean>;
|
|
@@ -107,12 +111,15 @@ export function useConfirm(): ConfirmFn {
|
|
|
107
111
|
throw new Error("useConfirm() must be called below a <ConfirmProvider>.");
|
|
108
112
|
}
|
|
109
113
|
// The lock of the CALLER's place: the dialog host usually sits above the app's
|
|
110
|
-
// `WriteLockProvider`, so it could not read the lock itself.
|
|
114
|
+
// `WriteLockProvider`, so it could not read the lock itself. Each call's own `commit`
|
|
115
|
+
// scope is applied to it when the call runs (`writeLockFor`, §12.36).
|
|
111
116
|
const lock = useWriteLock();
|
|
112
117
|
return useCallback(
|
|
113
|
-
(options: ConfirmOptions) =>
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
(options: ConfirmOptions) => {
|
|
119
|
+
const scoped = writeLockFor(lock, options.commit);
|
|
120
|
+
return confirm(scoped.locked ? ({ ...options, lockReason: scoped.reason } as ConfirmOptions) : options);
|
|
121
|
+
},
|
|
122
|
+
[confirm, lock],
|
|
116
123
|
);
|
|
117
124
|
}
|
|
118
125
|
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type ComboOption,
|
|
14
14
|
} from "./combobox-core";
|
|
15
15
|
import { assignRef, hasMessage, mergeDescribedBy } from "./choice-parts";
|
|
16
|
-
import { useCommitReason } from "./write-lock";
|
|
16
|
+
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
17
17
|
import {
|
|
18
18
|
DEFAULT_COMBOBOX_LABELS,
|
|
19
19
|
DEFAULT_COMMON_LABELS,
|
|
@@ -147,7 +147,7 @@ export interface CountrySelectProps<Clearable extends boolean = false>
|
|
|
147
147
|
* reason. A country field inside a form that has its own Save stays editable under
|
|
148
148
|
* the lock — leave this off there, and put `commit` on the Save.
|
|
149
149
|
*/
|
|
150
|
-
commit?:
|
|
150
|
+
commit?: CommitScope;
|
|
151
151
|
/** The language the names are in. Default: the `<UiKitProvider locale>`, else
|
|
152
152
|
* English (see {@link countryName}). */
|
|
153
153
|
locale?: string;
|
|
@@ -6,7 +6,7 @@ import { Button, Input, Label, Spinner } from "./ui";
|
|
|
6
6
|
import type { ButtonVariant, InputProps } from "./ui";
|
|
7
7
|
import { Checkbox } from "./checkbox";
|
|
8
8
|
import { Tooltip } from "./tooltip";
|
|
9
|
-
import { useCommitReason } from "./write-lock";
|
|
9
|
+
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* How typed text is compared with the text it has to repeat — shared by
|
|
@@ -567,7 +567,7 @@ export interface DangerConfirmProps extends Omit<ComponentPropsWithoutRef<"div">
|
|
|
567
567
|
* provider no longer threads `lockedReason={lock.locked ? lock.reason : undefined}`
|
|
568
568
|
* into every tile. No provider, or an unlocked one: no effect.
|
|
569
569
|
*/
|
|
570
|
-
commit?:
|
|
570
|
+
commit?: CommitScope;
|
|
571
571
|
/** Controlled armed state. The parent can then collapse the tile from a mutation's
|
|
572
572
|
* own `onSuccess` without returning a promise. */
|
|
573
573
|
armed?: boolean;
|
|
@@ -246,6 +246,23 @@ export interface DataTableProps<T> {
|
|
|
246
246
|
rowHref?: (row: T) => string | undefined;
|
|
247
247
|
expandedRow?: (row: T) => ReactNode | null;
|
|
248
248
|
isExpanded?: (row: T) => boolean;
|
|
249
|
+
/**
|
|
250
|
+
* Extra classes for a row — the `<tr>` on desktop, the card on mobile. Merged last,
|
|
251
|
+
* so a `hover:` fill here replaces the row's own `hover:bg-[var(--bg-hover)]`.
|
|
252
|
+
*
|
|
253
|
+
* **A row you paint has to pick its own hover** (0.33, docs/colour-roles-harmonization.md
|
|
254
|
+
* §12.2): the kit cannot see your rest colour, and `--bg-hover` is mixed against the
|
|
255
|
+
* card. The rule: a hover differs from its rest by 1.08:1 or more, and every text role
|
|
256
|
+
* on it keeps 4.5:1.
|
|
257
|
+
* - A row that rests on the well (`bg-surface-2`) hovers to the CARD,
|
|
258
|
+
* `hover:bg-surface`: in light the well is the page, and `--bg-hover` sits 1.04:1
|
|
259
|
+
* from it.
|
|
260
|
+
* - A row whose well MARKS A STATE (selected) keeps its fill and draws a 1 px inside
|
|
261
|
+
* line instead, since the card would read as "not selected":
|
|
262
|
+
* `bg-surface-2 hover:bg-surface-2 hover:outline hover:-outline-offset-1 hover:outline-strong`.
|
|
263
|
+
* An outline, not a shadow: a shadow on a `<tr>` is not painted by every engine.
|
|
264
|
+
* - Not `--bg-active` for a selected row: its muted and money text fall to 3.8–4.7:1.
|
|
265
|
+
*/
|
|
249
266
|
rowClassName?: (row: T) => string | undefined;
|
|
250
267
|
/**
|
|
251
268
|
* Extra DOM attributes for a row's container — the `<tr>` on desktop, the
|
|
@@ -1199,15 +1216,9 @@ export function DataTable<T>({
|
|
|
1199
1216
|
label: a.label,
|
|
1200
1217
|
icon: a.icon,
|
|
1201
1218
|
onCommit: () => void runRowAction(a.action, row),
|
|
1202
|
-
//
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
? "bg-[var(--danger)] text-[var(--danger-contrast)]"
|
|
1206
|
-
: "bg-[var(--brand)] text-[var(--brand-contrast)]",
|
|
1207
|
-
armedClassName:
|
|
1208
|
-
a.tone === "danger"
|
|
1209
|
-
? "bg-[var(--danger-hover)] text-[var(--danger-contrast)]"
|
|
1210
|
-
: "bg-[var(--brand-hover)] text-[var(--brand-contrast)]",
|
|
1219
|
+
// The row action's tone, painted by SwipeableRow: its soft wash idle, its fill
|
|
1220
|
+
// and that fill's foreground armed (0.33).
|
|
1221
|
+
tone: a.tone === "danger" ? "danger" : "brand",
|
|
1211
1222
|
});
|
|
1212
1223
|
return {
|
|
1213
1224
|
start: swipes.filter((a) => a.swipe === "start").map(toSwipe),
|
|
@@ -17,7 +17,7 @@ import { splitTriggerAria } from "./trigger-aria";
|
|
|
17
17
|
import type { TriggerAria } from "./trigger-aria";
|
|
18
18
|
import { Button, FIELD_BASE, FIELD_TRIGGER, FIELD_FLOATING_PAD, FIELD_INVALID } from "./ui";
|
|
19
19
|
import { FOCUS_RING_WIDTH } from "./focus-ring";
|
|
20
|
-
import { FieldBox, FieldLabelLine, useFieldMessages } from "./field-parts";
|
|
20
|
+
import { FieldBox, FieldLabelLine, LABEL_ABOVE_BOX, useFieldMessages } from "./field-parts";
|
|
21
21
|
import { FullBleedDialog } from "./full-bleed-dialog";
|
|
22
22
|
import { usePhoneLayout, type RemLength } from "../hooks/use-breakpoint";
|
|
23
23
|
import { DEFAULT_MINI_CALENDAR_LABELS, MiniCalendar, type MiniCalendarProps } from "./mini-calendar";
|
|
@@ -210,11 +210,12 @@ function CustomTrigger({
|
|
|
210
210
|
* The trigger's value wraps instead of truncating at Large and Extra large (0.32.1, the
|
|
211
211
|
* 360 px Extra-large sweep; docs/text-size-harmonization.md §4 "nothing truncates"). A
|
|
212
212
|
* range ("01/09/2026 – 30/09/2026") is 312 px at 150 %, and on a 360 px phone it read
|
|
213
|
-
* "01/09/2026 – 30/…" — its end, the half a reader needs, was the half cut off.
|
|
214
|
-
*
|
|
215
|
-
*
|
|
213
|
+
* "01/09/2026 – 30/…" — its end, the half a reader needs, was the half cut off. A
|
|
214
|
+
* `large:` class, so Normal keeps its one-line field: since 0.33 (§10.17) the kit's one
|
|
215
|
+
* `truncate-until-large` (tokens.css) in place of this file's own `truncate` +
|
|
216
|
+
* `large:whitespace-normal large:break-words`.
|
|
216
217
|
*/
|
|
217
|
-
const
|
|
218
|
+
const VALUE_TRUNCATE = "truncate-until-large";
|
|
218
219
|
|
|
219
220
|
/**
|
|
220
221
|
* The field-shaped button that opens the calendar.
|
|
@@ -330,7 +331,7 @@ function DateFieldTrigger({
|
|
|
330
331
|
|
|
331
332
|
return (
|
|
332
333
|
<button {...attrs}>
|
|
333
|
-
<span id={valueId} className={cn(
|
|
334
|
+
<span id={valueId} className={cn(VALUE_TRUNCATE, !hasValue && "text-[var(--text-placeholder)]")}>
|
|
334
335
|
{/* `|| " "` (a NON-BREAKING space) — triggerText is "" when there is no value and no
|
|
335
336
|
placeholder was passed. An empty span has no line box, so the
|
|
336
337
|
trigger collapses to its padding and sits shorter than every
|
|
@@ -515,7 +516,9 @@ function DateField({
|
|
|
515
516
|
return (
|
|
516
517
|
// The caller's attributes land here, on the field's own box — the trigger inside is
|
|
517
518
|
// named by `aria-labelledby` and must keep the id pair it is given.
|
|
518
|
-
|
|
519
|
+
// At Large a labelled box is a grid: the label above, the calendar glyph and the
|
|
520
|
+
// clear × measured against the trigger's row (LABEL_ABOVE_BOX, 0.33 §10.17).
|
|
521
|
+
<div {...wrapperRest} ref={rootRef} className={cn("relative", label !== undefined && LABEL_ABOVE_BOX, className)}>
|
|
519
522
|
{label !== undefined && <FieldLabelLine label={label} hint={labelHint} />}
|
|
520
523
|
{/* The visible FieldLabel is a plain span, not a `<label htmlFor>`, so it names
|
|
521
524
|
nothing on its own — this hidden twin is what the trigger is named by.
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
useLockReason,
|
|
24
24
|
} from "./field-parts";
|
|
25
25
|
import { assignRef, mergeDescribedBy } from "./choice-parts";
|
|
26
|
-
import { useCommitReason } from "./write-lock";
|
|
26
|
+
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
27
27
|
|
|
28
28
|
export type { ComboClearValue, ComboOption } from "./combobox-core";
|
|
29
29
|
|
|
@@ -89,7 +89,7 @@ export interface EntityComboboxProps<V extends string | number, C extends ComboC
|
|
|
89
89
|
* A picker inside a form with its own Save stays editable under the lock: leave this
|
|
90
90
|
* off there and put `commit` on the Save. No provider, or an unlocked one: no effect.
|
|
91
91
|
*/
|
|
92
|
-
commit?:
|
|
92
|
+
commit?: CommitScope;
|
|
93
93
|
/** When set, a "create" row appears for a non-empty query with no exact match. */
|
|
94
94
|
onCreate?: (query: string) => void;
|
|
95
95
|
createLabel?: (query: string) => string;
|
|
@@ -106,7 +106,7 @@ export interface EntityComboboxProps<V extends string | number, C extends ComboC
|
|
|
106
106
|
* taken, with the lock's reason as its second line — the picker itself stays live.
|
|
107
107
|
* keksdose dev#496: on the read-only demo choosing is draft state, minting is not.
|
|
108
108
|
*/
|
|
109
|
-
createCommit?:
|
|
109
|
+
createCommit?: CommitScope;
|
|
110
110
|
/** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */
|
|
111
111
|
invalid?: boolean;
|
|
112
112
|
/** What is wrong with the value, as {@link Input}'s `error`: rendered under the
|
|
@@ -5,7 +5,7 @@ import { cn } from "../lib/cn";
|
|
|
5
5
|
import { useInlineFacts } from "../hooks/use-large-text";
|
|
6
6
|
import { FOCUS_RING } from "./focus-ring";
|
|
7
7
|
import { Tooltip, type TooltipSide } from "./tooltip";
|
|
8
|
-
import { useCommitReason } from "./write-lock";
|
|
8
|
+
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
9
9
|
import { hasMessage, mergeDescribedBy } from "./choice-parts";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -34,12 +34,45 @@ import { hasMessage, mergeDescribedBy } from "./choice-parts";
|
|
|
34
34
|
export const STATIC_LABEL_TYPE =
|
|
35
35
|
"text-caption leading-tight text-[var(--text-muted)] peer-disabled:opacity-50";
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* A field's label at Large and Extra large (0.33, docs/text-size-harmonization.md
|
|
39
|
+
* §10.17): a static label ABOVE the field — the phone card's "label above value" (§4) —
|
|
40
|
+
* not a label squeezed into the field's top strip, where a long one ended in "…". It
|
|
41
|
+
* stops being absolute, so it wraps like any text (`truncate-until-large` beside it); it
|
|
42
|
+
* takes the pointer, since nothing of the field lies under it any more; and the field
|
|
43
|
+
* no longer reserves the strip for it (`FIELD_FLOATING_PAD`). In the first row of a box
|
|
44
|
+
* that is a grid at Large ({@link LABEL_ABOVE_BOX}), wherever it sits in the DOM — a
|
|
45
|
+
* floating label FOLLOWS its input, for `peer-*`. `large:` classes, so it costs no
|
|
46
|
+
* render and is right on the first paint; Normal keeps the floating label.
|
|
47
|
+
*
|
|
48
|
+
* The placement only: a label element adds `large:block` (a `<span>` or `<label>` in
|
|
49
|
+
* flow), a label ROW keeps its `flex`.
|
|
50
|
+
*/
|
|
51
|
+
export const LABEL_ABOVE_AT_LARGE =
|
|
52
|
+
"large:static large:row-start-1 large:mb-1 large:max-w-none large:pointer-events-auto";
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The box a field's label is positioned in — {@link FloatingField}'s `relative` wrapper,
|
|
56
|
+
* a date or month picker's root — at Large, while it holds a label above
|
|
57
|
+
* ({@link LABEL_ABOVE_AT_LARGE}): one column, the label's row over the field's. The
|
|
58
|
+
* field's own absolute parts in the box (a chevron, a clear ×, a reveal toggle, a
|
|
59
|
+
* calculator) are placed in the second row, so their `top-1/2` and `inset-y-*` still
|
|
60
|
+
* measure the FIELD and not label and field together; the label's own elements carry
|
|
61
|
+
* `data-field-label` and stay in the first. Only on a box with a visible label: without
|
|
62
|
+
* one the second row would be empty.
|
|
63
|
+
*/
|
|
64
|
+
export const LABEL_ABOVE_BOX =
|
|
65
|
+
"large:grid large:grid-cols-[minmax(0,1fr)] large:[&>.absolute:not([data-field-label])]:row-start-2";
|
|
66
|
+
|
|
37
67
|
// A field that always has a value (select / dropdown trigger) keeps the label
|
|
38
|
-
// permanently in the floated position — small, in the top strip, value below.
|
|
68
|
+
// permanently in the floated position — small, in the top strip, value below. At Large,
|
|
69
|
+
// a label above the field (LABEL_ABOVE_AT_LARGE).
|
|
39
70
|
export const FLOATING_LABEL_STATIC = cn(
|
|
40
71
|
"pointer-events-none absolute start-3 top-1",
|
|
41
72
|
STATIC_LABEL_TYPE,
|
|
42
|
-
"max-w-[calc(100%-1.5rem)] truncate",
|
|
73
|
+
"max-w-[calc(100%-1.5rem)] truncate-until-large",
|
|
74
|
+
LABEL_ABOVE_AT_LARGE,
|
|
75
|
+
"large:block",
|
|
43
76
|
);
|
|
44
77
|
|
|
45
78
|
/**
|
|
@@ -53,7 +86,9 @@ export interface FieldLabelProps extends ComponentPropsWithoutRef<"span"> {
|
|
|
53
86
|
|
|
54
87
|
export function FieldLabel({ children, className, ...rest }: FieldLabelProps) {
|
|
55
88
|
return (
|
|
56
|
-
|
|
89
|
+
// `data-field-label`: a label of the box it stands in, kept in the first row at Large
|
|
90
|
+
// (LABEL_ABOVE_BOX).
|
|
91
|
+
<span data-field-label="" {...rest} className={cn(FLOATING_LABEL_STATIC, "z-10", className)}>
|
|
57
92
|
{children}
|
|
58
93
|
</span>
|
|
59
94
|
);
|
|
@@ -207,9 +242,10 @@ export function FieldCaption({ parts, className }: { parts: FieldHintParts; clas
|
|
|
207
242
|
* Classes that take a static floating label (`FLOATING_LABEL_STATIC`: `FieldLabel`,
|
|
208
243
|
* `ComboboxFieldLabel`) out of its own absolute slot and into a {@link StaticLabelRow}.
|
|
209
244
|
* `static` wins over `absolute` through `cn`'s merge; `min-w-0` lets it truncate in
|
|
210
|
-
* the row.
|
|
245
|
+
* the row. At Large the ROW stands above the field, so the label inside it drops the
|
|
246
|
+
* margin a label above takes alone (`large:mb-0`), and stays level with its "?".
|
|
211
247
|
*/
|
|
212
|
-
export const LABEL_IN_ROW = "static min-w-0";
|
|
248
|
+
export const LABEL_IN_ROW = "static min-w-0 large:mb-0";
|
|
213
249
|
|
|
214
250
|
/**
|
|
215
251
|
* A static label and its "?" on one line, in the top strip of a field that lays out its
|
|
@@ -222,7 +258,10 @@ export const LABEL_IN_ROW = "static min-w-0";
|
|
|
222
258
|
*/
|
|
223
259
|
export function StaticLabelRow({ hint, children }: { hint: ReactNode; children: ReactNode }) {
|
|
224
260
|
return (
|
|
225
|
-
<div
|
|
261
|
+
<div
|
|
262
|
+
data-field-label=""
|
|
263
|
+
className={cn("pointer-events-none absolute start-3 end-9 top-1 z-10 flex items-center gap-1", LABEL_ABOVE_AT_LARGE)}
|
|
264
|
+
>
|
|
226
265
|
{children}
|
|
227
266
|
<span className="pointer-events-auto flex shrink-0 items-center">{hint}</span>
|
|
228
267
|
</div>
|
|
@@ -270,7 +309,7 @@ export interface LockReason {
|
|
|
270
309
|
* switch, a checkbox or a select that saves on change, a list row whose click writes.
|
|
271
310
|
* The lock's reason wins over the control's own (`useCommitReason`).
|
|
272
311
|
*/
|
|
273
|
-
export function useLockReason(commit:
|
|
312
|
+
export function useLockReason(commit: CommitScope | undefined, disabledReason: ReactNode): LockReason {
|
|
274
313
|
const reason = useCommitReason(commit, disabledReason);
|
|
275
314
|
const reasonId = useId();
|
|
276
315
|
return { reason, locked: hasMessage(reason), reasonId };
|
|
@@ -510,9 +549,12 @@ export function FieldLabelLine({ label, hint }: { label: ReactNode; hint?: React
|
|
|
510
549
|
if (!hasContent(hint)) return <FieldLabel>{label}</FieldLabel>;
|
|
511
550
|
return (
|
|
512
551
|
// `z-10` as FieldLabel has it: the trigger after it is `relative` and would paint
|
|
513
|
-
// over the row (and take the "?"'s hover) otherwise.
|
|
514
|
-
<div
|
|
515
|
-
|
|
552
|
+
// over the row (and take the "?"'s hover) otherwise. Above the field at Large.
|
|
553
|
+
<div
|
|
554
|
+
data-field-label=""
|
|
555
|
+
className={cn("pointer-events-none absolute start-3 end-9 top-1 z-10 flex items-center gap-1", LABEL_ABOVE_AT_LARGE)}
|
|
556
|
+
>
|
|
557
|
+
<FieldLabel className={cn(LABEL_IN_ROW, "max-w-full")}>{label}</FieldLabel>
|
|
516
558
|
<span className="pointer-events-auto flex shrink-0 items-center">{hint}</span>
|
|
517
559
|
</div>
|
|
518
560
|
);
|
|
@@ -570,12 +612,17 @@ export function LabelStrip({
|
|
|
570
612
|
children: ReactNode;
|
|
571
613
|
}) {
|
|
572
614
|
return (
|
|
573
|
-
|
|
574
|
-
|
|
615
|
+
// At Large the row is a label above the group, in flow (0.33, §10.17), and the
|
|
616
|
+
// strip's padding goes: nothing lies under it any more.
|
|
617
|
+
<div {...rest} className={cn("relative min-w-0", LABEL_STRIP_PAD[pad], "large:pt-0", className)}>
|
|
618
|
+
<div
|
|
619
|
+
data-field-label=""
|
|
620
|
+
className={cn("pointer-events-none absolute inset-x-3 top-1 flex items-center gap-1", LABEL_ABOVE_AT_LARGE)}
|
|
621
|
+
>
|
|
575
622
|
{/* `static`: FieldLabel positions itself absolutely; here the ROW is
|
|
576
623
|
positioned, so the label and its "?" are centred on one line by the flex
|
|
577
624
|
layout (dev#468). */}
|
|
578
|
-
<FieldLabel id={labelId} className={cn("
|
|
625
|
+
<FieldLabel id={labelId} className={cn(LABEL_IN_ROW, "max-w-full", disabled && "opacity-50")}>
|
|
579
626
|
{label}
|
|
580
627
|
</FieldLabel>
|
|
581
628
|
{hasContent(hint) && <span className="pointer-events-auto flex shrink-0 items-center">{hint}</span>}
|
|
@@ -5,7 +5,7 @@ import { useAnnounce } from "../hooks/use-announce";
|
|
|
5
5
|
import { useKitFileLabels, useKitLabels } from "../i18n/kit-labels";
|
|
6
6
|
import { Button, Spinner } from "./ui";
|
|
7
7
|
import { Tooltip } from "./tooltip";
|
|
8
|
-
import { useCommitReason } from "./write-lock";
|
|
8
|
+
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* A button that opens the file picker — the shape all three apps kept writing by hand
|
|
@@ -444,7 +444,7 @@ export interface FileButtonProps
|
|
|
444
444
|
* one: no effect. A picker that only fills a form the user saves later is not a
|
|
445
445
|
* commit, and is left live — the form's Save carries the lock.
|
|
446
446
|
*/
|
|
447
|
-
commit?:
|
|
447
|
+
commit?: CommitScope;
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
/** A reason that is really there — `null`, `false` and `""` are no reason. */
|
|
@@ -612,7 +612,8 @@ export const FileButton = forwardRef<HTMLButtonElement, FileButtonProps>(functio
|
|
|
612
612
|
}
|
|
613
613
|
return (
|
|
614
614
|
// One inline row, so the name sits beside the button wherever the button sits; the
|
|
615
|
-
// name truncates rather than pushing the row wider
|
|
615
|
+
// name truncates rather than pushing the row wider (at Normal: from Large up it wraps,
|
|
616
|
+
// `truncate-until-large`, text size §10.17), and the full list is in the
|
|
616
617
|
// kit's own Tooltip — not a native `title` (0.16.0 shipped one: the one-tooltip rule
|
|
617
618
|
// since keksdose dev#523, and a title never opens on touch). Lazy, so the closed
|
|
618
619
|
// bubble adds nothing to the read-out's text.
|
|
@@ -628,7 +629,7 @@ export const FileButton = forwardRef<HTMLButtonElement, FileButtonProps>(functio
|
|
|
628
629
|
className="flex min-w-0 text-sm text-[var(--text-secondary)]"
|
|
629
630
|
>
|
|
630
631
|
<Tooltip label={picked.map((f) => f.name).join(", ")} lazy className="min-w-0">
|
|
631
|
-
<span className="block truncate">{pickedText}</span>
|
|
632
|
+
<span className="block truncate-until-large">{pickedText}</span>
|
|
632
633
|
</Tooltip>
|
|
633
634
|
</span>
|
|
634
635
|
{picker.element}
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import type { FilePickHandler, FilePickerLabels, FileRejection } from "./file-button";
|
|
16
16
|
import { useDragTarget } from "../hooks/use-file-drop";
|
|
17
17
|
import { toast } from "./toast";
|
|
18
|
-
import { useCommitReason } from "./write-lock";
|
|
18
|
+
import { useCommitReason, type CommitScope } from "./write-lock";
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Where a refused file's message goes.
|
|
@@ -160,7 +160,7 @@ export interface FileDropzoneProps extends Omit<ComponentPropsWithoutRef<"div">,
|
|
|
160
160
|
* reason (which wins over one of its own). No provider, or an unlocked one: no
|
|
161
161
|
* effect. A zone that only stages a file for a form saved later is not a commit.
|
|
162
162
|
*/
|
|
163
|
-
commit?:
|
|
163
|
+
commit?: CommitScope;
|
|
164
164
|
/**
|
|
165
165
|
* Replace the zone's body — the icon, the text and the chosen-file list — with your
|
|
166
166
|
* own, given the zone's state: keksdose's spinner-plus-"uploading", its multi-shot
|
|
@@ -243,7 +243,7 @@ export function FloatingActionButton({
|
|
|
243
243
|
pressed === false &&
|
|
244
244
|
"bg-[var(--bg-surface)] text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)]",
|
|
245
245
|
pressed === true &&
|
|
246
|
-
"border-[var(--brand)] bg-[var(--brand-bg)] text-[var(--brand)] hover:bg-[var(--brand-bg-hover)]",
|
|
246
|
+
"border-[var(--brand)] bg-[var(--brand-bg)] text-[var(--brand)] hover:bg-[var(--brand-bg-hover)] hover:text-brand-muted",
|
|
247
247
|
"outline-none focus-visible:ring-offset-2",
|
|
248
248
|
FOCUS_RING,
|
|
249
249
|
className,
|
|
@@ -650,10 +650,12 @@ export interface FloatingActionProps extends Omit<ButtonHTMLAttributes<HTMLButto
|
|
|
650
650
|
ref?: Ref<HTMLButtonElement>;
|
|
651
651
|
}
|
|
652
652
|
|
|
653
|
+
// Each fill under its own foreground (0.33): warning borrowed `--text-inverse`, the
|
|
654
|
+
// surface colour, which was 4.24:1 on the light warning fill.
|
|
653
655
|
const BADGE_TONE: Record<NonNullable<FloatingActionProps["badgeTone"]>, string> = {
|
|
654
|
-
warning: "bg-
|
|
655
|
-
danger: "bg-
|
|
656
|
-
brand: "bg-
|
|
656
|
+
warning: "bg-warning text-warning-contrast",
|
|
657
|
+
danger: "bg-danger text-danger-contrast",
|
|
658
|
+
brand: "bg-brand text-brand-contrast",
|
|
657
659
|
};
|
|
658
660
|
|
|
659
661
|
/**
|
|
@@ -695,7 +697,7 @@ export function FloatingAction({
|
|
|
695
697
|
variant === "primary"
|
|
696
698
|
? "bg-[var(--brand)] text-[var(--brand-contrast)] hover:bg-[var(--brand-hover)]"
|
|
697
699
|
: pressed && !isLink && pressedStyle === "tint"
|
|
698
|
-
? "bg-[var(--brand-bg)] text-[var(--brand)] hover:bg-[var(--brand-bg-hover)]"
|
|
700
|
+
? "bg-[var(--brand-bg)] text-[var(--brand)] hover:bg-[var(--brand-bg-hover)] hover:text-brand-muted"
|
|
699
701
|
: "text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)]",
|
|
700
702
|
className,
|
|
701
703
|
);
|