@moontra/moonui-pro 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.
@@ -0,0 +1,61 @@
1
+ export * from '@moontra/moonui';
2
+ export { Alert, Avatar, Badge, Button, Card, Dialog, Input, Select, Switch, Tabs, Toast, Tooltip, cn } from '@moontra/moonui';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ColumnDef } from '@tanstack/react-table';
5
+
6
+ interface DataTableProps<TData, TValue> {
7
+ columns: ColumnDef<TData, TValue>[];
8
+ data: TData[];
9
+ searchable?: boolean;
10
+ filterable?: boolean;
11
+ exportable?: boolean;
12
+ selectable?: boolean;
13
+ pagination?: boolean;
14
+ pageSize?: number;
15
+ className?: string;
16
+ onRowSelect?: (rows: TData[]) => void;
17
+ onExport?: (data: TData[]) => void;
18
+ }
19
+ declare function DataTable<TData, TValue>({ columns, data, searchable, filterable, exportable, selectable, pagination, pageSize, className, onRowSelect, onExport, }: DataTableProps<TData, TValue>): react_jsx_runtime.JSX.Element;
20
+
21
+ interface UseDataTableOptions<TData> {
22
+ data: TData[];
23
+ columns: ColumnDef<TData, any>[];
24
+ searchable?: boolean;
25
+ filterable?: boolean;
26
+ sortable?: boolean;
27
+ pagination?: boolean;
28
+ pageSize?: number;
29
+ enableRowSelection?: boolean;
30
+ enableMultiRowSelection?: boolean;
31
+ }
32
+ interface UseDataTableReturn<TData> {
33
+ filteredData: TData[];
34
+ selectedRows: TData[];
35
+ searchQuery: string;
36
+ currentPage: number;
37
+ totalPages: number;
38
+ setSearchQuery: (query: string) => void;
39
+ setCurrentPage: (page: number) => void;
40
+ selectRow: (row: TData) => void;
41
+ selectAllRows: () => void;
42
+ clearSelection: () => void;
43
+ exportData: (format?: 'csv' | 'json') => void;
44
+ getRowCount: () => number;
45
+ getSelectedCount: () => number;
46
+ isRowSelected: (row: TData) => boolean;
47
+ }
48
+ declare function useDataTable<TData extends Record<string, any>>(options: UseDataTableOptions<TData>): UseDataTableReturn<TData>;
49
+
50
+ interface DataProcessor<T = any> {
51
+ filter: (data: T[], predicate: (item: T) => boolean) => T[];
52
+ sort: (data: T[], key: keyof T, direction?: 'asc' | 'desc') => T[];
53
+ group: (data: T[], key: keyof T) => Record<string, T[]>;
54
+ aggregate: (data: T[], key: keyof T, operation: 'sum' | 'avg' | 'min' | 'max' | 'count') => number;
55
+ paginate: (data: T[], page: number, pageSize: number) => T[];
56
+ search: (data: T[], query: string, searchKeys?: (keyof T)[]) => T[];
57
+ transform: <U>(data: T[], transformer: (item: T) => U) => U[];
58
+ }
59
+ declare function createDataProcessor<T = any>(): DataProcessor<T>;
60
+
61
+ export { DataProcessor, DataTable, createDataProcessor, useDataTable };
package/dist/index.js ADDED
@@ -0,0 +1,472 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/index.ts
32
+ var src_exports = {};
33
+ __export(src_exports, {
34
+ Alert: () => import_moonui4.Alert,
35
+ Avatar: () => import_moonui4.Avatar,
36
+ Badge: () => import_moonui4.Badge,
37
+ Button: () => import_moonui4.Button,
38
+ Card: () => import_moonui4.Card,
39
+ DataTable: () => DataTable,
40
+ Dialog: () => import_moonui4.Dialog,
41
+ Input: () => import_moonui4.Input,
42
+ Select: () => import_moonui4.Select,
43
+ Switch: () => import_moonui4.Switch,
44
+ Tabs: () => import_moonui4.Tabs,
45
+ Toast: () => import_moonui4.Toast,
46
+ Tooltip: () => import_moonui4.Tooltip,
47
+ cn: () => import_moonui4.cn,
48
+ createDataProcessor: () => createDataProcessor,
49
+ useDataTable: () => useDataTable
50
+ });
51
+ module.exports = __toCommonJS(src_exports);
52
+ __reExport(src_exports, require("@moontra/moonui"), module.exports);
53
+
54
+ // src/components/data-table/index.tsx
55
+ var import_react = __toESM(require("react"));
56
+ var import_react_table = require("@tanstack/react-table");
57
+ var import_moonui = require("@moontra/moonui");
58
+ var import_moonui2 = require("@moontra/moonui");
59
+ var import_lucide_react = require("lucide-react");
60
+ var import_moonui3 = require("@moontra/moonui");
61
+ var import_jsx_runtime = require("react/jsx-runtime");
62
+ function DataTable({
63
+ columns,
64
+ data,
65
+ searchable = true,
66
+ filterable = true,
67
+ exportable = true,
68
+ selectable = false,
69
+ pagination = true,
70
+ pageSize = 10,
71
+ className,
72
+ onRowSelect,
73
+ onExport
74
+ }) {
75
+ const [sorting, setSorting] = import_react.default.useState([]);
76
+ const [columnFilters, setColumnFilters] = import_react.default.useState([]);
77
+ const [columnVisibility, setColumnVisibility] = import_react.default.useState({});
78
+ const [rowSelection, setRowSelection] = import_react.default.useState({});
79
+ const [globalFilter, setGlobalFilter] = import_react.default.useState("");
80
+ const table = (0, import_react_table.useReactTable)({
81
+ data,
82
+ columns,
83
+ onSortingChange: setSorting,
84
+ onColumnFiltersChange: setColumnFilters,
85
+ getCoreRowModel: (0, import_react_table.getCoreRowModel)(),
86
+ getPaginationRowModel: (0, import_react_table.getPaginationRowModel)(),
87
+ getSortedRowModel: (0, import_react_table.getSortedRowModel)(),
88
+ getFilteredRowModel: (0, import_react_table.getFilteredRowModel)(),
89
+ onColumnVisibilityChange: setColumnVisibility,
90
+ onRowSelectionChange: setRowSelection,
91
+ onGlobalFilterChange: setGlobalFilter,
92
+ globalFilterFn: "includesString",
93
+ state: {
94
+ sorting,
95
+ columnFilters,
96
+ columnVisibility,
97
+ rowSelection,
98
+ globalFilter
99
+ },
100
+ initialState: {
101
+ pagination: {
102
+ pageSize
103
+ }
104
+ }
105
+ });
106
+ import_react.default.useEffect(() => {
107
+ if (onRowSelect && selectable) {
108
+ const selectedRows = table.getFilteredSelectedRowModel().rows.map((row) => row.original);
109
+ onRowSelect(selectedRows);
110
+ }
111
+ }, [rowSelection, onRowSelect, selectable, table]);
112
+ const handleExport = () => {
113
+ if (onExport) {
114
+ const selectedRows = table.getFilteredSelectedRowModel().rows;
115
+ const dataToExport = selectedRows.length > 0 ? selectedRows.map((row) => row.original) : table.getFilteredRowModel().rows.map((row) => row.original);
116
+ onExport(dataToExport);
117
+ }
118
+ };
119
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_moonui3.cn)("space-y-4", className), children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center space-x-2", children: [
122
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative", children: [
123
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Search, { className: "absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" }),
124
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
125
+ import_moonui2.Input,
126
+ {
127
+ placeholder: "Search all columns...",
128
+ value: globalFilter,
129
+ onChange: (e) => setGlobalFilter(e.target.value),
130
+ className: "pl-8 w-64"
131
+ }
132
+ )
133
+ ] }),
134
+ filterable && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_moonui.Button, { variant: "outline", size: "sm", children: [
135
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Filter, { className: "mr-2 h-4 w-4" }),
136
+ "Filters"
137
+ ] })
138
+ ] }),
139
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center space-x-2", children: [
140
+ exportable && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_moonui.Button, { variant: "outline", size: "sm", onClick: handleExport, children: [
141
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Download, { className: "mr-2 h-4 w-4" }),
142
+ "Export"
143
+ ] }),
144
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_moonui.Button, { variant: "outline", size: "sm", children: [
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Settings, { className: "mr-2 h-4 w-4" }),
146
+ "Columns"
147
+ ] })
148
+ ] })
149
+ ] }),
150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rounded-md border", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "w-full", children: [
151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { className: "border-b", children: headerGroup.headers.map((header) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
152
+ "th",
153
+ {
154
+ className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground",
155
+ children: header.isPlaceholder ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
156
+ "div",
157
+ {
158
+ className: (0, import_moonui3.cn)(
159
+ "flex items-center space-x-2",
160
+ header.column.getCanSort() && "cursor-pointer select-none"
161
+ ),
162
+ onClick: header.column.getToggleSortingHandler(),
163
+ children: [
164
+ (0, import_react_table.flexRender)(header.column.columnDef.header, header.getContext()),
165
+ header.column.getCanSort() && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ml-2", children: header.column.getIsSorted() === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowUp, { className: "h-4 w-4" }) : header.column.getIsSorted() === "desc" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowDown, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowUpDown, { className: "h-4 w-4" }) })
166
+ ]
167
+ }
168
+ )
169
+ },
170
+ header.id
171
+ )) }, headerGroup.id)) }),
172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
173
+ "tr",
174
+ {
175
+ className: (0, import_moonui3.cn)(
176
+ "border-b transition-colors hover:bg-muted/50",
177
+ row.getIsSelected() && "bg-muted"
178
+ ),
179
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "p-4 align-middle", children: (0, import_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
180
+ },
181
+ row.id
182
+ )) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: columns.length, className: "h-24 text-center", children: "No results found." }) }) })
183
+ ] }) }),
184
+ pagination && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between px-2", children: [
185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1 text-sm text-muted-foreground", children: selectable && table.getFilteredSelectedRowModel().rows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
186
+ table.getFilteredSelectedRowModel().rows.length,
187
+ " of",
188
+ " ",
189
+ table.getFilteredRowModel().rows.length,
190
+ " row(s) selected."
191
+ ] }) }),
192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center space-x-6 lg:space-x-8", children: [
193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center space-x-2", children: [
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-medium", children: "Rows per page" }),
195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
196
+ "select",
197
+ {
198
+ value: table.getState().pagination.pageSize,
199
+ onChange: (e) => table.setPageSize(Number(e.target.value)),
200
+ className: "h-8 w-[70px] rounded border border-input bg-background px-3 py-1 text-sm",
201
+ children: [10, 20, 30, 40, 50].map((pageSize2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: pageSize2, children: pageSize2 }, pageSize2))
202
+ }
203
+ )
204
+ ] }),
205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex w-[100px] items-center justify-center text-sm font-medium", children: [
206
+ "Page ",
207
+ table.getState().pagination.pageIndex + 1,
208
+ " of",
209
+ " ",
210
+ table.getPageCount()
211
+ ] }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center space-x-2", children: [
213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
214
+ import_moonui.Button,
215
+ {
216
+ variant: "outline",
217
+ className: "hidden h-8 w-8 p-0 lg:flex",
218
+ onClick: () => table.setPageIndex(0),
219
+ disabled: !table.getCanPreviousPage(),
220
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronsLeft, { className: "h-4 w-4" })
221
+ }
222
+ ),
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
224
+ import_moonui.Button,
225
+ {
226
+ variant: "outline",
227
+ className: "h-8 w-8 p-0",
228
+ onClick: () => table.previousPage(),
229
+ disabled: !table.getCanPreviousPage(),
230
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronLeft, { className: "h-4 w-4" })
231
+ }
232
+ ),
233
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
234
+ import_moonui.Button,
235
+ {
236
+ variant: "outline",
237
+ className: "h-8 w-8 p-0",
238
+ onClick: () => table.nextPage(),
239
+ disabled: !table.getCanNextPage(),
240
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronRight, { className: "h-4 w-4" })
241
+ }
242
+ ),
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
244
+ import_moonui.Button,
245
+ {
246
+ variant: "outline",
247
+ className: "hidden h-8 w-8 p-0 lg:flex",
248
+ onClick: () => table.setPageIndex(table.getPageCount() - 1),
249
+ disabled: !table.getCanNextPage(),
250
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronsRight, { className: "h-4 w-4" })
251
+ }
252
+ )
253
+ ] })
254
+ ] })
255
+ ] })
256
+ ] });
257
+ }
258
+
259
+ // src/hooks/use-data-table.ts
260
+ var import_react2 = __toESM(require("react"));
261
+ function useDataTable(options) {
262
+ const {
263
+ data,
264
+ searchable = true,
265
+ pageSize = 10,
266
+ enableRowSelection = false,
267
+ enableMultiRowSelection = true
268
+ } = options;
269
+ const [searchQuery, setSearchQuery] = import_react2.default.useState("");
270
+ const [currentPage, setCurrentPage] = import_react2.default.useState(1);
271
+ const [selectedRows, setSelectedRows] = import_react2.default.useState([]);
272
+ const filteredData = import_react2.default.useMemo(() => {
273
+ if (!searchable || !searchQuery.trim()) {
274
+ return data;
275
+ }
276
+ return data.filter((row) => {
277
+ return Object.values(row).some((value) => {
278
+ if (value == null)
279
+ return false;
280
+ return String(value).toLowerCase().includes(searchQuery.toLowerCase());
281
+ });
282
+ });
283
+ }, [data, searchQuery, searchable]);
284
+ const totalPages = Math.ceil(filteredData.length / pageSize);
285
+ const paginatedData = import_react2.default.useMemo(() => {
286
+ const startIndex = (currentPage - 1) * pageSize;
287
+ const endIndex = startIndex + pageSize;
288
+ return filteredData.slice(startIndex, endIndex);
289
+ }, [filteredData, currentPage, pageSize]);
290
+ const selectRow = import_react2.default.useCallback((row) => {
291
+ if (!enableRowSelection)
292
+ return;
293
+ setSelectedRows((prev) => {
294
+ if (!enableMultiRowSelection) {
295
+ return [row];
296
+ }
297
+ const isSelected = prev.some(
298
+ (selectedRow) => JSON.stringify(selectedRow) === JSON.stringify(row)
299
+ );
300
+ if (isSelected) {
301
+ return prev.filter(
302
+ (selectedRow) => JSON.stringify(selectedRow) !== JSON.stringify(row)
303
+ );
304
+ } else {
305
+ return [...prev, row];
306
+ }
307
+ });
308
+ }, [enableRowSelection, enableMultiRowSelection]);
309
+ const selectAllRows = import_react2.default.useCallback(() => {
310
+ if (!enableRowSelection)
311
+ return;
312
+ setSelectedRows((prev) => {
313
+ if (prev.length === filteredData.length) {
314
+ return [];
315
+ } else {
316
+ return [...filteredData];
317
+ }
318
+ });
319
+ }, [enableRowSelection, filteredData]);
320
+ const clearSelection = import_react2.default.useCallback(() => {
321
+ setSelectedRows([]);
322
+ }, []);
323
+ const isRowSelected = import_react2.default.useCallback((row) => {
324
+ return selectedRows.some(
325
+ (selectedRow) => JSON.stringify(selectedRow) === JSON.stringify(row)
326
+ );
327
+ }, [selectedRows]);
328
+ const exportData = import_react2.default.useCallback((format = "csv") => {
329
+ const dataToExport = selectedRows.length > 0 ? selectedRows : filteredData;
330
+ if (format === "csv") {
331
+ const headers = Object.keys(dataToExport[0] || {});
332
+ const csvContent = [
333
+ headers.join(","),
334
+ ...dataToExport.map(
335
+ (row) => headers.map((header) => {
336
+ const value = row[header];
337
+ if (typeof value === "string" && (value.includes(",") || value.includes('"'))) {
338
+ return `"${value.replace(/"/g, '""')}"`;
339
+ }
340
+ return value;
341
+ }).join(",")
342
+ )
343
+ ].join("\n");
344
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
345
+ const link = document.createElement("a");
346
+ link.href = URL.createObjectURL(blob);
347
+ link.download = "data.csv";
348
+ link.click();
349
+ } else if (format === "json") {
350
+ const jsonContent = JSON.stringify(dataToExport, null, 2);
351
+ const blob = new Blob([jsonContent], { type: "application/json;charset=utf-8;" });
352
+ const link = document.createElement("a");
353
+ link.href = URL.createObjectURL(blob);
354
+ link.download = "data.json";
355
+ link.click();
356
+ }
357
+ }, [selectedRows, filteredData]);
358
+ const getRowCount = import_react2.default.useCallback(() => filteredData.length, [filteredData]);
359
+ const getSelectedCount = import_react2.default.useCallback(() => selectedRows.length, [selectedRows]);
360
+ import_react2.default.useEffect(() => {
361
+ setCurrentPage(1);
362
+ }, [searchQuery]);
363
+ return {
364
+ filteredData: paginatedData,
365
+ selectedRows,
366
+ searchQuery,
367
+ currentPage,
368
+ totalPages,
369
+ setSearchQuery,
370
+ setCurrentPage,
371
+ selectRow,
372
+ selectAllRows,
373
+ clearSelection,
374
+ exportData,
375
+ getRowCount,
376
+ getSelectedCount,
377
+ isRowSelected
378
+ };
379
+ }
380
+
381
+ // src/index.ts
382
+ var import_moonui4 = require("@moontra/moonui");
383
+
384
+ // src/utils/data-processing.ts
385
+ function createDataProcessor() {
386
+ return {
387
+ filter: (data, predicate) => {
388
+ return data.filter(predicate);
389
+ },
390
+ sort: (data, key, direction = "asc") => {
391
+ return [...data].sort((a, b) => {
392
+ const aVal = a[key];
393
+ const bVal = b[key];
394
+ if (aVal === bVal)
395
+ return 0;
396
+ const comparison = aVal < bVal ? -1 : 1;
397
+ return direction === "asc" ? comparison : -comparison;
398
+ });
399
+ },
400
+ group: (data, key) => {
401
+ return data.reduce((groups, item) => {
402
+ const groupKey = String(item[key]);
403
+ if (!groups[groupKey]) {
404
+ groups[groupKey] = [];
405
+ }
406
+ groups[groupKey].push(item);
407
+ return groups;
408
+ }, {});
409
+ },
410
+ aggregate: (data, key, operation) => {
411
+ const values = data.map((item) => Number(item[key])).filter((val) => !isNaN(val));
412
+ if (values.length === 0)
413
+ return 0;
414
+ switch (operation) {
415
+ case "sum":
416
+ return values.reduce((sum, val) => sum + val, 0);
417
+ case "avg":
418
+ return values.reduce((sum, val) => sum + val, 0) / values.length;
419
+ case "min":
420
+ return Math.min(...values);
421
+ case "max":
422
+ return Math.max(...values);
423
+ case "count":
424
+ return values.length;
425
+ default:
426
+ return 0;
427
+ }
428
+ },
429
+ paginate: (data, page, pageSize) => {
430
+ const startIndex = (page - 1) * pageSize;
431
+ const endIndex = startIndex + pageSize;
432
+ return data.slice(startIndex, endIndex);
433
+ },
434
+ search: (data, query, searchKeys) => {
435
+ if (!query.trim())
436
+ return data;
437
+ const lowerQuery = query.toLowerCase();
438
+ return data.filter((item) => {
439
+ const keysToSearch = searchKeys || Object.keys(item);
440
+ return keysToSearch.some((key) => {
441
+ const value = item[key];
442
+ if (value == null)
443
+ return false;
444
+ return String(value).toLowerCase().includes(lowerQuery);
445
+ });
446
+ });
447
+ },
448
+ transform: (data, transformer) => {
449
+ return data.map(transformer);
450
+ }
451
+ };
452
+ }
453
+ // Annotate the CommonJS export names for ESM import in node:
454
+ 0 && (module.exports = {
455
+ Alert,
456
+ Avatar,
457
+ Badge,
458
+ Button,
459
+ Card,
460
+ DataTable,
461
+ Dialog,
462
+ Input,
463
+ Select,
464
+ Switch,
465
+ Tabs,
466
+ Toast,
467
+ Tooltip,
468
+ cn,
469
+ createDataProcessor,
470
+ useDataTable,
471
+ ...require("@moontra/moonui")
472
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,453 @@
1
+ // src/index.ts
2
+ export * from "@moontra/moonui";
3
+
4
+ // src/components/data-table/index.tsx
5
+ import React from "react";
6
+ import {
7
+ useReactTable,
8
+ getCoreRowModel,
9
+ getFilteredRowModel,
10
+ getPaginationRowModel,
11
+ getSortedRowModel,
12
+ flexRender
13
+ } from "@tanstack/react-table";
14
+ import { Button } from "@moontra/moonui";
15
+ import { Input } from "@moontra/moonui";
16
+ import {
17
+ ChevronLeft,
18
+ ChevronRight,
19
+ ChevronsLeft,
20
+ ChevronsRight,
21
+ ArrowUpDown,
22
+ ArrowUp,
23
+ ArrowDown,
24
+ Search,
25
+ Filter,
26
+ Download,
27
+ Settings
28
+ } from "lucide-react";
29
+ import { cn } from "@moontra/moonui";
30
+ import { jsx, jsxs } from "react/jsx-runtime";
31
+ function DataTable({
32
+ columns,
33
+ data,
34
+ searchable = true,
35
+ filterable = true,
36
+ exportable = true,
37
+ selectable = false,
38
+ pagination = true,
39
+ pageSize = 10,
40
+ className,
41
+ onRowSelect,
42
+ onExport
43
+ }) {
44
+ const [sorting, setSorting] = React.useState([]);
45
+ const [columnFilters, setColumnFilters] = React.useState([]);
46
+ const [columnVisibility, setColumnVisibility] = React.useState({});
47
+ const [rowSelection, setRowSelection] = React.useState({});
48
+ const [globalFilter, setGlobalFilter] = React.useState("");
49
+ const table = useReactTable({
50
+ data,
51
+ columns,
52
+ onSortingChange: setSorting,
53
+ onColumnFiltersChange: setColumnFilters,
54
+ getCoreRowModel: getCoreRowModel(),
55
+ getPaginationRowModel: getPaginationRowModel(),
56
+ getSortedRowModel: getSortedRowModel(),
57
+ getFilteredRowModel: getFilteredRowModel(),
58
+ onColumnVisibilityChange: setColumnVisibility,
59
+ onRowSelectionChange: setRowSelection,
60
+ onGlobalFilterChange: setGlobalFilter,
61
+ globalFilterFn: "includesString",
62
+ state: {
63
+ sorting,
64
+ columnFilters,
65
+ columnVisibility,
66
+ rowSelection,
67
+ globalFilter
68
+ },
69
+ initialState: {
70
+ pagination: {
71
+ pageSize
72
+ }
73
+ }
74
+ });
75
+ React.useEffect(() => {
76
+ if (onRowSelect && selectable) {
77
+ const selectedRows = table.getFilteredSelectedRowModel().rows.map((row) => row.original);
78
+ onRowSelect(selectedRows);
79
+ }
80
+ }, [rowSelection, onRowSelect, selectable, table]);
81
+ const handleExport = () => {
82
+ if (onExport) {
83
+ const selectedRows = table.getFilteredSelectedRowModel().rows;
84
+ const dataToExport = selectedRows.length > 0 ? selectedRows.map((row) => row.original) : table.getFilteredRowModel().rows.map((row) => row.original);
85
+ onExport(dataToExport);
86
+ }
87
+ };
88
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
89
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
90
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
91
+ searchable && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
92
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" }),
93
+ /* @__PURE__ */ jsx(
94
+ Input,
95
+ {
96
+ placeholder: "Search all columns...",
97
+ value: globalFilter,
98
+ onChange: (e) => setGlobalFilter(e.target.value),
99
+ className: "pl-8 w-64"
100
+ }
101
+ )
102
+ ] }),
103
+ filterable && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", children: [
104
+ /* @__PURE__ */ jsx(Filter, { className: "mr-2 h-4 w-4" }),
105
+ "Filters"
106
+ ] })
107
+ ] }),
108
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
109
+ exportable && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: handleExport, children: [
110
+ /* @__PURE__ */ jsx(Download, { className: "mr-2 h-4 w-4" }),
111
+ "Export"
112
+ ] }),
113
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", children: [
114
+ /* @__PURE__ */ jsx(Settings, { className: "mr-2 h-4 w-4" }),
115
+ "Columns"
116
+ ] })
117
+ ] })
118
+ ] }),
119
+ /* @__PURE__ */ jsx("div", { className: "rounded-md border", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
120
+ /* @__PURE__ */ jsx("thead", { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx("tr", { className: "border-b", children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx(
121
+ "th",
122
+ {
123
+ className: "h-12 px-4 text-left align-middle font-medium text-muted-foreground",
124
+ children: header.isPlaceholder ? null : /* @__PURE__ */ jsxs(
125
+ "div",
126
+ {
127
+ className: cn(
128
+ "flex items-center space-x-2",
129
+ header.column.getCanSort() && "cursor-pointer select-none"
130
+ ),
131
+ onClick: header.column.getToggleSortingHandler(),
132
+ children: [
133
+ flexRender(header.column.columnDef.header, header.getContext()),
134
+ header.column.getCanSort() && /* @__PURE__ */ jsx("div", { className: "ml-2", children: header.column.getIsSorted() === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" }) : header.column.getIsSorted() === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "h-4 w-4" }) })
135
+ ]
136
+ }
137
+ )
138
+ },
139
+ header.id
140
+ )) }, headerGroup.id)) }),
141
+ /* @__PURE__ */ jsx("tbody", { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(
142
+ "tr",
143
+ {
144
+ className: cn(
145
+ "border-b transition-colors hover:bg-muted/50",
146
+ row.getIsSelected() && "bg-muted"
147
+ ),
148
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx("td", { className: "p-4 align-middle", children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))
149
+ },
150
+ row.id
151
+ )) : /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: columns.length, className: "h-24 text-center", children: "No results found." }) }) })
152
+ ] }) }),
153
+ pagination && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-2", children: [
154
+ /* @__PURE__ */ jsx("div", { className: "flex-1 text-sm text-muted-foreground", children: selectable && table.getFilteredSelectedRowModel().rows.length > 0 && /* @__PURE__ */ jsxs("span", { children: [
155
+ table.getFilteredSelectedRowModel().rows.length,
156
+ " of",
157
+ " ",
158
+ table.getFilteredRowModel().rows.length,
159
+ " row(s) selected."
160
+ ] }) }),
161
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-6 lg:space-x-8", children: [
162
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
163
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Rows per page" }),
164
+ /* @__PURE__ */ jsx(
165
+ "select",
166
+ {
167
+ value: table.getState().pagination.pageSize,
168
+ onChange: (e) => table.setPageSize(Number(e.target.value)),
169
+ className: "h-8 w-[70px] rounded border border-input bg-background px-3 py-1 text-sm",
170
+ children: [10, 20, 30, 40, 50].map((pageSize2) => /* @__PURE__ */ jsx("option", { value: pageSize2, children: pageSize2 }, pageSize2))
171
+ }
172
+ )
173
+ ] }),
174
+ /* @__PURE__ */ jsxs("div", { className: "flex w-[100px] items-center justify-center text-sm font-medium", children: [
175
+ "Page ",
176
+ table.getState().pagination.pageIndex + 1,
177
+ " of",
178
+ " ",
179
+ table.getPageCount()
180
+ ] }),
181
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
182
+ /* @__PURE__ */ jsx(
183
+ Button,
184
+ {
185
+ variant: "outline",
186
+ className: "hidden h-8 w-8 p-0 lg:flex",
187
+ onClick: () => table.setPageIndex(0),
188
+ disabled: !table.getCanPreviousPage(),
189
+ children: /* @__PURE__ */ jsx(ChevronsLeft, { className: "h-4 w-4" })
190
+ }
191
+ ),
192
+ /* @__PURE__ */ jsx(
193
+ Button,
194
+ {
195
+ variant: "outline",
196
+ className: "h-8 w-8 p-0",
197
+ onClick: () => table.previousPage(),
198
+ disabled: !table.getCanPreviousPage(),
199
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
200
+ }
201
+ ),
202
+ /* @__PURE__ */ jsx(
203
+ Button,
204
+ {
205
+ variant: "outline",
206
+ className: "h-8 w-8 p-0",
207
+ onClick: () => table.nextPage(),
208
+ disabled: !table.getCanNextPage(),
209
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
210
+ }
211
+ ),
212
+ /* @__PURE__ */ jsx(
213
+ Button,
214
+ {
215
+ variant: "outline",
216
+ className: "hidden h-8 w-8 p-0 lg:flex",
217
+ onClick: () => table.setPageIndex(table.getPageCount() - 1),
218
+ disabled: !table.getCanNextPage(),
219
+ children: /* @__PURE__ */ jsx(ChevronsRight, { className: "h-4 w-4" })
220
+ }
221
+ )
222
+ ] })
223
+ ] })
224
+ ] })
225
+ ] });
226
+ }
227
+
228
+ // src/hooks/use-data-table.ts
229
+ import React2 from "react";
230
+ function useDataTable(options) {
231
+ const {
232
+ data,
233
+ searchable = true,
234
+ pageSize = 10,
235
+ enableRowSelection = false,
236
+ enableMultiRowSelection = true
237
+ } = options;
238
+ const [searchQuery, setSearchQuery] = React2.useState("");
239
+ const [currentPage, setCurrentPage] = React2.useState(1);
240
+ const [selectedRows, setSelectedRows] = React2.useState([]);
241
+ const filteredData = React2.useMemo(() => {
242
+ if (!searchable || !searchQuery.trim()) {
243
+ return data;
244
+ }
245
+ return data.filter((row) => {
246
+ return Object.values(row).some((value) => {
247
+ if (value == null)
248
+ return false;
249
+ return String(value).toLowerCase().includes(searchQuery.toLowerCase());
250
+ });
251
+ });
252
+ }, [data, searchQuery, searchable]);
253
+ const totalPages = Math.ceil(filteredData.length / pageSize);
254
+ const paginatedData = React2.useMemo(() => {
255
+ const startIndex = (currentPage - 1) * pageSize;
256
+ const endIndex = startIndex + pageSize;
257
+ return filteredData.slice(startIndex, endIndex);
258
+ }, [filteredData, currentPage, pageSize]);
259
+ const selectRow = React2.useCallback((row) => {
260
+ if (!enableRowSelection)
261
+ return;
262
+ setSelectedRows((prev) => {
263
+ if (!enableMultiRowSelection) {
264
+ return [row];
265
+ }
266
+ const isSelected = prev.some(
267
+ (selectedRow) => JSON.stringify(selectedRow) === JSON.stringify(row)
268
+ );
269
+ if (isSelected) {
270
+ return prev.filter(
271
+ (selectedRow) => JSON.stringify(selectedRow) !== JSON.stringify(row)
272
+ );
273
+ } else {
274
+ return [...prev, row];
275
+ }
276
+ });
277
+ }, [enableRowSelection, enableMultiRowSelection]);
278
+ const selectAllRows = React2.useCallback(() => {
279
+ if (!enableRowSelection)
280
+ return;
281
+ setSelectedRows((prev) => {
282
+ if (prev.length === filteredData.length) {
283
+ return [];
284
+ } else {
285
+ return [...filteredData];
286
+ }
287
+ });
288
+ }, [enableRowSelection, filteredData]);
289
+ const clearSelection = React2.useCallback(() => {
290
+ setSelectedRows([]);
291
+ }, []);
292
+ const isRowSelected = React2.useCallback((row) => {
293
+ return selectedRows.some(
294
+ (selectedRow) => JSON.stringify(selectedRow) === JSON.stringify(row)
295
+ );
296
+ }, [selectedRows]);
297
+ const exportData = React2.useCallback((format = "csv") => {
298
+ const dataToExport = selectedRows.length > 0 ? selectedRows : filteredData;
299
+ if (format === "csv") {
300
+ const headers = Object.keys(dataToExport[0] || {});
301
+ const csvContent = [
302
+ headers.join(","),
303
+ ...dataToExport.map(
304
+ (row) => headers.map((header) => {
305
+ const value = row[header];
306
+ if (typeof value === "string" && (value.includes(",") || value.includes('"'))) {
307
+ return `"${value.replace(/"/g, '""')}"`;
308
+ }
309
+ return value;
310
+ }).join(",")
311
+ )
312
+ ].join("\n");
313
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
314
+ const link = document.createElement("a");
315
+ link.href = URL.createObjectURL(blob);
316
+ link.download = "data.csv";
317
+ link.click();
318
+ } else if (format === "json") {
319
+ const jsonContent = JSON.stringify(dataToExport, null, 2);
320
+ const blob = new Blob([jsonContent], { type: "application/json;charset=utf-8;" });
321
+ const link = document.createElement("a");
322
+ link.href = URL.createObjectURL(blob);
323
+ link.download = "data.json";
324
+ link.click();
325
+ }
326
+ }, [selectedRows, filteredData]);
327
+ const getRowCount = React2.useCallback(() => filteredData.length, [filteredData]);
328
+ const getSelectedCount = React2.useCallback(() => selectedRows.length, [selectedRows]);
329
+ React2.useEffect(() => {
330
+ setCurrentPage(1);
331
+ }, [searchQuery]);
332
+ return {
333
+ filteredData: paginatedData,
334
+ selectedRows,
335
+ searchQuery,
336
+ currentPage,
337
+ totalPages,
338
+ setSearchQuery,
339
+ setCurrentPage,
340
+ selectRow,
341
+ selectAllRows,
342
+ clearSelection,
343
+ exportData,
344
+ getRowCount,
345
+ getSelectedCount,
346
+ isRowSelected
347
+ };
348
+ }
349
+
350
+ // src/index.ts
351
+ import {
352
+ cn as cn2,
353
+ Button as Button2,
354
+ Input as Input2,
355
+ Card,
356
+ Badge,
357
+ Avatar,
358
+ Alert,
359
+ Toast,
360
+ Tooltip,
361
+ Dialog,
362
+ Select,
363
+ Switch,
364
+ Tabs
365
+ } from "@moontra/moonui";
366
+
367
+ // src/utils/data-processing.ts
368
+ function createDataProcessor() {
369
+ return {
370
+ filter: (data, predicate) => {
371
+ return data.filter(predicate);
372
+ },
373
+ sort: (data, key, direction = "asc") => {
374
+ return [...data].sort((a, b) => {
375
+ const aVal = a[key];
376
+ const bVal = b[key];
377
+ if (aVal === bVal)
378
+ return 0;
379
+ const comparison = aVal < bVal ? -1 : 1;
380
+ return direction === "asc" ? comparison : -comparison;
381
+ });
382
+ },
383
+ group: (data, key) => {
384
+ return data.reduce((groups, item) => {
385
+ const groupKey = String(item[key]);
386
+ if (!groups[groupKey]) {
387
+ groups[groupKey] = [];
388
+ }
389
+ groups[groupKey].push(item);
390
+ return groups;
391
+ }, {});
392
+ },
393
+ aggregate: (data, key, operation) => {
394
+ const values = data.map((item) => Number(item[key])).filter((val) => !isNaN(val));
395
+ if (values.length === 0)
396
+ return 0;
397
+ switch (operation) {
398
+ case "sum":
399
+ return values.reduce((sum, val) => sum + val, 0);
400
+ case "avg":
401
+ return values.reduce((sum, val) => sum + val, 0) / values.length;
402
+ case "min":
403
+ return Math.min(...values);
404
+ case "max":
405
+ return Math.max(...values);
406
+ case "count":
407
+ return values.length;
408
+ default:
409
+ return 0;
410
+ }
411
+ },
412
+ paginate: (data, page, pageSize) => {
413
+ const startIndex = (page - 1) * pageSize;
414
+ const endIndex = startIndex + pageSize;
415
+ return data.slice(startIndex, endIndex);
416
+ },
417
+ search: (data, query, searchKeys) => {
418
+ if (!query.trim())
419
+ return data;
420
+ const lowerQuery = query.toLowerCase();
421
+ return data.filter((item) => {
422
+ const keysToSearch = searchKeys || Object.keys(item);
423
+ return keysToSearch.some((key) => {
424
+ const value = item[key];
425
+ if (value == null)
426
+ return false;
427
+ return String(value).toLowerCase().includes(lowerQuery);
428
+ });
429
+ });
430
+ },
431
+ transform: (data, transformer) => {
432
+ return data.map(transformer);
433
+ }
434
+ };
435
+ }
436
+ export {
437
+ Alert,
438
+ Avatar,
439
+ Badge,
440
+ Button2 as Button,
441
+ Card,
442
+ DataTable,
443
+ Dialog,
444
+ Input2 as Input,
445
+ Select,
446
+ Switch,
447
+ Tabs,
448
+ Toast,
449
+ Tooltip,
450
+ cn2 as cn,
451
+ createDataProcessor,
452
+ useDataTable
453
+ };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@moontra/moonui-pro",
3
+ "version": "0.1.0",
4
+ "description": "Premium UI components for MoonUI - Professional component library",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup src/index.ts --format cjs,esm --dts --external react --external react-dom",
14
+ "dev": "tsup src/index.ts --format cjs,esm --dts --external react --external react-dom --watch",
15
+ "clean": "rm -rf dist",
16
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
17
+ "test": "jest"
18
+ },
19
+ "keywords": [
20
+ "ui",
21
+ "components",
22
+ "react",
23
+ "typescript",
24
+ "moonui",
25
+ "premium",
26
+ "pro"
27
+ ],
28
+ "author": "MoonUI Team",
29
+ "license": "Commercial",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/moontra/moonui-pro.git"
33
+ },
34
+ "homepage": "https://moonui.dev",
35
+ "peerDependencies": {
36
+ "react": ">=18.0.0",
37
+ "react-dom": ">=18.0.0"
38
+ },
39
+ "dependencies": {
40
+ "@moontra/moonui": "^0.1.0",
41
+ "@radix-ui/react-accordion": "^1.2.11",
42
+ "@radix-ui/react-avatar": "^1.1.10",
43
+ "@radix-ui/react-checkbox": "^1.3.2",
44
+ "@radix-ui/react-switch": "^1.1.1",
45
+ "@radix-ui/react-tabs": "^1.1.1",
46
+ "@radix-ui/react-tooltip": "^1.1.3",
47
+ "@tanstack/react-table": "^8.20.5",
48
+ "class-variance-authority": "^0.7.0",
49
+ "clsx": "^2.1.1",
50
+ "date-fns": "^3.6.0",
51
+ "framer-motion": "^11.11.17",
52
+ "lucide-react": "^0.263.1",
53
+ "react": "^19.1.0",
54
+ "react-dom": "^19.1.0",
55
+ "recharts": "^2.12.7",
56
+ "tailwind-merge": "^2.5.4",
57
+ "tailwindcss-animate": "^1.0.7"
58
+ },
59
+ "devDependencies": {
60
+ "@types/react": "^19.0.0",
61
+ "@types/react-dom": "^19.0.0",
62
+ "@types/node": "^18.16.0",
63
+ "eslint": "^8.39.0",
64
+ "jest": "^29.5.0",
65
+ "tsup": "^6.7.0",
66
+ "typescript": "^5.0.4"
67
+ },
68
+ "publishConfig": {
69
+ "access": "restricted"
70
+ }
71
+ }