@object-ui/plugin-grid 0.3.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/LICENSE +21 -0
- package/README.md +269 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +425 -0
- package/dist/index.umd.cjs +6 -0
- package/dist/plugin-grid/src/ObjectGrid.d.ts +14 -0
- package/dist/plugin-grid/src/index.d.ts +3 -0
- package/package.json +40 -0
- package/src/ObjectGrid.tsx +377 -0
- package/src/index.tsx +22 -0
- package/tsconfig.json +8 -0
- package/vite.config.ts +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ObjectQL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# @object-ui/plugin-grid
|
|
2
|
+
|
|
3
|
+
Grid plugin for Object UI - Advanced data grid with sorting, filtering, and pagination.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Data Grid** - Enterprise-grade data grid component
|
|
8
|
+
- **Sorting** - Multi-column sorting support
|
|
9
|
+
- **Filtering** - Column-level filtering
|
|
10
|
+
- **Pagination** - Built-in pagination controls
|
|
11
|
+
- **Row Selection** - Single and multi-row selection
|
|
12
|
+
- **Customizable** - Tailwind CSS styling support
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @object-ui/plugin-grid
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Automatic Registration (Side-Effect Import)
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// In your app entry point (e.g., App.tsx or main.tsx)
|
|
26
|
+
import '@object-ui/plugin-grid';
|
|
27
|
+
|
|
28
|
+
// Now you can use grid types in your schemas
|
|
29
|
+
const schema = {
|
|
30
|
+
type: 'grid',
|
|
31
|
+
columns: [
|
|
32
|
+
{ header: 'Name', accessorKey: 'name' },
|
|
33
|
+
{ header: 'Email', accessorKey: 'email' }
|
|
34
|
+
],
|
|
35
|
+
data: []
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Manual Registration
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { gridComponents } from '@object-ui/plugin-grid';
|
|
43
|
+
import { ComponentRegistry } from '@object-ui/core';
|
|
44
|
+
|
|
45
|
+
// Register grid components
|
|
46
|
+
Object.entries(gridComponents).forEach(([type, component]) => {
|
|
47
|
+
ComponentRegistry.register(type, component);
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Schema API
|
|
52
|
+
|
|
53
|
+
### Grid
|
|
54
|
+
|
|
55
|
+
Data grid with advanced features:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
{
|
|
59
|
+
type: 'grid',
|
|
60
|
+
columns: GridColumn[],
|
|
61
|
+
data?: any[],
|
|
62
|
+
sortable?: boolean,
|
|
63
|
+
filterable?: boolean,
|
|
64
|
+
pagination?: boolean | PaginationConfig,
|
|
65
|
+
selectable?: boolean,
|
|
66
|
+
onRowClick?: (row) => void,
|
|
67
|
+
className?: string
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Column Definition
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
interface GridColumn {
|
|
75
|
+
header: string;
|
|
76
|
+
accessorKey: string;
|
|
77
|
+
sortable?: boolean;
|
|
78
|
+
filterable?: boolean;
|
|
79
|
+
width?: number | string;
|
|
80
|
+
align?: 'left' | 'center' | 'right';
|
|
81
|
+
cell?: (value, row) => ReactNode;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Examples
|
|
86
|
+
|
|
87
|
+
### Basic Grid
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const schema = {
|
|
91
|
+
type: 'grid',
|
|
92
|
+
columns: [
|
|
93
|
+
{ header: 'ID', accessorKey: 'id', width: 80 },
|
|
94
|
+
{ header: 'Name', accessorKey: 'name' },
|
|
95
|
+
{ header: 'Email', accessorKey: 'email' },
|
|
96
|
+
{ header: 'Role', accessorKey: 'role' },
|
|
97
|
+
{ header: 'Status', accessorKey: 'status' }
|
|
98
|
+
],
|
|
99
|
+
data: [
|
|
100
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'Active' },
|
|
101
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'Active' }
|
|
102
|
+
],
|
|
103
|
+
sortable: true,
|
|
104
|
+
filterable: true,
|
|
105
|
+
pagination: true
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Grid with Custom Cells
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const schema = {
|
|
113
|
+
type: 'grid',
|
|
114
|
+
columns: [
|
|
115
|
+
{ header: 'Name', accessorKey: 'name' },
|
|
116
|
+
{
|
|
117
|
+
header: 'Status',
|
|
118
|
+
accessorKey: 'status',
|
|
119
|
+
cell: (value) => ({
|
|
120
|
+
type: 'badge',
|
|
121
|
+
label: value,
|
|
122
|
+
variant: value === 'Active' ? 'success' : 'default'
|
|
123
|
+
})
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
header: 'Actions',
|
|
127
|
+
accessorKey: 'id',
|
|
128
|
+
cell: (value, row) => ({
|
|
129
|
+
type: 'button-group',
|
|
130
|
+
buttons: [
|
|
131
|
+
{ label: 'Edit', onClick: () => console.log('Edit', row) },
|
|
132
|
+
{ label: 'Delete', variant: 'destructive', onClick: () => console.log('Delete', row) }
|
|
133
|
+
]
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
data: [/* data */]
|
|
138
|
+
};
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Selectable Grid
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const schema = {
|
|
145
|
+
type: 'grid',
|
|
146
|
+
columns: [/* columns */],
|
|
147
|
+
data: [/* data */],
|
|
148
|
+
selectable: true,
|
|
149
|
+
onRowClick: (row) => {
|
|
150
|
+
console.log('Row clicked:', row);
|
|
151
|
+
},
|
|
152
|
+
onSelectionChange: (selectedRows) => {
|
|
153
|
+
console.log('Selection changed:', selectedRows);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Grid with Pagination
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const schema = {
|
|
162
|
+
type: 'grid',
|
|
163
|
+
columns: [/* columns */],
|
|
164
|
+
data: [/* data */],
|
|
165
|
+
pagination: {
|
|
166
|
+
pageSize: 10,
|
|
167
|
+
showSizeChanger: true,
|
|
168
|
+
pageSizeOptions: [10, 20, 50, 100]
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Integration with Data Sources
|
|
174
|
+
|
|
175
|
+
Connect grid to backend APIs:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { createObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
179
|
+
|
|
180
|
+
const dataSource = createObjectStackAdapter({
|
|
181
|
+
baseUrl: 'https://api.example.com',
|
|
182
|
+
token: 'your-auth-token'
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const schema = {
|
|
186
|
+
type: 'object-grid',
|
|
187
|
+
dataSource,
|
|
188
|
+
object: 'users',
|
|
189
|
+
columns: [
|
|
190
|
+
{ header: 'Name', accessorKey: 'name' },
|
|
191
|
+
{ header: 'Email', accessorKey: 'email' },
|
|
192
|
+
{ header: 'Created', accessorKey: 'created_at' }
|
|
193
|
+
],
|
|
194
|
+
pagination: true,
|
|
195
|
+
sortable: true,
|
|
196
|
+
filterable: true
|
|
197
|
+
};
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Features
|
|
201
|
+
|
|
202
|
+
### Sorting
|
|
203
|
+
|
|
204
|
+
Enable sorting on columns:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const schema = {
|
|
208
|
+
type: 'grid',
|
|
209
|
+
sortable: true, // Enable sorting on all columns
|
|
210
|
+
columns: [
|
|
211
|
+
{ header: 'Name', accessorKey: 'name', sortable: true },
|
|
212
|
+
{ header: 'Email', accessorKey: 'email', sortable: false } // Disable for specific column
|
|
213
|
+
]
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Filtering
|
|
218
|
+
|
|
219
|
+
Enable column filtering:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const schema = {
|
|
223
|
+
type: 'grid',
|
|
224
|
+
filterable: true,
|
|
225
|
+
columns: [
|
|
226
|
+
{ header: 'Status', accessorKey: 'status', filterable: true }
|
|
227
|
+
]
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Row Actions
|
|
232
|
+
|
|
233
|
+
Add actions to rows:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
const schema = {
|
|
237
|
+
type: 'grid',
|
|
238
|
+
columns: [/* columns */],
|
|
239
|
+
rowActions: [
|
|
240
|
+
{ label: 'View', onClick: (row) => console.log('View', row) },
|
|
241
|
+
{ label: 'Edit', onClick: (row) => console.log('Edit', row) },
|
|
242
|
+
{ label: 'Delete', onClick: (row) => console.log('Delete', row) }
|
|
243
|
+
]
|
|
244
|
+
};
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## TypeScript Support
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import type { GridSchema, GridColumn } from '@object-ui/plugin-grid';
|
|
251
|
+
|
|
252
|
+
const nameColumn: GridColumn = {
|
|
253
|
+
header: 'Name',
|
|
254
|
+
accessorKey: 'name',
|
|
255
|
+
sortable: true
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const grid: GridSchema = {
|
|
259
|
+
type: 'grid',
|
|
260
|
+
columns: [nameColumn],
|
|
261
|
+
data: [],
|
|
262
|
+
sortable: true,
|
|
263
|
+
pagination: true
|
|
264
|
+
};
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import le, { useState as z, useEffect as J, useCallback as Z } from "react";
|
|
2
|
+
import { ComponentRegistry as oe } from "@object-ui/core";
|
|
3
|
+
import { SchemaRenderer as se } from "@object-ui/react";
|
|
4
|
+
import { getCellRenderer as ue } from "@object-ui/fields";
|
|
5
|
+
import { DropdownMenu as ce, DropdownMenuTrigger as fe, Button as de, DropdownMenuContent as pe, DropdownMenuItem as Q } from "@object-ui/components";
|
|
6
|
+
import { MoreVertical as be, Edit as ge, Trash2 as ve } from "lucide-react";
|
|
7
|
+
var I = { exports: {} }, h = {};
|
|
8
|
+
var ee;
|
|
9
|
+
function me() {
|
|
10
|
+
if (ee) return h;
|
|
11
|
+
ee = 1;
|
|
12
|
+
var r = /* @__PURE__ */ Symbol.for("react.transitional.element"), g = /* @__PURE__ */ Symbol.for("react.fragment");
|
|
13
|
+
function j(_, p, b) {
|
|
14
|
+
var y = null;
|
|
15
|
+
if (b !== void 0 && (y = "" + b), p.key !== void 0 && (y = "" + p.key), "key" in p) {
|
|
16
|
+
b = {};
|
|
17
|
+
for (var T in p)
|
|
18
|
+
T !== "key" && (b[T] = p[T]);
|
|
19
|
+
} else b = p;
|
|
20
|
+
return p = b.ref, {
|
|
21
|
+
$$typeof: r,
|
|
22
|
+
type: _,
|
|
23
|
+
key: y,
|
|
24
|
+
ref: p !== void 0 ? p : null,
|
|
25
|
+
props: b
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return h.Fragment = g, h.jsx = j, h.jsxs = j, h;
|
|
29
|
+
}
|
|
30
|
+
var C = {};
|
|
31
|
+
var re;
|
|
32
|
+
function Ee() {
|
|
33
|
+
return re || (re = 1, process.env.NODE_ENV !== "production" && (function() {
|
|
34
|
+
function r(e) {
|
|
35
|
+
if (e == null) return null;
|
|
36
|
+
if (typeof e == "function")
|
|
37
|
+
return e.$$typeof === v ? null : e.displayName || e.name || null;
|
|
38
|
+
if (typeof e == "string") return e;
|
|
39
|
+
switch (e) {
|
|
40
|
+
case w:
|
|
41
|
+
return "Fragment";
|
|
42
|
+
case R:
|
|
43
|
+
return "Profiler";
|
|
44
|
+
case $:
|
|
45
|
+
return "StrictMode";
|
|
46
|
+
case U:
|
|
47
|
+
return "Suspense";
|
|
48
|
+
case W:
|
|
49
|
+
return "SuspenseList";
|
|
50
|
+
case c:
|
|
51
|
+
return "Activity";
|
|
52
|
+
}
|
|
53
|
+
if (typeof e == "object")
|
|
54
|
+
switch (typeof e.tag == "number" && console.error(
|
|
55
|
+
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
|
|
56
|
+
), e.$$typeof) {
|
|
57
|
+
case M:
|
|
58
|
+
return "Portal";
|
|
59
|
+
case L:
|
|
60
|
+
return e.displayName || "Context";
|
|
61
|
+
case D:
|
|
62
|
+
return (e._context.displayName || "Context") + ".Consumer";
|
|
63
|
+
case O:
|
|
64
|
+
var t = e.render;
|
|
65
|
+
return e = e.displayName, e || (e = t.displayName || t.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
|
|
66
|
+
case q:
|
|
67
|
+
return t = e.displayName || null, t !== null ? t : r(e.type) || "Memo";
|
|
68
|
+
case N:
|
|
69
|
+
t = e._payload, e = e._init;
|
|
70
|
+
try {
|
|
71
|
+
return r(e(t));
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function g(e) {
|
|
78
|
+
return "" + e;
|
|
79
|
+
}
|
|
80
|
+
function j(e) {
|
|
81
|
+
try {
|
|
82
|
+
g(e);
|
|
83
|
+
var t = !1;
|
|
84
|
+
} catch {
|
|
85
|
+
t = !0;
|
|
86
|
+
}
|
|
87
|
+
if (t) {
|
|
88
|
+
t = console;
|
|
89
|
+
var o = t.error, a = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
|
|
90
|
+
return o.call(
|
|
91
|
+
t,
|
|
92
|
+
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
|
|
93
|
+
a
|
|
94
|
+
), g(e);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function _(e) {
|
|
98
|
+
if (e === w) return "<>";
|
|
99
|
+
if (typeof e == "object" && e !== null && e.$$typeof === N)
|
|
100
|
+
return "<...>";
|
|
101
|
+
try {
|
|
102
|
+
var t = r(e);
|
|
103
|
+
return t ? "<" + t + ">" : "<...>";
|
|
104
|
+
} catch {
|
|
105
|
+
return "<...>";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function p() {
|
|
109
|
+
var e = f.A;
|
|
110
|
+
return e === null ? null : e.getOwner();
|
|
111
|
+
}
|
|
112
|
+
function b() {
|
|
113
|
+
return Error("react-stack-top-frame");
|
|
114
|
+
}
|
|
115
|
+
function y(e) {
|
|
116
|
+
if (n.call(e, "key")) {
|
|
117
|
+
var t = Object.getOwnPropertyDescriptor(e, "key").get;
|
|
118
|
+
if (t && t.isReactWarning) return !1;
|
|
119
|
+
}
|
|
120
|
+
return e.key !== void 0;
|
|
121
|
+
}
|
|
122
|
+
function T(e, t) {
|
|
123
|
+
function o() {
|
|
124
|
+
F || (F = !0, console.error(
|
|
125
|
+
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
|
|
126
|
+
t
|
|
127
|
+
));
|
|
128
|
+
}
|
|
129
|
+
o.isReactWarning = !0, Object.defineProperty(e, "key", {
|
|
130
|
+
get: o,
|
|
131
|
+
configurable: !0
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function S() {
|
|
135
|
+
var e = r(this.type);
|
|
136
|
+
return X[e] || (X[e] = !0, console.error(
|
|
137
|
+
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
|
|
138
|
+
)), e = this.props.ref, e !== void 0 ? e : null;
|
|
139
|
+
}
|
|
140
|
+
function P(e, t, o, a, Y, V) {
|
|
141
|
+
var i = o.ref;
|
|
142
|
+
return e = {
|
|
143
|
+
$$typeof: d,
|
|
144
|
+
type: e,
|
|
145
|
+
key: t,
|
|
146
|
+
props: o,
|
|
147
|
+
_owner: a
|
|
148
|
+
}, (i !== void 0 ? i : null) !== null ? Object.defineProperty(e, "ref", {
|
|
149
|
+
enumerable: !1,
|
|
150
|
+
get: S
|
|
151
|
+
}) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
|
|
152
|
+
configurable: !1,
|
|
153
|
+
enumerable: !1,
|
|
154
|
+
writable: !0,
|
|
155
|
+
value: 0
|
|
156
|
+
}), Object.defineProperty(e, "_debugInfo", {
|
|
157
|
+
configurable: !1,
|
|
158
|
+
enumerable: !1,
|
|
159
|
+
writable: !0,
|
|
160
|
+
value: null
|
|
161
|
+
}), Object.defineProperty(e, "_debugStack", {
|
|
162
|
+
configurable: !1,
|
|
163
|
+
enumerable: !1,
|
|
164
|
+
writable: !0,
|
|
165
|
+
value: Y
|
|
166
|
+
}), Object.defineProperty(e, "_debugTask", {
|
|
167
|
+
configurable: !1,
|
|
168
|
+
enumerable: !1,
|
|
169
|
+
writable: !0,
|
|
170
|
+
value: V
|
|
171
|
+
}), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
|
|
172
|
+
}
|
|
173
|
+
function A(e, t, o, a, Y, V) {
|
|
174
|
+
var i = t.children;
|
|
175
|
+
if (i !== void 0)
|
|
176
|
+
if (a)
|
|
177
|
+
if (u(i)) {
|
|
178
|
+
for (a = 0; a < i.length; a++)
|
|
179
|
+
m(i[a]);
|
|
180
|
+
Object.freeze && Object.freeze(i);
|
|
181
|
+
} else
|
|
182
|
+
console.error(
|
|
183
|
+
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
|
|
184
|
+
);
|
|
185
|
+
else m(i);
|
|
186
|
+
if (n.call(t, "key")) {
|
|
187
|
+
i = r(e);
|
|
188
|
+
var x = Object.keys(t).filter(function(ie) {
|
|
189
|
+
return ie !== "key";
|
|
190
|
+
});
|
|
191
|
+
a = 0 < x.length ? "{key: someKey, " + x.join(": ..., ") + ": ...}" : "{key: someKey}", H[i + a] || (x = 0 < x.length ? "{" + x.join(": ..., ") + ": ...}" : "{}", console.error(
|
|
192
|
+
`A props object containing a "key" prop is being spread into JSX:
|
|
193
|
+
let props = %s;
|
|
194
|
+
<%s {...props} />
|
|
195
|
+
React keys must be passed directly to JSX without using spread:
|
|
196
|
+
let props = %s;
|
|
197
|
+
<%s key={someKey} {...props} />`,
|
|
198
|
+
a,
|
|
199
|
+
i,
|
|
200
|
+
x,
|
|
201
|
+
i
|
|
202
|
+
), H[i + a] = !0);
|
|
203
|
+
}
|
|
204
|
+
if (i = null, o !== void 0 && (j(o), i = "" + o), y(t) && (j(t.key), i = "" + t.key), "key" in t) {
|
|
205
|
+
o = {};
|
|
206
|
+
for (var G in t)
|
|
207
|
+
G !== "key" && (o[G] = t[G]);
|
|
208
|
+
} else o = t;
|
|
209
|
+
return i && T(
|
|
210
|
+
o,
|
|
211
|
+
typeof e == "function" ? e.displayName || e.name || "Unknown" : e
|
|
212
|
+
), P(
|
|
213
|
+
e,
|
|
214
|
+
i,
|
|
215
|
+
o,
|
|
216
|
+
p(),
|
|
217
|
+
Y,
|
|
218
|
+
V
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
function m(e) {
|
|
222
|
+
k(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === N && (e._payload.status === "fulfilled" ? k(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
|
|
223
|
+
}
|
|
224
|
+
function k(e) {
|
|
225
|
+
return typeof e == "object" && e !== null && e.$$typeof === d;
|
|
226
|
+
}
|
|
227
|
+
var s = le, d = /* @__PURE__ */ Symbol.for("react.transitional.element"), M = /* @__PURE__ */ Symbol.for("react.portal"), w = /* @__PURE__ */ Symbol.for("react.fragment"), $ = /* @__PURE__ */ Symbol.for("react.strict_mode"), R = /* @__PURE__ */ Symbol.for("react.profiler"), D = /* @__PURE__ */ Symbol.for("react.consumer"), L = /* @__PURE__ */ Symbol.for("react.context"), O = /* @__PURE__ */ Symbol.for("react.forward_ref"), U = /* @__PURE__ */ Symbol.for("react.suspense"), W = /* @__PURE__ */ Symbol.for("react.suspense_list"), q = /* @__PURE__ */ Symbol.for("react.memo"), N = /* @__PURE__ */ Symbol.for("react.lazy"), c = /* @__PURE__ */ Symbol.for("react.activity"), v = /* @__PURE__ */ Symbol.for("react.client.reference"), f = s.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, n = Object.prototype.hasOwnProperty, u = Array.isArray, E = console.createTask ? console.createTask : function() {
|
|
228
|
+
return null;
|
|
229
|
+
};
|
|
230
|
+
s = {
|
|
231
|
+
react_stack_bottom_frame: function(e) {
|
|
232
|
+
return e();
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var F, X = {}, B = s.react_stack_bottom_frame.bind(
|
|
236
|
+
s,
|
|
237
|
+
b
|
|
238
|
+
)(), K = E(_(b)), H = {};
|
|
239
|
+
C.Fragment = w, C.jsx = function(e, t, o) {
|
|
240
|
+
var a = 1e4 > f.recentlyCreatedOwnerStacks++;
|
|
241
|
+
return A(
|
|
242
|
+
e,
|
|
243
|
+
t,
|
|
244
|
+
o,
|
|
245
|
+
!1,
|
|
246
|
+
a ? Error("react-stack-top-frame") : B,
|
|
247
|
+
a ? E(_(e)) : K
|
|
248
|
+
);
|
|
249
|
+
}, C.jsxs = function(e, t, o) {
|
|
250
|
+
var a = 1e4 > f.recentlyCreatedOwnerStacks++;
|
|
251
|
+
return A(
|
|
252
|
+
e,
|
|
253
|
+
t,
|
|
254
|
+
o,
|
|
255
|
+
!0,
|
|
256
|
+
a ? Error("react-stack-top-frame") : B,
|
|
257
|
+
a ? E(_(e)) : K
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
})()), C;
|
|
261
|
+
}
|
|
262
|
+
var te;
|
|
263
|
+
function je() {
|
|
264
|
+
return te || (te = 1, process.env.NODE_ENV === "production" ? I.exports = me() : I.exports = Ee()), I.exports;
|
|
265
|
+
}
|
|
266
|
+
var l = je();
|
|
267
|
+
function _e(r) {
|
|
268
|
+
return r.data ? r.data : r.staticData ? {
|
|
269
|
+
provider: "value",
|
|
270
|
+
items: r.staticData
|
|
271
|
+
} : r.objectName ? {
|
|
272
|
+
provider: "object",
|
|
273
|
+
object: r.objectName
|
|
274
|
+
} : null;
|
|
275
|
+
}
|
|
276
|
+
function ne(r) {
|
|
277
|
+
if (!(!r || r.length === 0))
|
|
278
|
+
return typeof r[0] == "object" && r[0] !== null, r;
|
|
279
|
+
}
|
|
280
|
+
const ye = ({
|
|
281
|
+
schema: r,
|
|
282
|
+
dataSource: g,
|
|
283
|
+
onEdit: j,
|
|
284
|
+
onDelete: _,
|
|
285
|
+
onRowSelect: p
|
|
286
|
+
}) => {
|
|
287
|
+
const [b, y] = z([]), [T, S] = z(!0), [P, A] = z(null), [m, k] = z(null), s = _e(r), d = s?.provider === "value";
|
|
288
|
+
J(() => {
|
|
289
|
+
d && s?.provider === "value" && (y(s.items), S(!1));
|
|
290
|
+
}, [d, s]), J(() => {
|
|
291
|
+
const c = async () => {
|
|
292
|
+
try {
|
|
293
|
+
if (!g)
|
|
294
|
+
throw new Error("DataSource required");
|
|
295
|
+
const f = s?.provider === "object" ? s.object : r.objectName;
|
|
296
|
+
if (!f)
|
|
297
|
+
throw new Error("Object name required for object provider");
|
|
298
|
+
const n = await g.getObjectSchema(f);
|
|
299
|
+
k(n);
|
|
300
|
+
} catch (f) {
|
|
301
|
+
A(f);
|
|
302
|
+
}
|
|
303
|
+
}, v = ne(r.columns) || r.fields;
|
|
304
|
+
d && v ? k({ name: r.objectName, fields: {} }) : r.objectName && !d && g && c();
|
|
305
|
+
}, [r.objectName, r.columns, r.fields, g, d, s]);
|
|
306
|
+
const M = Z(() => {
|
|
307
|
+
const c = ne(r.columns);
|
|
308
|
+
if (c)
|
|
309
|
+
return c.length > 0 && typeof c[0] == "object" && c[0] !== null ? c.filter((n) => n?.field && typeof n.field == "string").map((n) => ({
|
|
310
|
+
header: n.label || n.field.charAt(0).toUpperCase() + n.field.slice(1).replace(/_/g, " "),
|
|
311
|
+
accessorKey: n.field,
|
|
312
|
+
...n.width && { width: n.width },
|
|
313
|
+
...n.align && { align: n.align },
|
|
314
|
+
sortable: n.sortable !== !1
|
|
315
|
+
})) : c.filter((n) => typeof n == "string" && n.trim().length > 0).map((n) => ({
|
|
316
|
+
header: n.charAt(0).toUpperCase() + n.slice(1).replace(/_/g, " "),
|
|
317
|
+
accessorKey: n
|
|
318
|
+
}));
|
|
319
|
+
if (d) {
|
|
320
|
+
const n = s?.provider === "value" ? s.items : [];
|
|
321
|
+
if (n.length > 0)
|
|
322
|
+
return (r.fields || Object.keys(n[0])).map((E) => ({
|
|
323
|
+
header: E.charAt(0).toUpperCase() + E.slice(1).replace(/_/g, " "),
|
|
324
|
+
accessorKey: E
|
|
325
|
+
}));
|
|
326
|
+
}
|
|
327
|
+
if (!m) return [];
|
|
328
|
+
const v = [];
|
|
329
|
+
return (r.fields || Object.keys(m.fields || {})).forEach((n) => {
|
|
330
|
+
const u = m.fields?.[n];
|
|
331
|
+
if (!u || u.permissions && u.permissions.read === !1) return;
|
|
332
|
+
const E = ue(u.type);
|
|
333
|
+
v.push({
|
|
334
|
+
header: u.label || n,
|
|
335
|
+
accessorKey: n,
|
|
336
|
+
cell: (F) => /* @__PURE__ */ l.jsx(E, { value: F, field: u }),
|
|
337
|
+
sortable: u.sortable !== !1
|
|
338
|
+
});
|
|
339
|
+
}), v;
|
|
340
|
+
}, [m, r.fields, r.columns, s, d]), w = Z(async () => {
|
|
341
|
+
if (!(d || !g)) {
|
|
342
|
+
S(!0);
|
|
343
|
+
try {
|
|
344
|
+
const c = s?.provider === "object" ? s.object : r.objectName;
|
|
345
|
+
if (!c)
|
|
346
|
+
throw new Error("Object name required for data fetching");
|
|
347
|
+
const f = {
|
|
348
|
+
$select: (() => {
|
|
349
|
+
if (r.fields) return r.fields;
|
|
350
|
+
if (r.columns && Array.isArray(r.columns))
|
|
351
|
+
return r.columns.map((u) => typeof u == "string" ? u : u.field);
|
|
352
|
+
})(),
|
|
353
|
+
$top: r.pagination?.pageSize || r.pageSize || 50
|
|
354
|
+
};
|
|
355
|
+
r.filter && Array.isArray(r.filter) ? f.$filter = r.filter : "defaultFilters" in r && r.defaultFilters && (f.$filter = r.defaultFilters), r.sort ? typeof r.sort == "string" ? f.$orderby = r.sort : Array.isArray(r.sort) && (f.$orderby = r.sort.map((u) => `${u.field} ${u.order}`).join(", ")) : "defaultSort" in r && r.defaultSort && (f.$orderby = `${r.defaultSort.field} ${r.defaultSort.order}`);
|
|
356
|
+
const n = await g.find(c, f);
|
|
357
|
+
y(n.data || []);
|
|
358
|
+
} catch (c) {
|
|
359
|
+
A(c);
|
|
360
|
+
} finally {
|
|
361
|
+
S(!1);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}, [r, g, d, s]);
|
|
365
|
+
if (J(() => {
|
|
366
|
+
(m || d) && w();
|
|
367
|
+
}, [m, d, w]), P)
|
|
368
|
+
return /* @__PURE__ */ l.jsxs("div", { className: "p-4 border border-red-300 bg-red-50 rounded-md", children: [
|
|
369
|
+
/* @__PURE__ */ l.jsx("h3", { className: "text-red-800 font-semibold", children: "Error loading grid" }),
|
|
370
|
+
/* @__PURE__ */ l.jsx("p", { className: "text-red-600 text-sm mt-1", children: P.message })
|
|
371
|
+
] });
|
|
372
|
+
if (T && b.length === 0)
|
|
373
|
+
return /* @__PURE__ */ l.jsxs("div", { className: "p-8 text-center", children: [
|
|
374
|
+
/* @__PURE__ */ l.jsx("div", { className: "inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900" }),
|
|
375
|
+
/* @__PURE__ */ l.jsx("p", { className: "mt-2 text-sm text-gray-600", children: "Loading grid..." })
|
|
376
|
+
] });
|
|
377
|
+
const $ = M(), R = "operations" in r ? r.operations : void 0, D = R && (R.update || R.delete), L = D ? [
|
|
378
|
+
...$,
|
|
379
|
+
{
|
|
380
|
+
header: "Actions",
|
|
381
|
+
accessorKey: "_actions",
|
|
382
|
+
cell: (c, v) => /* @__PURE__ */ l.jsxs(ce, { children: [
|
|
383
|
+
/* @__PURE__ */ l.jsx(fe, { asChild: !0, children: /* @__PURE__ */ l.jsxs(de, { variant: "ghost", size: "icon", className: "h-8 w-8", children: [
|
|
384
|
+
/* @__PURE__ */ l.jsx(be, { className: "h-4 w-4" }),
|
|
385
|
+
/* @__PURE__ */ l.jsx("span", { className: "sr-only", children: "Open menu" })
|
|
386
|
+
] }) }),
|
|
387
|
+
/* @__PURE__ */ l.jsxs(pe, { align: "end", children: [
|
|
388
|
+
R?.update && j && /* @__PURE__ */ l.jsxs(Q, { onClick: () => j(v), children: [
|
|
389
|
+
/* @__PURE__ */ l.jsx(ge, { className: "mr-2 h-4 w-4" }),
|
|
390
|
+
"Edit"
|
|
391
|
+
] }),
|
|
392
|
+
R?.delete && _ && /* @__PURE__ */ l.jsxs(Q, { variant: "destructive", onClick: () => _(v), children: [
|
|
393
|
+
/* @__PURE__ */ l.jsx(ve, { className: "mr-2 h-4 w-4" }),
|
|
394
|
+
"Delete"
|
|
395
|
+
] })
|
|
396
|
+
] })
|
|
397
|
+
] }),
|
|
398
|
+
sortable: !1
|
|
399
|
+
}
|
|
400
|
+
] : $;
|
|
401
|
+
let O = !1;
|
|
402
|
+
r.selection?.type ? O = r.selection.type === "none" ? !1 : r.selection.type : r.selectable !== void 0 && (O = r.selectable);
|
|
403
|
+
const U = r.pagination !== void 0 ? !0 : r.showPagination !== void 0 ? r.showPagination : !0, W = r.pagination?.pageSize || r.pageSize || 10, q = r.searchableFields !== void 0 ? r.searchableFields.length > 0 : r.showSearch !== void 0 ? r.showSearch : !0, N = {
|
|
404
|
+
type: "data-table",
|
|
405
|
+
caption: r.label || r.title,
|
|
406
|
+
columns: L,
|
|
407
|
+
data: b,
|
|
408
|
+
pagination: U,
|
|
409
|
+
pageSize: W,
|
|
410
|
+
searchable: q,
|
|
411
|
+
selectable: O,
|
|
412
|
+
sortable: !0,
|
|
413
|
+
exportable: R?.export,
|
|
414
|
+
rowActions: D,
|
|
415
|
+
resizableColumns: r.resizable ?? r.resizableColumns ?? !0,
|
|
416
|
+
className: r.className,
|
|
417
|
+
onSelectionChange: p
|
|
418
|
+
};
|
|
419
|
+
return /* @__PURE__ */ l.jsx(se, { schema: N });
|
|
420
|
+
}, ae = ({ schema: r }) => /* @__PURE__ */ l.jsx(ye, { schema: r, dataSource: null });
|
|
421
|
+
oe.register("object-grid", ae);
|
|
422
|
+
oe.register("grid", ae);
|
|
423
|
+
export {
|
|
424
|
+
ye as ObjectGrid
|
|
425
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
(function(v,d){typeof exports=="object"&&typeof module<"u"?d(exports,require("react"),require("@object-ui/core"),require("@object-ui/react"),require("@object-ui/fields"),require("@object-ui/components"),require("lucide-react")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","@object-ui/react","@object-ui/fields","@object-ui/components","lucide-react"],d):(v=typeof globalThis<"u"?globalThis:v||self,d(v.ObjectUIPluginGrid={},v.React,v.core,v.react,v.fields,v.components,v.lucideReact))})(this,(function(v,d,K,se,le,x,W){"use strict";var Y={exports:{}},A={};var H;function ce(){if(H)return A;H=1;var r=Symbol.for("react.transitional.element"),g=Symbol.for("react.fragment");function y(R,p,j){var T=null;if(j!==void 0&&(T=""+j),p.key!==void 0&&(T=""+p.key),"key"in p){j={};for(var S in p)S!=="key"&&(j[S]=p[S])}else j=p;return p=j.ref,{$$typeof:r,type:R,key:T,ref:p!==void 0?p:null,props:j}}return A.Fragment=g,A.jsx=y,A.jsxs=y,A}var h={};var Z;function ue(){return Z||(Z=1,process.env.NODE_ENV!=="production"&&(function(){function r(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===E?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case O:return"Fragment";case w:return"Profiler";case M:return"StrictMode";case J:return"Suspense";case $:return"SuspenseList";case u:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case G:return"Portal";case V:return e.displayName||"Context";case z:return(e._context.displayName||"Context")+".Consumer";case D:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case q:return t=e.displayName||null,t!==null?t:r(e.type)||"Memo";case F:t=e._payload,e=e._init;try{return r(e(t))}catch{}}return null}function g(e){return""+e}function y(e){try{g(e);var t=!1}catch{t=!0}if(t){t=console;var o=t.error,i=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return o.call(t,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",i),g(e)}}function R(e){if(e===O)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===F)return"<...>";try{var t=r(e);return t?"<"+t+">":"<...>"}catch{return"<...>"}}function p(){var e=f.A;return e===null?null:e.getOwner()}function j(){return Error("react-stack-top-frame")}function T(e){if(n.call(e,"key")){var t=Object.getOwnPropertyDescriptor(e,"key").get;if(t&&t.isReactWarning)return!1}return e.key!==void 0}function S(e,t){function o(){U||(U=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",t))}o.isReactWarning=!0,Object.defineProperty(e,"key",{get:o,configurable:!0})}function C(){var e=r(this.type);return ne[e]||(ne[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function I(e,t,o,i,L,X){var s=o.ref;return e={$$typeof:b,type:e,key:t,props:o,_owner:i},(s!==void 0?s:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:C}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:L}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:X}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function N(e,t,o,i,L,X){var s=t.children;if(s!==void 0)if(i)if(c(s)){for(i=0;i<s.length;i++)_(s[i]);Object.freeze&&Object.freeze(s)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else _(s);if(n.call(t,"key")){s=r(e);var k=Object.keys(t).filter(function(be){return be!=="key"});i=0<k.length?"{key: someKey, "+k.join(": ..., ")+": ...}":"{key: someKey}",ie[s+i]||(k=0<k.length?"{"+k.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
|
|
2
|
+
let props = %s;
|
|
3
|
+
<%s {...props} />
|
|
4
|
+
React keys must be passed directly to JSX without using spread:
|
|
5
|
+
let props = %s;
|
|
6
|
+
<%s key={someKey} {...props} />`,i,s,k,s),ie[s+i]=!0)}if(s=null,o!==void 0&&(y(o),s=""+o),T(t)&&(y(t.key),s=""+t.key),"key"in t){o={};for(var B in t)B!=="key"&&(o[B]=t[B])}else o=t;return s&&S(o,typeof e=="function"?e.displayName||e.name||"Unknown":e),I(e,s,o,p(),L,X)}function _(e){P(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===F&&(e._payload.status==="fulfilled"?P(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function P(e){return typeof e=="object"&&e!==null&&e.$$typeof===b}var l=d,b=Symbol.for("react.transitional.element"),G=Symbol.for("react.portal"),O=Symbol.for("react.fragment"),M=Symbol.for("react.strict_mode"),w=Symbol.for("react.profiler"),z=Symbol.for("react.consumer"),V=Symbol.for("react.context"),D=Symbol.for("react.forward_ref"),J=Symbol.for("react.suspense"),$=Symbol.for("react.suspense_list"),q=Symbol.for("react.memo"),F=Symbol.for("react.lazy"),u=Symbol.for("react.activity"),E=Symbol.for("react.client.reference"),f=l.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,n=Object.prototype.hasOwnProperty,c=Array.isArray,m=console.createTask?console.createTask:function(){return null};l={react_stack_bottom_frame:function(e){return e()}};var U,ne={},oe=l.react_stack_bottom_frame.bind(l,j)(),ae=m(R(j)),ie={};h.Fragment=O,h.jsx=function(e,t,o){var i=1e4>f.recentlyCreatedOwnerStacks++;return N(e,t,o,!1,i?Error("react-stack-top-frame"):oe,i?m(R(e)):ae)},h.jsxs=function(e,t,o){var i=1e4>f.recentlyCreatedOwnerStacks++;return N(e,t,o,!0,i?Error("react-stack-top-frame"):oe,i?m(R(e)):ae)}})()),h}var Q;function fe(){return Q||(Q=1,process.env.NODE_ENV==="production"?Y.exports=ce():Y.exports=ue()),Y.exports}var a=fe();function de(r){return r.data?r.data:r.staticData?{provider:"value",items:r.staticData}:r.objectName?{provider:"object",object:r.objectName}:null}function ee(r){if(!(!r||r.length===0))return typeof r[0]=="object"&&r[0]!==null,r}const re=({schema:r,dataSource:g,onEdit:y,onDelete:R,onRowSelect:p})=>{const[j,T]=d.useState([]),[S,C]=d.useState(!0),[I,N]=d.useState(null),[_,P]=d.useState(null),l=de(r),b=l?.provider==="value";d.useEffect(()=>{b&&l?.provider==="value"&&(T(l.items),C(!1))},[b,l]),d.useEffect(()=>{const u=async()=>{try{if(!g)throw new Error("DataSource required");const f=l?.provider==="object"?l.object:r.objectName;if(!f)throw new Error("Object name required for object provider");const n=await g.getObjectSchema(f);P(n)}catch(f){N(f)}},E=ee(r.columns)||r.fields;b&&E?P({name:r.objectName,fields:{}}):r.objectName&&!b&&g&&u()},[r.objectName,r.columns,r.fields,g,b,l]);const G=d.useCallback(()=>{const u=ee(r.columns);if(u)return u.length>0&&typeof u[0]=="object"&&u[0]!==null?u.filter(n=>n?.field&&typeof n.field=="string").map(n=>({header:n.label||n.field.charAt(0).toUpperCase()+n.field.slice(1).replace(/_/g," "),accessorKey:n.field,...n.width&&{width:n.width},...n.align&&{align:n.align},sortable:n.sortable!==!1})):u.filter(n=>typeof n=="string"&&n.trim().length>0).map(n=>({header:n.charAt(0).toUpperCase()+n.slice(1).replace(/_/g," "),accessorKey:n}));if(b){const n=l?.provider==="value"?l.items:[];if(n.length>0)return(r.fields||Object.keys(n[0])).map(m=>({header:m.charAt(0).toUpperCase()+m.slice(1).replace(/_/g," "),accessorKey:m}))}if(!_)return[];const E=[];return(r.fields||Object.keys(_.fields||{})).forEach(n=>{const c=_.fields?.[n];if(!c||c.permissions&&c.permissions.read===!1)return;const m=le.getCellRenderer(c.type);E.push({header:c.label||n,accessorKey:n,cell:U=>a.jsx(m,{value:U,field:c}),sortable:c.sortable!==!1})}),E},[_,r.fields,r.columns,l,b]),O=d.useCallback(async()=>{if(!(b||!g)){C(!0);try{const u=l?.provider==="object"?l.object:r.objectName;if(!u)throw new Error("Object name required for data fetching");const f={$select:(()=>{if(r.fields)return r.fields;if(r.columns&&Array.isArray(r.columns))return r.columns.map(c=>typeof c=="string"?c:c.field)})(),$top:r.pagination?.pageSize||r.pageSize||50};r.filter&&Array.isArray(r.filter)?f.$filter=r.filter:"defaultFilters"in r&&r.defaultFilters&&(f.$filter=r.defaultFilters),r.sort?typeof r.sort=="string"?f.$orderby=r.sort:Array.isArray(r.sort)&&(f.$orderby=r.sort.map(c=>`${c.field} ${c.order}`).join(", ")):"defaultSort"in r&&r.defaultSort&&(f.$orderby=`${r.defaultSort.field} ${r.defaultSort.order}`);const n=await g.find(u,f);T(n.data||[])}catch(u){N(u)}finally{C(!1)}}},[r,g,b,l]);if(d.useEffect(()=>{(_||b)&&O()},[_,b,O]),I)return a.jsxs("div",{className:"p-4 border border-red-300 bg-red-50 rounded-md",children:[a.jsx("h3",{className:"text-red-800 font-semibold",children:"Error loading grid"}),a.jsx("p",{className:"text-red-600 text-sm mt-1",children:I.message})]});if(S&&j.length===0)return a.jsxs("div",{className:"p-8 text-center",children:[a.jsx("div",{className:"inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"}),a.jsx("p",{className:"mt-2 text-sm text-gray-600",children:"Loading grid..."})]});const M=G(),w="operations"in r?r.operations:void 0,z=w&&(w.update||w.delete),V=z?[...M,{header:"Actions",accessorKey:"_actions",cell:(u,E)=>a.jsxs(x.DropdownMenu,{children:[a.jsx(x.DropdownMenuTrigger,{asChild:!0,children:a.jsxs(x.Button,{variant:"ghost",size:"icon",className:"h-8 w-8",children:[a.jsx(W.MoreVertical,{className:"h-4 w-4"}),a.jsx("span",{className:"sr-only",children:"Open menu"})]})}),a.jsxs(x.DropdownMenuContent,{align:"end",children:[w?.update&&y&&a.jsxs(x.DropdownMenuItem,{onClick:()=>y(E),children:[a.jsx(W.Edit,{className:"mr-2 h-4 w-4"}),"Edit"]}),w?.delete&&R&&a.jsxs(x.DropdownMenuItem,{variant:"destructive",onClick:()=>R(E),children:[a.jsx(W.Trash2,{className:"mr-2 h-4 w-4"}),"Delete"]})]})]}),sortable:!1}]:M;let D=!1;r.selection?.type?D=r.selection.type==="none"?!1:r.selection.type:r.selectable!==void 0&&(D=r.selectable);const J=r.pagination!==void 0?!0:r.showPagination!==void 0?r.showPagination:!0,$=r.pagination?.pageSize||r.pageSize||10,q=r.searchableFields!==void 0?r.searchableFields.length>0:r.showSearch!==void 0?r.showSearch:!0,F={type:"data-table",caption:r.label||r.title,columns:V,data:j,pagination:J,pageSize:$,searchable:q,selectable:D,sortable:!0,exportable:w?.export,rowActions:z,resizableColumns:r.resizable??r.resizableColumns??!0,className:r.className,onSelectionChange:p};return a.jsx(se.SchemaRenderer,{schema:F})},te=({schema:r})=>a.jsx(re,{schema:r,dataSource:null});K.ComponentRegistry.register("object-grid",te),K.ComponentRegistry.register("grid",te),v.ObjectGrid=re,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { ObjectGridSchema, DataSource } from '../../types/src';
|
|
3
|
+
export interface ObjectGridProps {
|
|
4
|
+
schema: ObjectGridSchema;
|
|
5
|
+
dataSource?: DataSource;
|
|
6
|
+
className?: string;
|
|
7
|
+
onRowClick?: (record: any) => void;
|
|
8
|
+
onEdit?: (record: any) => void;
|
|
9
|
+
onDelete?: (record: any) => void;
|
|
10
|
+
onBulkDelete?: (records: any[]) => void;
|
|
11
|
+
onCellChange?: (rowIndex: number, columnKey: string, newValue: any) => void;
|
|
12
|
+
onRowSelect?: (selectedRows: any[]) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare const ObjectGrid: React.FC<ObjectGridProps>;
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@object-ui/plugin-grid",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Grid plugin for Object UI",
|
|
7
|
+
"main": "dist/index.umd.cjs",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.umd.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"lucide-react": "^0.563.0",
|
|
19
|
+
"@object-ui/components": "0.3.0",
|
|
20
|
+
"@object-ui/core": "0.3.0",
|
|
21
|
+
"@object-ui/fields": "0.3.0",
|
|
22
|
+
"@object-ui/react": "0.3.0",
|
|
23
|
+
"@object-ui/types": "0.3.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
27
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
31
|
+
"typescript": "^5.9.3",
|
|
32
|
+
"vite": "^7.3.1",
|
|
33
|
+
"vite-plugin-dts": "^4.5.4"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "vite build",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"lint": "eslint ."
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ObjectGrid Component
|
|
11
|
+
*
|
|
12
|
+
* A specialized grid component built on top of data-table.
|
|
13
|
+
* Auto-generates columns from ObjectQL schema with type-aware rendering.
|
|
14
|
+
* Implements the grid view type from @objectstack/spec view.zod ListView schema.
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Traditional table/grid with CRUD operations
|
|
18
|
+
* - Search, filters, pagination
|
|
19
|
+
* - Column resizing, sorting
|
|
20
|
+
* - Row selection
|
|
21
|
+
* - Inline editing support
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import React, { useEffect, useState, useCallback } from 'react';
|
|
25
|
+
import type { ObjectGridSchema, DataSource, ListColumn, ViewData } from '@object-ui/types';
|
|
26
|
+
import { SchemaRenderer } from '@object-ui/react';
|
|
27
|
+
import { getCellRenderer } from '@object-ui/fields';
|
|
28
|
+
import { Button } from '@object-ui/components';
|
|
29
|
+
import {
|
|
30
|
+
DropdownMenu,
|
|
31
|
+
DropdownMenuContent,
|
|
32
|
+
DropdownMenuItem,
|
|
33
|
+
DropdownMenuTrigger,
|
|
34
|
+
} from '@object-ui/components';
|
|
35
|
+
import { Edit, Trash2, MoreVertical } from 'lucide-react';
|
|
36
|
+
|
|
37
|
+
export interface ObjectGridProps {
|
|
38
|
+
schema: ObjectGridSchema;
|
|
39
|
+
dataSource?: DataSource;
|
|
40
|
+
className?: string;
|
|
41
|
+
onRowClick?: (record: any) => void;
|
|
42
|
+
onEdit?: (record: any) => void;
|
|
43
|
+
onDelete?: (record: any) => void;
|
|
44
|
+
onBulkDelete?: (records: any[]) => void;
|
|
45
|
+
onCellChange?: (rowIndex: number, columnKey: string, newValue: any) => void;
|
|
46
|
+
onRowSelect?: (selectedRows: any[]) => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Helper to get data configuration from schema
|
|
51
|
+
* Handles both new ViewData format and legacy inline data
|
|
52
|
+
*/
|
|
53
|
+
function getDataConfig(schema: ObjectGridSchema): ViewData | null {
|
|
54
|
+
// New format: explicit data configuration
|
|
55
|
+
if (schema.data) {
|
|
56
|
+
return schema.data;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Legacy format: staticData field
|
|
60
|
+
if (schema.staticData) {
|
|
61
|
+
return {
|
|
62
|
+
provider: 'value',
|
|
63
|
+
items: schema.staticData,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Default: use object provider with objectName
|
|
68
|
+
if (schema.objectName) {
|
|
69
|
+
return {
|
|
70
|
+
provider: 'object',
|
|
71
|
+
object: schema.objectName,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Helper to normalize columns configuration
|
|
80
|
+
* Handles both string[] and ListColumn[] formats
|
|
81
|
+
*/
|
|
82
|
+
function normalizeColumns(
|
|
83
|
+
columns: string[] | ListColumn[] | undefined
|
|
84
|
+
): ListColumn[] | string[] | undefined {
|
|
85
|
+
if (!columns || columns.length === 0) return undefined;
|
|
86
|
+
|
|
87
|
+
// Already in ListColumn format - check for object type with optional chaining
|
|
88
|
+
if (typeof columns[0] === 'object' && columns[0] !== null) {
|
|
89
|
+
return columns as ListColumn[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// String array format
|
|
93
|
+
return columns as string[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const ObjectGrid: React.FC<ObjectGridProps> = ({
|
|
97
|
+
schema,
|
|
98
|
+
dataSource,
|
|
99
|
+
onEdit,
|
|
100
|
+
onDelete,
|
|
101
|
+
onRowSelect,
|
|
102
|
+
}) => {
|
|
103
|
+
const [data, setData] = useState<any[]>([]);
|
|
104
|
+
const [loading, setLoading] = useState(true);
|
|
105
|
+
const [error, setError] = useState<Error | null>(null);
|
|
106
|
+
const [objectSchema, setObjectSchema] = useState<any>(null);
|
|
107
|
+
|
|
108
|
+
// Get data configuration (supports both new and legacy formats)
|
|
109
|
+
const dataConfig = getDataConfig(schema);
|
|
110
|
+
const hasInlineData = dataConfig?.provider === 'value';
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (hasInlineData && dataConfig?.provider === 'value') {
|
|
114
|
+
setData(dataConfig.items as any[]);
|
|
115
|
+
setLoading(false);
|
|
116
|
+
}
|
|
117
|
+
}, [hasInlineData, dataConfig]);
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
const fetchObjectSchema = async () => {
|
|
121
|
+
try {
|
|
122
|
+
if (!dataSource) {
|
|
123
|
+
throw new Error('DataSource required');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// For object provider, get the object name
|
|
127
|
+
const objectName = dataConfig?.provider === 'object'
|
|
128
|
+
? dataConfig.object
|
|
129
|
+
: schema.objectName;
|
|
130
|
+
|
|
131
|
+
if (!objectName) {
|
|
132
|
+
throw new Error('Object name required for object provider');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const schemaData = await dataSource.getObjectSchema(objectName);
|
|
136
|
+
setObjectSchema(schemaData);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
setError(err as Error);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Normalize columns (support both legacy 'fields' and new 'columns')
|
|
143
|
+
const cols = normalizeColumns(schema.columns) || schema.fields;
|
|
144
|
+
|
|
145
|
+
if (hasInlineData && cols) {
|
|
146
|
+
setObjectSchema({ name: schema.objectName, fields: {} });
|
|
147
|
+
} else if (schema.objectName && !hasInlineData && dataSource) {
|
|
148
|
+
fetchObjectSchema();
|
|
149
|
+
}
|
|
150
|
+
}, [schema.objectName, schema.columns, schema.fields, dataSource, hasInlineData, dataConfig]);
|
|
151
|
+
|
|
152
|
+
const generateColumns = useCallback(() => {
|
|
153
|
+
// Use normalized columns (support both new and legacy)
|
|
154
|
+
const cols = normalizeColumns(schema.columns);
|
|
155
|
+
|
|
156
|
+
if (cols) {
|
|
157
|
+
// If columns are already ListColumn objects, convert them to data-table format
|
|
158
|
+
if (cols.length > 0 && typeof cols[0] === 'object' && cols[0] !== null) {
|
|
159
|
+
return (cols as ListColumn[])
|
|
160
|
+
.filter((col) => col?.field && typeof col.field === 'string') // Filter out invalid column objects
|
|
161
|
+
.map((col) => ({
|
|
162
|
+
header: col.label || col.field.charAt(0).toUpperCase() + col.field.slice(1).replace(/_/g, ' '),
|
|
163
|
+
accessorKey: col.field,
|
|
164
|
+
...(col.width && { width: col.width }),
|
|
165
|
+
...(col.align && { align: col.align }),
|
|
166
|
+
sortable: col.sortable !== false,
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// String array format - filter out invalid entries
|
|
171
|
+
return (cols as string[])
|
|
172
|
+
.filter((fieldName) => typeof fieldName === 'string' && fieldName.trim().length > 0)
|
|
173
|
+
.map((fieldName) => ({
|
|
174
|
+
header: fieldName.charAt(0).toUpperCase() + fieldName.slice(1).replace(/_/g, ' '),
|
|
175
|
+
accessorKey: fieldName,
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Legacy support: use 'fields' if columns not provided
|
|
180
|
+
if (hasInlineData) {
|
|
181
|
+
const inlineData = dataConfig?.provider === 'value' ? dataConfig.items as any[] : [];
|
|
182
|
+
if (inlineData.length > 0) {
|
|
183
|
+
const fieldsToShow = schema.fields || Object.keys(inlineData[0]);
|
|
184
|
+
return fieldsToShow.map((fieldName) => ({
|
|
185
|
+
header: fieldName.charAt(0).toUpperCase() + fieldName.slice(1).replace(/_/g, ' '),
|
|
186
|
+
accessorKey: fieldName,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!objectSchema) return [];
|
|
192
|
+
|
|
193
|
+
const generatedColumns: any[] = [];
|
|
194
|
+
const fieldsToShow = schema.fields || Object.keys(objectSchema.fields || {});
|
|
195
|
+
|
|
196
|
+
fieldsToShow.forEach((fieldName) => {
|
|
197
|
+
const field = objectSchema.fields?.[fieldName];
|
|
198
|
+
if (!field) return;
|
|
199
|
+
|
|
200
|
+
if (field.permissions && field.permissions.read === false) return;
|
|
201
|
+
|
|
202
|
+
const CellRenderer = getCellRenderer(field.type);
|
|
203
|
+
generatedColumns.push({
|
|
204
|
+
header: field.label || fieldName,
|
|
205
|
+
accessorKey: fieldName,
|
|
206
|
+
cell: (value: any) => <CellRenderer value={value} field={field} />,
|
|
207
|
+
sortable: field.sortable !== false,
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return generatedColumns;
|
|
212
|
+
}, [objectSchema, schema.fields, schema.columns, dataConfig, hasInlineData]);
|
|
213
|
+
|
|
214
|
+
const fetchData = useCallback(async () => {
|
|
215
|
+
if (hasInlineData || !dataSource) return;
|
|
216
|
+
|
|
217
|
+
setLoading(true);
|
|
218
|
+
try {
|
|
219
|
+
// Get object name from data config or schema
|
|
220
|
+
const objectName = dataConfig?.provider === 'object'
|
|
221
|
+
? dataConfig.object
|
|
222
|
+
: schema.objectName;
|
|
223
|
+
|
|
224
|
+
if (!objectName) {
|
|
225
|
+
throw new Error('Object name required for data fetching');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Helper to get select fields
|
|
229
|
+
const getSelectFields = () => {
|
|
230
|
+
if (schema.fields) return schema.fields;
|
|
231
|
+
if (schema.columns && Array.isArray(schema.columns)) {
|
|
232
|
+
return schema.columns.map(c => typeof c === 'string' ? c : c.field);
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const params: any = {
|
|
238
|
+
$select: getSelectFields(),
|
|
239
|
+
$top: schema.pagination?.pageSize || schema.pageSize || 50,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Support new filter format
|
|
243
|
+
if (schema.filter && Array.isArray(schema.filter)) {
|
|
244
|
+
params.$filter = schema.filter;
|
|
245
|
+
} else if ('defaultFilters' in schema && schema.defaultFilters) {
|
|
246
|
+
// Legacy support
|
|
247
|
+
params.$filter = schema.defaultFilters;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Support new sort format
|
|
251
|
+
if (schema.sort) {
|
|
252
|
+
if (typeof schema.sort === 'string') {
|
|
253
|
+
// Legacy string format
|
|
254
|
+
params.$orderby = schema.sort;
|
|
255
|
+
} else if (Array.isArray(schema.sort)) {
|
|
256
|
+
// New array format
|
|
257
|
+
params.$orderby = schema.sort
|
|
258
|
+
.map(s => `${s.field} ${s.order}`)
|
|
259
|
+
.join(', ');
|
|
260
|
+
}
|
|
261
|
+
} else if ('defaultSort' in schema && schema.defaultSort) {
|
|
262
|
+
// Legacy support
|
|
263
|
+
params.$orderby = `${schema.defaultSort.field} ${schema.defaultSort.order}`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const result = await dataSource.find(objectName, params);
|
|
267
|
+
setData(result.data || []);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
setError(err as Error);
|
|
270
|
+
} finally {
|
|
271
|
+
setLoading(false);
|
|
272
|
+
}
|
|
273
|
+
}, [schema, dataSource, hasInlineData, dataConfig]);
|
|
274
|
+
|
|
275
|
+
useEffect(() => {
|
|
276
|
+
if (objectSchema || hasInlineData) {
|
|
277
|
+
fetchData();
|
|
278
|
+
}
|
|
279
|
+
}, [objectSchema, hasInlineData, fetchData]);
|
|
280
|
+
|
|
281
|
+
if (error) {
|
|
282
|
+
return (
|
|
283
|
+
<div className="p-4 border border-red-300 bg-red-50 rounded-md">
|
|
284
|
+
<h3 className="text-red-800 font-semibold">Error loading grid</h3>
|
|
285
|
+
<p className="text-red-600 text-sm mt-1">{error.message}</p>
|
|
286
|
+
</div>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (loading && data.length === 0) {
|
|
291
|
+
return (
|
|
292
|
+
<div className="p-8 text-center">
|
|
293
|
+
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
|
|
294
|
+
<p className="mt-2 text-sm text-gray-600">Loading grid...</p>
|
|
295
|
+
</div>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const columns = generateColumns();
|
|
300
|
+
const operations = 'operations' in schema ? schema.operations : undefined;
|
|
301
|
+
const hasActions = operations && (operations.update || operations.delete);
|
|
302
|
+
|
|
303
|
+
const columnsWithActions = hasActions ? [
|
|
304
|
+
...columns,
|
|
305
|
+
{
|
|
306
|
+
header: 'Actions',
|
|
307
|
+
accessorKey: '_actions',
|
|
308
|
+
cell: (_value: any, row: any) => (
|
|
309
|
+
<DropdownMenu>
|
|
310
|
+
<DropdownMenuTrigger asChild>
|
|
311
|
+
<Button variant="ghost" size="icon" className="h-8 w-8">
|
|
312
|
+
<MoreVertical className="h-4 w-4" />
|
|
313
|
+
<span className="sr-only">Open menu</span>
|
|
314
|
+
</Button>
|
|
315
|
+
</DropdownMenuTrigger>
|
|
316
|
+
<DropdownMenuContent align="end">
|
|
317
|
+
{operations?.update && onEdit && (
|
|
318
|
+
<DropdownMenuItem onClick={() => onEdit(row)}>
|
|
319
|
+
<Edit className="mr-2 h-4 w-4" />
|
|
320
|
+
Edit
|
|
321
|
+
</DropdownMenuItem>
|
|
322
|
+
)}
|
|
323
|
+
{operations?.delete && onDelete && (
|
|
324
|
+
<DropdownMenuItem variant="destructive" onClick={() => onDelete(row)}>
|
|
325
|
+
<Trash2 className="mr-2 h-4 w-4" />
|
|
326
|
+
Delete
|
|
327
|
+
</DropdownMenuItem>
|
|
328
|
+
)}
|
|
329
|
+
</DropdownMenuContent>
|
|
330
|
+
</DropdownMenu>
|
|
331
|
+
),
|
|
332
|
+
sortable: false,
|
|
333
|
+
},
|
|
334
|
+
] : columns;
|
|
335
|
+
|
|
336
|
+
// Determine selection mode (support both new and legacy formats)
|
|
337
|
+
let selectionMode: 'none' | 'single' | 'multiple' | boolean = false;
|
|
338
|
+
if (schema.selection?.type) {
|
|
339
|
+
selectionMode = schema.selection.type === 'none' ? false : schema.selection.type;
|
|
340
|
+
} else if (schema.selectable !== undefined) {
|
|
341
|
+
// Legacy support
|
|
342
|
+
selectionMode = schema.selectable;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Determine pagination settings (support both new and legacy formats)
|
|
346
|
+
const paginationEnabled = schema.pagination !== undefined
|
|
347
|
+
? true
|
|
348
|
+
: (schema.showPagination !== undefined ? schema.showPagination : true);
|
|
349
|
+
|
|
350
|
+
const pageSize = schema.pagination?.pageSize
|
|
351
|
+
|| schema.pageSize
|
|
352
|
+
|| 10;
|
|
353
|
+
|
|
354
|
+
// Determine search settings
|
|
355
|
+
const searchEnabled = schema.searchableFields !== undefined
|
|
356
|
+
? schema.searchableFields.length > 0
|
|
357
|
+
: (schema.showSearch !== undefined ? schema.showSearch : true);
|
|
358
|
+
|
|
359
|
+
const dataTableSchema: any = {
|
|
360
|
+
type: 'data-table',
|
|
361
|
+
caption: schema.label || schema.title,
|
|
362
|
+
columns: columnsWithActions,
|
|
363
|
+
data,
|
|
364
|
+
pagination: paginationEnabled,
|
|
365
|
+
pageSize: pageSize,
|
|
366
|
+
searchable: searchEnabled,
|
|
367
|
+
selectable: selectionMode,
|
|
368
|
+
sortable: true,
|
|
369
|
+
exportable: operations?.export,
|
|
370
|
+
rowActions: hasActions,
|
|
371
|
+
resizableColumns: schema.resizable ?? schema.resizableColumns ?? true,
|
|
372
|
+
className: schema.className,
|
|
373
|
+
onSelectionChange: onRowSelect,
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
return <SchemaRenderer schema={dataTableSchema} />;
|
|
377
|
+
};
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { ComponentRegistry } from '@object-ui/core';
|
|
11
|
+
import { ObjectGrid } from './ObjectGrid';
|
|
12
|
+
|
|
13
|
+
export { ObjectGrid };
|
|
14
|
+
export type { ObjectGridProps } from './ObjectGrid';
|
|
15
|
+
|
|
16
|
+
// Register object-grid component
|
|
17
|
+
const ObjectGridRenderer: React.FC<{ schema: any }> = ({ schema }) => {
|
|
18
|
+
return <ObjectGrid schema={schema} dataSource={null as any} />;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
ComponentRegistry.register('object-grid', ObjectGridRenderer);
|
|
22
|
+
ComponentRegistry.register('grid', ObjectGridRenderer); // Alias
|
package/tsconfig.json
ADDED
package/vite.config.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import dts from 'vite-plugin-dts';
|
|
4
|
+
import { resolve } from 'path';
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
react(),
|
|
9
|
+
dts({
|
|
10
|
+
insertTypesEntry: true,
|
|
11
|
+
include: ['src'],
|
|
12
|
+
}),
|
|
13
|
+
],
|
|
14
|
+
build: {
|
|
15
|
+
lib: {
|
|
16
|
+
entry: resolve(__dirname, 'src/index.tsx'),
|
|
17
|
+
name: 'ObjectUIPluginGrid',
|
|
18
|
+
fileName: 'index',
|
|
19
|
+
},
|
|
20
|
+
rollupOptions: {
|
|
21
|
+
external: [
|
|
22
|
+
'react',
|
|
23
|
+
'react-dom',
|
|
24
|
+
'@object-ui/components',
|
|
25
|
+
'@object-ui/core',
|
|
26
|
+
'@object-ui/fields',
|
|
27
|
+
'@object-ui/react',
|
|
28
|
+
'@object-ui/types',
|
|
29
|
+
'lucide-react'
|
|
30
|
+
],
|
|
31
|
+
output: {
|
|
32
|
+
globals: {
|
|
33
|
+
react: 'React',
|
|
34
|
+
'react-dom': 'ReactDOM',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|