@eifi1/ui-kit 0.9.0 → 0.11.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 +37 -23
- package/dist/components/alert-banner.d.ts +57 -5
- package/dist/components/alert-banner.js +63 -17
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +6 -0
- package/dist/components/breadcrumbs.d.ts +60 -0
- package/dist/components/breadcrumbs.js +88 -0
- package/dist/components/breadcrumbs.js.map +1 -0
- package/dist/components/bulk-action-bar.d.ts +112 -0
- package/dist/components/bulk-action-bar.js +196 -0
- package/dist/components/bulk-action-bar.js.map +1 -0
- package/dist/components/button-group.d.ts +58 -3
- package/dist/components/button-group.js +56 -5
- package/dist/components/button-group.js.map +1 -1
- package/dist/components/calculator.d.ts +6 -0
- package/dist/components/calendar-heatmap.d.ts +124 -0
- package/dist/components/calendar-heatmap.js +295 -0
- package/dist/components/calendar-heatmap.js.map +1 -0
- package/dist/components/chip.d.ts +31 -5
- package/dist/components/chip.js +85 -5
- package/dist/components/chip.js.map +1 -1
- package/dist/components/choice-card.d.ts +34 -2
- package/dist/components/choice-card.js +49 -0
- package/dist/components/choice-card.js.map +1 -1
- package/dist/components/copy-button.d.ts +14 -4
- package/dist/components/copy-button.js +17 -4
- package/dist/components/copy-button.js.map +1 -1
- package/dist/components/data-table.js +2 -0
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +22 -2
- package/dist/components/date-picker.js +11 -3
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/description-list.d.ts +43 -6
- package/dist/components/description-list.js +91 -11
- package/dist/components/description-list.js.map +1 -1
- package/dist/components/dialog-frame.d.ts +7 -0
- package/dist/components/dialog-frame.js.map +1 -1
- package/dist/components/disclosure.d.ts +57 -2
- package/dist/components/disclosure.js +24 -12
- package/dist/components/disclosure.js.map +1 -1
- package/dist/components/field.d.ts +43 -0
- package/dist/components/field.js +52 -0
- package/dist/components/field.js.map +1 -0
- package/dist/components/file-button.d.ts +1 -0
- package/dist/components/file-dropzone.d.ts +4 -4
- package/dist/components/file-dropzone.js +2 -1
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/floating-panel.d.ts +147 -4
- package/dist/components/floating-panel.js +188 -28
- package/dist/components/floating-panel.js.map +1 -1
- package/dist/components/full-bleed-dialog.d.ts +23 -4
- package/dist/components/full-bleed-dialog.js +11 -2
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/list.d.ts +187 -0
- package/dist/components/list.js +221 -0
- package/dist/components/list.js.map +1 -0
- package/dist/components/menu-item.d.ts +109 -0
- package/dist/components/menu-item.js +88 -0
- package/dist/components/menu-item.js.map +1 -0
- package/dist/components/mini-calendar.d.ts +70 -5
- package/dist/components/mini-calendar.js +166 -62
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +23 -1
- package/dist/components/modal.js +36 -10
- package/dist/components/modal.js.map +1 -1
- package/dist/components/nav-pills.d.ts +66 -0
- package/dist/components/nav-pills.js +65 -0
- package/dist/components/nav-pills.js.map +1 -0
- package/dist/components/number-field.d.ts +6 -0
- package/dist/components/number-input.d.ts +6 -0
- package/dist/components/numpad-sheet.d.ts +6 -0
- package/dist/components/page-contents.js +1 -2
- package/dist/components/page-contents.js.map +1 -1
- package/dist/components/page-header.d.ts +37 -0
- package/dist/components/page-header.js +36 -0
- package/dist/components/page-header.js.map +1 -0
- package/dist/components/progress-bar.d.ts +31 -2
- package/dist/components/progress-bar.js +73 -7
- package/dist/components/progress-bar.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +3 -0
- package/dist/components/series-chart-labels.js +2 -1
- package/dist/components/series-chart-labels.js.map +1 -1
- package/dist/components/series-chart.d.ts +32 -0
- package/dist/components/series-chart.js +141 -4
- package/dist/components/series-chart.js.map +1 -1
- package/dist/components/settings-fields.d.ts +1 -0
- package/dist/components/status-dot.d.ts +53 -0
- package/dist/components/status-dot.js +57 -0
- package/dist/components/status-dot.js.map +1 -0
- package/dist/components/table.d.ts +78 -13
- package/dist/components/table.js +84 -11
- package/dist/components/table.js.map +1 -1
- package/dist/components/text.d.ts +70 -0
- package/dist/components/text.js +24 -0
- package/dist/components/text.js.map +1 -0
- package/dist/components/time-input.d.ts +1 -0
- package/dist/components/toast.d.ts +167 -0
- package/dist/components/toast.js +226 -0
- package/dist/components/toast.js.map +1 -0
- package/dist/components/toggle-group.d.ts +44 -1
- package/dist/components/toggle-group.js +73 -4
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/tooltip.d.ts +46 -15
- package/dist/components/tooltip.js +87 -47
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/ui.d.ts +158 -7
- package/dist/components/ui.js +191 -10
- package/dist/components/ui.js.map +1 -1
- package/dist/components/user-avatar.d.ts +25 -3
- package/dist/components/user-avatar.js +32 -3
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/hooks/use-close-transition.d.ts +7 -1
- package/dist/hooks/use-close-transition.js +5 -2
- package/dist/hooks/use-close-transition.js.map +1 -1
- package/dist/hooks/use-copy-to-clipboard.js +1 -1
- package/dist/hooks/use-copy-to-clipboard.js.map +1 -1
- package/dist/hooks/use-file-drop.d.ts +1 -0
- package/dist/i18n/defaults.d.ts +6 -0
- package/dist/i18n/defaults.js +11 -1
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +11 -0
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/locales/de-CH-informal.d.ts +6 -0
- package/dist/i18n/locales/de-CH.d.ts +6 -0
- package/dist/i18n/locales/de-informal.d.ts +6 -0
- package/dist/i18n/locales/de.d.ts +6 -0
- package/dist/i18n/locales/de.js +30 -2
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +6 -0
- package/dist/i18n/locales/es.js +30 -2
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +6 -0
- package/dist/i18n/locales/fr.js +30 -2
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +6 -0
- package/dist/i18n/locales/hu.js +30 -2
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +6 -0
- package/dist/i18n/locales/it.js +30 -2
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +6 -0
- package/dist/i18n/locales/zh.js +30 -2
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.d.ts +27 -15
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/clipping.d.ts +25 -0
- package/dist/lib/clipping.js +17 -0
- package/dist/lib/clipping.js.map +1 -0
- package/dist/lib/dates.d.ts +17 -1
- package/dist/lib/dates.js +19 -0
- package/dist/lib/dates.js.map +1 -1
- package/dist/rhf/form.d.ts +1 -0
- package/dist/rhf/use-rhf-wizard-step.d.ts +22 -0
- package/dist/rhf/use-rhf-wizard-step.js +38 -0
- package/dist/rhf/use-rhf-wizard-step.js.map +1 -0
- package/dist/rhf.d.ts +2 -0
- package/dist/rhf.js +1 -0
- package/dist/rhf.js.map +1 -1
- package/dist/search/command-palette.d.ts +19 -2
- package/dist/search/command-palette.js +16 -5
- package/dist/search/command-palette.js.map +1 -1
- package/dist/search/global-search.d.ts +26 -1
- package/dist/search/global-search.js +29 -13
- package/dist/search/global-search.js.map +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/shell/topbar-action-menu.d.ts +73 -3
- package/dist/shell/topbar-action-menu.js +100 -27
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell.d.ts +2 -1
- package/dist/wizard/stepper-nav.d.ts +1 -0
- package/dist/wizard/stepper-nav.js +1 -1
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +2 -2
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +1 -1
- package/dist/wizard/use-wizard.js.map +1 -1
- package/dist/wizard/wizard-context.d.ts +7 -1
- package/dist/wizard/wizard-context.js +4 -0
- package/dist/wizard/wizard-context.js.map +1 -1
- package/dist/wizard/wizard-summary.js +3 -3
- package/dist/wizard/wizard-summary.js.map +1 -1
- package/dist/wizard.d.ts +2 -1
- package/package.json +16 -12
- package/src/components/alert-banner.tsx +149 -21
- package/src/components/breadcrumbs.tsx +168 -0
- package/src/components/bulk-action-bar.tsx +362 -0
- package/src/components/button-group.tsx +124 -4
- package/src/components/calendar-heatmap.tsx +504 -0
- package/src/components/chip.tsx +128 -7
- package/src/components/choice-card.tsx +97 -1
- package/src/components/copy-button.tsx +29 -7
- package/src/components/data-table.tsx +7 -0
- package/src/components/date-picker.tsx +49 -1
- package/src/components/description-list.tsx +168 -15
- package/src/components/dialog-frame.tsx +7 -0
- package/src/components/disclosure.tsx +93 -21
- package/src/components/field.tsx +137 -0
- package/src/components/file-dropzone.tsx +9 -10
- package/src/components/floating-panel.tsx +366 -15
- package/src/components/full-bleed-dialog.tsx +42 -5
- package/src/components/list.tsx +443 -0
- package/src/components/menu-item.tsx +235 -0
- package/src/components/mini-calendar.tsx +245 -54
- package/src/components/modal.tsx +80 -17
- package/src/components/nav-pills.tsx +145 -0
- package/src/components/page-contents.tsx +4 -4
- package/src/components/page-header.tsx +68 -0
- package/src/components/progress-bar.tsx +129 -11
- package/src/components/series-chart-labels.ts +4 -0
- package/src/components/series-chart.tsx +274 -3
- package/src/components/status-dot.tsx +107 -0
- package/src/components/table.tsx +176 -17
- package/src/components/text.tsx +97 -0
- package/src/components/toast.tsx +441 -0
- package/src/components/toggle-group.tsx +128 -4
- package/src/components/tooltip.tsx +200 -100
- package/src/components/ui.tsx +381 -14
- package/src/components/user-avatar.tsx +57 -3
- package/src/hooks/use-close-transition.ts +14 -5
- package/src/hooks/use-copy-to-clipboard.ts +1 -1
- package/src/i18n/defaults.ts +10 -0
- package/src/i18n/kit-labels.tsx +10 -0
- package/src/i18n/locales/de.ts +29 -0
- package/src/i18n/locales/es.ts +29 -0
- package/src/i18n/locales/fr.ts +29 -0
- package/src/i18n/locales/hu.ts +29 -0
- package/src/i18n/locales/it.ts +29 -0
- package/src/i18n/locales/zh.ts +29 -0
- package/src/index.ts +31 -0
- package/src/lib/clipping.ts +34 -0
- package/src/lib/dates.ts +37 -0
- package/src/rhf/use-rhf-wizard-step.ts +113 -0
- package/src/rhf.ts +2 -0
- package/src/search/command-palette.tsx +42 -5
- package/src/search/global-search.tsx +59 -18
- package/src/shell/topbar-action-menu.tsx +239 -49
- package/src/wizard/stepper-nav.tsx +2 -1
- package/src/wizard/types.ts +2 -2
- package/src/wizard/use-wizard.ts +3 -3
- package/src/wizard/wizard-context.tsx +9 -0
- package/src/wizard/wizard-summary.tsx +14 -10
- package/tokens.css +97 -0
package/dist/components/ui.d.ts
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ButtonHTMLAttributes, Ref,
|
|
2
|
+
import { ButtonHTMLAttributes, Ref, ReactNode, SelectHTMLAttributes, InputHTMLAttributes, ComponentPropsWithoutRef, TextareaHTMLAttributes } from 'react';
|
|
3
3
|
import { TooltipSide } from './tooltip.js';
|
|
4
4
|
import '../hooks/use-anchored-rect.js';
|
|
5
|
+
import '../lib/clipping.js';
|
|
5
6
|
|
|
6
7
|
type ButtonVariant = "primary" | "secondary" | "ghost" | "danger" | "brand" | "link";
|
|
7
8
|
type ButtonSize = "sm" | "md";
|
|
9
|
+
/**
|
|
10
|
+
* The text colour of a `link` or `ghost` button, over the variant's own.
|
|
11
|
+
*
|
|
12
|
+
* `muted` is the QUIET link keksdose hand-rolls in six places (tours-page:321's "Mark
|
|
13
|
+
* undone", transaction-fields:176/215's "Add line" / "Fill total",
|
|
14
|
+
* invoice-lines-table:376, invoice-review:464): secondary text that darkens to the
|
|
15
|
+
* body colour under the pointer, for an action that must be findable but not compete
|
|
16
|
+
* with the brand-coloured one beside it. `danger` is the same quiet look turning
|
|
17
|
+
* `--danger` on hover — transaction-editor:401's "Remove split", which is a text
|
|
18
|
+
* action that destroys something.
|
|
19
|
+
*
|
|
20
|
+
* A `tone` rather than a `link-muted` variant, because it is the axis IconButton
|
|
21
|
+
* already has (`tone="muted"`, `tone="danger"`, same quiet-until-hover meaning), and
|
|
22
|
+
* because the same two looks are wanted on `ghost`. The filled and bordered variants
|
|
23
|
+
* carry their meaning in the box, not the text, so a tone on them does nothing.
|
|
24
|
+
*/
|
|
25
|
+
type ButtonTone = "default" | "muted" | "danger";
|
|
8
26
|
/** The second argument of {@link buttonClasses} in its options form. */
|
|
9
27
|
interface ButtonClassesOptions {
|
|
10
28
|
/** See {@link ButtonProps.size}. */
|
|
@@ -44,10 +62,43 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
44
62
|
* ordinary prop, so it rides `...rest` onto the element with no `forwardRef` —
|
|
45
63
|
* declared here only because `ButtonHTMLAttributes` does not carry it. */
|
|
46
64
|
ref?: Ref<HTMLButtonElement>;
|
|
65
|
+
/** Text colour for `link` and `ghost`: `muted` (quiet, body colour on hover) or
|
|
66
|
+
* `danger` (quiet, `--danger` on hover). Ignored by the other variants. See
|
|
67
|
+
* {@link ButtonTone}. */
|
|
68
|
+
tone?: ButtonTone;
|
|
69
|
+
/**
|
|
70
|
+
* Make it a toggle button, as {@link IconButton}'s `pressed` does. `true` sets
|
|
71
|
+
* `aria-pressed="true"` and draws the "on" look — on a `link`, the brand colour and a
|
|
72
|
+
* medium weight; on the boxed variants, the quiet brand fill IconButton uses — and
|
|
73
|
+
* `false` sets `aria-pressed="false"` with the ordinary look. Left out, no
|
|
74
|
+
* `aria-pressed` (or the caller's own). For lenkbank's "All speeds" text toggle
|
|
75
|
+
* (gear/hysteresis-charts.tsx:482), which is a hand-rolled `<button aria-pressed>`
|
|
76
|
+
* swapping two class strings; with `variant="link" tone="muted"` it is this.
|
|
77
|
+
*/
|
|
78
|
+
pressed?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Why the action is not available — the button's half of DangerConfirm's
|
|
81
|
+
* `lockedReason` (kastlan handover-detail-page.tsx:197 locks a signed handover).
|
|
82
|
+
*
|
|
83
|
+
* A disabled button that cannot say why is a dead end: the native `disabled` takes it
|
|
84
|
+
* out of the tab order, so a keyboard user never lands on it, and a pointer gets a
|
|
85
|
+
* `not-allowed` cursor and nothing else. With a reason the button is `aria-disabled`
|
|
86
|
+
* instead — still focusable, still hoverable — clicks (and the Enter/Space and form
|
|
87
|
+
* submission they stand for) do nothing, and the reason is shown in the kit
|
|
88
|
+
* {@link Tooltip} and attached with `aria-describedby`, so a screen reader hears it
|
|
89
|
+
* on focus. It wins over `disabled`: passing both keeps the button reachable, which is
|
|
90
|
+
* the point of giving a reason.
|
|
91
|
+
*
|
|
92
|
+
* The description is a `hidden` copy of the reason rather than the bubble itself: the
|
|
93
|
+
* bubble exists only while hovered or focused once it is portalled, and a description
|
|
94
|
+
* that comes and goes is read inconsistently. The bubble is kept visual, the way
|
|
95
|
+
* DangerConfirm keeps its own.
|
|
96
|
+
*/
|
|
97
|
+
disabledReason?: ReactNode;
|
|
47
98
|
}
|
|
48
|
-
declare function Button({ variant, size, stretch, className, ...rest }: ButtonProps): react.JSX.Element;
|
|
49
|
-
type IconButtonSize = "lg" | "md" | "sm" | "xs" | "2xs";
|
|
50
|
-
type IconButtonTone = "default" | "muted" | "danger" | "warning" | "info";
|
|
99
|
+
declare function Button({ variant, size, stretch, tone, pressed, disabledReason, className, onClick, ...rest }: ButtonProps): react.JSX.Element;
|
|
100
|
+
type IconButtonSize = "xl" | "lg" | "md" | "sm" | "xs" | "2xs";
|
|
101
|
+
type IconButtonTone = "default" | "muted" | "danger" | "warning" | "info" | "success" | "custom";
|
|
51
102
|
interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
52
103
|
/** Any {@link ButtonVariant}, or `overlay` — a round translucent disc for use over
|
|
53
104
|
* an image (see `ICON_BUTTON_OVERLAY`). */
|
|
@@ -57,11 +108,31 @@ interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
57
108
|
* xs = 28px with a 16px icon (an action in a list row), 2xs = 24px with a 14px
|
|
58
109
|
* icon (an action on a chip or a tab). */
|
|
59
110
|
size?: IconButtonSize;
|
|
111
|
+
/**
|
|
112
|
+
* Fill the height of the flex row it stands in, keeping its width — the delete at
|
|
113
|
+
* the end of keksdose's split line (transaction-fields:162), level with the labelled
|
|
114
|
+
* fields beside it rather than floating at half their height. {@link Button}'s
|
|
115
|
+
* `stretch`; no effect outside a flex row. With a `label`, the tooltip's wrapper is
|
|
116
|
+
* what stretches.
|
|
117
|
+
*/
|
|
118
|
+
stretch?: boolean;
|
|
119
|
+
/** `round`: a circle instead of the rounded square — a compact status chip in a top
|
|
120
|
+
* bar (keksdose's sync indicator). `overlay` is always round. */
|
|
121
|
+
shape?: "square" | "round";
|
|
122
|
+
/**
|
|
123
|
+
* The colour of `tone="custom"`, as any CSS colour — a token (`var(--warning)`),
|
|
124
|
+
* ideally. Sets `--icon-button-tone` on the button, so passing a different one per
|
|
125
|
+
* state re-colours the glyph, its hover fill and its focus ring together. Passing it
|
|
126
|
+
* implies `tone="custom"`. Or leave it out and set the variable yourself from a class
|
|
127
|
+
* (`className="[--icon-button-tone:var(--success)]"`).
|
|
128
|
+
*/
|
|
129
|
+
toneColor?: string;
|
|
60
130
|
/** Glyph colour over the variant. `muted`: placeholder grey, full text colour on
|
|
61
131
|
* hover. `danger`: the same grey at rest, `--danger` on hover and focus — for a
|
|
62
132
|
* remove/delete that repeats down a list. `warning`: amber at rest — a flag that
|
|
63
133
|
* wants attention. `info`: sky at rest — a notice-worthy but harmless action
|
|
64
|
-
* (keksdose's reconcile).
|
|
134
|
+
* (keksdose's reconcile). `success`: green at rest — a good state worth seeing.
|
|
135
|
+
* `custom`: the colour of `toneColor` (see there). Default: the variant's own colours. See `quiet` for
|
|
65
136
|
* turning a coloured tone's resting look the other way. */
|
|
66
137
|
tone?: IconButtonTone;
|
|
67
138
|
/**
|
|
@@ -116,6 +187,53 @@ interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
116
187
|
* The pair look exactly like the nested version, click exactly like it, and
|
|
117
188
|
* are two tab stops a screen reader can tell apart. */
|
|
118
189
|
stopPropagation?: boolean;
|
|
190
|
+
/**
|
|
191
|
+
* The button's name, said once: it becomes the `aria-label` AND the text of a kit
|
|
192
|
+
* {@link Tooltip} round the button.
|
|
193
|
+
*
|
|
194
|
+
* Every app writes the pair by hand. lenkbank's nine icon actions all carry
|
|
195
|
+
* `aria-label={t(x)} title={t(x)}` (shared/lib/table-columns.tsx:103,
|
|
196
|
+
* projects-page.tsx:83, setpoint/segment-list.tsx:198/207, profile-bar.tsx:128/142,
|
|
197
|
+
* shortcut-dialog.tsx:815/857/1007) — and `title` is the browser's own tooltip,
|
|
198
|
+
* which shows late, never on focus and never on touch, and looks like no other
|
|
199
|
+
* label in the kit. kastlan's RowAction (shared/components/data-table/row-action.tsx)
|
|
200
|
+
* and keksdose's icon wrappers each exist to put a Tooltip round an IconButton.
|
|
201
|
+
*
|
|
202
|
+
* A caller's own `aria-label` still wins, for the rare name that should be longer
|
|
203
|
+
* than the bubble. The bubble is visual only: it would otherwise describe the button
|
|
204
|
+
* with its own name, and a screen reader would read the same word twice. Tooltip's
|
|
205
|
+
* default placement applies — in place, or portalled inside a scroll container —
|
|
206
|
+
* unless `tooltipPortal` says otherwise.
|
|
207
|
+
*/
|
|
208
|
+
label?: string;
|
|
209
|
+
/** Show `label` as a tooltip. Default `true`; `false` keeps `label` as the
|
|
210
|
+
* accessible name only — for a button whose glyph is universally read (a close ✕ in
|
|
211
|
+
* a dialog header) or that already sits under a tooltip of its own. */
|
|
212
|
+
tooltip?: boolean;
|
|
213
|
+
/** Where the `label` tooltip opens. See {@link Tooltip}'s `side`. */
|
|
214
|
+
tooltipSide?: TooltipSide;
|
|
215
|
+
/** Passed to the `label` tooltip's `portal`. Left out, Tooltip decides (see there). */
|
|
216
|
+
tooltipPortal?: boolean;
|
|
217
|
+
/**
|
|
218
|
+
* Why the action is not available — {@link Button}'s `disabledReason`, on the icon
|
|
219
|
+
* button: keksdose's accounts page cannot hide an account with a balance or delete
|
|
220
|
+
* one with bookings (accounts-page:879/913 — "hide requires zero", "delete blocked"),
|
|
221
|
+
* nor its budgets page delete the only budget (budgets-page:330), and each wrapped a
|
|
222
|
+
* natively disabled IconButton in a Tooltip that swaps its label for the reason — a
|
|
223
|
+
* bubble a keyboard never opens, since `disabled` leaves the tab order, and a reason
|
|
224
|
+
* a screen reader never hears.
|
|
225
|
+
*
|
|
226
|
+
* The same contract as Button's: `aria-disabled` instead of `disabled`, so it stays
|
|
227
|
+
* focusable and hoverable; clicks (and the Enter/Space they stand for) are swallowed
|
|
228
|
+
* — `stopPropagation` still applies, so a locked action in a clickable row does not
|
|
229
|
+
* open the row either; the reason shows in the kit {@link Tooltip} and is attached
|
|
230
|
+
* through `aria-describedby` as a `hidden` copy. The NAME stays `aria-label` /
|
|
231
|
+
* `label` — "Delete", described by "The active budget cannot be deleted" — and the
|
|
232
|
+
* bubble shows the reason in place of the label, since the glyph already says what
|
|
233
|
+
* the button is and the reason is the news. Shown even with `tooltip={false}`: a
|
|
234
|
+
* reason nobody can see is not one. Wins over `disabled`, as on Button.
|
|
235
|
+
*/
|
|
236
|
+
disabledReason?: ReactNode;
|
|
119
237
|
}
|
|
120
238
|
declare const IconButton: react.ForwardRefExoticComponent<IconButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
|
121
239
|
declare const FIELD_BASE: string;
|
|
@@ -441,8 +559,41 @@ interface EmptyStateProps extends Omit<ComponentPropsWithoutRef<"div">, "childre
|
|
|
441
559
|
* wrap and centre as one row), kastlan's "Create a rule". Buttons or links the
|
|
442
560
|
* caller renders; the box only places them. */
|
|
443
561
|
action?: ReactNode;
|
|
562
|
+
/**
|
|
563
|
+
* `box` (default): the dashed, padded card that stands in for a whole section.
|
|
564
|
+
* `inline`: one quiet line with no box — for the empty state INSIDE something that
|
|
565
|
+
* already has its own frame, where a dashed card would be a box in a box. keksdose
|
|
566
|
+
* writes that line by hand five times as a muted `<p className="py-6 text-center">`:
|
|
567
|
+
* the notification inbox (notification-inbox:140), a support thread
|
|
568
|
+
* (support-thread:144), the assistant (assistant-page:321), the admin support panel
|
|
569
|
+
* (support-panel:265) and the funding dialog (funding-dialog:89). The icon shrinks
|
|
570
|
+
* to the text's size and sits before the title, the hint follows on the same line,
|
|
571
|
+
* and the row wraps only when it must. Centred like the box; `className="justify-start"`
|
|
572
|
+
* for a list that reads from the start edge (funding-dialog).
|
|
573
|
+
*/
|
|
574
|
+
variant?: "box" | "inline";
|
|
575
|
+
/**
|
|
576
|
+
* Colour the icon and the title: `danger` for an error standing where the content
|
|
577
|
+
* should be — kastlan's ErrorState (shared/components/feedback/error-state.tsx) and
|
|
578
|
+
* its error boundary's fallback (error-boundary.tsx:16), both a red triangle over a
|
|
579
|
+
* message — and `success` for an empty state that is GOOD news, kastlan's "No defects
|
|
580
|
+
* recorded for this room" (handover defects-step.tsx:142, a green check). Left out,
|
|
581
|
+
* both stay muted. The hint stays muted either way: it is the explanation, not the
|
|
582
|
+
* verdict.
|
|
583
|
+
*/
|
|
584
|
+
tone?: "danger" | "success";
|
|
585
|
+
/**
|
|
586
|
+
* `inline` only (0.11.0). `sm`: 12px, start-aligned and tight (`py-1`) — the empty
|
|
587
|
+
* line INSIDE a detail panel, under a panel heading and among `text-xs` rows, where
|
|
588
|
+
* the default's 14px centred line with `py-4` read as a section of its own.
|
|
589
|
+
* keksdose's holdings panel (holdings-panel:563), asset-loan panel
|
|
590
|
+
* (asset-loan-panel:108) and loan-payment panel (loan-payment-panel:191) each wrote
|
|
591
|
+
* `className="justify-start py-1"` over it and still got body-size text. Default `md`,
|
|
592
|
+
* the look it had. Ignored by the box.
|
|
593
|
+
*/
|
|
594
|
+
size?: "sm" | "md";
|
|
444
595
|
}
|
|
445
|
-
declare function EmptyState({ title, hint, icon, action, headingAs, className, ...rest }: EmptyStateProps): react.JSX.Element;
|
|
596
|
+
declare function EmptyState({ title, hint, icon, action, headingAs, variant, tone, size, className, ...rest }: EmptyStateProps): react.JSX.Element;
|
|
446
597
|
/** One tab of a {@link Tabs} strip. */
|
|
447
598
|
interface TabItem<T extends string> {
|
|
448
599
|
id: T;
|
|
@@ -627,4 +778,4 @@ interface TabsProps<T extends string> extends Omit<ComponentPropsWithoutRef<"div
|
|
|
627
778
|
}
|
|
628
779
|
declare function Tabs<T extends string>({ tabs, active, onChange, className, wrap, label, panelId, onRemove, removeOn, onAdd, addLabel, labels, busy, orientation, "aria-label": ariaLabel, ...rest }: TabsProps<T>): react.JSX.Element;
|
|
629
780
|
|
|
630
|
-
export { Button, type ButtonClassesOptions, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardAction, type CardActionProps, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, DEFAULT_PASSWORD_REVEAL_LABELS, DEFAULT_TABS_LABELS, EmptyState, type EmptyStateProps, FIELD_BASE, FIELD_DISPLAY, FIELD_FLOATING_PAD, FIELD_INVALID, FIELD_TRIGGER, FIELD_WRITABLE_LOOK, FLOATING_INPUT_CLASS, FLOATING_LABEL_CLASS, FLOATING_LABEL_STATIC, FieldChevron, type FieldChevronProps, FieldHint, type FieldHintProps, FieldLabel, type FieldLabelProps, FloatingField, type FloatingFieldProps, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonTone, Input, type InputProps, Label, type LabelProps, PHONE_QUERY, type PasswordRevealLabels, Select, type SelectProps, Spinner, type SpinnerProps, type TabItem, Tabs, type TabsLabels, type TabsProps, Textarea, type TextareaProps, buttonClasses, resolvePasswordRevealLabels };
|
|
781
|
+
export { Button, type ButtonClassesOptions, type ButtonProps, type ButtonSize, type ButtonTone, type ButtonVariant, Card, CardAction, type CardActionProps, CardContent, type CardContentProps, CardDescription, type CardDescriptionProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, CardTitle, type CardTitleProps, DEFAULT_PASSWORD_REVEAL_LABELS, DEFAULT_TABS_LABELS, EmptyState, type EmptyStateProps, FIELD_BASE, FIELD_DISPLAY, FIELD_FLOATING_PAD, FIELD_INVALID, FIELD_TRIGGER, FIELD_WRITABLE_LOOK, FLOATING_INPUT_CLASS, FLOATING_LABEL_CLASS, FLOATING_LABEL_STATIC, FieldChevron, type FieldChevronProps, FieldHint, type FieldHintProps, FieldLabel, type FieldLabelProps, FloatingField, type FloatingFieldProps, IconButton, type IconButtonProps, type IconButtonSize, type IconButtonTone, Input, type InputProps, Label, type LabelProps, PHONE_QUERY, type PasswordRevealLabels, Select, type SelectProps, Spinner, type SpinnerProps, type TabItem, Tabs, type TabsLabels, type TabsProps, Textarea, type TextareaProps, buttonClasses, resolvePasswordRevealLabels };
|
package/dist/components/ui.js
CHANGED
|
@@ -27,15 +27,48 @@ const buttonVariantClasses = {
|
|
|
27
27
|
// ring hugging the word instead of drawing a pill round it.
|
|
28
28
|
link: "rounded-sm p-0 bg-transparent text-[var(--brand)] underline-offset-4 hover:underline focus:ring-[var(--brand)]"
|
|
29
29
|
};
|
|
30
|
+
const BUTTON_TONES = {
|
|
31
|
+
muted: "text-[var(--text-secondary)] hover:text-[var(--text-primary)] disabled:hover:text-[var(--text-secondary)]",
|
|
32
|
+
danger: "text-[var(--text-secondary)] hover:text-[var(--danger)] focus:ring-[var(--danger-border)] disabled:hover:text-[var(--text-secondary)]"
|
|
33
|
+
};
|
|
34
|
+
const TONED_VARIANTS = /* @__PURE__ */ new Set(["link", "ghost"]);
|
|
35
|
+
const BUTTON_LINK_PRESSED = "font-medium text-[var(--brand)] hover:text-[var(--brand)] disabled:hover:text-[var(--brand)]";
|
|
30
36
|
function buttonClasses(variant = "primary", classNameOrOptions) {
|
|
31
37
|
const { size = "md", className } = typeof classNameOrOptions === "object" ? classNameOrOptions : { className: classNameOrOptions };
|
|
32
38
|
return cn(BUTTON_BASE, BUTTON_SIZES[size], buttonVariantClasses[variant], className);
|
|
33
39
|
}
|
|
34
|
-
function
|
|
35
|
-
return
|
|
40
|
+
function hasContent(node) {
|
|
41
|
+
return node !== void 0 && node !== null && node !== false && node !== "";
|
|
42
|
+
}
|
|
43
|
+
function Button({
|
|
44
|
+
variant = "primary",
|
|
45
|
+
size = "md",
|
|
46
|
+
stretch,
|
|
47
|
+
tone = "default",
|
|
48
|
+
pressed,
|
|
49
|
+
disabledReason,
|
|
50
|
+
className,
|
|
51
|
+
onClick,
|
|
52
|
+
...rest
|
|
53
|
+
}) {
|
|
54
|
+
const reasonId = useId();
|
|
55
|
+
const locked = hasContent(disabledReason);
|
|
56
|
+
const ownDescribedBy = rest["aria-describedby"];
|
|
57
|
+
const button = /* @__PURE__ */ jsx(
|
|
36
58
|
"button",
|
|
37
59
|
{
|
|
38
60
|
...rest,
|
|
61
|
+
disabled: locked ? void 0 : rest.disabled,
|
|
62
|
+
"aria-disabled": locked || rest["aria-disabled"],
|
|
63
|
+
"aria-describedby": locked ? ownDescribedBy ? `${ownDescribedBy} ${reasonId}` : reasonId : ownDescribedBy,
|
|
64
|
+
"aria-pressed": pressed ?? rest["aria-pressed"],
|
|
65
|
+
onClick: (e) => {
|
|
66
|
+
if (locked) {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
onClick?.(e);
|
|
71
|
+
},
|
|
39
72
|
className: cn(
|
|
40
73
|
BUTTON_BASE,
|
|
41
74
|
BUTTON_SIZES[size],
|
|
@@ -44,13 +77,31 @@ function Button({ variant = "primary", size = "md", stretch, className, ...rest
|
|
|
44
77
|
// align-items). No effect outside a flex row / when it's already the tallest.
|
|
45
78
|
stretch && "self-stretch",
|
|
46
79
|
buttonVariantClasses[variant],
|
|
80
|
+
tone !== "default" && TONED_VARIANTS.has(variant) && BUTTON_TONES[tone],
|
|
81
|
+
pressed && (variant === "link" ? BUTTON_LINK_PRESSED : ICON_BUTTON_PRESSED),
|
|
82
|
+
// The disabled look for the focusable kind of disabled.
|
|
83
|
+
locked && "cursor-not-allowed opacity-50",
|
|
47
84
|
className
|
|
48
85
|
)
|
|
49
86
|
}
|
|
50
87
|
);
|
|
88
|
+
if (!locked) return button;
|
|
89
|
+
return (
|
|
90
|
+
// The button in a FRAGMENT so Tooltip does not clone its bubble into the
|
|
91
|
+
// description as well — the hidden copy below already describes it, and the same
|
|
92
|
+
// sentence twice would be read on every focus. `stretch` moves to the wrapper,
|
|
93
|
+
// which is now the flex item.
|
|
94
|
+
/* @__PURE__ */ jsx(Tooltip, { label: disabledReason, className: stretch ? "self-stretch" : void 0, children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
95
|
+
button,
|
|
96
|
+
/* @__PURE__ */ jsx("span", { id: reasonId, hidden: true, children: disabledReason })
|
|
97
|
+
] }) })
|
|
98
|
+
);
|
|
51
99
|
}
|
|
52
100
|
const ICON_BUTTON_BASE = "inline-flex items-center justify-center rounded-md transition-colors focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed";
|
|
53
101
|
const ICON_BUTTON_SIZES = {
|
|
102
|
+
// 48px with a 24px glyph: the one primary action of a phone screen or sheet, a step
|
|
103
|
+
// past the 44px minimum rather than on it.
|
|
104
|
+
xl: "size-12 [&_svg]:size-6",
|
|
54
105
|
// The 44px touch target (WCAG 2.5.5's size) with the same 20px glyph — keksdose's
|
|
55
106
|
// bulk-action bars write `size-11` by hand over an `md` button to get it on phones.
|
|
56
107
|
lg: "size-11 [&_svg]:size-5",
|
|
@@ -59,6 +110,14 @@ const ICON_BUTTON_SIZES = {
|
|
|
59
110
|
xs: "size-7 rounded [&_svg]:size-4",
|
|
60
111
|
"2xs": "size-6 rounded [&_svg]:size-3.5"
|
|
61
112
|
};
|
|
113
|
+
const ICON_BUTTON_STRETCH_SIZES = {
|
|
114
|
+
xl: "w-12 min-h-12 self-stretch [&_svg]:size-6",
|
|
115
|
+
lg: "w-11 min-h-11 self-stretch [&_svg]:size-5",
|
|
116
|
+
md: "w-9 min-h-9 self-stretch [&_svg]:size-5",
|
|
117
|
+
sm: "w-8 min-h-8 self-stretch [&_svg]:size-5",
|
|
118
|
+
xs: "w-7 min-h-7 self-stretch rounded [&_svg]:size-4",
|
|
119
|
+
"2xs": "w-6 min-h-6 self-stretch rounded [&_svg]:size-3.5"
|
|
120
|
+
};
|
|
62
121
|
const ICON_BUTTON_TONES = {
|
|
63
122
|
danger: {
|
|
64
123
|
quiet: "text-[var(--text-placeholder)] hover:bg-[var(--danger-bg)] hover:text-[var(--danger)] focus:ring-[var(--danger-border)]",
|
|
@@ -71,10 +130,27 @@ const ICON_BUTTON_TONES = {
|
|
|
71
130
|
info: {
|
|
72
131
|
quiet: "text-[var(--text-placeholder)] hover:bg-[var(--info-bg)] hover:text-[var(--info)] focus:ring-[var(--info-border)]",
|
|
73
132
|
toned: "text-[var(--info)] hover:bg-[var(--info-bg)] focus:ring-[var(--info-border)]"
|
|
133
|
+
},
|
|
134
|
+
success: {
|
|
135
|
+
quiet: "text-[var(--text-placeholder)] hover:bg-[var(--success-bg)] hover:text-[var(--success)] focus:ring-[var(--success-border)]",
|
|
136
|
+
toned: "text-[var(--success)] hover:bg-[var(--success-bg)] focus:ring-[var(--success-border)]"
|
|
137
|
+
},
|
|
138
|
+
// The hover fill and the ring are MIXED from the one colour, since a custom tone
|
|
139
|
+
// brings no `-bg` / `-border` pair of its own. The fallback is the body colour, so a
|
|
140
|
+
// `custom` tone with no colour set is the default look rather than invisible.
|
|
141
|
+
custom: {
|
|
142
|
+
quiet: "text-[var(--text-placeholder)] hover:bg-[color-mix(in_srgb,var(--icon-button-tone,var(--text-primary))_12%,transparent)] hover:text-[var(--icon-button-tone,var(--text-primary))] focus:ring-[color-mix(in_srgb,var(--icon-button-tone,var(--text-primary))_40%,transparent)]",
|
|
143
|
+
toned: "text-[var(--icon-button-tone,var(--text-primary))] hover:bg-[color-mix(in_srgb,var(--icon-button-tone,var(--text-primary))_12%,transparent)] focus:ring-[color-mix(in_srgb,var(--icon-button-tone,var(--text-primary))_40%,transparent)]"
|
|
74
144
|
}
|
|
75
145
|
};
|
|
76
146
|
const ICON_BUTTON_MUTED = "text-[var(--text-placeholder)] hover:bg-[var(--bg-surface-2)] hover:text-[var(--text-primary)]";
|
|
77
|
-
const QUIET_BY_DEFAULT = {
|
|
147
|
+
const QUIET_BY_DEFAULT = {
|
|
148
|
+
danger: true,
|
|
149
|
+
warning: false,
|
|
150
|
+
info: false,
|
|
151
|
+
success: false,
|
|
152
|
+
custom: false
|
|
153
|
+
};
|
|
78
154
|
function iconButtonToneClass(tone, quiet) {
|
|
79
155
|
if (tone === "default") return "";
|
|
80
156
|
if (tone === "muted") return cn(ICON_BUTTON_MUTED, ICON_BUTTON_QUIET_DISABLED_REST);
|
|
@@ -94,15 +170,48 @@ const ICON_BUTTON_DISABLED_REST = {
|
|
|
94
170
|
const ICON_BUTTON_QUIET_DISABLED_REST = "disabled:hover:text-[var(--text-placeholder)]";
|
|
95
171
|
const ICON_BUTTON_PRESSED = "bg-[var(--brand-bg)] text-[var(--brand)] hover:bg-[var(--brand-bg-hover)] hover:text-[var(--brand)] disabled:hover:bg-[var(--brand-bg)] disabled:hover:text-[var(--brand)]";
|
|
96
172
|
const ICON_BUTTON_OVERLAY = "rounded-full bg-[color-mix(in_srgb,var(--bg-inverse)_60%,transparent)] text-[var(--text-inverse)] backdrop-blur-sm hover:bg-[color-mix(in_srgb,var(--bg-inverse)_75%,transparent)] focus:ring-[var(--text-inverse)]";
|
|
97
|
-
const IconButton = forwardRef(function IconButton2({
|
|
98
|
-
|
|
173
|
+
const IconButton = forwardRef(function IconButton2({
|
|
174
|
+
variant = "ghost",
|
|
175
|
+
size = "md",
|
|
176
|
+
tone: toneProp,
|
|
177
|
+
quiet,
|
|
178
|
+
pressed,
|
|
179
|
+
stopPropagation,
|
|
180
|
+
stretch,
|
|
181
|
+
shape = "square",
|
|
182
|
+
toneColor,
|
|
183
|
+
label,
|
|
184
|
+
tooltip = true,
|
|
185
|
+
tooltipSide,
|
|
186
|
+
tooltipPortal,
|
|
187
|
+
disabledReason,
|
|
188
|
+
className,
|
|
189
|
+
style,
|
|
190
|
+
onClick,
|
|
191
|
+
onKeyDown,
|
|
192
|
+
...rest
|
|
193
|
+
}, ref) {
|
|
194
|
+
const tone = toneProp ?? (toneColor !== void 0 ? "custom" : "default");
|
|
195
|
+
const reasonId = useId();
|
|
196
|
+
const locked = hasContent(disabledReason);
|
|
197
|
+
const ownDescribedBy = rest["aria-describedby"];
|
|
198
|
+
const button = /* @__PURE__ */ jsx(
|
|
99
199
|
"button",
|
|
100
200
|
{
|
|
101
201
|
ref,
|
|
102
202
|
...rest,
|
|
203
|
+
disabled: locked ? void 0 : rest.disabled,
|
|
204
|
+
"aria-disabled": locked || rest["aria-disabled"],
|
|
205
|
+
"aria-describedby": locked ? ownDescribedBy ? `${ownDescribedBy} ${reasonId}` : reasonId : ownDescribedBy,
|
|
206
|
+
"aria-label": rest["aria-label"] ?? label,
|
|
103
207
|
"aria-pressed": pressed ?? rest["aria-pressed"],
|
|
208
|
+
style: toneColor !== void 0 ? { ...style, ["--icon-button-tone"]: toneColor } : style,
|
|
104
209
|
onClick: (e) => {
|
|
105
210
|
if (stopPropagation) e.stopPropagation();
|
|
211
|
+
if (locked) {
|
|
212
|
+
e.preventDefault();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
106
215
|
onClick?.(e);
|
|
107
216
|
},
|
|
108
217
|
onKeyDown: (e) => {
|
|
@@ -111,16 +220,44 @@ const IconButton = forwardRef(function IconButton2({ variant = "ghost", size = "
|
|
|
111
220
|
},
|
|
112
221
|
className: cn(
|
|
113
222
|
ICON_BUTTON_BASE,
|
|
114
|
-
ICON_BUTTON_SIZES[size],
|
|
223
|
+
stretch ? ICON_BUTTON_STRETCH_SIZES[size] : ICON_BUTTON_SIZES[size],
|
|
115
224
|
// After the size, so the overlay's `rounded-full` beats the small sizes' `rounded`.
|
|
116
225
|
variant === "overlay" ? ICON_BUTTON_OVERLAY : buttonVariantClasses[variant],
|
|
226
|
+
shape === "round" && "rounded-full",
|
|
117
227
|
ICON_BUTTON_DISABLED_REST[variant],
|
|
118
228
|
iconButtonToneClass(tone, quiet),
|
|
119
229
|
pressed && ICON_BUTTON_PRESSED,
|
|
230
|
+
// The disabled look for the focusable kind of disabled, as on Button.
|
|
231
|
+
locked && "cursor-not-allowed opacity-50",
|
|
120
232
|
className
|
|
121
233
|
)
|
|
122
234
|
}
|
|
123
235
|
);
|
|
236
|
+
if (locked) {
|
|
237
|
+
return (
|
|
238
|
+
// Button's shape: the button and the hidden reason in a FRAGMENT, so Tooltip does
|
|
239
|
+
// not add its bubble to the description as well.
|
|
240
|
+
/* @__PURE__ */ jsx(
|
|
241
|
+
Tooltip,
|
|
242
|
+
{
|
|
243
|
+
label: disabledReason,
|
|
244
|
+
side: tooltipSide,
|
|
245
|
+
portal: tooltipPortal,
|
|
246
|
+
className: stretch ? "self-stretch" : void 0,
|
|
247
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
248
|
+
button,
|
|
249
|
+
/* @__PURE__ */ jsx("span", { id: reasonId, hidden: true, children: disabledReason })
|
|
250
|
+
] })
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
if (!tooltip || label === void 0 || label === "") return button;
|
|
256
|
+
return (
|
|
257
|
+
// A fragment, so Tooltip leaves the button's description alone: the bubble says
|
|
258
|
+
// exactly what `aria-label` already does.
|
|
259
|
+
/* @__PURE__ */ jsx(Tooltip, { label, side: tooltipSide, portal: tooltipPortal, className: stretch ? "self-stretch" : void 0, children: /* @__PURE__ */ jsx(Fragment, { children: button }) })
|
|
260
|
+
);
|
|
124
261
|
});
|
|
125
262
|
IconButton.displayName = "IconButton";
|
|
126
263
|
const FIELD_BASE = "block w-full rounded-md border border-[var(--border)] bg-[var(--bg-surface)] px-3 py-2 text-sm text-[var(--text-primary)] shadow-sm placeholder:text-[var(--text-placeholder)] focus:border-[var(--brand)] focus:ring-[var(--brand)] disabled:cursor-default disabled:bg-[var(--bg-surface-2)] disabled:text-[var(--text-muted)] [&[readonly]]:bg-[var(--bg-surface-2)] [&[readonly]]:text-[var(--text-muted)]";
|
|
@@ -581,8 +718,52 @@ function Spinner({ className, label, ...rest }) {
|
|
|
581
718
|
" "
|
|
582
719
|
] }) });
|
|
583
720
|
}
|
|
584
|
-
|
|
721
|
+
const EMPTY_STATE_TONE = {
|
|
722
|
+
danger: "text-[var(--danger)]",
|
|
723
|
+
success: "text-[var(--success)]"
|
|
724
|
+
};
|
|
725
|
+
function EmptyState({
|
|
726
|
+
title,
|
|
727
|
+
hint,
|
|
728
|
+
icon,
|
|
729
|
+
action,
|
|
730
|
+
headingAs,
|
|
731
|
+
variant = "box",
|
|
732
|
+
tone,
|
|
733
|
+
size = "md",
|
|
734
|
+
className,
|
|
735
|
+
...rest
|
|
736
|
+
}) {
|
|
585
737
|
const Title = headingAs ?? "div";
|
|
738
|
+
const toneClass = tone ? EMPTY_STATE_TONE[tone] : void 0;
|
|
739
|
+
const hasHint = hint != null && hint !== false && hint !== "";
|
|
740
|
+
if (variant === "inline") {
|
|
741
|
+
const sm = size === "sm";
|
|
742
|
+
return /* @__PURE__ */ jsxs(
|
|
743
|
+
"div",
|
|
744
|
+
{
|
|
745
|
+
...rest,
|
|
746
|
+
className: cn(
|
|
747
|
+
"flex flex-wrap items-center text-[var(--text-muted)]",
|
|
748
|
+
sm ? "justify-start gap-x-1.5 gap-y-0.5 py-1 text-start text-xs" : "justify-center gap-x-2 gap-y-1 py-4 text-center text-sm",
|
|
749
|
+
className
|
|
750
|
+
),
|
|
751
|
+
children: [
|
|
752
|
+
icon != null && /* @__PURE__ */ jsx(
|
|
753
|
+
"span",
|
|
754
|
+
{
|
|
755
|
+
"aria-hidden": true,
|
|
756
|
+
className: cn("flex text-[var(--text-placeholder)]", sm ? "[&_svg]:size-3.5" : "[&_svg]:size-4", toneClass),
|
|
757
|
+
children: icon
|
|
758
|
+
}
|
|
759
|
+
),
|
|
760
|
+
/* @__PURE__ */ jsx(Title, { className: cn(sm ? "text-xs" : "text-sm", toneClass), children: title }),
|
|
761
|
+
hasHint && /* @__PURE__ */ jsx("span", { className: "text-xs", children: hint }),
|
|
762
|
+
action != null && /* @__PURE__ */ jsx("span", { className: "flex flex-wrap items-center gap-2", children: action })
|
|
763
|
+
]
|
|
764
|
+
}
|
|
765
|
+
);
|
|
766
|
+
}
|
|
586
767
|
return /* @__PURE__ */ jsxs(
|
|
587
768
|
"div",
|
|
588
769
|
{
|
|
@@ -592,9 +773,9 @@ function EmptyState({ title, hint, icon, action, headingAs, className, ...rest }
|
|
|
592
773
|
className
|
|
593
774
|
),
|
|
594
775
|
children: [
|
|
595
|
-
icon != null && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "mb-3 text-[var(--text-placeholder)] [&_svg]:size-8", children: icon }),
|
|
596
|
-
/* @__PURE__ */ jsx(Title, { className: "text-sm font-medium text-[var(--text-secondary)]", children: title }),
|
|
597
|
-
|
|
776
|
+
icon != null && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: cn("mb-3 text-[var(--text-placeholder)] [&_svg]:size-8", toneClass), children: icon }),
|
|
777
|
+
/* @__PURE__ */ jsx(Title, { className: cn("text-sm font-medium text-[var(--text-secondary)]", toneClass), children: title }),
|
|
778
|
+
hasHint && /* @__PURE__ */ jsx("div", { className: "mt-1 text-xs", children: hint }),
|
|
598
779
|
action != null && /* @__PURE__ */ jsx("div", { className: "mt-4 flex flex-wrap items-center justify-center gap-2", children: action })
|
|
599
780
|
]
|
|
600
781
|
}
|