@dust-tt/sparkle 0.2.591-rc-1 → 0.2.591-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/DataTable.d.ts +1 -1
- package/dist/esm/components/DataTable.d.ts.map +1 -1
- package/dist/esm/components/DataTable.js +51 -48
- package/dist/esm/components/DataTable.js.map +1 -1
- package/dist/esm/components/Dropdown.js +3 -3
- package/dist/esm/components/Dropdown.js.map +1 -1
- package/dist/esm/components/MultiPageSheet.d.ts +5 -0
- package/dist/esm/components/MultiPageSheet.d.ts.map +1 -1
- package/dist/esm/components/MultiPageSheet.js +1 -1
- package/dist/esm/components/MultiPageSheet.js.map +1 -1
- package/dist/esm/components/Sheet.d.ts +4 -1
- package/dist/esm/components/Sheet.d.ts.map +1 -1
- package/dist/esm/components/Sheet.js +5 -3
- package/dist/esm/components/Sheet.js.map +1 -1
- package/dist/esm/stories/DataTable.stories.d.ts +1 -0
- package/dist/esm/stories/DataTable.stories.d.ts.map +1 -1
- package/dist/esm/stories/DataTable.stories.js +28 -0
- package/dist/esm/stories/DataTable.stories.js.map +1 -1
- package/dist/esm/stories/MultiPageSheet.stories.d.ts +1 -0
- package/dist/esm/stories/MultiPageSheet.stories.d.ts.map +1 -1
- package/dist/esm/stories/MultiPageSheet.stories.js +154 -1
- package/dist/esm/stories/MultiPageSheet.stories.js.map +1 -1
- package/dist/sparkle.css +5 -4
- package/package.json +1 -1
- package/src/components/DataTable.tsx +115 -111
- package/src/components/Dropdown.tsx +21 -21
- package/src/components/MultiPageSheet.tsx +6 -0
- package/src/components/Sheet.tsx +17 -10
- package/src/stories/DataTable.stories.tsx +61 -0
- package/src/stories/MultiPageSheet.stories.tsx +214 -1
|
@@ -370,25 +370,25 @@ const DropdownMenuItem = React.forwardRef<
|
|
|
370
370
|
ref
|
|
371
371
|
) => {
|
|
372
372
|
return (
|
|
373
|
-
<
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
373
|
+
<DropdownMenuPrimitive.Item
|
|
374
|
+
ref={ref}
|
|
375
|
+
className={cn(
|
|
376
|
+
menuStyleClasses.item({ variant }),
|
|
377
|
+
inset ? menuStyleClasses.inset : "",
|
|
378
|
+
className
|
|
379
|
+
)}
|
|
380
|
+
{...props}
|
|
381
|
+
asChild={asChild}
|
|
380
382
|
>
|
|
381
|
-
<
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
>
|
|
391
|
-
<div className="s-h-full s-w-full">
|
|
383
|
+
<div className="s-h-full s-w-full">
|
|
384
|
+
<LinkWrapper
|
|
385
|
+
href={href}
|
|
386
|
+
target={target}
|
|
387
|
+
rel={rel}
|
|
388
|
+
replace={replace}
|
|
389
|
+
shallow={shallow}
|
|
390
|
+
prefetch={prefetch}
|
|
391
|
+
>
|
|
392
392
|
<ItemWithLabelIconAndDescription
|
|
393
393
|
label={label}
|
|
394
394
|
icon={icon}
|
|
@@ -398,9 +398,9 @@ const DropdownMenuItem = React.forwardRef<
|
|
|
398
398
|
>
|
|
399
399
|
{children}
|
|
400
400
|
</ItemWithLabelIconAndDescription>
|
|
401
|
-
</
|
|
402
|
-
</
|
|
403
|
-
</
|
|
401
|
+
</LinkWrapper>
|
|
402
|
+
</div>
|
|
403
|
+
</DropdownMenuPrimitive.Item>
|
|
404
404
|
);
|
|
405
405
|
}
|
|
406
406
|
);
|
|
@@ -22,6 +22,11 @@ interface MultiPageSheetPage {
|
|
|
22
22
|
content: React.ReactNode;
|
|
23
23
|
fixedContent?: React.ReactNode;
|
|
24
24
|
footerContent?: React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* Remove the default ScrollArea in the SheetContainer.
|
|
27
|
+
* To be used if you want to manage the scroll yourself
|
|
28
|
+
*/
|
|
29
|
+
noScroll?: boolean;
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
interface MultiPageSheetProps {
|
|
@@ -231,6 +236,7 @@ const MultiPageSheetContent = React.forwardRef<
|
|
|
231
236
|
)}
|
|
232
237
|
<SheetContainer
|
|
233
238
|
className={currentPage.fixedContent ? "s-flex-1" : undefined}
|
|
239
|
+
noScroll={currentPage.noScroll}
|
|
234
240
|
>
|
|
235
241
|
{currentPage.content}
|
|
236
242
|
</SheetContainer>
|
package/src/components/Sheet.tsx
CHANGED
|
@@ -180,18 +180,25 @@ const SheetHeader = ({
|
|
|
180
180
|
);
|
|
181
181
|
SheetHeader.displayName = "SheetHeader";
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
interface SheetContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
184
|
+
noScroll?: boolean;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const SheetContainer = ({ children, noScroll }: SheetContainerProps) => {
|
|
188
|
+
const ScrollContainer = noScroll ? React.Fragment : ScrollArea;
|
|
184
189
|
return (
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
190
|
+
<ScrollContainer>
|
|
191
|
+
<div
|
|
192
|
+
className={cn(
|
|
193
|
+
"s-h-full s-w-full s-flex-grow",
|
|
194
|
+
"s-border-t s-border-border/60 s-transition-all s-duration-300 dark:s-border-border-night/60"
|
|
195
|
+
)}
|
|
196
|
+
>
|
|
197
|
+
<div className="s-relative s-flex s-h-full s-flex-col s-gap-5 s-p-5 s-text-left s-text-sm s-text-foreground dark:s-text-foreground-night">
|
|
198
|
+
{children}
|
|
199
|
+
</div>
|
|
193
200
|
</div>
|
|
194
|
-
</
|
|
201
|
+
</ScrollContainer>
|
|
195
202
|
);
|
|
196
203
|
};
|
|
197
204
|
SheetContainer.displayName = "SheetContainer";
|
|
@@ -607,6 +607,67 @@ export const ScrollableDataTableExample = () => {
|
|
|
607
607
|
);
|
|
608
608
|
};
|
|
609
609
|
|
|
610
|
+
export const ScrollableDataTableFullHeightExample = () => {
|
|
611
|
+
const [filter, setFilter] = useState("");
|
|
612
|
+
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
|
613
|
+
const [data, setData] = useState(() => createData(0, 50));
|
|
614
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
615
|
+
|
|
616
|
+
// Load more data when user scrolls to bottom
|
|
617
|
+
const loadMore = useCallback(() => {
|
|
618
|
+
setIsLoading(true);
|
|
619
|
+
|
|
620
|
+
// Simulate API call delay
|
|
621
|
+
setTimeout(() => {
|
|
622
|
+
setData((prevData) => [...prevData, ...createData(prevData.length, 50)]);
|
|
623
|
+
setIsLoading(false);
|
|
624
|
+
}, 1000);
|
|
625
|
+
}, []);
|
|
626
|
+
|
|
627
|
+
const columnsWithSize = columns.map((column, index) => {
|
|
628
|
+
return { ...column, meta: { sizeRatio: index % 2 === 0 ? 15 : 10 } };
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
const columnsWithSelection: ColumnDef<Data>[] = useMemo(
|
|
632
|
+
() => [createSelectionColumn<Data>(), ...columnsWithSize],
|
|
633
|
+
[]
|
|
634
|
+
);
|
|
635
|
+
return (
|
|
636
|
+
<div className="s-flex s-w-full s-max-w-4xl s-flex-col s-gap-6">
|
|
637
|
+
<h3 className="s-text-lg s-font-medium">
|
|
638
|
+
Virtualized ScrollableDataTable with Infinite Scrolling based on parent
|
|
639
|
+
height
|
|
640
|
+
</h3>
|
|
641
|
+
|
|
642
|
+
<div className="s-flex s-h-[400px] s-flex-col s-gap-4">
|
|
643
|
+
<Input
|
|
644
|
+
name="filter"
|
|
645
|
+
placeholder="Filter"
|
|
646
|
+
value={filter}
|
|
647
|
+
onChange={(e) => setFilter(e.target.value)}
|
|
648
|
+
/>
|
|
649
|
+
|
|
650
|
+
<ScrollableDataTable
|
|
651
|
+
data={data}
|
|
652
|
+
filter={filter}
|
|
653
|
+
filterColumn="name"
|
|
654
|
+
columns={columnsWithSelection}
|
|
655
|
+
onLoadMore={loadMore}
|
|
656
|
+
isLoading={isLoading}
|
|
657
|
+
maxHeight
|
|
658
|
+
rowSelection={rowSelection}
|
|
659
|
+
setRowSelection={setRowSelection}
|
|
660
|
+
enableRowSelection={true}
|
|
661
|
+
/>
|
|
662
|
+
|
|
663
|
+
<div className="s-text-sm s-text-muted-foreground">
|
|
664
|
+
Loaded {data.length} rows. Scroll to the bottom to load more.
|
|
665
|
+
</div>
|
|
666
|
+
</div>
|
|
667
|
+
</div>
|
|
668
|
+
);
|
|
669
|
+
};
|
|
670
|
+
|
|
610
671
|
export const DataTableWithRowSelectionExample = () => {
|
|
611
672
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
|
612
673
|
const [data] = useState<Data[]>(() => createData(0, 10));
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import
|
|
2
|
+
import type { ColumnDef } from "@tanstack/react-table";
|
|
3
|
+
import React, { useCallback, useState } from "react";
|
|
3
4
|
|
|
4
5
|
import { Button } from "@sparkle/components/Button";
|
|
6
|
+
import { ScrollableDataTable } from "@sparkle/components/DataTable";
|
|
5
7
|
import {
|
|
6
8
|
MultiPageSheet,
|
|
7
9
|
MultiPageSheetContent,
|
|
@@ -466,3 +468,214 @@ export const WithConditionalNavigation: Story = {
|
|
|
466
468
|
);
|
|
467
469
|
},
|
|
468
470
|
};
|
|
471
|
+
|
|
472
|
+
// Sample data types for the ScrollableDataTable
|
|
473
|
+
interface UserData {
|
|
474
|
+
id: string;
|
|
475
|
+
name: string;
|
|
476
|
+
email: string;
|
|
477
|
+
role: string;
|
|
478
|
+
status: string;
|
|
479
|
+
lastActive: string;
|
|
480
|
+
onClick?: () => void;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Generate random user data
|
|
484
|
+
const generateRandomUsers = (
|
|
485
|
+
count: number,
|
|
486
|
+
startId: number = 0
|
|
487
|
+
): UserData[] => {
|
|
488
|
+
const roles = ["Admin", "User", "Manager", "Developer", "Designer"];
|
|
489
|
+
const statuses = ["Active", "Inactive", "Pending"];
|
|
490
|
+
const firstNames = [
|
|
491
|
+
"John",
|
|
492
|
+
"Jane",
|
|
493
|
+
"Mike",
|
|
494
|
+
"Sarah",
|
|
495
|
+
"David",
|
|
496
|
+
"Lisa",
|
|
497
|
+
"Tom",
|
|
498
|
+
"Anna",
|
|
499
|
+
"Chris",
|
|
500
|
+
"Emma",
|
|
501
|
+
];
|
|
502
|
+
const lastNames = [
|
|
503
|
+
"Smith",
|
|
504
|
+
"Johnson",
|
|
505
|
+
"Williams",
|
|
506
|
+
"Brown",
|
|
507
|
+
"Jones",
|
|
508
|
+
"Garcia",
|
|
509
|
+
"Miller",
|
|
510
|
+
"Davis",
|
|
511
|
+
"Rodriguez",
|
|
512
|
+
"Martinez",
|
|
513
|
+
];
|
|
514
|
+
|
|
515
|
+
return Array.from({ length: count }, (_, index) => {
|
|
516
|
+
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)];
|
|
517
|
+
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
|
|
518
|
+
const id = (startId + index + 1).toString();
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
id,
|
|
522
|
+
name: `${firstName} ${lastName}`,
|
|
523
|
+
email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}@example.com`,
|
|
524
|
+
role: roles[Math.floor(Math.random() * roles.length)],
|
|
525
|
+
status: statuses[Math.floor(Math.random() * statuses.length)],
|
|
526
|
+
lastActive: new Date(
|
|
527
|
+
Date.now() - Math.floor(Math.random() * 30) * 24 * 60 * 60 * 1000
|
|
528
|
+
).toLocaleDateString(),
|
|
529
|
+
onClick: () => alert(`Clicked on user: ${firstName} ${lastName}`),
|
|
530
|
+
};
|
|
531
|
+
});
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
export const WithScrollableDataTable: Story = {
|
|
535
|
+
render() {
|
|
536
|
+
const [currentPageId, setCurrentPageId] = useState("users");
|
|
537
|
+
const [users, setUsers] = useState<UserData[]>(() =>
|
|
538
|
+
generateRandomUsers(50)
|
|
539
|
+
);
|
|
540
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
541
|
+
const [hasMore, setHasMore] = useState(true);
|
|
542
|
+
|
|
543
|
+
// Define columns for the data table
|
|
544
|
+
const columns: ColumnDef<UserData>[] = [
|
|
545
|
+
{
|
|
546
|
+
accessorKey: "name",
|
|
547
|
+
header: "Name",
|
|
548
|
+
cell: ({ row }) => (
|
|
549
|
+
<div className="s-font-medium">{row.getValue("name")}</div>
|
|
550
|
+
),
|
|
551
|
+
meta: { sizeRatio: 25 },
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
accessorKey: "email",
|
|
555
|
+
header: "Email",
|
|
556
|
+
cell: ({ row }) => (
|
|
557
|
+
<div className="s-text-muted-foreground">{row.getValue("email")}</div>
|
|
558
|
+
),
|
|
559
|
+
meta: { sizeRatio: 30 },
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
accessorKey: "role",
|
|
563
|
+
header: "Role",
|
|
564
|
+
cell: ({ row }) => (
|
|
565
|
+
<div className="s-inline-flex s-rounded-full s-bg-blue-100 s-px-2 s-py-1 s-text-xs s-font-semibold s-text-blue-800">
|
|
566
|
+
{row.getValue("role")}
|
|
567
|
+
</div>
|
|
568
|
+
),
|
|
569
|
+
meta: { sizeRatio: 15 },
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
accessorKey: "status",
|
|
573
|
+
header: "Status",
|
|
574
|
+
cell: ({ row }) => {
|
|
575
|
+
const status = row.getValue("status") as string;
|
|
576
|
+
const colorClass =
|
|
577
|
+
status === "Active"
|
|
578
|
+
? "s-bg-green-100 s-text-green-800"
|
|
579
|
+
: status === "Inactive"
|
|
580
|
+
? "s-bg-red-100 s-text-red-800"
|
|
581
|
+
: "s-bg-yellow-100 s-text-yellow-800";
|
|
582
|
+
|
|
583
|
+
return (
|
|
584
|
+
<div
|
|
585
|
+
className={`s-inline-flex s-rounded-full s-px-2 s-py-1 s-text-xs s-font-semibold ${colorClass}`}
|
|
586
|
+
>
|
|
587
|
+
{status}
|
|
588
|
+
</div>
|
|
589
|
+
);
|
|
590
|
+
},
|
|
591
|
+
meta: { sizeRatio: 15 },
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
accessorKey: "lastActive",
|
|
595
|
+
header: "Last Active",
|
|
596
|
+
cell: ({ row }) => (
|
|
597
|
+
<div className="s-text-sm s-text-muted-foreground">
|
|
598
|
+
{row.getValue("lastActive")}
|
|
599
|
+
</div>
|
|
600
|
+
),
|
|
601
|
+
meta: { sizeRatio: 15 },
|
|
602
|
+
},
|
|
603
|
+
];
|
|
604
|
+
|
|
605
|
+
// Handle infinite loading
|
|
606
|
+
const handleLoadMore = useCallback(() => {
|
|
607
|
+
if (isLoading || !hasMore) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
setIsLoading(true);
|
|
612
|
+
|
|
613
|
+
// Simulate API call delay
|
|
614
|
+
setTimeout(() => {
|
|
615
|
+
const newUsers = generateRandomUsers(25, users.length);
|
|
616
|
+
setUsers((prev) => [...prev, ...newUsers]);
|
|
617
|
+
setIsLoading(false);
|
|
618
|
+
|
|
619
|
+
// Stop loading more after reaching 200 items for demo purposes
|
|
620
|
+
if (users.length >= 175) {
|
|
621
|
+
setHasMore(false);
|
|
622
|
+
}
|
|
623
|
+
}, 1000);
|
|
624
|
+
}, [isLoading, hasMore, users.length]);
|
|
625
|
+
|
|
626
|
+
const handleSave = () => {
|
|
627
|
+
alert("User data saved!");
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
const scrollableDataTablePages: MultiPageSheetPage[] = [
|
|
631
|
+
{
|
|
632
|
+
id: "users",
|
|
633
|
+
title: "User Management",
|
|
634
|
+
description: "Manage users with infinite scroll",
|
|
635
|
+
icon: UserIcon,
|
|
636
|
+
noScroll: true,
|
|
637
|
+
content: (
|
|
638
|
+
<div className="s-flex s-h-full s-flex-col s-space-y-4">
|
|
639
|
+
<div className="s-flex-shrink-0">
|
|
640
|
+
<h3 className="s-mb-2 s-text-lg s-font-semibold">
|
|
641
|
+
Users Database
|
|
642
|
+
</h3>
|
|
643
|
+
<p className="s-text-sm s-text-muted-foreground">
|
|
644
|
+
Browse through all users with infinite scrolling. Click on any
|
|
645
|
+
row to view details.
|
|
646
|
+
</p>
|
|
647
|
+
</div>
|
|
648
|
+
<ScrollableDataTable
|
|
649
|
+
className="s-min-h-0"
|
|
650
|
+
data={users}
|
|
651
|
+
columns={columns}
|
|
652
|
+
maxHeight={true}
|
|
653
|
+
onLoadMore={hasMore ? handleLoadMore : undefined}
|
|
654
|
+
isLoading={isLoading}
|
|
655
|
+
enableRowSelection={false}
|
|
656
|
+
/>
|
|
657
|
+
<div className="s-flex-shrink-0 s-text-xs s-text-muted-foreground">
|
|
658
|
+
Showing {users.length} users{" "}
|
|
659
|
+
{hasMore ? "(loading more available)" : "(all users loaded)"}
|
|
660
|
+
</div>
|
|
661
|
+
</div>
|
|
662
|
+
),
|
|
663
|
+
},
|
|
664
|
+
];
|
|
665
|
+
|
|
666
|
+
return (
|
|
667
|
+
<MultiPageSheet>
|
|
668
|
+
<MultiPageSheetTrigger asChild>
|
|
669
|
+
<Button label="Open User Management" />
|
|
670
|
+
</MultiPageSheetTrigger>
|
|
671
|
+
<MultiPageSheetContent
|
|
672
|
+
pages={scrollableDataTablePages}
|
|
673
|
+
currentPageId={currentPageId}
|
|
674
|
+
onPageChange={setCurrentPageId}
|
|
675
|
+
size="xl"
|
|
676
|
+
onSave={handleSave}
|
|
677
|
+
/>
|
|
678
|
+
</MultiPageSheet>
|
|
679
|
+
);
|
|
680
|
+
},
|
|
681
|
+
};
|