@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.
Files changed (144) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +32 -0
  3. package/dist/LazyPluginLoader.d.ts +44 -0
  4. package/dist/LazyPluginLoader.js +92 -3
  5. package/dist/SchemaRenderer.d.ts +20 -1
  6. package/dist/SchemaRenderer.js +76 -13
  7. package/dist/components/form/FieldFactory.js +44 -40
  8. package/dist/components/form/FormRenderer.js +45 -3
  9. package/dist/context/ActionContext.d.ts +76 -0
  10. package/dist/context/ActionContext.js +139 -0
  11. package/dist/context/DndContext.d.ts +135 -0
  12. package/dist/context/DndContext.js +120 -0
  13. package/dist/context/NotificationContext.d.ts +113 -0
  14. package/dist/context/NotificationContext.js +130 -0
  15. package/dist/context/ThemeContext.d.ts +94 -0
  16. package/dist/context/ThemeContext.js +215 -0
  17. package/dist/context/index.d.ts +4 -0
  18. package/dist/context/index.js +4 -0
  19. package/dist/hooks/index.d.ts +22 -0
  20. package/dist/hooks/index.js +22 -0
  21. package/dist/hooks/useActionRunner.d.ts +21 -7
  22. package/dist/hooks/useActionRunner.js +27 -7
  23. package/dist/hooks/useAnimation.d.ts +46 -0
  24. package/dist/hooks/useAnimation.js +59 -0
  25. package/dist/hooks/useClientNotifications.d.ts +53 -0
  26. package/dist/hooks/useClientNotifications.js +190 -0
  27. package/dist/hooks/useColumnSummary.d.ts +38 -0
  28. package/dist/hooks/useColumnSummary.js +54 -0
  29. package/dist/hooks/useCrudShortcuts.d.ts +39 -0
  30. package/dist/hooks/useCrudShortcuts.js +96 -0
  31. package/dist/hooks/useDensityMode.d.ts +48 -0
  32. package/dist/hooks/useDensityMode.js +60 -0
  33. package/dist/hooks/useDiscovery.d.ts +78 -0
  34. package/dist/hooks/useDiscovery.js +96 -0
  35. package/dist/hooks/useDynamicApp.d.ts +70 -0
  36. package/dist/hooks/useDynamicApp.js +92 -0
  37. package/dist/hooks/useETagCache.d.ts +75 -0
  38. package/dist/hooks/useETagCache.js +306 -0
  39. package/dist/hooks/useFocusManagement.d.ts +89 -0
  40. package/dist/hooks/useFocusManagement.js +188 -0
  41. package/dist/hooks/useFocusTrap.d.ts +41 -0
  42. package/dist/hooks/useFocusTrap.js +103 -0
  43. package/dist/hooks/useKeyboardShortcuts.d.ts +49 -0
  44. package/dist/hooks/useKeyboardShortcuts.js +86 -0
  45. package/dist/hooks/useNavigationOverlay.d.ts +78 -0
  46. package/dist/hooks/useNavigationOverlay.js +112 -0
  47. package/dist/hooks/useOffline.d.ts +96 -0
  48. package/dist/hooks/useOffline.js +185 -0
  49. package/dist/hooks/usePageTransition.d.ts +60 -0
  50. package/dist/hooks/usePageTransition.js +111 -0
  51. package/dist/hooks/usePageVariables.d.ts +67 -0
  52. package/dist/hooks/usePageVariables.js +109 -0
  53. package/dist/hooks/usePerformance.d.ts +87 -0
  54. package/dist/hooks/usePerformance.js +165 -0
  55. package/dist/hooks/usePerformanceBudget.d.ts +78 -0
  56. package/dist/hooks/usePerformanceBudget.js +209 -0
  57. package/dist/hooks/useReducedMotion.d.ts +28 -0
  58. package/dist/hooks/useReducedMotion.js +48 -0
  59. package/dist/hooks/useSchemaPersistence.d.ts +87 -0
  60. package/dist/hooks/useSchemaPersistence.js +173 -0
  61. package/dist/hooks/useViewData.d.ts +73 -0
  62. package/dist/hooks/useViewData.js +135 -0
  63. package/dist/hooks/useViewSharing.d.ts +57 -0
  64. package/dist/hooks/useViewSharing.js +74 -0
  65. package/dist/hooks/useViewTransition.d.ts +54 -0
  66. package/dist/hooks/useViewTransition.js +83 -0
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.js +2 -0
  69. package/dist/utils/i18n.d.ts +10 -0
  70. package/dist/utils/i18n.js +12 -0
  71. package/package.json +7 -5
  72. package/src/LazyPluginLoader.tsx +129 -7
  73. package/src/SchemaRenderer.tsx +122 -13
  74. package/src/__tests__/LazyPluginLoader.test.tsx +97 -1
  75. package/src/__tests__/SchemaRenderer.aria.test.tsx +118 -0
  76. package/src/__tests__/SchemaRenderer.errorBoundary.test.tsx +114 -0
  77. package/src/__tests__/SchemaRendererProvider.smoke.test.tsx +132 -0
  78. package/src/__tests__/ThemeProvider.test.tsx +451 -0
  79. package/src/__tests__/useNavigationOverlay.test.ts +416 -0
  80. package/src/__tests__/useOffline.test.ts +198 -0
  81. package/src/__tests__/usePageTransition.test.ts +214 -0
  82. package/src/__tests__/usePageVariables.test.tsx +334 -0
  83. package/src/__tests__/usePerformance.test.ts +137 -0
  84. package/src/__tests__/useViewData.test.tsx +422 -0
  85. package/src/__tests__/view-switching.test.tsx +456 -0
  86. package/src/components/form/FieldFactory.tsx +51 -46
  87. package/src/components/form/FormRenderer.test.tsx +145 -0
  88. package/src/components/form/FormRenderer.tsx +52 -3
  89. package/src/context/ActionContext.tsx +213 -0
  90. package/src/context/DndContext.tsx +260 -0
  91. package/src/context/NotificationContext.tsx +280 -0
  92. package/src/context/ThemeContext.tsx +325 -0
  93. package/src/context/__tests__/ActionContext.test.tsx +174 -0
  94. package/src/context/__tests__/DndContext.test.tsx +150 -0
  95. package/src/context/__tests__/NotificationContext.test.tsx +176 -0
  96. package/src/context/index.ts +4 -0
  97. package/src/hooks/__tests__/useActionRunner.test.ts +153 -0
  98. package/src/hooks/__tests__/useAnimation.test.ts +82 -0
  99. package/src/hooks/__tests__/useColumnSummary.test.ts +93 -0
  100. package/src/hooks/__tests__/useDensityMode.test.ts +81 -0
  101. package/src/hooks/__tests__/useDiscovery.test.tsx +149 -0
  102. package/src/hooks/__tests__/useDynamicApp.test.ts +161 -0
  103. package/src/hooks/__tests__/useETagCache.test.ts +153 -0
  104. package/src/hooks/__tests__/useExpression.test.ts +86 -0
  105. package/src/hooks/__tests__/useFocusTrap.test.ts +47 -0
  106. package/src/hooks/__tests__/useKeyboardShortcuts.test.ts +144 -0
  107. package/src/hooks/__tests__/usePerformanceBudget.test.ts +157 -0
  108. package/src/hooks/__tests__/useSchemaPersistence.test.ts +220 -0
  109. package/src/hooks/__tests__/useViewSharing.test.ts +121 -0
  110. package/src/hooks/__tests__/useViewTransition.test.ts +61 -0
  111. package/src/hooks/index.ts +22 -1
  112. package/src/hooks/useActionRunner.ts +49 -11
  113. package/src/hooks/useAnimation.ts +117 -0
  114. package/src/hooks/useClientNotifications.ts +251 -0
  115. package/src/hooks/useColumnSummary.ts +87 -0
  116. package/src/hooks/useCrudShortcuts.ts +126 -0
  117. package/src/hooks/useDensityMode.ts +102 -0
  118. package/src/hooks/useDiscovery.ts +141 -0
  119. package/src/hooks/useDynamicApp.ts +142 -0
  120. package/src/hooks/useETagCache.ts +404 -0
  121. package/src/hooks/useFocusManagement.ts +248 -0
  122. package/src/hooks/useFocusTrap.ts +138 -0
  123. package/src/hooks/useKeyboardShortcuts.ts +119 -0
  124. package/src/hooks/useNavigationOverlay.ts +180 -0
  125. package/src/hooks/useOffline.ts +301 -0
  126. package/src/hooks/usePageTransition.ts +187 -0
  127. package/src/hooks/usePageVariables.tsx +157 -0
  128. package/src/hooks/usePerformance.ts +246 -0
  129. package/src/hooks/usePerformanceBudget.ts +313 -0
  130. package/src/hooks/useReducedMotion.ts +53 -0
  131. package/src/hooks/useSchemaPersistence.ts +222 -0
  132. package/src/hooks/useViewData.ts +201 -0
  133. package/src/hooks/useViewSharing.ts +140 -0
  134. package/src/hooks/useViewTransition.ts +144 -0
  135. package/src/index.ts +21 -0
  136. package/src/utils/i18n.ts +10 -0
  137. package/tsconfig.json +1 -0
  138. package/tsconfig.tsbuildinfo +1 -1
  139. package/dist/__tests__/LazyPluginLoader.test.d.ts +0 -8
  140. package/dist/__tests__/LazyPluginLoader.test.js +0 -49
  141. package/dist/components/form/FormRenderer.test.d.ts +0 -8
  142. package/dist/components/form/FormRenderer.test.js +0 -534
  143. package/dist/index.test.d.ts +0 -8
  144. package/dist/index.test.js +0 -13
@@ -1,4 +1,4 @@
1
1
 
2
- > @object-ui/react@0.5.0 build /home/runner/work/objectui/objectui/packages/react
2
+ > @object-ui/react@3.0.0 build /home/runner/work/objectui/objectui/packages/react
3
3
  > tsc
4
4
 
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>;
@@ -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 LazyComponent = lazy(importFn);
33
- const PluginWrapper = (props) => (_jsx(Suspense, { fallback: options?.fallback || null, children: _jsx(LazyComponent, { ...props }) }));
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
+ }
@@ -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 {};
@@ -1,4 +1,4 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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
- return (_jsxs("div", { className: "p-4 border border-red-500 rounded text-red-500 bg-red-50 my-2", children: ["Unknown component type: ", _jsx("strong", { children: evaluatedSchema.type }), _jsx("pre", { className: "text-xs mt-2 overflow-auto", children: JSON.stringify(evaluatedSchema, null, 2) })] }));
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
- return React.createElement(Component, {
55
- schema: evaluatedSchema,
56
- ...componentProps, // Spread non-metadata schema properties as props
57
- ...(evaluatedSchema.props || {}), // Override with explicit props if provided
58
- className: evaluatedSchema.className,
59
- 'data-obj-id': evaluatedSchema.id,
60
- 'data-obj-type': evaluatedSchema.type,
61
- ...props
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
- // Note: visibleOn expression evaluation is not yet implemented
23
- // Fields are always visible unless explicitly hidden
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: [field.label && (_jsxs("label", { htmlFor: fieldName, className: "block text-sm font-medium text-gray-700", children: [field.label, field.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] })), input, field.helpText && (_jsx("p", { className: "text-sm text-gray-500", children: field.helpText })), error && (_jsx("p", { className: "text-sm text-red-600", children: error.message }))] }));
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: field.placeholder, 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, {
39
- required: field.required ? `${field.label || fieldName} is required` : false,
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: field.placeholder, 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, {
45
- required: field.required ? `${field.label || fieldName} is required` : false,
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: [field.label && (_jsxs("label", { htmlFor: fieldName, className: "text-sm font-medium text-gray-700", children: [field.label, field.required && _jsx("span", { className: "text-red-500 ml-1", children: "*" })] })), field.helpText && (_jsx("p", { className: "text-sm text-gray-500 mt-1", children: field.helpText })), error && (_jsx("p", { className: "text-sm text-red-600 mt-1", children: error.message }))] })] }));
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: field.placeholder, 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, {
53
- required: field.required ? `${field.label || fieldName} is required` : false,
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 ? `${field.label || fieldName} is required` : false,
62
+ required: field.required ? `${fieldLabel || fieldName} is required` : false,
59
63
  setValueAs: handleMultipleSelectValue,
60
- }), children: [!extendedField.multiple && _jsx("option", { value: "", children: field.placeholder || 'Select an option' }), extendedField.options?.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }));
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: field.placeholder, 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, {
63
- required: field.required ? `${field.label || fieldName} is required` : false,
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: field.placeholder, 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, {
68
- required: field.required ? `${field.label || fieldName} is required` : false,
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: field.placeholder, 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 ? `${field.label || fieldName} is required` : false,
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: field.placeholder, 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, {
76
- required: field.required ? `${field.label || fieldName} is required` : false,
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: field.placeholder, 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, {
81
- required: field.required ? `${field.label || fieldName} is required` : false,
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: field.placeholder || '+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, {
86
- required: field.required ? `${field.label || fieldName} is required` : false,
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: field.placeholder || '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, {
90
- required: field.required ? `${field.label || fieldName} is required` : false,
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: field.placeholder || '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, {
94
- required: field.required ? `${field.label || fieldName} is required` : false,
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 ? `${field.label || fieldName} is required` : false,
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 ? `${field.label || fieldName} is required` : false,
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 ? `${field.label || fieldName} is required` : false,
119
+ required: field.required ? `${fieldLabel || fieldName} is required` : false,
116
120
  setValueAs: handleMultipleSelectValue,
117
- }), children: [!extendedField.multiple && _jsx("option", { value: "", children: field.placeholder || 'Select an option' }), extendedField.options?.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }));
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 ? `${field.label || fieldName} is required` : false,
125
+ required: field.required ? `${fieldLabel || fieldName} is required` : false,
122
126
  setValueAs: handleMultipleSelectValue,
123
- }), children: [!extendedField.multiple && _jsx("option", { value: "", children: field.placeholder || 'Select user' }), extendedField.options?.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }));
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: field.placeholder || '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, {
131
- required: field.required ? `${field.label || fieldName} is required` : false,
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: field.placeholder || '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) }));
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: field.placeholder, 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, {
153
- required: field.required ? `${field.label || fieldName} is required` : false,
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
  };