@dust-tt/sparkle 0.7.6 → 0.7.7-rc-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.
@@ -0,0 +1,310 @@
1
+ import { cva } from "class-variance-authority";
2
+ import React from "react";
3
+
4
+ import { Button, type ButtonProps } from "@sparkle/components/Button";
5
+ import {
6
+ Dialog,
7
+ DialogContainer,
8
+ DialogContent,
9
+ DialogDescription,
10
+ DialogFooter,
11
+ DialogHeader,
12
+ DialogTitle,
13
+ } from "@sparkle/components/Dialog";
14
+ import { Input } from "@sparkle/components/Input";
15
+ import { ScrollArea } from "@sparkle/components/ScrollArea";
16
+ import { Separator } from "@sparkle/components/Separator";
17
+ import { LinkMIcon, XMarkIcon } from "@sparkle/icons/app";
18
+ import { cn } from "@sparkle/lib/utils";
19
+
20
+ export type ToolbarVariant = "inline" | "overlay";
21
+
22
+ type ToolbarButtonSize = NonNullable<ButtonProps["size"]>;
23
+
24
+ const toolbarRootVariants = cva("s-inline-flex s-items-center", {
25
+ variants: {
26
+ variant: {
27
+ overlay:
28
+ "s-absolute s-left-0 s-top-0 s-z-10 s-justify-start s-gap-3 s-overflow-hidden s-rounded-xl s-bg-primary-50 s-py-1 s-pl-3 s-duration-700 s-ease-in-out dark:s-bg-muted-background-night",
29
+ inline:
30
+ "s-gap-1 s-border-b s-border-t s-border-border s-bg-background s-p-1 dark:s-border-border-night/50 dark:s-bg-background-night sm:s-rounded-2xl sm:s-border sm:s-border-border/50 sm:s-shadow-md",
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ variant: "inline",
35
+ },
36
+ });
37
+
38
+ const toolbarContentVariants = cva("", {
39
+ variants: {
40
+ variant: {
41
+ overlay:
42
+ "s-flex s-h-full s-w-max s-flex-row s-items-center s-gap-3 s-px-3",
43
+ inline: "s-inline-flex s-items-center s-gap-1",
44
+ },
45
+ scrollable: {
46
+ true: "s-overflow-x-scroll",
47
+ false: "",
48
+ },
49
+ },
50
+ defaultVariants: {
51
+ variant: "inline",
52
+ scrollable: false,
53
+ },
54
+ });
55
+
56
+ const toolbarScrollAreaVariants = cva("s-h-full s-w-full", {
57
+ variants: {
58
+ variant: {
59
+ overlay: "s-border-l s-border-border dark:s-border-border-night/50",
60
+ inline: "",
61
+ },
62
+ },
63
+ defaultVariants: {
64
+ variant: "inline",
65
+ },
66
+ });
67
+
68
+ export interface ToolbarProps {
69
+ variant?: ToolbarVariant;
70
+ children: React.ReactNode;
71
+ className?: string;
72
+ contentClassName?: string;
73
+ scrollAreaClassName?: string;
74
+ scroll?: boolean;
75
+ onClose?: (e: React.MouseEvent<HTMLButtonElement>) => void;
76
+ closeButtonProps?: Omit<ButtonProps, "icon" | "onClick" | "label">;
77
+ startSlot?: React.ReactNode;
78
+ }
79
+
80
+ function Toolbar({
81
+ variant = "inline",
82
+ children,
83
+ className,
84
+ contentClassName,
85
+ scrollAreaClassName,
86
+ scroll,
87
+ onClose,
88
+ closeButtonProps,
89
+ startSlot,
90
+ }: ToolbarProps) {
91
+ const isOverlay = variant === "overlay";
92
+ const isScrollable = scroll ?? isOverlay;
93
+ const {
94
+ size: closeButtonSizeProp,
95
+ variant: closeVariant = "outline",
96
+ ...restCloseButtonProps
97
+ } = closeButtonProps ?? {};
98
+ const closeButtonSize: ToolbarButtonSize = closeButtonSizeProp ?? "mini";
99
+
100
+ const rootClassName = toolbarRootVariants({ variant, className });
101
+ const contentBaseClassName = toolbarContentVariants({
102
+ variant,
103
+ scrollable: isScrollable,
104
+ className: contentClassName,
105
+ });
106
+ const scrollAreaClassNames = toolbarScrollAreaVariants({
107
+ variant,
108
+ className: scrollAreaClassName,
109
+ });
110
+
111
+ function renderCloseButton(): JSX.Element | null {
112
+ if (!onClose) {
113
+ return null;
114
+ }
115
+
116
+ const buttonProps = {
117
+ variant: closeVariant,
118
+ icon: XMarkIcon,
119
+ onClick: onClose,
120
+ ...restCloseButtonProps,
121
+ };
122
+
123
+ if (closeButtonSize === "mini") {
124
+ return <Button size="mini" {...buttonProps} />;
125
+ }
126
+
127
+ return <Button size={closeButtonSize} {...buttonProps} />;
128
+ }
129
+
130
+ const closeButton = renderCloseButton();
131
+
132
+ const leadingContent = startSlot ?? closeButton;
133
+ const content = <div className={contentBaseClassName}>{children}</div>;
134
+
135
+ return (
136
+ <div className={rootClassName}>
137
+ {leadingContent}
138
+ {isScrollable ? (
139
+ <ScrollArea
140
+ orientation="horizontal"
141
+ className={scrollAreaClassNames}
142
+ hideScrollBar
143
+ >
144
+ {content}
145
+ </ScrollArea>
146
+ ) : (
147
+ content
148
+ )}
149
+ </div>
150
+ );
151
+ }
152
+
153
+ export interface ToolbarContentGroup {
154
+ id: string;
155
+ items: readonly React.ReactNode[];
156
+ }
157
+
158
+ export interface ToolbarContentProps {
159
+ groups: readonly ToolbarContentGroup[];
160
+ separatorClassName?: string;
161
+ }
162
+
163
+ function ToolbarContent({ groups, separatorClassName }: ToolbarContentProps) {
164
+ return (
165
+ <>
166
+ {groups.map((group, groupIndex) => (
167
+ <React.Fragment key={group.id}>
168
+ {group.items}
169
+ {groupIndex < groups.length - 1 && (
170
+ <Separator
171
+ orientation="vertical"
172
+ className={cn("s-my-1", separatorClassName)}
173
+ />
174
+ )}
175
+ </React.Fragment>
176
+ ))}
177
+ </>
178
+ );
179
+ }
180
+
181
+ export interface ToolbarIconProps {
182
+ icon: React.ComponentType<{ className?: string }>;
183
+ onClick: () => void;
184
+ size: ToolbarButtonSize;
185
+ active?: boolean;
186
+ tooltip?: string;
187
+ }
188
+
189
+ function ToolbarIcon({
190
+ icon,
191
+ onClick,
192
+ size,
193
+ active,
194
+ tooltip,
195
+ }: ToolbarIconProps) {
196
+ function handleClick(event: React.MouseEvent<HTMLButtonElement>): void {
197
+ event.preventDefault();
198
+ event.stopPropagation();
199
+ onClick();
200
+ }
201
+
202
+ if (size === "mini") {
203
+ return (
204
+ <Button
205
+ tooltip={tooltip}
206
+ icon={icon}
207
+ onClick={handleClick}
208
+ size="mini"
209
+ variant={active ? "ghost" : "ghost-secondary"}
210
+ />
211
+ );
212
+ }
213
+
214
+ return (
215
+ <Button
216
+ tooltip={tooltip}
217
+ icon={icon}
218
+ onClick={handleClick}
219
+ size={size}
220
+ variant={active ? "ghost" : "ghost-secondary"}
221
+ />
222
+ );
223
+ }
224
+
225
+ export interface ToolbarLinkProps {
226
+ isOpen: boolean;
227
+ onOpenChange: (open: boolean) => void;
228
+ onOpenDialog: () => void;
229
+ onSubmit: () => void;
230
+ linkText: string;
231
+ linkUrl: string;
232
+ onLinkTextChange: (value: string) => void;
233
+ onLinkUrlChange: (value: string) => void;
234
+ size: ToolbarButtonSize;
235
+ active?: boolean;
236
+ tooltip?: string;
237
+ }
238
+
239
+ function ToolbarLink({
240
+ isOpen,
241
+ onOpenChange,
242
+ onOpenDialog,
243
+ onSubmit,
244
+ linkText,
245
+ linkUrl,
246
+ onLinkTextChange,
247
+ onLinkUrlChange,
248
+ size,
249
+ active,
250
+ tooltip,
251
+ }: ToolbarLinkProps) {
252
+ function handleDialogClick(event: React.MouseEvent<HTMLDivElement>): void {
253
+ event.stopPropagation();
254
+ }
255
+
256
+ function handleCancelClick(): void {
257
+ onOpenChange(false);
258
+ }
259
+
260
+ return (
261
+ <Dialog open={isOpen} onOpenChange={onOpenChange}>
262
+ <ToolbarIcon
263
+ icon={LinkMIcon}
264
+ onClick={onOpenDialog}
265
+ active={active}
266
+ tooltip={tooltip}
267
+ size={size}
268
+ />
269
+ <DialogContent onClick={handleDialogClick}>
270
+ <DialogHeader>
271
+ <DialogTitle>Insert Link</DialogTitle>
272
+ <DialogDescription>
273
+ Add a link to your message with custom text.
274
+ </DialogDescription>
275
+ </DialogHeader>
276
+ <DialogContainer>
277
+ <Input
278
+ id="link-text"
279
+ label="Text"
280
+ placeholder="Text"
281
+ value={linkText}
282
+ onChange={(event) => onLinkTextChange(event.target.value)}
283
+ />
284
+ <Input
285
+ id="link-url"
286
+ label="Link"
287
+ placeholder="Link"
288
+ value={linkUrl}
289
+ autoFocus
290
+ onChange={(event) => onLinkUrlChange(event.target.value)}
291
+ />
292
+ </DialogContainer>
293
+ <DialogFooter
294
+ leftButtonProps={{
295
+ label: "Cancel",
296
+ variant: "outline",
297
+ onClick: handleCancelClick,
298
+ }}
299
+ rightButtonProps={{
300
+ label: "Save",
301
+ variant: "highlight",
302
+ onClick: onSubmit,
303
+ }}
304
+ />
305
+ </DialogContent>
306
+ </Dialog>
307
+ );
308
+ }
309
+
310
+ export { Toolbar, ToolbarContent, ToolbarIcon, ToolbarLink };
@@ -210,6 +210,15 @@ export { FlexSplitButton } from "./SplitButton";
210
210
  export { Tabs, TabsContent, TabsList, TabsTrigger } from "./Tabs";
211
211
  export { ReadOnlyTextArea, TextArea } from "./TextArea";
212
212
  export { Timeline, TimelineItem } from "./Timeline";
213
+ export type {
214
+ ToolbarContentGroup,
215
+ ToolbarContentProps,
216
+ ToolbarIconProps,
217
+ ToolbarLinkProps,
218
+ ToolbarProps,
219
+ ToolbarVariant,
220
+ } from "./Toolbar";
221
+ export { Toolbar, ToolbarContent, ToolbarIcon, ToolbarLink } from "./Toolbar";
213
222
  export {
214
223
  Tooltip,
215
224
  TooltipContent,