@dust-tt/sparkle 0.2.596 → 0.2.597

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.
@@ -2,7 +2,7 @@ import { cva } from "class-variance-authority";
2
2
  import * as React from "react";
3
3
  import { useState } from "react";
4
4
 
5
- import { Button, Icon, ScrollArea, Separator } from "@sparkle/components";
5
+ import { Avatar, Button, ScrollArea, Separator } from "@sparkle/components";
6
6
  import {
7
7
  Dialog,
8
8
  DialogClose,
@@ -51,7 +51,10 @@ const MultiPageDialogFooter = ({
51
51
  }: MultiPageDialogFooterProps) => {
52
52
  const content = (
53
53
  <div
54
- className={cn("s-flex s-flex-none s-flex-col s-gap-3 s-p-4", className)}
54
+ className={cn(
55
+ "s-flex s-flex-none s-flex-col s-gap-3 s-px-4 s-py-2",
56
+ className
57
+ )}
55
58
  {...props}
56
59
  >
57
60
  {children}
@@ -192,7 +195,7 @@ const MultiPageDialogContent = React.forwardRef<
192
195
  hideButton={hideCloseButton || showHeaderNavigation}
193
196
  className="s-flex-none"
194
197
  >
195
- <div className="s-flex s-items-center s-justify-between s-pr-8">
198
+ <div className="s-flex s-items-center s-justify-between">
196
199
  <div className="s-flex s-items-center s-gap-3">
197
200
  {showNavigation && showHeaderNavigation && (
198
201
  <div className="s-flex s-items-center s-gap-1">
@@ -224,7 +227,7 @@ const MultiPageDialogContent = React.forwardRef<
224
227
  )}
225
228
  <div
226
229
  className={cn(
227
- "s-flex s-items-center s-gap-2 s-transition-all s-duration-200 s-ease-out",
230
+ "s-flex s-items-center s-gap-3 s-transition-all s-duration-200 s-ease-out",
228
231
  {
229
232
  "s-transform s-opacity-0": isTransitioning,
230
233
  "s-translate-x-1":
@@ -235,15 +238,16 @@ const MultiPageDialogContent = React.forwardRef<
235
238
  }
236
239
  )}
237
240
  >
238
- {currentPage.icon && (
239
- <Icon
240
- visual={currentPage.icon}
241
- size="lg"
242
- className="s-text-foreground"
243
- />
244
- )}
245
241
  <div>
246
- <DialogTitle>{currentPage.title}</DialogTitle>
242
+ <DialogTitle
243
+ visual={
244
+ currentPage.icon ? (
245
+ <Avatar icon={currentPage.icon} size="sm" />
246
+ ) : undefined
247
+ }
248
+ >
249
+ {currentPage.title}
250
+ </DialogTitle>
247
251
  {currentPage.description && (
248
252
  <DialogDescription>
249
253
  {currentPage.description}
@@ -253,8 +257,13 @@ const MultiPageDialogContent = React.forwardRef<
253
257
  </div>
254
258
  </div>
255
259
  {showNavigation && pages.length > 1 && (
256
- <div className="s-text-xs s-text-muted-foreground dark:s-text-muted-foreground-night">
257
- {currentPageIndex + 1} / {pages.length}
260
+ <div
261
+ className={cn(
262
+ "s-text-xs s-font-semibold s-text-muted-foreground-night",
263
+ !hideCloseButton && "s-pr-8"
264
+ )}
265
+ >
266
+ {currentPageIndex + 1}/{pages.length}
258
267
  </div>
259
268
  )}
260
269
  </div>
@@ -286,7 +295,7 @@ const MultiPageDialogContent = React.forwardRef<
286
295
  <ScrollArea
287
296
  className={currentPage.fixedContent ? "s-flex-1" : "s-h-full"}
288
297
  >
289
- <div className="s-flex s-flex-col s-gap-2 s-px-5 s-py-4">
298
+ <div className="s-flex s-flex-col s-gap-2 s-px-5">
290
299
  {currentPage.content}
291
300
  </div>
292
301
  </ScrollArea>
@@ -22,7 +22,7 @@ const SheetOverlay = React.forwardRef<
22
22
  <SheetPrimitive.Overlay
23
23
  className={cn(
24
24
  "s-fixed s-inset-0 s-z-50",
25
- "s-bg-muted-foreground/75 dark:s-bg-muted-foreground-night/75",
25
+ "s-bg-muted-foreground/75 dark:s-bg-muted-background-night/75",
26
26
  "data-[state=open]:s-animate-in data-[state=closed]:s-animate-out",
27
27
  "data-[state=closed]:s-fade-out-0 data-[state=open]:s-fade-in-0",
28
28
  className
@@ -3,13 +3,21 @@ import React, { useState } from "react";
3
3
 
4
4
  import { SearchInput } from "@sparkle/components";
5
5
  import { Button } from "@sparkle/components/Button";
6
+ import { Checkbox } from "@sparkle/components/Checkbox";
7
+ import { CollapsibleComponent } from "@sparkle/components/Collapsible";
6
8
  import {
7
9
  MultiPageDialog,
8
10
  MultiPageDialogContent,
9
11
  type MultiPageDialogPage,
10
12
  MultiPageDialogTrigger,
11
13
  } from "@sparkle/components/MultiPageDialog";
12
- import { Cog6ToothIcon, DocumentTextIcon, UserIcon } from "@sparkle/icons/app";
14
+ import {
15
+ Cog6ToothIcon,
16
+ DocumentTextIcon,
17
+ ExclamationCircleIcon,
18
+ UserIcon,
19
+ } from "@sparkle/icons/app";
20
+ import { GmailLogo } from "@sparkle/logo/platforms";
13
21
 
14
22
  const meta: Meta<typeof MultiPageDialogContent> = {
15
23
  title: "Primitives/MultiPageDialog",
@@ -827,7 +835,6 @@ export const ScrollableContent: Story = {
827
835
  {
828
836
  id: "summary",
829
837
  title: "Summary",
830
- description: "Review your information",
831
838
  icon: Cog6ToothIcon,
832
839
  content: (
833
840
  <div className="s-space-y-4">
@@ -888,3 +895,267 @@ export const ScrollableContent: Story = {
888
895
  );
889
896
  },
890
897
  };
898
+
899
+ export const ActionValidation: Story = {
900
+ render: () => {
901
+ const [isOpen, setIsOpen] = useState(false);
902
+ const [currentPageId, setCurrentPageId] = useState("0");
903
+ const [neverAskAgain, setNeverAskAgain] = useState(false);
904
+ const [errorMessage, setErrorMessage] = useState<string | null>(null);
905
+ const [isValidating, setIsValidating] = useState(false);
906
+
907
+ const validationPages = [
908
+ {
909
+ id: "0",
910
+ title: "Tool Validation Required",
911
+ icon: GmailLogo,
912
+ content: (
913
+ <div className="s-space-y-6 s-pt-4">
914
+ <div>
915
+ <p className="s-mb-6 s-text-sm s-text-muted-foreground">
916
+ Allow{" "}
917
+ <span className="s-font-semibold">@Marketing Assistant</span> to
918
+ use the tool <span className="s-font-semibold">Send Email</span>{" "}
919
+ from <span className="s-font-semibold">Gmail</span>?
920
+ </p>
921
+
922
+ <div className="s-space-y-3">
923
+ <CollapsibleComponent
924
+ triggerChildren={
925
+ <span className="s-text-sm s-font-medium s-text-muted-foreground dark:s-text-muted-foreground-night">
926
+ Details
927
+ </span>
928
+ }
929
+ contentChildren={
930
+ <div className="s-mt-2 s-rounded-md s-border s-bg-muted s-p-3">
931
+ <h4 className="s-mb-2 s-text-sm s-font-medium">
932
+ Email Details
933
+ </h4>
934
+ <div className="s-space-y-2 s-text-sm">
935
+ <div>
936
+ <span className="s-font-medium">To:</span>{" "}
937
+ john.doe@example.com
938
+ </div>
939
+ <div>
940
+ <span className="s-font-medium">Subject:</span>{" "}
941
+ Welcome to our platform!
942
+ </div>
943
+ <div>
944
+ <span className="s-font-medium">Content:</span> Thank
945
+ you for signing up...
946
+ </div>
947
+ </div>
948
+ </div>
949
+ }
950
+ />
951
+
952
+ {errorMessage && (
953
+ <div className="s-flex s-items-center s-gap-2 s-text-sm s-font-medium s-text-warning-800">
954
+ <ExclamationCircleIcon className="s-h-4 s-w-4" />
955
+ {errorMessage}
956
+ </div>
957
+ )}
958
+
959
+ <div className="s-mt-4">
960
+ <label className="s-copy-xs s-flex s-w-fit s-cursor-pointer s-flex-row s-items-center s-gap-2 s-py-2 s-pr-2 s-font-normal">
961
+ <Checkbox
962
+ size="xs"
963
+ checked={neverAskAgain}
964
+ onCheckedChange={(check) => {
965
+ setNeverAskAgain(!!check);
966
+ }}
967
+ />
968
+ <span>Always allow this tool</span>
969
+ </label>
970
+ </div>
971
+ </div>
972
+ </div>
973
+ </div>
974
+ ),
975
+ },
976
+ {
977
+ id: "1",
978
+ title: "Bulk Email Validation",
979
+ icon: GmailLogo,
980
+ content: (
981
+ <div className="s-space-y-6 s-pt-4">
982
+ <div>
983
+ <p className="s-mb-6 s-text-sm s-text-muted-foreground">
984
+ Allow{" "}
985
+ <span className="s-font-semibold">@Marketing Assistant</span> to
986
+ use the tool{" "}
987
+ <span className="s-font-semibold">Send Bulk Email</span> from{" "}
988
+ <span className="s-font-semibold">Gmail</span>?
989
+ </p>
990
+
991
+ <div className="s-space-y-3">
992
+ <CollapsibleComponent
993
+ triggerChildren={
994
+ <span className="s-text-sm s-font-medium s-text-muted-foreground dark:s-text-muted-foreground-night">
995
+ Details
996
+ </span>
997
+ }
998
+ contentChildren={
999
+ <div className="s-mt-2 s-rounded-md s-border s-bg-muted s-p-3">
1000
+ <h4 className="s-mb-2 s-text-sm s-font-medium">
1001
+ Campaign Details
1002
+ </h4>
1003
+ <div className="s-space-y-2 s-text-sm">
1004
+ <div>
1005
+ <span className="s-font-medium">Recipients:</span>{" "}
1006
+ 1,250 subscribers
1007
+ </div>
1008
+ <div>
1009
+ <span className="s-font-medium">Subject:</span>{" "}
1010
+ Monthly Newsletter - March 2024
1011
+ </div>
1012
+ <div>
1013
+ <span className="s-font-medium">Template:</span>{" "}
1014
+ Newsletter Template v2
1015
+ </div>
1016
+ </div>
1017
+ </div>
1018
+ }
1019
+ />
1020
+
1021
+ <div className="s-rounded-md s-border s-bg-blue-50 s-p-3">
1022
+ <h4 className="s-mb-1 s-text-sm s-font-medium s-text-blue-900">
1023
+ Security Notice
1024
+ </h4>
1025
+ <p className="s-text-xs s-text-blue-700">
1026
+ This action will send emails to a large number of
1027
+ recipients. Please review the content carefully.
1028
+ </p>
1029
+ </div>
1030
+ </div>
1031
+ </div>
1032
+ </div>
1033
+ ),
1034
+ },
1035
+ {
1036
+ id: "2",
1037
+ title: "Email Template Validation",
1038
+ icon: GmailLogo,
1039
+ content: (
1040
+ <div className="s-space-y-6 s-pt-4">
1041
+ <div>
1042
+ <p className="s-mb-6 s-text-sm s-text-muted-foreground">
1043
+ Allow{" "}
1044
+ <span className="s-font-semibold">@Marketing Assistant</span> to
1045
+ use the tool{" "}
1046
+ <span className="s-font-semibold">Create Email Template</span>{" "}
1047
+ from <span className="s-font-semibold">Gmail</span>?
1048
+ </p>
1049
+
1050
+ <div className="s-space-y-3">
1051
+ <CollapsibleComponent
1052
+ triggerChildren={
1053
+ <span className="s-text-sm s-font-medium s-text-muted-foreground dark:s-text-muted-foreground-night">
1054
+ Details
1055
+ </span>
1056
+ }
1057
+ contentChildren={
1058
+ <div className="s-mt-2 s-rounded-md s-border s-bg-muted s-p-3">
1059
+ <h4 className="s-mb-2 s-text-sm s-font-medium">
1060
+ Template Details
1061
+ </h4>
1062
+ <div className="s-space-y-2 s-text-sm">
1063
+ <div>
1064
+ <span className="s-font-medium">Name:</span> Welcome
1065
+ Series - Day 1
1066
+ </div>
1067
+ <div>
1068
+ <span className="s-font-medium">Category:</span>{" "}
1069
+ Onboarding
1070
+ </div>
1071
+ <div>
1072
+ <span className="s-font-medium">Variables:</span>{" "}
1073
+ {"{{name}}"}, {"{{company}}"}, {"{{trial_end_date}}"}
1074
+ </div>
1075
+ </div>
1076
+ </div>
1077
+ }
1078
+ />
1079
+
1080
+ <div className="s-rounded-md s-border s-bg-green-50 s-p-3">
1081
+ <h4 className="s-mb-1 s-text-sm s-font-medium s-text-green-900">
1082
+ Low Risk Action
1083
+ </h4>
1084
+ <p className="s-text-xs s-text-green-700">
1085
+ This action only creates a template and does not send any
1086
+ emails.
1087
+ </p>
1088
+ </div>
1089
+ </div>
1090
+ </div>
1091
+ </div>
1092
+ ),
1093
+ },
1094
+ ];
1095
+
1096
+ const isLastPage =
1097
+ currentPageId === (validationPages.length - 1).toString();
1098
+
1099
+ const handleApprove = async () => {
1100
+ setIsValidating(true);
1101
+ // Simulate API call
1102
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1103
+ setIsValidating(false);
1104
+ if (isLastPage) {
1105
+ setIsOpen(false);
1106
+ } else {
1107
+ // Move to next page
1108
+ const nextPageIndex = parseInt(currentPageId) + 1;
1109
+ setCurrentPageId(nextPageIndex.toString());
1110
+ }
1111
+ };
1112
+
1113
+ const handleDecline = async () => {
1114
+ setIsValidating(true);
1115
+ // Simulate API call
1116
+ await new Promise((resolve) => setTimeout(resolve, 500));
1117
+ setIsValidating(false);
1118
+ setErrorMessage("Action was declined by user");
1119
+ if (isLastPage) {
1120
+ setIsOpen(false);
1121
+ }
1122
+ };
1123
+
1124
+ return (
1125
+ <MultiPageDialog open={isOpen} onOpenChange={setIsOpen}>
1126
+ <MultiPageDialogTrigger asChild>
1127
+ <Button label="Open Email Validation Dialog" />
1128
+ </MultiPageDialogTrigger>
1129
+ <MultiPageDialogContent
1130
+ pages={validationPages}
1131
+ currentPageId={currentPageId}
1132
+ onPageChange={setCurrentPageId}
1133
+ size="md"
1134
+ isAlertDialog
1135
+ showNavigation={true}
1136
+ showHeaderNavigation={false}
1137
+ hideCloseButton={true}
1138
+ footerContent={
1139
+ <div className="s-flex s-flex-row s-justify-end s-gap-2">
1140
+ <Button
1141
+ variant="outline"
1142
+ label={"Decline"}
1143
+ onClick={handleDecline}
1144
+ disabled={isValidating}
1145
+ isLoading={isValidating}
1146
+ />
1147
+ <Button
1148
+ variant="highlight"
1149
+ label={"Allow"}
1150
+ autoFocus
1151
+ onClick={handleApprove}
1152
+ disabled={isValidating}
1153
+ isLoading={isValidating}
1154
+ />
1155
+ </div>
1156
+ }
1157
+ />
1158
+ </MultiPageDialog>
1159
+ );
1160
+ },
1161
+ };