@dust-tt/sparkle 0.2.499 → 0.2.501

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.
@@ -1,6 +1,7 @@
1
1
  import { cva } from "class-variance-authority";
2
2
  import React, { useState } from "react";
3
3
 
4
+ import { Tooltip } from "@sparkle/components";
4
5
  import { UserIcon } from "@sparkle/icons/app";
5
6
  import { getEmojiAndBackgroundFromUrl } from "@sparkle/lib/avatar/utils";
6
7
  import { cn } from "@sparkle/lib/utils";
@@ -240,8 +241,8 @@ const AVATAR_STACK_SIZES = ["xs", "sm", "md"] as const;
240
241
  type AvatarStackSizeType = (typeof AVATAR_STACK_SIZES)[number];
241
242
 
242
243
  interface AvatarStackProps {
243
- children: React.ReactElement<AvatarProps> | React.ReactElement<AvatarProps>[];
244
- nbMoreItems?: number;
244
+ avatars: AvatarProps[];
245
+ nbVisibleItems?: number;
245
246
  size?: AvatarStackSizeType;
246
247
  isRounded?: boolean;
247
248
  hasMagnifier?: boolean;
@@ -254,14 +255,28 @@ const sizeClassesPx: Record<AvatarStackSizeType, number> = {
254
255
  };
255
256
 
256
257
  Avatar.Stack = function ({
257
- children,
258
- nbMoreItems,
258
+ avatars,
259
+ nbVisibleItems,
259
260
  size = "sm",
260
261
  isRounded = false,
261
262
  hasMagnifier = true,
262
263
  }: AvatarStackProps) {
263
264
  const [isHovered, setIsHovered] = useState(false);
264
- const childrenArray = React.Children.toArray(children);
265
+
266
+ // Get visible avatars and calculate remaining count
267
+ const shouldShowAll = !nbVisibleItems || avatars.length <= nbVisibleItems;
268
+ const visibleAvatars = shouldShowAll
269
+ ? avatars
270
+ : avatars.slice(0, nbVisibleItems - 1);
271
+ const remainingCount = shouldShowAll
272
+ ? 0
273
+ : avatars.length - (nbVisibleItems - 1);
274
+
275
+ // Get all names for tooltip
276
+ const avatarNames = avatars
277
+ .filter((avatar) => avatar.name)
278
+ .map((avatar) => avatar.name);
279
+ const tooltipLabel = avatarNames.join(", ");
265
280
 
266
281
  const sizeSetting = {
267
282
  marginLeft: 0,
@@ -270,77 +285,88 @@ Avatar.Stack = function ({
270
285
  };
271
286
 
272
287
  const collapsedWidth =
273
- sizeSetting.width * (childrenArray.length + Number(Boolean(nbMoreItems))) +
288
+ sizeSetting.width *
289
+ (visibleAvatars.length + Number(Boolean(remainingCount))) +
274
290
  (sizeClassesPx[size] - sizeSetting.width);
275
291
 
276
292
  const openedWidth =
277
293
  sizeSetting.widthHovered *
278
- (childrenArray.length + Number(Boolean(nbMoreItems))) +
294
+ (visibleAvatars.length + Number(Boolean(remainingCount))) +
279
295
  (sizeClassesPx[size] - sizeSetting.widthHovered);
280
296
 
281
297
  const transitionSettings = "width 200ms ease-out";
282
298
 
283
299
  return (
284
- <div
285
- className="s-flex s-flex-row"
286
- onMouseEnter={() => childrenArray.length > 1 && setIsHovered(true)}
287
- onMouseLeave={() => childrenArray.length > 1 && setIsHovered(false)}
288
- style={{
289
- width: `${isHovered ? openedWidth : collapsedWidth}px`,
290
- transition: transitionSettings,
291
- }}
292
- >
293
- {childrenArray.map((child, i) => {
294
- if (React.isValidElement<AvatarProps>(child)) {
295
- return (
296
- <div
297
- key={i}
298
- className="s-cursor-pointer s-drop-shadow-md"
299
- style={{
300
- width: isHovered ? sizeSetting.widthHovered : sizeSetting.width,
301
- transition: transitionSettings,
302
- }}
303
- >
304
- {hasMagnifier ? (
305
- <div
306
- style={{
307
- transform: `scale(${
308
- 1 - (childrenArray.length - i) * 0.06
309
- })`,
310
- }}
311
- >
312
- {React.cloneElement(child, {
313
- size: size,
314
- isRounded: isRounded,
315
- })}
316
- </div>
317
- ) : (
318
- React.cloneElement(child, {
319
- size: size,
320
- isRounded: isRounded,
321
- })
322
- )}
323
- </div>
324
- );
325
- }
326
- return null;
327
- })}
328
- {Boolean(nbMoreItems) && (
329
- <div
330
- className="s-cursor-pointer s-drop-shadow-md"
331
- style={{
332
- width: isHovered ? sizeSetting.widthHovered : sizeSetting.width,
333
- transition: transitionSettings,
334
- }}
335
- >
336
- <Avatar
337
- size={size}
338
- name={"+" + String(Number(nbMoreItems) < 10 ? nbMoreItems : "")}
339
- isRounded={isRounded}
340
- clickable
341
- />
342
- </div>
343
- )}
344
- </div>
300
+ <Tooltip
301
+ label={tooltipLabel}
302
+ triggerAsChild
303
+ trigger={
304
+ <>
305
+ <div
306
+ className="s-flex s-flex-row"
307
+ onMouseEnter={() => visibleAvatars.length > 1 && setIsHovered(true)}
308
+ onMouseLeave={() =>
309
+ visibleAvatars.length > 1 && setIsHovered(false)
310
+ }
311
+ style={{
312
+ width: `${isHovered ? openedWidth : collapsedWidth}px`,
313
+ transition: transitionSettings,
314
+ }}
315
+ >
316
+ {visibleAvatars.map((avatarProps, i) => (
317
+ <div
318
+ key={i}
319
+ className="s-cursor-pointer s-drop-shadow-md"
320
+ style={{
321
+ width: isHovered
322
+ ? sizeSetting.widthHovered
323
+ : sizeSetting.width,
324
+ transition: transitionSettings,
325
+ }}
326
+ >
327
+ {hasMagnifier ? (
328
+ <div
329
+ style={{
330
+ transform: `scale(${
331
+ 1 - (visibleAvatars.length - i) * 0.06
332
+ })`,
333
+ }}
334
+ >
335
+ <Avatar
336
+ {...avatarProps}
337
+ size={size}
338
+ isRounded={isRounded}
339
+ />
340
+ </div>
341
+ ) : (
342
+ <Avatar {...avatarProps} size={size} isRounded={isRounded} />
343
+ )}
344
+ </div>
345
+ ))}
346
+ {remainingCount > 0 && (
347
+ <div
348
+ className="s-cursor-pointer s-drop-shadow-md"
349
+ style={{
350
+ width: isHovered
351
+ ? sizeSetting.widthHovered
352
+ : sizeSetting.width,
353
+ transition: transitionSettings,
354
+ }}
355
+ >
356
+ <Avatar
357
+ size={size}
358
+ name={
359
+ "+" +
360
+ String(Number(remainingCount) < 10 ? remainingCount : "")
361
+ }
362
+ isRounded={isRounded}
363
+ clickable
364
+ />
365
+ </div>
366
+ )}
367
+ </div>
368
+ </>
369
+ }
370
+ />
345
371
  );
346
372
  };
@@ -45,9 +45,11 @@ TabsList.displayName = TabsPrimitive.List.displayName;
45
45
  const TabsTrigger = React.forwardRef<
46
46
  React.ElementRef<typeof TabsPrimitive.Trigger>,
47
47
  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> &
48
- Pick<
49
- React.ComponentProps<typeof Button>,
50
- "label" | "tooltip" | "icon" | "isCounter" | "counterValue" | "variant"
48
+ Partial<
49
+ Pick<
50
+ React.ComponentProps<typeof Button>,
51
+ "label" | "tooltip" | "icon" | "isCounter" | "counterValue" | "variant"
52
+ >
51
53
  > & {
52
54
  isLoading?: boolean;
53
55
  } & Omit<LinkWrapperProps, "children" | "className">
@@ -177,123 +177,176 @@ export const AvatarExample = () => (
177
177
  export const AvatarStackExample = () => (
178
178
  <div className="s-flex s-flex-col s-gap-6">
179
179
  <div className="s-flex s-flex-row s-gap-2">
180
- <Avatar.Stack size="xs" nbMoreItems={0} isRounded>
181
- <Avatar
182
- name="Isabelle Doe"
183
- visual="https://dust.tt/static/droidavatar/Droid_Lime_3.jpg"
184
- />
185
- <Avatar
186
- name="Rafael Doe"
187
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
188
- />
189
- <Avatar
190
- name="Aria Doe"
191
- visual="https://dust.tt/static/droidavatar/Droid_Red_3.jpg"
192
- />
193
- <Avatar
194
- name="Omar Doe"
195
- visual="https://dust.tt/static/droidavatar/Droid_Pink_3.jpg"
196
- />
197
- </Avatar.Stack>
180
+ <Avatar.Stack
181
+ size="xs"
182
+ nbVisibleItems={3}
183
+ isRounded
184
+ avatars={[
185
+ {
186
+ name: "Isabelle Doe",
187
+ visual: "https://dust.tt/static/droidavatar/Droid_Lime_3.jpg",
188
+ },
189
+ {
190
+ name: "Rafael Doe",
191
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
192
+ },
193
+ {
194
+ name: "Aria Doe",
195
+ visual: "https://dust.tt/static/droidavatar/Droid_Red_3.jpg",
196
+ },
197
+ {
198
+ name: "Omar Doe",
199
+ visual: "https://dust.tt/static/droidavatar/Droid_Pink_3.jpg",
200
+ },
201
+ ]}
202
+ />
203
+
204
+ <Avatar.Stack
205
+ size="xs"
206
+ nbVisibleItems={6}
207
+ avatars={[
208
+ {
209
+ name: "Rafael Doe",
210
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
211
+ },
212
+ { name: "Mason Johnson" },
213
+ {
214
+ name: "Omar Doe",
215
+ visual: "https://dust.tt/static/droidavatar/Droid_Pink_3.jpg",
216
+ },
217
+ { name: "Eleanor Wright" },
218
+ {
219
+ name: "Rafael Doe",
220
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
221
+ },
222
+ { name: "Mason Johnson" },
223
+ {
224
+ name: "Omar Doe",
225
+ visual: "https://dust.tt/static/droidavatar/Droid_Pink_3.jpg",
226
+ },
227
+ { name: "Eleanor Wright" },
228
+ ]}
229
+ />
198
230
 
199
- <Avatar.Stack size="xs" nbMoreItems={8}>
200
- <Avatar
201
- name="Rafael Doe"
202
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
203
- />
204
- <Avatar size="sm" name="Mason Johnson" />
205
- <Avatar
206
- size="sm"
207
- name="Omar Doe"
208
- visual="https://dust.tt/static/droidavatar/Droid_Pink_3.jpg"
209
- />
210
- <Avatar size="sm" name="Eleanor Wright" />
211
- </Avatar.Stack>
212
- <Avatar.Stack size="xs" nbMoreItems={0}>
213
- <Avatar
214
- name="Rafael Doe"
215
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
216
- />
217
- </Avatar.Stack>
231
+ <Avatar.Stack
232
+ size="xs"
233
+ nbVisibleItems={1}
234
+ avatars={[
235
+ {
236
+ name: "Rafael Doe",
237
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
238
+ },
239
+ ]}
240
+ />
218
241
  </div>
242
+
219
243
  <div className="s-flex s-flex-row s-gap-2">
220
- <Avatar.Stack size="sm" nbMoreItems={0} isRounded>
221
- <Avatar
222
- name="Isabelle Doe"
223
- visual="https://dust.tt/static/droidavatar/Droid_Lime_3.jpg"
224
- />
225
- <Avatar
226
- name="Rafael Doe"
227
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
228
- />
229
- <Avatar
230
- name="Aria Doe"
231
- visual="https://dust.tt/static/droidavatar/Droid_Red_3.jpg"
232
- />
233
- <Avatar
234
- name="Omar Doe"
235
- visual="https://dust.tt/static/droidavatar/Droid_Pink_3.jpg"
236
- />
237
- </Avatar.Stack>
244
+ <Avatar.Stack
245
+ size="sm"
246
+ nbVisibleItems={4}
247
+ isRounded
248
+ avatars={[
249
+ {
250
+ name: "Isabelle Doe",
251
+ visual: "https://dust.tt/static/droidavatar/Droid_Lime_3.jpg",
252
+ },
253
+ {
254
+ name: "Rafael Doe",
255
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
256
+ },
257
+ {
258
+ name: "Aria Doe",
259
+ visual: "https://dust.tt/static/droidavatar/Droid_Red_3.jpg",
260
+ },
261
+ {
262
+ name: "Omar Doe",
263
+ visual: "https://dust.tt/static/droidavatar/Droid_Pink_3.jpg",
264
+ },
265
+ ]}
266
+ />
267
+
268
+ <Avatar.Stack
269
+ size="sm"
270
+ nbVisibleItems={3}
271
+ avatars={[
272
+ {
273
+ name: "Rafael Doe",
274
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
275
+ },
276
+ { name: "Mason Johnson" },
277
+ {
278
+ name: "Omar Doe",
279
+ visual: "https://dust.tt/static/droidavatar/Droid_Pink_3.jpg",
280
+ },
281
+ { name: "Eleanor Wright" },
282
+ ]}
283
+ />
238
284
 
239
- <Avatar.Stack size="sm" nbMoreItems={8}>
240
- <Avatar
241
- name="Rafael Doe"
242
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
243
- />
244
- <Avatar size="sm" name="Mason Johnson" />
245
- <Avatar
246
- size="sm"
247
- name="Omar Doe"
248
- visual="https://dust.tt/static/droidavatar/Droid_Pink_3.jpg"
249
- />
250
- <Avatar size="sm" name="Eleanor Wright" />
251
- </Avatar.Stack>
252
- <Avatar.Stack nbMoreItems={0}>
253
- <Avatar
254
- name="Rafael Doe"
255
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
256
- />
257
- </Avatar.Stack>
285
+ <Avatar.Stack
286
+ nbVisibleItems={1}
287
+ avatars={[
288
+ {
289
+ name: "Rafael Doe",
290
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
291
+ },
292
+ ]}
293
+ />
258
294
  </div>
259
- <div className="s-flex s-flex-row s-gap-4">
260
- <Avatar.Stack nbMoreItems={0} size="md">
261
- <Avatar
262
- name="Isabelle Doe"
263
- visual="https://dust.tt/static/droidavatar/Droid_Lime_3.jpg"
264
- />
265
- <Avatar
266
- name="Rafael Doe"
267
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
268
- />
269
- <Avatar
270
- name="Aria Doe"
271
- visual="https://dust.tt/static/droidavatar/Droid_Red_3.jpg"
272
- />
273
- <Avatar
274
- name="Omar Doe"
275
- visual="https://dust.tt/static/droidavatar/Droid_Pink_3.jpg"
276
- />
277
- </Avatar.Stack>
278
295
 
279
- <Avatar.Stack nbMoreItems={8} size="md">
280
- <Avatar
281
- name="Rafael Doe"
282
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
283
- />
284
- <Avatar size="md" name="Mason Johnson" />
285
- <Avatar
286
- name="Omar Doe"
287
- visual="https://dust.tt/static/droidavatar/Droid_Pink_3.jpg"
288
- />
289
- <Avatar size="md" name="Eleanor Wright" />
290
- </Avatar.Stack>
291
- <Avatar.Stack nbMoreItems={0} size="md">
292
- <Avatar
293
- name="Rafael Doe"
294
- visual="https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg"
295
- />
296
- </Avatar.Stack>
296
+ <div className="s-flex s-flex-row s-gap-4">
297
+ <Avatar.Stack
298
+ nbVisibleItems={4}
299
+ size="md"
300
+ avatars={[
301
+ {
302
+ name: "Isabelle Doe",
303
+ visual: "https://dust.tt/static/droidavatar/Droid_Lime_3.jpg",
304
+ },
305
+ {
306
+ name: "Rafael Doe",
307
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
308
+ },
309
+ {
310
+ name: "Aria Doe",
311
+ visual: "https://dust.tt/static/droidavatar/Droid_Red_3.jpg",
312
+ },
313
+ {
314
+ name: "Omar Doe",
315
+ visual: "https://dust.tt/static/droidavatar/Droid_Pink_3.jpg",
316
+ },
317
+ ]}
318
+ />
319
+ <Avatar.Stack
320
+ size="md"
321
+ nbVisibleItems={3}
322
+ avatars={[
323
+ {
324
+ name: "Isabelle Doe",
325
+ visual: "https://dust.tt/static/droidavatar/Droid_Lime_3.jpg",
326
+ },
327
+ {
328
+ name: "Rafael Doe",
329
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
330
+ },
331
+ {
332
+ name: "Aria Doe",
333
+ visual: "https://dust.tt/static/droidavatar/Droid_Red_3.jpg",
334
+ },
335
+ {
336
+ name: "Omar Doe",
337
+ visual: "https://dust.tt/static/droidavatar/Droid_Pink_3.jpg",
338
+ },
339
+ ]}
340
+ />
341
+ <Avatar.Stack
342
+ size="md"
343
+ avatars={[
344
+ {
345
+ name: "Rafael Doe",
346
+ visual: "https://dust.tt/static/droidavatar/Droid_Yellow_3.jpg",
347
+ },
348
+ ]}
349
+ />
297
350
  </div>
298
351
  </div>
299
352
  );