@object-ui/react 0.5.0 → 3.0.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +32 -0
- package/dist/LazyPluginLoader.d.ts +44 -0
- package/dist/LazyPluginLoader.js +92 -3
- package/dist/SchemaRenderer.d.ts +20 -1
- package/dist/SchemaRenderer.js +76 -13
- package/dist/components/form/FieldFactory.js +44 -40
- package/dist/components/form/FormRenderer.js +45 -3
- package/dist/context/ActionContext.d.ts +76 -0
- package/dist/context/ActionContext.js +139 -0
- package/dist/context/DndContext.d.ts +135 -0
- package/dist/context/DndContext.js +120 -0
- package/dist/context/NotificationContext.d.ts +113 -0
- package/dist/context/NotificationContext.js +130 -0
- package/dist/context/ThemeContext.d.ts +94 -0
- package/dist/context/ThemeContext.js +215 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.js +4 -0
- package/dist/hooks/index.d.ts +22 -0
- package/dist/hooks/index.js +22 -0
- package/dist/hooks/useActionRunner.d.ts +21 -7
- package/dist/hooks/useActionRunner.js +27 -7
- package/dist/hooks/useAnimation.d.ts +46 -0
- package/dist/hooks/useAnimation.js +59 -0
- package/dist/hooks/useClientNotifications.d.ts +53 -0
- package/dist/hooks/useClientNotifications.js +190 -0
- package/dist/hooks/useColumnSummary.d.ts +38 -0
- package/dist/hooks/useColumnSummary.js +54 -0
- package/dist/hooks/useCrudShortcuts.d.ts +39 -0
- package/dist/hooks/useCrudShortcuts.js +96 -0
- package/dist/hooks/useDensityMode.d.ts +48 -0
- package/dist/hooks/useDensityMode.js +60 -0
- package/dist/hooks/useDiscovery.d.ts +78 -0
- package/dist/hooks/useDiscovery.js +96 -0
- package/dist/hooks/useDynamicApp.d.ts +70 -0
- package/dist/hooks/useDynamicApp.js +92 -0
- package/dist/hooks/useETagCache.d.ts +75 -0
- package/dist/hooks/useETagCache.js +306 -0
- package/dist/hooks/useFocusManagement.d.ts +89 -0
- package/dist/hooks/useFocusManagement.js +188 -0
- package/dist/hooks/useFocusTrap.d.ts +41 -0
- package/dist/hooks/useFocusTrap.js +103 -0
- package/dist/hooks/useKeyboardShortcuts.d.ts +49 -0
- package/dist/hooks/useKeyboardShortcuts.js +86 -0
- package/dist/hooks/useNavigationOverlay.d.ts +78 -0
- package/dist/hooks/useNavigationOverlay.js +112 -0
- package/dist/hooks/useOffline.d.ts +96 -0
- package/dist/hooks/useOffline.js +185 -0
- package/dist/hooks/usePageTransition.d.ts +60 -0
- package/dist/hooks/usePageTransition.js +111 -0
- package/dist/hooks/usePageVariables.d.ts +67 -0
- package/dist/hooks/usePageVariables.js +109 -0
- package/dist/hooks/usePerformance.d.ts +87 -0
- package/dist/hooks/usePerformance.js +165 -0
- package/dist/hooks/usePerformanceBudget.d.ts +78 -0
- package/dist/hooks/usePerformanceBudget.js +209 -0
- package/dist/hooks/useReducedMotion.d.ts +28 -0
- package/dist/hooks/useReducedMotion.js +48 -0
- package/dist/hooks/useSchemaPersistence.d.ts +87 -0
- package/dist/hooks/useSchemaPersistence.js +173 -0
- package/dist/hooks/useViewData.d.ts +73 -0
- package/dist/hooks/useViewData.js +135 -0
- package/dist/hooks/useViewSharing.d.ts +57 -0
- package/dist/hooks/useViewSharing.js +74 -0
- package/dist/hooks/useViewTransition.d.ts +54 -0
- package/dist/hooks/useViewTransition.js +83 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/utils/i18n.d.ts +10 -0
- package/dist/utils/i18n.js +12 -0
- package/package.json +7 -5
- package/src/LazyPluginLoader.tsx +129 -7
- package/src/SchemaRenderer.tsx +122 -13
- package/src/__tests__/LazyPluginLoader.test.tsx +97 -1
- package/src/__tests__/SchemaRenderer.aria.test.tsx +118 -0
- package/src/__tests__/SchemaRenderer.errorBoundary.test.tsx +114 -0
- package/src/__tests__/SchemaRendererProvider.smoke.test.tsx +132 -0
- package/src/__tests__/ThemeProvider.test.tsx +451 -0
- package/src/__tests__/useNavigationOverlay.test.ts +416 -0
- package/src/__tests__/useOffline.test.ts +198 -0
- package/src/__tests__/usePageTransition.test.ts +214 -0
- package/src/__tests__/usePageVariables.test.tsx +334 -0
- package/src/__tests__/usePerformance.test.ts +137 -0
- package/src/__tests__/useViewData.test.tsx +422 -0
- package/src/__tests__/view-switching.test.tsx +456 -0
- package/src/components/form/FieldFactory.tsx +51 -46
- package/src/components/form/FormRenderer.test.tsx +145 -0
- package/src/components/form/FormRenderer.tsx +52 -3
- package/src/context/ActionContext.tsx +213 -0
- package/src/context/DndContext.tsx +260 -0
- package/src/context/NotificationContext.tsx +280 -0
- package/src/context/ThemeContext.tsx +325 -0
- package/src/context/__tests__/ActionContext.test.tsx +174 -0
- package/src/context/__tests__/DndContext.test.tsx +150 -0
- package/src/context/__tests__/NotificationContext.test.tsx +176 -0
- package/src/context/index.ts +4 -0
- package/src/hooks/__tests__/useActionRunner.test.ts +153 -0
- package/src/hooks/__tests__/useAnimation.test.ts +82 -0
- package/src/hooks/__tests__/useColumnSummary.test.ts +93 -0
- package/src/hooks/__tests__/useDensityMode.test.ts +81 -0
- package/src/hooks/__tests__/useDiscovery.test.tsx +149 -0
- package/src/hooks/__tests__/useDynamicApp.test.ts +161 -0
- package/src/hooks/__tests__/useETagCache.test.ts +153 -0
- package/src/hooks/__tests__/useExpression.test.ts +86 -0
- package/src/hooks/__tests__/useFocusTrap.test.ts +47 -0
- package/src/hooks/__tests__/useKeyboardShortcuts.test.ts +144 -0
- package/src/hooks/__tests__/usePerformanceBudget.test.ts +157 -0
- package/src/hooks/__tests__/useSchemaPersistence.test.ts +220 -0
- package/src/hooks/__tests__/useViewSharing.test.ts +121 -0
- package/src/hooks/__tests__/useViewTransition.test.ts +61 -0
- package/src/hooks/index.ts +22 -1
- package/src/hooks/useActionRunner.ts +49 -11
- package/src/hooks/useAnimation.ts +117 -0
- package/src/hooks/useClientNotifications.ts +251 -0
- package/src/hooks/useColumnSummary.ts +87 -0
- package/src/hooks/useCrudShortcuts.ts +126 -0
- package/src/hooks/useDensityMode.ts +102 -0
- package/src/hooks/useDiscovery.ts +141 -0
- package/src/hooks/useDynamicApp.ts +142 -0
- package/src/hooks/useETagCache.ts +404 -0
- package/src/hooks/useFocusManagement.ts +248 -0
- package/src/hooks/useFocusTrap.ts +138 -0
- package/src/hooks/useKeyboardShortcuts.ts +119 -0
- package/src/hooks/useNavigationOverlay.ts +180 -0
- package/src/hooks/useOffline.ts +301 -0
- package/src/hooks/usePageTransition.ts +187 -0
- package/src/hooks/usePageVariables.tsx +157 -0
- package/src/hooks/usePerformance.ts +246 -0
- package/src/hooks/usePerformanceBudget.ts +313 -0
- package/src/hooks/useReducedMotion.ts +53 -0
- package/src/hooks/useSchemaPersistence.ts +222 -0
- package/src/hooks/useViewData.ts +201 -0
- package/src/hooks/useViewSharing.ts +140 -0
- package/src/hooks/useViewTransition.ts +144 -0
- package/src/index.ts +21 -0
- package/src/utils/i18n.ts +10 -0
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/__tests__/LazyPluginLoader.test.d.ts +0 -8
- package/dist/__tests__/LazyPluginLoader.test.js +0 -49
- package/dist/components/form/FormRenderer.test.d.ts +0 -8
- package/dist/components/form/FormRenderer.test.js +0 -534
- package/dist/index.test.d.ts +0 -8
- package/dist/index.test.js +0 -13
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @object-ui/react
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 87979c3: Upgrade to @objectstack v3.0.0 and console bundle optimization
|
|
8
|
+
- Upgraded all @objectstack/\* packages from ^2.0.7 to ^3.0.0
|
|
9
|
+
- Breaking change migrations: Hub → Cloud namespace, definePlugin removed, PaginatedResult.value → .records, PaginatedResult.count → .total, client.meta.getObject() → client.meta.getItem()
|
|
10
|
+
- Console bundle optimization: split monolithic 3.7 MB chunk into 17 granular cacheable chunks (95% main entry reduction)
|
|
11
|
+
- Added gzip + brotli pre-compression via vite-plugin-compression2
|
|
12
|
+
- Lazy MSW loading for build:server (~150 KB gzip saved)
|
|
13
|
+
- Added bundle analysis with rollup-plugin-visualizer
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [87979c3]
|
|
18
|
+
- @object-ui/types@3.0.0
|
|
19
|
+
- @object-ui/core@3.0.0
|
|
20
|
+
- @object-ui/i18n@3.0.0
|
|
21
|
+
|
|
22
|
+
## 2.0.0
|
|
23
|
+
|
|
24
|
+
### Major Changes
|
|
25
|
+
|
|
26
|
+
- b859617: Release v1.0.0 — unify all package versions to 1.0.0
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [b859617]
|
|
31
|
+
- @object-ui/types@2.0.0
|
|
32
|
+
- @object-ui/core@2.0.0
|
|
33
|
+
- @object-ui/i18n@2.0.0
|
|
34
|
+
|
|
3
35
|
## 0.3.1
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
|
@@ -11,6 +11,21 @@ export interface LazyPluginOptions {
|
|
|
11
11
|
* Fallback component to show while loading
|
|
12
12
|
*/
|
|
13
13
|
fallback?: React.ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Number of retry attempts on load failure (default: 2)
|
|
16
|
+
*/
|
|
17
|
+
retries?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Delay in ms between retries (default: 1000)
|
|
20
|
+
*/
|
|
21
|
+
retryDelay?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Custom error fallback component
|
|
24
|
+
*/
|
|
25
|
+
errorFallback?: React.ComponentType<{
|
|
26
|
+
error: Error;
|
|
27
|
+
retry: () => void;
|
|
28
|
+
}>;
|
|
14
29
|
}
|
|
15
30
|
/**
|
|
16
31
|
* Create a lazy-loaded plugin component with Suspense wrapper
|
|
@@ -31,8 +46,37 @@ export interface LazyPluginOptions {
|
|
|
31
46
|
* () => import('@object-ui/plugin-grid'),
|
|
32
47
|
* { fallback: <div>Loading grid...</div> }
|
|
33
48
|
* );
|
|
49
|
+
*
|
|
50
|
+
* // With retry and error handling
|
|
51
|
+
* const ObjectGrid = createLazyPlugin(
|
|
52
|
+
* () => import('@object-ui/plugin-grid'),
|
|
53
|
+
* {
|
|
54
|
+
* retries: 3,
|
|
55
|
+
* errorFallback: ({ error, retry }) => (
|
|
56
|
+
* <div>
|
|
57
|
+
* <p>Failed to load: {error.message}</p>
|
|
58
|
+
* <button onClick={retry}>Retry</button>
|
|
59
|
+
* </div>
|
|
60
|
+
* ),
|
|
61
|
+
* }
|
|
62
|
+
* );
|
|
34
63
|
* ```
|
|
35
64
|
*/
|
|
36
65
|
export declare function createLazyPlugin<P extends object = any>(importFn: () => Promise<{
|
|
37
66
|
default: React.ComponentType<P>;
|
|
38
67
|
}>, options?: LazyPluginOptions): React.ComponentType<P>;
|
|
68
|
+
/**
|
|
69
|
+
* Preload a plugin module without rendering it.
|
|
70
|
+
* Useful for preloading plugins that will be needed soon.
|
|
71
|
+
*
|
|
72
|
+
* @param importFn - Dynamic import function
|
|
73
|
+
* @returns Promise that resolves when the module is loaded
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* // Preload on hover
|
|
78
|
+
* const loadGrid = () => import('@object-ui/plugin-grid');
|
|
79
|
+
* <button onMouseEnter={() => preloadPlugin(loadGrid)}>Show Grid</button>
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function preloadPlugin<T = unknown>(importFn: () => Promise<T>): Promise<T>;
|
package/dist/LazyPluginLoader.js
CHANGED
|
@@ -6,7 +6,55 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
6
6
|
* This source code is licensed under the MIT license found in the
|
|
7
7
|
* LICENSE file in the root directory of this source tree.
|
|
8
8
|
*/
|
|
9
|
-
import { lazy, Suspense } from 'react';
|
|
9
|
+
import { lazy, Suspense, Component } from 'react';
|
|
10
|
+
class PluginErrorBoundary extends Component {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(...arguments);
|
|
13
|
+
Object.defineProperty(this, "state", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true,
|
|
17
|
+
value: { hasError: false, error: null }
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(this, "handleRetry", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: () => {
|
|
24
|
+
this.setState({ hasError: false, error: null });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
static getDerivedStateFromError(error) {
|
|
29
|
+
return { hasError: true, error };
|
|
30
|
+
}
|
|
31
|
+
render() {
|
|
32
|
+
if (this.state.hasError && this.state.error) {
|
|
33
|
+
const FallbackComponent = this.props.fallback;
|
|
34
|
+
if (FallbackComponent) {
|
|
35
|
+
return _jsx(FallbackComponent, { error: this.state.error, retry: this.handleRetry });
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return this.props.children;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a lazy-loaded import function with retry support
|
|
44
|
+
*/
|
|
45
|
+
function createRetryImport(importFn, retries, retryDelay) {
|
|
46
|
+
return () => {
|
|
47
|
+
let attempt = 0;
|
|
48
|
+
const tryImport = () => importFn().catch((err) => {
|
|
49
|
+
attempt++;
|
|
50
|
+
if (attempt <= retries) {
|
|
51
|
+
return new Promise((resolve) => setTimeout(() => resolve(tryImport()), retryDelay));
|
|
52
|
+
}
|
|
53
|
+
throw err;
|
|
54
|
+
});
|
|
55
|
+
return tryImport();
|
|
56
|
+
};
|
|
57
|
+
}
|
|
10
58
|
/**
|
|
11
59
|
* Create a lazy-loaded plugin component with Suspense wrapper
|
|
12
60
|
*
|
|
@@ -26,10 +74,51 @@ import { lazy, Suspense } from 'react';
|
|
|
26
74
|
* () => import('@object-ui/plugin-grid'),
|
|
27
75
|
* { fallback: <div>Loading grid...</div> }
|
|
28
76
|
* );
|
|
77
|
+
*
|
|
78
|
+
* // With retry and error handling
|
|
79
|
+
* const ObjectGrid = createLazyPlugin(
|
|
80
|
+
* () => import('@object-ui/plugin-grid'),
|
|
81
|
+
* {
|
|
82
|
+
* retries: 3,
|
|
83
|
+
* errorFallback: ({ error, retry }) => (
|
|
84
|
+
* <div>
|
|
85
|
+
* <p>Failed to load: {error.message}</p>
|
|
86
|
+
* <button onClick={retry}>Retry</button>
|
|
87
|
+
* </div>
|
|
88
|
+
* ),
|
|
89
|
+
* }
|
|
90
|
+
* );
|
|
29
91
|
* ```
|
|
30
92
|
*/
|
|
31
93
|
export function createLazyPlugin(importFn, options) {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
94
|
+
const { retries = 2, retryDelay = 1000, errorFallback } = options || {};
|
|
95
|
+
const retryImport = retries > 0
|
|
96
|
+
? createRetryImport(importFn, retries, retryDelay)
|
|
97
|
+
: importFn;
|
|
98
|
+
const LazyComponent = lazy(retryImport);
|
|
99
|
+
const PluginWrapper = (props) => {
|
|
100
|
+
const content = (_jsx(Suspense, { fallback: options?.fallback || null, children: _jsx(LazyComponent, { ...props }) }));
|
|
101
|
+
if (errorFallback) {
|
|
102
|
+
return (_jsx(PluginErrorBoundary, { fallback: errorFallback, children: content }));
|
|
103
|
+
}
|
|
104
|
+
return content;
|
|
105
|
+
};
|
|
34
106
|
return PluginWrapper;
|
|
35
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Preload a plugin module without rendering it.
|
|
110
|
+
* Useful for preloading plugins that will be needed soon.
|
|
111
|
+
*
|
|
112
|
+
* @param importFn - Dynamic import function
|
|
113
|
+
* @returns Promise that resolves when the module is loaded
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```tsx
|
|
117
|
+
* // Preload on hover
|
|
118
|
+
* const loadGrid = () => import('@object-ui/plugin-grid');
|
|
119
|
+
* <button onMouseEnter={() => preloadPlugin(loadGrid)}>Show Grid</button>
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export function preloadPlugin(importFn) {
|
|
123
|
+
return importFn();
|
|
124
|
+
}
|
package/dist/SchemaRenderer.d.ts
CHANGED
|
@@ -5,8 +5,27 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
import React from 'react';
|
|
8
|
+
import React, { Component } from 'react';
|
|
9
9
|
import { SchemaNode } from '@object-ui/core';
|
|
10
|
+
/**
|
|
11
|
+
* Per-component Error Boundary for SchemaRenderer.
|
|
12
|
+
* Catches render errors in individual components, preventing one broken
|
|
13
|
+
* component from crashing the entire page.
|
|
14
|
+
*/
|
|
15
|
+
interface SchemaErrorBoundaryState {
|
|
16
|
+
hasError: boolean;
|
|
17
|
+
error: Error | null;
|
|
18
|
+
}
|
|
19
|
+
export declare class SchemaErrorBoundary extends Component<{
|
|
20
|
+
componentType?: string;
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
}, SchemaErrorBoundaryState> {
|
|
23
|
+
state: SchemaErrorBoundaryState;
|
|
24
|
+
static getDerivedStateFromError(error: Error): SchemaErrorBoundaryState;
|
|
25
|
+
handleRetry: () => void;
|
|
26
|
+
render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
27
|
+
}
|
|
10
28
|
export declare const SchemaRenderer: React.ForwardRefExoticComponent<Omit<{
|
|
11
29
|
schema: SchemaNode;
|
|
12
30
|
} & Record<string, any>, "ref"> & React.RefAttributes<any>>;
|
|
31
|
+
export {};
|
package/dist/SchemaRenderer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* ObjectUI
|
|
4
4
|
* Copyright (c) 2024-present ObjectStack Inc.
|
|
@@ -6,9 +6,63 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
6
6
|
* This source code is licensed under the MIT license found in the
|
|
7
7
|
* LICENSE file in the root directory of this source tree.
|
|
8
8
|
*/
|
|
9
|
-
import React, { forwardRef, useContext, useMemo } from 'react';
|
|
10
|
-
import { ComponentRegistry, ExpressionEvaluator } from '@object-ui/core';
|
|
9
|
+
import React, { forwardRef, useContext, useMemo, Component } from 'react';
|
|
10
|
+
import { ComponentRegistry, ExpressionEvaluator, isObjectUIError, ERROR_CODES, debugLog, debugTime, debugTimeEnd, } from '@object-ui/core';
|
|
11
11
|
import { SchemaRendererContext } from './context/SchemaRendererContext';
|
|
12
|
+
import { resolveI18nLabel } from './utils/i18n';
|
|
13
|
+
/**
|
|
14
|
+
* Extract AriaPropsSchema properties from a schema node and convert
|
|
15
|
+
* them to standard HTML ARIA attributes.
|
|
16
|
+
*
|
|
17
|
+
* @objectstack/spec AriaPropsSchema defines:
|
|
18
|
+
* ariaLabel: string | I18nLabel (→ aria-label)
|
|
19
|
+
* ariaDescribedBy: string (→ aria-describedby)
|
|
20
|
+
* role: string (→ role)
|
|
21
|
+
*/
|
|
22
|
+
function resolveAriaProps(schema) {
|
|
23
|
+
const aria = {};
|
|
24
|
+
if (schema.ariaLabel) {
|
|
25
|
+
aria['aria-label'] = resolveI18nLabel(schema.ariaLabel);
|
|
26
|
+
}
|
|
27
|
+
if (schema.ariaDescribedBy) {
|
|
28
|
+
aria['aria-describedby'] = schema.ariaDescribedBy;
|
|
29
|
+
}
|
|
30
|
+
if (schema.role) {
|
|
31
|
+
aria['role'] = schema.role;
|
|
32
|
+
}
|
|
33
|
+
return aria;
|
|
34
|
+
}
|
|
35
|
+
export class SchemaErrorBoundary extends Component {
|
|
36
|
+
constructor() {
|
|
37
|
+
super(...arguments);
|
|
38
|
+
Object.defineProperty(this, "state", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: { hasError: false, error: null }
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "handleRetry", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: () => {
|
|
49
|
+
this.setState({ hasError: false, error: null });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
static getDerivedStateFromError(error) {
|
|
54
|
+
return { hasError: true, error };
|
|
55
|
+
}
|
|
56
|
+
render() {
|
|
57
|
+
if (this.state.hasError && this.state.error) {
|
|
58
|
+
const error = this.state.error;
|
|
59
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
60
|
+
const objuiError = isObjectUIError(error) ? error : null;
|
|
61
|
+
return (_jsxs("div", { className: "p-4 border border-orange-400 rounded bg-orange-50 text-orange-700 my-2", role: "alert", children: [_jsxs("p", { className: "font-medium", children: ["Component", this.props.componentType ? ` "${this.props.componentType}"` : '', " failed to render"] }), _jsx("p", { className: "text-sm mt-1", children: error.message }), isDev && objuiError?.code && (_jsxs("p", { className: "text-xs mt-1 text-orange-500", children: ["Error code: ", objuiError.code, objuiError.details?.suggestion ? (_jsxs("span", { className: "block mt-0.5", children: ["\uD83D\uDCA1 ", String(objuiError.details.suggestion)] })) : null] })), _jsx("button", { onClick: this.handleRetry, className: "mt-2 text-sm underline hover:no-underline", children: "Retry" })] }));
|
|
62
|
+
}
|
|
63
|
+
return this.props.children;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
12
66
|
export const SchemaRenderer = forwardRef(({ schema, ...props }, _ref) => {
|
|
13
67
|
const context = useContext(SchemaRendererContext);
|
|
14
68
|
const dataSource = context?.dataSource || {};
|
|
@@ -43,22 +97,31 @@ export const SchemaRenderer = forwardRef(({ schema, ...props }, _ref) => {
|
|
|
43
97
|
// If schema is just a string, render it as text
|
|
44
98
|
if (typeof evaluatedSchema === 'string')
|
|
45
99
|
return _jsx(_Fragment, { children: evaluatedSchema });
|
|
100
|
+
debugLog('schema', 'Rendering schema node', { type: evaluatedSchema.type, id: evaluatedSchema.id });
|
|
46
101
|
const Component = ComponentRegistry.get(evaluatedSchema.type);
|
|
47
102
|
if (!Component) {
|
|
48
|
-
|
|
103
|
+
debugLog('schema', 'Component not found in registry', { type: evaluatedSchema.type });
|
|
104
|
+
const errorInfo = ERROR_CODES['OBJUI-001'];
|
|
105
|
+
return (_jsxs("div", { className: "p-4 border border-red-500 rounded text-red-500 bg-red-50 my-2", role: "alert", children: [_jsxs("p", { className: "font-medium", children: ["Unknown component type: ", _jsx("strong", { children: evaluatedSchema.type })] }), process.env.NODE_ENV !== 'production' && (_jsxs("p", { className: "text-xs mt-1", children: ["\uD83D\uDCA1 ", errorInfo.suggestion, " (OBJUI-001)"] })), _jsx("pre", { className: "text-xs mt-2 overflow-auto", children: JSON.stringify(evaluatedSchema, null, 2) })] }));
|
|
49
106
|
}
|
|
50
107
|
// Note: We don't forward the ref to the Component because components in the registry
|
|
51
108
|
// may not support refs. The SchemaRenderer itself can still receive refs for its own use.
|
|
52
109
|
// Extract schema metadata properties that should NOT be passed as React props
|
|
53
110
|
const { type: _type, children: _children, body: _body, schema: _schema, visible: _visible, visibleOn: _visibleOn, hidden: _hidden, hiddenOn: _hiddenOn, disabled: _disabled, disabledOn: _disabledOn, ...componentProps } = evaluatedSchema;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
111
|
+
// Extract AriaPropsSchema properties for accessibility
|
|
112
|
+
const ariaProps = resolveAriaProps(evaluatedSchema);
|
|
113
|
+
debugTime(`render:${evaluatedSchema.type}:${evaluatedSchema.id ?? 'anon'}`);
|
|
114
|
+
const rendered = (_jsx(SchemaErrorBoundary, { componentType: evaluatedSchema.type, children: React.createElement(Component, {
|
|
115
|
+
schema: evaluatedSchema,
|
|
116
|
+
...componentProps, // Spread non-metadata schema properties as props
|
|
117
|
+
...(evaluatedSchema.props || {}), // Override with explicit props if provided
|
|
118
|
+
...ariaProps, // Inject ARIA attributes from AriaPropsSchema
|
|
119
|
+
className: evaluatedSchema.className,
|
|
120
|
+
'data-obj-id': evaluatedSchema.id,
|
|
121
|
+
'data-obj-type': evaluatedSchema.type,
|
|
122
|
+
...props
|
|
123
|
+
}) }));
|
|
124
|
+
debugTimeEnd(`render:${evaluatedSchema.type}:${evaluatedSchema.id ?? 'anon'}`);
|
|
125
|
+
return rendered;
|
|
63
126
|
});
|
|
64
127
|
SchemaRenderer.displayName = 'SchemaRenderer';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { resolveI18nLabel } from '../../utils/i18n';
|
|
2
3
|
/**
|
|
3
4
|
* FieldFactory component that renders different input types based on
|
|
4
5
|
* the widget property or field type
|
|
@@ -11,6 +12,10 @@ export const FieldFactory = ({ field, methods, disabled = false, }) => {
|
|
|
11
12
|
const widgetType = field.widget || 'text';
|
|
12
13
|
const fieldName = field.field;
|
|
13
14
|
const error = errors[fieldName];
|
|
15
|
+
// Resolve I18nLabel values to plain strings
|
|
16
|
+
const fieldLabel = resolveI18nLabel(field.label);
|
|
17
|
+
const fieldPlaceholder = resolveI18nLabel(field.placeholder);
|
|
18
|
+
const fieldHelpText = resolveI18nLabel(field.helpText);
|
|
14
19
|
// Helper function to handle multiple select value conversion
|
|
15
20
|
const handleMultipleSelectValue = (value) => {
|
|
16
21
|
if (extendedField.multiple && value instanceof HTMLCollection) {
|
|
@@ -19,14 +24,13 @@ export const FieldFactory = ({ field, methods, disabled = false, }) => {
|
|
|
19
24
|
return value;
|
|
20
25
|
};
|
|
21
26
|
// Handle conditional visibility
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// Skip if explicitly hidden
|
|
27
|
+
// Fields are hidden when explicitly hidden or when visibleOn evaluates to false
|
|
28
|
+
// Note: dependsOn is handled at the FormSectionRenderer level
|
|
25
29
|
if (field.hidden) {
|
|
26
30
|
return null;
|
|
27
31
|
}
|
|
28
32
|
// Common field wrapper
|
|
29
|
-
const renderField = (input) => (_jsxs("div", { className: "space-y-2", children: [
|
|
33
|
+
const renderField = (input) => (_jsxs("div", { className: "space-y-2", children: [fieldLabel && (_jsxs("label", { htmlFor: fieldName, className: "block text-sm font-medium text-gray-700", children: [fieldLabel, field.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] })), input, fieldHelpText && (_jsx("p", { className: "text-sm text-gray-500", children: fieldHelpText })), error && (_jsx("p", { className: "text-sm text-red-600", children: error.message }))] }));
|
|
30
34
|
// Render based on widget type
|
|
31
35
|
switch (widgetType.toLowerCase()) {
|
|
32
36
|
case 'text':
|
|
@@ -35,71 +39,71 @@ export const FieldFactory = ({ field, methods, disabled = false, }) => {
|
|
|
35
39
|
case 'password':
|
|
36
40
|
case 'url':
|
|
37
41
|
case 'tel':
|
|
38
|
-
return renderField(_jsx("input", { id: fieldName, type: widgetType === 'string' ? 'text' : widgetType, placeholder:
|
|
39
|
-
required: field.required ? `${
|
|
42
|
+
return renderField(_jsx("input", { id: fieldName, type: widgetType === 'string' ? 'text' : widgetType, placeholder: fieldPlaceholder, disabled: disabled, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
43
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
40
44
|
}) }));
|
|
41
45
|
case 'number':
|
|
42
46
|
case 'integer':
|
|
43
47
|
case 'float':
|
|
44
|
-
return renderField(_jsx("input", { id: fieldName, type: "number", placeholder:
|
|
45
|
-
required: field.required ? `${
|
|
48
|
+
return renderField(_jsx("input", { id: fieldName, type: "number", placeholder: fieldPlaceholder, disabled: disabled, step: widgetType === 'integer' ? '1' : 'any', className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
49
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
46
50
|
valueAsNumber: true,
|
|
47
51
|
}) }));
|
|
48
52
|
case 'checkbox':
|
|
49
53
|
case 'boolean':
|
|
50
|
-
return (_jsxs("div", { className: "flex items-start space-x-2", children: [_jsx("input", { id: fieldName, type: "checkbox", disabled: disabled, className: "mt-1 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-2 focus:ring-blue-500 disabled:opacity-50", ...register(fieldName) }), _jsxs("div", { className: "flex-1", children: [
|
|
54
|
+
return (_jsxs("div", { className: "flex items-start space-x-2", children: [_jsx("input", { id: fieldName, type: "checkbox", disabled: disabled, className: "mt-1 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-2 focus:ring-blue-500 disabled:opacity-50", ...register(fieldName) }), _jsxs("div", { className: "flex-1", children: [fieldLabel && (_jsxs("label", { htmlFor: fieldName, className: "text-sm font-medium text-gray-700", children: [fieldLabel, field.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] })), fieldHelpText && (_jsx("p", { className: "text-sm text-gray-500 mt-1", children: fieldHelpText })), error && (_jsx("p", { className: "text-sm text-red-600 mt-1", children: error.message }))] })] }));
|
|
51
55
|
case 'textarea':
|
|
52
|
-
return renderField(_jsx("textarea", { id: fieldName, placeholder:
|
|
53
|
-
required: field.required ? `${
|
|
56
|
+
return renderField(_jsx("textarea", { id: fieldName, placeholder: fieldPlaceholder, disabled: disabled, rows: 4, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
57
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
54
58
|
}) }));
|
|
55
59
|
case 'select':
|
|
56
60
|
case 'dropdown':
|
|
57
61
|
return renderField(_jsxs("select", { id: fieldName, disabled: disabled, multiple: extendedField.multiple, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
58
|
-
required: field.required ? `${
|
|
62
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
59
63
|
setValueAs: handleMultipleSelectValue,
|
|
60
|
-
}), children: [!extendedField.multiple && _jsx("option", { value: "", children:
|
|
64
|
+
}), children: [!extendedField.multiple && _jsx("option", { value: "", children: fieldPlaceholder || 'Select an option' }), extendedField.options?.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }));
|
|
61
65
|
case 'date':
|
|
62
|
-
return renderField(_jsx("input", { id: fieldName, type: "date", placeholder:
|
|
63
|
-
required: field.required ? `${
|
|
66
|
+
return renderField(_jsx("input", { id: fieldName, type: "date", placeholder: fieldPlaceholder, disabled: disabled, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
67
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
64
68
|
}) }));
|
|
65
69
|
case 'datetime':
|
|
66
70
|
case 'datetime-local':
|
|
67
|
-
return renderField(_jsx("input", { id: fieldName, type: "datetime-local", placeholder:
|
|
68
|
-
required: field.required ? `${
|
|
71
|
+
return renderField(_jsx("input", { id: fieldName, type: "datetime-local", placeholder: fieldPlaceholder, disabled: disabled, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
72
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
69
73
|
}) }));
|
|
70
74
|
case 'time':
|
|
71
|
-
return renderField(_jsx("input", { id: fieldName, type: "time", placeholder:
|
|
72
|
-
required: field.required ? `${
|
|
75
|
+
return renderField(_jsx("input", { id: fieldName, type: "time", placeholder: fieldPlaceholder, disabled: disabled, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
76
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
73
77
|
}) }));
|
|
74
78
|
case 'currency':
|
|
75
|
-
return renderField(_jsxs("div", { className: "relative", children: [_jsx("span", { className: "absolute left-3 top-2 text-gray-500", children: "$" }), _jsx("input", { id: fieldName, type: "number", placeholder:
|
|
76
|
-
required: field.required ? `${
|
|
79
|
+
return renderField(_jsxs("div", { className: "relative", children: [_jsx("span", { className: "absolute left-3 top-2 text-gray-500", children: "$" }), _jsx("input", { id: fieldName, type: "number", placeholder: fieldPlaceholder, disabled: disabled, step: "0.01", className: "w-full pl-8 pr-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
80
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
77
81
|
valueAsNumber: true,
|
|
78
82
|
}) })] }));
|
|
79
83
|
case 'percent':
|
|
80
|
-
return renderField(_jsxs("div", { className: "relative", children: [_jsx("input", { id: fieldName, type: "number", placeholder:
|
|
81
|
-
required: field.required ? `${
|
|
84
|
+
return renderField(_jsxs("div", { className: "relative", children: [_jsx("input", { id: fieldName, type: "number", placeholder: fieldPlaceholder, disabled: disabled, step: "0.01", min: "0", max: "100", className: "w-full pr-8 pl-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
85
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
82
86
|
valueAsNumber: true,
|
|
83
87
|
}) }), _jsx("span", { className: "absolute right-3 top-2 text-gray-500", children: "%" })] }));
|
|
84
88
|
case 'phone':
|
|
85
|
-
return renderField(_jsx("input", { id: fieldName, type: "tel", placeholder:
|
|
86
|
-
required: field.required ? `${
|
|
89
|
+
return renderField(_jsx("input", { id: fieldName, type: "tel", placeholder: fieldPlaceholder || '+1 (555) 000-0000', disabled: disabled, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
90
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
87
91
|
}) }));
|
|
88
92
|
case 'markdown':
|
|
89
|
-
return renderField(_jsx("textarea", { id: fieldName, placeholder:
|
|
90
|
-
required: field.required ? `${
|
|
93
|
+
return renderField(_jsx("textarea", { id: fieldName, placeholder: fieldPlaceholder || 'Enter markdown text...', disabled: disabled, rows: 8, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed font-mono", ...register(fieldName, {
|
|
94
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
91
95
|
}) }));
|
|
92
96
|
case 'html':
|
|
93
|
-
return renderField(_jsx("textarea", { id: fieldName, placeholder:
|
|
94
|
-
required: field.required ? `${
|
|
97
|
+
return renderField(_jsx("textarea", { id: fieldName, placeholder: fieldPlaceholder || 'Enter HTML...', disabled: disabled, rows: 8, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed font-mono", ...register(fieldName, {
|
|
98
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
95
99
|
}) }));
|
|
96
100
|
case 'file':
|
|
97
101
|
return renderField(_jsx("input", { id: fieldName, type: "file", disabled: disabled, multiple: extendedField.multiple, accept: extendedField.accept?.join(','), className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100", ...register(fieldName, {
|
|
98
|
-
required: field.required ? `${
|
|
102
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
99
103
|
}) }));
|
|
100
104
|
case 'image':
|
|
101
105
|
return renderField(_jsx("input", { id: fieldName, type: "file", disabled: disabled, multiple: extendedField.multiple, accept: extendedField.accept?.join(',') || 'image/*', className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100", ...register(fieldName, {
|
|
102
|
-
required: field.required ? `${
|
|
106
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
103
107
|
}) }));
|
|
104
108
|
case 'location':
|
|
105
109
|
return renderField(_jsxs("div", { className: "space-y-2", children: [_jsx("input", { id: `${fieldName}-lat`, type: "number", placeholder: "Latitude", disabled: disabled, step: "any", className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(`${fieldName}.lat`, {
|
|
@@ -112,23 +116,23 @@ export const FieldFactory = ({ field, methods, disabled = false, }) => {
|
|
|
112
116
|
case 'lookup':
|
|
113
117
|
case 'master_detail':
|
|
114
118
|
return renderField(_jsxs("select", { id: fieldName, disabled: disabled, multiple: extendedField.multiple, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
115
|
-
required: field.required ? `${
|
|
119
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
116
120
|
setValueAs: handleMultipleSelectValue,
|
|
117
|
-
}), children: [!extendedField.multiple && _jsx("option", { value: "", children:
|
|
121
|
+
}), children: [!extendedField.multiple && _jsx("option", { value: "", children: fieldPlaceholder || 'Select an option' }), extendedField.options?.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }));
|
|
118
122
|
case 'user':
|
|
119
123
|
case 'owner':
|
|
120
124
|
return renderField(_jsxs("select", { id: fieldName, disabled: disabled, multiple: extendedField.multiple, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
121
|
-
required: field.required ? `${
|
|
125
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
122
126
|
setValueAs: handleMultipleSelectValue,
|
|
123
|
-
}), children: [!extendedField.multiple && _jsx("option", { value: "", children:
|
|
127
|
+
}), children: [!extendedField.multiple && _jsx("option", { value: "", children: fieldPlaceholder || 'Select user' }), extendedField.options?.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }));
|
|
124
128
|
case 'formula':
|
|
125
129
|
case 'summary':
|
|
126
130
|
case 'auto_number':
|
|
127
131
|
// Read-only computed fields - display as disabled text input
|
|
128
132
|
return renderField(_jsx("input", { id: fieldName, type: "text", disabled: true, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-50 cursor-not-allowed text-gray-600", ...register(fieldName) }));
|
|
129
133
|
case 'object':
|
|
130
|
-
return renderField(_jsx("textarea", { id: fieldName, placeholder:
|
|
131
|
-
required: field.required ? `${
|
|
134
|
+
return renderField(_jsx("textarea", { id: fieldName, placeholder: fieldPlaceholder || 'Enter JSON object...', disabled: disabled, rows: 6, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed font-mono text-sm", ...register(fieldName, {
|
|
135
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
132
136
|
validate: (value) => {
|
|
133
137
|
if (!value)
|
|
134
138
|
return true;
|
|
@@ -143,14 +147,14 @@ export const FieldFactory = ({ field, methods, disabled = false, }) => {
|
|
|
143
147
|
}) }));
|
|
144
148
|
case 'vector':
|
|
145
149
|
// Vector fields are typically read-only or require specialized input
|
|
146
|
-
return renderField(_jsx("input", { id: fieldName, type: "text", placeholder:
|
|
150
|
+
return renderField(_jsx("input", { id: fieldName, type: "text", placeholder: fieldPlaceholder || 'Vector data (read-only)', disabled: true, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-50 cursor-not-allowed text-gray-600", ...register(fieldName) }));
|
|
147
151
|
case 'grid':
|
|
148
152
|
// Grid fields require complex table/grid editor - placeholder for now
|
|
149
153
|
return renderField(_jsx("div", { className: "w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-gray-600", children: _jsx("p", { className: "text-sm", children: "Grid editor not yet implemented" }) }));
|
|
150
154
|
default:
|
|
151
155
|
// Default to text input
|
|
152
|
-
return renderField(_jsx("input", { id: fieldName, type: "text", placeholder:
|
|
153
|
-
required: field.required ? `${
|
|
156
|
+
return renderField(_jsx("input", { id: fieldName, type: "text", placeholder: fieldPlaceholder, disabled: disabled, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed", ...register(fieldName, {
|
|
157
|
+
required: field.required ? `${fieldLabel || fieldName} is required` : false,
|
|
154
158
|
}) }));
|
|
155
159
|
}
|
|
156
160
|
};
|