@nccirtu/tablefy 0.2.0 → 0.5.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 +20 -4
- package/cli/templates/tablefy/avatar-list.tsx +51 -0
- package/cli/templates/tablefy/data-table-empty.tsx +77 -0
- package/cli/templates/tablefy/data-table-header.tsx +208 -0
- package/cli/templates/tablefy/data-table-pagination.tsx +137 -0
- package/cli/templates/tablefy/data-table-schema.tsx +13 -0
- package/cli/templates/tablefy/data-table.tsx +222 -0
- package/dist/cli/index.js +475 -0
- package/dist/columns/avatar-group-column.d.ts +1 -1
- package/dist/columns/columns/avatar-group-column.d.ts +1 -1
- package/dist/columns/index.d.ts +2 -2
- package/dist/columns/index.esm.js +153 -7921
- package/dist/columns/index.esm.js.map +1 -1
- package/dist/columns/index.js +214 -8000
- package/dist/columns/index.js.map +1 -1
- package/dist/columns/tablefy/avatar-list.d.ts +15 -0
- package/dist/columns/{components/ui/data-table → tablefy}/data-table-header.d.ts +1 -1
- package/dist/columns/{components/ui/data-table → tablefy}/data-table-pagination.d.ts +1 -1
- package/dist/columns/tablefy/data-table-schema.d.ts +1 -0
- package/dist/columns/tablefy/index.d.ts +6 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.esm.js +23 -7825
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +55 -7875
- package/dist/index.js.map +1 -1
- package/dist/tablefy/avatar-list.d.ts +15 -0
- package/dist/{components/ui/data-table → tablefy}/data-table-header.d.ts +1 -1
- package/dist/{components/ui/data-table → tablefy}/data-table-pagination.d.ts +1 -1
- package/dist/tablefy/data-table-schema.d.ts +1 -0
- package/dist/tablefy/index.d.ts +6 -0
- package/package.json +46 -13
- package/dist/columns/components/animata/list/avatar-list.d.ts +0 -12
- package/dist/columns/components/ui/badge.d.ts +0 -9
- package/dist/columns/components/ui/button.d.ts +0 -10
- package/dist/columns/components/ui/checkbox.d.ts +0 -4
- package/dist/columns/components/ui/data-table/data-table-schema.d.ts +0 -5
- package/dist/columns/components/ui/dropdown-menu.d.ts +0 -25
- package/dist/columns/components/ui/input.d.ts +0 -3
- package/dist/columns/components/ui/progress.d.ts +0 -4
- package/dist/columns/components/ui/select.d.ts +0 -15
- package/dist/columns/components/ui/table.d.ts +0 -10
- package/dist/columns/components/ui/tooltip.d.ts +0 -7
- package/dist/components/animata/list/avatar-list.d.ts +0 -12
- package/dist/components/ui/badge.d.ts +0 -9
- package/dist/components/ui/button.d.ts +0 -10
- package/dist/components/ui/checkbox.d.ts +0 -4
- package/dist/components/ui/data-table/data-table-schema.d.ts +0 -5
- package/dist/components/ui/dropdown-menu.d.ts +0 -25
- package/dist/components/ui/input.d.ts +0 -3
- package/dist/components/ui/progress.d.ts +0 -4
- package/dist/components/ui/select.d.ts +0 -15
- package/dist/components/ui/table.d.ts +0 -10
- package/dist/components/ui/tooltip.d.ts +0 -7
- package/dist/lib/table/schema/empty-state.d.ts +0 -23
- package/dist/lib/table/types.d.ts +0 -76
- package/dist/schema/data-table-schema.d.ts +0 -75
- package/dist/schema/empty-state.d.ts +0 -45
- package/dist/schema/table-schema.d.ts +0 -44
- package/dist/schema.d.ts +0 -11
- package/dist/src/columns/actions-column.d.ts +0 -26
- package/dist/src/columns/avatar-group-column.d.ts +0 -38
- package/dist/src/columns/badge-column.d.ts +0 -22
- package/dist/src/columns/base-column.d.ts +0 -19
- package/dist/src/columns/button-column.d.ts +0 -14
- package/dist/src/columns/checkbox-column.d.ts +0 -5
- package/dist/src/columns/date-column.d.ts +0 -24
- package/dist/src/columns/dropdown-column.d.ts +0 -17
- package/dist/src/columns/icon-column.d.ts +0 -58
- package/dist/src/columns/image-column.d.ts +0 -21
- package/dist/src/columns/index.d.ts +0 -13
- package/dist/src/columns/input-column.d.ts +0 -14
- package/dist/src/columns/link-column.d.ts +0 -27
- package/dist/src/columns/number-column.d.ts +0 -23
- package/dist/src/columns/progress-column.d.ts +0 -30
- package/dist/src/columns/select-column.d.ts +0 -19
- package/dist/src/columns/text-column.d.ts +0 -14
- package/dist/src/columns/types.d.ts +0 -37
- package/dist/src/components/animata/list/avatar-list.d.ts +0 -12
- package/dist/src/components/ui/badge.d.ts +0 -9
- package/dist/src/components/ui/button.d.ts +0 -10
- package/dist/src/components/ui/checkbox.d.ts +0 -4
- package/dist/src/components/ui/data-table/data-table-empty.d.ts +0 -8
- package/dist/src/components/ui/data-table/data-table-header.d.ts +0 -15
- package/dist/src/components/ui/data-table/data-table-pagination.d.ts +0 -9
- package/dist/src/components/ui/data-table/data-table-schema.d.ts +0 -5
- package/dist/src/components/ui/data-table/data-table.d.ts +0 -13
- package/dist/src/components/ui/dropdown-menu.d.ts +0 -25
- package/dist/src/components/ui/input.d.ts +0 -3
- package/dist/src/components/ui/progress.d.ts +0 -4
- package/dist/src/components/ui/select.d.ts +0 -15
- package/dist/src/components/ui/table.d.ts +0 -10
- package/dist/src/components/ui/tooltip.d.ts +0 -7
- package/dist/src/index.d.ts +0 -15
- package/dist/src/lib/table/schema/empty-state.d.ts +0 -23
- package/dist/src/lib/table/types.d.ts +0 -76
- package/dist/src/lib/utils.d.ts +0 -1
- package/dist/src/schema/data-table-schema.d.ts +0 -75
- package/dist/src/schema/empty-state.d.ts +0 -45
- package/dist/src/schema/table-schema.d.ts +0 -44
- package/dist/src/schema.d.ts +0 -11
- package/dist/src/types.d.ts +0 -76
- package/dist/types.d.ts +0 -76
- /package/dist/columns/{components/ui/data-table → tablefy}/data-table-empty.d.ts +0 -0
- /package/dist/columns/{components/ui/data-table → tablefy}/data-table.d.ts +0 -0
- /package/dist/{components/ui/data-table → tablefy}/data-table-empty.d.ts +0 -0
- /package/dist/{components/ui/data-table → tablefy}/data-table.d.ts +0 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import {
|
|
5
|
+
ColumnDef,
|
|
6
|
+
ColumnFiltersState,
|
|
7
|
+
SortingState,
|
|
8
|
+
VisibilityState,
|
|
9
|
+
flexRender,
|
|
10
|
+
getCoreRowModel,
|
|
11
|
+
getFilteredRowModel,
|
|
12
|
+
getPaginationRowModel,
|
|
13
|
+
getSortedRowModel,
|
|
14
|
+
useReactTable,
|
|
15
|
+
} from "@tanstack/react-table";
|
|
16
|
+
// shadcn components - installed by user via `npx shadcn add table`
|
|
17
|
+
import {
|
|
18
|
+
Table,
|
|
19
|
+
TableBody,
|
|
20
|
+
TableCell,
|
|
21
|
+
TableHead,
|
|
22
|
+
TableHeader,
|
|
23
|
+
TableRow,
|
|
24
|
+
} from "@/components/ui/table";
|
|
25
|
+
import { cn } from "@/lib/utils";
|
|
26
|
+
import { DataTableConfig } from "@/lib/types";
|
|
27
|
+
import { DataTableHeader } from "./data-table-header";
|
|
28
|
+
import { DataTableEmpty } from "./data-table-empty";
|
|
29
|
+
import { DataTablePagination } from "./data-table-pagination";
|
|
30
|
+
import { EmptyStateBuilder } from "@/lib/builders";
|
|
31
|
+
|
|
32
|
+
interface DataTableProps<TData, TValue> {
|
|
33
|
+
columns: ColumnDef<TData, TValue>[];
|
|
34
|
+
data: TData[];
|
|
35
|
+
config?: DataTableConfig<TData>;
|
|
36
|
+
className?: string;
|
|
37
|
+
isLoading?: boolean;
|
|
38
|
+
isError?: boolean;
|
|
39
|
+
onRetry?: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function DataTable<TData, TValue>({
|
|
43
|
+
columns,
|
|
44
|
+
data,
|
|
45
|
+
config = {},
|
|
46
|
+
className,
|
|
47
|
+
isLoading = false,
|
|
48
|
+
isError = false,
|
|
49
|
+
onRetry,
|
|
50
|
+
}: DataTableProps<TData, TValue>) {
|
|
51
|
+
const [sorting, setSorting] = useState<SortingState>(
|
|
52
|
+
config.defaultSort ? [config.defaultSort] : [],
|
|
53
|
+
);
|
|
54
|
+
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
|
55
|
+
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
|
56
|
+
const [rowSelection, setRowSelection] = useState({});
|
|
57
|
+
const [globalFilter, setGlobalFilter] = useState("");
|
|
58
|
+
|
|
59
|
+
const table = useReactTable({
|
|
60
|
+
data,
|
|
61
|
+
columns,
|
|
62
|
+
state: {
|
|
63
|
+
sorting,
|
|
64
|
+
columnFilters,
|
|
65
|
+
columnVisibility,
|
|
66
|
+
rowSelection,
|
|
67
|
+
globalFilter,
|
|
68
|
+
},
|
|
69
|
+
enableRowSelection: config.enableRowSelection ?? false,
|
|
70
|
+
enableMultiRowSelection: config.enableMultiRowSelection ?? true,
|
|
71
|
+
onSortingChange: setSorting,
|
|
72
|
+
onColumnFiltersChange: setColumnFilters,
|
|
73
|
+
onColumnVisibilityChange: setColumnVisibility,
|
|
74
|
+
onRowSelectionChange: setRowSelection,
|
|
75
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
76
|
+
getCoreRowModel: getCoreRowModel(),
|
|
77
|
+
getSortedRowModel: config.enableSorting ? getSortedRowModel() : undefined,
|
|
78
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
79
|
+
getPaginationRowModel: config.pagination?.enabled
|
|
80
|
+
? getPaginationRowModel()
|
|
81
|
+
: undefined,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const getEmptyState = () => {
|
|
85
|
+
if (isError) {
|
|
86
|
+
return (
|
|
87
|
+
config.emptyState || EmptyStateBuilder.make().error({ onRetry }).build()
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (globalFilter && data.length > 0) {
|
|
92
|
+
return (
|
|
93
|
+
config.searchEmptyState ||
|
|
94
|
+
EmptyStateBuilder.make().noSearchResults({}).build()
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (columnFilters.length > 0 && data.length > 0) {
|
|
99
|
+
return (
|
|
100
|
+
config.filterEmptyState ||
|
|
101
|
+
EmptyStateBuilder.make().noFilterResults({}).build()
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return config.emptyState || EmptyStateBuilder.make().noData().build();
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const densityClasses = {
|
|
109
|
+
compact: "[&_td]:py-1 [&_th]:py-1",
|
|
110
|
+
default: "[&_td]:py-3 [&_th]:py-3",
|
|
111
|
+
comfortable: "[&_td]:py-4 [&_th]:py-4",
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const selectedCount = table.getFilteredSelectedRowModel().rows.length;
|
|
115
|
+
const hasRows = table.getRowModel().rows?.length > 0;
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div className={cn("flex flex-col", className)}>
|
|
119
|
+
<DataTableHeader
|
|
120
|
+
title={config.title}
|
|
121
|
+
description={config.description}
|
|
122
|
+
actions={config.headerActions}
|
|
123
|
+
search={config.search}
|
|
124
|
+
searchValue={globalFilter}
|
|
125
|
+
onSearchChange={setGlobalFilter}
|
|
126
|
+
table={table}
|
|
127
|
+
selectedCount={selectedCount}
|
|
128
|
+
/>
|
|
129
|
+
|
|
130
|
+
<div
|
|
131
|
+
className={cn(
|
|
132
|
+
"overflow-hidden",
|
|
133
|
+
config.bordered !== false && "rounded-md border",
|
|
134
|
+
)}
|
|
135
|
+
>
|
|
136
|
+
<Table
|
|
137
|
+
className={cn(
|
|
138
|
+
densityClasses[config.density || "default"],
|
|
139
|
+
config.striped ? "[&_tr:nth-child(even)]:bg-muted/50" : "",
|
|
140
|
+
config.hoverable !== false && "[&_tr:hover]:bg-muted/50",
|
|
141
|
+
)}
|
|
142
|
+
>
|
|
143
|
+
<TableHeader>
|
|
144
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
145
|
+
<TableRow key={headerGroup.id}>
|
|
146
|
+
{headerGroup.headers.map((header) => (
|
|
147
|
+
<TableHead key={header.id}>
|
|
148
|
+
{header.isPlaceholder
|
|
149
|
+
? null
|
|
150
|
+
: flexRender(
|
|
151
|
+
header.column.columnDef.header,
|
|
152
|
+
header.getContext(),
|
|
153
|
+
)}
|
|
154
|
+
</TableHead>
|
|
155
|
+
))}
|
|
156
|
+
</TableRow>
|
|
157
|
+
))}
|
|
158
|
+
</TableHeader>
|
|
159
|
+
<TableBody>
|
|
160
|
+
{isLoading ? (
|
|
161
|
+
<tr>
|
|
162
|
+
<td colSpan={columns.length} className="h-[400px]">
|
|
163
|
+
<div className="flex h-full items-center justify-center">
|
|
164
|
+
<div className="flex flex-col items-center gap-4">
|
|
165
|
+
<svg
|
|
166
|
+
className="animate-spin h-8 w-8 text-muted-foreground"
|
|
167
|
+
viewBox="0 0 24 24"
|
|
168
|
+
>
|
|
169
|
+
<circle
|
|
170
|
+
className="opacity-25"
|
|
171
|
+
cx="12"
|
|
172
|
+
cy="12"
|
|
173
|
+
r="10"
|
|
174
|
+
stroke="currentColor"
|
|
175
|
+
strokeWidth="4"
|
|
176
|
+
fill="none"
|
|
177
|
+
/>
|
|
178
|
+
<path
|
|
179
|
+
className="opacity-75"
|
|
180
|
+
fill="currentColor"
|
|
181
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
182
|
+
/>
|
|
183
|
+
</svg>
|
|
184
|
+
<span className="text-sm text-muted-foreground">
|
|
185
|
+
Laden...
|
|
186
|
+
</span>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</td>
|
|
190
|
+
</tr>
|
|
191
|
+
) : hasRows ? (
|
|
192
|
+
table.getRowModel().rows.map((row) => (
|
|
193
|
+
<TableRow
|
|
194
|
+
key={row.id}
|
|
195
|
+
data-state={row.getIsSelected() && "selected"}
|
|
196
|
+
>
|
|
197
|
+
{row.getVisibleCells().map((cell) => (
|
|
198
|
+
<TableCell key={cell.id}>
|
|
199
|
+
{flexRender(
|
|
200
|
+
cell.column.columnDef.cell,
|
|
201
|
+
cell.getContext(),
|
|
202
|
+
)}
|
|
203
|
+
</TableCell>
|
|
204
|
+
))}
|
|
205
|
+
</TableRow>
|
|
206
|
+
))
|
|
207
|
+
) : (
|
|
208
|
+
<DataTableEmpty
|
|
209
|
+
config={getEmptyState()}
|
|
210
|
+
colSpan={columns.length}
|
|
211
|
+
/>
|
|
212
|
+
)}
|
|
213
|
+
</TableBody>
|
|
214
|
+
</Table>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
{config.pagination?.enabled && hasRows && (
|
|
218
|
+
<DataTablePagination table={table} config={config.pagination} />
|
|
219
|
+
)}
|
|
220
|
+
</div>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// cli/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// cli/commands/init.ts
|
|
7
|
+
import chalk2 from "chalk";
|
|
8
|
+
import prompts from "prompts";
|
|
9
|
+
|
|
10
|
+
// cli/utils/helpers.ts
|
|
11
|
+
import fs from "fs-extra";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import chalk from "chalk";
|
|
14
|
+
import { execSync } from "child_process";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
import { dirname } from "path";
|
|
17
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
var __dirname = dirname(__filename);
|
|
19
|
+
var SHADCN_COMPONENTS = [
|
|
20
|
+
"button",
|
|
21
|
+
"table",
|
|
22
|
+
"checkbox",
|
|
23
|
+
"dropdown-menu",
|
|
24
|
+
"input",
|
|
25
|
+
"select",
|
|
26
|
+
"badge",
|
|
27
|
+
"progress",
|
|
28
|
+
"tooltip"
|
|
29
|
+
];
|
|
30
|
+
var TABLEFY_COMPONENTS = {
|
|
31
|
+
"data-table": {
|
|
32
|
+
files: [
|
|
33
|
+
"tablefy/data-table.tsx",
|
|
34
|
+
"tablefy/data-table-empty.tsx",
|
|
35
|
+
"tablefy/data-table-header.tsx",
|
|
36
|
+
"tablefy/data-table-pagination.tsx",
|
|
37
|
+
"tablefy/data-table-schema.tsx"
|
|
38
|
+
],
|
|
39
|
+
description: "Main DataTable component with pagination, search, and more"
|
|
40
|
+
},
|
|
41
|
+
"avatar-list": {
|
|
42
|
+
files: ["tablefy/avatar-list.tsx"],
|
|
43
|
+
description: "Avatar list component for displaying user groups"
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function pathExists(filePath) {
|
|
47
|
+
return fs.existsSync(filePath);
|
|
48
|
+
}
|
|
49
|
+
function getProjectRoot() {
|
|
50
|
+
return process.cwd();
|
|
51
|
+
}
|
|
52
|
+
function isShadcnInitialized() {
|
|
53
|
+
const root = getProjectRoot();
|
|
54
|
+
if (pathExists(path.join(root, "components.json"))) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
const possiblePaths = [
|
|
58
|
+
path.join(root, "components", "ui"),
|
|
59
|
+
path.join(root, "src", "components", "ui"),
|
|
60
|
+
path.join(root, "app", "components", "ui"),
|
|
61
|
+
path.join(root, "resources", "js", "components", "ui")
|
|
62
|
+
];
|
|
63
|
+
return possiblePaths.some((p) => pathExists(p));
|
|
64
|
+
}
|
|
65
|
+
function getComponentsDir(customPath) {
|
|
66
|
+
const root = getProjectRoot();
|
|
67
|
+
if (customPath) {
|
|
68
|
+
return path.join(root, customPath);
|
|
69
|
+
}
|
|
70
|
+
const configPath = path.join(root, "components.json");
|
|
71
|
+
if (pathExists(configPath)) {
|
|
72
|
+
try {
|
|
73
|
+
const config = fs.readJsonSync(configPath);
|
|
74
|
+
if (config.aliases?.components) {
|
|
75
|
+
const alias = config.aliases.components;
|
|
76
|
+
if (alias.startsWith("@/")) {
|
|
77
|
+
return path.join(root, "src", alias.slice(2));
|
|
78
|
+
}
|
|
79
|
+
return path.join(root, alias);
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const possiblePaths = [
|
|
85
|
+
path.join(root, "src", "components"),
|
|
86
|
+
path.join(root, "components"),
|
|
87
|
+
path.join(root, "app", "components"),
|
|
88
|
+
path.join(root, "resources", "js", "components")
|
|
89
|
+
];
|
|
90
|
+
for (const p of possiblePaths) {
|
|
91
|
+
if (pathExists(p)) {
|
|
92
|
+
return p;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return path.join(root, "src", "components");
|
|
96
|
+
}
|
|
97
|
+
function getInstalledShadcnComponents(componentsDir) {
|
|
98
|
+
const uiDir = path.join(componentsDir, "ui");
|
|
99
|
+
if (!pathExists(uiDir)) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const installed = [];
|
|
103
|
+
for (const component of SHADCN_COMPONENTS) {
|
|
104
|
+
const componentPath = path.join(uiDir, `${component}.tsx`);
|
|
105
|
+
if (pathExists(componentPath)) {
|
|
106
|
+
installed.push(component);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return installed;
|
|
110
|
+
}
|
|
111
|
+
function getMissingShadcnComponents(componentsDir) {
|
|
112
|
+
const installed = getInstalledShadcnComponents(componentsDir);
|
|
113
|
+
return SHADCN_COMPONENTS.filter((c) => !installed.includes(c));
|
|
114
|
+
}
|
|
115
|
+
function installShadcnComponents(components) {
|
|
116
|
+
if (components.length === 0) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
console.log(chalk.blue("\n\u{1F4E6} Installing shadcn/ui components...\n"));
|
|
120
|
+
try {
|
|
121
|
+
const componentList = components.join(" ");
|
|
122
|
+
execSync(`npx shadcn@latest add ${componentList} -y`, {
|
|
123
|
+
stdio: "inherit",
|
|
124
|
+
cwd: getProjectRoot()
|
|
125
|
+
});
|
|
126
|
+
return true;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(chalk.red("\n\u274C Failed to install shadcn components"));
|
|
129
|
+
console.error(chalk.yellow("Please install them manually:"));
|
|
130
|
+
console.error(chalk.cyan(` npx shadcn@latest add ${components.join(" ")}`));
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function getTemplatesDir() {
|
|
135
|
+
const npmPath = path.join(__dirname, "..", "templates");
|
|
136
|
+
if (pathExists(npmPath)) {
|
|
137
|
+
return npmPath;
|
|
138
|
+
}
|
|
139
|
+
const devPath = path.join(__dirname, "..", "..", "cli", "templates");
|
|
140
|
+
if (pathExists(devPath)) {
|
|
141
|
+
return devPath;
|
|
142
|
+
}
|
|
143
|
+
throw new Error("Could not find Tablefy templates directory");
|
|
144
|
+
}
|
|
145
|
+
async function copyTablefyComponents(componentsDir, components = Object.keys(TABLEFY_COMPONENTS)) {
|
|
146
|
+
const templatesDir = getTemplatesDir();
|
|
147
|
+
console.log(chalk.blue("\n\u{1F4E6} Installing Tablefy components...\n"));
|
|
148
|
+
try {
|
|
149
|
+
for (const component of components) {
|
|
150
|
+
const componentConfig = TABLEFY_COMPONENTS[component];
|
|
151
|
+
for (const file of componentConfig.files) {
|
|
152
|
+
const sourcePath = path.join(templatesDir, file);
|
|
153
|
+
const destPath = path.join(componentsDir, file);
|
|
154
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
155
|
+
await fs.copy(sourcePath, destPath, { overwrite: true });
|
|
156
|
+
console.log(chalk.green(` \u2713 ${file}`));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error(chalk.red("\n\u274C Failed to copy Tablefy components"));
|
|
162
|
+
console.error(error);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function checkPeerDependencies() {
|
|
167
|
+
const root = getProjectRoot();
|
|
168
|
+
const packageJsonPath = path.join(root, "package.json");
|
|
169
|
+
const required = [
|
|
170
|
+
"@tanstack/react-table",
|
|
171
|
+
"lucide-react",
|
|
172
|
+
"class-variance-authority",
|
|
173
|
+
"clsx",
|
|
174
|
+
"tailwind-merge"
|
|
175
|
+
];
|
|
176
|
+
const missing = [];
|
|
177
|
+
const installed = [];
|
|
178
|
+
if (!pathExists(packageJsonPath)) {
|
|
179
|
+
return { missing: required, installed: [] };
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const packageJson = fs.readJsonSync(packageJsonPath);
|
|
183
|
+
const deps = {
|
|
184
|
+
...packageJson.dependencies,
|
|
185
|
+
...packageJson.devDependencies
|
|
186
|
+
};
|
|
187
|
+
for (const dep of required) {
|
|
188
|
+
if (deps[dep]) {
|
|
189
|
+
installed.push(dep);
|
|
190
|
+
} else {
|
|
191
|
+
missing.push(dep);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} catch {
|
|
195
|
+
return { missing: required, installed: [] };
|
|
196
|
+
}
|
|
197
|
+
return { missing, installed };
|
|
198
|
+
}
|
|
199
|
+
function installPeerDependencies(dependencies) {
|
|
200
|
+
if (dependencies.length === 0) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
console.log(chalk.blue("\n\u{1F4E6} Installing peer dependencies...\n"));
|
|
204
|
+
try {
|
|
205
|
+
const depList = dependencies.join(" ");
|
|
206
|
+
const root = getProjectRoot();
|
|
207
|
+
let cmd = "npm install";
|
|
208
|
+
if (pathExists(path.join(root, "pnpm-lock.yaml"))) {
|
|
209
|
+
cmd = "pnpm add";
|
|
210
|
+
} else if (pathExists(path.join(root, "yarn.lock"))) {
|
|
211
|
+
cmd = "yarn add";
|
|
212
|
+
} else if (pathExists(path.join(root, "bun.lockb"))) {
|
|
213
|
+
cmd = "bun add";
|
|
214
|
+
}
|
|
215
|
+
execSync(`${cmd} ${depList}`, {
|
|
216
|
+
stdio: "inherit",
|
|
217
|
+
cwd: root
|
|
218
|
+
});
|
|
219
|
+
return true;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error(chalk.red("\n\u274C Failed to install dependencies"));
|
|
222
|
+
console.error(chalk.yellow("Please install them manually:"));
|
|
223
|
+
console.error(chalk.cyan(` npm install ${dependencies.join(" ")}`));
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function showSuccess(message) {
|
|
228
|
+
console.log(chalk.green(`
|
|
229
|
+
\u2705 ${message}
|
|
230
|
+
`));
|
|
231
|
+
}
|
|
232
|
+
function showError(message) {
|
|
233
|
+
console.log(chalk.red(`
|
|
234
|
+
\u274C ${message}
|
|
235
|
+
`));
|
|
236
|
+
}
|
|
237
|
+
function showInfo(message) {
|
|
238
|
+
console.log(chalk.blue(`
|
|
239
|
+
\u2139\uFE0F ${message}
|
|
240
|
+
`));
|
|
241
|
+
}
|
|
242
|
+
function showWarning(message) {
|
|
243
|
+
console.log(chalk.yellow(`
|
|
244
|
+
\u26A0\uFE0F ${message}
|
|
245
|
+
`));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// cli/commands/init.ts
|
|
249
|
+
async function initCommand(options) {
|
|
250
|
+
console.log(chalk2.bold.blue("\n\u{1F680} Tablefy Setup\n"));
|
|
251
|
+
console.log(chalk2.gray("Setting up Tablefy in your project...\n"));
|
|
252
|
+
console.log(chalk2.bold("Step 1: Checking shadcn/ui setup..."));
|
|
253
|
+
if (!isShadcnInitialized()) {
|
|
254
|
+
showWarning("shadcn/ui is not initialized in this project.");
|
|
255
|
+
console.log(chalk2.yellow("Please run the following command first:"));
|
|
256
|
+
console.log(chalk2.cyan("\n npx shadcn@latest init\n"));
|
|
257
|
+
if (!options.yes) {
|
|
258
|
+
const response = await prompts({
|
|
259
|
+
type: "confirm",
|
|
260
|
+
name: "continue",
|
|
261
|
+
message: "Do you want to continue anyway?",
|
|
262
|
+
initial: false
|
|
263
|
+
});
|
|
264
|
+
if (!response.continue) {
|
|
265
|
+
showInfo("Setup cancelled. Please initialize shadcn/ui first.");
|
|
266
|
+
process.exit(0);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
console.log(chalk2.green(" \u2713 shadcn/ui is initialized"));
|
|
271
|
+
}
|
|
272
|
+
const componentsDir = getComponentsDir(options.components);
|
|
273
|
+
console.log(chalk2.gray(` Using components directory: ${componentsDir}
|
|
274
|
+
`));
|
|
275
|
+
console.log(chalk2.bold("Step 2: Checking peer dependencies..."));
|
|
276
|
+
const { missing: missingDeps, installed: installedDeps } = checkPeerDependencies();
|
|
277
|
+
if (installedDeps.length > 0) {
|
|
278
|
+
console.log(chalk2.green(` \u2713 ${installedDeps.length} dependencies already installed`));
|
|
279
|
+
}
|
|
280
|
+
if (missingDeps.length > 0) {
|
|
281
|
+
console.log(chalk2.yellow(` \u26A0 ${missingDeps.length} missing dependencies:`));
|
|
282
|
+
missingDeps.forEach((dep) => console.log(chalk2.gray(` - ${dep}`)));
|
|
283
|
+
let shouldInstall = options.yes;
|
|
284
|
+
if (!shouldInstall) {
|
|
285
|
+
const response = await prompts({
|
|
286
|
+
type: "confirm",
|
|
287
|
+
name: "install",
|
|
288
|
+
message: "Install missing peer dependencies?",
|
|
289
|
+
initial: true
|
|
290
|
+
});
|
|
291
|
+
shouldInstall = response.install;
|
|
292
|
+
}
|
|
293
|
+
if (shouldInstall) {
|
|
294
|
+
const success = installPeerDependencies(missingDeps);
|
|
295
|
+
if (!success) {
|
|
296
|
+
showError("Failed to install peer dependencies.");
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
showWarning("Skipping peer dependency installation.");
|
|
301
|
+
console.log(chalk2.yellow("Install them manually with:"));
|
|
302
|
+
console.log(chalk2.cyan(` npm install ${missingDeps.join(" ")}
|
|
303
|
+
`));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (!options.skipShadcn) {
|
|
307
|
+
console.log(chalk2.bold("\nStep 3: Checking shadcn/ui components..."));
|
|
308
|
+
const missingShadcn = getMissingShadcnComponents(componentsDir);
|
|
309
|
+
if (missingShadcn.length === 0) {
|
|
310
|
+
console.log(chalk2.green(" \u2713 All required shadcn/ui components are installed"));
|
|
311
|
+
} else {
|
|
312
|
+
console.log(chalk2.yellow(` \u26A0 ${missingShadcn.length} missing shadcn/ui components:`));
|
|
313
|
+
missingShadcn.forEach((comp) => console.log(chalk2.gray(` - ${comp}`)));
|
|
314
|
+
let shouldInstall = options.yes;
|
|
315
|
+
if (!shouldInstall) {
|
|
316
|
+
const response = await prompts({
|
|
317
|
+
type: "confirm",
|
|
318
|
+
name: "install",
|
|
319
|
+
message: "Install missing shadcn/ui components?",
|
|
320
|
+
initial: true
|
|
321
|
+
});
|
|
322
|
+
shouldInstall = response.install;
|
|
323
|
+
}
|
|
324
|
+
if (shouldInstall) {
|
|
325
|
+
const success = installShadcnComponents(missingShadcn);
|
|
326
|
+
if (!success) {
|
|
327
|
+
showWarning("Some shadcn components may not have been installed.");
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
showWarning("Skipping shadcn component installation.");
|
|
331
|
+
console.log(chalk2.yellow("Install them manually with:"));
|
|
332
|
+
console.log(chalk2.cyan(` npx shadcn@latest add ${missingShadcn.join(" ")}
|
|
333
|
+
`));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
console.log(chalk2.bold("\nStep 4: Installing Tablefy components..."));
|
|
338
|
+
const allComponents = Object.keys(TABLEFY_COMPONENTS);
|
|
339
|
+
console.log(chalk2.gray(" Components to install:"));
|
|
340
|
+
allComponents.forEach((comp) => {
|
|
341
|
+
console.log(chalk2.gray(` - ${comp}: ${TABLEFY_COMPONENTS[comp].description}`));
|
|
342
|
+
});
|
|
343
|
+
let shouldInstallTablefy = options.yes;
|
|
344
|
+
if (!shouldInstallTablefy) {
|
|
345
|
+
const response = await prompts({
|
|
346
|
+
type: "confirm",
|
|
347
|
+
name: "install",
|
|
348
|
+
message: "Install Tablefy components?",
|
|
349
|
+
initial: true
|
|
350
|
+
});
|
|
351
|
+
shouldInstallTablefy = response.install;
|
|
352
|
+
}
|
|
353
|
+
if (shouldInstallTablefy) {
|
|
354
|
+
const success = await copyTablefyComponents(componentsDir, allComponents);
|
|
355
|
+
if (!success) {
|
|
356
|
+
showError("Failed to install Tablefy components.");
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
showInfo("Tablefy component installation skipped.");
|
|
361
|
+
process.exit(0);
|
|
362
|
+
}
|
|
363
|
+
showSuccess("Tablefy has been successfully installed!");
|
|
364
|
+
console.log(chalk2.bold("Next steps:\n"));
|
|
365
|
+
console.log(chalk2.gray("1. Import and use Tablefy in your project:"));
|
|
366
|
+
console.log(chalk2.cyan(`
|
|
367
|
+
import { DataTable, TableSchema } from "@nccirtu/tablefy";
|
|
368
|
+
import { TextColumn, BadgeColumn } from "@nccirtu/tablefy/columns";
|
|
369
|
+
`));
|
|
370
|
+
console.log(chalk2.gray("2. Create your first table:"));
|
|
371
|
+
console.log(chalk2.cyan(`
|
|
372
|
+
const { columns, config } = TableSchema.make<User>()
|
|
373
|
+
.searchable()
|
|
374
|
+
.paginated()
|
|
375
|
+
.columns(
|
|
376
|
+
TextColumn.make("name").label("Name").sortable(),
|
|
377
|
+
BadgeColumn.make("status").label("Status")
|
|
378
|
+
)
|
|
379
|
+
.build();
|
|
380
|
+
|
|
381
|
+
<DataTable data={users} columns={columns} config={config} />
|
|
382
|
+
`));
|
|
383
|
+
console.log(chalk2.gray("\u{1F4DA} Full documentation: https://github.com/nccirtu/Tablefy\n"));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// cli/commands/add.ts
|
|
387
|
+
import chalk3 from "chalk";
|
|
388
|
+
import prompts2 from "prompts";
|
|
389
|
+
async function addCommand(componentNames, options) {
|
|
390
|
+
const availableComponents = Object.keys(TABLEFY_COMPONENTS);
|
|
391
|
+
if (componentNames.length === 0) {
|
|
392
|
+
const response = await prompts2({
|
|
393
|
+
type: "multiselect",
|
|
394
|
+
name: "components",
|
|
395
|
+
message: "Select components to add:",
|
|
396
|
+
choices: availableComponents.map((name) => ({
|
|
397
|
+
title: name,
|
|
398
|
+
description: TABLEFY_COMPONENTS[name].description,
|
|
399
|
+
value: name
|
|
400
|
+
})),
|
|
401
|
+
min: 1
|
|
402
|
+
});
|
|
403
|
+
if (!response.components || response.components.length === 0) {
|
|
404
|
+
showInfo("No components selected.");
|
|
405
|
+
process.exit(0);
|
|
406
|
+
}
|
|
407
|
+
componentNames = response.components;
|
|
408
|
+
}
|
|
409
|
+
const validComponents = [];
|
|
410
|
+
const invalidComponents = [];
|
|
411
|
+
for (const name of componentNames) {
|
|
412
|
+
if (availableComponents.includes(name)) {
|
|
413
|
+
validComponents.push(name);
|
|
414
|
+
} else {
|
|
415
|
+
invalidComponents.push(name);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (invalidComponents.length > 0) {
|
|
419
|
+
console.log(chalk3.yellow(`
|
|
420
|
+
\u26A0\uFE0F Unknown components: ${invalidComponents.join(", ")}`));
|
|
421
|
+
console.log(chalk3.gray("Available components:"));
|
|
422
|
+
availableComponents.forEach((comp) => {
|
|
423
|
+
console.log(chalk3.gray(` - ${comp}: ${TABLEFY_COMPONENTS[comp].description}`));
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
if (validComponents.length === 0) {
|
|
427
|
+
showError("No valid components to install.");
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
const componentsDir = getComponentsDir(options.components);
|
|
431
|
+
console.log(chalk3.gray(`
|
|
432
|
+
Using components directory: ${componentsDir}
|
|
433
|
+
`));
|
|
434
|
+
if (!options.yes) {
|
|
435
|
+
console.log(chalk3.bold("Components to install:"));
|
|
436
|
+
validComponents.forEach((comp) => {
|
|
437
|
+
console.log(chalk3.gray(` - ${comp}`));
|
|
438
|
+
TABLEFY_COMPONENTS[comp].files.forEach((file) => {
|
|
439
|
+
console.log(chalk3.gray(` \u2514\u2500 ${file}`));
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
const response = await prompts2({
|
|
443
|
+
type: "confirm",
|
|
444
|
+
name: "confirm",
|
|
445
|
+
message: "Proceed with installation?",
|
|
446
|
+
initial: true
|
|
447
|
+
});
|
|
448
|
+
if (!response.confirm) {
|
|
449
|
+
showInfo("Installation cancelled.");
|
|
450
|
+
process.exit(0);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const success = await copyTablefyComponents(componentsDir, validComponents);
|
|
454
|
+
if (success) {
|
|
455
|
+
showSuccess(`Successfully installed: ${validComponents.join(", ")}`);
|
|
456
|
+
} else {
|
|
457
|
+
showError("Failed to install some components.");
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// cli/index.ts
|
|
463
|
+
var program = new Command();
|
|
464
|
+
program.name("tablefy").description("CLI for setting up Tablefy in your project").version("0.2.0");
|
|
465
|
+
program.command("init").description("Initialize Tablefy in your project").option("-y, --yes", "Skip confirmation prompts").option("--skip-shadcn", "Skip shadcn/ui component installation").option(
|
|
466
|
+
"-c, --components <path>",
|
|
467
|
+
"Path to components directory",
|
|
468
|
+
"components"
|
|
469
|
+
).action(initCommand);
|
|
470
|
+
program.command("add").description("Add specific Tablefy components").argument("[components...]", "Components to add (e.g., data-table avatar-list)").option(
|
|
471
|
+
"-c, --components <path>",
|
|
472
|
+
"Path to components directory",
|
|
473
|
+
"components"
|
|
474
|
+
).option("-y, --yes", "Skip confirmation prompts").action(addCommand);
|
|
475
|
+
program.parse(process.argv);
|
|
@@ -8,7 +8,7 @@ interface AvatarItem {
|
|
|
8
8
|
}
|
|
9
9
|
interface AvatarGroupColumnConfig<TData> extends BaseColumnConfig<TData> {
|
|
10
10
|
maxVisible?: number;
|
|
11
|
-
size?:
|
|
11
|
+
size?: number;
|
|
12
12
|
overlap?: "none" | "sm" | "md" | "lg";
|
|
13
13
|
showTooltip?: boolean;
|
|
14
14
|
nameField?: keyof TData | ((row: TData) => AvatarItem[]);
|