@moontra/moonui 4.1.0 → 5.0.1
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/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.global.js +9 -9
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +21 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -15
- package/dist/index.mjs.map +1 -1
- package/dist/lib/theme.global.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/use-intersection-observer.test.tsx +18 -4
- package/src/__tests__/use-local-storage.test.tsx +34 -20
- package/src/components/ui/__tests__/avatar.test.tsx +5 -7
- package/src/components/ui/__tests__/badge.test.tsx +24 -14
- package/src/components/ui/__tests__/breadcrumb.test.tsx +4 -7
- package/src/components/ui/__tests__/button.test.tsx +6 -3
- package/src/components/ui/__tests__/card.test.tsx +4 -1
- package/src/components/ui/__tests__/checkbox.test.tsx +4 -1
- package/src/components/ui/__tests__/command.test.tsx +268 -124
- package/src/components/ui/__tests__/data-table-basic.test.tsx +271 -0
- package/src/components/ui/__tests__/date-picker.test.tsx +184 -197
- package/src/components/ui/__tests__/progress.test.tsx +0 -1
- package/src/components/ui/__tests__/table.test.tsx +8 -2
- package/src/components/ui/__tests__/tabs.test.tsx +5 -3
- package/src/components/ui/__tests__/toast.test.tsx +461 -159
- package/src/components/ui/__tests__/tooltip.test.tsx +387 -264
- package/src/components/ui/badge.tsx +4 -4
- package/src/components/ui/command.tsx +11 -7
- package/src/components/ui/date-picker.tsx +12 -6
- package/src/components/ui/toast.tsx +3 -0
- package/src/styles/tokens.css +21 -6
- package/src/use-intersection-observer.tsx +9 -0
- package/src/use-local-storage.tsx +35 -1
- package/src/components/ui/__tests__/data-table.test.tsx +0 -256
- package/src/components/ui/data-table.stories.tsx +0 -511
|
@@ -1,511 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
-
import { DataTable } from './data-table';
|
|
5
|
-
import { Badge } from './badge';
|
|
6
|
-
import { Button } from './button';
|
|
7
|
-
import { MoreHorizontal, ArrowUpDown, Edit, Trash2 } from 'lucide-react';
|
|
8
|
-
import { ColumnDef } from '@tanstack/react-table';
|
|
9
|
-
|
|
10
|
-
// Sample data types
|
|
11
|
-
type User = {
|
|
12
|
-
id: string;
|
|
13
|
-
name: string;
|
|
14
|
-
email: string;
|
|
15
|
-
role: 'admin' | 'user' | 'moderator';
|
|
16
|
-
status: 'active' | 'inactive' | 'pending';
|
|
17
|
-
createdAt: string;
|
|
18
|
-
lastLogin: string;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
type Product = {
|
|
22
|
-
id: string;
|
|
23
|
-
name: string;
|
|
24
|
-
category: string;
|
|
25
|
-
price: number;
|
|
26
|
-
stock: number;
|
|
27
|
-
status: 'available' | 'out_of_stock' | 'discontinued';
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// Sample data
|
|
31
|
-
const userData: User[] = [
|
|
32
|
-
{
|
|
33
|
-
id: '1',
|
|
34
|
-
name: 'John Doe',
|
|
35
|
-
email: 'john@example.com',
|
|
36
|
-
role: 'admin',
|
|
37
|
-
status: 'active',
|
|
38
|
-
createdAt: '2023-01-15',
|
|
39
|
-
lastLogin: '2024-01-15',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: '2',
|
|
43
|
-
name: 'Jane Smith',
|
|
44
|
-
email: 'jane@example.com',
|
|
45
|
-
role: 'user',
|
|
46
|
-
status: 'active',
|
|
47
|
-
createdAt: '2023-02-20',
|
|
48
|
-
lastLogin: '2024-01-14',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
id: '3',
|
|
52
|
-
name: 'Bob Johnson',
|
|
53
|
-
email: 'bob@example.com',
|
|
54
|
-
role: 'moderator',
|
|
55
|
-
status: 'inactive',
|
|
56
|
-
createdAt: '2023-03-10',
|
|
57
|
-
lastLogin: '2024-01-10',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: '4',
|
|
61
|
-
name: 'Alice Brown',
|
|
62
|
-
email: 'alice@example.com',
|
|
63
|
-
role: 'user',
|
|
64
|
-
status: 'pending',
|
|
65
|
-
createdAt: '2023-04-05',
|
|
66
|
-
lastLogin: '2024-01-12',
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
id: '5',
|
|
70
|
-
name: 'Charlie Wilson',
|
|
71
|
-
email: 'charlie@example.com',
|
|
72
|
-
role: 'user',
|
|
73
|
-
status: 'active',
|
|
74
|
-
createdAt: '2023-05-01',
|
|
75
|
-
lastLogin: '2024-01-11',
|
|
76
|
-
},
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
const productData: Product[] = [
|
|
80
|
-
{
|
|
81
|
-
id: '1',
|
|
82
|
-
name: 'Wireless Headphones',
|
|
83
|
-
category: 'Electronics',
|
|
84
|
-
price: 299.99,
|
|
85
|
-
stock: 45,
|
|
86
|
-
status: 'available',
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
id: '2',
|
|
90
|
-
name: 'Gaming Mouse',
|
|
91
|
-
category: 'Electronics',
|
|
92
|
-
price: 89.99,
|
|
93
|
-
stock: 0,
|
|
94
|
-
status: 'out_of_stock',
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
id: '3',
|
|
98
|
-
name: 'Mechanical Keyboard',
|
|
99
|
-
category: 'Electronics',
|
|
100
|
-
price: 159.99,
|
|
101
|
-
stock: 23,
|
|
102
|
-
status: 'available',
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
id: '4',
|
|
106
|
-
name: 'USB-C Cable',
|
|
107
|
-
category: 'Accessories',
|
|
108
|
-
price: 19.99,
|
|
109
|
-
stock: 100,
|
|
110
|
-
status: 'available',
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
id: '5',
|
|
114
|
-
name: 'Laptop Stand',
|
|
115
|
-
category: 'Accessories',
|
|
116
|
-
price: 49.99,
|
|
117
|
-
stock: 0,
|
|
118
|
-
status: 'discontinued',
|
|
119
|
-
},
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
// User columns
|
|
123
|
-
const userColumns: ColumnDef<User>[] = [
|
|
124
|
-
{
|
|
125
|
-
accessorKey: 'name',
|
|
126
|
-
header: ({ column }) => (
|
|
127
|
-
<Button
|
|
128
|
-
variant="ghost"
|
|
129
|
-
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
130
|
-
>
|
|
131
|
-
Name
|
|
132
|
-
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
133
|
-
</Button>
|
|
134
|
-
),
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
accessorKey: 'email',
|
|
138
|
-
header: 'Email',
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
accessorKey: 'role',
|
|
142
|
-
header: 'Role',
|
|
143
|
-
cell: ({ row }) => {
|
|
144
|
-
const role = row.getValue('role') as string;
|
|
145
|
-
return (
|
|
146
|
-
<Badge variant={role === 'admin' ? 'admin' : 'secondary'}>
|
|
147
|
-
{role}
|
|
148
|
-
</Badge>
|
|
149
|
-
);
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
accessorKey: 'status',
|
|
154
|
-
header: 'Status',
|
|
155
|
-
cell: ({ row }) => {
|
|
156
|
-
const status = row.getValue('status') as string;
|
|
157
|
-
return (
|
|
158
|
-
<Badge
|
|
159
|
-
variant={
|
|
160
|
-
status === 'active'
|
|
161
|
-
? 'success'
|
|
162
|
-
: status === 'inactive'
|
|
163
|
-
? 'secondary'
|
|
164
|
-
: 'outline'
|
|
165
|
-
}
|
|
166
|
-
>
|
|
167
|
-
{status}
|
|
168
|
-
</Badge>
|
|
169
|
-
);
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
accessorKey: 'createdAt',
|
|
174
|
-
header: 'Created',
|
|
175
|
-
cell: ({ row }) => {
|
|
176
|
-
const date = row.getValue('createdAt') as string;
|
|
177
|
-
return new Date(date).toLocaleDateString();
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
id: 'actions',
|
|
182
|
-
cell: ({ row }) => {
|
|
183
|
-
const user = row.original;
|
|
184
|
-
return (
|
|
185
|
-
<div className="flex items-center gap-2">
|
|
186
|
-
<Button variant="ghost" size="sm">
|
|
187
|
-
<Edit className="h-4 w-4" />
|
|
188
|
-
</Button>
|
|
189
|
-
<Button variant="ghost" size="sm">
|
|
190
|
-
<Trash2 className="h-4 w-4" />
|
|
191
|
-
</Button>
|
|
192
|
-
<Button variant="ghost" size="sm">
|
|
193
|
-
<MoreHorizontal className="h-4 w-4" />
|
|
194
|
-
</Button>
|
|
195
|
-
</div>
|
|
196
|
-
);
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
];
|
|
200
|
-
|
|
201
|
-
// Product columns
|
|
202
|
-
const productColumns: ColumnDef<Product>[] = [
|
|
203
|
-
{
|
|
204
|
-
accessorKey: 'name',
|
|
205
|
-
header: ({ column }) => (
|
|
206
|
-
<Button
|
|
207
|
-
variant="ghost"
|
|
208
|
-
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
209
|
-
>
|
|
210
|
-
Product
|
|
211
|
-
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
212
|
-
</Button>
|
|
213
|
-
),
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
accessorKey: 'category',
|
|
217
|
-
header: 'Category',
|
|
218
|
-
cell: ({ row }) => {
|
|
219
|
-
const category = row.getValue('category') as string;
|
|
220
|
-
return <Badge variant="outline">{category}</Badge>;
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
accessorKey: 'price',
|
|
225
|
-
header: ({ column }) => (
|
|
226
|
-
<Button
|
|
227
|
-
variant="ghost"
|
|
228
|
-
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
229
|
-
>
|
|
230
|
-
Price
|
|
231
|
-
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
232
|
-
</Button>
|
|
233
|
-
),
|
|
234
|
-
cell: ({ row }) => {
|
|
235
|
-
const price = parseFloat(row.getValue('price'));
|
|
236
|
-
return new Intl.NumberFormat('en-US', {
|
|
237
|
-
style: 'currency',
|
|
238
|
-
currency: 'USD',
|
|
239
|
-
}).format(price);
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
accessorKey: 'stock',
|
|
244
|
-
header: ({ column }) => (
|
|
245
|
-
<Button
|
|
246
|
-
variant="ghost"
|
|
247
|
-
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
248
|
-
>
|
|
249
|
-
Stock
|
|
250
|
-
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
251
|
-
</Button>
|
|
252
|
-
),
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
accessorKey: 'status',
|
|
256
|
-
header: 'Status',
|
|
257
|
-
cell: ({ row }) => {
|
|
258
|
-
const status = row.getValue('status') as string;
|
|
259
|
-
return (
|
|
260
|
-
<Badge
|
|
261
|
-
variant={
|
|
262
|
-
status === 'available'
|
|
263
|
-
? 'default'
|
|
264
|
-
: status === 'out_of_stock'
|
|
265
|
-
? 'secondary'
|
|
266
|
-
: 'outline'
|
|
267
|
-
}
|
|
268
|
-
>
|
|
269
|
-
{status.replace('_', ' ')}
|
|
270
|
-
</Badge>
|
|
271
|
-
);
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
];
|
|
275
|
-
|
|
276
|
-
const meta: Meta<typeof DataTable> = {
|
|
277
|
-
title: 'Components/DataTable',
|
|
278
|
-
component: DataTable,
|
|
279
|
-
parameters: {
|
|
280
|
-
layout: 'fullscreen',
|
|
281
|
-
docs: {
|
|
282
|
-
description: {
|
|
283
|
-
component: 'A powerful data table component with sorting, filtering, and pagination.',
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
tags: ['autodocs'],
|
|
288
|
-
argTypes: {
|
|
289
|
-
columns: {
|
|
290
|
-
description: 'Column definitions for the table',
|
|
291
|
-
},
|
|
292
|
-
data: {
|
|
293
|
-
description: 'Data to display in the table',
|
|
294
|
-
},
|
|
295
|
-
searchable: {
|
|
296
|
-
control: 'boolean',
|
|
297
|
-
description: 'Enable search functionality',
|
|
298
|
-
},
|
|
299
|
-
filterable: {
|
|
300
|
-
control: 'boolean',
|
|
301
|
-
description: 'Enable column filtering',
|
|
302
|
-
},
|
|
303
|
-
exportable: {
|
|
304
|
-
control: 'boolean',
|
|
305
|
-
description: 'Enable data export',
|
|
306
|
-
},
|
|
307
|
-
},
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
export default meta;
|
|
311
|
-
type Story = StoryObj<typeof meta>;
|
|
312
|
-
|
|
313
|
-
export const Default: Story = {
|
|
314
|
-
args: {
|
|
315
|
-
columns: userColumns,
|
|
316
|
-
data: userData,
|
|
317
|
-
},
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
export const WithSearch: Story = {
|
|
321
|
-
args: {
|
|
322
|
-
columns: userColumns,
|
|
323
|
-
data: userData,
|
|
324
|
-
searchable: true,
|
|
325
|
-
},
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
export const WithFilters: Story = {
|
|
329
|
-
args: {
|
|
330
|
-
columns: userColumns,
|
|
331
|
-
data: userData,
|
|
332
|
-
searchable: true,
|
|
333
|
-
filterable: true,
|
|
334
|
-
},
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
export const WithExport: Story = {
|
|
338
|
-
args: {
|
|
339
|
-
columns: userColumns,
|
|
340
|
-
data: userData,
|
|
341
|
-
searchable: true,
|
|
342
|
-
filterable: true,
|
|
343
|
-
exportable: true,
|
|
344
|
-
},
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
export const ProductTable: Story = {
|
|
348
|
-
args: {
|
|
349
|
-
columns: productColumns,
|
|
350
|
-
data: productData,
|
|
351
|
-
searchable: true,
|
|
352
|
-
filterable: true,
|
|
353
|
-
},
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
export const LargeDataset: Story = {
|
|
357
|
-
args: {
|
|
358
|
-
columns: userColumns,
|
|
359
|
-
data: Array.from({ length: 50 }, (_, i) => ({
|
|
360
|
-
id: `${i + 1}`,
|
|
361
|
-
name: `User ${i + 1}`,
|
|
362
|
-
email: `user${i + 1}@example.com`,
|
|
363
|
-
role: ['admin', 'user', 'moderator'][i % 3] as 'admin' | 'user' | 'moderator',
|
|
364
|
-
status: ['active', 'inactive', 'pending'][i % 3] as 'active' | 'inactive' | 'pending',
|
|
365
|
-
createdAt: new Date(2023, Math.floor(i / 12), (i % 30) + 1).toISOString().split('T')[0],
|
|
366
|
-
lastLogin: new Date(2024, 0, (i % 15) + 1).toISOString().split('T')[0],
|
|
367
|
-
})),
|
|
368
|
-
searchable: true,
|
|
369
|
-
filterable: true,
|
|
370
|
-
exportable: true,
|
|
371
|
-
},
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
export const EmptyState: Story = {
|
|
375
|
-
args: {
|
|
376
|
-
columns: userColumns,
|
|
377
|
-
data: [],
|
|
378
|
-
searchable: true,
|
|
379
|
-
filterable: true,
|
|
380
|
-
},
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
export const SingleRow: Story = {
|
|
384
|
-
args: {
|
|
385
|
-
columns: userColumns,
|
|
386
|
-
data: [userData[0]],
|
|
387
|
-
searchable: true,
|
|
388
|
-
},
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
export const CustomPageSize: Story = {
|
|
392
|
-
args: {
|
|
393
|
-
columns: productColumns,
|
|
394
|
-
data: productData,
|
|
395
|
-
searchable: true,
|
|
396
|
-
filterable: true,
|
|
397
|
-
// Custom page size would be handled by the DataTable component
|
|
398
|
-
},
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
export const CompactTable: Story = {
|
|
402
|
-
args: {
|
|
403
|
-
columns: productColumns.map(col => ({
|
|
404
|
-
...col,
|
|
405
|
-
// Add compact styling
|
|
406
|
-
})),
|
|
407
|
-
data: productData,
|
|
408
|
-
searchable: true,
|
|
409
|
-
// Compact prop would be handled by the DataTable component
|
|
410
|
-
},
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
export const InteractiveExample: Story = {
|
|
414
|
-
render: () => {
|
|
415
|
-
const columns: ColumnDef<User>[] = [
|
|
416
|
-
{
|
|
417
|
-
accessorKey: 'name',
|
|
418
|
-
header: ({ column }) => (
|
|
419
|
-
<Button
|
|
420
|
-
variant="ghost"
|
|
421
|
-
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
422
|
-
>
|
|
423
|
-
Name
|
|
424
|
-
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
425
|
-
</Button>
|
|
426
|
-
),
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
accessorKey: 'email',
|
|
430
|
-
header: 'Email',
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
accessorKey: 'role',
|
|
434
|
-
header: 'Role',
|
|
435
|
-
cell: ({ row }) => {
|
|
436
|
-
const role = row.getValue('role') as string;
|
|
437
|
-
return (
|
|
438
|
-
<Badge
|
|
439
|
-
variant={role === 'admin' ? 'default' : 'secondary'}
|
|
440
|
-
className="cursor-pointer"
|
|
441
|
-
onClick={() => alert(`Role: ${role}`)}
|
|
442
|
-
>
|
|
443
|
-
{role}
|
|
444
|
-
</Badge>
|
|
445
|
-
);
|
|
446
|
-
},
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
accessorKey: 'status',
|
|
450
|
-
header: 'Status',
|
|
451
|
-
cell: ({ row }) => {
|
|
452
|
-
const status = row.getValue('status') as string;
|
|
453
|
-
return (
|
|
454
|
-
<Badge
|
|
455
|
-
variant={
|
|
456
|
-
status === 'active'
|
|
457
|
-
? 'default'
|
|
458
|
-
: status === 'inactive'
|
|
459
|
-
? 'secondary'
|
|
460
|
-
: 'outline'
|
|
461
|
-
}
|
|
462
|
-
className="cursor-pointer"
|
|
463
|
-
onClick={() => alert(`Status: ${status}`)}
|
|
464
|
-
>
|
|
465
|
-
{status}
|
|
466
|
-
</Badge>
|
|
467
|
-
);
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
{
|
|
471
|
-
id: 'actions',
|
|
472
|
-
cell: ({ row }) => {
|
|
473
|
-
const user = row.original;
|
|
474
|
-
return (
|
|
475
|
-
<div className="flex items-center gap-2">
|
|
476
|
-
<Button
|
|
477
|
-
variant="ghost"
|
|
478
|
-
size="sm"
|
|
479
|
-
onClick={() => alert(`Edit user: ${user.name}`)}
|
|
480
|
-
>
|
|
481
|
-
<Edit className="h-4 w-4" />
|
|
482
|
-
</Button>
|
|
483
|
-
<Button
|
|
484
|
-
variant="ghost"
|
|
485
|
-
size="sm"
|
|
486
|
-
onClick={() => alert(`Delete user: ${user.name}`)}
|
|
487
|
-
>
|
|
488
|
-
<Trash2 className="h-4 w-4" />
|
|
489
|
-
</Button>
|
|
490
|
-
</div>
|
|
491
|
-
);
|
|
492
|
-
},
|
|
493
|
-
},
|
|
494
|
-
];
|
|
495
|
-
|
|
496
|
-
return (
|
|
497
|
-
<div className="space-y-4">
|
|
498
|
-
<div className="text-sm text-muted-foreground">
|
|
499
|
-
Click on badges or action buttons to see interactions
|
|
500
|
-
</div>
|
|
501
|
-
<DataTable
|
|
502
|
-
columns={columns}
|
|
503
|
-
data={userData}
|
|
504
|
-
searchable={true}
|
|
505
|
-
filterable={true}
|
|
506
|
-
exportable={true}
|
|
507
|
-
/>
|
|
508
|
-
</div>
|
|
509
|
-
);
|
|
510
|
-
},
|
|
511
|
-
};
|