@dust-tt/sparkle 0.2.552 → 0.2.553-rc-2

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,295 @@
1
+ import { cva } from "class-variance-authority";
2
+ import * as React from "react";
3
+ import { useState } from "react";
4
+
5
+ import { Button, Icon, ScrollArea } from "@sparkle/components";
6
+ import {
7
+ Dialog,
8
+ DialogClose,
9
+ DialogContent,
10
+ DialogDescription,
11
+ DialogFooter,
12
+ DialogHeader,
13
+ DialogTitle,
14
+ DialogTrigger,
15
+ } from "@sparkle/components/Dialog";
16
+ import { ChevronLeftIcon, ChevronRightIcon } from "@sparkle/icons/app";
17
+ import { cn } from "@sparkle/lib/utils";
18
+
19
+ const MULTI_PAGE_DIALOG_SIZES = ["md", "lg", "xl"] as const;
20
+ type MultiPageDialogSizeType = (typeof MULTI_PAGE_DIALOG_SIZES)[number];
21
+
22
+ const multiPageDialogSizeClasses: Record<MultiPageDialogSizeType, string> = {
23
+ md: "s-h-100 s-max-h-screen",
24
+ lg: "s-h-125 s-max-h-screen",
25
+ xl: "s-h-150 s-max-h-screen",
26
+ };
27
+
28
+ const multiPageDialogHeightVariants = cva("", {
29
+ variants: {
30
+ size: multiPageDialogSizeClasses,
31
+ },
32
+ defaultVariants: {
33
+ size: "md",
34
+ },
35
+ });
36
+
37
+ const multiPageDialogLayoutVariants = cva(
38
+ cn("s-flex s-flex-col s-h-full s-overflow-hidden")
39
+ );
40
+
41
+ interface MultiPageDialogPage {
42
+ id: string;
43
+ title: string;
44
+ description?: string;
45
+ icon?: React.ComponentType;
46
+ content: React.ReactNode;
47
+ }
48
+
49
+ interface MultiPageDialogProps {
50
+ pages: MultiPageDialogPage[];
51
+ currentPageId: string;
52
+ onPageChange: (pageId: string) => void;
53
+ size?: MultiPageDialogSizeType;
54
+ trapFocusScope?: boolean;
55
+ isAlertDialog?: boolean;
56
+ showNavigation?: boolean;
57
+ showHeaderNavigation?: boolean;
58
+ footerContent?: React.ReactNode;
59
+ onSave?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
60
+ className?: string;
61
+ disableNext?: boolean;
62
+ disableSave?: boolean;
63
+ }
64
+
65
+ const MultiPageDialogRoot = Dialog;
66
+ const MultiPageDialogTrigger = DialogTrigger;
67
+ const MultiPageDialogClose = DialogClose;
68
+
69
+ interface MultiPageDialogContentProps extends MultiPageDialogProps {
70
+ children?: never;
71
+ }
72
+
73
+ const MultiPageDialogContent = React.forwardRef<
74
+ React.ElementRef<typeof DialogContent>,
75
+ MultiPageDialogContentProps
76
+ >(
77
+ (
78
+ {
79
+ pages,
80
+ currentPageId,
81
+ onPageChange,
82
+ size = "md",
83
+ trapFocusScope,
84
+ isAlertDialog,
85
+ showNavigation = true,
86
+ showHeaderNavigation = true,
87
+ footerContent,
88
+ onSave,
89
+ className,
90
+ disableNext = false,
91
+ disableSave = false,
92
+ ...props
93
+ },
94
+ ref
95
+ ) => {
96
+ const currentPageIndex = pages.findIndex(
97
+ (page) => page.id === currentPageId
98
+ );
99
+ const currentPage = pages[currentPageIndex];
100
+
101
+ const [isTransitioning, setIsTransitioning] = useState(false);
102
+ const [transitionDirection, setTransitionDirection] = useState<
103
+ "next" | "prev"
104
+ >("next");
105
+
106
+ const handlePrevious = (
107
+ e: React.MouseEvent<HTMLButtonElement, MouseEvent>
108
+ ) => {
109
+ e.preventDefault();
110
+ if (currentPageIndex > 0 && !isTransitioning) {
111
+ setTransitionDirection("prev");
112
+ setIsTransitioning(true);
113
+ setTimeout(() => {
114
+ onPageChange(pages[currentPageIndex - 1].id);
115
+ setTimeout(() => setIsTransitioning(false), 50);
116
+ }, 150);
117
+ }
118
+ };
119
+
120
+ const handleNext = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
121
+ e.preventDefault();
122
+ if (currentPageIndex < pages.length - 1 && !isTransitioning) {
123
+ setTransitionDirection("next");
124
+ setIsTransitioning(true);
125
+ setTimeout(() => {
126
+ onPageChange(pages[currentPageIndex + 1].id);
127
+ setTimeout(() => setIsTransitioning(false), 50);
128
+ }, 150);
129
+ }
130
+ };
131
+
132
+ if (!currentPage) {
133
+ console.warn(`Page with id "${currentPageId}" not found`);
134
+ return null;
135
+ }
136
+
137
+ const hasPrevious = currentPageIndex > 0;
138
+ const hasNext = currentPageIndex < pages.length - 1;
139
+ const nextButtonDisabled = disableNext || !hasNext || isTransitioning;
140
+ const prevButtonDisabled = !hasPrevious || isTransitioning;
141
+
142
+ return (
143
+ <DialogContent
144
+ ref={ref}
145
+ size={size}
146
+ trapFocusScope={trapFocusScope}
147
+ isAlertDialog={isAlertDialog}
148
+ className={cn(multiPageDialogHeightVariants({ size }), className)}
149
+ {...props}
150
+ >
151
+ <div className={cn(multiPageDialogLayoutVariants())}>
152
+ <DialogHeader hideButton={true} className="s-flex-none">
153
+ <div className="s-flex s-items-center s-justify-between s-pr-8">
154
+ <div className="s-flex s-items-center s-gap-3">
155
+ {showNavigation && showHeaderNavigation && (
156
+ <div className="s-flex s-items-center s-gap-1">
157
+ <Button
158
+ icon={ChevronLeftIcon}
159
+ variant="ghost"
160
+ size="sm"
161
+ disabled={prevButtonDisabled}
162
+ onClick={handlePrevious}
163
+ tooltip={
164
+ hasPrevious && !isTransitioning
165
+ ? "Previous page"
166
+ : undefined
167
+ }
168
+ />
169
+ <Button
170
+ icon={ChevronRightIcon}
171
+ variant="ghost"
172
+ size="sm"
173
+ disabled={nextButtonDisabled}
174
+ onClick={handleNext}
175
+ tooltip={
176
+ hasNext && !disableNext && !isTransitioning
177
+ ? "Next page"
178
+ : undefined
179
+ }
180
+ />
181
+ </div>
182
+ )}
183
+ <div
184
+ className={cn(
185
+ "s-flex s-items-center s-gap-2 s-transition-all s-duration-200 s-ease-out",
186
+ {
187
+ "s-transform s-opacity-0": isTransitioning,
188
+ "s-translate-x-1":
189
+ isTransitioning && transitionDirection === "next",
190
+ "s--translate-x-1":
191
+ isTransitioning && transitionDirection === "prev",
192
+ "s-translate-x-0 s-opacity-100": !isTransitioning,
193
+ }
194
+ )}
195
+ >
196
+ {currentPage.icon && (
197
+ <Icon
198
+ visual={currentPage.icon}
199
+ size="lg"
200
+ className="s-text-foreground"
201
+ />
202
+ )}
203
+ <div>
204
+ <DialogTitle>{currentPage.title}</DialogTitle>
205
+ {currentPage.description && (
206
+ <DialogDescription>
207
+ {currentPage.description}
208
+ </DialogDescription>
209
+ )}
210
+ </div>
211
+ </div>
212
+ </div>
213
+ {showNavigation && pages.length > 1 && (
214
+ <div className="s-text-xs s-text-muted-foreground dark:s-text-muted-foreground-night">
215
+ {currentPageIndex + 1} / {pages.length}
216
+ </div>
217
+ )}
218
+ </div>
219
+ </DialogHeader>
220
+
221
+ <div className="s-min-h-0 s-flex-1 s-overflow-hidden">
222
+ <ScrollArea
223
+ className={cn(
224
+ "s-h-full s-transition-all s-duration-200 s-ease-out",
225
+ {
226
+ "s-transform s-opacity-0": isTransitioning,
227
+ "s-translate-x-2":
228
+ isTransitioning && transitionDirection === "next",
229
+ "s--translate-x-2":
230
+ isTransitioning && transitionDirection === "prev",
231
+ "s-translate-x-0 s-opacity-100": !isTransitioning,
232
+ }
233
+ )}
234
+ >
235
+ <div className="s-flex s-flex-col s-gap-2 s-px-5 s-py-4">
236
+ {currentPage.content}
237
+ </div>
238
+ </ScrollArea>
239
+ </div>
240
+
241
+ <DialogFooter
242
+ className="s-flex-none"
243
+ leftButtonProps={{
244
+ label: "Cancel",
245
+ variant: "outline",
246
+ size: "sm",
247
+ }}
248
+ rightButtonProps={
249
+ showNavigation && pages.length > 1 && hasPrevious
250
+ ? {
251
+ label: "Previous",
252
+ variant: "outline",
253
+ size: "sm",
254
+ disabled: isTransitioning,
255
+ onClick: handlePrevious,
256
+ }
257
+ : undefined
258
+ }
259
+ >
260
+ {showNavigation && pages.length > 1 && hasNext && (
261
+ <Button
262
+ label="Next"
263
+ variant="outline"
264
+ size="sm"
265
+ disabled={disableNext || isTransitioning}
266
+ onClick={handleNext}
267
+ />
268
+ )}
269
+ {showNavigation && pages.length > 1 && !hasNext && onSave && (
270
+ <Button
271
+ label="Save changes"
272
+ variant="primary"
273
+ size="sm"
274
+ disabled={disableSave || isTransitioning}
275
+ onClick={onSave}
276
+ />
277
+ )}
278
+ {footerContent}
279
+ </DialogFooter>
280
+ </div>
281
+ </DialogContent>
282
+ );
283
+ }
284
+ );
285
+
286
+ MultiPageDialogContent.displayName = "MultiPageDialogContent";
287
+
288
+ export {
289
+ MultiPageDialogRoot as MultiPageDialog,
290
+ MultiPageDialogClose,
291
+ MultiPageDialogContent,
292
+ type MultiPageDialogPage,
293
+ type MultiPageDialogProps,
294
+ MultiPageDialogTrigger,
295
+ };
@@ -30,6 +30,7 @@ interface MultiPageSheetProps {
30
30
  side?: React.ComponentProps<typeof SheetContent>["side"];
31
31
  trapFocusScope?: boolean;
32
32
  showNavigation?: boolean;
33
+ showHeaderNavigation?: boolean;
33
34
  footerContent?: React.ReactNode;
34
35
  onSave?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
35
36
  className?: string;
@@ -58,6 +59,7 @@ const MultiPageSheetContent = React.forwardRef<
58
59
  side = "right",
59
60
  trapFocusScope,
60
61
  showNavigation = true,
62
+ showHeaderNavigation = true,
61
63
  footerContent,
62
64
  onSave,
63
65
  className,
@@ -110,7 +112,7 @@ const MultiPageSheetContent = React.forwardRef<
110
112
  <SheetHeader hideButton={true}>
111
113
  <div className="s-flex s-items-center s-justify-between s-pr-8">
112
114
  <div className="s-flex s-items-center s-gap-3">
113
- {showNavigation && (
115
+ {showNavigation && showHeaderNavigation && (
114
116
  <div className="s-flex s-items-center s-gap-1">
115
117
  <Button
116
118
  icon={ChevronLeftIcon}
@@ -29,12 +29,12 @@ import { cn } from "@sparkle/lib/utils";
29
29
 
30
30
  const sizes = {
31
31
  p: "s-copy-sm @sm:s-text-base @sm:s-leading-7",
32
- h1: "s-heading-3xl",
33
- h2: "s-heading-2xl",
34
- h3: "s-heading-xl",
35
- h4: "s-heading-lg",
36
- h5: "s-text-base s-font-bold",
37
- h6: "s-text-base s-font-regular s-italic",
32
+ h1: "s-heading-2xl",
33
+ h2: "s-heading-xl",
34
+ h3: "s-heading-lg",
35
+ h4: "s-text-base s-font-semibold",
36
+ h5: "s-text-sm s-font-semibold",
37
+ h6: "s-text-sm s-font-regular s-italic",
38
38
  };
39
39
 
40
40
  function showUnsupportedDirective() {
@@ -29,6 +29,13 @@ const example = `
29
29
 
30
30
  ### Level 3 Title
31
31
 
32
+ #### Level 4 Title
33
+
34
+ ##### Level 5 Title
35
+
36
+ ###### Level 6 Title
37
+
38
+
32
39
  This is a paragraph with **bold** text and *italic* text. This is \`code\` block:
33
40
  \`\`\`
34
41
  Block