@stackframe/stack-ui 2.5.18 → 2.5.19
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/CHANGELOG.md +8 -0
- package/dist/components/data-table/cells.d.ts +27 -0
- package/dist/components/data-table/cells.js +38 -0
- package/dist/components/data-table/column-header.d.ts +8 -0
- package/dist/components/data-table/column-header.js +10 -0
- package/dist/components/data-table/data-table.d.ts +10 -0
- package/dist/components/data-table/data-table.js +39 -0
- package/dist/components/data-table/faceted-filter.d.ts +15 -0
- package/dist/components/data-table/faceted-filter.js +26 -0
- package/dist/components/data-table/index.d.ts +9 -0
- package/dist/components/data-table/index.js +9 -0
- package/dist/components/data-table/pagination.d.ts +6 -0
- package/dist/components/data-table/pagination.js +10 -0
- package/dist/components/data-table/toolbar-items.d.ts +6 -0
- package/dist/components/data-table/toolbar-items.js +5 -0
- package/dist/components/data-table/toolbar.d.ts +8 -0
- package/dist/components/data-table/toolbar.js +45 -0
- package/dist/components/data-table/utils.d.ts +2 -0
- package/dist/components/data-table/utils.js +6 -0
- package/dist/components/data-table/view-options.d.ts +6 -0
- package/dist/components/data-table/view-options.js +12 -0
- package/dist/components/editable-text.d.ts +4 -0
- package/dist/components/editable-text.js +16 -0
- package/dist/components/ui/container.js +4 -15
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare function TextCell(props: {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
size?: number;
|
|
5
|
+
icon?: React.ReactNode;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function AvatarCell(props: {
|
|
8
|
+
src?: string;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function DateCell(props: {
|
|
11
|
+
date: Date;
|
|
12
|
+
ignoreAfterYears?: number;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
type ActionItem = '-' | {
|
|
15
|
+
item: React.ReactNode;
|
|
16
|
+
onClick: (e: React.MouseEvent) => void | Promise<void>;
|
|
17
|
+
danger?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare function ActionCell(props: {
|
|
20
|
+
items?: ActionItem[];
|
|
21
|
+
invisible?: boolean;
|
|
22
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export declare function BadgeCell(props: {
|
|
24
|
+
badges: string[];
|
|
25
|
+
size?: number;
|
|
26
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { DotsHorizontalIcon } from "@radix-ui/react-icons";
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { SimpleTooltip, cn, Avatar, AvatarImage, Badge, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "../..";
|
|
6
|
+
export function TextCell(props) {
|
|
7
|
+
const textRef = useRef(null);
|
|
8
|
+
const [isOverflowing, setIsOverflowing] = useState(false);
|
|
9
|
+
const overflowStyle = "text-ellipsis text-nowrap overflow-x-hidden";
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const checkOverflow = () => {
|
|
12
|
+
if (textRef.current) {
|
|
13
|
+
const isOverflowing = textRef.current.scrollWidth > textRef.current.clientWidth;
|
|
14
|
+
setIsOverflowing(isOverflowing);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
checkOverflow();
|
|
18
|
+
window.addEventListener('resize', checkOverflow);
|
|
19
|
+
return () => {
|
|
20
|
+
window.removeEventListener('resize', checkOverflow);
|
|
21
|
+
};
|
|
22
|
+
}, []);
|
|
23
|
+
return (_jsx("div", { className: "relative", style: { minWidth: props.size }, children: _jsxs("div", { className: "flex items-center gap-2 absolute inset-0", children: [_jsx("div", { className: overflowStyle, ref: textRef, children: isOverflowing ? (_jsx(SimpleTooltip, { tooltip: props.children, children: _jsx("div", { className: overflowStyle, children: props.children }) })) : props.children }), props.icon && _jsx("div", { children: props.icon })] }) }));
|
|
24
|
+
}
|
|
25
|
+
export function AvatarCell(props) {
|
|
26
|
+
return (_jsx(Avatar, { className: "h-6 w-6", children: _jsx(AvatarImage, { src: props.src }) }));
|
|
27
|
+
}
|
|
28
|
+
export function DateCell(props) {
|
|
29
|
+
const ignore = !!props.ignoreAfterYears && new Date(new Date().setFullYear(new Date().getFullYear() + props.ignoreAfterYears)) < props.date;
|
|
30
|
+
const timeString = props.date.toLocaleTimeString([], { year: 'numeric', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' });
|
|
31
|
+
return (_jsx(TextCell, { size: 140, children: ignore ? 'Never' : timeString }));
|
|
32
|
+
}
|
|
33
|
+
export function ActionCell(props) {
|
|
34
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", className: cn("flex h-8 w-8 p-0 data-[state=open]:bg-muted", props.invisible && "invisible"), children: [_jsx(DotsHorizontalIcon, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Open menu" })] }) }), _jsx(DropdownMenuContent, { align: "end", className: "w-[160px]", children: props.items?.map((item, index) => item === '-' ? (_jsx(DropdownMenuSeparator, {}, index)) : (_jsx(DropdownMenuItem, { onClick: item.onClick, className: item.danger ? "text-destructive" : "", children: item.item }, index))) })] }));
|
|
35
|
+
}
|
|
36
|
+
export function BadgeCell(props) {
|
|
37
|
+
return (_jsx("div", { className: "flex items-center gap-1 flex-wrap", children: props.badges.map((badge, index) => (_jsx(Badge, { variant: "secondary", children: badge }, index))) }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { Column } from "@tanstack/react-table";
|
|
3
|
+
interface DataTableColumnHeaderProps<TData, TValue> extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
column: Column<TData, TValue>;
|
|
5
|
+
columnTitle: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
export declare function DataTableColumnHeader<TData, TValue>({ column, columnTitle, className, }: DataTableColumnHeaderProps<TData, TValue>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../..";
|
|
3
|
+
import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@stackframe/stack-ui";
|
|
4
|
+
import { EyeOff, ArrowUp, ArrowDown } from "lucide-react";
|
|
5
|
+
function Item(props) {
|
|
6
|
+
return (_jsx(DropdownMenuItem, { onClick: props.onClick, children: _jsxs("div", { className: "flex items-center", children: [_jsx(props.icon, { className: "mr-2 h-3.5 w-3.5 text-muted-foreground/70" }), props.children] }) }));
|
|
7
|
+
}
|
|
8
|
+
export function DataTableColumnHeader({ column, columnTitle, className, }) {
|
|
9
|
+
return (_jsx("div", { className: cn("flex items-center space-x-2", className), children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "-ml-3 h-8 data-[state=open]:bg-accent", children: [_jsx("span", { children: columnTitle }), column.getIsSorted() === "desc" ? (_jsx(ArrowDown, { className: "ml-2 h-4 w-4" })) : column.getIsSorted() === "asc" ? (_jsx(ArrowUp, { className: "ml-2 h-4 w-4" })) : null] }) }), _jsxs(DropdownMenuContent, { align: "start", children: [column.getCanSort() && (_jsxs(_Fragment, { children: [_jsx(Item, { icon: ArrowUp, onClick: () => column.toggleSorting(false), children: "Asc" }), _jsx(Item, { icon: ArrowDown, onClick: () => column.toggleSorting(true), children: "Desc" })] })), _jsx(Item, { icon: EyeOff, onClick: () => column.toggleVisibility(false), children: "Hide" })] })] }) }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ColumnDef, Table as TableType, VisibilityState } from "@tanstack/react-table";
|
|
2
|
+
import React from "react";
|
|
3
|
+
interface DataTableProps<TData, TValue> {
|
|
4
|
+
columns: ColumnDef<TData, TValue>[];
|
|
5
|
+
data: TData[];
|
|
6
|
+
toolbarRender?: (table: TableType<TData>) => React.ReactNode;
|
|
7
|
+
defaultVisibility?: VisibilityState;
|
|
8
|
+
}
|
|
9
|
+
export declare function DataTable<TData, TValue>({ columns, data, toolbarRender, defaultVisibility, }: DataTableProps<TData, TValue>): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@stackframe/stack-ui";
|
|
4
|
+
import { flexRender, getCoreRowModel, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from "@tanstack/react-table";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { DataTablePagination } from "./pagination";
|
|
7
|
+
import { DataTableToolbar } from "./toolbar";
|
|
8
|
+
export function DataTable({ columns, data, toolbarRender, defaultVisibility, }) {
|
|
9
|
+
const [rowSelection, setRowSelection] = React.useState({});
|
|
10
|
+
const [columnVisibility, setColumnVisibility] = React.useState(defaultVisibility || {});
|
|
11
|
+
const [columnFilters, setColumnFilters] = React.useState([]);
|
|
12
|
+
const [sorting, setSorting] = React.useState([]);
|
|
13
|
+
const table = useReactTable({
|
|
14
|
+
data,
|
|
15
|
+
columns,
|
|
16
|
+
state: {
|
|
17
|
+
sorting,
|
|
18
|
+
columnVisibility,
|
|
19
|
+
rowSelection,
|
|
20
|
+
columnFilters,
|
|
21
|
+
},
|
|
22
|
+
enableRowSelection: true,
|
|
23
|
+
onRowSelectionChange: setRowSelection,
|
|
24
|
+
onSortingChange: setSorting,
|
|
25
|
+
onColumnFiltersChange: setColumnFilters,
|
|
26
|
+
onColumnVisibilityChange: setColumnVisibility,
|
|
27
|
+
getCoreRowModel: getCoreRowModel(),
|
|
28
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
29
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
30
|
+
getSortedRowModel: getSortedRowModel(),
|
|
31
|
+
getFacetedRowModel: getFacetedRowModel(),
|
|
32
|
+
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
33
|
+
});
|
|
34
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(DataTableToolbar, { table: table, toolbarRender: toolbarRender }), _jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => (_jsx(TableRow, { children: headerGroup.headers.map((header) => {
|
|
35
|
+
return (_jsx(TableHead, { colSpan: header.colSpan, children: header.isPlaceholder
|
|
36
|
+
? null
|
|
37
|
+
: flexRender(header.column.columnDef.header, header.getContext()) }, header.id));
|
|
38
|
+
}) }, headerGroup.id))) }), _jsx(TableBody, { children: table.getRowModel().rows.length ? (table.getRowModel().rows.map((row) => (_jsx(TableRow, { "data-state": row.getIsSelected() && "selected", children: row.getVisibleCells().map((cell) => (_jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))) }, row.id)))) : (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length, className: "h-24 text-center", children: "No results." }) })) })] }) }), _jsx(DataTablePagination, { table: table })] }));
|
|
39
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Column } from "@tanstack/react-table";
|
|
2
|
+
import React from "react";
|
|
3
|
+
interface DataTableFacetedFilterProps<TData, TValue> {
|
|
4
|
+
column?: Column<TData, TValue>;
|
|
5
|
+
title?: string;
|
|
6
|
+
options: {
|
|
7
|
+
label: string;
|
|
8
|
+
value: string;
|
|
9
|
+
icon?: React.ComponentType<{
|
|
10
|
+
className?: string;
|
|
11
|
+
}>;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
export declare function DataTableFacetedFilter<TData, TValue>({ column, title, options, }: DataTableFacetedFilterProps<TData, TValue>): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../..";
|
|
3
|
+
import { CheckIcon } from "@radix-ui/react-icons";
|
|
4
|
+
import { Badge, Button, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, Popover, PopoverContent, PopoverTrigger, Separator } from "@stackframe/stack-ui";
|
|
5
|
+
import { ListFilter } from "lucide-react";
|
|
6
|
+
export function DataTableFacetedFilter({ column, title, options, }) {
|
|
7
|
+
const facets = column?.getFacetedUniqueValues();
|
|
8
|
+
const selectedValues = new Set(column?.getFilterValue());
|
|
9
|
+
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 border", children: [_jsx(ListFilter, { className: "mr-2 h-4 w-4 text-gray-500" }), title, selectedValues.size > 0 && (_jsxs(_Fragment, { children: [_jsx(Separator, { orientation: "vertical", className: "mx-2 h-4" }), _jsx(Badge, { variant: "secondary", className: "rounded-sm px-1 font-normal lg:hidden", children: selectedValues.size }), _jsx("div", { className: "hidden space-x-1 lg:flex", children: selectedValues.size > 2 ? (_jsxs(Badge, { variant: "secondary", className: "rounded-sm px-1 font-normal", children: [selectedValues.size, " selected"] })) : (options
|
|
10
|
+
.filter((option) => selectedValues.has(option.value))
|
|
11
|
+
.map((option) => (_jsx(Badge, { variant: "secondary", className: "rounded-sm px-1 font-normal", children: option.label }, option.value)))) })] }))] }) }), _jsx(PopoverContent, { className: "w-[200px] p-0", align: "start", children: _jsxs(Command, { children: [_jsx(CommandInput, { placeholder: title }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: "No results found." }), _jsx(CommandGroup, { children: options.map((option) => {
|
|
12
|
+
const isSelected = selectedValues.has(option.value);
|
|
13
|
+
return (_jsxs(CommandItem, { onSelect: () => {
|
|
14
|
+
if (isSelected) {
|
|
15
|
+
selectedValues.delete(option.value);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
selectedValues.add(option.value);
|
|
19
|
+
}
|
|
20
|
+
const filterValues = Array.from(selectedValues);
|
|
21
|
+
column?.setFilterValue(filterValues.length ? filterValues : undefined);
|
|
22
|
+
}, children: [_jsx("div", { className: cn("mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary", isSelected
|
|
23
|
+
? "bg-primary text-primary-foreground"
|
|
24
|
+
: "opacity-50 [&_svg]:invisible"), children: _jsx(CheckIcon, { className: cn("h-4 w-4") }) }), option.icon && (_jsx(option.icon, { className: "mr-2 h-4 w-4 text-muted-foreground" })), _jsx("span", { children: option.label }), facets?.get(option.value) && (_jsx("span", { className: "ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs", children: facets.get(option.value) }))] }, option.value));
|
|
25
|
+
}) }), selectedValues.size > 0 && (_jsxs(_Fragment, { children: [_jsx(CommandSeparator, {}), _jsx(CommandGroup, { children: _jsx(CommandItem, { onSelect: () => column?.setFilterValue(undefined), className: "justify-center text-center", children: "Clear filters" }) })] }))] })] }) })] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./cells";
|
|
2
|
+
export * from "./column-header";
|
|
3
|
+
export * from "./data-table";
|
|
4
|
+
export * from "./faceted-filter";
|
|
5
|
+
export * from "./pagination";
|
|
6
|
+
export * from "./toolbar-items";
|
|
7
|
+
export * from "./toolbar";
|
|
8
|
+
export * from "./utils";
|
|
9
|
+
export * from "./view-options";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./cells";
|
|
2
|
+
export * from "./column-header";
|
|
3
|
+
export * from "./data-table";
|
|
4
|
+
export * from "./faceted-filter";
|
|
5
|
+
export * from "./pagination";
|
|
6
|
+
export * from "./toolbar-items";
|
|
7
|
+
export * from "./toolbar";
|
|
8
|
+
export * from "./utils";
|
|
9
|
+
export * from "./view-options";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Table } from "@tanstack/react-table";
|
|
2
|
+
interface DataTablePaginationProps<TData> {
|
|
3
|
+
table: Table<TData>;
|
|
4
|
+
}
|
|
5
|
+
export declare function DataTablePagination<TData>({ table, }: DataTablePaginationProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronLeftIcon, ChevronRightIcon, DoubleArrowLeftIcon, DoubleArrowRightIcon, } from "@radix-ui/react-icons";
|
|
3
|
+
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@stackframe/stack-ui";
|
|
4
|
+
export function DataTablePagination({ table, }) {
|
|
5
|
+
return (_jsxs("div", { className: "flex items-center justify-between px-2 flex-col sm:flex-row gap-y-4 sm:gap-y-0", children: [_jsx("div", { className: "flex-1 text-sm text-muted-foreground", children: table.getFilteredSelectedRowModel().rows.length === 0 ?
|
|
6
|
+
`${table.getFilteredRowModel().rows.length} row(s) found` :
|
|
7
|
+
`${table.getFilteredSelectedRowModel().rows.length} of ${table.getFilteredRowModel().rows.length} row(s) selected` }), _jsxs("div", { className: "flex items-center gap-x-6 lg:gap-x-8 flex-col sm:flex-row gap-y-4 sm:gap-y-0", children: [_jsxs("div", { className: "flex items-center space-x-2", children: [_jsx("p", { className: "text-sm font-medium", children: "Rows per page" }), _jsxs(Select, { value: `${table.getState().pagination.pageSize}`, onValueChange: (value) => {
|
|
8
|
+
table.setPageSize(Number(value));
|
|
9
|
+
}, children: [_jsx(SelectTrigger, { className: "h-8 w-[70px]", children: _jsx(SelectValue, { placeholder: table.getState().pagination.pageSize }) }), _jsx(SelectContent, { side: "top", children: [10, 20, 30, 40, 50].map((pageSize) => (_jsx(SelectItem, { value: `${pageSize}`, children: pageSize }, pageSize))) })] })] }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex items-center justify-center text-sm font-medium", children: ["Page ", table.getState().pagination.pageIndex + 1, " of", " ", table.getPageCount()] }), _jsxs("div", { className: "flex items-center space-x-2", children: [_jsxs(Button, { variant: "outline", className: "hidden h-8 w-8 p-0 lg:flex", onClick: () => table.setPageIndex(0), disabled: !table.getCanPreviousPage(), children: [_jsx("span", { className: "sr-only", children: "Go to first page" }), _jsx(DoubleArrowLeftIcon, { className: "h-4 w-4" })] }), _jsxs(Button, { variant: "outline", className: "h-8 w-8 p-0", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [_jsx("span", { className: "sr-only", children: "Go to previous page" }), _jsx(ChevronLeftIcon, { className: "h-4 w-4" })] }), _jsxs(Button, { variant: "outline", className: "h-8 w-8 p-0", onClick: () => table.nextPage(), disabled: !table.getCanNextPage(), children: [_jsx("span", { className: "sr-only", children: "Go to next page" }), _jsx(ChevronRightIcon, { className: "h-4 w-4" })] }), _jsxs(Button, { variant: "outline", className: "hidden h-8 w-8 p-0 lg:flex", onClick: () => table.setPageIndex(table.getPageCount() - 1), disabled: !table.getCanNextPage(), children: [_jsx("span", { className: "sr-only", children: "Go to last page" }), _jsx(DoubleArrowRightIcon, { className: "h-4 w-4" })] })] })] })] })] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Input } from "../..";
|
|
3
|
+
export function SearchToolbarItem(props) {
|
|
4
|
+
return (_jsx(Input, { placeholder: props.placeholder, value: `${props.table.getColumn(props.keyName)?.getFilterValue() ?? ""}`, onChange: (event) => props.table.getColumn(props.keyName)?.setFilterValue(event.target.value), className: "h-8 w-[150px] lg:w-[250px]" }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { Table } from "@tanstack/react-table";
|
|
3
|
+
interface DataTableToolbarProps<TData> {
|
|
4
|
+
table: Table<TData>;
|
|
5
|
+
toolbarRender?: (table: Table<TData>) => React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
export declare function DataTableToolbar<TData>({ table, toolbarRender }: DataTableToolbarProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Cross2Icon } from "@radix-ui/react-icons";
|
|
4
|
+
import { Button } from "@stackframe/stack-ui";
|
|
5
|
+
import { DataTableViewOptions } from "./view-options";
|
|
6
|
+
import { DownloadIcon } from "lucide-react";
|
|
7
|
+
import { mkConfig, generateCsv, download } from 'export-to-csv';
|
|
8
|
+
export function DataTableToolbar({ table, toolbarRender }) {
|
|
9
|
+
const isFiltered = table.getState().columnFilters.length > 0;
|
|
10
|
+
const isSorted = table.getState().sorting.length > 0;
|
|
11
|
+
return (_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [toolbarRender?.(table), (isFiltered || isSorted) && (_jsxs(Button, { variant: "ghost", onClick: () => {
|
|
12
|
+
table.resetColumnFilters();
|
|
13
|
+
table.resetSorting();
|
|
14
|
+
}, className: "h-8 px-2 lg:px-3", children: ["Reset", _jsx(Cross2Icon, { className: "ml-2 h-4 w-4" })] }))] }), _jsx("div", { className: "flex-1" }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(DataTableViewOptions, { table: table }), _jsxs(Button, { variant: "outline", size: "sm", className: "ml-auto hidden h-8 lg:flex", onClick: () => {
|
|
15
|
+
const csvConfig = mkConfig({
|
|
16
|
+
fieldSeparator: ',',
|
|
17
|
+
filename: 'data',
|
|
18
|
+
decimalSeparator: '.',
|
|
19
|
+
useKeysAsHeaders: true,
|
|
20
|
+
});
|
|
21
|
+
const renderCellValue = (cell) => {
|
|
22
|
+
const rendered = cell.renderValue();
|
|
23
|
+
if (rendered === null) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (['string', 'number', 'boolean', 'undefined'].includes(typeof rendered)) {
|
|
27
|
+
return rendered;
|
|
28
|
+
}
|
|
29
|
+
if (rendered instanceof Date) {
|
|
30
|
+
return rendered.toISOString();
|
|
31
|
+
}
|
|
32
|
+
if (typeof rendered === 'object') {
|
|
33
|
+
return JSON.stringify(rendered);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const rowModel = table.getCoreRowModel();
|
|
37
|
+
const rows = rowModel.rows.map(row => Object.fromEntries(row.getAllCells().map(c => [c.column.id, renderCellValue(c)]).filter(([_, v]) => v !== undefined)));
|
|
38
|
+
if (rows.length === 0) {
|
|
39
|
+
alert("No data to export");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const csv = generateCsv(csvConfig)(rows);
|
|
43
|
+
download(csvConfig)(csv);
|
|
44
|
+
}, children: [_jsx(DownloadIcon, { className: "mr-2 h-4 w-4" }), "Export CSV"] })] })] }));
|
|
45
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Table } from "@tanstack/react-table";
|
|
2
|
+
interface DataTableViewOptionsProps<TData> {
|
|
3
|
+
table: Table<TData>;
|
|
4
|
+
}
|
|
5
|
+
export declare function DataTableViewOptions<TData>({ table, }: DataTableViewOptionsProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { MixerHorizontalIcon } from "@radix-ui/react-icons";
|
|
4
|
+
import { Button, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@stackframe/stack-ui";
|
|
5
|
+
export function DataTableViewOptions({ table, }) {
|
|
6
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "ml-auto hidden h-8 lg:flex", children: [_jsx(MixerHorizontalIcon, { className: "mr-2 h-4 w-4" }), "View"] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuLabel, { children: "Toggle columns" }), _jsx(DropdownMenuSeparator, {}), table
|
|
7
|
+
.getAllColumns()
|
|
8
|
+
.filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide())
|
|
9
|
+
.map((column) => {
|
|
10
|
+
return (_jsx(DropdownMenuCheckboxItem, { className: "capitalize", checked: column.getIsVisible(), onCheckedChange: (value) => column.toggleVisibility(!!value), children: column.id }, column.id));
|
|
11
|
+
})] })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Edit } from "lucide-react";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { Button, Input, Typography } from "..";
|
|
6
|
+
export function EditableText(props) {
|
|
7
|
+
const [editing, setEditing] = useState(false);
|
|
8
|
+
const [editingValue, setEditingValue] = useState(props.value);
|
|
9
|
+
return (_jsx("div", { className: 'flex items-center gap-2', children: editing ? (_jsxs(_Fragment, { children: [_jsx(Input, { value: editingValue, onChange: (e) => setEditingValue(e.target.value) }), _jsx(Button, { onClick: async () => {
|
|
10
|
+
await props.onSave?.(editingValue);
|
|
11
|
+
setEditing(false);
|
|
12
|
+
}, children: "Save" }), _jsx(Button, { variant: 'outline', onClick: () => {
|
|
13
|
+
setEditingValue(props.value);
|
|
14
|
+
setEditing(false);
|
|
15
|
+
}, children: "Cancel" })] })) : (_jsxs(_Fragment, { children: [_jsx(Typography, { children: props.value }), _jsx(Button, { onClick: () => setEditing(true), size: 'icon', variant: 'ghost', children: _jsx(Edit, { className: "w-4 h-4" }) })] })) }));
|
|
16
|
+
}
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, Fragment as _Fragment
|
|
3
|
-
import
|
|
4
|
-
import
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { filterUndefined } from '@stackframe/stack-shared/dist/utils/objects';
|
|
4
|
+
import React from 'react';
|
|
5
5
|
const Container = React.forwardRef(({ size, ...props }, ref) => {
|
|
6
|
-
|
|
7
|
-
const styleSheet = `
|
|
8
|
-
.stack-inner-container-${styleId} {
|
|
9
|
-
max-width: 100%;
|
|
10
|
-
}
|
|
11
|
-
@media (min-width: ${size}px) {
|
|
12
|
-
.stack-inner-container-${styleId} {
|
|
13
|
-
width: ${size}px;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
`;
|
|
17
|
-
return (_jsxs(_Fragment, { children: [_jsx("style", { children: styleSheet }), _jsx("div", { className: "flex justify-center w-full", children: _jsx("div", { ...props, ref: ref, className: cn(props.className, `stack-inner-container-${styleId}`), children: props.children }) })] }));
|
|
6
|
+
return (_jsx(_Fragment, { children: _jsx("div", { className: "flex justify-center w-full", children: _jsx("div", { ...props, ref: ref, style: { width: '100%', maxWidth: size, ...props.style ? filterUndefined(props.style) : {} }, children: props.children }) }) }));
|
|
18
7
|
});
|
|
19
8
|
export { Container };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export * from "./components/action-dialog";
|
|
|
2
2
|
export * from "./components/browser-frame";
|
|
3
3
|
export * from "./components/copy-button";
|
|
4
4
|
export * from "./components/copy-field";
|
|
5
|
+
export * from "./components/data-table";
|
|
6
|
+
export * from "./components/editable-text";
|
|
5
7
|
export * from "./components/simple-tooltip";
|
|
6
8
|
export * from "./components/ui/accordion";
|
|
7
9
|
export * from "./components/ui/alert";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@ export * from "./components/action-dialog";
|
|
|
2
2
|
export * from "./components/browser-frame";
|
|
3
3
|
export * from "./components/copy-button";
|
|
4
4
|
export * from "./components/copy-field";
|
|
5
|
+
export * from "./components/data-table";
|
|
6
|
+
export * from "./components/editable-text";
|
|
5
7
|
export * from "./components/simple-tooltip";
|
|
6
8
|
export * from "./components/ui/accordion";
|
|
7
9
|
export * from "./components/ui/alert";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackframe/stack-ui",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.19",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
"@radix-ui/react-toggle-group": "^1.0.4",
|
|
58
58
|
"@radix-ui/react-tooltip": "^1.0.7",
|
|
59
59
|
"class-variance-authority": "^0.7.0",
|
|
60
|
+
"@tanstack/react-table": "^8.17.0",
|
|
61
|
+
"export-to-csv": "^1.3.0",
|
|
60
62
|
"clsx": "^2.0.0",
|
|
61
63
|
"cmdk": "^1.0.0",
|
|
62
64
|
"input-otp": "^1.2.4",
|
|
@@ -66,7 +68,7 @@
|
|
|
66
68
|
"react-hook-form": "^7.51.4",
|
|
67
69
|
"react-resizable-panels": "^2.0.19",
|
|
68
70
|
"tailwind-merge": "^2.3.0",
|
|
69
|
-
"@stackframe/stack-shared": "2.5.
|
|
71
|
+
"@stackframe/stack-shared": "2.5.19"
|
|
70
72
|
},
|
|
71
73
|
"devDependencies": {
|
|
72
74
|
"@types/react": "^18.2.66",
|