@dust-tt/sparkle 0.2.589-rc-1 → 0.2.589-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.
@@ -327,7 +327,7 @@ export function DataTable<TData extends TBaseData>({
327
327
 
328
328
  export interface ScrollableDataTableProps<TData extends TBaseData>
329
329
  extends DataTableProps<TData> {
330
- maxHeight?: string;
330
+ maxHeight?: string | boolean;
331
331
  onLoadMore?: () => void;
332
332
  isLoading?: boolean;
333
333
  }
@@ -343,7 +343,7 @@ export function ScrollableDataTable<TData extends TBaseData>({
343
343
  className,
344
344
  widthClassName = "s-w-full",
345
345
  columnsBreakpoints = {},
346
- maxHeight = "s-h-100",
346
+ maxHeight,
347
347
  onLoadMore,
348
348
  isLoading = false,
349
349
  rowSelection,
@@ -480,24 +480,95 @@ export function ScrollableDataTable<TData extends TBaseData>({
480
480
  }, [onLoadMore, isLoading]);
481
481
 
482
482
  return (
483
- <div className={cn("s-flex s-flex-col s-gap-2", className, widthClassName)}>
484
- <div
485
- className={cn(
486
- "s-relative s-overflow-y-auto s-overflow-x-hidden",
487
- maxHeight
488
- )}
489
- ref={tableContainerRef}
490
- >
491
- <div className="s-relative">
492
- <DataTable.Root className="s-w-full s-table-fixed">
493
- <DataTable.Header className="s-sticky s-top-0 s-z-20 s-bg-white s-shadow-sm dark:s-bg-background-night">
494
- {table.getHeaderGroups().map((headerGroup) => (
483
+ <div
484
+ className={cn(
485
+ "s-relative s-overflow-y-auto s-overflow-x-hidden",
486
+ className,
487
+ widthClassName,
488
+ maxHeight === true
489
+ ? "s-flex-1"
490
+ : typeof maxHeight === "string"
491
+ ? maxHeight
492
+ : "s-max-h-100"
493
+ )}
494
+ ref={tableContainerRef}
495
+ >
496
+ <div className="s-relative">
497
+ <DataTable.Root className="s-w-full s-table-fixed">
498
+ <DataTable.Header className="s-sticky s-top-0 s-z-20 s-bg-white s-shadow-sm dark:s-bg-background-night">
499
+ {table.getHeaderGroups().map((headerGroup) => (
500
+ <DataTable.Row
501
+ key={headerGroup.id}
502
+ widthClassName={widthClassName}
503
+ >
504
+ {headerGroup.headers.map((header) => {
505
+ const breakpoint = columnsBreakpoints[header.id];
506
+ if (
507
+ !windowSize.width ||
508
+ !shouldRenderColumn(windowSize.width, breakpoint)
509
+ ) {
510
+ return null;
511
+ }
512
+
513
+ return (
514
+ <DataTable.Head
515
+ column={header.column}
516
+ key={header.id}
517
+ className="s-max-w-0"
518
+ style={{
519
+ width: columnSizing[header.id],
520
+ minWidth: columnSizing[header.id],
521
+ }}
522
+ >
523
+ <div className="s-flex s-w-full s-items-center s-space-x-1">
524
+ <span className="s-truncate">
525
+ {flexRender(
526
+ header.column.columnDef.header,
527
+ header.getContext()
528
+ )}
529
+ </span>
530
+ </div>
531
+ </DataTable.Head>
532
+ );
533
+ })}
534
+ </DataTable.Row>
535
+ ))}
536
+ </DataTable.Header>
537
+ <DataTable.Body
538
+ className="s-relative s-w-full"
539
+ style={{
540
+ height: `${rowVirtualizer.getTotalSize()}px`,
541
+ }}
542
+ >
543
+ {rowVirtualizer.getVirtualItems().map((virtualRow) => {
544
+ const row = rows[virtualRow.index];
545
+ const handleRowClick = () => {
546
+ if (enableRowSelection && row.getCanSelect()) {
547
+ row.toggleSelected(
548
+ !enableMultiRowSelection ? true : undefined
549
+ );
550
+ }
551
+ row.original.onClick?.();
552
+ };
553
+
554
+ return (
495
555
  <DataTable.Row
496
- key={headerGroup.id}
556
+ key={row.id}
557
+ id={row.id}
497
558
  widthClassName={widthClassName}
559
+ onClick={
560
+ enableRowSelection ? handleRowClick : row.original.onClick
561
+ }
562
+ className="s-absolute s-w-full"
563
+ {...(enableRowSelection && {
564
+ "data-selected": row.getIsSelected(),
565
+ })}
566
+ style={{
567
+ transform: `translateY(${virtualRow.start}px)`,
568
+ }}
498
569
  >
499
- {headerGroup.headers.map((header) => {
500
- const breakpoint = columnsBreakpoints[header.id];
570
+ {row.getVisibleCells().map((cell) => {
571
+ const breakpoint = columnsBreakpoints[cell.column.id];
501
572
  if (
502
573
  !windowSize.width ||
503
574
  !shouldRenderColumn(windowSize.width, breakpoint)
@@ -506,114 +577,47 @@ export function ScrollableDataTable<TData extends TBaseData>({
506
577
  }
507
578
 
508
579
  return (
509
- <DataTable.Head
510
- column={header.column}
511
- key={header.id}
580
+ <DataTable.Cell
581
+ column={cell.column}
582
+ key={cell.id}
583
+ id={cell.id}
512
584
  className="s-max-w-0"
513
585
  style={{
514
- width: columnSizing[header.id],
515
- minWidth: columnSizing[header.id],
586
+ width: columnSizing[cell.column.id],
587
+ minWidth: columnSizing[cell.column.id],
516
588
  }}
517
589
  >
518
- <div className="s-flex s-w-full s-items-center s-space-x-1">
590
+ <div className="s-flex s-items-center s-space-x-1">
519
591
  <span className="s-truncate">
520
592
  {flexRender(
521
- header.column.columnDef.header,
522
- header.getContext()
593
+ cell.column.columnDef.cell,
594
+ cell.getContext()
523
595
  )}
524
596
  </span>
525
597
  </div>
526
- </DataTable.Head>
598
+ </DataTable.Cell>
527
599
  );
528
600
  })}
529
601
  </DataTable.Row>
530
- ))}
531
- </DataTable.Header>
532
- <DataTable.Body
533
- className="s-relative s-w-full"
534
- style={{
535
- height: `${rowVirtualizer.getTotalSize()}px`,
536
- }}
537
- >
538
- {rowVirtualizer.getVirtualItems().map((virtualRow) => {
539
- const row = rows[virtualRow.index];
540
- const handleRowClick = () => {
541
- if (enableRowSelection && row.getCanSelect()) {
542
- row.toggleSelected(
543
- !enableMultiRowSelection ? true : undefined
544
- );
545
- }
546
- row.original.onClick?.();
547
- };
548
-
549
- return (
550
- <DataTable.Row
551
- key={row.id}
552
- id={row.id}
553
- widthClassName={widthClassName}
554
- onClick={
555
- enableRowSelection ? handleRowClick : row.original.onClick
556
- }
557
- className="s-absolute s-w-full"
558
- {...(enableRowSelection && {
559
- "data-selected": row.getIsSelected(),
560
- })}
561
- style={{
562
- transform: `translateY(${virtualRow.start}px)`,
563
- }}
564
- >
565
- {row.getVisibleCells().map((cell) => {
566
- const breakpoint = columnsBreakpoints[cell.column.id];
567
- if (
568
- !windowSize.width ||
569
- !shouldRenderColumn(windowSize.width, breakpoint)
570
- ) {
571
- return null;
572
- }
573
-
574
- return (
575
- <DataTable.Cell
576
- column={cell.column}
577
- key={cell.id}
578
- id={cell.id}
579
- className="s-max-w-0"
580
- style={{
581
- width: columnSizing[cell.column.id],
582
- minWidth: columnSizing[cell.column.id],
583
- }}
584
- >
585
- <div className="s-flex s-items-center s-space-x-1">
586
- <span className="s-truncate">
587
- {flexRender(
588
- cell.column.columnDef.cell,
589
- cell.getContext()
590
- )}
591
- </span>
592
- </div>
593
- </DataTable.Cell>
594
- );
595
- })}
596
- </DataTable.Row>
597
- );
598
- })}
599
- </DataTable.Body>
600
- </DataTable.Root>
601
- {/*sentinel div used for the intersection observer*/}
602
- <div
603
- ref={loadMoreRef}
604
- className="s-absolute s-bottom-0 s-h-1 s-w-full"
605
- />
606
- </div>
602
+ );
603
+ })}
604
+ </DataTable.Body>
605
+ </DataTable.Root>
606
+ {/*sentinel div used for the intersection observer*/}
607
+ <div
608
+ ref={loadMoreRef}
609
+ className="s-absolute s-bottom-0 s-h-1 s-w-full"
610
+ />
611
+ </div>
607
612
 
608
- {isLoading && (
609
- <div className="s-sticky s-bottom-0 s-left-0 s-right-0 s-flex s-justify-center s-bg-white/80 s-py-2 s-backdrop-blur-sm dark:s-bg-background-night/80">
610
- <div className="s-flex s-items-center s-gap-2 s-text-sm s-text-muted-foreground">
611
- <Spinner size="xs" />
612
- <span>Loading more data...</span>
613
- </div>
613
+ {isLoading && (
614
+ <div className="s-sticky s-bottom-0 s-left-0 s-right-0 s-flex s-justify-center s-bg-white/80 s-py-2 s-backdrop-blur-sm dark:s-bg-background-night/80">
615
+ <div className="s-flex s-items-center s-gap-2 s-text-sm s-text-muted-foreground">
616
+ <Spinner size="xs" />
617
+ <span>Loading more data...</span>
614
618
  </div>
615
- )}
616
- </div>
619
+ </div>
620
+ )}
617
621
  </div>
618
622
  );
619
623
  }
@@ -0,0 +1,18 @@
1
+ import type { SVGProps } from "react";
2
+ import * as React from "react";
3
+ const SvgBell = (props: SVGProps<SVGSVGElement>) => (
4
+ <svg
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ width="1em"
7
+ height="1em"
8
+ fill="none"
9
+ viewBox="0 0 24 24"
10
+ {...props}
11
+ >
12
+ <path
13
+ fill="currentColor"
14
+ d="M20 17h2v2H2v-2h2v-7a8 8 0 1 1 16 0v7ZM9 21h6v2H9v-2Z"
15
+ />
16
+ </svg>
17
+ );
18
+ export default SvgBell;
@@ -13,6 +13,7 @@ export { default as ArrowUpOnSquareIcon } from "./ArrowUpOnSquare";
13
13
  export { default as ArrowUpSIcon } from "./ArrowUpS";
14
14
  export { default as AttachmentIcon } from "./Attachment";
15
15
  export { default as BarChartIcon } from "./BarChart";
16
+ export { default as BellIcon } from "./Bell";
16
17
  export { default as BoltIcon } from "./Bolt";
17
18
  export { default as BookOpenIcon } from "./BookOpen";
18
19
  export { default as BracesIcon } from "./Braces";
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M20 17H22V19H2V17H4V10C4 5.58172 7.58172 2 12 2C16.4183 2 20 5.58172 20 10V17ZM9 21H15V23H9V21Z" fill="#111418"/>
3
+ </svg>
@@ -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));