@object-ui/plugin-view 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 +291 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +475 -0
- package/dist/index.umd.cjs +6 -0
- package/dist/plugin-view/src/ObjectView.d.ts +36 -0
- package/dist/plugin-view/src/index.d.ts +3 -0
- package/package.json +41 -0
- package/src/ObjectView.tsx +409 -0
- package/src/index.tsx +21 -0
- package/tsconfig.json +8 -0
- package/vite.config.ts +40 -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,291 @@
|
|
|
1
|
+
# @object-ui/plugin-view
|
|
2
|
+
|
|
3
|
+
Object View plugin for Object UI - Unified component for displaying and managing ObjectQL data with automatic form and grid generation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Automatic Views** - Generate views from ObjectQL schemas
|
|
8
|
+
- **Form Generation** - Auto-generate forms from object definitions
|
|
9
|
+
- **Grid Generation** - Auto-generate data grids
|
|
10
|
+
- **CRUD Operations** - Built-in create, read, update, delete
|
|
11
|
+
- **Field Mapping** - Automatic field type detection
|
|
12
|
+
- **Validation** - Schema-based validation
|
|
13
|
+
- **ObjectQL Integration** - Native ObjectStack support
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @object-ui/plugin-view
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Automatic Registration (Side-Effect Import)
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// In your app entry point (e.g., App.tsx or main.tsx)
|
|
27
|
+
import '@object-ui/plugin-view';
|
|
28
|
+
|
|
29
|
+
// Now you can use view types in your schemas
|
|
30
|
+
const schema = {
|
|
31
|
+
type: 'object-view',
|
|
32
|
+
object: 'users',
|
|
33
|
+
viewMode: 'grid'
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Manual Registration
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { viewComponents } from '@object-ui/plugin-view';
|
|
41
|
+
import { ComponentRegistry } from '@object-ui/core';
|
|
42
|
+
|
|
43
|
+
// Register view components
|
|
44
|
+
Object.entries(viewComponents).forEach(([type, component]) => {
|
|
45
|
+
ComponentRegistry.register(type, component);
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Schema API
|
|
50
|
+
|
|
51
|
+
### ObjectView
|
|
52
|
+
|
|
53
|
+
Unified view component for ObjectQL objects:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
{
|
|
57
|
+
type: 'object-view',
|
|
58
|
+
object: string, // ObjectQL object name
|
|
59
|
+
viewMode?: 'grid' | 'form' | 'detail',
|
|
60
|
+
fields?: string[], // Fields to display
|
|
61
|
+
dataSource?: DataSource,
|
|
62
|
+
onCreate?: (data) => void,
|
|
63
|
+
onUpdate?: (id, data) => void,
|
|
64
|
+
onDelete?: (id) => void,
|
|
65
|
+
className?: string
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Examples
|
|
70
|
+
|
|
71
|
+
### Grid View
|
|
72
|
+
|
|
73
|
+
Display objects in a data grid:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const schema = {
|
|
77
|
+
type: 'object-view',
|
|
78
|
+
object: 'users',
|
|
79
|
+
viewMode: 'grid',
|
|
80
|
+
fields: ['name', 'email', 'role', 'created_at'],
|
|
81
|
+
dataSource: myDataSource
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Form View
|
|
86
|
+
|
|
87
|
+
Create or edit objects with a form:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const schema = {
|
|
91
|
+
type: 'object-view',
|
|
92
|
+
object: 'users',
|
|
93
|
+
viewMode: 'form',
|
|
94
|
+
mode: 'create',
|
|
95
|
+
fields: ['name', 'email', 'role'],
|
|
96
|
+
onSubmit: (data) => {
|
|
97
|
+
console.log('Form submitted:', data);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Detail View
|
|
103
|
+
|
|
104
|
+
Display a single object's details:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const schema = {
|
|
108
|
+
type: 'object-view',
|
|
109
|
+
object: 'users',
|
|
110
|
+
viewMode: 'detail',
|
|
111
|
+
recordId: '123',
|
|
112
|
+
fields: ['name', 'email', 'role', 'bio', 'created_at']
|
|
113
|
+
};
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## CRUD Operations
|
|
117
|
+
|
|
118
|
+
### Create
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const schema = {
|
|
122
|
+
type: 'object-view',
|
|
123
|
+
object: 'products',
|
|
124
|
+
viewMode: 'form',
|
|
125
|
+
mode: 'create',
|
|
126
|
+
onCreate: async (data) => {
|
|
127
|
+
const newProduct = await dataSource.create('products', data);
|
|
128
|
+
console.log('Created:', newProduct);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Read/List
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const schema = {
|
|
137
|
+
type: 'object-view',
|
|
138
|
+
object: 'products',
|
|
139
|
+
viewMode: 'grid',
|
|
140
|
+
pagination: true,
|
|
141
|
+
searchable: true,
|
|
142
|
+
filters: {
|
|
143
|
+
category: 'electronics'
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Update
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const schema = {
|
|
152
|
+
type: 'object-view',
|
|
153
|
+
object: 'products',
|
|
154
|
+
viewMode: 'form',
|
|
155
|
+
mode: 'edit',
|
|
156
|
+
recordId: '123',
|
|
157
|
+
onUpdate: async (id, data) => {
|
|
158
|
+
await dataSource.update('products', id, data);
|
|
159
|
+
console.log('Updated product:', id);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Delete
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const schema = {
|
|
168
|
+
type: 'object-view',
|
|
169
|
+
object: 'products',
|
|
170
|
+
viewMode: 'grid',
|
|
171
|
+
enableDelete: true,
|
|
172
|
+
onDelete: async (id) => {
|
|
173
|
+
await dataSource.delete('products', id);
|
|
174
|
+
console.log('Deleted product:', id);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Integration with ObjectQL
|
|
180
|
+
|
|
181
|
+
The plugin works seamlessly with ObjectStack:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { createObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
185
|
+
|
|
186
|
+
const dataSource = createObjectStackAdapter({
|
|
187
|
+
baseUrl: 'https://api.example.com',
|
|
188
|
+
token: 'your-auth-token'
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const schema = {
|
|
192
|
+
type: 'object-view',
|
|
193
|
+
object: 'contacts',
|
|
194
|
+
viewMode: 'grid',
|
|
195
|
+
dataSource,
|
|
196
|
+
fields: ['first_name', 'last_name', 'email', 'company'],
|
|
197
|
+
searchable: true,
|
|
198
|
+
sortable: true,
|
|
199
|
+
pagination: {
|
|
200
|
+
pageSize: 25
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Field Configuration
|
|
206
|
+
|
|
207
|
+
Customize field display and behavior:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const schema = {
|
|
211
|
+
type: 'object-view',
|
|
212
|
+
object: 'users',
|
|
213
|
+
viewMode: 'form',
|
|
214
|
+
fieldConfig: {
|
|
215
|
+
name: {
|
|
216
|
+
label: 'Full Name',
|
|
217
|
+
required: true,
|
|
218
|
+
placeholder: 'Enter name'
|
|
219
|
+
},
|
|
220
|
+
email: {
|
|
221
|
+
label: 'Email Address',
|
|
222
|
+
type: 'email',
|
|
223
|
+
required: true,
|
|
224
|
+
validation: [
|
|
225
|
+
{ type: 'email', message: 'Invalid email format' }
|
|
226
|
+
]
|
|
227
|
+
},
|
|
228
|
+
role: {
|
|
229
|
+
label: 'User Role',
|
|
230
|
+
type: 'select',
|
|
231
|
+
options: [
|
|
232
|
+
{ label: 'Admin', value: 'admin' },
|
|
233
|
+
{ label: 'User', value: 'user' },
|
|
234
|
+
{ label: 'Guest', value: 'guest' }
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Advanced Features
|
|
242
|
+
|
|
243
|
+
### Nested Objects
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
const schema = {
|
|
247
|
+
type: 'object-view',
|
|
248
|
+
object: 'orders',
|
|
249
|
+
viewMode: 'detail',
|
|
250
|
+
fields: ['order_number', 'customer.name', 'items', 'total'],
|
|
251
|
+
nestedFields: {
|
|
252
|
+
items: {
|
|
253
|
+
type: 'object-grid',
|
|
254
|
+
object: 'order_items',
|
|
255
|
+
fields: ['product.name', 'quantity', 'price']
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Tabs View
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
const schema = {
|
|
265
|
+
type: 'object-view',
|
|
266
|
+
object: 'users',
|
|
267
|
+
viewMode: 'tabs',
|
|
268
|
+
tabs: [
|
|
269
|
+
{ label: 'Details', fields: ['name', 'email', 'bio'] },
|
|
270
|
+
{ label: 'Settings', fields: ['theme', 'notifications', 'timezone'] },
|
|
271
|
+
{ label: 'Activity', type: 'object-grid', object: 'user_activities' }
|
|
272
|
+
]
|
|
273
|
+
};
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## TypeScript Support
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import type { ObjectViewSchema } from '@object-ui/plugin-view';
|
|
280
|
+
|
|
281
|
+
const userView: ObjectViewSchema = {
|
|
282
|
+
type: 'object-view',
|
|
283
|
+
object: 'users',
|
|
284
|
+
viewMode: 'grid',
|
|
285
|
+
fields: ['name', 'email', 'role']
|
|
286
|
+
};
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## License
|
|
290
|
+
|
|
291
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import ne, { useState as E, useEffect as ae, useCallback as v } from "react";
|
|
2
|
+
import { ComponentRegistry as le } from "@object-ui/core";
|
|
3
|
+
import { ObjectGrid as se } from "@object-ui/plugin-grid";
|
|
4
|
+
import { ObjectForm as K } from "@object-ui/plugin-form";
|
|
5
|
+
import { Input as ie, Button as Z, Drawer as ce, DrawerContent as ue, DrawerHeader as fe, DrawerTitle as de, DrawerDescription as me, Dialog as be, DialogContent as pe, DialogHeader as ve, DialogTitle as xe, DialogDescription as je } from "@object-ui/components";
|
|
6
|
+
import { Search as we, Filter as _e, X as Re, RefreshCw as Ee, Plus as ge } from "lucide-react";
|
|
7
|
+
var z = { exports: {} }, C = {};
|
|
8
|
+
var ee;
|
|
9
|
+
function he() {
|
|
10
|
+
if (ee) return C;
|
|
11
|
+
ee = 1;
|
|
12
|
+
var r = /* @__PURE__ */ Symbol.for("react.transitional.element"), d = /* @__PURE__ */ Symbol.for("react.fragment");
|
|
13
|
+
function R(x, c, u) {
|
|
14
|
+
var f = null;
|
|
15
|
+
if (u !== void 0 && (f = "" + u), c.key !== void 0 && (f = "" + c.key), "key" in c) {
|
|
16
|
+
u = {};
|
|
17
|
+
for (var b in c)
|
|
18
|
+
b !== "key" && (u[b] = c[b]);
|
|
19
|
+
} else u = c;
|
|
20
|
+
return c = u.ref, {
|
|
21
|
+
$$typeof: r,
|
|
22
|
+
type: x,
|
|
23
|
+
key: f,
|
|
24
|
+
ref: c !== void 0 ? c : null,
|
|
25
|
+
props: u
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return C.Fragment = d, C.jsx = R, C.jsxs = R, C;
|
|
29
|
+
}
|
|
30
|
+
var A = {};
|
|
31
|
+
var re;
|
|
32
|
+
function Ne() {
|
|
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 === X ? null : e.displayName || e.name || null;
|
|
38
|
+
if (typeof e == "string") return e;
|
|
39
|
+
switch (e) {
|
|
40
|
+
case _:
|
|
41
|
+
return "Fragment";
|
|
42
|
+
case p:
|
|
43
|
+
return "Profiler";
|
|
44
|
+
case m:
|
|
45
|
+
return "StrictMode";
|
|
46
|
+
case U:
|
|
47
|
+
return "Suspense";
|
|
48
|
+
case q:
|
|
49
|
+
return "SuspenseList";
|
|
50
|
+
case G:
|
|
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 L:
|
|
58
|
+
return "Portal";
|
|
59
|
+
case B:
|
|
60
|
+
return e.displayName || "Context";
|
|
61
|
+
case W:
|
|
62
|
+
return (e._context.displayName || "Context") + ".Consumer";
|
|
63
|
+
case $:
|
|
64
|
+
var t = e.render;
|
|
65
|
+
return e = e.displayName, e || (e = t.displayName || t.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
|
|
66
|
+
case J:
|
|
67
|
+
return t = e.displayName || null, t !== null ? t : r(e.type) || "Memo";
|
|
68
|
+
case T:
|
|
69
|
+
t = e._payload, e = e._init;
|
|
70
|
+
try {
|
|
71
|
+
return r(e(t));
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function d(e) {
|
|
78
|
+
return "" + e;
|
|
79
|
+
}
|
|
80
|
+
function R(e) {
|
|
81
|
+
try {
|
|
82
|
+
d(e);
|
|
83
|
+
var t = !1;
|
|
84
|
+
} catch {
|
|
85
|
+
t = !0;
|
|
86
|
+
}
|
|
87
|
+
if (t) {
|
|
88
|
+
t = console;
|
|
89
|
+
var a = t.error, l = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
|
|
90
|
+
return a.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
|
+
l
|
|
94
|
+
), d(e);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function x(e) {
|
|
98
|
+
if (e === _) return "<>";
|
|
99
|
+
if (typeof e == "object" && e !== null && e.$$typeof === T)
|
|
100
|
+
return "<...>";
|
|
101
|
+
try {
|
|
102
|
+
var t = r(e);
|
|
103
|
+
return t ? "<" + t + ">" : "<...>";
|
|
104
|
+
} catch {
|
|
105
|
+
return "<...>";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function c() {
|
|
109
|
+
var e = y.A;
|
|
110
|
+
return e === null ? null : e.getOwner();
|
|
111
|
+
}
|
|
112
|
+
function u() {
|
|
113
|
+
return Error("react-stack-top-frame");
|
|
114
|
+
}
|
|
115
|
+
function f(e) {
|
|
116
|
+
if (S.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 b(e, t) {
|
|
123
|
+
function a() {
|
|
124
|
+
Y || (Y = !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
|
+
a.isReactWarning = !0, Object.defineProperty(e, "key", {
|
|
130
|
+
get: a,
|
|
131
|
+
configurable: !0
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function h() {
|
|
135
|
+
var e = r(this.type);
|
|
136
|
+
return M[e] || (M[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 N(e, t, a, l, V, H) {
|
|
141
|
+
var s = a.ref;
|
|
142
|
+
return e = {
|
|
143
|
+
$$typeof: D,
|
|
144
|
+
type: e,
|
|
145
|
+
key: t,
|
|
146
|
+
props: a,
|
|
147
|
+
_owner: l
|
|
148
|
+
}, (s !== void 0 ? s : null) !== null ? Object.defineProperty(e, "ref", {
|
|
149
|
+
enumerable: !1,
|
|
150
|
+
get: h
|
|
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: V
|
|
166
|
+
}), Object.defineProperty(e, "_debugTask", {
|
|
167
|
+
configurable: !1,
|
|
168
|
+
enumerable: !1,
|
|
169
|
+
writable: !0,
|
|
170
|
+
value: H
|
|
171
|
+
}), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
|
|
172
|
+
}
|
|
173
|
+
function j(e, t, a, l, V, H) {
|
|
174
|
+
var s = t.children;
|
|
175
|
+
if (s !== void 0)
|
|
176
|
+
if (l)
|
|
177
|
+
if (I(s)) {
|
|
178
|
+
for (l = 0; l < s.length; l++)
|
|
179
|
+
P(s[l]);
|
|
180
|
+
Object.freeze && Object.freeze(s);
|
|
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 P(s);
|
|
186
|
+
if (S.call(t, "key")) {
|
|
187
|
+
s = r(e);
|
|
188
|
+
var g = Object.keys(t).filter(function(oe) {
|
|
189
|
+
return oe !== "key";
|
|
190
|
+
});
|
|
191
|
+
l = 0 < g.length ? "{key: someKey, " + g.join(": ..., ") + ": ...}" : "{key: someKey}", O[s + l] || (g = 0 < g.length ? "{" + g.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
|
+
l,
|
|
199
|
+
s,
|
|
200
|
+
g,
|
|
201
|
+
s
|
|
202
|
+
), O[s + l] = !0);
|
|
203
|
+
}
|
|
204
|
+
if (s = null, a !== void 0 && (R(a), s = "" + a), f(t) && (R(t.key), s = "" + t.key), "key" in t) {
|
|
205
|
+
a = {};
|
|
206
|
+
for (var Q in t)
|
|
207
|
+
Q !== "key" && (a[Q] = t[Q]);
|
|
208
|
+
} else a = t;
|
|
209
|
+
return s && b(
|
|
210
|
+
a,
|
|
211
|
+
typeof e == "function" ? e.displayName || e.name || "Unknown" : e
|
|
212
|
+
), N(
|
|
213
|
+
e,
|
|
214
|
+
s,
|
|
215
|
+
a,
|
|
216
|
+
c(),
|
|
217
|
+
V,
|
|
218
|
+
H
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
function P(e) {
|
|
222
|
+
F(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === T && (e._payload.status === "fulfilled" ? F(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
|
|
223
|
+
}
|
|
224
|
+
function F(e) {
|
|
225
|
+
return typeof e == "object" && e !== null && e.$$typeof === D;
|
|
226
|
+
}
|
|
227
|
+
var w = ne, D = /* @__PURE__ */ Symbol.for("react.transitional.element"), L = /* @__PURE__ */ Symbol.for("react.portal"), _ = /* @__PURE__ */ Symbol.for("react.fragment"), m = /* @__PURE__ */ Symbol.for("react.strict_mode"), p = /* @__PURE__ */ Symbol.for("react.profiler"), W = /* @__PURE__ */ Symbol.for("react.consumer"), B = /* @__PURE__ */ Symbol.for("react.context"), $ = /* @__PURE__ */ Symbol.for("react.forward_ref"), U = /* @__PURE__ */ Symbol.for("react.suspense"), q = /* @__PURE__ */ Symbol.for("react.suspense_list"), J = /* @__PURE__ */ Symbol.for("react.memo"), T = /* @__PURE__ */ Symbol.for("react.lazy"), G = /* @__PURE__ */ Symbol.for("react.activity"), X = /* @__PURE__ */ Symbol.for("react.client.reference"), y = w.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, S = Object.prototype.hasOwnProperty, I = Array.isArray, k = console.createTask ? console.createTask : function() {
|
|
228
|
+
return null;
|
|
229
|
+
};
|
|
230
|
+
w = {
|
|
231
|
+
react_stack_bottom_frame: function(e) {
|
|
232
|
+
return e();
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var Y, M = {}, n = w.react_stack_bottom_frame.bind(
|
|
236
|
+
w,
|
|
237
|
+
u
|
|
238
|
+
)(), i = k(x(u)), O = {};
|
|
239
|
+
A.Fragment = _, A.jsx = function(e, t, a) {
|
|
240
|
+
var l = 1e4 > y.recentlyCreatedOwnerStacks++;
|
|
241
|
+
return j(
|
|
242
|
+
e,
|
|
243
|
+
t,
|
|
244
|
+
a,
|
|
245
|
+
!1,
|
|
246
|
+
l ? Error("react-stack-top-frame") : n,
|
|
247
|
+
l ? k(x(e)) : i
|
|
248
|
+
);
|
|
249
|
+
}, A.jsxs = function(e, t, a) {
|
|
250
|
+
var l = 1e4 > y.recentlyCreatedOwnerStacks++;
|
|
251
|
+
return j(
|
|
252
|
+
e,
|
|
253
|
+
t,
|
|
254
|
+
a,
|
|
255
|
+
!0,
|
|
256
|
+
l ? Error("react-stack-top-frame") : n,
|
|
257
|
+
l ? k(x(e)) : i
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
})()), A;
|
|
261
|
+
}
|
|
262
|
+
var te;
|
|
263
|
+
function Te() {
|
|
264
|
+
return te || (te = 1, process.env.NODE_ENV === "production" ? z.exports = he() : z.exports = Ne()), z.exports;
|
|
265
|
+
}
|
|
266
|
+
var o = Te();
|
|
267
|
+
const ye = ({
|
|
268
|
+
schema: r,
|
|
269
|
+
dataSource: d,
|
|
270
|
+
className: R
|
|
271
|
+
}) => {
|
|
272
|
+
const [x, c] = E(null), [u, f] = E(!1), [b, h] = E("create"), [N, j] = E(null), [P, F] = E(""), [w, D] = E(!1), [L, _] = E(0);
|
|
273
|
+
ae(() => {
|
|
274
|
+
const n = async () => {
|
|
275
|
+
try {
|
|
276
|
+
const i = await d.getObjectSchema(r.objectName);
|
|
277
|
+
c(i);
|
|
278
|
+
} catch (i) {
|
|
279
|
+
console.error("Failed to fetch object schema:", i);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
r.objectName && d && n();
|
|
283
|
+
}, [r.objectName, d]);
|
|
284
|
+
const m = r.layout || "drawer", p = r.operations || r.table?.operations || {
|
|
285
|
+
create: !0,
|
|
286
|
+
read: !0,
|
|
287
|
+
update: !0,
|
|
288
|
+
delete: !0
|
|
289
|
+
}, W = v(() => {
|
|
290
|
+
m === "page" && r.onNavigate ? r.onNavigate("new", "edit") : (h("create"), j(null), f(!0));
|
|
291
|
+
}, [m, r]), B = v((n) => {
|
|
292
|
+
if (m === "page" && r.onNavigate) {
|
|
293
|
+
const i = n._id || n.id;
|
|
294
|
+
r.onNavigate(i, "edit");
|
|
295
|
+
} else
|
|
296
|
+
h("edit"), j(n), f(!0);
|
|
297
|
+
}, [m, r]), $ = v((n) => {
|
|
298
|
+
if (m === "page" && r.onNavigate) {
|
|
299
|
+
const i = n._id || n.id;
|
|
300
|
+
r.onNavigate(i, "view");
|
|
301
|
+
} else
|
|
302
|
+
h("view"), j(n), f(!0);
|
|
303
|
+
}, [m, r]), U = v((n) => {
|
|
304
|
+
p.read !== !1 && $(n);
|
|
305
|
+
}, [p.read, $]), q = v((n) => {
|
|
306
|
+
_((i) => i + 1);
|
|
307
|
+
}, []), J = v((n) => {
|
|
308
|
+
_((i) => i + 1);
|
|
309
|
+
}, []), T = v(() => {
|
|
310
|
+
f(!1), j(null), _((n) => n + 1);
|
|
311
|
+
}, []), G = v(() => {
|
|
312
|
+
f(!1), j(null);
|
|
313
|
+
}, []), X = v(() => {
|
|
314
|
+
_((n) => n + 1);
|
|
315
|
+
}, []), y = {
|
|
316
|
+
type: "object-grid",
|
|
317
|
+
objectName: r.objectName,
|
|
318
|
+
title: r.table?.title,
|
|
319
|
+
description: r.table?.description,
|
|
320
|
+
fields: r.table?.fields,
|
|
321
|
+
columns: r.table?.columns,
|
|
322
|
+
operations: {
|
|
323
|
+
...p,
|
|
324
|
+
create: !1
|
|
325
|
+
// Create is handled by the view's create button
|
|
326
|
+
},
|
|
327
|
+
defaultFilters: r.table?.defaultFilters,
|
|
328
|
+
defaultSort: r.table?.defaultSort,
|
|
329
|
+
pageSize: r.table?.pageSize,
|
|
330
|
+
selectable: r.table?.selectable,
|
|
331
|
+
className: r.table?.className
|
|
332
|
+
}, S = () => {
|
|
333
|
+
const n = N ? N._id || N.id : void 0;
|
|
334
|
+
return {
|
|
335
|
+
type: "object-form",
|
|
336
|
+
objectName: r.objectName,
|
|
337
|
+
mode: b,
|
|
338
|
+
recordId: n,
|
|
339
|
+
title: r.form?.title,
|
|
340
|
+
description: r.form?.description,
|
|
341
|
+
fields: r.form?.fields,
|
|
342
|
+
customFields: r.form?.customFields,
|
|
343
|
+
groups: r.form?.groups,
|
|
344
|
+
layout: r.form?.layout,
|
|
345
|
+
columns: r.form?.columns,
|
|
346
|
+
showSubmit: r.form?.showSubmit,
|
|
347
|
+
submitText: r.form?.submitText,
|
|
348
|
+
showCancel: r.form?.showCancel,
|
|
349
|
+
cancelText: r.form?.cancelText,
|
|
350
|
+
showReset: r.form?.showReset,
|
|
351
|
+
initialValues: r.form?.initialValues,
|
|
352
|
+
readOnly: r.form?.readOnly || b === "view",
|
|
353
|
+
className: r.form?.className,
|
|
354
|
+
onSuccess: T,
|
|
355
|
+
onCancel: G
|
|
356
|
+
};
|
|
357
|
+
}, I = () => {
|
|
358
|
+
if (r.form?.title) return r.form.title;
|
|
359
|
+
const n = x?.label || r.objectName;
|
|
360
|
+
switch (b) {
|
|
361
|
+
case "create":
|
|
362
|
+
return `Create ${n}`;
|
|
363
|
+
case "edit":
|
|
364
|
+
return `Edit ${n}`;
|
|
365
|
+
case "view":
|
|
366
|
+
return `View ${n}`;
|
|
367
|
+
default:
|
|
368
|
+
return n;
|
|
369
|
+
}
|
|
370
|
+
}, k = () => /* @__PURE__ */ o.jsx(ce, { open: u, onOpenChange: f, direction: "right", children: /* @__PURE__ */ o.jsxs(ue, { className: "w-full sm:max-w-2xl", children: [
|
|
371
|
+
/* @__PURE__ */ o.jsxs(fe, { children: [
|
|
372
|
+
/* @__PURE__ */ o.jsx(de, { children: I() }),
|
|
373
|
+
r.form?.description && /* @__PURE__ */ o.jsx(me, { children: r.form.description })
|
|
374
|
+
] }),
|
|
375
|
+
/* @__PURE__ */ o.jsx("div", { className: "flex-1 overflow-y-auto px-4 pb-4", children: /* @__PURE__ */ o.jsx(
|
|
376
|
+
K,
|
|
377
|
+
{
|
|
378
|
+
schema: S(),
|
|
379
|
+
dataSource: d
|
|
380
|
+
}
|
|
381
|
+
) })
|
|
382
|
+
] }) }), Y = () => /* @__PURE__ */ o.jsx(be, { open: u, onOpenChange: f, children: /* @__PURE__ */ o.jsxs(pe, { className: "max-w-2xl max-h-[90vh] overflow-y-auto", children: [
|
|
383
|
+
/* @__PURE__ */ o.jsxs(ve, { children: [
|
|
384
|
+
/* @__PURE__ */ o.jsx(xe, { children: I() }),
|
|
385
|
+
r.form?.description && /* @__PURE__ */ o.jsx(je, { children: r.form.description })
|
|
386
|
+
] }),
|
|
387
|
+
/* @__PURE__ */ o.jsx(
|
|
388
|
+
K,
|
|
389
|
+
{
|
|
390
|
+
schema: S(),
|
|
391
|
+
dataSource: d
|
|
392
|
+
}
|
|
393
|
+
)
|
|
394
|
+
] }) }), M = () => {
|
|
395
|
+
const n = r.showSearch !== !1, i = r.showFilters !== !1, O = r.showCreate !== !1 && p.create !== !1, e = r.showRefresh !== !1;
|
|
396
|
+
return !n && !i && !O && !e ? null : /* @__PURE__ */ o.jsxs("div", { className: "flex flex-col gap-4 mb-4", children: [
|
|
397
|
+
/* @__PURE__ */ o.jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
398
|
+
/* @__PURE__ */ o.jsx("div", { className: "flex-1 max-w-md", children: n && /* @__PURE__ */ o.jsxs("div", { className: "relative", children: [
|
|
399
|
+
/* @__PURE__ */ o.jsx(we, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
|
|
400
|
+
/* @__PURE__ */ o.jsx(
|
|
401
|
+
ie,
|
|
402
|
+
{
|
|
403
|
+
type: "search",
|
|
404
|
+
placeholder: `Search ${x?.label || r.objectName}...`,
|
|
405
|
+
value: P,
|
|
406
|
+
onChange: (t) => F(t.target.value),
|
|
407
|
+
className: "pl-9"
|
|
408
|
+
}
|
|
409
|
+
)
|
|
410
|
+
] }) }),
|
|
411
|
+
/* @__PURE__ */ o.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
412
|
+
i && /* @__PURE__ */ o.jsxs(
|
|
413
|
+
Z,
|
|
414
|
+
{
|
|
415
|
+
variant: "outline",
|
|
416
|
+
size: "sm",
|
|
417
|
+
onClick: () => D(!w),
|
|
418
|
+
children: [
|
|
419
|
+
/* @__PURE__ */ o.jsx(_e, { className: "h-4 w-4" }),
|
|
420
|
+
"Filters",
|
|
421
|
+
w && /* @__PURE__ */ o.jsx(Re, { className: "h-3 w-3 ml-1" })
|
|
422
|
+
]
|
|
423
|
+
}
|
|
424
|
+
),
|
|
425
|
+
e && /* @__PURE__ */ o.jsx(
|
|
426
|
+
Z,
|
|
427
|
+
{
|
|
428
|
+
variant: "outline",
|
|
429
|
+
size: "sm",
|
|
430
|
+
onClick: X,
|
|
431
|
+
children: /* @__PURE__ */ o.jsx(Ee, { className: "h-4 w-4" })
|
|
432
|
+
}
|
|
433
|
+
),
|
|
434
|
+
O && /* @__PURE__ */ o.jsxs(
|
|
435
|
+
Z,
|
|
436
|
+
{
|
|
437
|
+
size: "sm",
|
|
438
|
+
onClick: W,
|
|
439
|
+
children: [
|
|
440
|
+
/* @__PURE__ */ o.jsx(ge, { className: "h-4 w-4" }),
|
|
441
|
+
"Create"
|
|
442
|
+
]
|
|
443
|
+
}
|
|
444
|
+
)
|
|
445
|
+
] })
|
|
446
|
+
] }),
|
|
447
|
+
w && /* @__PURE__ */ o.jsx("div", { className: "p-4 border rounded-md bg-muted/50", children: /* @__PURE__ */ o.jsx("p", { className: "text-sm text-muted-foreground", children: "Filter functionality will be integrated with FilterBuilder component" }) })
|
|
448
|
+
] });
|
|
449
|
+
};
|
|
450
|
+
return /* @__PURE__ */ o.jsxs("div", { className: R, children: [
|
|
451
|
+
(r.title || r.description) && /* @__PURE__ */ o.jsxs("div", { className: "mb-6", children: [
|
|
452
|
+
r.title && /* @__PURE__ */ o.jsx("h2", { className: "text-2xl font-bold tracking-tight", children: r.title }),
|
|
453
|
+
r.description && /* @__PURE__ */ o.jsx("p", { className: "text-muted-foreground mt-1", children: r.description })
|
|
454
|
+
] }),
|
|
455
|
+
M(),
|
|
456
|
+
/* @__PURE__ */ o.jsx(
|
|
457
|
+
se,
|
|
458
|
+
{
|
|
459
|
+
schema: y,
|
|
460
|
+
dataSource: d,
|
|
461
|
+
onRowClick: U,
|
|
462
|
+
onEdit: p.update !== !1 ? B : void 0,
|
|
463
|
+
onDelete: p.delete !== !1 ? q : void 0,
|
|
464
|
+
onBulkDelete: p.delete !== !1 ? J : void 0
|
|
465
|
+
},
|
|
466
|
+
L
|
|
467
|
+
),
|
|
468
|
+
m === "drawer" && k(),
|
|
469
|
+
m === "modal" && Y()
|
|
470
|
+
] });
|
|
471
|
+
}, Se = ({ schema: r }) => /* @__PURE__ */ o.jsx(ye, { schema: r, dataSource: null });
|
|
472
|
+
le.register("object-view", Se);
|
|
473
|
+
export {
|
|
474
|
+
ye as ObjectView
|
|
475
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
(function(m,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("react"),require("@object-ui/core"),require("@object-ui/plugin-grid"),require("@object-ui/plugin-form"),require("@object-ui/components"),require("lucide-react")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","@object-ui/plugin-grid","@object-ui/plugin-form","@object-ui/components","lucide-react"],a):(m=typeof globalThis<"u"?globalThis:m||self,a(m.ObjectUIPluginView={},m.React,m.core,m.pluginGrid,m.pluginForm,m.components,m.lucideReact))})(this,(function(m,a,ae,le,ee,u,T){"use strict";var D={exports:{}},N={};var te;function se(){if(te)return N;te=1;var t=Symbol.for("react.transitional.element"),p=Symbol.for("react.fragment");function g(w,f,d){var b=null;if(d!==void 0&&(b=""+d),f.key!==void 0&&(b=""+f.key),"key"in f){d={};for(var v in f)v!=="key"&&(d[v]=f[v])}else d=f;return f=d.ref,{$$typeof:t,type:w,key:b,ref:f!==void 0?f:null,props:d}}return N.Fragment=p,N.jsx=g,N.jsxs=g,N}var S={};var re;function ie(){return re||(re=1,process.env.NODE_ENV!=="production"&&(function(){function t(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===K?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case R:return"Fragment";case x:return"Profiler";case j:return"StrictMode";case X:return"Suspense";case H:return"SuspenseList";case Z: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 W:return"Portal";case J:return e.displayName||"Context";case G:return(e._context.displayName||"Context")+".Consumer";case M:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Q:return r=e.displayName||null,r!==null?r:t(e.type)||"Memo";case O:r=e._payload,e=e._init;try{return t(e(r))}catch{}}return null}function p(e){return""+e}function g(e){try{p(e);var r=!1}catch{r=!0}if(r){r=console;var l=r.error,s=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return l.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",s),p(e)}}function w(e){if(e===R)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===O)return"<...>";try{var r=t(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function f(){var e=C.A;return e===null?null:e.getOwner()}function d(){return Error("react-stack-top-frame")}function b(e){if(P.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function v(e,r){function l(){B||(B=!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)",r))}l.isReactWarning=!0,Object.defineProperty(e,"key",{get:l,configurable:!0})}function k(){var e=t(this.type);return L[e]||(L[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 y(e,r,l,s,U,q){var i=l.ref;return e={$$typeof:V,type:e,key:r,props:l,_owner:s},(i!==void 0?i:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:k}):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:U}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:q}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function _(e,r,l,s,U,q){var i=r.children;if(i!==void 0)if(s)if(z(i)){for(s=0;s<i.length;s++)I(i[s]);Object.freeze&&Object.freeze(i)}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 I(i);if(P.call(r,"key")){i=t(e);var h=Object.keys(r).filter(function(fe){return fe!=="key"});s=0<h.length?"{key: someKey, "+h.join(": ..., ")+": ...}":"{key: someKey}",F[i+s]||(h=0<h.length?"{"+h.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} />`,s,i,h,i),F[i+s]=!0)}if(i=null,l!==void 0&&(g(l),i=""+l),b(r)&&(g(r.key),i=""+r.key),"key"in r){l={};for(var $ in r)$!=="key"&&(l[$]=r[$])}else l=r;return i&&v(l,typeof e=="function"?e.displayName||e.name||"Unknown":e),y(e,i,l,f(),U,q)}function I(e){Y(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===O&&(e._payload.status==="fulfilled"?Y(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function Y(e){return typeof e=="object"&&e!==null&&e.$$typeof===V}var E=a,V=Symbol.for("react.transitional.element"),W=Symbol.for("react.portal"),R=Symbol.for("react.fragment"),j=Symbol.for("react.strict_mode"),x=Symbol.for("react.profiler"),G=Symbol.for("react.consumer"),J=Symbol.for("react.context"),M=Symbol.for("react.forward_ref"),X=Symbol.for("react.suspense"),H=Symbol.for("react.suspense_list"),Q=Symbol.for("react.memo"),O=Symbol.for("react.lazy"),Z=Symbol.for("react.activity"),K=Symbol.for("react.client.reference"),C=E.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,P=Object.prototype.hasOwnProperty,z=Array.isArray,A=console.createTask?console.createTask:function(){return null};E={react_stack_bottom_frame:function(e){return e()}};var B,L={},n=E.react_stack_bottom_frame.bind(E,d)(),c=A(w(d)),F={};S.Fragment=R,S.jsx=function(e,r,l){var s=1e4>C.recentlyCreatedOwnerStacks++;return _(e,r,l,!1,s?Error("react-stack-top-frame"):n,s?A(w(e)):c)},S.jsxs=function(e,r,l){var s=1e4>C.recentlyCreatedOwnerStacks++;return _(e,r,l,!0,s?Error("react-stack-top-frame"):n,s?A(w(e)):c)}})()),S}var oe;function ce(){return oe||(oe=1,process.env.NODE_ENV==="production"?D.exports=se():D.exports=ie()),D.exports}var o=ce();const ne=({schema:t,dataSource:p,className:g})=>{const[w,f]=a.useState(null),[d,b]=a.useState(!1),[v,k]=a.useState("create"),[y,_]=a.useState(null),[I,Y]=a.useState(""),[E,V]=a.useState(!1),[W,R]=a.useState(0);a.useEffect(()=>{const n=async()=>{try{const c=await p.getObjectSchema(t.objectName);f(c)}catch(c){console.error("Failed to fetch object schema:",c)}};t.objectName&&p&&n()},[t.objectName,p]);const j=t.layout||"drawer",x=t.operations||t.table?.operations||{create:!0,read:!0,update:!0,delete:!0},G=a.useCallback(()=>{j==="page"&&t.onNavigate?t.onNavigate("new","edit"):(k("create"),_(null),b(!0))},[j,t]),J=a.useCallback(n=>{if(j==="page"&&t.onNavigate){const c=n._id||n.id;t.onNavigate(c,"edit")}else k("edit"),_(n),b(!0)},[j,t]),M=a.useCallback(n=>{if(j==="page"&&t.onNavigate){const c=n._id||n.id;t.onNavigate(c,"view")}else k("view"),_(n),b(!0)},[j,t]),X=a.useCallback(n=>{x.read!==!1&&M(n)},[x.read,M]),H=a.useCallback(n=>{R(c=>c+1)},[]),Q=a.useCallback(n=>{R(c=>c+1)},[]),O=a.useCallback(()=>{b(!1),_(null),R(n=>n+1)},[]),Z=a.useCallback(()=>{b(!1),_(null)},[]),K=a.useCallback(()=>{R(n=>n+1)},[]),C={type:"object-grid",objectName:t.objectName,title:t.table?.title,description:t.table?.description,fields:t.table?.fields,columns:t.table?.columns,operations:{...x,create:!1},defaultFilters:t.table?.defaultFilters,defaultSort:t.table?.defaultSort,pageSize:t.table?.pageSize,selectable:t.table?.selectable,className:t.table?.className},P=()=>{const n=y?y._id||y.id:void 0;return{type:"object-form",objectName:t.objectName,mode:v,recordId:n,title:t.form?.title,description:t.form?.description,fields:t.form?.fields,customFields:t.form?.customFields,groups:t.form?.groups,layout:t.form?.layout,columns:t.form?.columns,showSubmit:t.form?.showSubmit,submitText:t.form?.submitText,showCancel:t.form?.showCancel,cancelText:t.form?.cancelText,showReset:t.form?.showReset,initialValues:t.form?.initialValues,readOnly:t.form?.readOnly||v==="view",className:t.form?.className,onSuccess:O,onCancel:Z}},z=()=>{if(t.form?.title)return t.form.title;const n=w?.label||t.objectName;switch(v){case"create":return`Create ${n}`;case"edit":return`Edit ${n}`;case"view":return`View ${n}`;default:return n}},A=()=>o.jsx(u.Drawer,{open:d,onOpenChange:b,direction:"right",children:o.jsxs(u.DrawerContent,{className:"w-full sm:max-w-2xl",children:[o.jsxs(u.DrawerHeader,{children:[o.jsx(u.DrawerTitle,{children:z()}),t.form?.description&&o.jsx(u.DrawerDescription,{children:t.form.description})]}),o.jsx("div",{className:"flex-1 overflow-y-auto px-4 pb-4",children:o.jsx(ee.ObjectForm,{schema:P(),dataSource:p})})]})}),B=()=>o.jsx(u.Dialog,{open:d,onOpenChange:b,children:o.jsxs(u.DialogContent,{className:"max-w-2xl max-h-[90vh] overflow-y-auto",children:[o.jsxs(u.DialogHeader,{children:[o.jsx(u.DialogTitle,{children:z()}),t.form?.description&&o.jsx(u.DialogDescription,{children:t.form.description})]}),o.jsx(ee.ObjectForm,{schema:P(),dataSource:p})]})}),L=()=>{const n=t.showSearch!==!1,c=t.showFilters!==!1,F=t.showCreate!==!1&&x.create!==!1,e=t.showRefresh!==!1;return!n&&!c&&!F&&!e?null:o.jsxs("div",{className:"flex flex-col gap-4 mb-4",children:[o.jsxs("div",{className:"flex items-center justify-between gap-4",children:[o.jsx("div",{className:"flex-1 max-w-md",children:n&&o.jsxs("div",{className:"relative",children:[o.jsx(T.Search,{className:"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground"}),o.jsx(u.Input,{type:"search",placeholder:`Search ${w?.label||t.objectName}...`,value:I,onChange:r=>Y(r.target.value),className:"pl-9"})]})}),o.jsxs("div",{className:"flex items-center gap-2",children:[c&&o.jsxs(u.Button,{variant:"outline",size:"sm",onClick:()=>V(!E),children:[o.jsx(T.Filter,{className:"h-4 w-4"}),"Filters",E&&o.jsx(T.X,{className:"h-3 w-3 ml-1"})]}),e&&o.jsx(u.Button,{variant:"outline",size:"sm",onClick:K,children:o.jsx(T.RefreshCw,{className:"h-4 w-4"})}),F&&o.jsxs(u.Button,{size:"sm",onClick:G,children:[o.jsx(T.Plus,{className:"h-4 w-4"}),"Create"]})]})]}),E&&o.jsx("div",{className:"p-4 border rounded-md bg-muted/50",children:o.jsx("p",{className:"text-sm text-muted-foreground",children:"Filter functionality will be integrated with FilterBuilder component"})})]})};return o.jsxs("div",{className:g,children:[(t.title||t.description)&&o.jsxs("div",{className:"mb-6",children:[t.title&&o.jsx("h2",{className:"text-2xl font-bold tracking-tight",children:t.title}),t.description&&o.jsx("p",{className:"text-muted-foreground mt-1",children:t.description})]}),L(),o.jsx(le.ObjectGrid,{schema:C,dataSource:p,onRowClick:X,onEdit:x.update!==!1?J:void 0,onDelete:x.delete!==!1?H:void 0,onBulkDelete:x.delete!==!1?Q:void 0},W),j==="drawer"&&A(),j==="modal"&&B()]})},ue=({schema:t})=>o.jsx(ne,{schema:t,dataSource:null});ae.ComponentRegistry.register("object-view",ue),m.ObjectView=ne,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { ObjectViewSchema, DataSource } from '../../types/src';
|
|
3
|
+
export interface ObjectViewProps {
|
|
4
|
+
/**
|
|
5
|
+
* The schema configuration for the view
|
|
6
|
+
*/
|
|
7
|
+
schema: ObjectViewSchema;
|
|
8
|
+
/**
|
|
9
|
+
* Data source (ObjectQL or ObjectStack adapter)
|
|
10
|
+
*/
|
|
11
|
+
dataSource: DataSource;
|
|
12
|
+
/**
|
|
13
|
+
* Additional CSS class
|
|
14
|
+
*/
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* ObjectView Component
|
|
19
|
+
*
|
|
20
|
+
* Renders a complete object management interface with table and forms.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* <ObjectView
|
|
25
|
+
* schema={{
|
|
26
|
+
* type: 'object-view',
|
|
27
|
+
* objectName: 'users',
|
|
28
|
+
* layout: 'drawer',
|
|
29
|
+
* showSearch: true,
|
|
30
|
+
* showFilters: true
|
|
31
|
+
* }}
|
|
32
|
+
* dataSource={dataSource}
|
|
33
|
+
* />
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare const ObjectView: React.FC<ObjectViewProps>;
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@object-ui/plugin-view",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Object View 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/plugin-form": "0.3.0",
|
|
21
|
+
"@object-ui/core": "0.3.0",
|
|
22
|
+
"@object-ui/plugin-grid": "0.3.0",
|
|
23
|
+
"@object-ui/react": "0.3.0",
|
|
24
|
+
"@object-ui/types": "0.3.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
28
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vite": "^7.3.1",
|
|
34
|
+
"vite-plugin-dts": "^4.5.4"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "vite build",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"lint": "eslint ."
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,409 @@
|
|
|
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
|
+
* ObjectView Component
|
|
11
|
+
*
|
|
12
|
+
* A complete object management interface that combines ObjectGrid and ObjectForm.
|
|
13
|
+
* Provides list view with integrated search, filters, and create/edit operations.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React, { useEffect, useState, useCallback } from 'react';
|
|
17
|
+
import type { ObjectViewSchema, ObjectGridSchema, ObjectFormSchema, DataSource } from '@object-ui/types';
|
|
18
|
+
import { ObjectGrid } from '@object-ui/plugin-grid';
|
|
19
|
+
import { ObjectForm } from '@object-ui/plugin-form';
|
|
20
|
+
import {
|
|
21
|
+
Dialog,
|
|
22
|
+
DialogContent,
|
|
23
|
+
DialogHeader,
|
|
24
|
+
DialogTitle,
|
|
25
|
+
DialogDescription,
|
|
26
|
+
Drawer,
|
|
27
|
+
DrawerContent,
|
|
28
|
+
DrawerHeader,
|
|
29
|
+
DrawerTitle,
|
|
30
|
+
DrawerDescription,
|
|
31
|
+
Button,
|
|
32
|
+
Input,
|
|
33
|
+
} from '@object-ui/components';
|
|
34
|
+
import { Plus, Search, RefreshCw, Filter, X } from 'lucide-react';
|
|
35
|
+
|
|
36
|
+
export interface ObjectViewProps {
|
|
37
|
+
/**
|
|
38
|
+
* The schema configuration for the view
|
|
39
|
+
*/
|
|
40
|
+
schema: ObjectViewSchema;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Data source (ObjectQL or ObjectStack adapter)
|
|
44
|
+
*/
|
|
45
|
+
dataSource: DataSource;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Additional CSS class
|
|
49
|
+
*/
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type FormMode = 'create' | 'edit' | 'view';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* ObjectView Component
|
|
57
|
+
*
|
|
58
|
+
* Renders a complete object management interface with table and forms.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```tsx
|
|
62
|
+
* <ObjectView
|
|
63
|
+
* schema={{
|
|
64
|
+
* type: 'object-view',
|
|
65
|
+
* objectName: 'users',
|
|
66
|
+
* layout: 'drawer',
|
|
67
|
+
* showSearch: true,
|
|
68
|
+
* showFilters: true
|
|
69
|
+
* }}
|
|
70
|
+
* dataSource={dataSource}
|
|
71
|
+
* />
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
75
|
+
schema,
|
|
76
|
+
dataSource,
|
|
77
|
+
className,
|
|
78
|
+
}) => {
|
|
79
|
+
const [objectSchema, setObjectSchema] = useState<Record<string, unknown> | null>(null);
|
|
80
|
+
const [isFormOpen, setIsFormOpen] = useState(false);
|
|
81
|
+
const [formMode, setFormMode] = useState<FormMode>('create');
|
|
82
|
+
const [selectedRecord, setSelectedRecord] = useState<Record<string, unknown> | null>(null);
|
|
83
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
84
|
+
const [showFilters, setShowFilters] = useState(false);
|
|
85
|
+
const [refreshKey, setRefreshKey] = useState(0);
|
|
86
|
+
|
|
87
|
+
// Fetch object schema from ObjectQL/ObjectStack
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const fetchObjectSchema = async () => {
|
|
90
|
+
try {
|
|
91
|
+
const schemaData = await dataSource.getObjectSchema(schema.objectName);
|
|
92
|
+
setObjectSchema(schemaData);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error('Failed to fetch object schema:', err);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (schema.objectName && dataSource) {
|
|
99
|
+
fetchObjectSchema();
|
|
100
|
+
}
|
|
101
|
+
}, [schema.objectName, dataSource]);
|
|
102
|
+
|
|
103
|
+
// Determine layout mode
|
|
104
|
+
const layout = schema.layout || 'drawer';
|
|
105
|
+
|
|
106
|
+
// Determine enabled operations
|
|
107
|
+
const operations = schema.operations || schema.table?.operations || {
|
|
108
|
+
create: true,
|
|
109
|
+
read: true,
|
|
110
|
+
update: true,
|
|
111
|
+
delete: true,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Handle create action
|
|
115
|
+
const handleCreate = useCallback(() => {
|
|
116
|
+
if (layout === 'page' && schema.onNavigate) {
|
|
117
|
+
schema.onNavigate('new', 'edit');
|
|
118
|
+
} else {
|
|
119
|
+
setFormMode('create');
|
|
120
|
+
setSelectedRecord(null);
|
|
121
|
+
setIsFormOpen(true);
|
|
122
|
+
}
|
|
123
|
+
}, [layout, schema]);
|
|
124
|
+
|
|
125
|
+
// Handle edit action
|
|
126
|
+
const handleEdit = useCallback((record: Record<string, unknown>) => {
|
|
127
|
+
if (layout === 'page' && schema.onNavigate) {
|
|
128
|
+
const recordId = record._id || record.id;
|
|
129
|
+
schema.onNavigate(recordId as string | number, 'edit');
|
|
130
|
+
} else {
|
|
131
|
+
setFormMode('edit');
|
|
132
|
+
setSelectedRecord(record);
|
|
133
|
+
setIsFormOpen(true);
|
|
134
|
+
}
|
|
135
|
+
}, [layout, schema]);
|
|
136
|
+
|
|
137
|
+
// Handle view action
|
|
138
|
+
const handleView = useCallback((record: Record<string, unknown>) => {
|
|
139
|
+
if (layout === 'page' && schema.onNavigate) {
|
|
140
|
+
const recordId = record._id || record.id;
|
|
141
|
+
schema.onNavigate(recordId as string | number, 'view');
|
|
142
|
+
} else {
|
|
143
|
+
setFormMode('view');
|
|
144
|
+
setSelectedRecord(record);
|
|
145
|
+
setIsFormOpen(true);
|
|
146
|
+
}
|
|
147
|
+
}, [layout, schema]);
|
|
148
|
+
|
|
149
|
+
// Handle row click
|
|
150
|
+
const handleRowClick = useCallback((record: Record<string, unknown>) => {
|
|
151
|
+
if (operations.read !== false) {
|
|
152
|
+
handleView(record);
|
|
153
|
+
}
|
|
154
|
+
}, [operations.read, handleView]);
|
|
155
|
+
|
|
156
|
+
// Handle delete action
|
|
157
|
+
const handleDelete = useCallback((_record: Record<string, unknown>) => {
|
|
158
|
+
// Trigger table refresh after delete
|
|
159
|
+
setRefreshKey(prev => prev + 1);
|
|
160
|
+
}, []);
|
|
161
|
+
|
|
162
|
+
// Handle bulk delete action
|
|
163
|
+
const handleBulkDelete = useCallback((_records: Record<string, unknown>[]) => {
|
|
164
|
+
// Trigger table refresh after bulk delete
|
|
165
|
+
setRefreshKey(prev => prev + 1);
|
|
166
|
+
}, []);
|
|
167
|
+
|
|
168
|
+
// Handle form submission
|
|
169
|
+
const handleFormSuccess = useCallback(() => {
|
|
170
|
+
// Close the form
|
|
171
|
+
setIsFormOpen(false);
|
|
172
|
+
setSelectedRecord(null);
|
|
173
|
+
|
|
174
|
+
// Trigger table refresh
|
|
175
|
+
setRefreshKey(prev => prev + 1);
|
|
176
|
+
}, []);
|
|
177
|
+
|
|
178
|
+
// Handle form cancellation
|
|
179
|
+
const handleFormCancel = useCallback(() => {
|
|
180
|
+
setIsFormOpen(false);
|
|
181
|
+
setSelectedRecord(null);
|
|
182
|
+
}, []);
|
|
183
|
+
|
|
184
|
+
// Handle refresh
|
|
185
|
+
const handleRefresh = useCallback(() => {
|
|
186
|
+
setRefreshKey(prev => prev + 1);
|
|
187
|
+
}, []);
|
|
188
|
+
|
|
189
|
+
// Build grid schema
|
|
190
|
+
const gridSchema: ObjectGridSchema = {
|
|
191
|
+
type: 'object-grid',
|
|
192
|
+
objectName: schema.objectName,
|
|
193
|
+
title: schema.table?.title,
|
|
194
|
+
description: schema.table?.description,
|
|
195
|
+
fields: schema.table?.fields,
|
|
196
|
+
columns: schema.table?.columns,
|
|
197
|
+
operations: {
|
|
198
|
+
...operations,
|
|
199
|
+
create: false, // Create is handled by the view's create button
|
|
200
|
+
},
|
|
201
|
+
defaultFilters: schema.table?.defaultFilters,
|
|
202
|
+
defaultSort: schema.table?.defaultSort,
|
|
203
|
+
pageSize: schema.table?.pageSize,
|
|
204
|
+
selectable: schema.table?.selectable,
|
|
205
|
+
className: schema.table?.className,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Build form schema
|
|
209
|
+
const buildFormSchema = (): ObjectFormSchema => {
|
|
210
|
+
const recordId = selectedRecord ? (selectedRecord._id || selectedRecord.id) as string | number | undefined : undefined;
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
type: 'object-form',
|
|
214
|
+
objectName: schema.objectName,
|
|
215
|
+
mode: formMode,
|
|
216
|
+
recordId,
|
|
217
|
+
title: schema.form?.title,
|
|
218
|
+
description: schema.form?.description,
|
|
219
|
+
fields: schema.form?.fields,
|
|
220
|
+
customFields: schema.form?.customFields,
|
|
221
|
+
groups: schema.form?.groups,
|
|
222
|
+
layout: schema.form?.layout,
|
|
223
|
+
columns: schema.form?.columns,
|
|
224
|
+
showSubmit: schema.form?.showSubmit,
|
|
225
|
+
submitText: schema.form?.submitText,
|
|
226
|
+
showCancel: schema.form?.showCancel,
|
|
227
|
+
cancelText: schema.form?.cancelText,
|
|
228
|
+
showReset: schema.form?.showReset,
|
|
229
|
+
initialValues: schema.form?.initialValues,
|
|
230
|
+
readOnly: schema.form?.readOnly || formMode === 'view',
|
|
231
|
+
className: schema.form?.className,
|
|
232
|
+
onSuccess: handleFormSuccess,
|
|
233
|
+
onCancel: handleFormCancel,
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// Get form title based on mode
|
|
238
|
+
const getFormTitle = (): string => {
|
|
239
|
+
if (schema.form?.title) return schema.form.title;
|
|
240
|
+
|
|
241
|
+
const objectLabel = (objectSchema?.label as string) || schema.objectName;
|
|
242
|
+
|
|
243
|
+
switch (formMode) {
|
|
244
|
+
case 'create':
|
|
245
|
+
return `Create ${objectLabel}`;
|
|
246
|
+
case 'edit':
|
|
247
|
+
return `Edit ${objectLabel}`;
|
|
248
|
+
case 'view':
|
|
249
|
+
return `View ${objectLabel}`;
|
|
250
|
+
default:
|
|
251
|
+
return objectLabel;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Render the form in a drawer
|
|
256
|
+
const renderDrawerForm = () => (
|
|
257
|
+
<Drawer open={isFormOpen} onOpenChange={setIsFormOpen} direction="right">
|
|
258
|
+
<DrawerContent className="w-full sm:max-w-2xl">
|
|
259
|
+
<DrawerHeader>
|
|
260
|
+
<DrawerTitle>{getFormTitle()}</DrawerTitle>
|
|
261
|
+
{schema.form?.description && (
|
|
262
|
+
<DrawerDescription>{schema.form.description}</DrawerDescription>
|
|
263
|
+
)}
|
|
264
|
+
</DrawerHeader>
|
|
265
|
+
<div className="flex-1 overflow-y-auto px-4 pb-4">
|
|
266
|
+
<ObjectForm
|
|
267
|
+
schema={buildFormSchema()}
|
|
268
|
+
dataSource={dataSource}
|
|
269
|
+
/>
|
|
270
|
+
</div>
|
|
271
|
+
</DrawerContent>
|
|
272
|
+
</Drawer>
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// Render the form in a modal
|
|
276
|
+
const renderModalForm = () => (
|
|
277
|
+
<Dialog open={isFormOpen} onOpenChange={setIsFormOpen}>
|
|
278
|
+
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
|
279
|
+
<DialogHeader>
|
|
280
|
+
<DialogTitle>{getFormTitle()}</DialogTitle>
|
|
281
|
+
{schema.form?.description && (
|
|
282
|
+
<DialogDescription>{schema.form.description}</DialogDescription>
|
|
283
|
+
)}
|
|
284
|
+
</DialogHeader>
|
|
285
|
+
<ObjectForm
|
|
286
|
+
schema={buildFormSchema()}
|
|
287
|
+
dataSource={dataSource}
|
|
288
|
+
/>
|
|
289
|
+
</DialogContent>
|
|
290
|
+
</Dialog>
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// Render toolbar
|
|
294
|
+
const renderToolbar = () => {
|
|
295
|
+
const showSearchBox = schema.showSearch !== false;
|
|
296
|
+
const showFiltersButton = schema.showFilters !== false;
|
|
297
|
+
const showCreateButton = schema.showCreate !== false && operations.create !== false;
|
|
298
|
+
const showRefreshButton = schema.showRefresh !== false;
|
|
299
|
+
|
|
300
|
+
// Don't render toolbar if no elements are shown
|
|
301
|
+
if (!showSearchBox && !showFiltersButton && !showCreateButton && !showRefreshButton) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return (
|
|
306
|
+
<div className="flex flex-col gap-4 mb-4">
|
|
307
|
+
{/* Main toolbar row */}
|
|
308
|
+
<div className="flex items-center justify-between gap-4">
|
|
309
|
+
{/* Left side: Search */}
|
|
310
|
+
<div className="flex-1 max-w-md">
|
|
311
|
+
{showSearchBox && (
|
|
312
|
+
<div className="relative">
|
|
313
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
314
|
+
<Input
|
|
315
|
+
type="search"
|
|
316
|
+
placeholder={`Search ${objectSchema?.label || schema.objectName}...`}
|
|
317
|
+
value={searchQuery}
|
|
318
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearchQuery(e.target.value)}
|
|
319
|
+
className="pl-9"
|
|
320
|
+
/>
|
|
321
|
+
</div>
|
|
322
|
+
)}
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
{/* Right side: Actions */}
|
|
326
|
+
<div className="flex items-center gap-2">
|
|
327
|
+
{showFiltersButton && (
|
|
328
|
+
<Button
|
|
329
|
+
variant="outline"
|
|
330
|
+
size="sm"
|
|
331
|
+
onClick={() => setShowFilters(!showFilters)}
|
|
332
|
+
>
|
|
333
|
+
<Filter className="h-4 w-4" />
|
|
334
|
+
Filters
|
|
335
|
+
{showFilters && (
|
|
336
|
+
<X className="h-3 w-3 ml-1" />
|
|
337
|
+
)}
|
|
338
|
+
</Button>
|
|
339
|
+
)}
|
|
340
|
+
|
|
341
|
+
{showRefreshButton && (
|
|
342
|
+
<Button
|
|
343
|
+
variant="outline"
|
|
344
|
+
size="sm"
|
|
345
|
+
onClick={handleRefresh}
|
|
346
|
+
>
|
|
347
|
+
<RefreshCw className="h-4 w-4" />
|
|
348
|
+
</Button>
|
|
349
|
+
)}
|
|
350
|
+
|
|
351
|
+
{showCreateButton && (
|
|
352
|
+
<Button
|
|
353
|
+
size="sm"
|
|
354
|
+
onClick={handleCreate}
|
|
355
|
+
>
|
|
356
|
+
<Plus className="h-4 w-4" />
|
|
357
|
+
Create
|
|
358
|
+
</Button>
|
|
359
|
+
)}
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
{/* Filter panel (shown when filters are active) */}
|
|
364
|
+
{showFilters && (
|
|
365
|
+
<div className="p-4 border rounded-md bg-muted/50">
|
|
366
|
+
<p className="text-sm text-muted-foreground">
|
|
367
|
+
Filter functionality will be integrated with FilterBuilder component
|
|
368
|
+
</p>
|
|
369
|
+
{/* TODO: Integrate FilterBuilder component here */}
|
|
370
|
+
</div>
|
|
371
|
+
)}
|
|
372
|
+
</div>
|
|
373
|
+
);
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<div className={className}>
|
|
378
|
+
{/* Title and description */}
|
|
379
|
+
{(schema.title || schema.description) && (
|
|
380
|
+
<div className="mb-6">
|
|
381
|
+
{schema.title && (
|
|
382
|
+
<h2 className="text-2xl font-bold tracking-tight">{schema.title}</h2>
|
|
383
|
+
)}
|
|
384
|
+
{schema.description && (
|
|
385
|
+
<p className="text-muted-foreground mt-1">{schema.description}</p>
|
|
386
|
+
)}
|
|
387
|
+
</div>
|
|
388
|
+
)}
|
|
389
|
+
|
|
390
|
+
{/* Toolbar */}
|
|
391
|
+
{renderToolbar()}
|
|
392
|
+
|
|
393
|
+
{/* Grid */}
|
|
394
|
+
<ObjectGrid
|
|
395
|
+
key={refreshKey}
|
|
396
|
+
schema={gridSchema}
|
|
397
|
+
dataSource={dataSource}
|
|
398
|
+
onRowClick={handleRowClick}
|
|
399
|
+
onEdit={operations.update !== false ? handleEdit : undefined}
|
|
400
|
+
onDelete={operations.delete !== false ? handleDelete : undefined}
|
|
401
|
+
onBulkDelete={operations.delete !== false ? handleBulkDelete : undefined}
|
|
402
|
+
/>
|
|
403
|
+
|
|
404
|
+
{/* Form (drawer or modal) */}
|
|
405
|
+
{layout === 'drawer' && renderDrawerForm()}
|
|
406
|
+
{layout === 'modal' && renderModalForm()}
|
|
407
|
+
</div>
|
|
408
|
+
);
|
|
409
|
+
};
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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 { ObjectView } from './ObjectView';
|
|
12
|
+
|
|
13
|
+
export { ObjectView };
|
|
14
|
+
export type { ObjectViewProps } from './ObjectView';
|
|
15
|
+
|
|
16
|
+
// Register object-view component
|
|
17
|
+
const ObjectViewRenderer: React.FC<{ schema: any }> = ({ schema }) => {
|
|
18
|
+
return <ObjectView schema={schema} dataSource={null as any} />;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
ComponentRegistry.register('object-view', ObjectViewRenderer);
|
package/tsconfig.json
ADDED
package/vite.config.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
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: 'ObjectUIPluginView',
|
|
18
|
+
fileName: 'index',
|
|
19
|
+
},
|
|
20
|
+
rollupOptions: {
|
|
21
|
+
external: [
|
|
22
|
+
'react',
|
|
23
|
+
'react-dom',
|
|
24
|
+
'@object-ui/components',
|
|
25
|
+
'@object-ui/core',
|
|
26
|
+
'@object-ui/plugin-form',
|
|
27
|
+
'@object-ui/plugin-grid',
|
|
28
|
+
'@object-ui/react',
|
|
29
|
+
'@object-ui/types',
|
|
30
|
+
'lucide-react'
|
|
31
|
+
],
|
|
32
|
+
output: {
|
|
33
|
+
globals: {
|
|
34
|
+
react: 'React',
|
|
35
|
+
'react-dom': 'ReactDOM',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
});
|