@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.
- package/README.md +537 -0
- package/package.json +80 -0
- package/src/components/index.ts +50 -0
- package/src/components/navigation/sidebar.tsx +178 -0
- package/src/components/ui/accordion.tsx +58 -0
- package/src/components/ui/alert.tsx +59 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/button.tsx +57 -0
- package/src/components/ui/calendar.tsx +70 -0
- package/src/components/ui/card.tsx +68 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.tsx +12 -0
- package/src/components/ui/dialog.tsx +122 -0
- package/src/components/ui/dropdown-menu.tsx +200 -0
- package/src/components/ui/index.ts +24 -0
- package/src/components/ui/input.tsx +25 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/popover.tsx +31 -0
- package/src/components/ui/progress.tsx +28 -0
- package/src/components/ui/scroll-area.tsx +48 -0
- package/src/components/ui/select.tsx +160 -0
- package/src/components/ui/separator.tsx +31 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/table.tsx +117 -0
- package/src/components/ui/tabs.tsx +55 -0
- package/src/components/ui/textarea.tsx +24 -0
- package/src/components/ui/tooltip.tsx +30 -0
- package/src/components/unified-table/UnifiedTable.tsx +553 -0
- package/src/components/unified-table/__tests__/components/BulkActionBar.test.tsx +477 -0
- package/src/components/unified-table/__tests__/components/ExportButton.test.tsx +467 -0
- package/src/components/unified-table/__tests__/components/InlineEditCell.test.tsx +159 -0
- package/src/components/unified-table/__tests__/components/SavedViewsDropdown.test.tsx +128 -0
- package/src/components/unified-table/__tests__/components/TablePagination.test.tsx +374 -0
- package/src/components/unified-table/__tests__/hooks/useColumnReorder.test.ts +191 -0
- package/src/components/unified-table/__tests__/hooks/useColumnResize.test.ts +122 -0
- package/src/components/unified-table/__tests__/hooks/useColumnVisibility.test.ts +594 -0
- package/src/components/unified-table/__tests__/hooks/useFilters.test.ts +460 -0
- package/src/components/unified-table/__tests__/hooks/usePagination.test.ts +439 -0
- package/src/components/unified-table/__tests__/hooks/useResponsive.test.ts +421 -0
- package/src/components/unified-table/__tests__/hooks/useSelection.test.ts +367 -0
- package/src/components/unified-table/__tests__/hooks/useTableKeyboard.test.ts +803 -0
- package/src/components/unified-table/__tests__/hooks/useTableState.test.ts +210 -0
- package/src/components/unified-table/__tests__/integration/table-with-selection.test.tsx +624 -0
- package/src/components/unified-table/__tests__/utils/export.test.ts +427 -0
- package/src/components/unified-table/components/BulkActionBar/index.tsx +119 -0
- package/src/components/unified-table/components/DataTableCore/index.tsx +473 -0
- package/src/components/unified-table/components/InlineEditCell/index.tsx +159 -0
- package/src/components/unified-table/components/MobileView/Card.tsx +218 -0
- package/src/components/unified-table/components/MobileView/CardActions.tsx +126 -0
- package/src/components/unified-table/components/MobileView/README.md +411 -0
- package/src/components/unified-table/components/MobileView/index.tsx +77 -0
- package/src/components/unified-table/components/MobileView/types.ts +77 -0
- package/src/components/unified-table/components/TableFilters/index.tsx +298 -0
- package/src/components/unified-table/components/TablePagination/index.tsx +157 -0
- package/src/components/unified-table/components/Toolbar/ExportButton.tsx +229 -0
- package/src/components/unified-table/components/Toolbar/SavedViewsDropdown.tsx +251 -0
- package/src/components/unified-table/components/Toolbar/StandardTableToolbar.tsx +146 -0
- package/src/components/unified-table/components/Toolbar/index.tsx +3 -0
- package/src/components/unified-table/hooks/index.ts +21 -0
- package/src/components/unified-table/hooks/useColumnReorder.ts +90 -0
- package/src/components/unified-table/hooks/useColumnResize.ts +123 -0
- package/src/components/unified-table/hooks/useColumnVisibility.ts +92 -0
- package/src/components/unified-table/hooks/useFilters.ts +53 -0
- package/src/components/unified-table/hooks/usePagination.ts +120 -0
- package/src/components/unified-table/hooks/useResponsive.ts +50 -0
- package/src/components/unified-table/hooks/useSelection.ts +152 -0
- package/src/components/unified-table/hooks/useTableKeyboard.ts +206 -0
- package/src/components/unified-table/hooks/useTablePreferences.ts +198 -0
- package/src/components/unified-table/hooks/useTableState.ts +103 -0
- package/src/components/unified-table/hooks/useTableURL.test.tsx +921 -0
- package/src/components/unified-table/hooks/useTableURL.ts +301 -0
- package/src/components/unified-table/index.ts +16 -0
- package/src/components/unified-table/types.ts +393 -0
- package/src/components/unified-table/utils/export.ts +236 -0
- package/src/components/unified-table/utils/index.ts +4 -0
- package/src/components/unified-table/utils/renderers.ts +105 -0
- package/src/components/unified-table/utils/themes.ts +87 -0
- package/src/components/unified-table/utils/validation.ts +122 -0
- package/src/index.ts +6 -0
- package/src/lib/utils.ts +1 -0
- package/src/theme/contract.ts +46 -0
- package/src/theme/index.ts +9 -0
- package/src/theme/tailwind.config.js +70 -0
- package/src/theme/tailwind.preset.ts +93 -0
- package/src/utils/cn.ts +6 -0
- package/src/utils/index.ts +91 -0
package/README.md
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
# @startsimpli/ui
|
|
2
|
+
|
|
3
|
+
Shared UI components package for StartSimpli applications. Provides a comprehensive set of React components including the powerful UnifiedTable component and shadcn/ui primitives.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **UnifiedTable**: Production-ready data table with advanced features
|
|
8
|
+
- Server-side pagination, sorting, and search
|
|
9
|
+
- Column visibility, reordering, and resizing
|
|
10
|
+
- Bulk actions and row actions
|
|
11
|
+
- Inline editing
|
|
12
|
+
- Filters with presets
|
|
13
|
+
- Export to CSV/Excel
|
|
14
|
+
- Saved views
|
|
15
|
+
- Mobile-responsive card view
|
|
16
|
+
- Keyboard navigation
|
|
17
|
+
- URL persistence
|
|
18
|
+
|
|
19
|
+
- **shadcn/ui Components**: Complete set of accessible UI primitives
|
|
20
|
+
- **Tailwind CSS Integration**: Pre-configured theme and utilities
|
|
21
|
+
- **TypeScript**: Full type safety
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @startsimpli/ui
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Peer Dependencies
|
|
30
|
+
|
|
31
|
+
This package requires the following peer dependencies:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install react react-dom next
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### UnifiedTable
|
|
40
|
+
|
|
41
|
+
The UnifiedTable component provides a complete data table solution with server-side operations.
|
|
42
|
+
|
|
43
|
+
#### Basic Example
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { UnifiedTable } from '@startsimpli/ui/table'
|
|
47
|
+
|
|
48
|
+
function MyTable() {
|
|
49
|
+
const [data, setData] = useState([])
|
|
50
|
+
const [loading, setLoading] = useState(false)
|
|
51
|
+
const [page, setPage] = useState(1)
|
|
52
|
+
const [totalCount, setTotalCount] = useState(0)
|
|
53
|
+
|
|
54
|
+
const columns = [
|
|
55
|
+
{
|
|
56
|
+
id: 'name',
|
|
57
|
+
header: 'Name',
|
|
58
|
+
accessorKey: 'name',
|
|
59
|
+
sortable: true,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'email',
|
|
63
|
+
header: 'Email',
|
|
64
|
+
accessorKey: 'email',
|
|
65
|
+
sortable: true,
|
|
66
|
+
},
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<UnifiedTable
|
|
71
|
+
data={data}
|
|
72
|
+
columns={columns}
|
|
73
|
+
tableId="users-table"
|
|
74
|
+
getRowId={(row) => row.id}
|
|
75
|
+
pagination={{
|
|
76
|
+
enabled: true,
|
|
77
|
+
pageSize: 25,
|
|
78
|
+
totalCount,
|
|
79
|
+
currentPage: page,
|
|
80
|
+
serverSide: true, // CRITICAL: Always use server-side pagination
|
|
81
|
+
onPageChange: setPage,
|
|
82
|
+
}}
|
|
83
|
+
sorting={{
|
|
84
|
+
enabled: true,
|
|
85
|
+
serverSide: true, // CRITICAL: Always use server-side sorting
|
|
86
|
+
onChange: (sort) => {
|
|
87
|
+
// Pass sort params to API
|
|
88
|
+
fetchData({ page, sortBy: sort.sortBy, sortDirection: sort.sortDirection })
|
|
89
|
+
},
|
|
90
|
+
}}
|
|
91
|
+
search={{
|
|
92
|
+
enabled: true,
|
|
93
|
+
placeholder: 'Search users...',
|
|
94
|
+
value: searchTerm,
|
|
95
|
+
onChange: (value) => {
|
|
96
|
+
// Pass search query to API
|
|
97
|
+
setSearchTerm(value)
|
|
98
|
+
fetchData({ page: 1, search: value })
|
|
99
|
+
},
|
|
100
|
+
}}
|
|
101
|
+
loading={loading}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### Server-Side Operations (CRITICAL)
|
|
108
|
+
|
|
109
|
+
**ALL tables MUST use server-side operations. This is non-negotiable.**
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
// ✅ CORRECT - Server-side pagination
|
|
113
|
+
pagination={{
|
|
114
|
+
enabled: true,
|
|
115
|
+
pageSize: 25,
|
|
116
|
+
totalCount: 1000, // Total from API
|
|
117
|
+
currentPage: page,
|
|
118
|
+
serverSide: true, // Fetch pages from API
|
|
119
|
+
onPageChange: (newPage) => {
|
|
120
|
+
// Call API with ?page=newPage&pageSize=25
|
|
121
|
+
fetchData({ page: newPage, pageSize: 25 })
|
|
122
|
+
},
|
|
123
|
+
}}
|
|
124
|
+
|
|
125
|
+
// ❌ WRONG - Client-side pagination
|
|
126
|
+
pagination={{
|
|
127
|
+
enabled: true,
|
|
128
|
+
serverSide: false, // Loads all data into memory
|
|
129
|
+
}}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
// ✅ CORRECT - Server-side sorting
|
|
134
|
+
sorting={{
|
|
135
|
+
enabled: true,
|
|
136
|
+
serverSide: true, // Pass sort params to API
|
|
137
|
+
onChange: (sort) => {
|
|
138
|
+
// Call API with ?sortField=name&sortDirection=asc
|
|
139
|
+
fetchData({ sortField: sort.sortBy, sortDirection: sort.sortDirection })
|
|
140
|
+
},
|
|
141
|
+
}}
|
|
142
|
+
|
|
143
|
+
// ❌ WRONG - Client-side sorting
|
|
144
|
+
sorting={{
|
|
145
|
+
enabled: true,
|
|
146
|
+
serverSide: false, // Sorts in memory
|
|
147
|
+
}}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
// ✅ CORRECT - Server-side search
|
|
152
|
+
search={{
|
|
153
|
+
enabled: true,
|
|
154
|
+
value: searchTerm,
|
|
155
|
+
onChange: (value) => {
|
|
156
|
+
// Call API with ?search=value
|
|
157
|
+
fetchData({ search: value })
|
|
158
|
+
},
|
|
159
|
+
}}
|
|
160
|
+
|
|
161
|
+
// ❌ WRONG - Client-side search
|
|
162
|
+
// (No client-side search mode - always passes to API)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### Selection and Bulk Actions
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<UnifiedTable
|
|
169
|
+
selection={{
|
|
170
|
+
enabled: true,
|
|
171
|
+
selectedIds: selectedIds,
|
|
172
|
+
onSelectionChange: setSelectedIds,
|
|
173
|
+
}}
|
|
174
|
+
bulkActions={[
|
|
175
|
+
{
|
|
176
|
+
id: 'delete',
|
|
177
|
+
label: 'Delete',
|
|
178
|
+
icon: Trash2,
|
|
179
|
+
variant: 'gradient-purple',
|
|
180
|
+
onClick: async (ids) => {
|
|
181
|
+
await deleteUsers(Array.from(ids))
|
|
182
|
+
},
|
|
183
|
+
confirmMessage: 'Delete {count} users?',
|
|
184
|
+
},
|
|
185
|
+
]}
|
|
186
|
+
/>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### Filters
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
<UnifiedTable
|
|
193
|
+
filters={{
|
|
194
|
+
enabled: true,
|
|
195
|
+
position: 'top',
|
|
196
|
+
collapsible: true,
|
|
197
|
+
config: {
|
|
198
|
+
sections: [
|
|
199
|
+
{
|
|
200
|
+
id: 'status',
|
|
201
|
+
type: 'chips',
|
|
202
|
+
label: 'Status',
|
|
203
|
+
filters: [
|
|
204
|
+
{ id: 'status', label: 'Status', type: 'chips', options: ['active', 'inactive'] },
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
value: filters,
|
|
210
|
+
onChange: (newFilters) => {
|
|
211
|
+
setFilters(newFilters)
|
|
212
|
+
// Pass to API: ?status=active
|
|
213
|
+
},
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### Inline Editing
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
<UnifiedTable
|
|
222
|
+
columns={[
|
|
223
|
+
{
|
|
224
|
+
id: 'name',
|
|
225
|
+
header: 'Name',
|
|
226
|
+
accessorKey: 'name',
|
|
227
|
+
editable: true,
|
|
228
|
+
editType: 'text',
|
|
229
|
+
validate: (value) => {
|
|
230
|
+
if (!value) return 'Name is required'
|
|
231
|
+
return null
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
]}
|
|
235
|
+
inlineEdit={{
|
|
236
|
+
enabled: true,
|
|
237
|
+
onSave: async (rowId, columnId, value, row) => {
|
|
238
|
+
await updateUser(rowId, { [columnId]: value })
|
|
239
|
+
},
|
|
240
|
+
}}
|
|
241
|
+
/>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### Export
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
<UnifiedTable
|
|
248
|
+
export={{
|
|
249
|
+
enabled: true,
|
|
250
|
+
baseFilename: 'users',
|
|
251
|
+
formats: ['csv', 'excel'],
|
|
252
|
+
showProgress: true,
|
|
253
|
+
onExportComplete: (format, scope, rowCount) => {
|
|
254
|
+
console.log(`Exported ${rowCount} rows as ${format}`)
|
|
255
|
+
},
|
|
256
|
+
}}
|
|
257
|
+
/>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### Saved Views
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
<UnifiedTable
|
|
264
|
+
savedViews={{
|
|
265
|
+
enabled: true,
|
|
266
|
+
views: savedViews,
|
|
267
|
+
currentViewId: currentViewId,
|
|
268
|
+
onSaveView: async (view) => {
|
|
269
|
+
const newView = await saveView(view)
|
|
270
|
+
return newView
|
|
271
|
+
},
|
|
272
|
+
onLoadView: (viewId) => {
|
|
273
|
+
const view = savedViews.find(v => v.id === viewId)
|
|
274
|
+
if (view) {
|
|
275
|
+
// Apply view settings
|
|
276
|
+
setFilters(view.filters || {})
|
|
277
|
+
setColumnVisibility(view.columnVisibility || {})
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
}}
|
|
281
|
+
/>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Mobile Support
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
<UnifiedTable
|
|
288
|
+
mobileConfig={{
|
|
289
|
+
titleKey: 'name',
|
|
290
|
+
subtitleKey: 'email',
|
|
291
|
+
primaryFields: ['name', 'email'],
|
|
292
|
+
secondaryFields: ['status', 'createdAt'],
|
|
293
|
+
}}
|
|
294
|
+
/>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### UI Components
|
|
298
|
+
|
|
299
|
+
Import shadcn/ui components:
|
|
300
|
+
|
|
301
|
+
```tsx
|
|
302
|
+
import { Button } from '@startsimpli/ui/components'
|
|
303
|
+
import { Dialog, DialogContent, DialogHeader } from '@startsimpli/ui/components'
|
|
304
|
+
import { Input, Label } from '@startsimpli/ui/components'
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Utilities
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
import { cn } from '@startsimpli/ui/utils'
|
|
311
|
+
|
|
312
|
+
// Merge Tailwind classes
|
|
313
|
+
const className = cn('base-class', condition && 'conditional-class')
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Tailwind Configuration
|
|
317
|
+
|
|
318
|
+
Extend your Tailwind config with the shared theme:
|
|
319
|
+
|
|
320
|
+
```js
|
|
321
|
+
// tailwind.config.js
|
|
322
|
+
const baseConfig = require('@startsimpli/ui/theme/tailwind.config')
|
|
323
|
+
|
|
324
|
+
module.exports = {
|
|
325
|
+
...baseConfig,
|
|
326
|
+
content: [
|
|
327
|
+
'./src/**/*.{ts,tsx}',
|
|
328
|
+
'./node_modules/@startsimpli/ui/src/**/*.{ts,tsx}',
|
|
329
|
+
],
|
|
330
|
+
theme: {
|
|
331
|
+
...baseConfig.theme,
|
|
332
|
+
extend: {
|
|
333
|
+
...baseConfig.theme.extend,
|
|
334
|
+
// Your custom extensions
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## UnifiedTable API Reference
|
|
341
|
+
|
|
342
|
+
### Props
|
|
343
|
+
|
|
344
|
+
#### Core Props
|
|
345
|
+
|
|
346
|
+
- `data: TData[]` - Array of data to display
|
|
347
|
+
- `columns: ColumnConfig<TData>[]` - Column definitions
|
|
348
|
+
- `tableId: string` - Unique identifier for the table
|
|
349
|
+
- `getRowId: (row: TData) => string` - Function to get unique row ID
|
|
350
|
+
|
|
351
|
+
#### Pagination
|
|
352
|
+
|
|
353
|
+
- `pagination.enabled: boolean` - Enable pagination
|
|
354
|
+
- `pagination.pageSize: number` - Rows per page
|
|
355
|
+
- `pagination.totalCount: number` - Total number of rows (from API)
|
|
356
|
+
- `pagination.currentPage: number` - Current page number (1-indexed)
|
|
357
|
+
- `pagination.serverSide: boolean` - **MUST be true** for production
|
|
358
|
+
- `pagination.onPageChange: (page: number) => void` - Page change callback
|
|
359
|
+
|
|
360
|
+
#### Sorting
|
|
361
|
+
|
|
362
|
+
- `sorting.enabled: boolean` - Enable sorting
|
|
363
|
+
- `sorting.serverSide: boolean` - **MUST be true** for production
|
|
364
|
+
- `sorting.value: SortState` - Current sort state (controlled)
|
|
365
|
+
- `sorting.onChange: (sort: SortState) => void` - Sort change callback
|
|
366
|
+
|
|
367
|
+
#### Search
|
|
368
|
+
|
|
369
|
+
- `search.enabled: boolean` - Enable search
|
|
370
|
+
- `search.placeholder: string` - Search input placeholder
|
|
371
|
+
- `search.value: string` - Current search value (controlled)
|
|
372
|
+
- `search.onChange: (value: string) => void` - Search change callback
|
|
373
|
+
|
|
374
|
+
#### Selection
|
|
375
|
+
|
|
376
|
+
- `selection.enabled: boolean` - Enable row selection
|
|
377
|
+
- `selection.selectedIds: Set<string>` - Selected row IDs (controlled)
|
|
378
|
+
- `selection.onSelectionChange: (ids: Set<string>) => void` - Selection change callback
|
|
379
|
+
- `selection.selectAllPages: boolean` - Whether all pages are selected
|
|
380
|
+
|
|
381
|
+
#### Bulk Actions
|
|
382
|
+
|
|
383
|
+
- `bulkActions: BulkAction[]` - Array of bulk action definitions
|
|
384
|
+
|
|
385
|
+
#### Row Actions
|
|
386
|
+
|
|
387
|
+
- `rowActions: RowAction<TData>[]` - Array of row-level actions
|
|
388
|
+
|
|
389
|
+
#### Filters
|
|
390
|
+
|
|
391
|
+
- `filters.enabled: boolean` - Enable filters
|
|
392
|
+
- `filters.config: FilterConfig` - Filter configuration
|
|
393
|
+
- `filters.value: FilterState` - Current filter values (controlled)
|
|
394
|
+
- `filters.onChange: (filters: FilterState) => void` - Filter change callback
|
|
395
|
+
|
|
396
|
+
#### Column Visibility
|
|
397
|
+
|
|
398
|
+
- `columnVisibility.enabled: boolean` - Enable column visibility controls
|
|
399
|
+
- `columnVisibility.defaultVisible: string[]` - Initially visible columns
|
|
400
|
+
- `columnVisibility.alwaysVisible: string[]` - Columns that cannot be hidden
|
|
401
|
+
- `columnVisibility.persistKey: string` - localStorage key for persistence
|
|
402
|
+
|
|
403
|
+
#### Column Reordering
|
|
404
|
+
|
|
405
|
+
- `columnReorder.enabled: boolean` - Enable drag-and-drop column reordering
|
|
406
|
+
- `columnReorder.initialOrder: string[]` - Initial column order
|
|
407
|
+
- `columnReorder.onOrderChange: (order: string[]) => void` - Order change callback
|
|
408
|
+
|
|
409
|
+
#### Column Resizing
|
|
410
|
+
|
|
411
|
+
- `columnResize.enabled: boolean` - Enable column resizing
|
|
412
|
+
- `columnResize.initialWidths: Record<string, number>` - Initial column widths
|
|
413
|
+
- `columnResize.minWidth: number` - Minimum column width (default: 50)
|
|
414
|
+
- `columnResize.onWidthChange: (widths: Record<string, number>) => void` - Width change callback
|
|
415
|
+
|
|
416
|
+
#### Inline Editing
|
|
417
|
+
|
|
418
|
+
- `inlineEdit.enabled: boolean` - Enable inline editing
|
|
419
|
+
- `inlineEdit.onSave: (rowId, columnId, value, row) => Promise<void>` - Save callback
|
|
420
|
+
- `inlineEdit.optimisticUpdate: boolean` - Apply changes immediately (default: true)
|
|
421
|
+
|
|
422
|
+
#### Export
|
|
423
|
+
|
|
424
|
+
- `export.enabled: boolean` - Enable export functionality
|
|
425
|
+
- `export.baseFilename: string` - Base filename for exports
|
|
426
|
+
- `export.formats: ('csv' | 'excel')[]` - Available export formats
|
|
427
|
+
- `export.showProgress: boolean` - Show progress during export
|
|
428
|
+
|
|
429
|
+
#### Saved Views
|
|
430
|
+
|
|
431
|
+
- `savedViews.enabled: boolean` - Enable saved views
|
|
432
|
+
- `savedViews.views: SavedView[]` - Array of saved views
|
|
433
|
+
- `savedViews.currentViewId: string` - Currently active view ID
|
|
434
|
+
- `savedViews.onSaveView: (view) => Promise<SavedView>` - Save view callback
|
|
435
|
+
- `savedViews.onLoadView: (viewId) => void` - Load view callback
|
|
436
|
+
|
|
437
|
+
#### URL Persistence
|
|
438
|
+
|
|
439
|
+
- `urlPersistence.enabled: boolean` - Enable URL state persistence
|
|
440
|
+
- `urlPersistence.debounceMs: number` - Debounce delay (default: 300ms)
|
|
441
|
+
|
|
442
|
+
#### Mobile
|
|
443
|
+
|
|
444
|
+
- `mobileConfig.titleKey: string` - Key for card title
|
|
445
|
+
- `mobileConfig.subtitleKey: string` - Key for card subtitle
|
|
446
|
+
- `mobileConfig.primaryFields: string[]` - Primary fields to display
|
|
447
|
+
- `mobileConfig.secondaryFields: string[]` - Secondary fields to display
|
|
448
|
+
|
|
449
|
+
#### Other
|
|
450
|
+
|
|
451
|
+
- `loading: boolean` - Show loading state
|
|
452
|
+
- `loadingRows: Set<string>` - Row IDs that are loading
|
|
453
|
+
- `className: string` - Additional CSS classes
|
|
454
|
+
- `emptyState: ReactNode` - Custom empty state component
|
|
455
|
+
- `errorState: ReactNode` - Custom error state component
|
|
456
|
+
- `onRowClick: (row: TData) => void` - Row click handler
|
|
457
|
+
|
|
458
|
+
### Column Configuration
|
|
459
|
+
|
|
460
|
+
```tsx
|
|
461
|
+
interface ColumnConfig<TData> {
|
|
462
|
+
id: string
|
|
463
|
+
header: string | ((props: any) => ReactNode)
|
|
464
|
+
accessorKey?: string // Dot notation supported: 'user.name'
|
|
465
|
+
accessorFn?: (row: TData) => any
|
|
466
|
+
cell?: (row: TData) => ReactNode
|
|
467
|
+
sortable?: boolean
|
|
468
|
+
sortingFn?: (a: TData, b: TData) => number
|
|
469
|
+
width?: string | number
|
|
470
|
+
minWidth?: string | number
|
|
471
|
+
maxWidth?: string | number
|
|
472
|
+
mobilePrimary?: boolean
|
|
473
|
+
mobileSecondary?: boolean
|
|
474
|
+
hideOnMobile?: boolean
|
|
475
|
+
hideable?: boolean
|
|
476
|
+
editable?: boolean
|
|
477
|
+
editType?: 'text' | 'number' | 'select' | 'date'
|
|
478
|
+
editOptions?: string[]
|
|
479
|
+
validate?: (value: any, row: TData) => string | null
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## Testing
|
|
484
|
+
|
|
485
|
+
The package includes comprehensive test coverage for all components.
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# Run tests
|
|
489
|
+
npm test
|
|
490
|
+
|
|
491
|
+
# Watch mode
|
|
492
|
+
npm run test:watch
|
|
493
|
+
|
|
494
|
+
# Coverage
|
|
495
|
+
npm run test:coverage
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## Type Safety
|
|
499
|
+
|
|
500
|
+
All components are fully typed with TypeScript. The UnifiedTable component uses generics for type-safe data handling:
|
|
501
|
+
|
|
502
|
+
```tsx
|
|
503
|
+
interface User {
|
|
504
|
+
id: string
|
|
505
|
+
name: string
|
|
506
|
+
email: string
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
<UnifiedTable<User>
|
|
510
|
+
data={users}
|
|
511
|
+
columns={columns}
|
|
512
|
+
getRowId={(user) => user.id} // Type-safe
|
|
513
|
+
/>
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
## Performance Considerations
|
|
517
|
+
|
|
518
|
+
1. **Always use server-side operations** - Never load entire datasets into memory
|
|
519
|
+
2. **Debounce search inputs** - Use the built-in debouncing (default: 300ms)
|
|
520
|
+
3. **Virtualization for large lists** - Consider implementing virtual scrolling for 1000+ rows
|
|
521
|
+
4. **Optimize column renderers** - Use `useMemo` for complex cell renderers
|
|
522
|
+
5. **Minimize re-renders** - Use controlled state carefully
|
|
523
|
+
|
|
524
|
+
## Browser Support
|
|
525
|
+
|
|
526
|
+
- Chrome (latest)
|
|
527
|
+
- Firefox (latest)
|
|
528
|
+
- Safari (latest)
|
|
529
|
+
- Edge (latest)
|
|
530
|
+
|
|
531
|
+
## Contributing
|
|
532
|
+
|
|
533
|
+
This package is part of the StartSimpli monorepo. Follow the monorepo contribution guidelines.
|
|
534
|
+
|
|
535
|
+
## License
|
|
536
|
+
|
|
537
|
+
Proprietary - StartSimpli
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startsimpli/ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared UI components package for StartSimpli applications",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"files": ["src"],
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./src/index.ts",
|
|
13
|
+
"./table": "./src/components/unified-table/index.ts",
|
|
14
|
+
"./components": "./src/components/index.ts",
|
|
15
|
+
"./hooks": "./src/hooks/index.ts",
|
|
16
|
+
"./utils": "./src/utils/index.ts",
|
|
17
|
+
"./theme": "./src/theme/index.ts",
|
|
18
|
+
"./theme/contract": "./theme/contract.css",
|
|
19
|
+
"./tailwind": "./src/theme/tailwind.preset.ts"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "jest",
|
|
23
|
+
"test:watch": "jest --watch",
|
|
24
|
+
"test:coverage": "jest --coverage",
|
|
25
|
+
"type-check": "tsc --noEmit",
|
|
26
|
+
"lint": "eslint src/**/*.{ts,tsx}"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
30
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
31
|
+
"next": "^14.0.0 || ^15.0.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@hello-pangea/dnd": "^18.0.1",
|
|
35
|
+
"@radix-ui/react-accordion": "^1.2.12",
|
|
36
|
+
"@radix-ui/react-checkbox": "^1.3.3",
|
|
37
|
+
"@radix-ui/react-collapsible": "^1.1.12",
|
|
38
|
+
"@radix-ui/react-dialog": "^1.1.1",
|
|
39
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
40
|
+
"@radix-ui/react-label": "^2.1.0",
|
|
41
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
42
|
+
"@radix-ui/react-progress": "^1.1.7",
|
|
43
|
+
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
44
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
45
|
+
"@radix-ui/react-separator": "^1.1.7",
|
|
46
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
47
|
+
"@radix-ui/react-tabs": "^1.1.13",
|
|
48
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
49
|
+
"class-variance-authority": "^0.7.0",
|
|
50
|
+
"clsx": "^2.1.1",
|
|
51
|
+
"lucide-react": "^0.408.0",
|
|
52
|
+
"react-day-picker": "^9.13.2",
|
|
53
|
+
"tailwind-merge": "^2.4.0",
|
|
54
|
+
"tailwindcss-animate": "^1.0.7",
|
|
55
|
+
"xlsx": "^0.18.5"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@testing-library/jest-dom": "^6.8.0",
|
|
59
|
+
"@testing-library/react": "^16.3.0",
|
|
60
|
+
"@testing-library/user-event": "^14.6.1",
|
|
61
|
+
"@types/jest": "^30.0.0",
|
|
62
|
+
"@types/node": "^20.14.10",
|
|
63
|
+
"@types/react": "^18.3.3",
|
|
64
|
+
"@types/react-dom": "^18.3.0",
|
|
65
|
+
"eslint": "^8.57.0",
|
|
66
|
+
"identity-obj-proxy": "^3.0.0",
|
|
67
|
+
"jest": "^30.1.3",
|
|
68
|
+
"jest-environment-jsdom": "^30.1.2",
|
|
69
|
+
"tailwindcss": "^3.4.6",
|
|
70
|
+
"typescript": "^5.5.3"
|
|
71
|
+
},
|
|
72
|
+
"keywords": [
|
|
73
|
+
"ui",
|
|
74
|
+
"components",
|
|
75
|
+
"table",
|
|
76
|
+
"react",
|
|
77
|
+
"nextjs",
|
|
78
|
+
"startsimpli"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Export UI primitives
|
|
2
|
+
export * from './ui'
|
|
3
|
+
|
|
4
|
+
// Export UnifiedTable (explicit to avoid Card collision with shadcn Card)
|
|
5
|
+
export { UnifiedTable } from './unified-table'
|
|
6
|
+
export type { UnifiedTableProps } from './unified-table'
|
|
7
|
+
export * from './unified-table/types'
|
|
8
|
+
|
|
9
|
+
// MobileView - Card re-exported under non-conflicting names
|
|
10
|
+
export { MobileView, Card as MobileCard, CardActions as MobileCardActions } from './unified-table'
|
|
11
|
+
export type {
|
|
12
|
+
MobileViewProps,
|
|
13
|
+
MobileCardConfig,
|
|
14
|
+
MobileCardField,
|
|
15
|
+
MobileCardAction,
|
|
16
|
+
CardProps as MobileCardProps,
|
|
17
|
+
CardActionsProps as MobileCardActionsProps,
|
|
18
|
+
} from './unified-table'
|
|
19
|
+
export { MOBILE_BREAKPOINT } from './unified-table'
|
|
20
|
+
|
|
21
|
+
// UnifiedTable hooks
|
|
22
|
+
export {
|
|
23
|
+
useTableState,
|
|
24
|
+
useSelection,
|
|
25
|
+
usePagination,
|
|
26
|
+
useFilters,
|
|
27
|
+
useResponsive,
|
|
28
|
+
useColumnVisibility,
|
|
29
|
+
useTablePreferences,
|
|
30
|
+
useTableKeyboard,
|
|
31
|
+
useTableURL,
|
|
32
|
+
useColumnReorder,
|
|
33
|
+
useColumnResize,
|
|
34
|
+
} from './unified-table'
|
|
35
|
+
export type {
|
|
36
|
+
ResponsiveBreakpoints,
|
|
37
|
+
ViewMode,
|
|
38
|
+
UseColumnVisibilityOptions,
|
|
39
|
+
UseTableKeyboardProps,
|
|
40
|
+
UseTableKeyboardReturn,
|
|
41
|
+
UseTableURLConfig,
|
|
42
|
+
TableURLState,
|
|
43
|
+
UseTableURLReturn,
|
|
44
|
+
} from './unified-table'
|
|
45
|
+
|
|
46
|
+
// UnifiedTable utils
|
|
47
|
+
export * from './unified-table/utils'
|
|
48
|
+
|
|
49
|
+
// Export Navigation
|
|
50
|
+
export * from './navigation/sidebar'
|