@object-ui/plugin-form 3.3.0 → 3.3.1

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/src/autoLayout.ts DELETED
@@ -1,166 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- /**
10
- * Auto-Layout for ObjectForm
11
- *
12
- * Provides intelligent, zero-configuration default layout for metadata-driven forms.
13
- * When the user has not explicitly set columns/colSpan/sections, this module
14
- * infers optimal layout based on field count and field types.
15
- *
16
- * Priority: User configuration > Auto-layout inference
17
- */
18
-
19
- import type { FormField } from '@object-ui/types';
20
-
21
- /** Field types that should span full width in multi-column layouts */
22
- const WIDE_FIELD_TYPES = new Set([
23
- 'field:textarea',
24
- 'field:markdown',
25
- 'field:html',
26
- 'field:grid',
27
- 'field:rich-text',
28
- 'textarea',
29
- 'markdown',
30
- 'html',
31
- 'grid',
32
- 'rich-text',
33
- ]);
34
-
35
- /** Object field types that are auto-generated and should be hidden in create mode */
36
- const AUTO_GENERATED_FIELD_TYPES = new Set([
37
- 'formula',
38
- 'summary',
39
- 'auto_number',
40
- 'autonumber',
41
- ]);
42
-
43
- /**
44
- * Check if a field type is "wide" (should span full row in multi-column layout).
45
- */
46
- export function isWideFieldType(type: string): boolean {
47
- return WIDE_FIELD_TYPES.has(type);
48
- }
49
-
50
- /**
51
- * Check if an object field type is auto-generated (formula, summary, auto_number).
52
- */
53
- export function isAutoGeneratedFieldType(type: string): boolean {
54
- return AUTO_GENERATED_FIELD_TYPES.has(type);
55
- }
56
-
57
- /**
58
- * Infer optimal number of columns based on the number of visible fields
59
- * and whether any wide fields are present.
60
- *
61
- * Rules:
62
- * - 0-3 fields → 1 column
63
- * - 4+ fields → 2 columns
64
- */
65
- export function inferColumns(fieldCount: number): number {
66
- if (fieldCount <= 3) return 1;
67
- return 2;
68
- }
69
-
70
- /**
71
- * Apply colSpan to wide fields so they span the full row.
72
- * Only sets colSpan if the field does not already have one explicitly set.
73
- *
74
- * @returns A new array of fields with colSpan applied where needed.
75
- */
76
- export function applyAutoColSpan(fields: FormField[], columns: number): FormField[] {
77
- if (columns <= 1) return fields;
78
-
79
- return fields.map(field => {
80
- // User-defined colSpan takes priority
81
- if (field.colSpan !== undefined) return field;
82
-
83
- // Wide field types should span full row
84
- if (field.type && isWideFieldType(field.type)) {
85
- return { ...field, colSpan: columns };
86
- }
87
-
88
- return field;
89
- });
90
- }
91
-
92
- /**
93
- * Filter out auto-generated/readonly fields for create mode.
94
- * These fields (formula, summary, auto_number) are computed server-side
95
- * and should not appear in create forms.
96
- *
97
- * @param fields - The form fields array
98
- * @param objectSchema - The object schema with original field metadata
99
- * @returns Filtered fields array
100
- */
101
- export function filterCreateModeFields(
102
- fields: FormField[],
103
- objectSchema: any
104
- ): FormField[] {
105
- if (!objectSchema?.fields) return fields;
106
-
107
- return fields.filter(field => {
108
- const objField = objectSchema.fields[field.name];
109
- if (!objField) return true; // keep fields not in schema (custom fields)
110
-
111
- return !isAutoGeneratedFieldType(objField.type);
112
- });
113
- }
114
-
115
- /**
116
- * Infer an appropriate modal size based on the number of layout columns.
117
- * Used to auto-upgrade the modal width when auto-layout detects multi-column forms.
118
- *
119
- * Mapping:
120
- * - 1 column → 'default' (max-w-lg)
121
- * - 2 columns → 'xl' (max-w-5xl)
122
- * - 3+ columns → 'full' (max-w-[95vw])
123
- */
124
- export function inferModalSize(columns: number): 'sm' | 'default' | 'lg' | 'xl' | 'full' {
125
- if (columns <= 1) return 'default';
126
- if (columns === 2) return 'xl';
127
- return 'full';
128
- }
129
-
130
- /**
131
- * Main auto-layout orchestrator.
132
- * Applies intelligent defaults only when the user has not explicitly configured layout.
133
- *
134
- * @param formFields - Generated form fields
135
- * @param objectSchema - The original object schema (with field types)
136
- * @param schemaColumns - User-provided columns (from ObjectFormSchema)
137
- * @param mode - Form mode ('create' | 'edit' | 'view')
138
- * @returns Object with processed fields and inferred columns
139
- */
140
- export function applyAutoLayout(
141
- formFields: FormField[],
142
- objectSchema: any,
143
- schemaColumns: number | undefined,
144
- mode: string | undefined
145
- ): { fields: FormField[]; columns: number | undefined } {
146
- let fields = [...formFields];
147
-
148
- // Step 1: Filter auto-generated fields in create mode
149
- if (mode === 'create') {
150
- fields = filterCreateModeFields(fields, objectSchema);
151
- }
152
-
153
- // Step 2: If user explicitly set columns, respect it but still apply auto colSpan
154
- if (schemaColumns !== undefined) {
155
- fields = applyAutoColSpan(fields, schemaColumns);
156
- return { fields, columns: schemaColumns };
157
- }
158
-
159
- // Step 3: Infer columns from field count
160
- const columns = inferColumns(fields.length);
161
-
162
- // Step 4: Apply auto colSpan for wide fields
163
- fields = applyAutoColSpan(fields, columns);
164
-
165
- return { fields, columns };
166
- }
package/src/index.tsx DELETED
@@ -1,134 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- import React from 'react';
10
- import { ComponentRegistry } from '@object-ui/core';
11
- import { ObjectForm } from './ObjectForm';
12
-
13
- export { ObjectForm };
14
- export type { ObjectFormProps } from './ObjectForm';
15
- export { FormSection } from './FormSection';
16
- export type { FormSectionProps } from './FormSection';
17
- export {
18
- applyAutoLayout,
19
- inferColumns,
20
- inferModalSize,
21
- isWideFieldType,
22
- isAutoGeneratedFieldType,
23
- applyAutoColSpan,
24
- filterCreateModeFields,
25
- } from './autoLayout';
26
- export { TabbedForm } from './TabbedForm';
27
- export type { TabbedFormProps, TabbedFormSchema, FormSectionConfig } from './TabbedForm';
28
- export { WizardForm } from './WizardForm';
29
- export type { WizardFormProps, WizardFormSchema } from './WizardForm';
30
- export { SplitForm } from './SplitForm';
31
- export type { SplitFormProps, SplitFormSchema } from './SplitForm';
32
- export { DrawerForm } from './DrawerForm';
33
- export type { DrawerFormProps, DrawerFormSchema } from './DrawerForm';
34
- export { ModalForm } from './ModalForm';
35
- export type { ModalFormProps, ModalFormSchema } from './ModalForm';
36
- export { EmbeddableForm } from './EmbeddableForm';
37
- export type { EmbeddableFormProps, EmbeddableFormConfig } from './EmbeddableForm';
38
- export { FormAnalytics } from './FormAnalytics';
39
- export type { FormAnalyticsProps, FormSubmissionMetric } from './FormAnalytics';
40
-
41
- // Register object-form component
42
- const ObjectFormRenderer: React.FC<{ schema: any }> = ({ schema }) => {
43
- return <ObjectForm schema={schema} />;
44
- };
45
-
46
- ComponentRegistry.register('object-form', ObjectFormRenderer, {
47
- namespace: 'plugin-form',
48
- label: 'Object Form',
49
- category: 'plugin',
50
- inputs: [
51
- { name: 'objectName', type: 'string', label: 'Object Name', required: true },
52
- { name: 'fields', type: 'array', label: 'Fields' },
53
- { name: 'mode', type: 'enum', label: 'Mode', enum: ['create', 'edit', 'view'] },
54
- { name: 'formType', type: 'enum', label: 'Form Type', enum: ['simple', 'tabbed', 'wizard', 'split', 'drawer', 'modal'] },
55
- { name: 'sections', type: 'array', label: 'Sections' },
56
- { name: 'title', type: 'string', label: 'Title' },
57
- { name: 'description', type: 'string', label: 'Description' },
58
- { name: 'layout', type: 'enum', label: 'Layout', enum: ['vertical', 'horizontal', 'inline', 'grid'] },
59
- { name: 'columns', type: 'number', label: 'Columns' },
60
- // Tabbed
61
- { name: 'defaultTab', type: 'string', label: 'Default Tab' },
62
- { name: 'tabPosition', type: 'enum', label: 'Tab Position', enum: ['top', 'bottom', 'left', 'right'] },
63
- // Wizard
64
- { name: 'allowSkip', type: 'boolean', label: 'Allow Skip Steps' },
65
- { name: 'showStepIndicator', type: 'boolean', label: 'Show Step Indicator' },
66
- // Split
67
- { name: 'splitDirection', type: 'enum', label: 'Split Direction', enum: ['horizontal', 'vertical'] },
68
- { name: 'splitSize', type: 'number', label: 'Split Panel Size (%)' },
69
- { name: 'splitResizable', type: 'boolean', label: 'Split Resizable' },
70
- // Drawer
71
- { name: 'drawerSide', type: 'enum', label: 'Drawer Side', enum: ['top', 'bottom', 'left', 'right'] },
72
- { name: 'drawerWidth', type: 'string', label: 'Drawer Width' },
73
- // Modal
74
- { name: 'modalSize', type: 'enum', label: 'Modal Size', enum: ['sm', 'default', 'lg', 'xl', 'full'] },
75
- ]
76
- });
77
-
78
- // Register as view:form for the standard view protocol
79
- // This allows using { type: 'view:form', objectName: '...' } in schemas
80
- // skipFallback prevents overwriting the basic 'form' component from @object-ui/components
81
- ComponentRegistry.register('form', ObjectFormRenderer, {
82
- namespace: 'view',
83
- skipFallback: true,
84
- label: 'Data Form View',
85
- category: 'view',
86
- inputs: [
87
- { name: 'objectName', type: 'string', label: 'Object Name', required: true },
88
- { name: 'fields', type: 'array', label: 'Fields' },
89
- { name: 'mode', type: 'enum', label: 'Mode', enum: ['create', 'edit', 'view'] },
90
- ]
91
- });
92
-
93
- // Note: 'form' type (without namespace) is handled by @object-ui/components Form component
94
- // This plugin registers as 'view:form' (with view namespace) for the view protocol
95
- // ObjectForm internally uses { type: 'form' } to render the basic Form component
96
-
97
- // Register embeddable-form component for standalone public forms
98
- import { EmbeddableForm } from './EmbeddableForm';
99
-
100
- const EmbeddableFormRenderer: React.FC<{ schema: any }> = ({ schema }) => {
101
- return <EmbeddableForm config={schema} />;
102
- };
103
-
104
- ComponentRegistry.register('embeddable-form', EmbeddableFormRenderer, {
105
- namespace: 'plugin-form',
106
- label: 'Embeddable Form',
107
- category: 'plugin',
108
- inputs: [
109
- { name: 'formId', type: 'string', label: 'Form ID', required: true },
110
- { name: 'objectName', type: 'string', label: 'Object Name', required: true },
111
- { name: 'title', type: 'string', label: 'Form Title' },
112
- { name: 'description', type: 'string', label: 'Description' },
113
- { name: 'fields', type: 'array', label: 'Fields' },
114
- { name: 'allowMultiple', type: 'boolean', label: 'Allow Multiple Submissions' },
115
- ]
116
- });
117
-
118
- // Register form-analytics component for submission dashboards
119
- import { FormAnalytics } from './FormAnalytics';
120
-
121
- const FormAnalyticsRenderer: React.FC<{ schema: any }> = ({ schema }) => {
122
- return <FormAnalytics formId={schema.formId} formTitle={schema.formTitle} metrics={schema.metrics || { totalSubmissions: 0 }} />;
123
- };
124
-
125
- ComponentRegistry.register('form-analytics', FormAnalyticsRenderer, {
126
- namespace: 'plugin-form',
127
- label: 'Form Analytics',
128
- category: 'plugin',
129
- inputs: [
130
- { name: 'formId', type: 'string', label: 'Form ID', required: true },
131
- { name: 'formTitle', type: 'string', label: 'Form Title' },
132
- { name: 'metrics', type: 'object', label: 'Submission Metrics' },
133
- ]
134
- });
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "jsx": "react-jsx"
6
- },
7
- "include": ["src"],
8
- "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx", "**/*.stories.tsx"]
9
- }
package/vite.config.ts DELETED
@@ -1,58 +0,0 @@
1
- import { defineConfig } from 'vite';
2
- import react from '@vitejs/plugin-react';
3
- import dts from 'vite-plugin-dts';
4
- import { resolve } from 'path';
5
-
6
- export default defineConfig({
7
- plugins: [
8
- react(),
9
- dts({
10
- insertTypesEntry: true,
11
- compilerOptions: { rootDir: resolve(__dirname, '../..') },
12
- include: ['src'],
13
- }),
14
- ],
15
- resolve: {
16
- alias: {
17
- '@object-ui/core': resolve(__dirname, '../core/src'),
18
- '@object-ui/types': resolve(__dirname, '../types/src'),
19
- '@object-ui/data-objectstack': resolve(__dirname, '../data-objectstack/src'),
20
- '@object-ui/react': resolve(__dirname, '../react/src'),
21
- '@object-ui/components': resolve(__dirname, '../components/src'),
22
- '@object-ui/fields': resolve(__dirname, '../fields/src'),
23
- '@object-ui/plugin-dashboard': resolve(__dirname, '../plugin-dashboard/src'),
24
- '@object-ui/plugin-grid': resolve(__dirname, '../plugin-grid/src'),
25
- }
26
- },
27
- build: {
28
- lib: {
29
- entry: resolve(__dirname, 'src/index.tsx'),
30
- name: 'ObjectUIPluginForm',
31
- fileName: 'index',
32
- },
33
- rollupOptions: {
34
- external: [
35
- 'react',
36
- 'react-dom',
37
- '@object-ui/components',
38
- '@object-ui/core',
39
- '@object-ui/fields',
40
- '@object-ui/react',
41
- '@object-ui/types',
42
- 'lucide-react'
43
- ],
44
- output: {
45
- globals: {
46
- react: 'React',
47
- 'react-dom': 'ReactDOM',
48
- },
49
- },
50
- },
51
- },
52
- test: {
53
- globals: true,
54
- environment: 'happy-dom',
55
- setupFiles: ['../../vitest.setup.tsx'],
56
- passWithNoTests: true,
57
- },
58
- });
package/vitest.config.ts DELETED
@@ -1,12 +0,0 @@
1
- /// <reference types="vitest" />
2
- import { defineConfig } from 'vite';
3
- import react from '@vitejs/plugin-react';
4
-
5
- export default defineConfig({
6
- plugins: [react()],
7
- test: {
8
- environment: 'happy-dom',
9
- globals: true,
10
- setupFiles: ['./vitest.setup.ts'],
11
- },
12
- });
package/vitest.setup.ts DELETED
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';