@startsimpli/ui 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.
Files changed (86) hide show
  1. package/README.md +537 -0
  2. package/package.json +80 -0
  3. package/src/components/index.ts +50 -0
  4. package/src/components/navigation/sidebar.tsx +178 -0
  5. package/src/components/ui/accordion.tsx +58 -0
  6. package/src/components/ui/alert.tsx +59 -0
  7. package/src/components/ui/badge.tsx +36 -0
  8. package/src/components/ui/button.tsx +57 -0
  9. package/src/components/ui/calendar.tsx +70 -0
  10. package/src/components/ui/card.tsx +68 -0
  11. package/src/components/ui/checkbox.tsx +30 -0
  12. package/src/components/ui/collapsible.tsx +12 -0
  13. package/src/components/ui/dialog.tsx +122 -0
  14. package/src/components/ui/dropdown-menu.tsx +200 -0
  15. package/src/components/ui/index.ts +24 -0
  16. package/src/components/ui/input.tsx +25 -0
  17. package/src/components/ui/label.tsx +26 -0
  18. package/src/components/ui/popover.tsx +31 -0
  19. package/src/components/ui/progress.tsx +28 -0
  20. package/src/components/ui/scroll-area.tsx +48 -0
  21. package/src/components/ui/select.tsx +160 -0
  22. package/src/components/ui/separator.tsx +31 -0
  23. package/src/components/ui/skeleton.tsx +15 -0
  24. package/src/components/ui/table.tsx +117 -0
  25. package/src/components/ui/tabs.tsx +55 -0
  26. package/src/components/ui/textarea.tsx +24 -0
  27. package/src/components/ui/tooltip.tsx +30 -0
  28. package/src/components/unified-table/UnifiedTable.tsx +553 -0
  29. package/src/components/unified-table/__tests__/components/BulkActionBar.test.tsx +477 -0
  30. package/src/components/unified-table/__tests__/components/ExportButton.test.tsx +467 -0
  31. package/src/components/unified-table/__tests__/components/InlineEditCell.test.tsx +159 -0
  32. package/src/components/unified-table/__tests__/components/SavedViewsDropdown.test.tsx +128 -0
  33. package/src/components/unified-table/__tests__/components/TablePagination.test.tsx +374 -0
  34. package/src/components/unified-table/__tests__/hooks/useColumnReorder.test.ts +191 -0
  35. package/src/components/unified-table/__tests__/hooks/useColumnResize.test.ts +122 -0
  36. package/src/components/unified-table/__tests__/hooks/useColumnVisibility.test.ts +594 -0
  37. package/src/components/unified-table/__tests__/hooks/useFilters.test.ts +460 -0
  38. package/src/components/unified-table/__tests__/hooks/usePagination.test.ts +439 -0
  39. package/src/components/unified-table/__tests__/hooks/useResponsive.test.ts +421 -0
  40. package/src/components/unified-table/__tests__/hooks/useSelection.test.ts +367 -0
  41. package/src/components/unified-table/__tests__/hooks/useTableKeyboard.test.ts +803 -0
  42. package/src/components/unified-table/__tests__/hooks/useTableState.test.ts +210 -0
  43. package/src/components/unified-table/__tests__/integration/table-with-selection.test.tsx +624 -0
  44. package/src/components/unified-table/__tests__/utils/export.test.ts +427 -0
  45. package/src/components/unified-table/components/BulkActionBar/index.tsx +119 -0
  46. package/src/components/unified-table/components/DataTableCore/index.tsx +473 -0
  47. package/src/components/unified-table/components/InlineEditCell/index.tsx +159 -0
  48. package/src/components/unified-table/components/MobileView/Card.tsx +218 -0
  49. package/src/components/unified-table/components/MobileView/CardActions.tsx +126 -0
  50. package/src/components/unified-table/components/MobileView/README.md +411 -0
  51. package/src/components/unified-table/components/MobileView/index.tsx +77 -0
  52. package/src/components/unified-table/components/MobileView/types.ts +77 -0
  53. package/src/components/unified-table/components/TableFilters/index.tsx +298 -0
  54. package/src/components/unified-table/components/TablePagination/index.tsx +157 -0
  55. package/src/components/unified-table/components/Toolbar/ExportButton.tsx +229 -0
  56. package/src/components/unified-table/components/Toolbar/SavedViewsDropdown.tsx +251 -0
  57. package/src/components/unified-table/components/Toolbar/StandardTableToolbar.tsx +146 -0
  58. package/src/components/unified-table/components/Toolbar/index.tsx +3 -0
  59. package/src/components/unified-table/hooks/index.ts +21 -0
  60. package/src/components/unified-table/hooks/useColumnReorder.ts +90 -0
  61. package/src/components/unified-table/hooks/useColumnResize.ts +123 -0
  62. package/src/components/unified-table/hooks/useColumnVisibility.ts +92 -0
  63. package/src/components/unified-table/hooks/useFilters.ts +53 -0
  64. package/src/components/unified-table/hooks/usePagination.ts +120 -0
  65. package/src/components/unified-table/hooks/useResponsive.ts +50 -0
  66. package/src/components/unified-table/hooks/useSelection.ts +152 -0
  67. package/src/components/unified-table/hooks/useTableKeyboard.ts +206 -0
  68. package/src/components/unified-table/hooks/useTablePreferences.ts +198 -0
  69. package/src/components/unified-table/hooks/useTableState.ts +103 -0
  70. package/src/components/unified-table/hooks/useTableURL.test.tsx +921 -0
  71. package/src/components/unified-table/hooks/useTableURL.ts +301 -0
  72. package/src/components/unified-table/index.ts +16 -0
  73. package/src/components/unified-table/types.ts +393 -0
  74. package/src/components/unified-table/utils/export.ts +236 -0
  75. package/src/components/unified-table/utils/index.ts +4 -0
  76. package/src/components/unified-table/utils/renderers.ts +105 -0
  77. package/src/components/unified-table/utils/themes.ts +87 -0
  78. package/src/components/unified-table/utils/validation.ts +122 -0
  79. package/src/index.ts +6 -0
  80. package/src/lib/utils.ts +1 -0
  81. package/src/theme/contract.ts +46 -0
  82. package/src/theme/index.ts +9 -0
  83. package/src/theme/tailwind.config.js +70 -0
  84. package/src/theme/tailwind.preset.ts +93 -0
  85. package/src/utils/cn.ts +6 -0
  86. package/src/utils/index.ts +91 -0
@@ -0,0 +1,198 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react'
2
+ import { ColumnVisibilityState, SortState } from '../types'
3
+
4
+ interface TablePreferences {
5
+ columnVisibility: ColumnVisibilityState
6
+ columnOrder: string[] | null
7
+ defaultSortColumn: string | null
8
+ defaultSortDirection: 'asc' | 'desc' | null
9
+ pageSize: number
10
+ }
11
+
12
+ interface UseTablePreferencesOptions {
13
+ tableId: string
14
+ enabled?: boolean
15
+ defaultColumnVisibility?: ColumnVisibilityState
16
+ defaultPageSize?: number
17
+ debounceMs?: number
18
+ // Custom fetch function for API calls
19
+ // Applications should provide their own apiFetch implementation
20
+ apiFetch?: typeof fetch
21
+ }
22
+
23
+ interface UseTablePreferencesReturn {
24
+ preferences: TablePreferences | null
25
+ isLoading: boolean
26
+ error: Error | null
27
+ updateColumnVisibility: (visibility: ColumnVisibilityState) => void
28
+ updateColumnOrder: (order: string[]) => void
29
+ updateSortPreference: (sort: SortState) => void
30
+ updatePageSize: (size: number) => void
31
+ resetPreferences: () => Promise<void>
32
+ }
33
+
34
+ export function useTablePreferences({
35
+ tableId,
36
+ enabled = true,
37
+ defaultColumnVisibility = {},
38
+ defaultPageSize = 25,
39
+ debounceMs = 500,
40
+ apiFetch = fetch // Default to native fetch if not provided
41
+ }: UseTablePreferencesOptions): UseTablePreferencesReturn {
42
+ const [preferences, setPreferences] = useState<TablePreferences | null>(null)
43
+ const [isLoading, setIsLoading] = useState(true)
44
+ const [error, setError] = useState<Error | null>(null)
45
+ const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null)
46
+ const pendingUpdatesRef = useRef<Partial<TablePreferences>>({})
47
+
48
+ // Load preferences on mount
49
+ useEffect(() => {
50
+ if (!enabled) {
51
+ setIsLoading(false)
52
+ return
53
+ }
54
+
55
+ const loadPreferences = async () => {
56
+ try {
57
+ const response = await apiFetch(`/api/v1/table-preferences/?tableId=${encodeURIComponent(tableId)}`)
58
+ if (!response.ok) {
59
+ throw new Error('Failed to load table preferences')
60
+ }
61
+ const data = await response.json()
62
+
63
+ if (data) {
64
+ setPreferences(data)
65
+ } else {
66
+ // Use defaults if no saved preferences
67
+ setPreferences({
68
+ columnVisibility: defaultColumnVisibility,
69
+ columnOrder: null,
70
+ defaultSortColumn: null,
71
+ defaultSortDirection: null,
72
+ pageSize: defaultPageSize
73
+ })
74
+ }
75
+ } catch (err) {
76
+ console.error('Error loading table preferences:', err)
77
+ setError(err instanceof Error ? err : new Error('Unknown error'))
78
+ // Fall back to defaults on error
79
+ setPreferences({
80
+ columnVisibility: defaultColumnVisibility,
81
+ columnOrder: null,
82
+ defaultSortColumn: null,
83
+ defaultSortDirection: null,
84
+ pageSize: defaultPageSize
85
+ })
86
+ } finally {
87
+ setIsLoading(false)
88
+ }
89
+ }
90
+
91
+ loadPreferences()
92
+ }, [tableId, enabled, defaultPageSize])
93
+
94
+ // Debounced save function
95
+ const savePreferences = useCallback(async (updates: Partial<TablePreferences>) => {
96
+ // Merge with pending updates
97
+ pendingUpdatesRef.current = { ...pendingUpdatesRef.current, ...updates }
98
+
99
+ // Clear existing timeout
100
+ if (saveTimeoutRef.current) {
101
+ clearTimeout(saveTimeoutRef.current)
102
+ }
103
+
104
+ // Set new debounced timeout
105
+ saveTimeoutRef.current = setTimeout(async () => {
106
+ const updatesToSave = { ...pendingUpdatesRef.current }
107
+ pendingUpdatesRef.current = {}
108
+
109
+ try {
110
+ const response = await apiFetch('/api/v1/table-preferences/', {
111
+ method: 'POST',
112
+ headers: { 'Content-Type': 'application/json' },
113
+ body: JSON.stringify({
114
+ tableId,
115
+ ...updatesToSave
116
+ })
117
+ })
118
+
119
+ if (!response.ok) {
120
+ throw new Error('Failed to save table preferences')
121
+ }
122
+ } catch (err) {
123
+ console.error('Error saving table preferences:', err)
124
+ // Don't update error state for save failures - user can continue working
125
+ }
126
+ }, debounceMs)
127
+ }, [tableId, debounceMs])
128
+
129
+ // Cleanup timeout on unmount
130
+ useEffect(() => {
131
+ return () => {
132
+ if (saveTimeoutRef.current) {
133
+ clearTimeout(saveTimeoutRef.current)
134
+ }
135
+ }
136
+ }, [])
137
+
138
+ const updateColumnVisibility = useCallback((visibility: ColumnVisibilityState) => {
139
+ setPreferences(prev => prev ? { ...prev, columnVisibility: visibility } : null)
140
+ savePreferences({ columnVisibility: visibility })
141
+ }, [savePreferences])
142
+
143
+ const updateColumnOrder = useCallback((order: string[]) => {
144
+ setPreferences(prev => prev ? { ...prev, columnOrder: order } : null)
145
+ savePreferences({ columnOrder: order })
146
+ }, [savePreferences])
147
+
148
+ const updateSortPreference = useCallback((sort: SortState) => {
149
+ setPreferences(prev => prev ? {
150
+ ...prev,
151
+ defaultSortColumn: sort.sortBy,
152
+ defaultSortDirection: sort.sortDirection
153
+ } : null)
154
+ savePreferences({
155
+ defaultSortColumn: sort.sortBy,
156
+ defaultSortDirection: sort.sortDirection
157
+ })
158
+ }, [savePreferences])
159
+
160
+ const updatePageSize = useCallback((size: number) => {
161
+ setPreferences(prev => prev ? { ...prev, pageSize: size } : null)
162
+ savePreferences({ pageSize: size })
163
+ }, [savePreferences])
164
+
165
+ const resetPreferences = useCallback(async () => {
166
+ try {
167
+ const response = await apiFetch(`/api/v1/table-preferences/?tableId=${encodeURIComponent(tableId)}`, {
168
+ method: 'DELETE'
169
+ })
170
+
171
+ if (!response.ok) {
172
+ throw new Error('Failed to reset table preferences')
173
+ }
174
+
175
+ setPreferences({
176
+ columnVisibility: defaultColumnVisibility,
177
+ columnOrder: null,
178
+ defaultSortColumn: null,
179
+ defaultSortDirection: null,
180
+ pageSize: defaultPageSize
181
+ })
182
+ } catch (err) {
183
+ console.error('Error resetting table preferences:', err)
184
+ throw err
185
+ }
186
+ }, [tableId, defaultColumnVisibility, defaultPageSize])
187
+
188
+ return {
189
+ preferences,
190
+ isLoading,
191
+ error,
192
+ updateColumnVisibility,
193
+ updateColumnOrder,
194
+ updateSortPreference,
195
+ updatePageSize,
196
+ resetPreferences
197
+ }
198
+ }
@@ -0,0 +1,103 @@
1
+ import { useState, useCallback, useMemo } from 'react'
2
+ import { UseTableStateReturn, TableState } from '../types'
3
+
4
+ interface UseTableStateProps<TData> {
5
+ initialData?: TData[]
6
+ initialPageSize?: number
7
+ }
8
+
9
+ export function useTableState<TData>({
10
+ initialData = [],
11
+ initialPageSize = 25,
12
+ }: UseTableStateProps<TData>): UseTableStateReturn<TData> {
13
+ const [data, setDataState] = useState<TData[]>(initialData)
14
+ const [currentPage, setCurrentPage] = useState(1)
15
+ const [pageSize, setPageSizeState] = useState(initialPageSize)
16
+ const [searchTerm, setSearchTerm] = useState('')
17
+ const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('')
18
+ const [sortBy, setSortBy] = useState<string | null>(null)
19
+ const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc')
20
+ const [loading, setLoadingState] = useState(false)
21
+ const [loadingRows, setLoadingRowsState] = useState<Set<string>>(new Set())
22
+ const [error, setErrorState] = useState<Error | null>(null)
23
+ const [viewMode, setViewModeState] = useState<'table' | 'card'>('table')
24
+
25
+ // Computed values
26
+ const totalCount = data.length
27
+ const totalPages = Math.max(1, Math.ceil(totalCount / pageSize))
28
+
29
+ const state: TableState<TData> = useMemo(() => ({
30
+ data,
31
+ filteredData: data, // Will be computed by filters hook
32
+ displayedData: data, // Will be computed by pagination
33
+ currentPage,
34
+ pageSize,
35
+ totalPages,
36
+ totalCount,
37
+ selectedIds: new Set(),
38
+ selectAllPages: false,
39
+ activeFilters: {},
40
+ sortBy,
41
+ sortDirection,
42
+ searchTerm,
43
+ debouncedSearchTerm,
44
+ loading,
45
+ loadingRows,
46
+ error,
47
+ viewMode,
48
+ }), [
49
+ data,
50
+ currentPage,
51
+ pageSize,
52
+ totalPages,
53
+ totalCount,
54
+ sortBy,
55
+ sortDirection,
56
+ searchTerm,
57
+ debouncedSearchTerm,
58
+ loading,
59
+ loadingRows,
60
+ error,
61
+ viewMode,
62
+ ])
63
+
64
+ const setData = useCallback((newData: TData[]) => {
65
+ setDataState(newData)
66
+ }, [])
67
+
68
+ const setPage = useCallback((page: number) => {
69
+ setCurrentPage(page)
70
+ }, [])
71
+
72
+ const setPageSize = useCallback((size: number) => {
73
+ setPageSizeState(size)
74
+ setCurrentPage(1) // Reset to first page when changing page size
75
+ }, [])
76
+
77
+ const setLoading = useCallback((isLoading: boolean) => {
78
+ setLoadingState(isLoading)
79
+ }, [])
80
+
81
+ const setLoadingRows = useCallback((ids: Set<string>) => {
82
+ setLoadingRowsState(ids)
83
+ }, [])
84
+
85
+ const setError = useCallback((err: Error | null) => {
86
+ setErrorState(err)
87
+ }, [])
88
+
89
+ const setViewMode = useCallback((mode: 'table' | 'card') => {
90
+ setViewModeState(mode)
91
+ }, [])
92
+
93
+ return {
94
+ state,
95
+ setData,
96
+ setPage,
97
+ setPageSize,
98
+ setLoading,
99
+ setLoadingRows,
100
+ setError,
101
+ setViewMode,
102
+ }
103
+ }