@object-ui/plugin-view 3.1.2 → 3.1.4
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/.turbo/turbo-build.log +27 -15
- package/CHANGELOG.md +22 -0
- package/dist/index.js +4357 -4841
- package/dist/index.umd.cjs +3 -3
- package/package.json +10 -10
- package/src/ObjectView.tsx +28 -20
- package/src/__tests__/ObjectView.test.tsx +1 -1
- package/src/__tests__/config-sync-integration.test.tsx +1 -1
- package/src/index.tsx +2 -14
package/src/ObjectView.tsx
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* - ViewSwitcher for toggling between view types
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
|
25
|
+
import React, { useEffect, useState, useCallback, useMemo, useRef } from 'react';
|
|
26
26
|
import type {
|
|
27
27
|
ObjectViewSchema,
|
|
28
28
|
ObjectGridSchema,
|
|
@@ -57,20 +57,13 @@ import {
|
|
|
57
57
|
} from '@object-ui/components';
|
|
58
58
|
import { Plus } from 'lucide-react';
|
|
59
59
|
import { buildExpandFields } from '@object-ui/core';
|
|
60
|
+
import { SchemaRenderer as ImportedSchemaRenderer } from '@object-ui/react';
|
|
60
61
|
import { ViewSwitcher } from './ViewSwitcher';
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
|
-
*
|
|
64
|
-
* Falls back to null if not available.
|
|
64
|
+
* SchemaRenderer from @object-ui/react, used to render sub-view schemas.
|
|
65
65
|
*/
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
69
|
-
const mod = require('@object-ui/react');
|
|
70
|
-
SchemaRendererComponent = mod.SchemaRenderer || null;
|
|
71
|
-
} catch {
|
|
72
|
-
// @object-ui/react not available
|
|
73
|
-
}
|
|
66
|
+
const SchemaRendererComponent: React.FC<any> = ImportedSchemaRenderer;
|
|
74
67
|
|
|
75
68
|
export interface ObjectViewProps {
|
|
76
69
|
/**
|
|
@@ -219,6 +212,11 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
219
212
|
onViewAction,
|
|
220
213
|
}) => {
|
|
221
214
|
const [objectSchema, setObjectSchema] = useState<Record<string, unknown> | null>(null);
|
|
215
|
+
// Assigned in the render body (not in an effect) so the fetchData effect always
|
|
216
|
+
// reads the latest objectSchema without needing it as a dependency. This matches
|
|
217
|
+
// the same pattern used in ObjectCalendar's objectSchemaRef.
|
|
218
|
+
const objectSchemaRef = useRef<Record<string, unknown> | null>(null);
|
|
219
|
+
objectSchemaRef.current = objectSchema;
|
|
222
220
|
const [isFormOpen, setIsFormOpen] = useState(false);
|
|
223
221
|
const [formMode, setFormMode] = useState<FormMode>('create');
|
|
224
222
|
const [selectedRecord, setSelectedRecord] = useState<Record<string, unknown> | null>(null);
|
|
@@ -294,8 +292,12 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
294
292
|
let isMounted = true;
|
|
295
293
|
|
|
296
294
|
const fetchData = async () => {
|
|
295
|
+
// When renderListView is provided, the custom list view (e.g. ListView)
|
|
296
|
+
// handles its own data fetching — skip to avoid duplicate requests and
|
|
297
|
+
// unnecessary re-renders that can cause duplicate records in child views.
|
|
298
|
+
if (renderListView) return;
|
|
297
299
|
// Only fetch for non-grid views (ObjectGrid has its own data fetching)
|
|
298
|
-
if (currentViewType === 'grid'
|
|
300
|
+
if (currentViewType === 'grid') return;
|
|
299
301
|
if (!dataSource || !schema.objectName) return;
|
|
300
302
|
|
|
301
303
|
setLoading(true);
|
|
@@ -322,8 +324,11 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
322
324
|
? sortConfig.map(s => ({ field: s.field, order: s.direction }))
|
|
323
325
|
: (currentNamedViewConfig?.sort || activeView?.sort || schema.table?.defaultSort || undefined);
|
|
324
326
|
|
|
325
|
-
// Auto-inject $expand for lookup/master_detail fields
|
|
326
|
-
|
|
327
|
+
// Auto-inject $expand for lookup/master_detail fields.
|
|
328
|
+
// Use a ref instead of the state variable to avoid re-running this effect
|
|
329
|
+
// every time the object schema loads — that would cause a double-fetch and
|
|
330
|
+
// duplicate events in child views like the calendar.
|
|
331
|
+
const expand = buildExpandFields((objectSchemaRef.current as any)?.fields);
|
|
327
332
|
const results = await dataSource.find(schema.objectName, {
|
|
328
333
|
$filter: finalFilter.length > 0 ? finalFilter : undefined,
|
|
329
334
|
$orderby: sort,
|
|
@@ -339,6 +344,8 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
339
344
|
items = (results as any).data;
|
|
340
345
|
} else if (Array.isArray((results as any).records)) {
|
|
341
346
|
items = (results as any).records;
|
|
347
|
+
} else if (Array.isArray((results as any).value)) {
|
|
348
|
+
items = (results as any).value;
|
|
342
349
|
}
|
|
343
350
|
}
|
|
344
351
|
|
|
@@ -352,8 +359,9 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
352
359
|
|
|
353
360
|
fetchData();
|
|
354
361
|
return () => { isMounted = false; };
|
|
362
|
+
// objectSchema intentionally omitted from deps — read via ref to prevent double-fetch
|
|
355
363
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
356
|
-
}, [schema.objectName, dataSource, currentViewType, filterValues, sortConfig, refreshKey, currentNamedViewConfig, activeView, renderListView
|
|
364
|
+
}, [schema.objectName, dataSource, currentViewType, filterValues, sortConfig, refreshKey, currentNamedViewConfig, activeView, renderListView]);
|
|
357
365
|
|
|
358
366
|
// Determine layout mode
|
|
359
367
|
const layout = schema.layout || 'drawer';
|
|
@@ -384,7 +392,7 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
384
392
|
return;
|
|
385
393
|
}
|
|
386
394
|
if (layout === 'page' && schema.onNavigate) {
|
|
387
|
-
const recordId = record.
|
|
395
|
+
const recordId = record.id || record._id;
|
|
388
396
|
schema.onNavigate(recordId as string | number, 'edit');
|
|
389
397
|
} else {
|
|
390
398
|
setFormMode('edit');
|
|
@@ -396,7 +404,7 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
396
404
|
// Handle view action (read a record)
|
|
397
405
|
const handleView = useCallback((record: Record<string, unknown>) => {
|
|
398
406
|
if (layout === 'page' && schema.onNavigate) {
|
|
399
|
-
const recordId = record.
|
|
407
|
+
const recordId = record.id || record._id;
|
|
400
408
|
schema.onNavigate(recordId as string | number, 'view');
|
|
401
409
|
} else {
|
|
402
410
|
setFormMode('view');
|
|
@@ -418,7 +426,7 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
418
426
|
return; // Do nothing
|
|
419
427
|
}
|
|
420
428
|
if (navigationConfig.mode === 'new_window' || navigationConfig.openNewTab) {
|
|
421
|
-
const recordId = record.
|
|
429
|
+
const recordId = record.id || record._id;
|
|
422
430
|
const url = `/${schema.objectName}/${encodeURIComponent(String(recordId))}`;
|
|
423
431
|
window.open(url, '_blank');
|
|
424
432
|
return;
|
|
@@ -436,7 +444,7 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
436
444
|
return;
|
|
437
445
|
}
|
|
438
446
|
if (navigationConfig.mode === 'page') {
|
|
439
|
-
const recordId = record.
|
|
447
|
+
const recordId = record.id || record._id;
|
|
440
448
|
if (schema.onNavigate) {
|
|
441
449
|
schema.onNavigate(recordId as string | number, 'view');
|
|
442
450
|
}
|
|
@@ -727,7 +735,7 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
|
|
|
727
735
|
// Build form schema
|
|
728
736
|
const buildFormSchema = (): ObjectFormSchema => {
|
|
729
737
|
const recordId = selectedRecord
|
|
730
|
-
? ((selectedRecord.
|
|
738
|
+
? ((selectedRecord.id || selectedRecord._id) as string | number | undefined)
|
|
731
739
|
: undefined;
|
|
732
740
|
|
|
733
741
|
return {
|
|
@@ -25,7 +25,7 @@ vi.mock('@object-ui/react', () => ({
|
|
|
25
25
|
vi.mock('@object-ui/plugin-grid', () => ({
|
|
26
26
|
ObjectGrid: ({ schema, onRowClick }: any) => (
|
|
27
27
|
<div data-testid="object-grid" data-object={schema?.objectName}>
|
|
28
|
-
<button data-testid="grid-row" onClick={() => onRowClick?.({
|
|
28
|
+
<button data-testid="grid-row" onClick={() => onRowClick?.({ id: '1', name: 'Test' })}>
|
|
29
29
|
Row 1
|
|
30
30
|
</button>
|
|
31
31
|
</div>
|
|
@@ -32,7 +32,7 @@ vi.mock('@object-ui/react', () => ({
|
|
|
32
32
|
vi.mock('@object-ui/plugin-grid', () => ({
|
|
33
33
|
ObjectGrid: ({ schema, onRowClick }: any) => (
|
|
34
34
|
<div data-testid="object-grid" data-object={schema?.objectName}>
|
|
35
|
-
<button data-testid="grid-row" onClick={() => onRowClick?.({
|
|
35
|
+
<button data-testid="grid-row" onClick={() => onRowClick?.({ id: '1', name: 'Test' })}>
|
|
36
36
|
Row 1
|
|
37
37
|
</button>
|
|
38
38
|
</div>
|
package/src/index.tsx
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import React, { useContext } from 'react';
|
|
10
10
|
import { ComponentRegistry } from '@object-ui/core';
|
|
11
|
+
import { SchemaRendererContext as ImportedSchemaRendererContext } from '@object-ui/react';
|
|
11
12
|
import { ObjectView } from './ObjectView';
|
|
12
13
|
import { ViewSwitcher } from './ViewSwitcher';
|
|
13
14
|
import { FilterUI } from './FilterUI';
|
|
@@ -25,22 +26,9 @@ export type { SharedViewLinkProps } from './SharedViewLink';
|
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* SchemaRendererContext is created by @object-ui/react.
|
|
28
|
-
* We import it dynamically to avoid a circular dependency.
|
|
29
29
|
* The context value provides { dataSource }.
|
|
30
|
-
* A fallback context is created so hooks are never called conditionally.
|
|
31
30
|
*/
|
|
32
|
-
const
|
|
33
|
-
let SchemaRendererContext: React.Context<any> = FallbackContext;
|
|
34
|
-
try {
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36
|
-
const mod = require('@object-ui/react');
|
|
37
|
-
// The context is re-exported from @object-ui/react
|
|
38
|
-
if (mod.SchemaRendererContext) {
|
|
39
|
-
SchemaRendererContext = mod.SchemaRendererContext;
|
|
40
|
-
}
|
|
41
|
-
} catch {
|
|
42
|
-
// @object-ui/react not available — registry-based dataSource only
|
|
43
|
-
}
|
|
31
|
+
const SchemaRendererContext: React.Context<any> = ImportedSchemaRendererContext;
|
|
44
32
|
|
|
45
33
|
// Register object-view component
|
|
46
34
|
const ObjectViewRenderer: React.FC<{ schema: any }> = ({ schema }) => {
|