@eifi1/ui-kit 0.48.2 → 0.50.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 +34 -16
- package/dist/admin/admin-action-confirm.js +1 -1
- package/dist/admin/admin-action-confirm.js.map +1 -1
- package/dist/admin/invitations-panel.js +5 -1
- package/dist/admin/invitations-panel.js.map +1 -1
- package/dist/admin/user-roster.d.ts +24 -12
- package/dist/admin/user-roster.js +21 -6
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/auth/accept-invitation.js +2 -2
- package/dist/auth/accept-invitation.js.map +1 -1
- package/dist/auth/not-found-page.js +1 -1
- package/dist/auth/not-found-page.js.map +1 -1
- package/dist/auth/reset-password-form.js +2 -2
- package/dist/auth/reset-password-form.js.map +1 -1
- package/dist/auth/verify-email.d.ts +9 -1
- package/dist/auth/verify-email.js +5 -3
- package/dist/auth/verify-email.js.map +1 -1
- package/dist/billing/plan-card.js +2 -2
- package/dist/billing/plan-card.js.map +1 -1
- package/dist/components/amount-input.d.ts +2 -1
- package/dist/components/amount-input.js +3 -2
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/autocomplete.js +1 -0
- package/dist/components/autocomplete.js.map +1 -1
- package/dist/components/breadcrumbs.d.ts +19 -1
- package/dist/components/breadcrumbs.js +11 -4
- package/dist/components/breadcrumbs.js.map +1 -1
- package/dist/components/button-look.d.ts +37 -7
- package/dist/components/button-look.js +33 -6
- package/dist/components/button-look.js.map +1 -1
- package/dist/components/button.d.ts +4 -1
- package/dist/components/button.js +2 -2
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar-heatmap.js +2 -1
- package/dist/components/calendar-heatmap.js.map +1 -1
- package/dist/components/chart-edge.d.ts +10 -0
- package/dist/components/chart-edge.js +57 -0
- package/dist/components/chart-edge.js.map +1 -0
- package/dist/components/chart.js +2 -1
- package/dist/components/chart.js.map +1 -1
- package/dist/components/chip.d.ts +8 -1
- package/dist/components/chip.js +4 -1
- package/dist/components/chip.js.map +1 -1
- package/dist/components/combobox-core.js +1 -0
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox-suggestions.js +1 -0
- package/dist/components/combobox-suggestions.js.map +1 -1
- package/dist/components/confirm-context.d.ts +5 -0
- package/dist/components/confirm-context.js +7 -0
- package/dist/components/confirm-context.js.map +1 -0
- package/dist/components/confirm-dialog.js +3 -3
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/currency-select.d.ts +28 -2
- package/dist/components/currency-select.js +56 -10
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/danger-confirm.d.ts +2 -2
- package/dist/components/danger-confirm.js +1 -1
- package/dist/components/danger-confirm.js.map +1 -1
- package/dist/components/data-table-fit.js +21 -3
- package/dist/components/data-table-fit.js.map +1 -1
- package/dist/components/data-table-mobile.d.ts +58 -3
- package/dist/components/data-table-mobile.js +147 -16
- package/dist/components/data-table-mobile.js.map +1 -1
- package/dist/components/data-table-row-actions.d.ts +2 -1
- package/dist/components/data-table-row-actions.js +7 -0
- package/dist/components/data-table-row-actions.js.map +1 -1
- package/dist/components/data-table-row.d.ts +1 -1
- package/dist/components/data-table-row.js.map +1 -1
- package/dist/components/data-table.d.ts +34 -1
- package/dist/components/data-table.js +37 -10
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/dropdown.js +1 -0
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/error-boundary.js +2 -2
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/floating-panel.d.ts +36 -13
- package/dist/components/floating-panel.js +27 -7
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/form-actions.d.ts +1 -1
- package/dist/components/form-actions.js +1 -1
- package/dist/components/form-actions.js.map +1 -1
- package/dist/components/full-bleed-dialog.js +1 -0
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/icon-button.d.ts +11 -0
- package/dist/components/icon-button.js +7 -5
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/modal.js +1 -0
- package/dist/components/modal.js.map +1 -1
- package/dist/components/nav-pills.d.ts +8 -1
- package/dist/components/nav-pills.js +61 -38
- package/dist/components/nav-pills.js.map +1 -1
- package/dist/components/number-input.d.ts +2 -1
- package/dist/components/number-input.js +6 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.js +2 -0
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/page-header-context.d.ts +8 -0
- package/dist/components/page-header-context.js +7 -0
- package/dist/components/page-header-context.js.map +1 -0
- package/dist/components/page-header.d.ts +14 -2
- package/dist/components/page-header.js +10 -1
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/picker-sheet.js +2 -0
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/pie-chart.js +2 -1
- package/dist/components/pie-chart.js.map +1 -1
- package/dist/components/popover.js +1 -0
- package/dist/components/popover.js.map +1 -1
- package/dist/components/progress-bar.js +5 -4
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/reauth-dialog.js +1 -1
- package/dist/components/reauth-dialog.js.map +1 -1
- package/dist/components/row-actions-labels.js +3 -1
- package/dist/components/row-actions-labels.js.map +1 -1
- package/dist/components/row-actions.d.ts +43 -9
- package/dist/components/row-actions.js +78 -15
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/section-nav.d.ts +37 -0
- package/dist/components/section-nav.js +33 -0
- package/dist/components/section-nav.js.map +1 -0
- package/dist/components/series-chart.js +8 -3
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/share-card.js +1 -1
- package/dist/components/share-card.js.map +1 -1
- package/dist/components/signature-pad.js +1 -1
- package/dist/components/signature-pad.js.map +1 -1
- package/dist/components/sparkline.js +2 -1
- package/dist/components/sparkline.js.map +1 -1
- package/dist/components/stat-tile.js +14 -7
- package/dist/components/stat-tile.js.map +1 -1
- package/dist/components/status-dot.d.ts +5 -1
- package/dist/components/status-dot.js +4 -1
- package/dist/components/status-dot.js.map +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/translation-review-editor.js +2 -2
- package/dist/components/translation-review-editor.js.map +1 -1
- package/dist/components/translation-review.js +2 -2
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/treemap.d.ts +5 -2
- package/dist/components/treemap.js +26 -6
- package/dist/components/treemap.js.map +1 -1
- package/dist/components/use-table-state.js +37 -6
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/data-table.js.map +1 -1
- package/dist/demo/demo-banner.js +1 -1
- package/dist/demo/demo-banner.js.map +1 -1
- package/dist/demo/demo-ended.js +1 -1
- package/dist/demo/demo-ended.js.map +1 -1
- package/dist/demo/demo-start.js +1 -1
- package/dist/demo/demo-start.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/feedback/feedback-inbox.js +1 -1
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/hooks/use-dismiss.d.ts +9 -1
- package/dist/hooks/use-dismiss.js +25 -5
- package/dist/hooks/use-dismiss.js.map +1 -1
- package/dist/hooks/use-document-title.d.ts +14 -0
- package/dist/hooks/use-document-title.js +22 -0
- package/dist/hooks/use-document-title.js.map +1 -0
- package/dist/i18n/defaults.js +2 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.js +20 -3
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +34 -1
- package/dist/i18n/kit-labels.js +43 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/en.js +22 -3
- package/dist/i18n/locales/en.js.map +3 -3
- package/dist/i18n/locales/es.js +20 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +20 -3
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +20 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +20 -3
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +20 -3
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +3 -0
- package/dist/i18n/review.js.map +1 -1
- package/dist/i18n/shared-labels.js +1 -0
- package/dist/i18n/shared-labels.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/landing/landing-actions.js +2 -2
- package/dist/landing/landing-actions.js.map +1 -1
- package/dist/landing/public-header.js +2 -2
- package/dist/landing/public-header.js.map +1 -1
- package/dist/lib/clipboard-minus.d.ts +27 -0
- package/dist/lib/clipboard-minus.js +63 -0
- package/dist/lib/clipboard-minus.js.map +1 -0
- package/dist/lib/data-attributes.d.ts +11 -0
- package/dist/lib/data-attributes.js +10 -0
- package/dist/lib/data-attributes.js.map +1 -0
- package/dist/lib/document-title.d.ts +17 -0
- package/dist/lib/document-title.js +29 -0
- package/dist/lib/document-title.js.map +1 -0
- package/dist/lib/float-clearance.js +8 -2
- package/dist/lib/float-clearance.js.map +1 -1
- package/dist/lib/format.d.ts +36 -10
- package/dist/lib/format.js +57 -11
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/minus.d.ts +32 -0
- package/dist/lib/minus.js +14 -0
- package/dist/lib/minus.js.map +1 -0
- package/dist/lib/status-tone.d.ts +30 -0
- package/dist/lib/status-tone.js +17 -0
- package/dist/lib/status-tone.js.map +1 -0
- package/dist/settings/settings-layout.js +3 -2
- package/dist/settings/settings-layout.js.map +1 -1
- package/dist/shell/account-menu-language.d.ts +1 -1
- package/dist/shell/account-menu-language.js +9 -1
- package/dist/shell/account-menu-language.js.map +1 -1
- package/dist/shell/app-shell-labels.js +2 -1
- package/dist/shell/app-shell-labels.js.map +1 -1
- package/dist/shell/app-shell.d.ts +58 -16
- package/dist/shell/app-shell.js +151 -43
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/language-code.d.ts +3 -0
- package/dist/shell/language-code.js +8 -0
- package/dist/shell/language-code.js.map +1 -0
- package/dist/shell/topbar-controls.d.ts +14 -6
- package/dist/shell/topbar-controls.js +14 -17
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/testing/css-variables.generated.d.ts +1 -1
- package/dist/testing/css-variables.generated.js +1 -1
- package/dist/testing/css-variables.generated.js.map +1 -1
- package/dist/testing/react.d.ts +25 -0
- package/dist/testing/react.js +19 -0
- package/dist/testing/react.js.map +1 -0
- package/dist/theme/chart-palette.d.ts +82 -15
- package/dist/theme/chart-palette.js +51 -24
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/palette-presets.d.ts +9 -0
- package/dist/theme/palette-presets.js +77 -62
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/tour/tour-dock.d.ts +42 -0
- package/dist/tour/tour-dock.js +80 -0
- package/dist/tour/tour-dock.js.map +1 -0
- package/dist/tour/tour-gates.d.ts +71 -0
- package/dist/tour/tour-gates.js +114 -0
- package/dist/tour/tour-gates.js.map +1 -0
- package/dist/tour/tour-guide-button.d.ts +27 -0
- package/dist/tour/tour-guide-button.js +45 -0
- package/dist/tour/tour-guide-button.js.map +1 -0
- package/dist/tour/tour-guide-labels.d.ts +8 -0
- package/dist/tour/tour-guide-labels.js +9 -0
- package/dist/tour/tour-guide-labels.js.map +1 -0
- package/dist/tour/tour-labels.js +9 -1
- package/dist/tour/tour-labels.js.map +1 -1
- package/dist/tour/tour.d.ts +133 -9
- package/dist/tour/tour.js +594 -70
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour/use-tour-url-sync.d.ts +5 -2
- package/dist/tour/use-tour-url-sync.js +7 -1
- package/dist/tour/use-tour-url-sync.js.map +1 -1
- package/dist/tour.d.ts +2 -0
- package/dist/tour.js +3 -0
- package/dist/tour.js.map +1 -1
- package/dist/wizard/stepper-nav.d.ts +1 -1
- package/dist/wizard/stepper-nav.js +18 -5
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +11 -0
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +11 -5
- package/dist/wizard/use-wizard.js.map +1 -1
- package/package.json +9 -1
- package/src/admin/admin-action-confirm.tsx +1 -1
- package/src/admin/invitations-panel.tsx +7 -3
- package/src/admin/user-roster.tsx +41 -14
- package/src/auth/accept-invitation.tsx +3 -3
- package/src/auth/not-found-page.tsx +1 -1
- package/src/auth/reset-password-form.tsx +3 -3
- package/src/auth/verify-email.tsx +13 -3
- package/src/billing/plan-card.tsx +2 -2
- package/src/components/amount-input.tsx +5 -4
- package/src/components/autocomplete.tsx +2 -0
- package/src/components/breadcrumbs.tsx +32 -4
- package/src/components/button-look.ts +86 -15
- package/src/components/button.tsx +6 -3
- package/src/components/calendar-heatmap.tsx +2 -1
- package/src/components/chart-edge.ts +73 -0
- package/src/components/chart.tsx +3 -1
- package/src/components/chip.tsx +10 -1
- package/src/components/combobox-core.tsx +2 -0
- package/src/components/combobox-suggestions.tsx +2 -0
- package/src/components/confirm-context.ts +7 -0
- package/src/components/confirm-dialog.tsx +3 -3
- package/src/components/currency-select.tsx +95 -6
- package/src/components/danger-confirm.tsx +3 -3
- package/src/components/data-table-fit.ts +41 -6
- package/src/components/data-table-mobile.tsx +259 -17
- package/src/components/data-table-row-actions.tsx +9 -1
- package/src/components/data-table-row.tsx +3 -0
- package/src/components/data-table.tsx +91 -19
- package/src/components/dropdown.tsx +2 -0
- package/src/components/error-boundary.tsx +2 -2
- package/src/components/floating-panel.tsx +67 -16
- package/src/components/form-actions.tsx +2 -2
- package/src/components/full-bleed-dialog.tsx +3 -0
- package/src/components/icon-button.tsx +21 -8
- package/src/components/modal.tsx +3 -0
- package/src/components/nav-pills.tsx +29 -2
- package/src/components/number-input.tsx +8 -3
- package/src/components/numpad-sheet.tsx +4 -0
- package/src/components/page-header-context.ts +8 -0
- package/src/components/page-header.tsx +34 -2
- package/src/components/picker-sheet.tsx +4 -0
- package/src/components/pie-chart.tsx +2 -1
- package/src/components/popover.tsx +2 -0
- package/src/components/progress-bar.tsx +6 -3
- package/src/components/reauth-dialog.tsx +1 -1
- package/src/components/row-actions-labels.ts +2 -0
- package/src/components/row-actions.tsx +154 -33
- package/src/components/section-nav.tsx +65 -0
- package/src/components/series-chart.tsx +12 -3
- package/src/components/share-card.tsx +1 -1
- package/src/components/signature-pad.tsx +1 -1
- package/src/components/sparkline.tsx +2 -1
- package/src/components/stat-tile.tsx +19 -7
- package/src/components/status-dot.tsx +7 -1
- package/src/components/tooltip.tsx +3 -1
- package/src/components/translation-review-editor.tsx +2 -2
- package/src/components/translation-review.tsx +2 -2
- package/src/components/treemap.tsx +34 -8
- package/src/components/use-table-state.ts +63 -8
- package/src/data-table.ts +3 -3
- package/src/demo/demo-banner.tsx +1 -1
- package/src/demo/demo-ended.tsx +1 -1
- package/src/demo/demo-start.tsx +1 -1
- package/src/feedback/feedback-inbox.tsx +1 -1
- package/src/hooks/use-dismiss.ts +43 -4
- package/src/hooks/use-document-title.ts +31 -0
- package/src/i18n/defaults.ts +2 -0
- package/src/i18n/german.ts +17 -0
- package/src/i18n/kit-labels.tsx +79 -1
- package/src/i18n/locales/es.ts +17 -0
- package/src/i18n/locales/fr.ts +17 -0
- package/src/i18n/locales/hu.ts +17 -0
- package/src/i18n/locales/it.ts +17 -0
- package/src/i18n/locales/zh.ts +17 -0
- package/src/i18n/review.ts +3 -0
- package/src/i18n/shared-labels.ts +1 -0
- package/src/index.ts +13 -0
- package/src/landing/landing-actions.tsx +2 -2
- package/src/landing/public-header.tsx +2 -2
- package/src/lib/clipboard-minus.ts +94 -0
- package/src/lib/data-attributes.ts +14 -0
- package/src/lib/document-title.ts +41 -0
- package/src/lib/float-clearance.ts +19 -3
- package/src/lib/format.ts +117 -30
- package/src/lib/minus.ts +50 -0
- package/src/lib/status-tone.ts +43 -0
- package/src/settings/settings-layout.tsx +3 -2
- package/src/shell/account-menu-language.tsx +14 -2
- package/src/shell/app-shell-labels.ts +1 -0
- package/src/shell/app-shell.tsx +291 -68
- package/src/shell/language-code.ts +5 -0
- package/src/shell/topbar-controls.tsx +35 -15
- package/src/testing/css-variables.generated.ts +6 -6
- package/src/testing/react.ts +59 -0
- package/src/theme/chart-palette.ts +102 -20
- package/src/theme/palette-presets.ts +121 -84
- package/src/tour/tour-dock.ts +132 -0
- package/src/tour/tour-gates.ts +195 -0
- package/src/tour/tour-guide-button.tsx +64 -0
- package/src/tour/tour-guide-labels.ts +15 -0
- package/src/tour/tour-labels.ts +8 -0
- package/src/tour/tour.tsx +949 -86
- package/src/tour/use-tour-url-sync.ts +14 -4
- package/src/tour.ts +3 -0
- package/src/wizard/stepper-nav.tsx +27 -7
- package/src/wizard/types.ts +12 -0
- package/src/wizard/use-wizard.ts +16 -9
- package/tokens.css +28 -18
|
@@ -38,8 +38,9 @@ import { DEFAULT_USER_ROSTER_LABELS } from "./user-roster-labels";
|
|
|
38
38
|
*
|
|
39
39
|
* The column keys ARE the server's sort keys (§3.1): `name`, `email`, `role`, `created`,
|
|
40
40
|
* `last_login` — so {@link userRosterSort} turns the table's sort state into the query
|
|
41
|
-
* as it is. Phone cards come from the same columns: the identity is the card's headline
|
|
42
|
-
* the
|
|
41
|
+
* as it is. Phone cards come from the same columns: the identity is the card's headline
|
|
42
|
+
* with the account's states as chips beside it, the role, last login and the actions are
|
|
43
|
+
* its three fields, created is left off.
|
|
43
44
|
*/
|
|
44
45
|
|
|
45
46
|
/* ── Rows and states ────────────────────────────────────────────────────────── */
|
|
@@ -327,16 +328,27 @@ function StateChips({ states }: { states: readonly RosterState[] }) {
|
|
|
327
328
|
/**
|
|
328
329
|
* The roster's columns, in the contract's order (§3.3):
|
|
329
330
|
*
|
|
330
|
-
* | key | cell | sort | phone |
|
|
331
|
-
*
|
|
332
|
-
* | `name` | {@link UserIdentityCell} | last, first | the card's headline |
|
|
333
|
-
* | `email` | the address (`emailColumn` only) | address | hidden |
|
|
334
|
-
* | `role` | `RoleChip`(s), or {@link RoleSelect} where `editableRole` | role | field |
|
|
335
|
-
* | `state` | `AccountStateChip`s from `stateOf` | — |
|
|
336
|
-
* | `created` | `dateColumn` | instant | hidden |
|
|
337
|
-
* | `last_login` | `dateColumn`, relative, "Never" | instant | field |
|
|
338
|
-
* | …`extra` | the app's | the app's | the app's |
|
|
339
|
-
* | `actions` | {@link UserRowActions} | — |
|
|
331
|
+
* | key | cell | sort | phone | fit (0.50) |
|
|
332
|
+
* |---|---|---|---|---|
|
|
333
|
+
* | `name` | {@link UserIdentityCell} | last, first | the card's headline | `grow` |
|
|
334
|
+
* | `email` | the address (`emailColumn` only) | address | hidden | — |
|
|
335
|
+
* | `role` | `RoleChip`(s), or {@link RoleSelect} where `editableRole` | role | field | — |
|
|
336
|
+
* | `state` | `AccountStateChip`s from `stateOf` | — | chips beside the headline | — |
|
|
337
|
+
* | `created` | `dateColumn` | instant | hidden | `fitPriority: 1` |
|
|
338
|
+
* | `last_login` | `dateColumn`, relative, "Never" | instant | field | `fitPriority: 2` |
|
|
339
|
+
* | …`extra` | the app's | the app's | off the card unless marked `mobile: "secondary"` | the app's |
|
|
340
|
+
* | `actions` | {@link UserRowActions} | — | the card's actions row (`role: "actions"`) | — |
|
|
341
|
+
*
|
|
342
|
+
* The fit (0.50, kastlan K2): Name takes the rest of the row, and a table narrower than
|
|
343
|
+
* its columns leaves out Created, then Last login (DataTable's `grow` / `fitPriority`).
|
|
344
|
+
* The role and state chips still wrap in their cells (0.48), and the other cells keep one
|
|
345
|
+
* line at Normal. `{ ...col, grow: undefined, fitPriority: undefined }` restores the 0.49
|
|
346
|
+
* layout; an `extra` column takes a `fitPriority` of its own to leave too.
|
|
347
|
+
*
|
|
348
|
+
* The card holds three fields — `role`, `last_login` and one more: an `extra` column marked
|
|
349
|
+
* `mobile: "secondary"` takes that place. The actions are `role: "actions"` (0.49), drawn
|
|
350
|
+
* after the fields and never one of the three. Drop them from the card with
|
|
351
|
+
* `mobile: "hidden"`, or draw a `mobileCard`.
|
|
340
352
|
*
|
|
341
353
|
* Pure: hand it resolved words, or use {@link useUserRosterColumns}. The keys are stable,
|
|
342
354
|
* so an app may drop or move a column (`columns.filter((c) => c.key !== "created")`).
|
|
@@ -379,6 +391,9 @@ export function userRosterColumns<T extends UserRosterRow, R extends string = st
|
|
|
379
391
|
.filter(Boolean)
|
|
380
392
|
.join(" "),
|
|
381
393
|
mobilePrimary: true,
|
|
394
|
+
// Takes the rest of the row and ellipsises (0.50, kastlan K2): the roster fits its
|
|
395
|
+
// pane — a settings column, an admin page — instead of scrolling sideways.
|
|
396
|
+
grow: true,
|
|
382
397
|
},
|
|
383
398
|
];
|
|
384
399
|
if (emailColumn) {
|
|
@@ -437,17 +452,26 @@ export function userRosterColumns<T extends UserRosterRow, R extends string = st
|
|
|
437
452
|
getValue: (row) => rolesOf(row, roleOf)[0] ?? "",
|
|
438
453
|
options: (Object.keys(roles) as R[]).map((value) => ({ value, label: roles[value].label })),
|
|
439
454
|
},
|
|
455
|
+
mobile: "secondary",
|
|
440
456
|
},
|
|
441
457
|
{
|
|
442
458
|
key: "state",
|
|
443
459
|
header: labels.state,
|
|
444
|
-
cell:
|
|
460
|
+
// No states, no cell: the phone card then shows no chip beside the name.
|
|
461
|
+
cell: (row) => {
|
|
462
|
+
const states = stateOf(row);
|
|
463
|
+
return states.length === 0 ? null : <StateChips states={states} />;
|
|
464
|
+
},
|
|
465
|
+
// Beside the name on the phone card, so the role, last login and the actions keep
|
|
466
|
+
// the card's three fields.
|
|
467
|
+
role: "status",
|
|
445
468
|
},
|
|
446
469
|
dateColumn<T>({
|
|
447
470
|
key: "created",
|
|
448
471
|
header: labels.created,
|
|
449
472
|
value: (row) => row.created_at ?? null,
|
|
450
|
-
|
|
473
|
+
// The first to leave a narrow table (0.50, kastlan K2), then last login.
|
|
474
|
+
column: { mobileHidden: true, fitPriority: 1 },
|
|
451
475
|
}),
|
|
452
476
|
// How long ago rather than a date — the question is "recently?" — with the exact
|
|
453
477
|
// time beside it (keksdose's last-seen column, Kurvenschmiede's; 0.32 §4: never only
|
|
@@ -458,6 +482,7 @@ export function userRosterColumns<T extends UserRosterRow, R extends string = st
|
|
|
458
482
|
value: (row) => row.last_login_at ?? null,
|
|
459
483
|
display: "relative",
|
|
460
484
|
empty: labels.never,
|
|
485
|
+
column: { mobile: "secondary", fitPriority: 2 },
|
|
461
486
|
}),
|
|
462
487
|
...extra,
|
|
463
488
|
);
|
|
@@ -479,6 +504,8 @@ export function userRosterColumns<T extends UserRosterRow, R extends string = st
|
|
|
479
504
|
headClassName: "w-px text-end",
|
|
480
505
|
// The menu button keeps its own click: a row link must not wrap it.
|
|
481
506
|
noRowLink: true,
|
|
507
|
+
// Always on the phone card, after the fields and never one of the three (0.49).
|
|
508
|
+
role: "actions",
|
|
482
509
|
});
|
|
483
510
|
}
|
|
484
511
|
return columns;
|
|
@@ -147,7 +147,7 @@ export function AcceptInvitation({
|
|
|
147
147
|
? "accepting"
|
|
148
148
|
: "signIn";
|
|
149
149
|
|
|
150
|
-
const continueButton = (variant: "
|
|
150
|
+
const continueButton = (variant: "accent" | "secondary") => (
|
|
151
151
|
<Button href={continueHref} replace variant={variant}>
|
|
152
152
|
{labels.continue}
|
|
153
153
|
</Button>
|
|
@@ -162,7 +162,7 @@ export function AcceptInvitation({
|
|
|
162
162
|
tone="success"
|
|
163
163
|
icon={<CheckCircle2 />}
|
|
164
164
|
title={settled?.name ? labels.joined(settled.name) : labels.accepted}
|
|
165
|
-
action={continueButton("
|
|
165
|
+
action={continueButton("accent")}
|
|
166
166
|
className={OUTCOME_CLASS}
|
|
167
167
|
/>
|
|
168
168
|
);
|
|
@@ -174,7 +174,7 @@ export function AcceptInvitation({
|
|
|
174
174
|
hint={labels.signInHint}
|
|
175
175
|
action={
|
|
176
176
|
<>
|
|
177
|
-
<Button href={signInHref} variant="
|
|
177
|
+
<Button href={signInHref} variant="accent">
|
|
178
178
|
{labels.signIn}
|
|
179
179
|
</Button>
|
|
180
180
|
{registerHref !== undefined && (
|
|
@@ -81,7 +81,7 @@ export function NotFoundPage({
|
|
|
81
81
|
<p className="max-w-prose text-sm text-secondary">{labels.body}</p>
|
|
82
82
|
{children}
|
|
83
83
|
<div className="mt-4 flex flex-wrap justify-center gap-3">
|
|
84
|
-
<Button href={homeHref} variant="
|
|
84
|
+
<Button href={homeHref} variant="accent">
|
|
85
85
|
{labels.home}
|
|
86
86
|
</Button>
|
|
87
87
|
{appHref !== undefined && (
|
|
@@ -232,7 +232,7 @@ export function ResetPasswordForm({
|
|
|
232
232
|
title={token === "" ? labels.noToken : labels.invalid}
|
|
233
233
|
hint={labels.invalidHint}
|
|
234
234
|
action={
|
|
235
|
-
<Button href={forgotHref} variant="
|
|
235
|
+
<Button href={forgotHref} variant="accent">
|
|
236
236
|
{labels.requestNew}
|
|
237
237
|
</Button>
|
|
238
238
|
}
|
|
@@ -253,11 +253,11 @@ export function ResetPasswordForm({
|
|
|
253
253
|
{notices}
|
|
254
254
|
<p className="text-xs text-muted">{labels.sessionsEnded}</p>
|
|
255
255
|
{onSignIn ? (
|
|
256
|
-
<Button type="button" variant="
|
|
256
|
+
<Button type="button" variant="accent" className="w-full" onClick={() => onSignIn(email)}>
|
|
257
257
|
{labels.signIn}
|
|
258
258
|
</Button>
|
|
259
259
|
) : (
|
|
260
|
-
<Button href={email ? withQuery(signInHref, "email", email) : signInHref} variant="
|
|
260
|
+
<Button href={email ? withQuery(signInHref, "email", email) : signInHref} variant="accent" className="w-full">
|
|
261
261
|
{labels.signIn}
|
|
262
262
|
</Button>
|
|
263
263
|
)}
|
|
@@ -119,7 +119,7 @@ function ResendButton({
|
|
|
119
119
|
}: {
|
|
120
120
|
state: ReturnType<typeof useResend>;
|
|
121
121
|
labels: VerifyEmailLabels;
|
|
122
|
-
variant: "secondary" | "
|
|
122
|
+
variant: "secondary" | "accent";
|
|
123
123
|
}) {
|
|
124
124
|
return (
|
|
125
125
|
<Button
|
|
@@ -269,7 +269,7 @@ export function VerifyEmailStatus({
|
|
|
269
269
|
const href = continueHref ?? (signedIn ? "/" : "/login");
|
|
270
270
|
// `replace`: the token is spent, so Back must not land on this page again (keksdose F1).
|
|
271
271
|
const continueButton = (
|
|
272
|
-
<Button href={href} replace variant={outcome === "verified" ? "
|
|
272
|
+
<Button href={href} replace variant={outcome === "verified" ? "accent" : "secondary"}>
|
|
273
273
|
{labels.continue}
|
|
274
274
|
</Button>
|
|
275
275
|
);
|
|
@@ -304,7 +304,7 @@ export function VerifyEmailStatus({
|
|
|
304
304
|
hint={hint}
|
|
305
305
|
action={
|
|
306
306
|
<>
|
|
307
|
-
{canResend && <ResendButton state={resend} labels={labels} variant="
|
|
307
|
+
{canResend && <ResendButton state={resend} labels={labels} variant="accent" />}
|
|
308
308
|
{continueButton}
|
|
309
309
|
</>
|
|
310
310
|
}
|
|
@@ -366,6 +366,14 @@ export interface EmailVerificationBannerProps {
|
|
|
366
366
|
* is never hidden behind "More". Default `false`: the outcome stays in the message.
|
|
367
367
|
*/
|
|
368
368
|
clampMobile?: boolean;
|
|
369
|
+
/**
|
|
370
|
+
* 0.50 (kastlan K1): where "Send again" goes on a phone in the `box` variant —
|
|
371
|
+
* {@link AlertBanner}'s `actionPlacement`, passed through. `"flow"` keeps it on the
|
|
372
|
+
* message's line while the message keeps 10rem, else at the end of the next line,
|
|
373
|
+
* instead of a line of its own (kastlan's ≤ 56px banner on a 390px phone, KX-5).
|
|
374
|
+
* Default `"own-line"`, as before. The `strip` variant flows like that already.
|
|
375
|
+
*/
|
|
376
|
+
actionPlacement?: "own-line" | "flow";
|
|
369
377
|
}
|
|
370
378
|
|
|
371
379
|
/**
|
|
@@ -388,6 +396,7 @@ export function EmailVerificationBanner({
|
|
|
388
396
|
className,
|
|
389
397
|
labels: labelsProp,
|
|
390
398
|
clampMobile = false,
|
|
399
|
+
actionPlacement,
|
|
391
400
|
}: EmailVerificationBannerProps) {
|
|
392
401
|
const labels = useKitLabels("verifyEmail", DEFAULT_VERIFY_EMAIL_LABELS, labelsProp);
|
|
393
402
|
const resend = useResend(onResend, cooldown);
|
|
@@ -421,6 +430,7 @@ export function EmailVerificationBanner({
|
|
|
421
430
|
onDismiss={onDismiss}
|
|
422
431
|
dismissLabel={onDismiss ? labels.dismiss : undefined}
|
|
423
432
|
clampMobile={clampMobile}
|
|
433
|
+
actionPlacement={actionPlacement}
|
|
424
434
|
className={className}
|
|
425
435
|
>
|
|
426
436
|
{labels.banner}
|
|
@@ -471,7 +471,7 @@ export function PlanPicker({
|
|
|
471
471
|
<div className="flex justify-end">
|
|
472
472
|
<Button
|
|
473
473
|
type="button"
|
|
474
|
-
variant="
|
|
474
|
+
variant="default"
|
|
475
475
|
className="max-sm:w-full"
|
|
476
476
|
disabledReason={blocked}
|
|
477
477
|
pending={pending}
|
|
@@ -538,7 +538,7 @@ export function GrantWarningConfirm({ open, onOpenChange, onConfirm, labels: lab
|
|
|
538
538
|
{common.back}
|
|
539
539
|
</Button>
|
|
540
540
|
<Button
|
|
541
|
-
variant="
|
|
541
|
+
variant="default"
|
|
542
542
|
onClick={() => {
|
|
543
543
|
onConfirm();
|
|
544
544
|
onOpenChange(false);
|
|
@@ -41,10 +41,11 @@ import {
|
|
|
41
41
|
} from "./field-parts";
|
|
42
42
|
import { useEndWidth, useNumericField } from "./numeric-field";
|
|
43
43
|
import type { CommitScope } from "./write-lock";
|
|
44
|
+
import { dataAttributes, type DataAttributes } from "../lib/data-attributes";
|
|
44
45
|
|
|
45
46
|
/** {@link AmountInput}'s props — exported since 0.34 (refactor audit 2026-10, API-9): a
|
|
46
47
|
* wrapper typed itself off `ComponentProps<typeof AmountInput>` until then. */
|
|
47
|
-
export interface AmountInputProps extends FieldHelpProps {
|
|
48
|
+
export interface AmountInputProps extends FieldHelpProps, DataAttributes {
|
|
48
49
|
value: string;
|
|
49
50
|
onChange: (value: string) => void;
|
|
50
51
|
currency?: string;
|
|
@@ -427,8 +428,8 @@ export function AmountInput(props: AmountInputProps) {
|
|
|
427
428
|
const { lock, locked, off, isMobile, asDisplay, showCalc, showNumpad, setFocused, mark, draft, setDraft, derived } =
|
|
428
429
|
numeric;
|
|
429
430
|
const invalid = numeric.isInvalid;
|
|
430
|
-
// A locked chip is a read-out: picking a currency is a change too.
|
|
431
|
-
const editable = !!onCurrencyChange && !locked;
|
|
431
|
+
// A locked or disabled chip is a read-out: picking a currency is a change too.
|
|
432
|
+
const editable = !!onCurrencyChange && !locked && !off;
|
|
432
433
|
// Room for the trailing controls — the calculator and the currency chip — once
|
|
433
434
|
// measured, as NumberInput reserves its own (keksdose live #367, DUP-10): their
|
|
434
435
|
// width, the track's `end-1` inset in the boxed shape, and NumberInput's 4px gap.
|
|
@@ -533,7 +534,7 @@ export function AmountInput(props: AmountInputProps) {
|
|
|
533
534
|
const selected = getCurrency(currency);
|
|
534
535
|
|
|
535
536
|
return (
|
|
536
|
-
<div className={cn("w-full", className)}>
|
|
537
|
+
<div {...dataAttributes(props)} className={cn("w-full", className)}>
|
|
537
538
|
{/* Inner relative box holds the input + its absolutely-positioned
|
|
538
539
|
trailing controls (calculator / currency), so those stay anchored to
|
|
539
540
|
the INPUT even when the mobile math bar is rendered below (#334). It is
|
|
@@ -519,6 +519,8 @@ export function Autocomplete<V extends string | number = string>(props: Autocomp
|
|
|
519
519
|
// Every press inside the list keeps focus in the field — rows, the status
|
|
520
520
|
// line, the scrollbar — or the input's blur would close the list first.
|
|
521
521
|
role="presentation"
|
|
522
|
+
// A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
|
|
523
|
+
data-kit-layer=""
|
|
522
524
|
// Portalled out of the form's `dir`; the field's is put back. The list is
|
|
523
525
|
// exactly the field's width, so `left` places it in either direction.
|
|
524
526
|
dir={dir}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fragment, useEffect, useRef, useState } from "react";
|
|
1
|
+
import { Fragment, useContext, useEffect, useRef, useState } from "react";
|
|
2
2
|
import type { ComponentPropsWithRef, ReactElement, ReactNode } from "react";
|
|
3
3
|
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
|
4
4
|
import { cn } from "../lib/cn";
|
|
@@ -6,6 +6,7 @@ import { FOCUS_RING } from "./focus-ring";
|
|
|
6
6
|
import { useKitLabels, useKitLink, type LabelOverride } from "../i18n/kit-labels";
|
|
7
7
|
import { pickLinkRenderer, RenderedKitLink } from "./text-link";
|
|
8
8
|
import { DEFAULT_BREADCRUMBS_LABELS } from "./breadcrumbs-labels";
|
|
9
|
+
import { PageHeaderTrailContext } from "./page-header-context";
|
|
9
10
|
|
|
10
11
|
export interface BreadcrumbsLabels {
|
|
11
12
|
/** The `nav` landmark's name — what a screen reader lists it as. */
|
|
@@ -24,6 +25,12 @@ export interface BreadcrumbItem {
|
|
|
24
25
|
href?: string;
|
|
25
26
|
/** A stable React key, when `label` is not a string. */
|
|
26
27
|
key?: string;
|
|
28
|
+
/**
|
|
29
|
+
* The app's home entry (0.49 KIT-9). Default: an entry whose `href` is `"/"`. No trail
|
|
30
|
+
* is drawn when only home entries are left — inside a `PageHeader`, or wherever the
|
|
31
|
+
* current entry is hidden (`hideCurrent`): "Home" above a page's title says nothing.
|
|
32
|
+
*/
|
|
33
|
+
home?: boolean;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
/** What {@link BreadcrumbsProps.renderLink} is handed. Spread it onto your router's
|
|
@@ -57,6 +64,18 @@ export interface BreadcrumbsProps extends Omit<ComponentPropsWithRef<"nav">, "ch
|
|
|
57
64
|
keepEnd?: number;
|
|
58
65
|
/** Overrides the {@link UiKitProvider}'s `breadcrumbs` labels. */
|
|
59
66
|
labels?: LabelOverride<BreadcrumbsLabels>;
|
|
67
|
+
/**
|
|
68
|
+
* Leave out the last entry, the current page (0.49 KIT-9). The entry before it stays a
|
|
69
|
+
* link, and no entry carries `aria-current`: the page's title names where you are.
|
|
70
|
+
*
|
|
71
|
+
* Default `true` inside a `PageHeader`'s `breadcrumbs` (the title is right below, so
|
|
72
|
+
* the trail would repeat it), `false` elsewhere. With the current entry hidden — and
|
|
73
|
+
* inside a `PageHeader` either way — the trail is drawn only when at least one entry
|
|
74
|
+
* that is not home ({@link BreadcrumbItem.home}) is left, so a trail drawn by the
|
|
75
|
+
* layout above the page shows no lone "Home" link either. `false` keeps the current
|
|
76
|
+
* entry as text with `aria-current="page"`.
|
|
77
|
+
*/
|
|
78
|
+
hideCurrent?: boolean;
|
|
60
79
|
}
|
|
61
80
|
|
|
62
81
|
/**
|
|
@@ -69,16 +88,18 @@ export interface BreadcrumbsProps extends Omit<ComponentPropsWithRef<"nav">, "ch
|
|
|
69
88
|
* the server's first paint is already right.
|
|
70
89
|
*/
|
|
71
90
|
export function Breadcrumbs({
|
|
72
|
-
items,
|
|
91
|
+
items: allItems,
|
|
73
92
|
renderLink,
|
|
74
93
|
separator,
|
|
75
94
|
collapse = true,
|
|
76
95
|
keepEnd = 2,
|
|
77
96
|
labels: labelsProp,
|
|
97
|
+
hideCurrent,
|
|
78
98
|
className,
|
|
79
99
|
"aria-label": ariaLabel,
|
|
80
100
|
...rest
|
|
81
101
|
}: BreadcrumbsProps) {
|
|
102
|
+
const host = useContext(PageHeaderTrailContext);
|
|
82
103
|
const labels = useKitLabels("breadcrumbs", DEFAULT_BREADCRUMBS_LABELS, labelsProp);
|
|
83
104
|
const kitLink = useKitLink();
|
|
84
105
|
const [expanded, setExpanded] = useState(false);
|
|
@@ -92,7 +113,13 @@ export function Breadcrumbs({
|
|
|
92
113
|
const li = revealedRef.current;
|
|
93
114
|
(li?.querySelector<HTMLElement>("a[href]") ?? li)?.focus();
|
|
94
115
|
}, [expanded]);
|
|
95
|
-
|
|
116
|
+
const hides = hideCurrent ?? host?.titled ?? false;
|
|
117
|
+
const shown = hides ? allItems.slice(0, -1) : allItems;
|
|
118
|
+
if (shown.length === 0) return null;
|
|
119
|
+
// A trail of home entries alone repeats nothing worth a line: in a PageHeader, and
|
|
120
|
+
// wherever the current entry is hidden (a layout's trail above the page's title).
|
|
121
|
+
if ((host || hides) && !shown.some((item) => !(item.home ?? item.href === "/"))) return null;
|
|
122
|
+
const items = shown;
|
|
96
123
|
|
|
97
124
|
const end = Math.max(1, keepEnd);
|
|
98
125
|
// Crumbs [1, firstKept) are the collapsible middle. Collapsing a single crumb into a
|
|
@@ -116,7 +143,8 @@ export function Breadcrumbs({
|
|
|
116
143
|
<nav {...rest} aria-label={ariaLabel ?? labels.label} className={cn("min-w-0 text-sm", className)}>
|
|
117
144
|
<ol className="m-0 flex min-w-0 list-none flex-wrap items-center gap-x-1.5 gap-y-1 p-0 text-muted">
|
|
118
145
|
{items.map((item, i) => {
|
|
119
|
-
|
|
146
|
+
// With the current entry hidden, the last one shown is a parent: a link.
|
|
147
|
+
const last = !hides && i === items.length - 1;
|
|
120
148
|
const key = item.key ?? (typeof item.label === "string" ? `${i}-${item.label}` : String(i));
|
|
121
149
|
let content: ReactNode;
|
|
122
150
|
if (last) {
|
|
@@ -13,7 +13,75 @@ import type { KitLinkComponent, KitLinkProps } from "../i18n/kit-labels";
|
|
|
13
13
|
* The other exports are the two buttons' shared parts (`@internal`).
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* A button's look.
|
|
18
|
+
*
|
|
19
|
+
* - `default`: the quiet action — a warm filled chip with a hairline border. What a
|
|
20
|
+
* Button with no `variant` draws.
|
|
21
|
+
* - `accent`: the strong one, solid `--brand` with `--brand-contrast` text — the one
|
|
22
|
+
* call to action a view leads with. "Accent" is the button's role here, painted in
|
|
23
|
+
* `--brand`; it is not the palette's second anchor colour, also called `accent`
|
|
24
|
+
* (theme/palette-derive.ts), which no button paints.
|
|
25
|
+
* - `secondary`: an outline. `ghost`: text or glyph only. `danger`: solid `--danger`,
|
|
26
|
+
* for an action that destroys something. `link`: a text link that acts (a `<button>`).
|
|
27
|
+
* - `primary` and `brand` are **deprecated** aliases of `default` and `accent` (0.49,
|
|
28
|
+
* same look; a development build warns once per name). They are removed in 0.51, and
|
|
29
|
+
* `primary` will not come back with another meaning.
|
|
30
|
+
*/
|
|
31
|
+
export type ButtonVariant =
|
|
32
|
+
| "default"
|
|
33
|
+
| "accent"
|
|
34
|
+
| "secondary"
|
|
35
|
+
| "ghost"
|
|
36
|
+
| "danger"
|
|
37
|
+
| "link"
|
|
38
|
+
/** @deprecated 0.49 — use `default` (same look); removed in 0.51. */
|
|
39
|
+
| "primary"
|
|
40
|
+
/** @deprecated 0.49 — use `accent` (same look); removed in 0.51. */
|
|
41
|
+
| "brand";
|
|
42
|
+
|
|
43
|
+
/** @internal A {@link ButtonVariant} without the deprecated aliases. */
|
|
44
|
+
export type CanonicalButtonVariant = Exclude<ButtonVariant, "primary" | "brand">;
|
|
45
|
+
|
|
46
|
+
/** The deprecated names and what they stand for — see {@link ButtonVariant}. */
|
|
47
|
+
const DEPRECATED_BUTTON_VARIANTS: Readonly<Record<string, CanonicalButtonVariant>> = {
|
|
48
|
+
primary: "default",
|
|
49
|
+
brand: "accent",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** The kit's dev switch, as `lib/logger.ts` reads it: a development build, never a
|
|
53
|
+
* production one (the consumer's bundler makes `import.meta.env.DEV` false) and never
|
|
54
|
+
* under vitest. Read per call so a test can switch it on with `vi.stubEnv`. */
|
|
55
|
+
function devWarnings(): boolean {
|
|
56
|
+
return Boolean(import.meta.env?.DEV) && !import.meta.env?.VITEST;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const warnedVariants = new Set<string>();
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @internal A deprecated variant name warns once (per `component` and name) in a
|
|
63
|
+
* development build, then reads as the name it stands for. `component` is the prop's
|
|
64
|
+
* owner in the message ("Button variant", "FormActions submitVariant", …).
|
|
65
|
+
*/
|
|
66
|
+
export function warnDeprecatedVariant(component: string, name: string, replacement: string): void {
|
|
67
|
+
if (!devWarnings()) return;
|
|
68
|
+
const key = `${component}|${name}`;
|
|
69
|
+
if (warnedVariants.has(key)) return;
|
|
70
|
+
warnedVariants.add(key);
|
|
71
|
+
console.warn(
|
|
72
|
+
`[ui-kit] ${component}="${name}" is deprecated: use "${replacement}" (same look). ` +
|
|
73
|
+
`"${name}" is removed in 0.51.`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** @internal `primary` → `default`, `brand` → `accent` (warning once in development);
|
|
78
|
+
* every other variant as it is. Runs before every lookup keyed by the variant. */
|
|
79
|
+
export function resolveButtonVariant(variant: ButtonVariant, component = "Button variant"): CanonicalButtonVariant {
|
|
80
|
+
const replacement = DEPRECATED_BUTTON_VARIANTS[variant];
|
|
81
|
+
if (replacement === undefined) return variant as CanonicalButtonVariant;
|
|
82
|
+
warnDeprecatedVariant(component, variant, replacement);
|
|
83
|
+
return replacement;
|
|
84
|
+
}
|
|
17
85
|
|
|
18
86
|
// Shared base ring for every button-styled element. Kept as a named const so the
|
|
19
87
|
// <Button> component and the {@link buttonClasses} helper draw from one source and
|
|
@@ -83,13 +151,14 @@ const BUTTON_SIZES_LARGE: Record<ButtonSize, string> = {
|
|
|
83
151
|
export const HOVER_INK = "hover:bg-[color-mix(in_srgb,var(--text-primary)_7%,transparent)]";
|
|
84
152
|
|
|
85
153
|
// Warm, palette-token-driven so buttons blend with the fields + cards in every theme.
|
|
86
|
-
// Actions default to a warm bordered look (
|
|
87
|
-
// outline); `
|
|
154
|
+
// Actions default to a warm bordered look (`default` = filled warm chip, secondary =
|
|
155
|
+
// outline); `accent` is the solid brand fill for the rare strong CTA; `danger` takes the
|
|
88
156
|
// semantic `--danger` family (destructive semantics), so a consumer can re-point the
|
|
89
157
|
// destructive hue instead of inheriting a hard-coded red. Focus rings follow each
|
|
90
|
-
// variant's own accent: brand for the four neutral ones, danger for `danger`.
|
|
91
|
-
|
|
92
|
-
|
|
158
|
+
// variant's own accent: brand for the four neutral ones, danger for `danger`. Keyed by
|
|
159
|
+
// the canonical names: {@link resolveButtonVariant} maps the deprecated ones first.
|
|
160
|
+
export const buttonVariantClasses: Record<CanonicalButtonVariant, string> = {
|
|
161
|
+
default:
|
|
93
162
|
// Hovers onto `--bg-active`, not `--border` (0.33, §12.9): More contrast steps the
|
|
94
163
|
// border until it is a 3:1 LINE, and as a fill under the label that left 2.7:1.
|
|
95
164
|
"border border-subtle bg-surface-2 text-primary hover:bg-active focus-visible:ring-brand",
|
|
@@ -97,7 +166,7 @@ export const buttonVariantClasses: Record<ButtonVariant, string> = {
|
|
|
97
166
|
ghost: `bg-transparent text-primary ${HOVER_INK} focus-visible:ring-brand`,
|
|
98
167
|
danger:
|
|
99
168
|
"bg-danger text-danger-contrast hover:bg-danger-hover focus-visible:ring-danger-strong",
|
|
100
|
-
|
|
169
|
+
accent:
|
|
101
170
|
"bg-brand text-brand-contrast hover:bg-brand-hover focus-visible:ring-brand",
|
|
102
171
|
// A text link that is still a `<button>` — keksdose's six hand-rolled
|
|
103
172
|
// `<button className="text-brand underline">` sites ("Resend code", "Show all",
|
|
@@ -132,14 +201,14 @@ export const buttonVariantClasses: Record<ButtonVariant, string> = {
|
|
|
132
201
|
*
|
|
133
202
|
* `danger` also reaches the two NEUTRAL boxed variants, where the box carries it:
|
|
134
203
|
* `secondary` becomes an outlined destructive button (`--danger` text and a
|
|
135
|
-
* `--danger-border` outline, the quiet danger fill on hover) and `
|
|
204
|
+
* `--danger-border` outline, the quiet danger fill on hover) and `default` a soft one
|
|
136
205
|
* (the quiet fill at rest, solid `--danger` on hover). kastlan's meeting-invitations-
|
|
137
206
|
* tab.tsx "Remove all" is `variant="secondary" className="text-destructive"` — a
|
|
138
207
|
* destructive action that must sit in a row of secondary buttons without shouting
|
|
139
208
|
* like the solid `variant="danger"` would, and whose red text alone left the border
|
|
140
209
|
* and hover saying "neutral". `muted` stays a text look and does nothing on a box, and
|
|
141
|
-
* `danger`, `
|
|
142
|
-
* solid danger or
|
|
210
|
+
* `danger`, `accent` and `link`'s own colours are left alone: a tone that recoloured a
|
|
211
|
+
* solid danger or accent button would be a second variant under another name.
|
|
143
212
|
*/
|
|
144
213
|
export type ButtonTone = "default" | "muted" | "danger";
|
|
145
214
|
|
|
@@ -154,19 +223,19 @@ const BUTTON_TONES: Record<Exclude<ButtonTone, "default">, string> = {
|
|
|
154
223
|
};
|
|
155
224
|
|
|
156
225
|
/** Only the two transparent variants take every tone; see {@link ButtonTone}. */
|
|
157
|
-
const TONED_VARIANTS = new Set<
|
|
226
|
+
const TONED_VARIANTS = new Set<CanonicalButtonVariant>(["link", "ghost"]);
|
|
158
227
|
|
|
159
228
|
// `tone="danger"` on the neutral boxed variants — see {@link ButtonTone}. Each string
|
|
160
229
|
// restates every colour the variant sets (border, fill, text, hover, ring) so none of
|
|
161
230
|
// the neutral ones survives the merge.
|
|
162
|
-
const BUTTON_BOXED_DANGER: Partial<Record<
|
|
231
|
+
const BUTTON_BOXED_DANGER: Partial<Record<CanonicalButtonVariant, string>> = {
|
|
163
232
|
// Not boxed, but its hover is a fill: the danger wash under the danger text, as
|
|
164
233
|
// IconButton's danger tone (0.33). On B′'s translucent ink `--danger` fell to 4.2:1
|
|
165
234
|
// over a light page.
|
|
166
235
|
ghost: "hover:bg-danger-soft",
|
|
167
236
|
secondary:
|
|
168
237
|
"border-danger bg-transparent text-danger hover:bg-danger-soft focus-visible:ring-danger-strong",
|
|
169
|
-
|
|
238
|
+
default:
|
|
170
239
|
"border-danger bg-danger-soft text-danger hover:border-[var(--danger)] hover:bg-danger hover:text-danger-contrast focus-visible:ring-danger-strong disabled:hover:border-danger disabled:hover:bg-danger-soft disabled:hover:text-danger",
|
|
171
240
|
};
|
|
172
241
|
|
|
@@ -197,11 +266,12 @@ export interface ButtonClassesOptions {
|
|
|
197
266
|
* compact toolbar links.
|
|
198
267
|
*/
|
|
199
268
|
export function buttonClasses(
|
|
200
|
-
|
|
269
|
+
given: ButtonVariant = "default",
|
|
201
270
|
classNameOrOptions?: string | ButtonClassesOptions,
|
|
202
271
|
): string {
|
|
203
272
|
const { size = "md", className } =
|
|
204
273
|
typeof classNameOrOptions === "object" ? classNameOrOptions : { className: classNameOrOptions };
|
|
274
|
+
const variant = resolveButtonVariant(given, "buttonClasses variant");
|
|
205
275
|
return cn(
|
|
206
276
|
BUTTON_BASE,
|
|
207
277
|
BUTTON_SIZES[size],
|
|
@@ -221,7 +291,8 @@ export const ICON_BUTTON_PRESSED =
|
|
|
221
291
|
"bg-brand-soft text-brand hover:bg-brand-soft-hover hover:text-brand-muted disabled:hover:bg-brand-soft disabled:hover:text-brand";
|
|
222
292
|
|
|
223
293
|
/** @internal The button look shared by the `<button>` and the `<a>` form. */
|
|
224
|
-
export function buttonLook(
|
|
294
|
+
export function buttonLook(given: ButtonVariant, size: ButtonSize, stretch: boolean | undefined, tone: ButtonTone) {
|
|
295
|
+
const variant = resolveButtonVariant(given);
|
|
225
296
|
return cn(
|
|
226
297
|
BUTTON_BASE,
|
|
227
298
|
BUTTON_SIZES[size],
|
|
@@ -24,6 +24,9 @@ import type { ButtonSize, ButtonTone, ButtonVariant } from "./button-look";
|
|
|
24
24
|
* link buttons submitting their form).
|
|
25
25
|
*/
|
|
26
26
|
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
27
|
+
/** `default` (the quiet action, what a Button without `variant` draws), `accent` (the
|
|
28
|
+
* strong call to action, solid brand), `secondary`, `ghost`, `danger` or `link`.
|
|
29
|
+
* `primary` / `brand` are deprecated aliases of `default` / `accent`. See {@link ButtonVariant}. */
|
|
27
30
|
variant?: ButtonVariant;
|
|
28
31
|
/** `md` (default) is the page's action button. `sm` is the compact one — 12px text
|
|
29
32
|
* and `px-2 py-1` — for the secondary actions in a toolbar, a card header or a
|
|
@@ -39,7 +42,7 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
39
42
|
ref?: Ref<HTMLButtonElement>;
|
|
40
43
|
/** Text colour for `link` and `ghost`: `muted` (quiet, body colour on hover) or
|
|
41
44
|
* `danger` (quiet, `--danger` on hover). `danger` also makes `secondary` an
|
|
42
|
-
* outlined destructive button and `
|
|
45
|
+
* outlined destructive button and `default` a soft one; ignored by the other
|
|
43
46
|
* variants. See {@link ButtonTone}. */
|
|
44
47
|
tone?: ButtonTone;
|
|
45
48
|
/**
|
|
@@ -181,7 +184,7 @@ function ButtonLink({
|
|
|
181
184
|
replace,
|
|
182
185
|
reloadDocument,
|
|
183
186
|
disabled,
|
|
184
|
-
variant = "
|
|
187
|
+
variant = "default",
|
|
185
188
|
size = "md",
|
|
186
189
|
stretch,
|
|
187
190
|
tone = "default",
|
|
@@ -257,7 +260,7 @@ function ButtonLink({
|
|
|
257
260
|
}
|
|
258
261
|
|
|
259
262
|
function ButtonElement({
|
|
260
|
-
variant = "
|
|
263
|
+
variant = "default",
|
|
261
264
|
size = "md",
|
|
262
265
|
stretch,
|
|
263
266
|
tone = "default",
|
|
@@ -9,6 +9,7 @@ import { parseHex } from "../theme/color";
|
|
|
9
9
|
import { Tooltip } from "./tooltip";
|
|
10
10
|
import type { WeekDay } from "./mini-calendar";
|
|
11
11
|
import { DEFAULT_CALENDAR_HEATMAP_LABELS } from "./calendar-heatmap-labels";
|
|
12
|
+
import { formatWithMinus } from "../lib/minus";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Every string the heatmap speaks — the `calendarHeatmap` namespace of
|
|
@@ -248,7 +249,7 @@ export function CalendarHeatmap({
|
|
|
248
249
|
const providerWeekStart = useKitWeekStart();
|
|
249
250
|
const weekStart: WeekDay = weekStartsOn ?? providerWeekStart ?? localeWeekStart(locale);
|
|
250
251
|
const numberFormat = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
251
|
-
const formatValue = formatValueProp ?? ((v: number) => numberFormat
|
|
252
|
+
const formatValue = formatValueProp ?? ((v: number) => formatWithMinus(numberFormat, v));
|
|
252
253
|
|
|
253
254
|
const values = useMemo(() => {
|
|
254
255
|
const m = new Map<string, number>();
|