@getgreenline/blaze-ui 1.0.18 → 1.0.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.
@@ -4,7 +4,7 @@ import { CheckIcon } from 'lucide-react';
4
4
  import { cn } from '../lib/utils.js';
5
5
 
6
6
  function Checkbox({ className, ...props }) {
7
- return (jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("tw:peer tw:border-input tw:dark:bg-input/30 tw:data-[state=checked]:bg-primary tw:data-[state=checked]:text-primary-foreground tw:dark:data-[state=checked]:bg-primary tw:data-[state=checked]:border-primary tw:focus-visible:border-ring tw:focus-visible:ring-ring/50 tw:aria-invalid:ring-destructive/20 tw:dark:aria-invalid:ring-destructive/40 tw:aria-invalid:border-destructive tw:size-4 tw:shrink-0 tw:rounded-[4px] tw:border tw:shadow-xs tw:transition-shadow tw:outline-none tw:focus-visible:ring-[3px] tw:disabled:cursor-not-allowed tw:disabled:opacity-50", className), ...props, children: jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "tw:flex tw:items-center tw:justify-center tw:text-current tw:transition-none", children: jsx(CheckIcon, { className: "tw:size-3.5" }) }) }));
7
+ return (jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("tw:peer tw:border-border tw:dark:bg-input/30 tw:data-[state=checked]:bg-primary tw:data-[state=checked]:text-primary-foreground tw:dark:data-[state=checked]:bg-primary tw:data-[state=checked]:border-primary tw:focus-visible:border-ring tw:focus-visible:ring-ring/50 tw:aria-invalid:ring-destructive/20 tw:dark:aria-invalid:ring-destructive/40 tw:aria-invalid:border-destructive tw:size-4 tw:shrink-0 tw:rounded-[4px] tw:border tw:shadow-xs tw:transition-shadow tw:outline-none tw:focus-visible:ring-[3px] tw:disabled:cursor-not-allowed tw:disabled:opacity-50 tw:align-middle", className), ...props, children: jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "tw:flex tw:items-center tw:justify-center tw:text-current tw:transition-none", children: jsx(CheckIcon, { className: "tw:size-3.5" }) }) }));
8
8
  }
9
9
 
10
10
  export { Checkbox };
@@ -0,0 +1,78 @@
1
+ import * as React from "react";
2
+ import { Check, X } from "lucide-react";
3
+ export type SortDirection = "asc" | "desc" | null;
4
+ export interface DataTableColumn<T> {
5
+ key: string;
6
+ label: string;
7
+ sortable?: boolean;
8
+ align?: "left" | "center" | "right";
9
+ width?: string;
10
+ render?: (value: any, row: T, index: number) => React.ReactNode;
11
+ }
12
+ export interface DataTableRow {
13
+ id: string;
14
+ children?: DataTableRow[];
15
+ [key: string]: any;
16
+ }
17
+ export interface IClickableField<T = any> {
18
+ field: string;
19
+ onClick: (row: T) => void;
20
+ }
21
+ export interface DataTableAction<T> {
22
+ label: string;
23
+ icon?: React.ReactNode;
24
+ onClick: (row: T) => void;
25
+ variant?: "default" | "destructive";
26
+ condition?: (row: T) => boolean;
27
+ }
28
+ export type PaginationMode = "offset" | "cursor";
29
+ export interface DataTablePaginationProps {
30
+ currentPage: number;
31
+ totalPages: number;
32
+ onPageChange: (page: number) => void;
33
+ pageSize?: number;
34
+ totalItems?: number;
35
+ pageSizeOptions?: number[];
36
+ onPageSizeChange?: (pageSize: number) => void;
37
+ mode?: PaginationMode;
38
+ serverSide?: boolean;
39
+ showTotalItems?: boolean;
40
+ disableNext?: boolean;
41
+ disablePrevious?: boolean;
42
+ }
43
+ export interface DataTableProps<T extends DataTableRow> {
44
+ columns: DataTableColumn<T>[];
45
+ data: T[];
46
+ onSelectionChange?: (selectedIds: string[]) => void;
47
+ selectedRowIds?: string[];
48
+ onSort?: (key: string, direction: SortDirection) => void;
49
+ sortState?: {
50
+ key: string;
51
+ direction: SortDirection;
52
+ };
53
+ expandable?: boolean;
54
+ renderExpandedRow?: (row: T) => React.ReactNode;
55
+ actions?: DataTableAction<T>[];
56
+ className?: string;
57
+ pagination?: DataTablePaginationProps;
58
+ treeData?: boolean;
59
+ reorderable?: boolean;
60
+ onReorder?: (rowId: string, direction: "up" | "down", parentId?: string) => void;
61
+ onDragReorder?: (draggedId: string, targetId: string, parentId?: string) => void;
62
+ hiddenColumns?: string[];
63
+ onColumnVisibilityChange?: (hiddenColumns: string[]) => void;
64
+ loading?: boolean;
65
+ loadingRows?: number;
66
+ emptyState?: {
67
+ title?: string;
68
+ description?: string;
69
+ icon?: React.ReactNode;
70
+ };
71
+ clickableFields?: IClickableField<T>[];
72
+ showColumnVisibility?: boolean;
73
+ expandedRowIds?: string[];
74
+ onExpandedChange?: (expandedIds: string[]) => void;
75
+ }
76
+ export declare function DataTable<T extends DataTableRow>({ columns, data, onSelectionChange, selectedRowIds, onSort, sortState, expandable, renderExpandedRow, actions, className, pagination, treeData, reorderable, onReorder, onDragReorder, hiddenColumns, onColumnVisibilityChange, loading, loadingRows, emptyState, clickableFields, showColumnVisibility, expandedRowIds, onExpandedChange, }: DataTableProps<T>): import("react/jsx-runtime").JSX.Element;
77
+ export { Check, X };
78
+ //# sourceMappingURL=data-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table.d.ts","sourceRoot":"","sources":["../../src/components/data-table.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,254 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime.js';
2
+ import * as React from 'react';
3
+ import { Settings, Inbox, ArrowUpDown, ChevronDown, ChevronRight, ChevronUp, MoreHorizontal } from 'lucide-react';
4
+ export { Check, X } from 'lucide-react';
5
+ import { cn } from '../lib/utils.js';
6
+ import { Checkbox } from './checkbox.js';
7
+ import { Button } from './button.js';
8
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem } from './dropdown-menu.js';
9
+ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './select.js';
10
+ import { Pagination, PaginationContent, PaginationItem, PaginationPrevious, PaginationNext, PaginationLink, PaginationEllipsis } from './pagination.js';
11
+
12
+ const DEFAULT_PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
13
+ function DataTable({ columns, data, onSelectionChange, selectedRowIds, onSort, sortState, expandable = false, renderExpandedRow, actions, className, pagination, treeData = false, reorderable = false, onReorder, onDragReorder, hiddenColumns = [], onColumnVisibilityChange, loading = false, loadingRows = 5, emptyState, clickableFields, showColumnVisibility = true, expandedRowIds, onExpandedChange, }) {
14
+ const [internalSelectedRows, setInternalSelectedRows] = React.useState(new Set());
15
+ const [internalExpandedRows, setInternalExpandedRows] = React.useState(new Set());
16
+ const [internalSortConfig, setInternalSortConfig] = React.useState({ key: "", direction: null });
17
+ const [draggedRowId, setDraggedRowId] = React.useState(null);
18
+ const [dragOverRowId, setDragOverRowId] = React.useState(null);
19
+ const scrollContainerRef = React.useRef(null);
20
+ React.useEffect(() => {
21
+ if (pagination?.currentPage && scrollContainerRef.current) {
22
+ scrollContainerRef.current.scrollTo({ top: 0, behavior: 'smooth' });
23
+ }
24
+ }, [pagination?.currentPage]);
25
+ const selectedRows = React.useMemo(() => {
26
+ if (selectedRowIds !== undefined) {
27
+ return new Set(selectedRowIds);
28
+ }
29
+ return internalSelectedRows;
30
+ }, [selectedRowIds, internalSelectedRows]);
31
+ const expandedRows = React.useMemo(() => {
32
+ if (expandedRowIds !== undefined) {
33
+ return new Set(expandedRowIds);
34
+ }
35
+ return internalExpandedRows;
36
+ }, [expandedRowIds, internalExpandedRows]);
37
+ const sortConfig = React.useMemo(() => {
38
+ if (sortState !== undefined) {
39
+ return sortState;
40
+ }
41
+ return internalSortConfig;
42
+ }, [sortState, internalSortConfig]);
43
+ const setSelectedRows = React.useCallback((newSelected) => {
44
+ if (selectedRowIds === undefined) {
45
+ setInternalSelectedRows(newSelected);
46
+ }
47
+ }, [selectedRowIds]);
48
+ const setExpandedRows = React.useCallback((newExpanded) => {
49
+ if (expandedRowIds === undefined) {
50
+ setInternalExpandedRows(newExpanded);
51
+ }
52
+ }, [expandedRowIds]);
53
+ const hasRowSelection = !!onSelectionChange;
54
+ const hasActions = actions && actions.length > 0;
55
+ const showActionsColumn = hasActions || (showColumnVisibility && onColumnVisibilityChange);
56
+ const toggleRowSelection = (rowId) => {
57
+ const newSelected = new Set(selectedRows);
58
+ if (newSelected.has(rowId)) {
59
+ newSelected.delete(rowId);
60
+ }
61
+ else {
62
+ newSelected.add(rowId);
63
+ }
64
+ setSelectedRows(newSelected);
65
+ const selectedIdsArray = Array.from(newSelected);
66
+ onSelectionChange?.(selectedIdsArray);
67
+ };
68
+ const toggleAllRows = () => {
69
+ const currentPageIds = data.map((row) => row.id);
70
+ const allCurrentPageSelected = currentPageIds.every(id => selectedRows.has(id));
71
+ let newSelected;
72
+ if (allCurrentPageSelected) {
73
+ newSelected = new Set(selectedRows);
74
+ currentPageIds.forEach(id => newSelected.delete(id));
75
+ }
76
+ else {
77
+ newSelected = new Set(selectedRows);
78
+ currentPageIds.forEach(id => newSelected.add(id));
79
+ }
80
+ setSelectedRows(newSelected);
81
+ const selectedIdsArray = Array.from(newSelected);
82
+ onSelectionChange?.(selectedIdsArray);
83
+ };
84
+ const toggleRowExpansion = (rowId) => {
85
+ const newExpanded = new Set(expandedRows);
86
+ if (newExpanded.has(rowId)) {
87
+ newExpanded.delete(rowId);
88
+ }
89
+ else {
90
+ newExpanded.add(rowId);
91
+ }
92
+ setExpandedRows(newExpanded);
93
+ onExpandedChange?.(Array.from(newExpanded));
94
+ };
95
+ const handleSort = (key) => {
96
+ let direction = "asc";
97
+ if (sortConfig.key === key) {
98
+ if (sortConfig.direction === "asc") {
99
+ direction = "desc";
100
+ }
101
+ else if (sortConfig.direction === "desc") {
102
+ direction = null;
103
+ }
104
+ }
105
+ if (sortState === undefined) {
106
+ setInternalSortConfig({ key, direction });
107
+ }
108
+ onSort?.(key, direction);
109
+ };
110
+ const getSortIcon = (columnKey) => {
111
+ if (sortConfig.key !== columnKey) {
112
+ return jsx(ArrowUpDown, { className: "tw:ml-2 tw:h-4 tw:w-4 tw:opacity-50" });
113
+ }
114
+ if (sortConfig.direction === "asc") {
115
+ return jsx(ArrowUpDown, { className: "tw:ml-2 tw:h-4 tw:w-4 tw:rotate-180" });
116
+ }
117
+ if (sortConfig.direction === "desc") {
118
+ return jsx(ArrowUpDown, { className: "tw:ml-2 tw:h-4 tw:w-4" });
119
+ }
120
+ return jsx(ArrowUpDown, { className: "tw:ml-2 tw:h-4 tw:w-4 tw:opacity-50" });
121
+ };
122
+ const hasChildren = (row) => treeData && row.children && row.children.length > 0;
123
+ const isExpandable = expandable || treeData;
124
+ const visibleColumns = columns.filter((col) => !hiddenColumns.includes(col.key));
125
+ const toggleColumnVisibility = (columnKey) => {
126
+ if (!onColumnVisibilityChange)
127
+ return;
128
+ const newHidden = hiddenColumns.includes(columnKey)
129
+ ? hiddenColumns.filter((k) => k !== columnKey)
130
+ : [...hiddenColumns, columnKey];
131
+ onColumnVisibilityChange(newHidden);
132
+ };
133
+ const getRowBackgroundClass = (row, rowIndex, depth) => {
134
+ if (selectedRows.has(row.id))
135
+ return "tw:bg-accent";
136
+ if (depth > 0)
137
+ return "tw:bg-muted";
138
+ if (rowIndex % 2 === 1)
139
+ return "tw:bg-muted";
140
+ return "tw:bg-background";
141
+ };
142
+ const getClickableField = (fieldKey) => {
143
+ return clickableFields?.find(cf => cf.field === fieldKey);
144
+ };
145
+ const getVisibleActions = (row) => {
146
+ if (!actions)
147
+ return [];
148
+ return actions.filter(action => !action.condition || action.condition(row));
149
+ };
150
+ const renderRow = (row, rowIndex, depth = 0, parentRow, siblingCount) => {
151
+ const canExpand = hasChildren(row) || (expandable && renderExpandedRow);
152
+ const isExpanded = expandedRows.has(row.id);
153
+ const indentPadding = depth * 24;
154
+ const rowBgClass = getRowBackgroundClass(row, rowIndex, depth);
155
+ const visibleActionsForRow = getVisibleActions(row);
156
+ const isDragging = draggedRowId === row.id;
157
+ const isDragOver = dragOverRowId === row.id;
158
+ const totalSiblings = siblingCount ?? data.length;
159
+ const handleDragStart = (e) => {
160
+ setDraggedRowId(row.id);
161
+ e.dataTransfer.effectAllowed = 'move';
162
+ e.dataTransfer.setData('text/plain', row.id);
163
+ };
164
+ const handleDragEnd = () => {
165
+ setDraggedRowId(null);
166
+ setDragOverRowId(null);
167
+ };
168
+ const handleDragOver = (e) => {
169
+ e.preventDefault();
170
+ e.dataTransfer.dropEffect = 'move';
171
+ if (draggedRowId !== row.id) {
172
+ setDragOverRowId(row.id);
173
+ }
174
+ };
175
+ const handleDragLeave = () => {
176
+ setDragOverRowId(null);
177
+ };
178
+ const handleDrop = (e) => {
179
+ e.preventDefault();
180
+ const draggedId = e.dataTransfer.getData('text/plain');
181
+ if (draggedId && draggedId !== row.id) {
182
+ onDragReorder?.(draggedId, row.id, parentRow?.id);
183
+ }
184
+ setDraggedRowId(null);
185
+ setDragOverRowId(null);
186
+ };
187
+ return (jsxs(React.Fragment, { children: [jsxs("tr", { className: cn("tw:border-b tw:transition-colors hover:tw:bg-accent/50", rowBgClass, isDragging && "tw:opacity-50", isDragOver && "tw:ring-2 tw:ring-primary tw:ring-inset"), draggable: reorderable, onDragStart: reorderable ? handleDragStart : undefined, onDragEnd: reorderable ? handleDragEnd : undefined, onDragOver: reorderable ? handleDragOver : undefined, onDragLeave: reorderable ? handleDragLeave : undefined, onDrop: reorderable ? handleDrop : undefined, children: [isExpandable && (jsx("td", { className: cn("tw:p-2 tw:w-12", rowBgClass), children: jsx("div", { style: { paddingLeft: indentPadding }, children: canExpand ? (jsx("button", { onClick: () => toggleRowExpansion(row.id), className: "tw:flex tw:items-center tw:justify-center hover:tw:bg-accent tw:rounded tw:p-1", "aria-label": isExpanded ? "Collapse row" : "Expand row", "aria-expanded": isExpanded, children: isExpanded ? (jsx(ChevronDown, { className: "tw:h-4 tw:w-4" })) : (jsx(ChevronRight, { className: "tw:h-4 tw:w-4" })) })) : (jsx("span", { className: "tw:w-6" })) }) })), hasRowSelection && (jsx("td", { className: cn("tw:p-2 tw:w-12", rowBgClass), children: jsx("div", { className: "tw:flex tw:items-center tw:justify-center", children: jsx(Checkbox, { checked: selectedRows.has(row.id), onCheckedChange: () => toggleRowSelection(row.id), "aria-label": `Select row ${rowIndex + 1}` }) }) })), reorderable && (jsx("td", { className: cn("tw:p-2 tw:w-12", rowBgClass), children: jsxs("div", { className: "tw:flex tw:flex-col tw:items-center tw:gap-0.5", children: [jsx("button", { onClick: () => onReorder?.(row.id, "up", parentRow?.id), className: "tw:flex tw:items-center tw:justify-center hover:tw:bg-accent tw:rounded tw:p-0.5 tw:text-muted-foreground hover:tw:text-foreground tw:transition-colors", disabled: rowIndex === 0, "aria-label": "Move row up", children: jsx(ChevronUp, { className: cn("tw:h-4 tw:w-4", rowIndex === 0 && "tw:opacity-30") }) }), jsx("button", { onClick: () => onReorder?.(row.id, "down", parentRow?.id), className: "tw:flex tw:items-center tw:justify-center hover:tw:bg-accent tw:rounded tw:p-0.5 tw:text-muted-foreground hover:tw:text-foreground tw:transition-colors", disabled: rowIndex === totalSiblings - 1, "aria-label": "Move row down", children: jsx(ChevronDown, { className: cn("tw:h-4 tw:w-4", rowIndex === totalSiblings - 1 && "tw:opacity-30") }) })] }) })), visibleColumns.map((column, colIndex) => {
188
+ const clickableField = getClickableField(column.key);
189
+ const cellContent = column.render ? column.render(row[column.key], row, rowIndex) : row[column.key];
190
+ return (jsx("td", { className: cn("tw:p-4 tw:align-middle tw:whitespace-nowrap", column.align === "center" && "tw:text-center", column.align === "right" && "tw:text-right", rowBgClass), children: jsx("div", { style: { paddingLeft: colIndex === 0 ? (isExpandable ? (depth > 0 ? 16 : 0) : indentPadding) : 0 }, children: clickableField ? (jsx("button", { onClick: () => clickableField.onClick(row), className: "tw:text-sky-500 hover:tw:text-sky-600 hover:tw:underline tw:cursor-pointer tw:transition-colors", children: cellContent })) : (cellContent) }) }, column.key));
191
+ }), showActionsColumn && (jsx("td", { className: cn("tw:p-2 tw:text-right tw:sticky tw:right-0", rowBgClass), children: visibleActionsForRow.length > 0 && (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button, { variant: "ghost", size: "icon", className: "tw:h-8 tw:w-8", "aria-label": "Row actions", children: jsx(MoreHorizontal, { className: "tw:h-4 tw:w-4" }) }) }), jsx(DropdownMenuContent, { align: "end", children: visibleActionsForRow.map((action, index) => (jsxs(DropdownMenuItem, { onClick: () => action.onClick(row), className: cn(action.variant === "destructive" && "tw:text-destructive focus:tw:text-destructive"), children: [action.icon && jsx("span", { className: "tw:mr-2", children: action.icon }), action.label] }, index))) })] })) }))] }), expandable && !treeData && isExpanded && renderExpandedRow && (jsx("tr", { className: "tw:border-b tw:bg-muted/30", children: jsx("td", { colSpan: visibleColumns.length + (showActionsColumn ? 1 : 0) + (hasRowSelection ? 1 : 0) + (isExpandable ? 1 : 0) + (reorderable ? 1 : 0), className: "tw:p-4", children: renderExpandedRow(row) }) })), treeData && isExpanded && row.children && row.children.map((child, childIndex) => renderRow(child, childIndex, depth + 1, row, row.children?.length))] }, row.id));
192
+ };
193
+ const renderPaginationItems = () => {
194
+ if (!pagination)
195
+ return null;
196
+ const { currentPage, totalPages, onPageChange } = pagination;
197
+ const items = [];
198
+ const maxVisiblePages = 5;
199
+ let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
200
+ const endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
201
+ if (endPage - startPage + 1 < maxVisiblePages) {
202
+ startPage = Math.max(1, endPage - maxVisiblePages + 1);
203
+ }
204
+ if (startPage > 1) {
205
+ items.push(jsx(PaginationItem, { children: jsx(PaginationLink, { href: "#", onClick: (e) => {
206
+ e.preventDefault();
207
+ if (!loading)
208
+ onPageChange(1);
209
+ }, "aria-label": "Go to page 1", "aria-disabled": loading, className: cn(loading && "tw:pointer-events-none tw:opacity-50"), children: "1" }) }, 1));
210
+ if (startPage > 2) {
211
+ items.push(jsx(PaginationItem, { children: jsx(PaginationEllipsis, {}) }, "ellipsis-start"));
212
+ }
213
+ }
214
+ for (let i = startPage; i <= endPage; i++) {
215
+ items.push(jsx(PaginationItem, { children: jsx(PaginationLink, { href: "#", isActive: i === currentPage, onClick: (e) => {
216
+ e.preventDefault();
217
+ if (!loading)
218
+ onPageChange(i);
219
+ }, "aria-label": i === currentPage ? `Page ${i}, current page` : `Go to page ${i}`, "aria-current": i === currentPage ? "page" : undefined, "aria-disabled": loading, className: cn(loading && "tw:pointer-events-none tw:opacity-50"), children: i }) }, i));
220
+ }
221
+ if (endPage < totalPages) {
222
+ if (endPage < totalPages - 1) {
223
+ items.push(jsx(PaginationItem, { children: jsx(PaginationEllipsis, {}) }, "ellipsis-end"));
224
+ }
225
+ items.push(jsx(PaginationItem, { children: jsx(PaginationLink, { href: "#", onClick: (e) => {
226
+ e.preventDefault();
227
+ if (!loading)
228
+ onPageChange(totalPages);
229
+ }, "aria-label": `Go to page ${totalPages}`, "aria-disabled": loading, className: cn(loading && "tw:pointer-events-none tw:opacity-50"), children: totalPages }) }, totalPages));
230
+ }
231
+ return items;
232
+ };
233
+ return (jsxs("div", { className: cn("tw:w-full tw:h-full tw:flex tw:flex-col", className), children: [jsx("div", { ref: scrollContainerRef, className: "tw:flex-1 tw:overflow-auto [&::-webkit-scrollbar]:tw:h-2 [&::-webkit-scrollbar]:tw:w-2 [&::-webkit-scrollbar-track]:tw:bg-muted [&::-webkit-scrollbar-thumb]:tw:bg-muted-foreground/30 [&::-webkit-scrollbar-thumb]:tw:rounded-full hover:[&::-webkit-scrollbar-thumb]:tw:bg-muted-foreground/50", children: jsxs("table", { className: "tw:w-full tw:caption-bottom tw:text-sm", role: "grid", "aria-busy": loading, children: [jsx("thead", { className: "tw:border-b tw:sticky tw:top-0 tw:z-10 tw:bg-background", children: jsxs("tr", { children: [isExpandable && jsx("th", { className: "tw:h-10 tw:w-12 tw:px-2" }), hasRowSelection && (jsx("th", { className: "tw:h-10 tw:w-12 tw:px-2", children: jsx("div", { className: "tw:flex tw:items-center tw:justify-center", children: jsx(Checkbox, { checked: data.length > 0 && data.every(row => selectedRows.has(row.id)), onCheckedChange: toggleAllRows, "aria-label": "Select all rows" }) }) })), reorderable && jsx("th", { className: "tw:h-10 tw:w-12 tw:px-2" }), visibleColumns.map((column) => (jsx("th", { className: cn("tw:h-10 tw:px-4 tw:text-left tw:align-middle tw:font-medium tw:text-foreground tw:whitespace-nowrap", column.align === "center" && "tw:text-center", column.align === "right" && "tw:text-right"), style: { width: column.width }, children: column.sortable && onSort ? (jsxs("button", { onClick: () => !loading && handleSort(column.key), disabled: loading, "aria-label": `Sort by ${column.label}${sortConfig.key === column.key ? (sortConfig.direction === 'asc' ? ', currently sorted ascending' : sortConfig.direction === 'desc' ? ', currently sorted descending' : '') : ''}`, className: cn("tw:flex tw:items-center hover:tw:text-foreground/80 tw:transition-colors", loading && "tw:opacity-50 tw:cursor-not-allowed"), children: [column.label, getSortIcon(column.key)] })) : (column.label) }, column.key))), showActionsColumn && (jsx("th", { className: "tw:h-10 tw:w-20 tw:px-2 tw:sticky tw:right-0 tw:bg-background", children: jsx("div", { className: "tw:flex tw:items-center tw:justify-end tw:pr-1", children: showColumnVisibility && onColumnVisibilityChange && (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button, { variant: "ghost", size: "icon", className: "tw:h-8 tw:w-8", "aria-label": "Toggle column visibility", children: jsx(Settings, { className: "tw:h-4 tw:w-4 tw:text-muted-foreground" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsx(DropdownMenuLabel, { children: "Toggle Columns" }), jsx(DropdownMenuSeparator, {}), columns.map((column) => (jsxs(DropdownMenuItem, { onClick: (e) => {
234
+ e.preventDefault();
235
+ toggleColumnVisibility(column.key);
236
+ }, className: "tw:flex tw:items-center tw:gap-2", children: [jsx(Checkbox, { checked: !hiddenColumns.includes(column.key), onCheckedChange: () => toggleColumnVisibility(column.key) }), jsx("span", { children: column.label })] }, column.key)))] })] })) }) }))] }) }), jsx("tbody", { children: loading ? (Array.from({ length: loadingRows }).map((_, rowIndex) => (jsxs("tr", { className: cn("tw:border-b", rowIndex % 2 === 1 ? "tw:bg-muted" : "tw:bg-background"), children: [isExpandable && (jsx("td", { className: "tw:p-2 tw:w-12", children: jsx("div", { className: "tw:h-4 tw:w-4 tw:rounded tw:bg-muted tw:animate-pulse" }) })), hasRowSelection && (jsx("td", { className: "tw:p-2 tw:w-5", children: jsx("div", { className: "tw:h-4 tw:w-4 tw:rounded tw:bg-muted tw:animate-pulse" }) })), reorderable && (jsx("td", { className: "tw:p-2 tw:w-12", children: jsx("div", { className: "tw:h-8 tw:w-4 tw:rounded tw:bg-muted tw:animate-pulse tw:mx-auto" }) })), visibleColumns.map((column, colIndex) => (jsx("td", { className: cn("tw:p-4 tw:align-middle", column.align === "center" && "tw:text-center", column.align === "right" && "tw:text-right"), children: colIndex === 0 ? (jsxs("div", { className: "tw:flex tw:items-center tw:gap-3", children: [jsx("div", { className: "tw:h-10 tw:w-10 tw:rounded-md tw:bg-muted tw:animate-pulse" }), jsx("div", { className: "tw:h-4 tw:w-32 tw:rounded tw:bg-muted tw:animate-pulse" })] })) : (jsx("div", { className: cn("tw:h-4 tw:rounded tw:bg-muted tw:animate-pulse", column.align === "right" ? "tw:ml-auto tw:w-16" : "tw:w-20") })) }, column.key))), showActionsColumn && (jsx("td", { className: cn("tw:p-2 tw:text-right tw:sticky tw:right-0", rowIndex % 2 === 1 ? "tw:bg-muted" : "tw:bg-background"), children: jsx("div", { className: "tw:h-8 tw:w-8 tw:rounded tw:bg-accent tw:animate-pulse tw:ml-auto" }) }))] }, `skeleton-${rowIndex}`)))) : data.length === 0 ? (jsx("tr", { children: jsx("td", { colSpan: visibleColumns.length + (showActionsColumn ? 1 : 0) + (hasRowSelection ? 1 : 0) + (isExpandable ? 1 : 0) + (reorderable ? 1 : 0), className: "tw:h-48", children: jsxs("div", { className: "tw:flex tw:flex-col tw:items-center tw:justify-center tw:h-full tw:text-center", children: [emptyState?.icon || (jsx("div", { className: "tw:h-12 tw:w-12 tw:rounded-full tw:bg-muted tw:flex tw:items-center tw:justify-center tw:mb-3", children: jsx(Inbox, { className: "tw:h-6 tw:w-6 tw:text-muted-foreground" }) })), jsx("p", { className: "tw:text-base tw:font-medium tw:text-foreground", children: emptyState?.title || "No results found" }), jsx("p", { className: "tw:text-sm tw:text-muted-foreground tw:mt-1", children: emptyState?.description || "There are no items to display." })] }) }) })) : (data.map((row, rowIndex) => renderRow(row, rowIndex, 0))) })] }) }), pagination && (pagination.totalPages > 1 || pagination.mode === "cursor" || pagination.showTotalItems || (pagination.pageSizeOptions ?? DEFAULT_PAGE_SIZE_OPTIONS).length > 0) && (jsxs("div", { className: "tw:flex tw:items-center tw:border-t tw:px-4 tw:py-4", children: [(() => {
237
+ const options = pagination.pageSizeOptions ?? DEFAULT_PAGE_SIZE_OPTIONS;
238
+ if (options.length === 0)
239
+ return jsx("div", {});
240
+ return (jsxs("div", { className: "tw:flex tw:items-center tw:gap-2", children: [jsx("span", { className: "tw:text-sm tw:text-muted-foreground tw:whitespace-nowrap", id: "rows-per-page-label", children: "Rows per page:" }), jsxs(Select, { value: String(pagination.pageSize ?? options[0]), onValueChange: (value) => !loading && pagination.onPageSizeChange?.(Number(value)), disabled: loading, children: [jsx(SelectTrigger, { className: "tw:h-8 tw:w-[70px]", "aria-labelledby": "rows-per-page-label", children: jsx(SelectValue, { placeholder: pagination.pageSize ?? options[0] }) }), jsx(SelectContent, { children: options.map((size) => (jsx(SelectItem, { value: String(size), children: size }, size))) })] })] }));
241
+ })(), pagination.showTotalItems && pagination.totalItems !== undefined && (jsx("div", { className: "tw:flex-1 tw:flex tw:justify-center", children: jsxs("span", { className: "tw:text-sm tw:text-muted-foreground", children: ["Total: ", pagination.totalItems] }) })), !pagination.showTotalItems && jsx("div", { className: "tw:flex-1" }), jsx(Pagination, { className: "tw:mx-0 tw:w-auto", "aria-label": "Table pagination", children: jsxs(PaginationContent, { children: [jsx(PaginationItem, { children: jsx(PaginationPrevious, { href: "#", onClick: (e) => {
242
+ e.preventDefault();
243
+ if (!loading && !pagination.disablePrevious && pagination.currentPage > 1) {
244
+ pagination.onPageChange(pagination.currentPage - 1);
245
+ }
246
+ }, "aria-label": "Go to previous page", "aria-disabled": loading || pagination.disablePrevious || pagination.currentPage <= 1, className: cn((loading || pagination.disablePrevious || pagination.currentPage <= 1) && "tw:pointer-events-none tw:opacity-50") }) }), pagination.mode !== "cursor" && renderPaginationItems(), jsx(PaginationItem, { children: jsx(PaginationNext, { href: "#", onClick: (e) => {
247
+ e.preventDefault();
248
+ if (!loading && !pagination.disableNext && pagination.currentPage < pagination.totalPages) {
249
+ pagination.onPageChange(pagination.currentPage + 1);
250
+ }
251
+ }, "aria-label": "Go to next page", "aria-disabled": loading || pagination.disableNext || pagination.currentPage >= pagination.totalPages, className: cn((loading || pagination.disableNext || pagination.currentPage >= pagination.totalPages) && "tw:pointer-events-none tw:opacity-50") }) })] }) })] }))] }));
252
+ }
253
+
254
+ export { DataTable };
package/dist/index.d.ts CHANGED
@@ -36,6 +36,7 @@ export * from "./components/slider";
36
36
  export * from "./components/sonner";
37
37
  export * from "./components/spinner";
38
38
  export * from "./components/table";
39
+ export * from "./components/data-table";
39
40
  export * from "./components/tabs";
40
41
  export * from "./components/textarea";
41
42
  export * from "./components/toggle";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA"}
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ export { Slider } from './components/slider.js';
36
36
  export { Toaster } from './components/sonner.js';
37
37
  export { Spinner } from './components/spinner.js';
38
38
  export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from './components/table.js';
39
+ export { DataTable } from './components/data-table.js';
39
40
  export { Tabs, TabsContent, TabsList, TabsTrigger } from './components/tabs.js';
40
41
  export { Textarea } from './components/textarea.js';
41
42
  export { Toggle, toggleVariants } from './components/toggle.js';
@@ -53,3 +54,4 @@ export { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, For
53
54
  export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea } from './components/input-group.js';
54
55
  export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar } from './components/sidebar.js';
55
56
  export { cn } from './lib/utils.js';
57
+ export { Check, X } from 'lucide-react';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getgreenline/blaze-ui",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@hookform/resolvers": "^5.2.2",