@dust-tt/sparkle 0.2.557 → 0.2.558-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.
@@ -117,13 +117,19 @@ const samplePages: MultiPageDialogPage[] = [
117
117
 
118
118
  const MultiPageDialogDemo = () => {
119
119
  const [currentPageId, setCurrentPageId] = useState("profile");
120
+ const [isOpen, setIsOpen] = useState(false);
120
121
 
121
122
  const handleSave = () => {
122
123
  alert("Changes saved!");
124
+ setIsOpen(false);
125
+ };
126
+
127
+ const handleCancel = () => {
128
+ setIsOpen(false);
123
129
  };
124
130
 
125
131
  return (
126
- <MultiPageDialog>
132
+ <MultiPageDialog open={isOpen} onOpenChange={setIsOpen}>
127
133
  <MultiPageDialogTrigger asChild>
128
134
  <Button label="Open Multi-Page Dialog" />
129
135
  </MultiPageDialogTrigger>
@@ -132,7 +138,16 @@ const MultiPageDialogDemo = () => {
132
138
  currentPageId={currentPageId}
133
139
  onPageChange={setCurrentPageId}
134
140
  size="xl"
135
- onSave={handleSave}
141
+ leftButton={{
142
+ label: "Cancel",
143
+ variant: "outline",
144
+ onClick: handleCancel,
145
+ }}
146
+ rightButton={{
147
+ label: "Save Changes",
148
+ variant: "primary",
149
+ onClick: handleSave,
150
+ }}
136
151
  />
137
152
  </MultiPageDialog>
138
153
  );
@@ -142,9 +157,150 @@ export const Default: Story = {
142
157
  render: () => <MultiPageDialogDemo />,
143
158
  };
144
159
 
160
+ // Simple two-button example (like your screenshot)
161
+ export const SimpleToolDialog: Story = {
162
+ render: () => {
163
+ const [isOpen, setIsOpen] = useState(false);
164
+ const [selectedTools, setSelectedTools] = useState<string[]>([]);
165
+
166
+ const handleAddTools = () => {
167
+ alert(
168
+ `Adding ${selectedTools.length} tools: ${selectedTools.join(", ")}`
169
+ );
170
+ setIsOpen(false);
171
+ };
172
+
173
+ const handleCancel = () => {
174
+ setSelectedTools([]);
175
+ setIsOpen(false);
176
+ };
177
+
178
+ const toggleTool = (tool: string) => {
179
+ setSelectedTools((prev) =>
180
+ prev.includes(tool) ? prev.filter((t) => t !== tool) : [...prev, tool]
181
+ );
182
+ };
183
+
184
+ const toolPages: MultiPageDialogPage[] = [
185
+ {
186
+ id: "tool-selection",
187
+ title: "Add tools",
188
+ content: (
189
+ <div className="s-space-y-6">
190
+ <div className="s-space-y-4">
191
+ <h3 className="s-text-lg s-font-semibold">Capabilities</h3>
192
+ <div className="s-grid s-grid-cols-2 s-gap-3">
193
+ {[
194
+ {
195
+ name: "Image generation",
196
+ desc: "Generate images using natural language",
197
+ },
198
+ {
199
+ name: "Run agent",
200
+ desc: "Run a child agent (agent as tool)",
201
+ },
202
+ {
203
+ name: "Interactive content",
204
+ desc: "Generate interactive content",
205
+ },
206
+ {
207
+ name: "Agent memory",
208
+ desc: "Store and recall information",
209
+ },
210
+ ].map((tool) => (
211
+ <div
212
+ key={tool.name}
213
+ className={`s-cursor-pointer s-rounded-lg s-border s-p-4 s-transition-colors hover:s-bg-gray-50 ${
214
+ selectedTools.includes(tool.name)
215
+ ? "s-border-blue-300 s-bg-blue-50"
216
+ : "s-border-gray-200"
217
+ }`}
218
+ onClick={() => toggleTool(tool.name)}
219
+ >
220
+ <div className="s-flex s-items-start s-justify-between">
221
+ <div>
222
+ <h4 className="s-font-medium">{tool.name}</h4>
223
+ <p className="s-text-sm s-text-gray-600">{tool.desc}</p>
224
+ </div>
225
+ {selectedTools.includes(tool.name) && (
226
+ <span className="s-rounded s-bg-green-100 s-px-2 s-py-1 s-text-xs s-text-green-700">
227
+ ADDED
228
+ </span>
229
+ )}
230
+ </div>
231
+ {!selectedTools.includes(tool.name) && (
232
+ <button className="s-mt-2 s-text-sm s-text-blue-600 hover:s-text-blue-700">
233
+ + Add
234
+ </button>
235
+ )}
236
+ </div>
237
+ ))}
238
+ </div>
239
+ </div>
240
+
241
+ {selectedTools.length > 0 && (
242
+ <div className="s-space-y-3">
243
+ <h4 className="s-font-medium">Added tools</h4>
244
+ <div className="s-flex s-flex-wrap s-gap-2">
245
+ {selectedTools.map((tool) => (
246
+ <span
247
+ key={tool}
248
+ className="s-flex s-items-center s-gap-1 s-rounded s-bg-gray-100 s-px-3 s-py-1 s-text-sm"
249
+ >
250
+ {tool}
251
+ <button
252
+ onClick={(e) => {
253
+ e.stopPropagation();
254
+ toggleTool(tool);
255
+ }}
256
+ className="s-ml-1 s-text-gray-500 hover:s-text-gray-700"
257
+ >
258
+ ×
259
+ </button>
260
+ </span>
261
+ ))}
262
+ </div>
263
+ </div>
264
+ )}
265
+ </div>
266
+ ),
267
+ },
268
+ ];
269
+
270
+ return (
271
+ <MultiPageDialog open={isOpen} onOpenChange={setIsOpen}>
272
+ <MultiPageDialogTrigger asChild>
273
+ <Button label="Add tools" />
274
+ </MultiPageDialogTrigger>
275
+ <MultiPageDialogContent
276
+ pages={toolPages}
277
+ currentPageId="tool-selection"
278
+ onPageChange={() => {}}
279
+ size="xl"
280
+ leftButton={{
281
+ label: "Cancel",
282
+ variant: "outline",
283
+ onClick: handleCancel,
284
+ }}
285
+ rightButton={{
286
+ label:
287
+ selectedTools.length > 0
288
+ ? `Add ${selectedTools.length} tool${selectedTools.length > 1 ? "s" : ""}`
289
+ : "Add tools",
290
+ variant: "primary",
291
+ disabled: selectedTools.length === 0,
292
+ onClick: handleAddTools,
293
+ }}
294
+ />
295
+ </MultiPageDialog>
296
+ );
297
+ },
298
+ };
299
+
145
300
  export const InteractiveContent: Story = {
146
301
  render: () => {
147
302
  const [currentPageId, setCurrentPageId] = useState("step1");
303
+ const [isOpen, setIsOpen] = useState(false);
148
304
  const [formData, setFormData] = useState({
149
305
  name: "",
150
306
  email: "",
@@ -154,8 +310,42 @@ export const InteractiveContent: Story = {
154
310
 
155
311
  const handleSave = () => {
156
312
  alert(`Setup completed! Data: ${JSON.stringify(formData, null, 2)}`);
313
+ setIsOpen(false);
314
+ };
315
+
316
+ const handleCancel = () => {
317
+ setIsOpen(false);
318
+ };
319
+
320
+ const handleNext = () => {
321
+ if (currentPageId === "step1") {
322
+ setCurrentPageId("step2");
323
+ } else if (currentPageId === "step2") {
324
+ setCurrentPageId("step3");
325
+ }
326
+ };
327
+
328
+ const handlePrevious = () => {
329
+ if (currentPageId === "step3") {
330
+ setCurrentPageId("step2");
331
+ } else if (currentPageId === "step2") {
332
+ setCurrentPageId("step1");
333
+ }
157
334
  };
158
335
 
336
+ const canProceed = () => {
337
+ if (currentPageId === "step1") {
338
+ return formData.name && formData.email;
339
+ }
340
+ if (currentPageId === "step2") {
341
+ return formData.selectedFile;
342
+ }
343
+ return true;
344
+ };
345
+
346
+ const isFirstPage = currentPageId === "step1";
347
+ const isLastPage = currentPageId === "step3";
348
+
159
349
  const interactivePages: MultiPageDialogPage[] = [
160
350
  {
161
351
  id: "step1",
@@ -197,15 +387,6 @@ export const InteractiveContent: Story = {
197
387
  }
198
388
  />
199
389
  </div>
200
- <div className="s-pt-2">
201
- <Button
202
- label="Continue to File Selection"
203
- variant="primary"
204
- size="md"
205
- disabled={!formData.name || !formData.email}
206
- onClick={() => setCurrentPageId("step2")}
207
- />
208
- </div>
209
390
  </div>
210
391
  </div>
211
392
  ),
@@ -254,16 +435,6 @@ export const InteractiveContent: Story = {
254
435
  </div>
255
436
  ))}
256
437
  </div>
257
- {formData.selectedFile && (
258
- <div className="s-pt-2">
259
- <Button
260
- label="Continue to Settings"
261
- variant="primary"
262
- size="md"
263
- onClick={() => setCurrentPageId("step3")}
264
- />
265
- </div>
266
- )}
267
438
  </div>
268
439
  ),
269
440
  },
@@ -314,7 +485,7 @@ export const InteractiveContent: Story = {
314
485
  ];
315
486
 
316
487
  return (
317
- <MultiPageDialog>
488
+ <MultiPageDialog open={isOpen} onOpenChange={setIsOpen}>
318
489
  <MultiPageDialogTrigger asChild>
319
490
  <Button label="Open Interactive Setup" />
320
491
  </MultiPageDialogTrigger>
@@ -323,7 +494,26 @@ export const InteractiveContent: Story = {
323
494
  currentPageId={currentPageId}
324
495
  onPageChange={setCurrentPageId}
325
496
  size="lg"
326
- onSave={handleSave}
497
+ leftButton={{
498
+ label: "Cancel",
499
+ variant: "outline",
500
+ onClick: handleCancel,
501
+ }}
502
+ centerButton={
503
+ !isFirstPage
504
+ ? {
505
+ label: "Previous",
506
+ variant: "outline",
507
+ onClick: handlePrevious,
508
+ }
509
+ : undefined
510
+ }
511
+ rightButton={{
512
+ label: isLastPage ? "Complete Setup" : "Next",
513
+ variant: "primary",
514
+ disabled: !canProceed(),
515
+ onClick: isLastPage ? handleSave : handleNext,
516
+ }}
327
517
  />
328
518
  </MultiPageDialog>
329
519
  );
@@ -333,6 +523,7 @@ export const InteractiveContent: Story = {
333
523
  export const WithConditionalNavigation: Story = {
334
524
  render: () => {
335
525
  const [currentPageId, setCurrentPageId] = useState("data-selection");
526
+ const [isOpen, setIsOpen] = useState(false);
336
527
  const [selectedItems, setSelectedItems] = useState<string[]>([]);
337
528
  const [description, setDescription] = useState("");
338
529
 
@@ -340,8 +531,30 @@ export const WithConditionalNavigation: Story = {
340
531
  alert(
341
532
  `Configuration saved! Selected: ${selectedItems.join(", ")}, Description: ${description}`
342
533
  );
534
+ setIsOpen(false);
343
535
  };
344
536
 
537
+ const handleCancel = () => {
538
+ setIsOpen(false);
539
+ };
540
+
541
+ const handleNext = () => {
542
+ if (currentPageId === "data-selection") {
543
+ setCurrentPageId("description");
544
+ }
545
+ };
546
+
547
+ const handlePrevious = () => {
548
+ if (currentPageId === "description") {
549
+ setCurrentPageId("data-selection");
550
+ }
551
+ };
552
+
553
+ const isFirstPage = currentPageId === "data-selection";
554
+ const isLastPage = currentPageId === "description";
555
+ const canProceedFromFirst = selectedItems.length > 0;
556
+ const canSave = description.trim().length > 0;
557
+
345
558
  const conditionalPages: MultiPageDialogPage[] = [
346
559
  {
347
560
  id: "data-selection",
@@ -441,7 +654,7 @@ export const WithConditionalNavigation: Story = {
441
654
  ];
442
655
 
443
656
  return (
444
- <MultiPageDialog>
657
+ <MultiPageDialog open={isOpen} onOpenChange={setIsOpen}>
445
658
  <MultiPageDialogTrigger asChild>
446
659
  <Button label="Open Configuration Wizard" />
447
660
  </MultiPageDialogTrigger>
@@ -450,15 +663,37 @@ export const WithConditionalNavigation: Story = {
450
663
  currentPageId={currentPageId}
451
664
  onPageChange={setCurrentPageId}
452
665
  size="lg"
453
- onSave={handleSave}
454
- showNavigation={true}
455
- disableNext={
456
- currentPageId === "data-selection" && selectedItems.length === 0
666
+ leftButton={{
667
+ label: "Cancel",
668
+ variant: "outline",
669
+ onClick: handleCancel,
670
+ }}
671
+ centerButton={
672
+ !isFirstPage
673
+ ? {
674
+ label: "Previous",
675
+ variant: "outline",
676
+ onClick: handlePrevious,
677
+ }
678
+ : undefined
457
679
  }
458
- disableSave={!description.trim()}
680
+ rightButton={{
681
+ label: isLastPage ? "Save Configuration" : "Next",
682
+ variant: "primary",
683
+ disabled: isFirstPage ? !canProceedFromFirst : !canSave,
684
+ onClick: isLastPage ? handleSave : handleNext,
685
+ }}
459
686
  footerContent={
460
- <div className="s-w-full s-border s-border-border-dark">
461
- This is a footer content
687
+ <div className="s-rounded s-bg-blue-50 s-px-3 s-py-2">
688
+ <p className="s-text-xs s-text-blue-700">
689
+ {selectedItems.length > 0 && (
690
+ <>
691
+ {selectedItems.length} data source
692
+ {selectedItems.length !== 1 ? "s" : ""} selected •{" "}
693
+ </>
694
+ )}
695
+ Step {isFirstPage ? "1" : "2"} of 2
696
+ </p>
462
697
  </div>
463
698
  }
464
699
  />
@@ -470,11 +705,32 @@ export const WithConditionalNavigation: Story = {
470
705
  export const ScrollableContent: Story = {
471
706
  render: () => {
472
707
  const [currentPageId, setCurrentPageId] = useState("long-form");
708
+ const [isOpen, setIsOpen] = useState(false);
473
709
 
474
710
  const handleSave = () => {
475
711
  alert("Long form submitted!");
712
+ setIsOpen(false);
476
713
  };
477
714
 
715
+ const handleCancel = () => {
716
+ setIsOpen(false);
717
+ };
718
+
719
+ const handleNext = () => {
720
+ if (currentPageId === "long-form") {
721
+ setCurrentPageId("summary");
722
+ }
723
+ };
724
+
725
+ const handlePrevious = () => {
726
+ if (currentPageId === "summary") {
727
+ setCurrentPageId("long-form");
728
+ }
729
+ };
730
+
731
+ const isFirstPage = currentPageId === "long-form";
732
+ const isLastPage = currentPageId === "summary";
733
+
478
734
  const scrollablePages: MultiPageDialogPage[] = [
479
735
  {
480
736
  id: "long-form",
@@ -575,7 +831,7 @@ export const ScrollableContent: Story = {
575
831
  ];
576
832
 
577
833
  return (
578
- <MultiPageDialog>
834
+ <MultiPageDialog open={isOpen} onOpenChange={setIsOpen}>
579
835
  <MultiPageDialogTrigger asChild>
580
836
  <Button label="Open Scrollable Content Dialog" />
581
837
  </MultiPageDialogTrigger>
@@ -584,7 +840,25 @@ export const ScrollableContent: Story = {
584
840
  currentPageId={currentPageId}
585
841
  onPageChange={setCurrentPageId}
586
842
  size="lg"
587
- onSave={handleSave}
843
+ leftButton={{
844
+ label: "Cancel",
845
+ variant: "outline",
846
+ onClick: handleCancel,
847
+ }}
848
+ centerButton={
849
+ !isFirstPage
850
+ ? {
851
+ label: "Previous",
852
+ variant: "outline",
853
+ onClick: handlePrevious,
854
+ }
855
+ : undefined
856
+ }
857
+ rightButton={{
858
+ label: isLastPage ? "Submit Form" : "Next",
859
+ variant: "primary",
860
+ onClick: isLastPage ? handleSave : handleNext,
861
+ }}
588
862
  />
589
863
  </MultiPageDialog>
590
864
  );