extended-dynamic-forms 0.2.0 → 0.2.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.
@@ -1,6 +1,7 @@
1
1
  import { FormProps } from '@rjsf/core';
2
2
  import { RJSFSchema, UiSchema } from '@rjsf/utils';
3
3
 
4
+ export type WebhookEventType = 'focus' | 'blur' | 'change' | 'submit' | 'stepChange' | 'validationError';
4
5
  export interface FormEventHandlers {
5
6
  beforeSubmit?: (data: any) => Promise<any> | any;
6
7
  afterSubmit?: (data: any) => Promise<void> | void;
@@ -20,7 +21,7 @@ export interface WebhookConfig {
20
21
  url: string;
21
22
  method?: 'POST' | 'PUT' | 'PATCH';
22
23
  headers?: Record<string, string>;
23
- events?: ('focus' | 'blur' | 'change')[];
24
+ events?: WebhookEventType[];
24
25
  debounceMs?: number;
25
26
  retries?: number;
26
27
  timeout?: number;
@@ -34,6 +35,10 @@ export interface ExtendedFormProps extends Omit<FormProps, 'validator'>, FormEve
34
35
  schema: RJSFSchema;
35
36
  uiSchema?: UiSchema;
36
37
  webhooks?: WebhookConfig[];
38
+ eventHub?: any;
39
+ onWebhookStart?: (webhook: WebhookConfig, event: any) => void;
40
+ onWebhookComplete?: (webhook: WebhookConfig, event: any, response: any) => void;
41
+ onWebhookError?: (webhook: WebhookConfig, event: any, error: Error) => void;
37
42
  }
38
43
  export interface CustomWidgetProps {
39
44
  label?: string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Utility for detecting field-level changes in form data
3
+ */
4
+ export interface FieldChange {
5
+ fieldId: string;
6
+ fieldPath: string[];
7
+ oldValue: any;
8
+ newValue: any;
9
+ }
10
+ /**
11
+ * Deep comparison to find changed fields between two form data objects
12
+ */
13
+ export declare function detectFieldChanges(oldData: any, newData: any, parentPath?: string[]): FieldChange[];
14
+ /**
15
+ * Convert field path to RJSF field ID format
16
+ * RJSF uses a specific format like "root_fieldName_0_subField"
17
+ */
18
+ export declare function pathToFieldId(path: string[], idPrefix?: string): string;
19
+ /**
20
+ * Extract schema for a specific field path
21
+ */
22
+ export declare function getFieldSchema(schema: any, path: string[]): any | undefined;
@@ -1,6 +1,7 @@
1
1
  import { default as React } from 'react';
2
2
  import { default as ReactDOM } from 'react-dom/client';
3
3
  import { default as dayjs } from 'dayjs';
4
+ import { default as jsonLogic } from 'json-logic-js';
4
5
 
5
6
  import * as antd from 'antd';
6
7
  import * as ExtendedDynamicFormsExports from './index';
@@ -52,6 +53,32 @@ declare const ExtendedDynamicForms: {
52
53
  file: string;
53
54
  hidden: string;
54
55
  };
56
+ jsonLogic: {
57
+ apply: typeof jsonLogic.apply;
58
+ add_operation: typeof jsonLogic.add_operation;
59
+ rm_operation: typeof jsonLogic.rm_operation;
60
+ equals: (field: string, value: any) => {
61
+ "==": any[];
62
+ };
63
+ and: (...conditions: any[]) => {
64
+ and: any[];
65
+ };
66
+ or: (...conditions: any[]) => {
67
+ or: any[];
68
+ };
69
+ not: (condition: any) => {
70
+ "!": any;
71
+ };
72
+ greaterThan: (field: string, value: any) => {
73
+ ">": any[];
74
+ };
75
+ lessThan: (field: string, value: any) => {
76
+ "<": any[];
77
+ };
78
+ contains: (array: string, value: any) => {
79
+ in: any[];
80
+ };
81
+ };
55
82
  };
56
83
  validator: import('@rjsf/validator-ajv8/lib/validator.js').default<any, ExtendedDynamicFormsExports.RJSFSchema, any>;
57
84
  ExtendedForm: React.ForwardRefExoticComponent<Omit<ExtendedDynamicFormsExports.ExtendedFormProps, "ref"> & React.RefAttributes<any>>;
@@ -200,5 +227,6 @@ declare const ExtendedDynamicForms: {
200
227
  ReactDOM: typeof ReactDOM;
201
228
  antd: typeof antd;
202
229
  dayjs: typeof dayjs;
230
+ jsonLogic: typeof jsonLogic;
203
231
  };
204
232
  export default ExtendedDynamicForms;
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "extended-dynamic-forms",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Extended React JSON Schema Form (RJSF) v6 with custom components, widgets, templates, layouts, and form events",
5
5
  "main": "dist/index.umd.js",
6
6
  "module": "dist/index.es.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
- "examples",
11
10
  "LICENSE",
12
11
  "README.md"
13
12
  ],
@@ -41,10 +40,10 @@
41
40
  "prepublishOnly": "npm run build",
42
41
  "playground": "cd playground && npm install && npm run dev",
43
42
  "playground:build": "cd playground && npm run build",
44
- "serve:examples": "npx http-server examples/vanilla-js -p 8080 -o",
45
43
  "setup:dev": "git clone https://github.com/rjsf-team/react-jsonschema-form ../react-jsonschema-form && cd ../react-jsonschema-form && npm install && npm run build",
46
44
  "link:rjsf": "cd ../react-jsonschema-form/packages/core && npm link && cd ../antd && npm link && cd ../utils && npm link && cd ../validator-ajv8 && npm link",
47
- "dev:install": "npm run setup:dev && npm run link:rjsf && npm install"
45
+ "dev:install": "npm run setup:dev && npm run link:rjsf && npm install",
46
+ "generate:widget": "plop --plopfile plopfile.js --dest ."
48
47
  },
49
48
  "keywords": [
50
49
  "react",
@@ -93,6 +92,7 @@
93
92
  "eslint-plugin-react-hooks": "^4.6.0",
94
93
  "eslint-plugin-react-refresh": "^0.4.5",
95
94
  "jsdom": "^26.1.0",
95
+ "plop": "^4.0.1",
96
96
  "prettier": "^3.5.3",
97
97
  "react": "^18.2.0",
98
98
  "react-dom": "^18.2.0",
@@ -1,64 +0,0 @@
1
- import React from 'react';
2
- import { ExtendedForm } from '../src/ExtendedForm';
3
- import { RJSFSchema, UiSchema } from '@rjsf/utils';
4
- import { useConditionalUi, UiRule, JsonLogicRule } from '../src/conditionals/v2';
5
-
6
- const schema: RJSFSchema = {
7
- type: 'object',
8
- properties: {
9
- requiresShipping: {
10
- type: 'boolean',
11
- title: 'Requires Shipping?'
12
- },
13
- shippingAddress: {
14
- type: 'object',
15
- title: 'Shipping Address',
16
- properties: {
17
- street: { type: 'string', title: 'Street' },
18
- city: { type: 'string', title: 'City' },
19
- zip: { type: 'string', title: 'ZIP Code' }
20
- }
21
- }
22
- }
23
- };
24
-
25
- const baseUiSchema: UiSchema = {
26
- requiresShipping: {
27
- 'ui:widget': 'checkbox'
28
- },
29
- shippingAddress: {
30
- street: { 'ui:widget': 'text' },
31
- city: { 'ui:widget': 'text' },
32
- zip: { 'ui:widget': 'text' }
33
- }
34
- };
35
-
36
- export function ConditionalV2Example() {
37
- const [formData, setFormData] = React.useState({});
38
-
39
- // Define UI rules
40
- const uiRules: UiRule[] = [
41
- {
42
- name: 'Hide shipping address when not required',
43
- condition: { "!": { "var": "requiresShipping" } } as JsonLogicRule,
44
- effect: [
45
- { op: 'add', path: '/shippingAddress/ui:widget', value: 'hidden' }
46
- ]
47
- }
48
- ];
49
-
50
- // Apply conditional UI logic
51
- const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
52
-
53
- return (
54
- <div>
55
- <h1>Conditional V2 Example - Three Pillars Architecture</h1>
56
- <ExtendedForm
57
- schema={schema}
58
- uiSchema={conditionalUiSchema}
59
- formData={formData}
60
- onChange={({ formData }) => setFormData(formData)}
61
- />
62
- </div>
63
- );
64
- }
@@ -1,211 +0,0 @@
1
- import React from 'react';
2
- import { ExtendedForm } from '../src/ExtendedForm';
3
- import { RJSFSchema, UiSchema } from '@rjsf/utils';
4
- import {
5
- createDynamicArrayItemsUiSchema,
6
- arrayItemPatterns
7
- } from '../src/conditionals/v2/utils/dynamicArrayItems';
8
-
9
- const schema: RJSFSchema = {
10
- type: 'object',
11
- properties: {
12
- directors: {
13
- type: 'array',
14
- title: 'Company Directors',
15
- items: {
16
- type: 'object',
17
- required: ['name', 'directorType'],
18
- properties: {
19
- name: { type: 'string', title: 'Name' },
20
- directorType: {
21
- type: 'string',
22
- title: 'Director Type',
23
- enum: ['executive', 'non-executive'],
24
- enumNames: ['Executive', 'Non-Executive']
25
- },
26
- executiveRole: {
27
- type: 'string',
28
- title: 'Executive Role',
29
- enum: ['CEO', 'CFO', 'CTO', 'COO', 'Other'],
30
- enumNames: ['CEO', 'CFO', 'CTO', 'COO', 'Other']
31
- },
32
- otherRoleDetails: {
33
- type: 'string',
34
- title: 'Other Role Details'
35
- },
36
- hasShareholding: { type: 'boolean', title: 'Has Shareholding?' },
37
- shareholdingPercentage: {
38
- type: 'number',
39
- title: 'Shareholding %',
40
- minimum: 0,
41
- maximum: 100
42
- }
43
- }
44
- }
45
- }
46
- }
47
- };
48
-
49
- // Base UI schema for all array items
50
- const baseItemUiSchema: UiSchema = {
51
- 'ui:order': ['name', 'directorType', 'executiveRole', 'otherRoleDetails', 'hasShareholding', 'shareholdingPercentage'],
52
- name: {
53
- 'ui:widget': 'text',
54
- 'ui:placeholder': 'Enter director name'
55
- },
56
- directorType: {
57
- 'ui:widget': 'select'
58
- },
59
- executiveRole: {
60
- 'ui:widget': 'select'
61
- },
62
- otherRoleDetails: {
63
- 'ui:widget': 'text',
64
- 'ui:placeholder': 'Specify other role'
65
- },
66
- hasShareholding: {
67
- 'ui:widget': 'checkbox'
68
- },
69
- shareholdingPercentage: {
70
- 'ui:widget': 'updown'
71
- }
72
- };
73
-
74
- // Create dynamic UI schema using the utility
75
- const uiSchema: UiSchema = {
76
- directors: {
77
- items: createDynamicArrayItemsUiSchema(
78
- baseItemUiSchema,
79
- (itemData, index) => {
80
- const conditionalUi: Partial<UiSchema> = {};
81
-
82
- // Three-level conditional dependency chain
83
- // Level 1: Director type determines if executive role is shown
84
- if (itemData?.directorType !== 'executive') {
85
- conditionalUi.executiveRole = { 'ui:widget': 'hidden' };
86
- // Also hide dependent field since parent is hidden
87
- conditionalUi.otherRoleDetails = { 'ui:widget': 'hidden' };
88
- } else {
89
- // Level 2: Executive role determines if other role details is shown
90
- if (itemData?.executiveRole !== 'Other') {
91
- conditionalUi.otherRoleDetails = { 'ui:widget': 'hidden' };
92
- }
93
- }
94
-
95
- // Hide shareholding percentage if not a shareholder
96
- if (!itemData?.hasShareholding) {
97
- conditionalUi.shareholdingPercentage = { 'ui:widget': 'hidden' };
98
- }
99
-
100
- // Add special styling for first director
101
- if (index === 0) {
102
- conditionalUi['ui:classNames'] = 'primary-director';
103
- conditionalUi['ui:title'] = 'Primary Director';
104
- }
105
-
106
- return conditionalUi;
107
- }
108
- )
109
- }
110
- };
111
-
112
- export function DynamicArrayItemsExample() {
113
- const [formData, setFormData] = React.useState({
114
- directors: [
115
- {
116
- name: 'John Doe',
117
- directorType: 'executive',
118
- executiveRole: 'CEO',
119
- hasShareholding: true,
120
- shareholdingPercentage: 51
121
- },
122
- {
123
- name: 'Jane Smith',
124
- directorType: 'executive',
125
- executiveRole: 'Other',
126
- otherRoleDetails: 'Head of Innovation',
127
- hasShareholding: true,
128
- shareholdingPercentage: 30
129
- },
130
- {
131
- name: 'Bob Johnson',
132
- directorType: 'non-executive',
133
- hasShareholding: false
134
- }
135
- ]
136
- });
137
-
138
- return (
139
- <div>
140
- <h1>Dynamic Array Items Example</h1>
141
- <p>
142
- This example demonstrates O(n) performance for array conditionals using
143
- the RJSF fork's dynamic uiSchema.items capability. It showcases:
144
- </p>
145
- <ul>
146
- <li>Three-level conditional dependencies (Director Type → Executive Role → Other Role Details)</li>
147
- <li>Enum-based conditions for more realistic form patterns</li>
148
- <li>The common "Other" option pattern with additional text field</li>
149
- <li>Multiple independent conditionals within each array item</li>
150
- </ul>
151
-
152
- <ExtendedForm
153
- schema={schema}
154
- uiSchema={uiSchema}
155
- formData={formData}
156
- onChange={({ formData }) => setFormData(formData)}
157
- />
158
-
159
- <div style={{ marginTop: '20px' }}>
160
- <h3>Performance Benefits:</h3>
161
- <ul>
162
- <li>Each array item's UI is calculated independently</li>
163
- <li>No wildcard pattern expansion needed</li>
164
- <li>O(n) complexity instead of O(n²)</li>
165
- <li>Scales efficiently with large arrays</li>
166
- </ul>
167
- </div>
168
- </div>
169
- );
170
- }
171
-
172
- // Alternative example using helper patterns
173
- export function DynamicArrayPatternsExample() {
174
- const uiSchemaWithPatterns: UiSchema = {
175
- directors: {
176
- items: createDynamicArrayItemsUiSchema(
177
- baseItemUiSchema,
178
- (itemData, index) => {
179
- // Combine multiple pattern helpers for cleaner code
180
- const hideShareholding = arrayItemPatterns.hideField(
181
- 'shareholdingPercentage',
182
- (data) => !data?.hasShareholding
183
- )(itemData, index);
184
-
185
- const hideExecutiveRole = arrayItemPatterns.hideField(
186
- 'executiveRole',
187
- (data) => data?.directorType !== 'executive'
188
- )(itemData, index);
189
-
190
- const hideOtherRoleDetails = arrayItemPatterns.hideField(
191
- 'otherRoleDetails',
192
- (data) => data?.directorType !== 'executive' || data?.executiveRole !== 'Other'
193
- )(itemData, index);
194
-
195
- return {
196
- ...hideShareholding,
197
- ...hideExecutiveRole,
198
- ...hideOtherRoleDetails
199
- };
200
- }
201
- )
202
- }
203
- };
204
-
205
- return (
206
- <ExtendedForm
207
- schema={schema}
208
- uiSchema={uiSchemaWithPatterns}
209
- />
210
- );
211
- }