@anymux/ui-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ExplorerLayout-CSIJd7N4.js +105 -0
- package/dist/ExplorerLayout-CSIJd7N4.js.map +1 -0
- package/dist/FileBrowserContext-B6jixa2j.js +11 -0
- package/dist/FileBrowserContext-B6jixa2j.js.map +1 -0
- package/dist/calendar-DSlrbHoj.js +761 -0
- package/dist/calendar-DSlrbHoj.js.map +1 -0
- package/dist/calendar.d.ts +3 -0
- package/dist/calendar.js +3 -0
- package/dist/contacts-DQXTZzHc.js +539 -0
- package/dist/contacts-DQXTZzHc.js.map +1 -0
- package/dist/contacts.d.ts +3 -0
- package/dist/contacts.js +3 -0
- package/dist/file-browser-m5atC3kF.js +6755 -0
- package/dist/file-browser-m5atC3kF.js.map +1 -0
- package/dist/file-browser.d.ts +11 -0
- package/dist/file-browser.js +9 -0
- package/dist/git-B55e6LL-.js +561 -0
- package/dist/git-B55e6LL-.js.map +1 -0
- package/dist/git.d.ts +2 -0
- package/dist/git.js +3 -0
- package/dist/iconMap-V4B8P-Uh.js +206 -0
- package/dist/iconMap-V4B8P-Uh.js.map +1 -0
- package/dist/icons-CIsIOZXR.js +0 -0
- package/dist/icons.d.ts +2 -0
- package/dist/icons.js +4 -0
- package/dist/index-BNmNIWBL.d.ts +71 -0
- package/dist/index-BNmNIWBL.d.ts.map +1 -0
- package/dist/index-Bryv_GCG.d.ts +1481 -0
- package/dist/index-Bryv_GCG.d.ts.map +1 -0
- package/dist/index-CuQIjSXs.d.ts +134 -0
- package/dist/index-CuQIjSXs.d.ts.map +1 -0
- package/dist/index-DSu19mq0.d.ts +153 -0
- package/dist/index-DSu19mq0.d.ts.map +1 -0
- package/dist/index-DmsyeHFr.d.ts +149 -0
- package/dist/index-DmsyeHFr.d.ts.map +1 -0
- package/dist/index-DxnJ8FYM.d.ts +17 -0
- package/dist/index-DxnJ8FYM.d.ts.map +1 -0
- package/dist/index-DzfY1Tok.d.ts +32 -0
- package/dist/index-DzfY1Tok.d.ts.map +1 -0
- package/dist/index-Ml_SgiKa.d.ts +1847 -0
- package/dist/index-Ml_SgiKa.d.ts.map +1 -0
- package/dist/index-kHr9udZD.d.ts +1025 -0
- package/dist/index-kHr9udZD.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +15 -0
- package/dist/layout-Ca_4r8ka.js +89 -0
- package/dist/layout-Ca_4r8ka.js.map +1 -0
- package/dist/layout.d.ts +2 -0
- package/dist/layout.js +5 -0
- package/dist/list-CxfT6hix.js +6831 -0
- package/dist/list-CxfT6hix.js.map +1 -0
- package/dist/list.d.ts +2 -0
- package/dist/list.js +5 -0
- package/dist/media-DZ292aKK.js +557 -0
- package/dist/media-DZ292aKK.js.map +1 -0
- package/dist/media.d.ts +3 -0
- package/dist/media.js +3 -0
- package/dist/tree-Dd9Z0Aso.js +3351 -0
- package/dist/tree-Dd9Z0Aso.js.map +1 -0
- package/dist/tree.d.ts +2 -0
- package/dist/tree.js +6 -0
- package/dist/types-common-CB3kRek8.d.ts +26 -0
- package/dist/types-common-CB3kRek8.d.ts.map +1 -0
- package/dist/utils-B4fdKKsy.js +3 -0
- package/package.json +109 -0
- package/src/calendar/AgendaView.tsx +37 -0
- package/src/calendar/CalendarBrowser.tsx +90 -0
- package/src/calendar/CalendarModel.ts +142 -0
- package/src/calendar/CalendarSidebar.tsx +81 -0
- package/src/calendar/DayView.tsx +76 -0
- package/src/calendar/EventCard.tsx +51 -0
- package/src/calendar/MockCalendarProvider.ts +98 -0
- package/src/calendar/MonthView.tsx +77 -0
- package/src/calendar/WeekView.tsx +129 -0
- package/src/calendar/index.ts +18 -0
- package/src/calendar/types.ts +25 -0
- package/src/contacts/ContactAvatar.tsx +35 -0
- package/src/contacts/ContactBrowser.tsx +56 -0
- package/src/contacts/ContactCard.tsx +37 -0
- package/src/contacts/ContactDetail.tsx +63 -0
- package/src/contacts/ContactGroupSidebar.tsx +40 -0
- package/src/contacts/ContactList.tsx +32 -0
- package/src/contacts/ContactListModel.ts +120 -0
- package/src/contacts/MockContactProvider.ts +77 -0
- package/src/contacts/index.ts +17 -0
- package/src/contacts/types.ts +26 -0
- package/src/demos/CalendarBrowserDemo.tsx +15 -0
- package/src/demos/ContactBrowserDemo.tsx +15 -0
- package/src/demos/MediaBrowserDemo.tsx +15 -0
- package/src/file-browser/adapters/DocumentViewerAdapter.ts +371 -0
- package/src/file-browser/adapters/FileSystemBridge.ts +168 -0
- package/src/file-browser/adapters/GitBrowserAdapter.ts +546 -0
- package/src/file-browser/adapters/README.md +504 -0
- package/src/file-browser/adapters/index.ts +27 -0
- package/src/file-browser/adapters/types.ts +70 -0
- package/src/file-browser/architecture.md +645 -0
- package/src/file-browser/components/CreateItemDialog.tsx +71 -0
- package/src/file-browser/components/DeleteConfirmDialog.tsx +58 -0
- package/src/file-browser/components/FileBrowser.tsx +473 -0
- package/src/file-browser/components/FileBrowserContent.tsx +209 -0
- package/src/file-browser/components/FileBrowserHeader.tsx +151 -0
- package/src/file-browser/components/FileBrowserToolbar.tsx +145 -0
- package/src/file-browser/components/LeftPanel/LeftPanel.tsx +103 -0
- package/src/file-browser/components/LeftPanel/LeftPanelTabs.tsx +70 -0
- package/src/file-browser/components/LeftPanel/TreeNavigationView.tsx +256 -0
- package/src/file-browser/components/PreviewPane.tsx +146 -0
- package/src/file-browser/components/RightPanel/FilePreview.tsx +219 -0
- package/src/file-browser/components/RightPanel/RightPanel.tsx +186 -0
- package/src/file-browser/components/RightPanel/RightPanelToolbar.tsx +113 -0
- package/src/file-browser/components/UploadProgress.tsx +123 -0
- package/src/file-browser/components/ViewerHost.tsx +208 -0
- package/src/file-browser/components/mobile/MobileNavigation.tsx +227 -0
- package/src/file-browser/components/navigation/NavigationButtons.tsx +171 -0
- package/src/file-browser/components/shared/ErrorBoundary.tsx +116 -0
- package/src/file-browser/components/shared/FileBrowserItem.tsx +195 -0
- package/src/file-browser/components/shared/FileIcon.tsx +169 -0
- package/src/file-browser/components/toolbar/ViewModeToggle.tsx +200 -0
- package/src/file-browser/components/views/ListView/ListView.tsx +484 -0
- package/src/file-browser/components/views/ThumbnailView/ThumbnailView.tsx +323 -0
- package/src/file-browser/components/views/TreeView/TreeNode.tsx +186 -0
- package/src/file-browser/components/views/TreeView/TreeNodeList.tsx +191 -0
- package/src/file-browser/components/views/TreeView/TreeView.tsx +200 -0
- package/src/file-browser/components/views/TreemapView/TreemapView.tsx +339 -0
- package/src/file-browser/context/FileBrowserContext.tsx +13 -0
- package/src/file-browser/examples/BasicUsage.tsx +20 -0
- package/src/file-browser/index.ts +98 -0
- package/src/file-browser/models/FileBrowserModel.ts +623 -0
- package/src/file-browser/models/LeftPanelManagerModel.ts +105 -0
- package/src/file-browser/models/NavigationManagerModel.ts +312 -0
- package/src/file-browser/models/ResponsiveLayoutManagerModel.ts +437 -0
- package/src/file-browser/models/RightPanelManagerModel.ts +190 -0
- package/src/file-browser/models/SelectionManagerModel.ts +252 -0
- package/src/file-browser/models/ToolbarManagerModel.ts +144 -0
- package/src/file-browser/models/UploadModel.ts +147 -0
- package/src/file-browser/models/ViewModeManagerModel.ts +185 -0
- package/src/file-browser/models/ViewerHostModel.ts +44 -0
- package/src/file-browser/models/ui/ListViewUIModel.ts +265 -0
- package/src/file-browser/models/ui/PreviewUIModel.ts +297 -0
- package/src/file-browser/models/ui/ThumbnailViewUIModel.ts +254 -0
- package/src/file-browser/models/ui/TreeViewUIModel.ts +128 -0
- package/src/file-browser/models/ui/TreemapViewUIModel.ts +350 -0
- package/src/file-browser/providers/FileSystemListProvider.ts +552 -0
- package/src/file-browser/providers/FileSystemProvider.ts +401 -0
- package/src/file-browser/providers/FileSystemTreeProvider.ts +231 -0
- package/src/file-browser/providers/GitProvider.ts +337 -0
- package/src/file-browser/providers/GitRepositoryProvider.ts +376 -0
- package/src/file-browser/providers/IFileBrowserProvider.ts +56 -0
- package/src/file-browser/providers/MemoryProvider.ts +303 -0
- package/src/file-browser/providers/index.ts +4 -0
- package/src/file-browser/registry/ViewerRegistry.ts +551 -0
- package/src/file-browser/registry/types.ts +144 -0
- package/src/file-browser/scripts/performanceBenchmark.ts +553 -0
- package/src/file-browser/services/ThumbnailCacheService.ts +128 -0
- package/src/file-browser/tasks.md +537 -0
- package/src/file-browser/types/FileBrowserTypes.ts +126 -0
- package/src/file-browser/types/ProviderTypes.ts +155 -0
- package/src/file-browser/types/UITypes.ts +235 -0
- package/src/file-browser/types/ViewModeTypes.ts +150 -0
- package/src/file-browser/utils/gestures.ts +327 -0
- package/src/file-browser/utils/performance.ts +563 -0
- package/src/file-browser/viewers/ImageViewer.tsx +163 -0
- package/src/file-browser/viewers/ImageViewerModel.ts +79 -0
- package/src/file-browser/viewers/TextViewer.tsx +95 -0
- package/src/file-browser/viewers/UnsupportedFileViewer.tsx +57 -0
- package/src/file-browser/viewers/index.ts +61 -0
- package/src/git/BranchList.tsx +128 -0
- package/src/git/CommitGraph.tsx +239 -0
- package/src/git/CommitList.tsx +258 -0
- package/src/git/DiffViewer.tsx +219 -0
- package/src/git/index.ts +4 -0
- package/src/icons/iconMap.ts +146 -0
- package/src/icons/index.ts +9 -0
- package/src/index.ts +13 -0
- package/src/layout/README.md +307 -0
- package/src/layout/components/ExplorerLayout/ExplorerLayout.tsx +178 -0
- package/src/layout/examples/SimpleExample.tsx +60 -0
- package/src/layout/index.ts +6 -0
- package/src/lib/utils.ts +1 -0
- package/src/list/README.md +303 -0
- package/src/list/architecture.md +807 -0
- package/src/list/components/CalculatedGridView.tsx +252 -0
- package/src/list/components/DragPreview.tsx +102 -0
- package/src/list/components/ListContextMenu.tsx +274 -0
- package/src/list/components/ListItem.tsx +761 -0
- package/src/list/components/ListItems.tsx +919 -0
- package/src/list/components/MasonryView.tsx +241 -0
- package/src/list/components/SearchFilter.tsx +44 -0
- package/src/list/components/TreemapView.tsx +709 -0
- package/src/list/components/ViewSizeControls.tsx +205 -0
- package/src/list/components/ViewTypeSelector.tsx +312 -0
- package/src/list/components/VirtualizedDetailsView.tsx +231 -0
- package/src/list/components/VirtualizedGrid.tsx +164 -0
- package/src/list/components/VirtualizedList.tsx +154 -0
- package/src/list/components/VirtualizedMasonryView.tsx +344 -0
- package/src/list/components/shared/EmptyState.tsx +103 -0
- package/src/list/components/shared/ErrorBoundary.tsx +123 -0
- package/src/list/components/shared/ErrorDisplay.tsx +100 -0
- package/src/list/components/shared/ListLoader.tsx +146 -0
- package/src/list/components/shared/LoadingIndicator.tsx +80 -0
- package/src/list/index.ts +92 -0
- package/src/list/models/ListItemsModel.ts +1301 -0
- package/src/list/models/TreemapModel.ts +204 -0
- package/src/list/providers/ListItemsProvider.ts +313 -0
- package/src/list/providers/TestListProvider.ts +604 -0
- package/src/list/tasks.md +937 -0
- package/src/list/types/ListTypes.ts +178 -0
- package/src/list/utils/BenchmarkLogger.ts +243 -0
- package/src/list/utils/DragDropManager.ts +320 -0
- package/src/list/utils/GridLayoutCalculator.ts +290 -0
- package/src/list/utils/ListAccessibility.ts +367 -0
- package/src/list/utils/ListKeyboard.ts +414 -0
- package/src/list/utils/MasonryLayoutCalculator.ts +302 -0
- package/src/list/utils/MasonryLayoutEngine.ts +401 -0
- package/src/list/utils/__tests__/MasonryLayoutEngine.test.ts +157 -0
- package/src/list/utils/__tests__/VirtualizedMasonryView.test.tsx +251 -0
- package/src/media/AlbumSidebar.tsx +48 -0
- package/src/media/MediaBrowser.tsx +92 -0
- package/src/media/MediaBrowserModel.ts +138 -0
- package/src/media/MediaGrid.tsx +50 -0
- package/src/media/MediaList.tsx +49 -0
- package/src/media/MediaPreview.tsx +63 -0
- package/src/media/MediaTimeline.tsx +38 -0
- package/src/media/MockMediaProvider.ts +70 -0
- package/src/media/index.ts +18 -0
- package/src/media/types.ts +21 -0
- package/src/styles/variables.css +60 -0
- package/src/tree/DEVELOPMENT_SUMMARY.md +170 -0
- package/src/tree/__tests__/TreeModel.test.ts +16 -0
- package/src/tree/architecture.md +530 -0
- package/src/tree/components/Tree.tsx +283 -0
- package/src/tree/components/TreeCheckbox.tsx +147 -0
- package/src/tree/components/TreeContextMenu.tsx +139 -0
- package/src/tree/components/TreeNodeList.tsx +329 -0
- package/src/tree/components/TreeTable.tsx +382 -0
- package/src/tree/index.ts +58 -0
- package/src/tree/models/TreeModel.ts +839 -0
- package/src/tree/providers/SimpleTreeProvider.ts +463 -0
- package/src/tree/providers/TestTreeProvider.ts +946 -0
- package/src/tree/providers/TreeProvider.ts +308 -0
- package/src/tree/tasks.md +2046 -0
- package/src/tree/types/TreeTypes.ts +279 -0
- package/src/tree/utils/SelectionTheme.ts +150 -0
- package/src/tree/utils/logger.ts +203 -0
- package/src/types-common.ts +21 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TreeTable - Table component for displaying tree data in tabular format
|
|
3
|
+
*
|
|
4
|
+
* Provides a table view of tree data with:
|
|
5
|
+
* - Hierarchical indentation in tree column
|
|
6
|
+
* - Sortable columns
|
|
7
|
+
* - Filterable columns
|
|
8
|
+
* - Export functionality
|
|
9
|
+
* - Expand/collapse support
|
|
10
|
+
* - Selection support
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import React, { useMemo, useCallback, useState } from 'react';
|
|
14
|
+
import { observer } from 'mobx-react-lite';
|
|
15
|
+
import { ChevronRight, ChevronDown, ArrowUp, ArrowDown, ArrowUpDown, Download, Search } from 'lucide-react';
|
|
16
|
+
import { cn } from '../../lib/utils';
|
|
17
|
+
import { getSelectionClasses, getCustomColorVariables, DEFAULT_SELECTION_THEME } from '../utils/SelectionTheme';
|
|
18
|
+
import type { TreeModel } from '../models/TreeModel';
|
|
19
|
+
import type { TreeTableColumn, TreeNodeData } from '../types/TreeTypes';
|
|
20
|
+
|
|
21
|
+
export interface TreeTableProps {
|
|
22
|
+
/** The tree model managing state */
|
|
23
|
+
treeModel: TreeModel;
|
|
24
|
+
|
|
25
|
+
/** Table columns configuration */
|
|
26
|
+
columns?: TreeTableColumn[];
|
|
27
|
+
|
|
28
|
+
/** Enable animations for expand/collapse transitions */
|
|
29
|
+
enableAnimations?: boolean;
|
|
30
|
+
|
|
31
|
+
/** Additional CSS classes */
|
|
32
|
+
className?: string;
|
|
33
|
+
|
|
34
|
+
/** Whether to show export functionality */
|
|
35
|
+
enableExport?: boolean;
|
|
36
|
+
|
|
37
|
+
/** Whether to enable filtering */
|
|
38
|
+
enableFiltering?: boolean;
|
|
39
|
+
|
|
40
|
+
/** Whether to enable sorting */
|
|
41
|
+
enableSorting?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* TreeTable Component - Table view of tree data
|
|
46
|
+
*/
|
|
47
|
+
export const TreeTable = observer<TreeTableProps>(({
|
|
48
|
+
treeModel,
|
|
49
|
+
columns: propColumns,
|
|
50
|
+
enableAnimations = true,
|
|
51
|
+
className,
|
|
52
|
+
enableExport = true,
|
|
53
|
+
enableFiltering = false,
|
|
54
|
+
enableSorting = true
|
|
55
|
+
}) => {
|
|
56
|
+
const [sortColumn, setSortColumn] = useState<string | null>(null);
|
|
57
|
+
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
|
|
58
|
+
const [filters, setFilters] = useState<Record<string, string>>({});
|
|
59
|
+
|
|
60
|
+
// Default columns if none provided
|
|
61
|
+
const defaultColumns: TreeTableColumn[] = useMemo(() => [
|
|
62
|
+
{
|
|
63
|
+
id: 'name',
|
|
64
|
+
label: 'Name',
|
|
65
|
+
dataKey: 'name',
|
|
66
|
+
width: '100%',
|
|
67
|
+
sortable: true,
|
|
68
|
+
filterable: true,
|
|
69
|
+
isTreeColumn: true
|
|
70
|
+
}
|
|
71
|
+
], []);
|
|
72
|
+
|
|
73
|
+
const columns = propColumns || defaultColumns;
|
|
74
|
+
const isHeaderVisible = columns.length > 1 || enableFiltering || enableExport;
|
|
75
|
+
|
|
76
|
+
// Flatten the tree into a list for table rendering
|
|
77
|
+
const tableData = useMemo(() => {
|
|
78
|
+
const flattenNode = (node: TreeNodeData, level: number = 0): Array<{ node: TreeNodeData; level: number }> => {
|
|
79
|
+
const result = [{ node, level }];
|
|
80
|
+
|
|
81
|
+
if (treeModel.isNodeExpanded(node.id) && node.children) {
|
|
82
|
+
for (const child of node.children) {
|
|
83
|
+
result.push(...flattenNode(child, level + 1));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
let data: Array<{ node: TreeNodeData; level: number }> = [];
|
|
91
|
+
const rootNodes = treeModel.nodes;
|
|
92
|
+
|
|
93
|
+
for (const node of rootNodes) {
|
|
94
|
+
data.push(...flattenNode(node));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Apply filters
|
|
98
|
+
if (Object.values(filters).some(f => f.trim())) {
|
|
99
|
+
data = data.filter(({ node }) => {
|
|
100
|
+
return Object.entries(filters).every(([columnKey, filterValue]) => {
|
|
101
|
+
if (!filterValue.trim()) return true;
|
|
102
|
+
|
|
103
|
+
const column = columns.find(c => c.id === columnKey);
|
|
104
|
+
if (!column) return true;
|
|
105
|
+
|
|
106
|
+
let cellValue = '';
|
|
107
|
+
if (column.renderCell) {
|
|
108
|
+
// If custom renderer, use it to get the text value
|
|
109
|
+
const rendered = column.renderCell((node as any)[column.dataKey], node);
|
|
110
|
+
cellValue = typeof rendered === 'string' ? rendered : String(rendered || '');
|
|
111
|
+
} else if (column.formatValue) {
|
|
112
|
+
cellValue = column.formatValue((node as any)[column.dataKey], node);
|
|
113
|
+
} else {
|
|
114
|
+
cellValue = (node as any)[column.dataKey]?.toString() || '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return cellValue.toLowerCase().includes(filterValue.toLowerCase());
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Apply sorting
|
|
123
|
+
if (sortColumn) {
|
|
124
|
+
const column = columns.find(c => c.id === sortColumn);
|
|
125
|
+
if (column) {
|
|
126
|
+
data.sort((a, b) => {
|
|
127
|
+
let aValue = '';
|
|
128
|
+
let bValue = '';
|
|
129
|
+
|
|
130
|
+
if (column.formatValue) {
|
|
131
|
+
aValue = column.formatValue((a.node as any)[column.dataKey], a.node);
|
|
132
|
+
bValue = column.formatValue((b.node as any)[column.dataKey], b.node);
|
|
133
|
+
} else {
|
|
134
|
+
aValue = (a.node as any)[column.dataKey]?.toString() || '';
|
|
135
|
+
bValue = (b.node as any)[column.dataKey]?.toString() || '';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const compareResult = aValue.localeCompare(bValue);
|
|
139
|
+
return sortDirection === 'asc' ? compareResult : -compareResult;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return data;
|
|
145
|
+
}, [treeModel.nodes, sortColumn, sortDirection, filters, columns, treeModel]);
|
|
146
|
+
|
|
147
|
+
const handleSort = useCallback((columnKey: string) => {
|
|
148
|
+
if (sortColumn === columnKey) {
|
|
149
|
+
setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc');
|
|
150
|
+
} else {
|
|
151
|
+
setSortColumn(columnKey);
|
|
152
|
+
setSortDirection('asc');
|
|
153
|
+
}
|
|
154
|
+
}, [sortColumn]);
|
|
155
|
+
|
|
156
|
+
const handleExport = useCallback(() => {
|
|
157
|
+
const csvContent = [
|
|
158
|
+
// Header
|
|
159
|
+
columns.map(col => col.label).join(','),
|
|
160
|
+
// Rows
|
|
161
|
+
...tableData.map(({ node }) =>
|
|
162
|
+
columns.map(col => {
|
|
163
|
+
let value = '';
|
|
164
|
+
if (col.formatValue) {
|
|
165
|
+
value = col.formatValue((node as any)[col.dataKey], node);
|
|
166
|
+
} else {
|
|
167
|
+
value = (node as any)[col.dataKey]?.toString() || '';
|
|
168
|
+
}
|
|
169
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
170
|
+
}).join(',')
|
|
171
|
+
)
|
|
172
|
+
].join('\n');
|
|
173
|
+
|
|
174
|
+
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
175
|
+
const link = document.createElement('a');
|
|
176
|
+
link.href = URL.createObjectURL(blob);
|
|
177
|
+
link.download = 'tree-data.csv';
|
|
178
|
+
link.click();
|
|
179
|
+
}, [tableData, columns]);
|
|
180
|
+
|
|
181
|
+
const selectionTheme = treeModel.provider.getSelectionTheme?.() || DEFAULT_SELECTION_THEME;
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div className={cn('flex flex-col h-full', className)}>
|
|
185
|
+
{/* Header with controls */}
|
|
186
|
+
{isHeaderVisible && (
|
|
187
|
+
<div className="flex-shrink-0 p-3 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
188
|
+
<div className="flex items-center justify-between gap-4">
|
|
189
|
+
<div className="flex items-center gap-2">
|
|
190
|
+
{enableFiltering && (
|
|
191
|
+
<div className="flex items-center gap-2">
|
|
192
|
+
<Search className="w-4 h-4 text-muted-foreground" />
|
|
193
|
+
<span className="text-sm text-muted-foreground">Filter:</span>
|
|
194
|
+
{columns.filter(c => c.filterable).map(column => (
|
|
195
|
+
<input
|
|
196
|
+
key={column.id}
|
|
197
|
+
type="text"
|
|
198
|
+
placeholder={`Filter ${column.label}`}
|
|
199
|
+
value={filters[column.id] || ''}
|
|
200
|
+
onChange={(e) => setFilters(prev => ({ ...prev, [column.id]: e.target.value }))}
|
|
201
|
+
className="px-2 py-1 text-sm border rounded-md w-32"
|
|
202
|
+
/>
|
|
203
|
+
))}
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
{enableExport && (
|
|
209
|
+
<button
|
|
210
|
+
onClick={handleExport}
|
|
211
|
+
className="flex items-center gap-2 px-3 py-1 text-sm bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
|
212
|
+
>
|
|
213
|
+
<Download className="w-4 h-4" />
|
|
214
|
+
Export CSV
|
|
215
|
+
</button>
|
|
216
|
+
)}
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
{/* Table content */}
|
|
222
|
+
<div className="flex-1 overflow-auto">
|
|
223
|
+
<div className="min-w-full">
|
|
224
|
+
{/* Column headers - only show if more than one column */}
|
|
225
|
+
{columns.length > 1 && (
|
|
226
|
+
<div className="sticky top-0 bg-background border-b z-10">
|
|
227
|
+
<div className="flex">
|
|
228
|
+
{columns.map((column) => (
|
|
229
|
+
<div
|
|
230
|
+
key={column.id}
|
|
231
|
+
className={cn(
|
|
232
|
+
'px-2 py-2 text-sm font-medium text-muted-foreground',
|
|
233
|
+
column.sortable && enableSorting && 'cursor-pointer hover:text-foreground',
|
|
234
|
+
'flex items-center gap-1'
|
|
235
|
+
)}
|
|
236
|
+
style={{ width: column.width }}
|
|
237
|
+
onClick={() => column.sortable && enableSorting && handleSort(column.id)}
|
|
238
|
+
>
|
|
239
|
+
<span>{column.label}</span>
|
|
240
|
+
{column.sortable && enableSorting && (
|
|
241
|
+
<div className="ml-auto">
|
|
242
|
+
{sortColumn === column.id ? (
|
|
243
|
+
sortDirection === 'asc' ? (
|
|
244
|
+
<ArrowUp className="w-3 h-3" />
|
|
245
|
+
) : (
|
|
246
|
+
<ArrowDown className="w-3 h-3" />
|
|
247
|
+
)
|
|
248
|
+
) : (
|
|
249
|
+
<ArrowUpDown className="w-3 h-3 opacity-50" />
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
</div>
|
|
254
|
+
))}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
|
|
259
|
+
{/* Rows */}
|
|
260
|
+
<div>
|
|
261
|
+
{tableData.length === 0 ? (
|
|
262
|
+
<div className="p-8 text-center">
|
|
263
|
+
<p className="text-sm text-muted-foreground">
|
|
264
|
+
{Object.values(filters).some(f => f.trim()) ? 'No results found for current filters' : 'No data available'}
|
|
265
|
+
</p>
|
|
266
|
+
</div>
|
|
267
|
+
) : (
|
|
268
|
+
tableData.map(({ node, level }) => {
|
|
269
|
+
const isSelected = treeModel.isNodeSelected(node.id);
|
|
270
|
+
const isFocused = treeModel.focusedNode === node.id;
|
|
271
|
+
const isExpanded = treeModel.isNodeExpanded(node.id);
|
|
272
|
+
const hasChildren = node.hasChildren || (node.children && node.children.length > 0);
|
|
273
|
+
const useCheckboxes = treeModel.provider.useCheckboxSelection;
|
|
274
|
+
const customColorVars = getCustomColorVariables(selectionTheme);
|
|
275
|
+
|
|
276
|
+
return (
|
|
277
|
+
<div
|
|
278
|
+
key={node.id}
|
|
279
|
+
className={cn(
|
|
280
|
+
'flex w-full cursor-pointer group',
|
|
281
|
+
getSelectionClasses(selectionTheme, isSelected, isFocused, !!useCheckboxes),
|
|
282
|
+
useCheckboxes && 'select-none'
|
|
283
|
+
)}
|
|
284
|
+
style={customColorVars}
|
|
285
|
+
onClick={(event) => {
|
|
286
|
+
if (useCheckboxes) return;
|
|
287
|
+
|
|
288
|
+
const isMultiSelectKeyPressed = event.ctrlKey || event.metaKey;
|
|
289
|
+
|
|
290
|
+
if (treeModel.provider.isMultiSelectEnabled && isMultiSelectKeyPressed) {
|
|
291
|
+
// Toggle selection for multi-select mode with Ctrl/Cmd
|
|
292
|
+
if (isSelected) {
|
|
293
|
+
treeModel.deselectNode(node);
|
|
294
|
+
} else {
|
|
295
|
+
treeModel.selectNode(node);
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
// Single click without modifier
|
|
299
|
+
if (treeModel.provider.isMultiSelectEnabled) {
|
|
300
|
+
// In multi-select mode, single click replaces selection
|
|
301
|
+
treeModel.clearSelection();
|
|
302
|
+
treeModel.selectNode(node);
|
|
303
|
+
} else {
|
|
304
|
+
// In single-select mode, toggle selection
|
|
305
|
+
if (isSelected) {
|
|
306
|
+
treeModel.clearSelection();
|
|
307
|
+
} else {
|
|
308
|
+
treeModel.selectNode(node);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}}
|
|
313
|
+
onDoubleClick={() => hasChildren && treeModel.toggleExpansion(node)}
|
|
314
|
+
>
|
|
315
|
+
{columns.map((column, columnIndex) => {
|
|
316
|
+
let cellContent: React.ReactNode = '';
|
|
317
|
+
|
|
318
|
+
if (column.renderCell) {
|
|
319
|
+
cellContent = column.renderCell((node as any)[column.dataKey], node);
|
|
320
|
+
} else if (column.formatValue) {
|
|
321
|
+
cellContent = column.formatValue((node as any)[column.dataKey], node);
|
|
322
|
+
} else {
|
|
323
|
+
cellContent = (node as any)[column.dataKey]?.toString() || '';
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// For tree column, add indentation and controls
|
|
327
|
+
if (column.isTreeColumn) {
|
|
328
|
+
return (
|
|
329
|
+
<div
|
|
330
|
+
key={column.id}
|
|
331
|
+
className="flex items-center px-2 py-1 text-sm"
|
|
332
|
+
style={{ width: column.width }}
|
|
333
|
+
>
|
|
334
|
+
{/* Indentation */}
|
|
335
|
+
<div style={{ width: level * 16 }} className="flex-shrink-0" />
|
|
336
|
+
|
|
337
|
+
{/* Expand/Collapse Icon */}
|
|
338
|
+
<div className="flex-shrink-0 w-4 h-4 mr-1">
|
|
339
|
+
{hasChildren && (
|
|
340
|
+
<button
|
|
341
|
+
onClick={(e) => {
|
|
342
|
+
e.stopPropagation();
|
|
343
|
+
treeModel.toggleExpansion(node);
|
|
344
|
+
}}
|
|
345
|
+
className="w-4 h-4 flex items-center justify-center transition-transform duration-200 hover:bg-accent/20"
|
|
346
|
+
>
|
|
347
|
+
{isExpanded ? (
|
|
348
|
+
<ChevronDown className="w-3 h-3" />
|
|
349
|
+
) : (
|
|
350
|
+
<ChevronRight className="w-3 h-3" />
|
|
351
|
+
)}
|
|
352
|
+
</button>
|
|
353
|
+
)}
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
{/* Content */}
|
|
357
|
+
<span className="flex-1 truncate">{cellContent}</span>
|
|
358
|
+
</div>
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Regular column
|
|
363
|
+
return (
|
|
364
|
+
<div
|
|
365
|
+
key={column.id}
|
|
366
|
+
className="px-2 py-1 text-sm truncate"
|
|
367
|
+
style={{ width: column.width }}
|
|
368
|
+
>
|
|
369
|
+
{cellContent}
|
|
370
|
+
</div>
|
|
371
|
+
);
|
|
372
|
+
})}
|
|
373
|
+
</div>
|
|
374
|
+
);
|
|
375
|
+
})
|
|
376
|
+
)}
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
);
|
|
382
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TreeComponent - Reusable tree component with MobX state management
|
|
3
|
+
*
|
|
4
|
+
* A high-performance, accessible tree component that supports:
|
|
5
|
+
* - Multi-selection
|
|
6
|
+
* - Keyboard navigation
|
|
7
|
+
* - Context menus
|
|
8
|
+
* - Lazy loading
|
|
9
|
+
* - Custom node rendering
|
|
10
|
+
* - Drag and drop (future)
|
|
11
|
+
* - Virtualization (future)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* TreeComponent Module Exports
|
|
16
|
+
*
|
|
17
|
+
* This file exports all public APIs for the TreeComponent system,
|
|
18
|
+
* including types, providers, models, and React components.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// Core type definitions
|
|
22
|
+
export * from './types/TreeTypes';
|
|
23
|
+
|
|
24
|
+
// Provider interfaces
|
|
25
|
+
export * from './providers/TreeProvider';
|
|
26
|
+
export { TestTreeProvider } from './providers/TestTreeProvider';
|
|
27
|
+
export { SimpleTreeProvider } from './providers/SimpleTreeProvider';
|
|
28
|
+
|
|
29
|
+
// Models
|
|
30
|
+
export * from './models/TreeModel';
|
|
31
|
+
|
|
32
|
+
// Components
|
|
33
|
+
export * from './components/Tree';
|
|
34
|
+
export * from './components/TreeNodeList';
|
|
35
|
+
export * from './components/TreeContextMenu';
|
|
36
|
+
export * from './components/TreeTable';
|
|
37
|
+
export { TreeCheckbox } from './components/TreeCheckbox';
|
|
38
|
+
export type { CheckboxState } from './components/TreeCheckbox';
|
|
39
|
+
// export * from './components/TreeNode';
|
|
40
|
+
|
|
41
|
+
// Core Types
|
|
42
|
+
export type { TreeNodeData, TreeLoadResult, TreeLoadOptions, TreeTableColumn, TreeTableSort, TreeTableExportOptions } from './types/TreeTypes';
|
|
43
|
+
|
|
44
|
+
// Provider System
|
|
45
|
+
export type { TreeProvider } from './providers/TreeProvider';
|
|
46
|
+
|
|
47
|
+
// MobX Models
|
|
48
|
+
export { TreeModel } from './models/TreeModel';
|
|
49
|
+
|
|
50
|
+
// React Components
|
|
51
|
+
export { Tree } from './components/Tree';
|
|
52
|
+
export type { TreeProps } from './components/Tree';
|
|
53
|
+
export { TreeNodeList } from './components/TreeNodeList';
|
|
54
|
+
export type { TreeNodeListProps } from './components/TreeNodeList';
|
|
55
|
+
|
|
56
|
+
// Utilities
|
|
57
|
+
export { getCustomColorVariables, getSelectionClasses, DEFAULT_SELECTION_THEME } from './utils/SelectionTheme';
|
|
58
|
+
export { logger } from './utils/logger';
|