@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.
- package/dist/cjs/index.js +1 -1
- package/dist/esm/components/MultiPageDialog.d.ts +34 -0
- package/dist/esm/components/MultiPageDialog.d.ts.map +1 -0
- package/dist/esm/components/MultiPageDialog.js +116 -0
- package/dist/esm/components/MultiPageDialog.js.map +1 -0
- package/dist/esm/components/MultiPageSheet.d.ts +1 -0
- package/dist/esm/components/MultiPageSheet.d.ts.map +1 -1
- package/dist/esm/components/MultiPageSheet.js +2 -2
- package/dist/esm/components/MultiPageSheet.js.map +1 -1
- package/dist/esm/components/markdown/Markdown.js +6 -6
- package/dist/esm/components/markdown/Markdown.js.map +1 -1
- package/dist/esm/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/esm/stories/Markdown.stories.js +1 -1
- package/dist/esm/stories/Markdown.stories.js.map +1 -1
- package/dist/esm/stories/MultiPageDialog.stories.d.ts +10 -0
- package/dist/esm/stories/MultiPageDialog.stories.d.ts.map +1 -0
- package/dist/esm/stories/MultiPageDialog.stories.js +301 -0
- package/dist/esm/stories/MultiPageDialog.stories.js.map +1 -0
- package/dist/sparkle.css +47 -0
- package/package.json +1 -1
- package/src/components/MultiPageDialog.tsx +295 -0
- package/src/components/MultiPageSheet.tsx +3 -1
- package/src/components/markdown/Markdown.tsx +6 -6
- package/src/stories/Markdown.stories.tsx +7 -0
- package/src/stories/MultiPageDialog.stories.tsx +592 -0
|
@@ -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-
|
|
33
|
-
h2: "s-heading-
|
|
34
|
-
h3: "s-heading-
|
|
35
|
-
h4: "s-
|
|
36
|
-
h5: "s-text-
|
|
37
|
-
h6: "s-text-
|
|
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() {
|