@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.
- package/dist/cjs/index.js +11 -10
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/components/Citation.d.ts +7 -1
- package/dist/esm/components/Citation.d.ts.map +1 -1
- package/dist/esm/components/Citation.js +24 -13
- package/dist/esm/components/Citation.js.map +1 -1
- package/dist/esm/components/ConversationListItem.d.ts +38 -0
- package/dist/esm/components/ConversationListItem.d.ts.map +1 -0
- package/dist/esm/components/ConversationListItem.js +37 -0
- package/dist/esm/components/ConversationListItem.js.map +1 -0
- package/dist/esm/components/ConversationMessage.js +1 -1
- package/dist/esm/components/ImagePreview.d.ts +27 -0
- package/dist/esm/components/ImagePreview.d.ts.map +1 -0
- package/dist/esm/components/ImagePreview.js +87 -0
- package/dist/esm/components/ImagePreview.js.map +1 -0
- package/dist/esm/components/ImageZoomDialog.d.ts +26 -0
- package/dist/esm/components/ImageZoomDialog.d.ts.map +1 -0
- package/dist/esm/components/ImageZoomDialog.js +46 -0
- package/dist/esm/components/ImageZoomDialog.js.map +1 -0
- package/dist/esm/components/InteractiveImageGrid.d.ts.map +1 -1
- package/dist/esm/components/InteractiveImageGrid.js +25 -67
- package/dist/esm/components/InteractiveImageGrid.js.map +1 -1
- package/dist/esm/components/ListItem.d.ts +4 -1
- package/dist/esm/components/ListItem.d.ts.map +1 -1
- package/dist/esm/components/ListItem.js +42 -2
- package/dist/esm/components/ListItem.js.map +1 -1
- package/dist/esm/components/NavigationList.d.ts.map +1 -1
- package/dist/esm/components/NavigationList.js +22 -14
- package/dist/esm/components/NavigationList.js.map +1 -1
- package/dist/esm/components/Sheet.js +1 -1
- package/dist/esm/components/Sheet.js.map +1 -1
- package/dist/esm/components/SidebarLayout.d.ts +1 -0
- package/dist/esm/components/SidebarLayout.d.ts.map +1 -1
- package/dist/esm/components/SidebarLayout.js +18 -36
- package/dist/esm/components/SidebarLayout.js.map +1 -1
- package/dist/esm/components/index.d.ts +6 -0
- package/dist/esm/components/index.d.ts.map +1 -1
- package/dist/esm/components/index.js +3 -0
- package/dist/esm/components/index.js.map +1 -1
- package/dist/esm/lottie/collapseBar.d.ts +21 -21
- package/dist/esm/lottie/spinnerColorLG.d.ts +125 -125
- package/dist/esm/lottie/spinnerColorXS.d.ts +11 -11
- package/dist/esm/lottie/spinnerDarkXS.d.ts +38 -38
- package/dist/esm/lottie/spinnerLight.d.ts +66 -66
- package/dist/esm/stories/Citation.stories.d.ts.map +1 -1
- package/dist/esm/stories/Citation.stories.js +7 -15
- package/dist/esm/stories/Citation.stories.js.map +1 -1
- package/dist/esm/stories/ConversationListItem.stories.d.ts +12 -0
- package/dist/esm/stories/ConversationListItem.stories.d.ts.map +1 -0
- package/dist/esm/stories/ConversationListItem.stories.js +60 -0
- package/dist/esm/stories/ConversationListItem.stories.js.map +1 -0
- package/dist/esm/stories/ImagePreview.stories.d.ts +47 -0
- package/dist/esm/stories/ImagePreview.stories.d.ts.map +1 -0
- package/dist/esm/stories/ImagePreview.stories.js +107 -0
- package/dist/esm/stories/ImagePreview.stories.js.map +1 -0
- package/dist/esm/styles/allotment.css +14 -36
- package/dist/sparkle.css +53 -44
- package/package.json +1 -1
- package/src/components/Citation.tsx +52 -41
- package/src/components/ConversationListItem.tsx +125 -0
- package/src/components/ConversationMessage.tsx +1 -1
- package/src/components/ImagePreview.tsx +228 -0
- package/src/components/ImageZoomDialog.tsx +151 -0
- package/src/components/InteractiveImageGrid.tsx +60 -267
- package/src/components/ListItem.tsx +73 -5
- package/src/components/NavigationList.tsx +38 -27
- package/src/components/Sheet.tsx +1 -1
- package/src/components/SidebarLayout.tsx +23 -43
- package/src/components/index.ts +17 -0
- package/src/styles/allotment.css +14 -36
|
@@ -1,140 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
</
|
|
340
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
140
|
+
<div
|
|
104
141
|
className={cn(
|
|
105
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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 &&
|
|
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 &&
|
|
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
|
|
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=
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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>
|
package/src/components/Sheet.tsx
CHANGED
|
@@ -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>
|