@boxcustodia/library 2.0.0-alpha.13 → 2.0.0-alpha.14
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/dist/index.cjs.js +1 -138
- package/dist/index.d.ts +1083 -715
- package/dist/index.es.js +7077 -56175
- package/dist/theme.css +1 -1
- package/package.json +34 -26
- package/src/__doc__/Examples.tsx +1 -1
- package/src/__doc__/Intro.mdx +3 -3
- package/src/__doc__/Tabs.mdx +112 -0
- package/src/__doc__/V2.mdx +1246 -0
- package/src/components/accordion/accordion.stories.tsx +143 -0
- package/src/components/accordion/accordion.tsx +135 -0
- package/src/components/accordion/index.ts +1 -0
- package/src/components/alert/alert.stories.tsx +24 -4
- package/src/components/alert/alert.tsx +17 -9
- package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
- package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
- package/src/components/alert-dialog/alert-dialog.tsx +58 -10
- package/src/components/auto-complete/auto-complete.stories.tsx +616 -200
- package/src/components/auto-complete/auto-complete.tsx +420 -68
- package/src/components/auto-complete/index.ts +0 -1
- package/src/components/avatar/avatar.stories.tsx +162 -21
- package/src/components/avatar/avatar.tsx +79 -20
- package/src/components/button/button.stories.tsx +219 -294
- package/src/components/button/button.test.tsx +10 -17
- package/src/components/button/button.tsx +78 -19
- package/src/components/button/components/base-button.tsx +30 -53
- package/src/components/button/index.ts +0 -1
- package/src/components/calendar/calendar.stories.tsx +1 -1
- package/src/components/calendar/calendar.tsx +4 -4
- package/src/components/card/card.stories.tsx +141 -69
- package/src/components/card/card.tsx +155 -54
- package/src/components/center/center.stories.tsx +22 -39
- package/src/components/checkbox/checkbox.stories.tsx +25 -5
- package/src/components/checkbox/checkbox.tsx +76 -15
- package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
- package/src/components/checkbox-group/checkbox-group.tsx +84 -3
- package/src/components/combobox/combobox.stories.tsx +33 -23
- package/src/components/combobox/combobox.tsx +119 -103
- package/src/components/date-picker/date-input.stories.tsx +14 -6
- package/src/components/date-picker/date-input.tsx +2 -2
- package/src/components/date-picker/date-picker.model.ts +13 -4
- package/src/components/date-picker/date-picker.stories.tsx +38 -12
- package/src/components/date-picker/date-picker.tsx +28 -14
- package/src/components/dialog/dialog.stories.tsx +18 -0
- package/src/components/dialog/dialog.test.tsx +1 -1
- package/src/components/dialog/dialog.tsx +51 -20
- package/src/components/divider/divider.stories.tsx +6 -0
- package/src/components/dropzone/dropzone.stories.tsx +71 -90
- package/src/components/dropzone/dropzone.tsx +383 -105
- package/src/components/dropzone/index.ts +0 -1
- package/src/components/empty/empty.stories.tsx +165 -0
- package/src/components/empty/empty.tsx +156 -0
- package/src/components/empty/index.ts +1 -0
- package/src/components/field/field.stories.tsx +226 -3
- package/src/components/field/field.tsx +77 -42
- package/src/components/form/form.stories.tsx +320 -197
- package/src/components/form/form.tsx +3 -23
- package/src/components/index.ts +2 -6
- package/src/components/input/input.stories.tsx +5 -5
- package/src/components/input/input.tsx +4 -4
- package/src/components/kbd/kbd.stories.tsx +1 -0
- package/src/components/label/label.stories.tsx +16 -0
- package/src/components/label/label.tsx +13 -2
- package/src/components/loader/loader.stories.tsx +7 -5
- package/src/components/loader/loader.tsx +8 -3
- package/src/components/menu/menu-primitives.tsx +207 -196
- package/src/components/menu/menu.stories.tsx +276 -146
- package/src/components/menu/menu.tsx +146 -54
- package/src/components/number-input/number-input.stories.tsx +27 -4
- package/src/components/number-input/number-input.test.tsx +2 -2
- package/src/components/number-input/number-input.tsx +25 -29
- package/src/components/otp/index.ts +1 -0
- package/src/components/otp/otp.stories.tsx +209 -0
- package/src/components/otp/otp.tsx +100 -0
- package/src/components/pagination/index.ts +1 -0
- package/src/components/pagination/pagination.model.ts +2 -0
- package/src/components/pagination/pagination.stories.tsx +154 -59
- package/src/components/pagination/pagination.test.tsx +122 -57
- package/src/components/pagination/pagination.tsx +575 -77
- package/src/components/password/password.stories.tsx +18 -3
- package/src/components/password/password.tsx +26 -10
- package/src/components/popover/popover.stories.tsx +26 -5
- package/src/components/popover/popover.tsx +15 -23
- package/src/components/progress/progress.stories.tsx +1 -0
- package/src/components/radio-group/index.ts +1 -0
- package/src/components/radio-group/radio-group.stories.tsx +251 -0
- package/src/components/radio-group/radio-group.tsx +212 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
- package/src/components/select/select.stories.tsx +118 -19
- package/src/components/select/select.tsx +67 -62
- package/src/components/skeleton/skeleton.stories.tsx +1 -0
- package/src/components/stack/stack.stories.tsx +179 -89
- package/src/components/stack/stack.tsx +2 -2
- package/src/components/stepper/index.ts +1 -1
- package/src/components/stepper/stepper.stories.tsx +767 -83
- package/src/components/stepper/stepper.test.tsx +18 -18
- package/src/components/stepper/stepper.tsx +554 -0
- package/src/components/switch/switch.stories.tsx +15 -1
- package/src/components/switch/switch.tsx +17 -4
- package/src/components/table/index.ts +0 -2
- package/src/components/table/table.stories.tsx +131 -18
- package/src/components/table/table.test.tsx +1 -1
- package/src/components/table/table.tsx +183 -77
- package/src/components/tabs/tabs.stories.tsx +373 -155
- package/src/components/tabs/tabs.test.tsx +12 -12
- package/src/components/tabs/tabs.tsx +72 -149
- package/src/components/tag/index.ts +0 -1
- package/src/components/tag/tag.stories.tsx +155 -120
- package/src/components/tag/tag.tsx +47 -95
- package/src/components/textarea/textarea.stories.tsx +8 -22
- package/src/components/textarea/textarea.tsx +17 -79
- package/src/components/timeline/timeline.stories.tsx +323 -42
- package/src/components/timeline/timeline.tsx +359 -132
- package/src/components/toast/toast.stories.tsx +1 -0
- package/src/components/tooltip/tooltip.tsx +11 -9
- package/src/components/tree/index.ts +0 -1
- package/src/components/tree/tree.stories.tsx +365 -408
- package/src/components/tree/tree.test.tsx +163 -0
- package/src/components/tree/tree.tsx +212 -36
- package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
- package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
- package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
- package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
- package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
- package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
- package/src/hooks/usePagination/usePagination.tsx +36 -24
- package/src/styles/theme.css +1 -1
- package/src/utils/form.tsx +67 -37
- package/src/utils/index.ts +1 -1
- package/src/__doc__/Migration.mdx +0 -451
- package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
- package/src/components/background-image/background-image.stories.tsx +0 -21
- package/src/components/background-image/background-image.test.tsx +0 -29
- package/src/components/background-image/background-image.tsx +0 -23
- package/src/components/background-image/index.ts +0 -1
- package/src/components/button/button.variants.ts +0 -44
- package/src/components/button/components/loader-overlay.tsx +0 -21
- package/src/components/button/components/loading-icon.tsx +0 -47
- package/src/components/dropzone/upload-primitives.tsx +0 -310
- package/src/components/dropzone/use-dropzone.ts +0 -122
- package/src/components/empty-state/empty-state.stories.tsx +0 -56
- package/src/components/empty-state/empty-state.tsx +0 -39
- package/src/components/empty-state/index.ts +0 -1
- package/src/components/heading/heading.stories.tsx +0 -74
- package/src/components/heading/heading.tsx +0 -28
- package/src/components/heading/heading.variants.ts +0 -27
- package/src/components/heading/index.ts +0 -1
- package/src/components/kbd/kbd.variants.ts +0 -26
- package/src/components/menu/util/render-menu-item.tsx +0 -54
- package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
- package/src/components/multi-select/index.ts +0 -1
- package/src/components/multi-select/multi-select.stories.tsx +0 -294
- package/src/components/multi-select/multi-select.tsx +0 -300
- package/src/components/multi-select/multi-select.variants.ts +0 -22
- package/src/components/pagination/components/pagination-option.tsx +0 -27
- package/src/components/show/index.ts +0 -1
- package/src/components/show/show.stories.tsx +0 -197
- package/src/components/show/show.test.tsx +0 -41
- package/src/components/show/show.tsx +0 -16
- package/src/components/stepper/Stepper.tsx +0 -190
- package/src/components/stepper/context/stepper-context.tsx +0 -11
- package/src/components/table/table-primitives.tsx +0 -122
- package/src/components/table/table.model.ts +0 -20
- package/src/components/table-pagination/index.ts +0 -2
- package/src/components/table-pagination/table-pagination.model.ts +0 -2
- package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
- package/src/components/table-pagination/table-pagination.test.tsx +0 -32
- package/src/components/table-pagination/table-pagination.tsx +0 -108
- package/src/components/tabs/context/tabs-context.tsx +0 -14
- package/src/components/tag/tag.variants.ts +0 -31
- package/src/components/timeline/timeline-status.ts +0 -5
- package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
- package/src/components/tree/tree-primitives.tsx +0 -126
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
import { faker } from "@faker-js/faker";
|
|
2
1
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
3
|
-
import { User } from "lucide-react";
|
|
2
|
+
import { Check, User } from "lucide-react";
|
|
4
3
|
import { action } from "storybook/actions";
|
|
5
4
|
import {
|
|
6
5
|
Avatar,
|
|
6
|
+
AvatarBadge,
|
|
7
7
|
AvatarFallback,
|
|
8
|
+
AvatarGroup,
|
|
9
|
+
AvatarGroupCount,
|
|
8
10
|
AvatarImage,
|
|
9
11
|
AvatarRoot,
|
|
10
12
|
} from "../../components";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
|
-
*
|
|
15
|
+
* Circular user profile image with automatic fallback to text initials derived from `alt`.
|
|
14
16
|
* Built on Base UI Avatar — image loading and fallback visibility are handled natively.
|
|
15
17
|
* Use `delay` to defer the fallback appearance and avoid a flash during image load.
|
|
16
|
-
*
|
|
18
|
+
* Pass `badge` to render a status/notification dot via `AvatarBadge`.
|
|
19
|
+
* Exposes `AvatarRoot`, `AvatarImage`, `AvatarFallback`, `AvatarBadge`,
|
|
20
|
+
* `AvatarGroup`, `AvatarGroupCount` primitives for custom compositions.
|
|
17
21
|
*/
|
|
18
22
|
const meta: Meta<typeof Avatar> = {
|
|
19
23
|
title: "components/Avatar",
|
|
20
24
|
component: Avatar,
|
|
25
|
+
parameters: { layout: "centered" },
|
|
21
26
|
args: {
|
|
22
|
-
alt:
|
|
23
|
-
src:
|
|
27
|
+
alt: "Sara Lane",
|
|
28
|
+
src: "https://i.pravatar.cc/150?img=47",
|
|
24
29
|
delay: 1000,
|
|
25
30
|
},
|
|
26
31
|
} satisfies Meta<typeof Avatar>;
|
|
@@ -30,6 +35,22 @@ type Story = StoryObj<typeof meta>;
|
|
|
30
35
|
|
|
31
36
|
export const Default: Story = {};
|
|
32
37
|
|
|
38
|
+
/**
|
|
39
|
+
* `className` styles the avatar root. `classNames` exposes the `image`,
|
|
40
|
+
* `fallback`, and `badge` slots for fine-grained styling without dropping
|
|
41
|
+
* to primitives.
|
|
42
|
+
*/
|
|
43
|
+
export const WithClassNames: Story = {
|
|
44
|
+
args: {
|
|
45
|
+
badge: <Check />,
|
|
46
|
+
className: "ring-2 ring-ring",
|
|
47
|
+
classNames: {
|
|
48
|
+
fallback: "bg-primary text-primary-foreground",
|
|
49
|
+
badge: "bg-emerald-500",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
33
54
|
/**
|
|
34
55
|
* When `src` is empty or the image fails to load, initials are extracted from `alt` automatically.
|
|
35
56
|
*/
|
|
@@ -39,46 +60,166 @@ export const FallbackText: Story = {
|
|
|
39
60
|
},
|
|
40
61
|
};
|
|
41
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Pass any node to `fallback` to override the default initials — useful for generic
|
|
65
|
+
* placeholders, icons, or branded glyphs.
|
|
66
|
+
*/
|
|
42
67
|
export const CustomFallback: Story = {
|
|
43
68
|
args: {
|
|
44
69
|
src: "",
|
|
45
|
-
fallback: <User />,
|
|
70
|
+
fallback: <User className="size-1/2" />,
|
|
46
71
|
},
|
|
47
72
|
};
|
|
48
73
|
|
|
49
74
|
export const Sizes: Story = {
|
|
50
75
|
render: (args) => (
|
|
51
|
-
<div className="flex items-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
76
|
+
<div className="flex items-end gap-6">
|
|
77
|
+
{(["xs", "sm", "md", "lg", "xl"] as const).map((size) => (
|
|
78
|
+
<div key={size} className="flex flex-col items-center gap-2">
|
|
79
|
+
<Avatar {...args} size={size} />
|
|
80
|
+
<span className="text-xs text-muted-foreground font-mono">
|
|
81
|
+
{size}
|
|
82
|
+
</span>
|
|
83
|
+
</div>
|
|
84
|
+
))}
|
|
57
85
|
</div>
|
|
58
86
|
),
|
|
59
87
|
};
|
|
60
88
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
89
|
+
/**
|
|
90
|
+
* `badge` mounts an `AvatarBadge` inside the root. The badge auto-scales with the
|
|
91
|
+
* avatar's `size` via `group-data-[size=*]/avatar` selectors and clips icons on
|
|
92
|
+
* the smallest sizes to keep the dot legible.
|
|
93
|
+
*
|
|
94
|
+
* For a plain status indicator, leave the badge empty (`<span />`) and override
|
|
95
|
+
* its color via `className`.
|
|
96
|
+
*/
|
|
97
|
+
export const Badge: Story = {
|
|
98
|
+
render: () => (
|
|
99
|
+
<div className="flex items-end gap-6">
|
|
100
|
+
{(["sm", "md", "lg", "xl"] as const).map((size) => (
|
|
101
|
+
<div key={size} className="flex flex-col items-center gap-2">
|
|
102
|
+
<Avatar
|
|
103
|
+
src="https://i.pravatar.cc/150?img=47"
|
|
104
|
+
alt="Sara Lane"
|
|
105
|
+
size={size}
|
|
106
|
+
badge={<Check />}
|
|
107
|
+
/>
|
|
108
|
+
<span className="text-xs text-muted-foreground font-mono">
|
|
109
|
+
{size}
|
|
110
|
+
</span>
|
|
111
|
+
</div>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Status pattern via `AvatarBadge` on the primitive. The badge's color comes
|
|
119
|
+
* from `className`, the position and ring from the primitive itself.
|
|
120
|
+
*/
|
|
121
|
+
export const Status: Story = {
|
|
122
|
+
render: () => (
|
|
123
|
+
<div className="flex items-center gap-6">
|
|
124
|
+
{(
|
|
125
|
+
[
|
|
126
|
+
{ label: "online", color: "bg-emerald-500" },
|
|
127
|
+
{ label: "away", color: "bg-amber-500" },
|
|
128
|
+
{ label: "offline", color: "bg-zinc-400" },
|
|
129
|
+
] as const
|
|
130
|
+
).map(({ label, color }) => (
|
|
131
|
+
<div key={label} className="flex flex-col items-center gap-2">
|
|
132
|
+
<AvatarRoot size="lg">
|
|
133
|
+
<AvatarImage
|
|
134
|
+
src={`https://i.pravatar.cc/150?u=${label}`}
|
|
135
|
+
alt={label}
|
|
136
|
+
/>
|
|
137
|
+
<AvatarFallback>{label.slice(0, 2).toUpperCase()}</AvatarFallback>
|
|
138
|
+
<AvatarBadge className={color} />
|
|
139
|
+
</AvatarRoot>
|
|
140
|
+
<span className="text-xs text-muted-foreground font-mono">
|
|
141
|
+
{label}
|
|
142
|
+
</span>
|
|
143
|
+
</div>
|
|
144
|
+
))}
|
|
66
145
|
</div>
|
|
67
146
|
),
|
|
68
147
|
};
|
|
69
148
|
|
|
149
|
+
/**
|
|
150
|
+
* `AvatarGroup` clusters avatars with negative spacing and applies a background
|
|
151
|
+
* ring to every direct `Avatar` child so they read as overlapping discs.
|
|
152
|
+
* Combine with `AvatarGroupCount` for the trailing `+N` slot. Match `size`
|
|
153
|
+
* across children and the count to scale the cluster.
|
|
154
|
+
*/
|
|
155
|
+
export const Group: Story = {
|
|
156
|
+
render: () => (
|
|
157
|
+
<AvatarGroup>
|
|
158
|
+
<Avatar src="https://i.pravatar.cc/150?img=47" alt="Sara Lane" />
|
|
159
|
+
<Avatar src="https://i.pravatar.cc/150?img=12" alt="John Doe" />
|
|
160
|
+
<Avatar src="https://i.pravatar.cc/150?img=32" alt="Alex Kim" />
|
|
161
|
+
<Avatar src="https://i.pravatar.cc/150?img=68" alt="Mia Chen" />
|
|
162
|
+
<AvatarGroupCount>+12</AvatarGroupCount>
|
|
163
|
+
</AvatarGroup>
|
|
164
|
+
),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Direct composition with `AvatarRoot`, `AvatarImage`, `AvatarFallback`,
|
|
169
|
+
* `AvatarBadge` for full structural control. Use when the composite API
|
|
170
|
+
* doesn't expose what you need — custom children inside the fallback,
|
|
171
|
+
* layered overlays, etc.
|
|
172
|
+
*
|
|
173
|
+
* ```tsx
|
|
174
|
+
* <AvatarRoot size="lg">
|
|
175
|
+
* <AvatarImage src={src} alt="John Doe" />
|
|
176
|
+
* <AvatarFallback delay={1000}>JD</AvatarFallback>
|
|
177
|
+
* <AvatarBadge className="bg-emerald-500" />
|
|
178
|
+
* </AvatarRoot>
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
70
181
|
export const Primitive: Story = {
|
|
71
182
|
render: () => (
|
|
72
|
-
<AvatarRoot
|
|
73
|
-
<AvatarImage src=
|
|
183
|
+
<AvatarRoot size="lg">
|
|
184
|
+
<AvatarImage src="https://i.pravatar.cc/150?img=13" alt="John Doe" />
|
|
74
185
|
<AvatarFallback delay={1000}>JD</AvatarFallback>
|
|
186
|
+
<AvatarBadge className="bg-emerald-500" />
|
|
75
187
|
</AvatarRoot>
|
|
76
188
|
),
|
|
77
189
|
};
|
|
78
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Avatars accept any `div` prop including `onClick`. Pair with `cursor-pointer`
|
|
193
|
+
* and a ring/hover style for interactive profile triggers.
|
|
194
|
+
*/
|
|
79
195
|
export const Clickable: Story = {
|
|
80
196
|
args: {
|
|
81
197
|
onClick: action("avatar:click"),
|
|
82
|
-
className:
|
|
198
|
+
className:
|
|
199
|
+
"cursor-pointer ring-2 ring-transparent hover:ring-ring transition-shadow",
|
|
83
200
|
},
|
|
84
201
|
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Real-world usage: avatar paired with name, role, and an `AvatarBadge`
|
|
205
|
+
* showing online status — canonical card layout for user lists, mentions,
|
|
206
|
+
* and team rosters.
|
|
207
|
+
*/
|
|
208
|
+
export const UserCard: Story = {
|
|
209
|
+
render: () => (
|
|
210
|
+
<div className="flex items-center gap-3 rounded-lg border bg-background p-4 shadow-sm">
|
|
211
|
+
<AvatarRoot size="lg">
|
|
212
|
+
<AvatarImage src="https://i.pravatar.cc/150?img=47" alt="Sara Lane" />
|
|
213
|
+
<AvatarFallback>SL</AvatarFallback>
|
|
214
|
+
<AvatarBadge className="bg-emerald-500" />
|
|
215
|
+
</AvatarRoot>
|
|
216
|
+
<div className="flex flex-col">
|
|
217
|
+
<span className="font-semibold leading-tight">Sara Lane</span>
|
|
218
|
+
<span className="text-sm text-muted-foreground leading-tight">
|
|
219
|
+
Senior Product Designer
|
|
220
|
+
</span>
|
|
221
|
+
<span className="text-xs text-muted-foreground mt-1">Active now</span>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
),
|
|
225
|
+
};
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
-
import { type ReactNode } from "react";
|
|
3
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
4
4
|
import { cn } from "../../lib";
|
|
5
5
|
import { extractInitials } from "../../utils";
|
|
6
6
|
|
|
7
7
|
const avatarVariants = cva(
|
|
8
|
-
"
|
|
8
|
+
"group/avatar relative flex aspect-square shrink-0 select-none rounded-full bg-muted items-center justify-center font-medium",
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
|
-
shape: {
|
|
12
|
-
circle: "rounded-full",
|
|
13
|
-
square: "rounded-xl",
|
|
14
|
-
},
|
|
15
11
|
size: {
|
|
16
12
|
xs: "size-6 text-[10px]",
|
|
17
13
|
sm: "size-8 text-xs",
|
|
@@ -21,7 +17,6 @@ const avatarVariants = cva(
|
|
|
21
17
|
},
|
|
22
18
|
},
|
|
23
19
|
defaultVariants: {
|
|
24
|
-
shape: "circle",
|
|
25
20
|
size: "md",
|
|
26
21
|
},
|
|
27
22
|
},
|
|
@@ -29,14 +24,14 @@ const avatarVariants = cva(
|
|
|
29
24
|
|
|
30
25
|
export function AvatarRoot({
|
|
31
26
|
className,
|
|
32
|
-
shape,
|
|
33
27
|
size,
|
|
34
28
|
...props
|
|
35
29
|
}: AvatarPrimitive.Root.Props & VariantProps<typeof avatarVariants>) {
|
|
36
30
|
return (
|
|
37
31
|
<AvatarPrimitive.Root
|
|
38
|
-
className={cn(avatarVariants({
|
|
32
|
+
className={cn(avatarVariants({ size }), className)}
|
|
39
33
|
data-slot="avatar"
|
|
34
|
+
data-size={size ?? "md"}
|
|
40
35
|
{...props}
|
|
41
36
|
/>
|
|
42
37
|
);
|
|
@@ -48,7 +43,10 @@ export function AvatarImage({
|
|
|
48
43
|
}: AvatarPrimitive.Image.Props) {
|
|
49
44
|
return (
|
|
50
45
|
<AvatarPrimitive.Image
|
|
51
|
-
className={cn(
|
|
46
|
+
className={cn(
|
|
47
|
+
"aspect-square size-full rounded-full object-cover",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
52
50
|
data-slot="avatar-image"
|
|
53
51
|
{...props}
|
|
54
52
|
/>
|
|
@@ -61,42 +59,103 @@ export function AvatarFallback({
|
|
|
61
59
|
}: AvatarPrimitive.Fallback.Props) {
|
|
62
60
|
return (
|
|
63
61
|
<AvatarPrimitive.Fallback
|
|
64
|
-
className={cn(
|
|
62
|
+
className={cn(
|
|
63
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-muted-foreground",
|
|
64
|
+
className,
|
|
65
|
+
)}
|
|
65
66
|
data-slot="avatar-fallback"
|
|
66
67
|
{...props}
|
|
67
68
|
/>
|
|
68
69
|
);
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
export function AvatarBadge({ className, ...props }: ComponentProps<"span">) {
|
|
73
|
+
return (
|
|
74
|
+
<span
|
|
75
|
+
data-slot="avatar-badge"
|
|
76
|
+
className={cn(
|
|
77
|
+
"absolute bottom-0 right-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground ring-2 ring-background select-none",
|
|
78
|
+
"group-data-[size=xs]/avatar:size-1.5 group-data-[size=xs]/avatar:[&>svg]:hidden",
|
|
79
|
+
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
80
|
+
"group-data-[size=md]/avatar:size-2.5 group-data-[size=md]/avatar:[&>svg]:size-2",
|
|
81
|
+
"group-data-[size=lg]/avatar:size-3.5 group-data-[size=lg]/avatar:[&>svg]:size-2.5",
|
|
82
|
+
"group-data-[size=xl]/avatar:size-4 group-data-[size=xl]/avatar:[&>svg]:size-3",
|
|
83
|
+
className,
|
|
84
|
+
)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function AvatarGroup({ className, ...props }: ComponentProps<"div">) {
|
|
91
|
+
return (
|
|
92
|
+
<div
|
|
93
|
+
data-slot="avatar-group"
|
|
94
|
+
className={cn(
|
|
95
|
+
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
|
|
96
|
+
className,
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function AvatarGroupCount({
|
|
104
|
+
className,
|
|
105
|
+
size,
|
|
106
|
+
...props
|
|
107
|
+
}: ComponentProps<"div"> & VariantProps<typeof avatarVariants>) {
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
data-slot="avatar-group-count"
|
|
111
|
+
className={cn(
|
|
112
|
+
avatarVariants({ size }),
|
|
113
|
+
"ring-2 ring-background bg-muted text-muted-foreground",
|
|
114
|
+
className,
|
|
115
|
+
)}
|
|
116
|
+
{...props}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
71
121
|
interface AvatarProps extends Omit<AvatarPrimitive.Root.Props, "children"> {
|
|
72
122
|
src?: string;
|
|
73
123
|
alt: string;
|
|
74
124
|
fallback?: ReactNode;
|
|
125
|
+
badge?: ReactNode;
|
|
75
126
|
delay?: AvatarPrimitive.Fallback.Props["delay"];
|
|
76
|
-
shape?: VariantProps<typeof avatarVariants>["shape"];
|
|
77
127
|
size?: VariantProps<typeof avatarVariants>["size"];
|
|
78
|
-
|
|
79
|
-
|
|
128
|
+
/** Styles applied to each internal slot. */
|
|
129
|
+
classNames?: {
|
|
130
|
+
/** Image element rendered when `src` loads successfully. */
|
|
131
|
+
image?: string;
|
|
132
|
+
/** Fallback rendered when no image is available. */
|
|
133
|
+
fallback?: string;
|
|
134
|
+
/** Status/notification badge rendered over the avatar. */
|
|
135
|
+
badge?: string;
|
|
136
|
+
};
|
|
80
137
|
}
|
|
81
138
|
|
|
82
139
|
export function Avatar({
|
|
83
140
|
src,
|
|
84
141
|
alt,
|
|
85
142
|
fallback,
|
|
143
|
+
badge,
|
|
86
144
|
delay,
|
|
87
|
-
shape,
|
|
88
145
|
size,
|
|
89
146
|
className,
|
|
90
|
-
|
|
91
|
-
fallbackProps,
|
|
147
|
+
classNames,
|
|
92
148
|
...props
|
|
93
149
|
}: AvatarProps) {
|
|
94
150
|
return (
|
|
95
|
-
<AvatarRoot
|
|
96
|
-
<AvatarImage src={src} alt={alt} {
|
|
97
|
-
<AvatarFallback delay={delay} {
|
|
151
|
+
<AvatarRoot size={size} className={className} {...props}>
|
|
152
|
+
<AvatarImage src={src} alt={alt} className={classNames?.image} />
|
|
153
|
+
<AvatarFallback delay={delay} className={classNames?.fallback}>
|
|
98
154
|
{fallback ?? extractInitials(alt)}
|
|
99
155
|
</AvatarFallback>
|
|
156
|
+
{badge ? (
|
|
157
|
+
<AvatarBadge className={classNames?.badge}>{badge}</AvatarBadge>
|
|
158
|
+
) : null}
|
|
100
159
|
</AvatarRoot>
|
|
101
160
|
);
|
|
102
161
|
}
|