flikkui 0.2.0-beta.4 → 0.2.0-beta.5

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.
Files changed (59) hide show
  1. package/README.md +92 -0
  2. package/dist/components/core/Table/Table.animations.d.ts +5 -16
  3. package/dist/components/core/Table/Table.animations.js +46 -0
  4. package/dist/components/core/Table/Table.d.ts +0 -27
  5. package/dist/components/core/Table/Table.js +58 -156
  6. package/dist/components/core/Table/Table.theme.js +28 -19
  7. package/dist/components/core/Table/Table.types.d.ts +95 -8
  8. package/dist/components/core/Table/Table.utils.d.ts +7 -0
  9. package/dist/components/core/Table/Table.utils.js +11 -1
  10. package/dist/components/core/Table/{components/TableActions/TableActions.d.ts → TableActions.d.ts} +3 -3
  11. package/dist/components/core/Table/{components/TableActions/TableActions.js → TableActions.js} +14 -24
  12. package/dist/components/core/Table/{components/TableActions/TableActionsMenu.d.ts → TableActionsMenu.d.ts} +1 -1
  13. package/dist/components/core/Table/{components/TableActions/TableActionsMenu.js → TableActionsMenu.js} +4 -4
  14. package/dist/components/core/Table/{components/core/TableBody.d.ts → TableBody.d.ts} +1 -1
  15. package/dist/components/core/Table/{components/core/TableBody.js → TableBody.js} +14 -20
  16. package/dist/components/core/Table/{components/core/TableCell.d.ts → TableCell.d.ts} +1 -9
  17. package/dist/components/core/Table/{components/core/TableCell.js → TableCell.js} +5 -13
  18. package/dist/components/core/Table/TableColumnManager.d.ts +3 -0
  19. package/dist/components/core/Table/TableColumnManager.js +34 -0
  20. package/dist/components/core/Table/{components/DeclarativeComponents.d.ts → TableDeclarative.d.ts} +1 -1
  21. package/dist/components/core/Table/{components/DeclarativeComponents.js → TableDeclarative.js} +6 -56
  22. package/dist/components/core/Table/TableFilter.d.ts +3 -0
  23. package/dist/components/core/Table/TableFilter.js +122 -0
  24. package/dist/components/core/Table/{components/core/TableHeader.d.ts → TableHeader.d.ts} +1 -1
  25. package/dist/components/core/Table/{components/core/TableHeader.js → TableHeader.js} +15 -29
  26. package/dist/components/core/Table/TablePagination.d.ts +7 -0
  27. package/dist/components/core/Table/{components/TablePagination/TablePagination.js → TablePagination.js} +5 -16
  28. package/dist/components/core/Table/TableRow.d.ts +8 -0
  29. package/dist/components/core/Table/TableRow.js +45 -0
  30. package/dist/components/core/Table/TableSelectionHeader.d.ts +7 -0
  31. package/dist/components/core/Table/{components/TableSelectionHeader/TableSelectionHeader.js → TableSelectionHeader.js} +4 -5
  32. package/dist/components/core/Table/hooks/index.d.ts +10 -0
  33. package/dist/components/core/Table/hooks/useTableColumns.d.ts +16 -0
  34. package/dist/components/core/Table/hooks/useTableColumns.js +67 -0
  35. package/dist/components/core/Table/hooks/useTableExpansion.d.ts +8 -0
  36. package/dist/components/core/Table/hooks/useTableExpansion.js +15 -0
  37. package/dist/components/core/Table/hooks/useTableFilter.d.ts +12 -0
  38. package/dist/components/core/Table/hooks/useTableFilter.js +37 -0
  39. package/dist/components/core/Table/hooks/useTablePagination.d.ts +12 -0
  40. package/dist/components/core/Table/hooks/useTablePagination.js +13 -0
  41. package/dist/components/core/Table/hooks/useTableSelection.d.ts +17 -0
  42. package/dist/components/core/Table/hooks/useTableSelection.js +40 -0
  43. package/dist/components/core/Table/index.d.ts +9 -8
  44. package/dist/components/core/Table/index.js +7 -5
  45. package/dist/components/core/index.js +9 -3
  46. package/dist/index.js +9 -3
  47. package/dist/styles.css +1 -1
  48. package/package.json +1 -1
  49. package/dist/components/core/Table/components/TableActions/TableActions.types.d.ts +0 -40
  50. package/dist/components/core/Table/components/TableActions/index.d.ts +0 -3
  51. package/dist/components/core/Table/components/TableActionsMenu.d.ts +0 -6
  52. package/dist/components/core/Table/components/TablePagination/TablePagination.d.ts +0 -17
  53. package/dist/components/core/Table/components/TablePagination/TablePagination.types.d.ts +0 -21
  54. package/dist/components/core/Table/components/TablePagination/index.d.ts +0 -2
  55. package/dist/components/core/Table/components/TableSelectionHeader/TableSelectionHeader.d.ts +0 -15
  56. package/dist/components/core/Table/components/TableSelectionHeader/index.d.ts +0 -3
  57. package/dist/components/core/Table/components/core/TableRow.d.ts +0 -3
  58. package/dist/components/core/Table/components/core/TableRow.js +0 -44
  59. package/dist/components/core/Table/components/core/index.d.ts +0 -4
@@ -0,0 +1,122 @@
1
+ import React__default, { useState } from 'react';
2
+ import { Button } from '../Button/Button.js';
3
+ import { Input } from '../../forms/Input/Input.js';
4
+ import '../../forms/Input/Input.theme.js';
5
+ import { Select } from '../../forms/Select/Select.js';
6
+ import '../../forms/Select/Select.theme.js';
7
+ import { FunnelIcon, XMarkIcon, PlusIcon } from '@heroicons/react/24/outline';
8
+ import { cn } from '../../../utils/cn.js';
9
+
10
+ const operatorOptions = [
11
+ { id: 'contains', label: 'Contains', value: 'contains' },
12
+ { id: 'equals', label: 'Equals', value: 'equals' },
13
+ { id: 'startsWith', label: 'Starts with', value: 'startsWith' },
14
+ { id: 'endsWith', label: 'Ends with', value: 'endsWith' },
15
+ { id: 'greaterThan', label: 'Greater than', value: 'greaterThan' },
16
+ { id: 'lessThan', label: 'Less than', value: 'lessThan' },
17
+ ];
18
+ function TableFilter({ columns, filterConfig, onFilterChange, className, }) {
19
+ const filterableColumns = columns.filter(col => col.filterable);
20
+ const [activeFilters, setActiveFilters] = useState(() => filterConfig.map((fc, i) => ({
21
+ id: `filter-${i}`,
22
+ columnId: fc.columnId,
23
+ operator: fc.operator || 'contains',
24
+ value: String(fc.value || ''),
25
+ })));
26
+ if (filterableColumns.length === 0)
27
+ return null;
28
+ const columnOptions = filterableColumns.map(col => ({
29
+ id: col.id,
30
+ label: typeof col.header === 'string' ? col.header : col.id,
31
+ value: col.id,
32
+ }));
33
+ const getFilterOperatorOptions = (column) => {
34
+ if (!column)
35
+ return operatorOptions;
36
+ if (column.filterType === 'number') {
37
+ return operatorOptions;
38
+ }
39
+ // Text filters: no greaterThan/lessThan
40
+ return operatorOptions.filter(op => op.value !== 'greaterThan' && op.value !== 'lessThan');
41
+ };
42
+ const syncFilters = (filters) => {
43
+ const newFilterConfig = filters
44
+ .filter(f => f.columnId && f.value)
45
+ .map(f => ({
46
+ columnId: f.columnId,
47
+ value: f.value,
48
+ operator: f.operator,
49
+ }));
50
+ onFilterChange(newFilterConfig);
51
+ };
52
+ const handleAddFilter = () => {
53
+ const defaultColumn = filterableColumns[0];
54
+ if (!defaultColumn)
55
+ return;
56
+ setActiveFilters(prev => [
57
+ ...prev,
58
+ {
59
+ id: `filter-${Date.now()}`,
60
+ columnId: defaultColumn.id,
61
+ operator: 'contains',
62
+ value: '',
63
+ },
64
+ ]);
65
+ };
66
+ const handleRemoveFilter = (filterId) => {
67
+ setActiveFilters(prev => {
68
+ const updated = prev.filter(f => f.id !== filterId);
69
+ syncFilters(updated);
70
+ return updated;
71
+ });
72
+ };
73
+ const handleFilterColumnChange = (filterId, columnId) => {
74
+ setActiveFilters(prev => {
75
+ const updated = prev.map(f => f.id === filterId ? { ...f, columnId, value: '' } : f);
76
+ syncFilters(updated);
77
+ return updated;
78
+ });
79
+ };
80
+ const handleFilterOperatorChange = (filterId, operator) => {
81
+ setActiveFilters(prev => {
82
+ const updated = prev.map(f => f.id === filterId ? { ...f, operator } : f);
83
+ syncFilters(updated);
84
+ return updated;
85
+ });
86
+ };
87
+ const handleFilterValueChange = (filterId, value) => {
88
+ setActiveFilters(prev => {
89
+ const updated = prev.map(f => f.id === filterId ? { ...f, value } : f);
90
+ syncFilters(updated);
91
+ return updated;
92
+ });
93
+ };
94
+ const handleClearAll = () => {
95
+ setActiveFilters([]);
96
+ onFilterChange([]);
97
+ };
98
+ const getSelectedColumn = (columnId) => filterableColumns.find(col => col.id === columnId);
99
+ return (React__default.createElement("div", { className: cn('space-y-2', className) },
100
+ activeFilters.map((filter) => {
101
+ const selectedColumn = getSelectedColumn(filter.columnId);
102
+ const filteredOperatorOptions = getFilterOperatorOptions(selectedColumn);
103
+ return (React__default.createElement("div", { key: filter.id, className: "flex items-center gap-2" },
104
+ React__default.createElement(FunnelIcon, { className: "size-4 flex-shrink-0 text-[var(--color-text-muted)]" }),
105
+ React__default.createElement(Select, { value: filter.columnId, onChange: (value) => handleFilterColumnChange(filter.id, value), options: columnOptions, size: "sm", className: "w-36" }),
106
+ React__default.createElement(Select, { value: filter.operator, onChange: (value) => handleFilterOperatorChange(filter.id, value), options: filteredOperatorOptions, size: "sm", className: "w-32" }),
107
+ (selectedColumn === null || selectedColumn === void 0 ? void 0 : selectedColumn.filterType) === 'select' && selectedColumn.filterOptions ? (React__default.createElement(Select, { value: filter.value, onChange: (value) => handleFilterValueChange(filter.id, value), options: selectedColumn.filterOptions.map(opt => ({
108
+ id: opt.value,
109
+ label: opt.label,
110
+ value: opt.value,
111
+ })), size: "sm", className: "w-40" })) : (React__default.createElement(Input, { value: filter.value, onChange: (e) => handleFilterValueChange(filter.id, e.target.value), placeholder: "Filter value...", size: "sm", type: (selectedColumn === null || selectedColumn === void 0 ? void 0 : selectedColumn.filterType) === 'number' ? 'number' : 'text', className: "w-40" })),
112
+ React__default.createElement(Button, { color: "neutral", variant: "ghost", size: "sm", iconOnly: true, onClick: () => handleRemoveFilter(filter.id), "aria-label": "Remove filter" },
113
+ React__default.createElement(XMarkIcon, { className: "size-4" }))));
114
+ }),
115
+ React__default.createElement("div", { className: "flex items-center gap-2" },
116
+ React__default.createElement(Button, { color: "neutral", variant: "ghost", size: "sm", onClick: handleAddFilter },
117
+ React__default.createElement(PlusIcon, { className: "size-4" }),
118
+ "Add Filter"),
119
+ activeFilters.length > 0 && (React__default.createElement(Button, { color: "neutral", variant: "ghost", size: "sm", onClick: handleClearAll }, "Clear All")))));
120
+ }
121
+
122
+ export { TableFilter };
@@ -1,3 +1,3 @@
1
1
  import React from 'react';
2
- import { InternalTableHeaderProps } from '../../Table.types';
2
+ import { InternalTableHeaderProps } from './Table.types';
3
3
  export declare const TableHeader: <T extends Record<string, any>>(props: InternalTableHeaderProps<T>) => React.ReactElement;
@@ -1,16 +1,15 @@
1
- import React__default from 'react';
1
+ import React__default, { useCallback } from 'react';
2
2
  import { TableCell } from './TableCell.js';
3
- import { Checkbox } from '../../../../forms/Checkbox/Checkbox.js';
4
- import { getRowId } from '../../Table.utils.js';
3
+ import { Checkbox } from '../../forms/Checkbox/Checkbox.js';
4
+ import { getRowId, UTILITY_COLUMN_WIDTH, UTILITY_COLUMN_ID } from './Table.utils.js';
5
5
 
6
- function TableHeaderComponent({ columns, sortConfig = [], onSortChange, filterConfig = [], onFilterChange, freezeHeader, freezeFirstColumn, freezeLastColumn, expandable, expandedRowRender, selectable, selectionType, selectedRows = [], onSelectionChange, totalRowCount = 0, theme, data = [], rowKey, }) {
7
- const handleSort = (columnId) => {
6
+ function TableHeaderComponent({ columns, sortConfig = [], onSortChange, freezeHeader, freezeFirstColumn, freezeLastColumn, expandable, expandedRowRender, selectable, selectionType, selectedRows = [], onSelectionChange, theme, data = [], rowKey, }) {
7
+ const handleSort = useCallback((columnId) => {
8
8
  if (!onSortChange)
9
9
  return;
10
10
  const currentSort = sortConfig.find(sort => sort.columnId === columnId);
11
11
  const newSortConfig = [...sortConfig.filter(sort => sort.columnId !== columnId)];
12
12
  if (!currentSort) {
13
- // Add new sort
14
13
  newSortConfig.push({
15
14
  columnId,
16
15
  direction: 'asc',
@@ -18,58 +17,45 @@ function TableHeaderComponent({ columns, sortConfig = [], onSortChange, filterCo
18
17
  });
19
18
  }
20
19
  else if (currentSort.direction === 'asc') {
21
- // Change to desc
22
20
  newSortConfig.push({
23
21
  columnId,
24
22
  direction: 'desc',
25
23
  priority: currentSort.priority,
26
24
  });
27
25
  }
28
- // If desc, remove sort
29
26
  onSortChange(newSortConfig);
30
- };
31
- const handleSelectAll = (checked) => {
27
+ }, [sortConfig, onSortChange]);
28
+ const handleSelectAll = useCallback((checked) => {
32
29
  if (!onSelectionChange || selectionType !== 'checkbox')
33
30
  return;
34
31
  if (checked) {
35
- // Select all rows - generate row IDs using the same logic as TableBody
36
32
  const allRowIds = data.map((row, index) => getRowId(row, index, rowKey));
37
33
  onSelectionChange(allRowIds);
38
34
  }
39
35
  else {
40
- // Deselect all
41
36
  onSelectionChange([]);
42
37
  }
43
- };
44
- // Calculate select all checkbox state
38
+ }, [data, rowKey, selectionType, onSelectionChange]);
45
39
  const dataLength = data.length;
46
40
  const isAllSelected = selectedRows.length === dataLength && dataLength > 0;
47
41
  const isIndeterminate = selectedRows.length > 0 && selectedRows.length < dataLength;
48
42
  return (React__default.createElement("thead", { className: freezeHeader ? theme === null || theme === void 0 ? void 0 : theme.frozenHeaderStyle : '' },
49
43
  React__default.createElement("tr", null,
50
- selectable && (React__default.createElement(TableCell, { column: {
51
- id: 'selection',
44
+ (selectable || (expandable && expandedRowRender)) && (React__default.createElement(TableCell, { column: {
45
+ id: UTILITY_COLUMN_ID,
52
46
  header: '',
53
47
  accessor: () => '',
54
- width: '48px',
55
- align: 'center'
56
- }, row: {}, rowId: "", isHeader: true, theme: theme }, selectionType === 'checkbox' && (React__default.createElement(Checkbox, { id: "table-select-all", name: "table-select-all", value: "select-all", checked: isAllSelected, indeterminate: isIndeterminate, onChange: handleSelectAll, "aria-label": "Select all rows" })))),
57
- expandable && expandedRowRender && (React__default.createElement(TableCell, { column: {
58
- id: 'expand',
59
- header: '',
60
- accessor: () => '',
61
- width: '48px',
62
- align: 'center'
63
- }, row: {}, rowId: "", isHeader: true, theme: theme })),
48
+ width: UTILITY_COLUMN_WIDTH,
49
+ }, row: {}, rowId: "", isHeader: true, theme: theme },
50
+ React__default.createElement("div", { className: "flex items-center gap-2" }, selectable && selectionType === 'checkbox' && (React__default.createElement(Checkbox, { id: "table-select-all", name: "table-select-all", value: "select-all", checked: isAllSelected, indeterminate: isIndeterminate, onChange: handleSelectAll, "aria-label": "Select all rows" }))))),
64
51
  columns.map((column, index) => {
65
52
  const currentSort = sortConfig.find(sort => sort.columnId === column.id);
66
53
  const isSortable = column.sortable && onSortChange;
67
- return (React__default.createElement(TableCell, { key: column.id, column: column, row: {}, rowId: "header", isHeader: true, isFrozen: freezeFirstColumn && index === 0, isLastFrozen: freezeLastColumn && index === columns.length - 1, theme: theme }, isSortable ? (React__default.createElement("button", { onClick: () => handleSort(column.id), className: "flex items-center gap-1 w-full text-left hover:text-primary-600 transition-colors", "aria-label": `Sort by ${typeof column.header === 'string' ? column.header : column.id}` },
54
+ return (React__default.createElement(TableCell, { key: column.id, column: column, row: {}, rowId: "header", isHeader: true, isFrozen: freezeFirstColumn && index === 0, isLastFrozen: freezeLastColumn && index === columns.length - 1, theme: theme }, isSortable ? (React__default.createElement("button", { onClick: () => handleSort(column.id), className: "flex items-center gap-1 w-full text-left hover:text-[var(--color-primary)] transition-colors", "aria-label": `Sort by ${typeof column.header === 'string' ? column.header : column.id}` },
68
55
  column.header,
69
- currentSort && (React__default.createElement("span", { className: "text-xs" }, currentSort.direction === 'asc' ? '' : '')))) : (column.header)));
56
+ currentSort && (React__default.createElement("span", { className: "text-xs" }, currentSort.direction === 'asc' ? '\u2191' : '\u2193')))) : (column.header)));
70
57
  }))));
71
58
  }
72
- // Export with proper type annotation
73
59
  const TableHeader = TableHeaderComponent;
74
60
 
75
61
  export { TableHeader };
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { TablePaginationProps } from "./Table.types";
3
+ /**
4
+ * TablePagination provides a standardized pagination interface for tables
5
+ * Combines the core Pagination component with page size selection controls
6
+ */
7
+ export declare const TablePagination: React.FC<TablePaginationProps>;
@@ -1,34 +1,23 @@
1
1
  import React__default from 'react';
2
- import { Pagination } from '../../../Pagination/Pagination.js';
3
- import { Select } from '../../../../forms/Select/Select.js';
4
- import '../../../../forms/Select/Select.theme.js';
5
- import { cn } from '../../../../../utils/cn.js';
2
+ import { Pagination } from '../Pagination/Pagination.js';
3
+ import { Select } from '../../forms/Select/Select.js';
4
+ import '../../forms/Select/Select.theme.js';
5
+ import { cn } from '../../../utils/cn.js';
6
6
 
7
7
  /**
8
8
  * TablePagination provides a standardized pagination interface for tables
9
9
  * Combines the core Pagination component with page size selection controls
10
- *
11
- * @example
12
- * <Table.Pagination
13
- * currentPage={currentPage}
14
- * totalItems={data.length}
15
- * pageSize={pageSize}
16
- * onPageChange={setCurrentPage}
17
- * onPageSizeChange={setPageSize}
18
- * pageSizeOptions={[10, 25, 50, 100]}
19
- * />
20
10
  */
21
11
  const TablePagination = ({ currentPage, totalItems, pageSize, pageSizeOptions = [10, 25, 50, 100], onPageChange, onPageSizeChange, showPageSizeSelector = true, className, }) => {
22
12
  const totalPages = Math.ceil(totalItems / pageSize);
23
13
  const startItem = (currentPage - 1) * pageSize + 1;
24
14
  const endItem = Math.min(currentPage * pageSize, totalItems);
25
- // Create options for the Select component
26
15
  const pageSizeSelectOptions = pageSizeOptions.map((option) => ({
27
16
  id: option,
28
17
  label: option.toString(),
29
18
  value: option,
30
19
  }));
31
- return (React__default.createElement("div", { className: cn("flex items-center justify-between px-4 py-3 border-t border-border bg-white", className) },
20
+ return (React__default.createElement("div", { className: cn("flex items-center justify-between px-4 py-3 border-t border-[var(--color-border)] bg-white", className) },
32
21
  React__default.createElement("div", { className: "flex items-center gap-4" },
33
22
  showPageSizeSelector && (React__default.createElement("div", { className: "flex items-center gap-2" },
34
23
  React__default.createElement("span", { className: "text-sm text-[var(--color-text-muted)]" }, "Rows per page:"),
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { InternalTableRowProps } from "./Table.types";
3
+ declare function TableRowComponent<T extends Record<string, any>>({ row, rowId, columns, expandedRowRender, isExpanded, onExpandChange, selectable, selectionType, isSelected, onSelectionChange, freezeFirstColumn, freezeLastColumn, theme, }: InternalTableRowProps<T>): React.JSX.Element;
4
+ declare namespace TableRowComponent {
5
+ var displayName: string;
6
+ }
7
+ export declare const TableRow: typeof TableRowComponent;
8
+ export {};
@@ -0,0 +1,45 @@
1
+ import React__default from 'react';
2
+ import { AnimatePresence, motion } from 'motion/react';
3
+ import { TableCell } from './TableCell.js';
4
+ import { Button } from '../Button/Button.js';
5
+ import { Checkbox } from '../../forms/Checkbox/Checkbox.js';
6
+ import { Radio } from '../../forms/Radio/Radio.js';
7
+ import { MinusIcon, PlusIcon } from '@heroicons/react/24/outline';
8
+ import { UTILITY_COLUMN_WIDTH, UTILITY_COLUMN_ID } from './Table.utils.js';
9
+ import { tableExpandAnimations, tableExpandContentAnimations } from './Table.animations.js';
10
+
11
+ function TableRowComponent({ row, rowId, columns, expandedRowRender, isExpanded = false, onExpandChange, selectable, selectionType, isSelected = false, onSelectionChange, freezeFirstColumn, freezeLastColumn, theme, }) {
12
+ const rowClasses = [
13
+ (theme === null || theme === void 0 ? void 0 : theme.rowStyle) || "",
14
+ isSelected ? (theme === null || theme === void 0 ? void 0 : theme.selectedRowStyle) || "" : "",
15
+ ]
16
+ .filter(Boolean)
17
+ .join(" ");
18
+ const handleExpandClick = (e) => {
19
+ e.stopPropagation();
20
+ onExpandChange === null || onExpandChange === void 0 ? void 0 : onExpandChange(!isExpanded);
21
+ };
22
+ const colSpan = columns.length + (selectable || !!expandedRowRender ? 1 : 0);
23
+ return (React__default.createElement(React__default.Fragment, null,
24
+ React__default.createElement("tr", { className: rowClasses, "data-selected": isSelected, role: "row", "aria-selected": selectable ? isSelected : undefined, "aria-expanded": Boolean(expandedRowRender) ? isExpanded : undefined },
25
+ (selectable || expandedRowRender) && (React__default.createElement(TableCell, { column: {
26
+ id: UTILITY_COLUMN_ID,
27
+ header: "",
28
+ accessor: () => "",
29
+ width: UTILITY_COLUMN_WIDTH,
30
+ }, row: row, rowId: rowId, theme: theme },
31
+ React__default.createElement("div", { className: "flex items-center gap-2" },
32
+ selectable &&
33
+ (selectionType === "checkbox" ? (React__default.createElement(Checkbox, { id: `table-checkbox-${rowId}`, name: `table-selection-${rowId}`, value: String(rowId), checked: isSelected, onChange: (checked) => onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange(checked), onClick: (e) => e.stopPropagation(), "aria-label": "Select row" })) : (React__default.createElement("div", { onClick: (e) => e.stopPropagation() },
34
+ React__default.createElement(Radio, { id: `table-radio-${rowId}`, name: "table-selection", value: String(rowId), checked: isSelected, onChange: (checked) => onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange(checked), "aria-label": "Select row" })))),
35
+ expandedRowRender && (React__default.createElement(Button, { color: "neutral", variant: "outline", size: "sm", iconOnly: true, onClick: handleExpandClick, "aria-label": isExpanded ? "Collapse row" : "Expand row" }, isExpanded ? (React__default.createElement(MinusIcon, { className: "size-4", strokeWidth: 2 })) : (React__default.createElement(PlusIcon, { className: "size-4", strokeWidth: 2 }))))))),
36
+ columns.map((column, index) => (React__default.createElement(TableCell, { key: column.id, column: column, row: row, rowId: rowId, isFrozen: freezeFirstColumn && index === 0, isLastFrozen: freezeLastColumn && index === columns.length - 1, theme: theme })))),
37
+ React__default.createElement(AnimatePresence, { initial: false }, isExpanded && expandedRowRender && (React__default.createElement(motion.tr, { key: `${rowId}-expanded`, initial: "collapsed", animate: "expanded", exit: "collapsed" },
38
+ React__default.createElement("td", { colSpan: colSpan, className: "p-0 border-b border-[var(--color-border)] dark:border-[var(--color-neutral-700)]" },
39
+ React__default.createElement(motion.div, { variants: tableExpandAnimations, style: { overflow: "hidden" } },
40
+ React__default.createElement(motion.div, { variants: tableExpandContentAnimations }, expandedRowRender(row)))))))));
41
+ }
42
+ TableRowComponent.displayName = "TableRow";
43
+ const TableRow = React__default.memo(TableRowComponent);
44
+
45
+ export { TableRow };
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { TableSelectionHeaderProps } from "./Table.types";
3
+ /**
4
+ * Selection header that appears when rows are selected
5
+ * Shows selection count and bulk action buttons
6
+ */
7
+ export declare const TableSelectionHeader: React.FC<TableSelectionHeaderProps>;
@@ -1,7 +1,7 @@
1
1
  import React__default from 'react';
2
2
  import { XMarkIcon } from '@heroicons/react/24/outline';
3
- import { Button } from '../../../Button/Button.js';
4
- import { cn } from '../../../../../utils/cn.js';
3
+ import { Button } from '../Button/Button.js';
4
+ import { cn } from '../../../utils/cn.js';
5
5
 
6
6
  /**
7
7
  * Selection header that appears when rows are selected
@@ -10,13 +10,12 @@ import { cn } from '../../../../../utils/cn.js';
10
10
  const TableSelectionHeader = ({ selectedCount, totalCount, selectedRows, bulkActions = [], onClearSelection, className, }) => {
11
11
  if (selectedCount === 0)
12
12
  return null;
13
- return (React__default.createElement("div", { className: cn("flex items-center justify-between px-3 py-1 rounded-lg shadow-xl bg-neutral-50 z-10", className) },
13
+ return (React__default.createElement("div", { className: cn("flex items-center justify-between px-3 py-1 rounded-lg shadow-xl bg-[var(--color-background-secondary)] z-10", className) },
14
14
  React__default.createElement("div", { className: "flex items-center gap-3" },
15
- React__default.createElement("span", { className: "text-[var(--color-text-muted)] text-sm border-r border-border pr-3" }, selectedCount === totalCount
15
+ React__default.createElement("span", { className: "text-[var(--color-text-muted)] text-sm border-r border-[var(--color-border)] pr-3" }, selectedCount === totalCount
16
16
  ? `All ${selectedCount} selected`
17
17
  : `${selectedCount} selected`),
18
18
  bulkActions.length > 0 && (React__default.createElement("div", { className: "flex items-center gap-2" }, bulkActions.map((action) => {
19
- action.icon;
20
19
  return (React__default.createElement(Button, { key: action.id, color: "neutral", variant: "filled", onClick: () => action.onClick(selectedRows), className: "text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]", size: "sm" }, action.label));
21
20
  })))),
22
21
  React__default.createElement(Button, { color: "primary", variant: "link", onClick: onClearSelection, "aria-label": "Clear selection" },
@@ -0,0 +1,10 @@
1
+ export { useTableColumns } from './useTableColumns';
2
+ export { useTableSelection } from './useTableSelection';
3
+ export { useTableExpansion } from './useTableExpansion';
4
+ export { useTablePagination } from './useTablePagination';
5
+ export { useTableFilter } from './useTableFilter';
6
+ export type { UseTableColumnsReturn } from './useTableColumns';
7
+ export type { UseTableSelectionReturn } from './useTableSelection';
8
+ export type { UseTableExpansionReturn } from './useTableExpansion';
9
+ export type { UseTablePaginationReturn } from './useTablePagination';
10
+ export type { UseTableFilterReturn } from './useTableFilter';
@@ -0,0 +1,16 @@
1
+ import { Column } from '../Table.types';
2
+ export interface UseTableColumnsOptions<T> {
3
+ columns?: Column<T>[];
4
+ initialVisibleColumns?: string[];
5
+ onVisibleColumnsChange?: (visibleColumns: string[]) => void;
6
+ onColumnReorder?: (fromIndex: number, toIndex: number) => void;
7
+ }
8
+ export interface UseTableColumnsReturn<T> {
9
+ visibleColumns: string[];
10
+ columnOrder: string[];
11
+ visibleColumnsList: Column<T>[];
12
+ handleToggleColumn: (columnId: string) => void;
13
+ handleColumnReorder: (fromIndex: number, toIndex: number) => void;
14
+ handleResetToDefault: () => void;
15
+ }
16
+ export declare function useTableColumns<T>({ columns, initialVisibleColumns, onVisibleColumnsChange, onColumnReorder, }: UseTableColumnsOptions<T>): UseTableColumnsReturn<T>;
@@ -0,0 +1,67 @@
1
+ import { useState, useEffect, useCallback, useMemo } from 'react';
2
+
3
+ function useTableColumns({ columns, initialVisibleColumns, onVisibleColumnsChange, onColumnReorder, }) {
4
+ const defaultVisibleColumns = initialVisibleColumns || (columns ? columns.map((col) => col.id) : []);
5
+ const defaultColumnOrder = columns ? columns.map((col) => col.id) : [];
6
+ const [visibleColumns, setVisibleColumns] = useState(defaultVisibleColumns);
7
+ const [columnOrder, setColumnOrder] = useState(defaultColumnOrder);
8
+ // Sync column order when columns prop changes
9
+ useEffect(() => {
10
+ if (columns) {
11
+ setColumnOrder(columns.map((col) => col.id));
12
+ }
13
+ }, [columns]);
14
+ const handleVisibleColumnsChange = useCallback((newVisibleColumns) => {
15
+ setVisibleColumns(newVisibleColumns);
16
+ onVisibleColumnsChange === null || onVisibleColumnsChange === void 0 ? void 0 : onVisibleColumnsChange(newVisibleColumns);
17
+ }, [onVisibleColumnsChange]);
18
+ const handleToggleColumn = useCallback((columnId) => {
19
+ const column = columns === null || columns === void 0 ? void 0 : columns.find(col => col.id === columnId);
20
+ if (column === null || column === void 0 ? void 0 : column.locked)
21
+ return;
22
+ setVisibleColumns(prev => {
23
+ const newVisibleColumns = prev.includes(columnId)
24
+ ? prev.filter((id) => id !== columnId)
25
+ : [...prev, columnId];
26
+ onVisibleColumnsChange === null || onVisibleColumnsChange === void 0 ? void 0 : onVisibleColumnsChange(newVisibleColumns);
27
+ return newVisibleColumns;
28
+ });
29
+ }, [columns, onVisibleColumnsChange]);
30
+ const handleColumnReorder = useCallback((fromIndex, toIndex) => {
31
+ setColumnOrder(prev => {
32
+ const newColumnOrder = [...prev];
33
+ const [movedColumn] = newColumnOrder.splice(fromIndex, 1);
34
+ newColumnOrder.splice(toIndex, 0, movedColumn);
35
+ return newColumnOrder;
36
+ });
37
+ onColumnReorder === null || onColumnReorder === void 0 ? void 0 : onColumnReorder(fromIndex, toIndex);
38
+ }, [onColumnReorder]);
39
+ const handleResetToDefault = useCallback(() => {
40
+ const lockedColumnIds = (columns === null || columns === void 0 ? void 0 : columns.filter(col => col.locked).map(col => col.id)) || [];
41
+ const resetVisibleColumns = Array.from(new Set([...defaultVisibleColumns, ...lockedColumnIds]));
42
+ handleVisibleColumnsChange(resetVisibleColumns);
43
+ if (columns) {
44
+ setColumnOrder(columns.map((col) => col.id));
45
+ }
46
+ }, [columns, defaultVisibleColumns, handleVisibleColumnsChange]);
47
+ const visibleColumnsList = useMemo(() => {
48
+ if (!columns)
49
+ return [];
50
+ if (columnOrder.length > 0) {
51
+ return columnOrder
52
+ .map(colId => columns.find(col => col.id === colId))
53
+ .filter((col) => col !== undefined && visibleColumns.includes(col.id));
54
+ }
55
+ return columns.filter((col) => visibleColumns.includes(col.id));
56
+ }, [columns, columnOrder, visibleColumns]);
57
+ return {
58
+ visibleColumns,
59
+ columnOrder,
60
+ visibleColumnsList,
61
+ handleToggleColumn,
62
+ handleColumnReorder,
63
+ handleResetToDefault,
64
+ };
65
+ }
66
+
67
+ export { useTableColumns };
@@ -0,0 +1,8 @@
1
+ export interface UseTableExpansionOptions {
2
+ expandedRows?: string[];
3
+ onExpandedRowsChange?: (expandedRows: string[]) => void;
4
+ }
5
+ export interface UseTableExpansionReturn {
6
+ handleExpandChange: (rowId: string, expanded: boolean) => void;
7
+ }
8
+ export declare function useTableExpansion({ expandedRows, onExpandedRowsChange, }: UseTableExpansionOptions): UseTableExpansionReturn;
@@ -0,0 +1,15 @@
1
+ import { useCallback } from 'react';
2
+
3
+ function useTableExpansion({ expandedRows = [], onExpandedRowsChange, }) {
4
+ const handleExpandChange = useCallback((rowId, expanded) => {
5
+ if (!onExpandedRowsChange)
6
+ return;
7
+ const newExpandedRows = expanded
8
+ ? [...expandedRows, rowId]
9
+ : expandedRows.filter(id => id !== rowId);
10
+ onExpandedRowsChange(newExpandedRows);
11
+ }, [expandedRows, onExpandedRowsChange]);
12
+ return { handleExpandChange };
13
+ }
14
+
15
+ export { useTableExpansion };
@@ -0,0 +1,12 @@
1
+ import { FilterConfig, FilterOperator } from '../Table.types';
2
+ export interface UseTableFilterOptions {
3
+ filterConfig?: FilterConfig[];
4
+ onFilterChange?: (filterConfig: FilterConfig[]) => void;
5
+ }
6
+ export interface UseTableFilterReturn {
7
+ handleAddFilter: (columnId: string, value: any, operator?: FilterOperator) => void;
8
+ handleRemoveFilter: (columnId: string) => void;
9
+ handleUpdateFilter: (columnId: string, value: any, operator?: FilterOperator) => void;
10
+ handleClearFilters: () => void;
11
+ }
12
+ export declare function useTableFilter({ filterConfig, onFilterChange, }: UseTableFilterOptions): UseTableFilterReturn;
@@ -0,0 +1,37 @@
1
+ import { useCallback } from 'react';
2
+
3
+ function useTableFilter({ filterConfig = [], onFilterChange, }) {
4
+ const handleAddFilter = useCallback((columnId, value, operator) => {
5
+ if (!onFilterChange)
6
+ return;
7
+ const existing = filterConfig.find(f => f.columnId === columnId);
8
+ if (existing) {
9
+ const updated = filterConfig.map(f => f.columnId === columnId ? { ...f, value, operator } : f);
10
+ onFilterChange(updated);
11
+ }
12
+ else {
13
+ onFilterChange([...filterConfig, { columnId, value, operator }]);
14
+ }
15
+ }, [filterConfig, onFilterChange]);
16
+ const handleRemoveFilter = useCallback((columnId) => {
17
+ if (!onFilterChange)
18
+ return;
19
+ onFilterChange(filterConfig.filter(f => f.columnId !== columnId));
20
+ }, [filterConfig, onFilterChange]);
21
+ const handleUpdateFilter = useCallback((columnId, value, operator) => {
22
+ if (!onFilterChange)
23
+ return;
24
+ onFilterChange(filterConfig.map(f => f.columnId === columnId ? { ...f, value, operator } : f));
25
+ }, [filterConfig, onFilterChange]);
26
+ const handleClearFilters = useCallback(() => {
27
+ onFilterChange === null || onFilterChange === void 0 ? void 0 : onFilterChange([]);
28
+ }, [onFilterChange]);
29
+ return {
30
+ handleAddFilter,
31
+ handleRemoveFilter,
32
+ handleUpdateFilter,
33
+ handleClearFilters,
34
+ };
35
+ }
36
+
37
+ export { useTableFilter };
@@ -0,0 +1,12 @@
1
+ export interface UseTablePaginationOptions<T> {
2
+ data?: T[];
3
+ pagination?: {
4
+ pageSize: number;
5
+ currentPage: number;
6
+ totalItems: number;
7
+ };
8
+ }
9
+ export interface UseTablePaginationReturn<T> {
10
+ paginatedData: T[];
11
+ }
12
+ export declare function useTablePagination<T>({ data, pagination, }: UseTablePaginationOptions<T>): UseTablePaginationReturn<T>;
@@ -0,0 +1,13 @@
1
+ import { useMemo } from 'react';
2
+
3
+ function useTablePagination({ data, pagination, }) {
4
+ const paginatedData = useMemo(() => {
5
+ if (pagination && data) {
6
+ return data.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
7
+ }
8
+ return data || [];
9
+ }, [data, pagination]);
10
+ return { paginatedData };
11
+ }
12
+
13
+ export { useTablePagination };
@@ -0,0 +1,17 @@
1
+ import { SelectionType } from '../Table.types';
2
+ export interface UseTableSelectionOptions<T> {
3
+ data: T[];
4
+ rowKey?: keyof T | ((row: T, index: number) => string | number);
5
+ selectable?: boolean;
6
+ selectionType?: SelectionType;
7
+ selectedRows?: string[];
8
+ onSelectionChange?: (selectedRows: string[]) => void;
9
+ }
10
+ export interface UseTableSelectionReturn {
11
+ isAllSelected: boolean;
12
+ isIndeterminate: boolean;
13
+ handleSelectAll: (checked: boolean) => void;
14
+ handleSelectionChange: (rowId: string, selected: boolean) => void;
15
+ handleClearSelection: () => void;
16
+ }
17
+ export declare function useTableSelection<T extends Record<string, any>>({ data, rowKey, selectionType, selectedRows, onSelectionChange, }: UseTableSelectionOptions<T>): UseTableSelectionReturn;
@@ -0,0 +1,40 @@
1
+ import { useMemo, useCallback } from 'react';
2
+ import { getRowId } from '../Table.utils.js';
3
+
4
+ function useTableSelection({ data, rowKey, selectionType = 'checkbox', selectedRows = [], onSelectionChange, }) {
5
+ const isAllSelected = useMemo(() => selectedRows.length === data.length && data.length > 0, [selectedRows.length, data.length]);
6
+ const isIndeterminate = useMemo(() => selectedRows.length > 0 && selectedRows.length < data.length, [selectedRows.length, data.length]);
7
+ const handleSelectAll = useCallback((checked) => {
8
+ if (!onSelectionChange || selectionType !== 'checkbox')
9
+ return;
10
+ if (checked) {
11
+ const allRowIds = data.map((row, index) => getRowId(row, index, rowKey));
12
+ onSelectionChange(allRowIds);
13
+ }
14
+ else {
15
+ onSelectionChange([]);
16
+ }
17
+ }, [data, rowKey, selectionType, onSelectionChange]);
18
+ const handleSelectionChange = useCallback((rowId, selected) => {
19
+ if (!onSelectionChange)
20
+ return;
21
+ const newSelectedRows = selectionType === 'checkbox'
22
+ ? selected
23
+ ? [...selectedRows, rowId]
24
+ : selectedRows.filter(id => id !== rowId)
25
+ : selected ? [rowId] : [];
26
+ onSelectionChange(newSelectedRows);
27
+ }, [selectionType, selectedRows, onSelectionChange]);
28
+ const handleClearSelection = useCallback(() => {
29
+ onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange([]);
30
+ }, [onSelectionChange]);
31
+ return {
32
+ isAllSelected,
33
+ isIndeterminate,
34
+ handleSelectAll,
35
+ handleSelectionChange,
36
+ handleClearSelection,
37
+ };
38
+ }
39
+
40
+ export { useTableSelection };