@dust-tt/sparkle 0.6.0 → 0.7.1

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.
Files changed (70) hide show
  1. package/dist/cjs/index.js +11 -10
  2. package/dist/cjs/index.js.map +4 -4
  3. package/dist/esm/components/Citation.d.ts +7 -1
  4. package/dist/esm/components/Citation.d.ts.map +1 -1
  5. package/dist/esm/components/Citation.js +24 -13
  6. package/dist/esm/components/Citation.js.map +1 -1
  7. package/dist/esm/components/ConversationListItem.d.ts +38 -0
  8. package/dist/esm/components/ConversationListItem.d.ts.map +1 -0
  9. package/dist/esm/components/ConversationListItem.js +37 -0
  10. package/dist/esm/components/ConversationListItem.js.map +1 -0
  11. package/dist/esm/components/ConversationMessage.js +1 -1
  12. package/dist/esm/components/ImagePreview.d.ts +27 -0
  13. package/dist/esm/components/ImagePreview.d.ts.map +1 -0
  14. package/dist/esm/components/ImagePreview.js +87 -0
  15. package/dist/esm/components/ImagePreview.js.map +1 -0
  16. package/dist/esm/components/ImageZoomDialog.d.ts +26 -0
  17. package/dist/esm/components/ImageZoomDialog.d.ts.map +1 -0
  18. package/dist/esm/components/ImageZoomDialog.js +46 -0
  19. package/dist/esm/components/ImageZoomDialog.js.map +1 -0
  20. package/dist/esm/components/InteractiveImageGrid.d.ts.map +1 -1
  21. package/dist/esm/components/InteractiveImageGrid.js +25 -67
  22. package/dist/esm/components/InteractiveImageGrid.js.map +1 -1
  23. package/dist/esm/components/ListItem.d.ts +4 -1
  24. package/dist/esm/components/ListItem.d.ts.map +1 -1
  25. package/dist/esm/components/ListItem.js +42 -2
  26. package/dist/esm/components/ListItem.js.map +1 -1
  27. package/dist/esm/components/NavigationList.d.ts.map +1 -1
  28. package/dist/esm/components/NavigationList.js +22 -14
  29. package/dist/esm/components/NavigationList.js.map +1 -1
  30. package/dist/esm/components/Sheet.js +1 -1
  31. package/dist/esm/components/Sheet.js.map +1 -1
  32. package/dist/esm/components/SidebarLayout.d.ts +1 -0
  33. package/dist/esm/components/SidebarLayout.d.ts.map +1 -1
  34. package/dist/esm/components/SidebarLayout.js +18 -36
  35. package/dist/esm/components/SidebarLayout.js.map +1 -1
  36. package/dist/esm/components/index.d.ts +6 -0
  37. package/dist/esm/components/index.d.ts.map +1 -1
  38. package/dist/esm/components/index.js +3 -0
  39. package/dist/esm/components/index.js.map +1 -1
  40. package/dist/esm/lottie/collapseBar.d.ts +21 -21
  41. package/dist/esm/lottie/spinnerColorLG.d.ts +125 -125
  42. package/dist/esm/lottie/spinnerColorXS.d.ts +11 -11
  43. package/dist/esm/lottie/spinnerDarkXS.d.ts +38 -38
  44. package/dist/esm/lottie/spinnerLight.d.ts +66 -66
  45. package/dist/esm/stories/Citation.stories.d.ts.map +1 -1
  46. package/dist/esm/stories/Citation.stories.js +7 -15
  47. package/dist/esm/stories/Citation.stories.js.map +1 -1
  48. package/dist/esm/stories/ConversationListItem.stories.d.ts +12 -0
  49. package/dist/esm/stories/ConversationListItem.stories.d.ts.map +1 -0
  50. package/dist/esm/stories/ConversationListItem.stories.js +60 -0
  51. package/dist/esm/stories/ConversationListItem.stories.js.map +1 -0
  52. package/dist/esm/stories/ImagePreview.stories.d.ts +47 -0
  53. package/dist/esm/stories/ImagePreview.stories.d.ts.map +1 -0
  54. package/dist/esm/stories/ImagePreview.stories.js +107 -0
  55. package/dist/esm/stories/ImagePreview.stories.js.map +1 -0
  56. package/dist/esm/styles/allotment.css +14 -36
  57. package/dist/sparkle.css +53 -44
  58. package/package.json +1 -1
  59. package/src/components/Citation.tsx +52 -41
  60. package/src/components/ConversationListItem.tsx +125 -0
  61. package/src/components/ConversationMessage.tsx +1 -1
  62. package/src/components/ImagePreview.tsx +228 -0
  63. package/src/components/ImageZoomDialog.tsx +151 -0
  64. package/src/components/InteractiveImageGrid.tsx +60 -267
  65. package/src/components/ListItem.tsx +73 -5
  66. package/src/components/NavigationList.tsx +38 -27
  67. package/src/components/Sheet.tsx +1 -1
  68. package/src/components/SidebarLayout.tsx +23 -43
  69. package/src/components/index.ts +17 -0
  70. package/src/styles/allotment.css +14 -36
@@ -1,140 +1,9 @@
1
1
  import React from "react";
2
2
 
3
- import {
4
- Button,
5
- Dialog,
6
- DialogClose,
7
- DialogContent,
8
- DialogTrigger,
9
- Spinner,
10
- } from "@sparkle/components/";
11
- import {
12
- ArrowDownOnSquareIcon,
13
- ChevronLeftIcon,
14
- ChevronRightIcon,
15
- XMarkIcon,
16
- } from "@sparkle/icons/app";
3
+ import { ImagePreview } from "@sparkle/components/ImagePreview";
4
+ import { ImageZoomDialog } from "@sparkle/components/ImageZoomDialog";
17
5
  import { cn } from "@sparkle/lib/utils";
18
6
 
19
- interface ImageLoadingStateProps {
20
- className?: string;
21
- size?: "sm" | "md" | "lg";
22
- }
23
-
24
- const ImageLoadingState = React.forwardRef<
25
- HTMLDivElement,
26
- ImageLoadingStateProps
27
- >(({ className, size = "lg" }, ref) => {
28
- return (
29
- <div
30
- ref={ref}
31
- className={cn(
32
- "s-mx-auto s-flex s-aspect-square s-w-full s-min-w-[50vh]",
33
- "s-max-w-[80vh] s-items-center s-justify-center",
34
- "s-bg-muted-background dark:s-bg-muted-background-night",
35
- className
36
- )}
37
- >
38
- <Spinner variant="dark" size={size} />
39
- </div>
40
- );
41
- });
42
-
43
- ImageLoadingState.displayName = "ImageLoadingState";
44
- interface ImagePreviewProps {
45
- image: {
46
- alt: string;
47
- downloadUrl?: string;
48
- imageUrl?: string;
49
- isLoading?: boolean;
50
- title: string;
51
- };
52
- onClick: () => void;
53
- onDownload: (e: React.MouseEvent) => Promise<void>;
54
- onClose?: (e: React.MouseEvent) => void;
55
- }
56
-
57
- const ImagePreview = React.forwardRef<HTMLDivElement, ImagePreviewProps>(
58
- ({ image, onClick, onDownload, onClose }, ref) => {
59
- return (
60
- <div
61
- ref={ref}
62
- onClick={onClick}
63
- className={cn(
64
- "s-group/preview s-relative s-aspect-square",
65
- "s-cursor-pointer s-overflow-hidden s-rounded-2xl",
66
- "s-bg-muted-background dark:s-bg-muted-background-night"
67
- )}
68
- >
69
- {image.isLoading ? (
70
- <div className="s-flex s-h-full s-w-full s-items-center s-justify-center">
71
- <Spinner variant="dark" size="md" />
72
- </div>
73
- ) : (
74
- <>
75
- <img
76
- src={image.imageUrl}
77
- alt={image.alt}
78
- className="s-h-full s-w-full s-rounded-2xl s-object-cover"
79
- />
80
- {/* Blur overlay with filename - hidden by default, shown on hover */}
81
- <div
82
- className={cn(
83
- "s-absolute s-inset-0 s-z-10",
84
- "s-flex s-items-center s-justify-center",
85
- "s-bg-primary-100/80 dark:s-bg-primary-100-night/80",
86
- "s-backdrop-blur-sm",
87
- "s-opacity-0 s-transition s-duration-200",
88
- "group-hover/preview:s-opacity-100"
89
- )}
90
- >
91
- <span
92
- className={cn(
93
- "s-max-w-[90%] s-truncate s-px-2 s-text-center",
94
- "s-text-sm s-font-medium",
95
- "s-text-foreground dark:s-text-foreground-night"
96
- )}
97
- >
98
- {image.title}
99
- </span>
100
- </div>
101
- <div
102
- className={cn(
103
- "s-absolute s-right-1 s-top-1 s-z-10 s-flex",
104
- "s-opacity-0 s-transition-opacity s-duration-200",
105
- "group-hover/preview:s-opacity-100"
106
- )}
107
- >
108
- {onClose && (
109
- <Button
110
- variant="ghost"
111
- size="xs"
112
- icon={XMarkIcon}
113
- className="s-text-white dark:s-text-white"
114
- tooltip="Remove"
115
- onClick={onClose}
116
- />
117
- )}
118
- {!onClose && (
119
- <Button
120
- variant="ghost"
121
- size="xs"
122
- icon={ArrowDownOnSquareIcon}
123
- className="s-text-white dark:s-text-white"
124
- tooltip="Download"
125
- onClick={onDownload}
126
- />
127
- )}
128
- </div>
129
- </>
130
- )}
131
- </div>
132
- );
133
- }
134
- );
135
-
136
- ImagePreview.displayName = "ImagePreview";
137
-
138
7
  const SIZE_CLASSES = {
139
8
  sm: "s-h-24 s-w-24",
140
9
  md: "s-h-48 s-w-48",
@@ -165,7 +34,6 @@ function InteractiveImageGrid({
165
34
  const [currentImageIndex, setCurrentImageIndex] = React.useState<
166
35
  number | null
167
36
  >(null);
168
- const [imageLoaded, setImageLoaded] = React.useState(false);
169
37
 
170
38
  const handleNext = React.useCallback(() => {
171
39
  if (currentImageIndex === null) {
@@ -183,29 +51,12 @@ function InteractiveImageGrid({
183
51
  );
184
52
  }, [currentImageIndex, images.length]);
185
53
 
186
- const handleDownload = React.useCallback(
187
- async (downloadUrl?: string, title?: string) => {
188
- if (!downloadUrl || !title) {
189
- return;
190
- }
191
-
192
- // Create a hidden link and click it.
193
- const link = document.createElement("a");
194
- link.href = downloadUrl;
195
- link.download = title;
196
- document.body.appendChild(link);
197
- link.click();
198
- document.body.removeChild(link);
199
- },
200
- []
201
- );
202
-
54
+ // Keyboard navigation for the zoomed image
203
55
  React.useEffect(() => {
204
56
  if (currentImageIndex === null) {
205
57
  return;
206
58
  }
207
59
 
208
- // Only handle keyboard events if the image is zoomed.
209
60
  const handleKeyDown = (e: KeyboardEvent) => {
210
61
  if (e.key === "ArrowRight") {
211
62
  handleNext();
@@ -218,126 +69,68 @@ function InteractiveImageGrid({
218
69
  return () => window.removeEventListener("keydown", handleKeyDown);
219
70
  }, [currentImageIndex, handleNext, handlePrevious]);
220
71
 
72
+ const currentImage =
73
+ currentImageIndex !== null ? images[currentImageIndex] : null;
74
+
221
75
  return (
222
- <Dialog
223
- open={currentImageIndex !== null}
224
- onOpenChange={(open) => !open && setCurrentImageIndex(null)}
225
- >
226
- <DialogTrigger asChild>
227
- <div className={cn("s-@container", className)}>
228
- {images.length === 1 ? (
229
- <div className={SIZE_CLASSES[size]}>
76
+ <>
77
+ <div className={cn("s-@container", className)}>
78
+ {images.length === 1 ? (
79
+ <div className={SIZE_CLASSES[size]}>
80
+ <ImagePreview
81
+ imgSrc={images[0].imageUrl ?? ""}
82
+ alt={images[0].alt}
83
+ title={images[0].title}
84
+ downloadUrl={images[0].downloadUrl}
85
+ isLoading={images[0].isLoading}
86
+ onClick={() => setCurrentImageIndex(0)}
87
+ onClose={onClose ? () => onClose() : undefined}
88
+ variant="square"
89
+ titlePosition="center"
90
+ manageZoomDialog={false}
91
+ />
92
+ </div>
93
+ ) : (
94
+ <div className="s-grid s-grid-cols-2 s-gap-2 @xxs:s-grid-cols-3 @xs:s-grid-cols-4">
95
+ {images.map((image, idx) => (
230
96
  <ImagePreview
231
- image={images[0]}
232
- onClick={() => {
233
- setCurrentImageIndex(0);
234
- }}
235
- onDownload={async (e) => {
236
- e.stopPropagation();
237
- await handleDownload(images[0].downloadUrl, images[0].title);
238
- }}
239
- onClose={
240
- onClose
241
- ? (e) => {
242
- e.stopPropagation();
243
- onClose();
244
- }
245
- : undefined
246
- }
247
- />
248
- </div>
249
- ) : (
250
- <div className="s-grid s-grid-cols-2 s-gap-2 @xxs:s-grid-cols-3 @xs:s-grid-cols-4">
251
- {images.map((image, idx) => (
252
- <ImagePreview
253
- key={idx}
254
- image={image}
255
- onClick={() => {
256
- setCurrentImageIndex(idx);
257
- }}
258
- onDownload={async (e) => {
259
- e.stopPropagation();
260
- await handleDownload(image.downloadUrl, image.title);
261
- }}
262
- />
263
- ))}
264
- </div>
265
- )}
266
- </div>
267
- </DialogTrigger>
268
- <DialogContent size="xl" className="s-max-w-[90vw] s-overflow-hidden s-p-3">
269
- {currentImageIndex !== null && (
270
- <div className="s-relative s-flex s-items-center s-justify-center s-gap-2">
271
- {/* Previous button */}
272
- {images.length > 1 && (
273
- <Button
274
- variant="ghost"
275
- size="sm"
276
- icon={ChevronLeftIcon}
277
- onClick={(e) => {
278
- e.stopPropagation();
279
- handlePrevious();
280
- }}
97
+ key={idx}
98
+ imgSrc={image.imageUrl ?? ""}
99
+ alt={image.alt}
100
+ title={image.title}
101
+ downloadUrl={image.downloadUrl}
102
+ isLoading={image.isLoading}
103
+ onClick={() => setCurrentImageIndex(idx)}
104
+ variant="square"
105
+ titlePosition="center"
106
+ manageZoomDialog={false}
281
107
  />
282
- )}
283
-
284
- {/* Image container with overlay buttons */}
285
- <div className="s-relative">
286
- {images[currentImageIndex].isLoading ? (
287
- <ImageLoadingState size="lg" />
288
- ) : (
289
- <>
290
- <img
291
- src={images[currentImageIndex].imageUrl}
292
- alt={images[currentImageIndex].alt}
293
- className="s-max-h-full s-max-w-full s-rounded-lg s-object-contain"
294
- onLoad={() => setImageLoaded(true)}
295
- />
296
- {/* Close button - top right of image */}
297
- <DialogClose asChild>
298
- <Button
299
- variant="outline"
300
- size="xs"
301
- icon={XMarkIcon}
302
- className="s-absolute s-right-2 s-top-2"
303
- />
304
- </DialogClose>
305
- {/* Download button - bottom right of image */}
306
- {imageLoaded && (
307
- <Button
308
- variant="outline"
309
- size="xs"
310
- icon={ArrowDownOnSquareIcon}
311
- tooltip="Download"
312
- className="s-absolute s-bottom-2 s-right-2"
313
- onClick={async () => {
314
- await handleDownload(
315
- images[currentImageIndex].downloadUrl,
316
- images[currentImageIndex].title
317
- );
318
- }}
319
- />
320
- )}
321
- </>
322
- )}
323
- </div>
324
-
325
- {/* Next button */}
326
- {images.length > 1 && (
327
- <Button
328
- variant="ghost"
329
- size="sm"
330
- icon={ChevronRightIcon}
331
- onClick={(e) => {
332
- e.stopPropagation();
333
- handleNext();
334
- }}
335
- />
336
- )}
108
+ ))}
337
109
  </div>
338
110
  )}
339
- </DialogContent>
340
- </Dialog>
111
+ </div>
112
+ <ImageZoomDialog
113
+ open={currentImageIndex !== null}
114
+ onOpenChange={(open) => !open && setCurrentImageIndex(null)}
115
+ image={{
116
+ src: currentImage?.imageUrl ?? "",
117
+ alt: currentImage?.alt,
118
+ title: currentImage?.title,
119
+ downloadUrl: currentImage?.downloadUrl,
120
+ isLoading: currentImage?.isLoading,
121
+ }}
122
+ navigation={
123
+ images.length > 1
124
+ ? {
125
+ onPrevious: handlePrevious,
126
+ onNext: handleNext,
127
+ hasPrevious: true,
128
+ hasNext: true,
129
+ }
130
+ : undefined
131
+ }
132
+ />
133
+ </>
341
134
  );
342
135
  }
343
136
 
@@ -96,17 +96,85 @@ export function ListGroup({ children, className }: ListGroupProps) {
96
96
  type ListItemSectionProps = {
97
97
  children: ReactNode;
98
98
  className?: string;
99
+ size?: "xs" | "sm";
100
+ action?: ReactNode;
101
+ onClick?: () => void;
99
102
  };
100
103
 
101
- export function ListItemSection({ children, className }: ListItemSectionProps) {
104
+ const listItemSectionVariants = cva("", {
105
+ variants: {
106
+ size: {
107
+ xs: "s-heading-xs s-uppercase s-pb-2 s-pt-4 s-text-muted-foreground dark:s-text-muted-foreground-night",
108
+ sm: "s-heading-sm s-bg-muted-background s-p-2 dark:s-bg-muted-background-night/50 s-text-foreground dark:s-text-foreground-night",
109
+ },
110
+ interactive: {
111
+ true: cn(
112
+ "s-cursor-pointer s-transition s-duration-200",
113
+ "active:s-bg-primary-100 dark:active:s-bg-primary-100-night"
114
+ ),
115
+ false: "",
116
+ },
117
+ isHovered: {
118
+ true: "hover:s-bg-primary-100 hover:dark:s-bg-primary-100-night active:s-bg-primary-150 active:dark:s-bg-primary-150-night",
119
+ false: "",
120
+ },
121
+ },
122
+ defaultVariants: {
123
+ size: "xs",
124
+ interactive: false,
125
+ isHovered: false,
126
+ },
127
+ });
128
+
129
+ export function ListItemSection({
130
+ children,
131
+ className,
132
+ size = "xs",
133
+ action,
134
+ onClick,
135
+ }: ListItemSectionProps) {
136
+ const [isHoveringAction, setIsHoveringAction] = React.useState(false);
137
+ const [isHoveringMain, setIsHoveringMain] = React.useState(false);
138
+
102
139
  return (
103
- <h3
140
+ <div
104
141
  className={cn(
105
- "s-pb-2 s-pt-6 s-text-xs s-font-semibold s-uppercase s-tracking-wide s-text-muted-foreground dark:s-text-muted-foreground-night",
142
+ listItemSectionVariants({
143
+ size,
144
+ interactive: !!onClick,
145
+ isHovered: !!onClick && isHoveringMain && !isHoveringAction,
146
+ }),
147
+ "s-group/section-item s-flex s-items-center s-justify-between",
106
148
  className
107
149
  )}
150
+ onClick={onClick}
151
+ onMouseEnter={() => {
152
+ setIsHoveringMain(true);
153
+ }}
154
+ onMouseLeave={() => {
155
+ setIsHoveringMain(false);
156
+ setIsHoveringAction(false);
157
+ }}
108
158
  >
109
- {children}
110
- </h3>
159
+ <div className="s-flex s-items-center s-gap-1 s-overflow-hidden s-text-ellipsis">
160
+ {children}
161
+ </div>
162
+ {action && (
163
+ <div
164
+ className="s-flex s-gap-1"
165
+ onClick={(e) => {
166
+ e.stopPropagation();
167
+ }}
168
+ onMouseEnter={() => {
169
+ setIsHoveringAction(true);
170
+ }}
171
+ onMouseLeave={() => {
172
+ setIsHoveringAction(false);
173
+ }}
174
+ >
175
+ {action}
176
+ </div>
177
+ )}
178
+ </div>
111
179
  );
112
180
  }
@@ -130,6 +130,7 @@ const NavigationListItem = React.forwardRef<
130
130
 
131
131
  const shouldShowStatusDot = status !== "idle";
132
132
  const counterValue = count && count > 0 ? count : undefined;
133
+ const shouldHideStatusIndicators = Boolean(moreMenu && selected);
133
134
 
134
135
  return (
135
136
  <div
@@ -166,26 +167,28 @@ const NavigationListItem = React.forwardRef<
166
167
  {icon && <Icon visual={icon} size="xs" className="s-m-0.5" />}
167
168
  {avatar}
168
169
  {label && (
169
- <span className="s-grow s-overflow-hidden s-text-ellipsis s-whitespace-nowrap group-hover/menu-item:s-pr-8 group-data-[selected=true]/menu-item:s-pr-8">
170
+ <span className="s-grow s-overflow-hidden s-text-ellipsis s-whitespace-nowrap group-hover/menu-item:s-pr-8 group-focus-within/menu-item:s-pr-8 group-data-[selected=true]/menu-item:s-pr-8">
170
171
  {label}
171
172
  </span>
172
173
  )}
173
- {counterValue && (
174
+ {counterValue !== undefined && !shouldHideStatusIndicators && (
174
175
  <Counter
175
176
  value={counterValue}
176
177
  size="xs"
177
178
  variant="outline"
178
179
  className={cn(
179
180
  "s-flex-shrink-0 s-translate-x-0.5",
180
- moreMenu && "group-hover/menu-item:s-hidden"
181
+ moreMenu &&
182
+ "group-hover/menu-item:s-hidden group-focus-within/menu-item:s-hidden"
181
183
  )}
182
184
  />
183
185
  )}
184
- {shouldShowStatusDot && (
186
+ {shouldShowStatusDot && !shouldHideStatusIndicators && (
185
187
  <div
186
188
  className={cn(
187
189
  "s-heading-xs s-flex s-flex-shrink-0 s-items-center s-justify-center s-rounded-full",
188
- moreMenu && "group-hover/menu-item:s-hidden",
190
+ moreMenu &&
191
+ "group-hover/menu-item:s-hidden group-focus-within/menu-item:s-hidden",
189
192
  getStatusDotColor()
190
193
  )}
191
194
  />
@@ -213,7 +216,7 @@ const NavigationListItemAction = React.forwardRef<
213
216
  data-sidebar="menu-action"
214
217
  className={cn(
215
218
  "s-absolute s-right-2 s-top-1.5 s-opacity-0 s-transition-opacity",
216
- "s-opacity-0 group-focus-within/menu-item:s-opacity-100 group-hover/menu-item:s-opacity-100 group-data-[selected=true]/menu-item:s-opacity-100",
219
+ "s-opacity-0 group-focus-within/menu-item:s-opacity-100 group-hover/menu-item:s-opacity-100",
217
220
  className
218
221
  )}
219
222
  {...props}
@@ -384,32 +387,34 @@ const NavigationListCollapsibleSection = React.forwardRef<
384
387
  ) => {
385
388
  const isCollapsible = type !== "static";
386
389
  const labelElement = (
387
- <div className="s-group/menu-item s-relative s-mt-2 s-flex s-flex-1 s-items-center s-justify-start s-gap-1">
388
- <div className={collapseableStyles({ variant, isCollapsible })}>
389
- {label}
390
- </div>
391
- {action && (
392
- <div
393
- className={cn(
394
- "s-m-1.5 s-flex s-gap-1 s-pr-0.5 s-transition-opacity",
395
- actionOnHover
396
- ? "s-opacity-0 hover:s-opacity-100 group-focus-within/menu-item:s-opacity-100 group-hover/menu-item:s-opacity-100"
397
- : "s-opacity-100"
398
- )}
399
- onClick={(e) => {
400
- e.stopPropagation();
401
- }}
402
- >
403
- {action}
404
- </div>
390
+ <div className={collapseableStyles({ variant, isCollapsible })}>
391
+ {label}
392
+ </div>
393
+ );
394
+
395
+ const actionElement = action && (
396
+ <div
397
+ className={cn(
398
+ "s-m-1.5 s-flex s-gap-1 s-pr-0.5 s-transition-opacity",
399
+ actionOnHover
400
+ ? "s-opacity-0 hover:s-opacity-100 group-focus-within/menu-item:s-opacity-100 group-hover/menu-item:s-opacity-100"
401
+ : "s-opacity-100"
405
402
  )}
403
+ onClick={(e) => {
404
+ e.stopPropagation();
405
+ }}
406
+ >
407
+ {action}
406
408
  </div>
407
409
  );
408
410
 
409
411
  if (type === "static") {
410
412
  return (
411
413
  <div ref={ref} className={className} {...props}>
412
- {labelElement}
414
+ <div className="s-group/menu-item s-relative s-mt-2 s-flex s-flex-1 s-items-center s-justify-start s-gap-1">
415
+ {labelElement}
416
+ {actionElement}
417
+ </div>
413
418
  <div className="s-flex s-flex-col s-gap-0.5">{children}</div>
414
419
  </div>
415
420
  );
@@ -425,7 +430,10 @@ const NavigationListCollapsibleSection = React.forwardRef<
425
430
  if (type === "collapseAndScroll") {
426
431
  return (
427
432
  <Collapsible ref={ref} className={className} {...collapsibleProps}>
428
- <CollapsibleTrigger hideChevron>{labelElement}</CollapsibleTrigger>
433
+ <div className="s-group/menu-item s-relative s-mt-2 s-flex s-flex-1 s-items-center s-justify-start s-gap-1">
434
+ <CollapsibleTrigger hideChevron>{labelElement}</CollapsibleTrigger>
435
+ {actionElement}
436
+ </div>
429
437
  <CollapsibleContent>
430
438
  <ScrollArea>
431
439
  <div className="s-flex s-flex-col s-gap-0.5">{children}</div>
@@ -439,7 +447,10 @@ const NavigationListCollapsibleSection = React.forwardRef<
439
447
  // type === "collapse" (default collapsible behavior)
440
448
  return (
441
449
  <Collapsible ref={ref} className={className} {...collapsibleProps}>
442
- <CollapsibleTrigger hideChevron>{labelElement}</CollapsibleTrigger>
450
+ <div className="s-group/menu-item s-relative s-mt-2 s-flex s-flex-1 s-items-center s-justify-start s-gap-1">
451
+ <CollapsibleTrigger hideChevron>{labelElement}</CollapsibleTrigger>
452
+ {actionElement}
453
+ </div>
443
454
  <CollapsibleContent>
444
455
  <div className="s-flex s-flex-col s-gap-0.5">{children}</div>
445
456
  </CollapsibleContent>
@@ -191,7 +191,7 @@ const SheetHeader = ({
191
191
  {...props}
192
192
  >
193
193
  {children}
194
- <SheetClose asChild className="s-absolute s-right-3">
194
+ <SheetClose asChild className="s-absolute s-right-3 s-top-4">
195
195
  {!hideButton && <Button icon={XMarkIcon} variant="ghost" size="sm" />}
196
196
  </SheetClose>
197
197
  </div>