extended-dynamic-forms 0.1.17 → 0.1.19

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +834 -394
  3. package/dist/extended-dynamic-forms.standalone.js +882 -0
  4. package/dist/style.css +1 -1
  5. package/examples/conditional-v2-example.tsx +64 -0
  6. package/examples/dynamic-array-items-example.tsx +211 -0
  7. package/examples/form-devtools-example.tsx +386 -0
  8. package/examples/global-ui-rules-example.tsx +356 -0
  9. package/examples/vanilla-js/README.md +212 -0
  10. package/examples/vanilla-js/basic-form.html +142 -0
  11. package/examples/vanilla-js/conditional-form.html +159 -0
  12. package/examples/vanilla-js/wizard-form.html +185 -0
  13. package/package.json +40 -47
  14. package/dist/ExtendedForm.d.ts +0 -4
  15. package/dist/WizardForm.d.ts +0 -26
  16. package/dist/components/arrays/ArrayFieldItemTemplate.d.ts +0 -3
  17. package/dist/components/arrays/ArrayFieldTemplate.d.ts +0 -3
  18. package/dist/components/arrays/index.d.ts +0 -2
  19. package/dist/components/fields/AddressField.d.ts +0 -3
  20. package/dist/components/fields/CurrencyField.d.ts +0 -4
  21. package/dist/components/fields/PhoneField.d.ts +0 -4
  22. package/dist/components/index.d.ts +0 -24
  23. package/dist/components/templates/FieldTemplate.d.ts +0 -4
  24. package/dist/components/templates/SubmitButton.d.ts +0 -4
  25. package/dist/components/templates/index.d.ts +0 -2
  26. package/dist/conditionals/ConditionalActions.d.ts +0 -13
  27. package/dist/conditionals/ConditionalContext.d.ts +0 -32
  28. package/dist/conditionals/DynamicArrayConditionalEngine.d.ts +0 -77
  29. package/dist/conditionals/SimpleConditionalEngine.d.ts +0 -41
  30. package/dist/conditionals/SmartJsonRulesTransformer.d.ts +0 -10
  31. package/dist/conditionals/index.d.ts +0 -7
  32. package/dist/events/FormEventContext.d.ts +0 -20
  33. package/dist/events/FormEventHub.d.ts +0 -27
  34. package/dist/events/FormEventManager.d.ts +0 -10
  35. package/dist/events/index.d.ts +0 -4
  36. package/dist/index.d.ts +0 -14
  37. package/dist/index.es.js +0 -44459
  38. package/dist/index.umd.js +0 -399
  39. package/dist/layouts/index.d.ts +0 -2
  40. package/dist/layouts/wizard/ProgressIndicator.d.ts +0 -10
  41. package/dist/layouts/wizard/StepNavigation.d.ts +0 -11
  42. package/dist/layouts/wizard/WizardLayout.d.ts +0 -31
  43. package/dist/layouts/wizard/WizardNavigation.d.ts +0 -16
  44. package/dist/layouts/wizard/WizardObjectFieldTemplate.d.ts +0 -4
  45. package/dist/layouts/wizard/WizardSectionTemplate.d.ts +0 -11
  46. package/dist/layouts/wizard/index.d.ts +0 -7
  47. package/dist/types/index.d.ts +0 -88
  48. package/dist/utils/formDataUtils.d.ts +0 -22
  49. package/dist/utils/index.d.ts +0 -4
  50. package/dist/utils/schemaUtils.d.ts +0 -24
  51. package/dist/utils/validationUtils.d.ts +0 -26
  52. package/dist/vanilla-wrapper.d.ts +0 -33
  53. package/dist/widgets/date/DateWidget.d.ts +0 -4
  54. package/dist/widgets/enhanced/EnhancedWidgets.d.ts +0 -36
  55. package/dist/widgets/file/FileWidget.d.ts +0 -4
  56. package/dist/widgets/index.d.ts +0 -33
  57. package/dist/widgets/input/ColorWidget.d.ts +0 -4
  58. package/dist/widgets/input/RangeWidget.d.ts +0 -4
  59. package/dist/widgets/input/RatingWidget.d.ts +0 -4
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- html,body{width:100%;height:100%}input::-ms-clear,input::-ms-reveal{display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5em;font-weight:500}p{margin-top:0;margin-bottom:1em}abbr[title],abbr[data-original-title]{-webkit-text-decoration:underline dotted;text-decoration:underline;text-decoration:underline dotted;border-bottom:0;cursor:help}address{margin-bottom:1em;font-style:normal;line-height:inherit}input[type=text],input[type=password],input[type=number],textarea{-webkit-appearance:none}ol,ul,dl{margin-top:0;margin-bottom:1em}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}pre,code,kbd,samp{font-size:1em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace}pre{margin-top:0;margin-bottom:1em;overflow:auto}figure{margin:0 0 1em}img{vertical-align:middle;border-style:none}a,area,button,[role=button],input:not([type=range]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75em;padding-bottom:.3em;text-align:left;caption-side:bottom}input,button,select,optgroup,textarea{margin:0;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}button,input{overflow:visible}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{display:block;width:100%;max-width:100%;margin-bottom:.5em;padding:0;color:inherit;font-size:1.5em;line-height:inherit;white-space:normal}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}template{display:none}[hidden]{display:none!important}mark{padding:.2em;background-color:#feffe6}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:2rem;padding-left:2rem}@media (min-width: 1400px){.container{max-width:1400px}}.relative{position:relative}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.resize{resize:both}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.border{border-width:1px}.bg-background{background-color:hsl(var(--background))}.p-6{padding:1.5rem}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.rjsf-form{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.vertical-form-item .ant-form-item-label{padding-bottom:0!important;margin-bottom:0}.vertical-form-item .ant-form-item-label>label{margin-bottom:0}.ant-form-item-has-error .ant-form-item-label>label{color:#ff4d4f}.ant-form-item-required:before{display:inline-block;margin-right:4px;color:#ff4d4f;font-size:14px;font-family:SimSun,sans-serif;line-height:1;content:"*"}.file\:mr-4::file-selector-button{margin-right:1rem}.file\:rounded::file-selector-button{border-radius:.25rem}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-blue-50::file-selector-button{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.file\:px-2::file-selector-button{padding-left:.5rem;padding-right:.5rem}.file\:py-1::file-selector-button{padding-top:.25rem;padding-bottom:.25rem}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-blue-700::file-selector-button{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.hover\:file\:bg-blue-100::file-selector-button:hover{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}
1
+ html,body{width:100%;height:100%}input::-ms-clear,input::-ms-reveal{display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}@-ms-viewport{width:device-width}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5em;font-weight:500}p{margin-top:0;margin-bottom:1em}abbr[title],abbr[data-original-title]{-webkit-text-decoration:underline dotted;text-decoration:underline;text-decoration:underline dotted;border-bottom:0;cursor:help}address{margin-bottom:1em;font-style:normal;line-height:inherit}input[type=text],input[type=password],input[type=number],textarea{-webkit-appearance:none}ol,ul,dl{margin-top:0;margin-bottom:1em}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}pre,code,kbd,samp{font-size:1em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace}pre{margin-top:0;margin-bottom:1em;overflow:auto}figure{margin:0 0 1em}img{vertical-align:middle;border-style:none}a,area,button,[role=button],input:not([type=range]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75em;padding-bottom:.3em;text-align:left;caption-side:bottom}input,button,select,optgroup,textarea{margin:0;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{display:block;width:100%;max-width:100%;margin-bottom:.5em;padding:0;color:inherit;font-size:1.5em;line-height:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}mark{padding:.2em;background-color:#feffe6}.rjsf-form{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.vertical-form-item .ant-form-item-label{padding-bottom:0!important;margin-bottom:0}.vertical-form-item .ant-form-item-label>label{margin-bottom:0}.ant-form-item-has-error .ant-form-item-label>label{color:#ff4d4f}.ant-form-item-required:before{display:inline-block;margin-right:4px;color:#ff4d4f;font-size:14px;font-family:SimSun,sans-serif;line-height:1;content:"*"}
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+ import { ExtendedForm } from '../src/ExtendedForm';
3
+ import { RJSFSchema, UiSchema } from '@rjsf/utils';
4
+ import { useConditionalUi, UiRule } 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: (data) => !data.requiresShipping,
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
+ }
@@ -0,0 +1,211 @@
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
+ }
@@ -0,0 +1,386 @@
1
+ import React, { useState } from 'react';
2
+ import Form from '@rjsf/antd';
3
+ import { IChangeEvent } from '@rjsf/core';
4
+ import { RJSFSchema, UiSchema } from '@rjsf/utils';
5
+ import validator from '@rjsf/validator-ajv8';
6
+ import {
7
+ Card,
8
+ Collapse,
9
+ Badge,
10
+ Tag,
11
+ Space,
12
+ Switch,
13
+ Typography,
14
+ Alert,
15
+ } from 'antd';
16
+ import {
17
+ Rule,
18
+ UiEffect,
19
+ SchemaEffect,
20
+ ValidationEffect,
21
+ } from '../src/conditionals/v2/types';
22
+ import {
23
+ useConditionalUi,
24
+ useConditionalSchema,
25
+ useConditionalValidation,
26
+ } from '../src/conditionals/v2';
27
+ import { useFormDevTools } from '../src/hooks/useFormDevTools';
28
+
29
+ const { Panel } = Collapse;
30
+ const { Text } = Typography;
31
+
32
+ // Base schema
33
+ const baseSchema: RJSFSchema = {
34
+ type: 'object',
35
+ title: 'Form with DevTools Example',
36
+ properties: {
37
+ country: {
38
+ type: 'string',
39
+ title: 'Country',
40
+ enum: ['USA', 'Canada', 'UK', 'Other'],
41
+ },
42
+ requiresShipping: {
43
+ type: 'boolean',
44
+ title: 'Requires Shipping?',
45
+ },
46
+ shippingAddress: {
47
+ type: 'object',
48
+ title: 'Shipping Address',
49
+ properties: {
50
+ street: { type: 'string', title: 'Street' },
51
+ city: { type: 'string', title: 'City' },
52
+ state: { type: 'string', title: 'State' },
53
+ zip: { type: 'string', title: 'ZIP Code' },
54
+ },
55
+ },
56
+ maritalStatus: {
57
+ type: 'string',
58
+ title: 'Marital Status',
59
+ enum: ['single', 'married', 'divorced', 'widowed'],
60
+ },
61
+ spouseName: {
62
+ type: 'string',
63
+ title: 'Spouse Name',
64
+ },
65
+ },
66
+ };
67
+
68
+ // Base UI schema
69
+ const baseUiSchema: UiSchema = {
70
+ 'ui:order': [
71
+ 'country',
72
+ 'requiresShipping',
73
+ 'shippingAddress',
74
+ 'maritalStatus',
75
+ 'spouseName',
76
+ ],
77
+ };
78
+
79
+ // UI Rules
80
+ const uiRules: Rule<UiEffect>[] = [
81
+ {
82
+ name: 'Hide shipping address when not required',
83
+ condition: formData => !formData.requiresShipping,
84
+ effect: [
85
+ { op: 'add', path: '/shippingAddress/ui:widget', value: 'hidden' },
86
+ ],
87
+ },
88
+ {
89
+ name: 'Show state dropdown for US/Canada',
90
+ condition: formData =>
91
+ formData.country === 'USA' || formData.country === 'Canada',
92
+ effect: [
93
+ {
94
+ op: 'add',
95
+ path: '/shippingAddress/state/ui:widget',
96
+ value: 'select',
97
+ },
98
+ ],
99
+ },
100
+ ];
101
+
102
+ // Schema Rules
103
+ const schemaRules: Rule<SchemaEffect>[] = [
104
+ {
105
+ name: 'Add spouse fields when married',
106
+ condition: formData => formData.maritalStatus === 'married',
107
+ effect: [{ op: 'add', path: '/required/-', value: 'spouseName' }],
108
+ },
109
+ {
110
+ name: 'Remove spouse field when not married',
111
+ condition: formData => formData.maritalStatus !== 'married',
112
+ effect: [{ op: 'remove', path: '/properties/spouseName' }],
113
+ },
114
+ ];
115
+
116
+ // Validation Rules
117
+ const validationRules: Rule<ValidationEffect>[] = [
118
+ {
119
+ name: 'US ZIP code format validation',
120
+ effect: {
121
+ name: 'US ZIP code format',
122
+ isActive: formData =>
123
+ formData.country === 'USA' && formData.requiresShipping,
124
+ validate: (errors, formData) => {
125
+ if (
126
+ formData.shippingAddress?.zip &&
127
+ !/^\d{5}(-\d{4})?$/.test(formData.shippingAddress.zip)
128
+ ) {
129
+ if (!errors.shippingAddress) errors.shippingAddress = {};
130
+ if (!errors.shippingAddress.zip) errors.shippingAddress.zip = {};
131
+ errors.shippingAddress.zip.__errors = [
132
+ 'ZIP code must be XXXXX or XXXXX-XXXX format',
133
+ ];
134
+ }
135
+ },
136
+ },
137
+ },
138
+ {
139
+ name: 'Canadian postal code validation',
140
+ effect: {
141
+ name: 'Canadian postal code',
142
+ isActive: formData =>
143
+ formData.country === 'Canada' && formData.requiresShipping,
144
+ validate: (errors, formData) => {
145
+ if (
146
+ formData.shippingAddress?.zip &&
147
+ !/^[A-Z]\d[A-Z] \d[A-Z]\d$/.test(formData.shippingAddress.zip)
148
+ ) {
149
+ if (!errors.shippingAddress) errors.shippingAddress = {};
150
+ if (!errors.shippingAddress.zip) errors.shippingAddress.zip = {};
151
+ errors.shippingAddress.zip.__errors = [
152
+ 'Postal code must be A1A 1A1 format',
153
+ ];
154
+ }
155
+ },
156
+ },
157
+ },
158
+ ];
159
+
160
+ // DevTools Panel Component
161
+ const DevToolsPanel: React.FC<{
162
+ devTools: ReturnType<typeof useFormDevTools>;
163
+ }> = ({ devTools }) => {
164
+ const { evaluations, activeRules, hasErrors, errors, summary } = devTools;
165
+
166
+ return (
167
+ <Card title='Form DevTools' style={{ marginTop: 24 }}>
168
+ <Space direction='vertical' style={{ width: '100%' }}>
169
+ {/* Summary */}
170
+ <Alert
171
+ message={
172
+ <Space>
173
+ <Text>Total Rules: {summary.totalRules}</Text>
174
+ <Badge
175
+ count={summary.activeCount}
176
+ style={{ backgroundColor: '#52c41a' }}
177
+ />
178
+ <Text>Active</Text>
179
+ {hasErrors && (
180
+ <>
181
+ <Badge count={summary.errorCount} />
182
+ <Text>Errors</Text>
183
+ </>
184
+ )}
185
+ </Space>
186
+ }
187
+ type={hasErrors ? 'warning' : 'info'}
188
+ />
189
+
190
+ {/* Errors */}
191
+ {hasErrors && (
192
+ <Alert
193
+ message='Rule Evaluation Errors'
194
+ description={
195
+ <ul>
196
+ {errors.map((error, idx) => (
197
+ <li key={idx}>
198
+ <Text type='danger'>
199
+ {error.pillar} Rule "{error.ruleName}": {error.error}
200
+ </Text>
201
+ </li>
202
+ ))}
203
+ </ul>
204
+ }
205
+ type='error'
206
+ />
207
+ )}
208
+
209
+ {/* Rule Details */}
210
+ <Collapse defaultActiveKey={['ui']}>
211
+ <Panel
212
+ header={`UI Rules (${activeRules.ui.length} active)`}
213
+ key='ui'
214
+ extra={
215
+ <Badge
216
+ count={activeRules.ui.length}
217
+ style={{ backgroundColor: '#52c41a' }}
218
+ />
219
+ }
220
+ >
221
+ {evaluations
222
+ .filter(e => e.pillar === 'UI')
223
+ .map((evaluation, idx) => (
224
+ <div key={idx} style={{ marginBottom: 8 }}>
225
+ <Tag color={evaluation.active ? 'green' : 'default'}>
226
+ {evaluation.active ? '✓' : '○'}
227
+ </Tag>
228
+ <Text>{evaluation.ruleName}</Text>
229
+ {evaluation.error && (
230
+ <Text type='danger'> (Error: {evaluation.error})</Text>
231
+ )}
232
+ </div>
233
+ ))}
234
+ </Panel>
235
+
236
+ <Panel
237
+ header={`Schema Rules (${activeRules.schema.length} active)`}
238
+ key='schema'
239
+ extra={
240
+ <Badge
241
+ count={activeRules.schema.length}
242
+ style={{ backgroundColor: '#52c41a' }}
243
+ />
244
+ }
245
+ >
246
+ {evaluations
247
+ .filter(e => e.pillar === 'Schema')
248
+ .map((evaluation, idx) => (
249
+ <div key={idx} style={{ marginBottom: 8 }}>
250
+ <Tag color={evaluation.active ? 'green' : 'default'}>
251
+ {evaluation.active ? '✓' : '○'}
252
+ </Tag>
253
+ <Text>{evaluation.ruleName}</Text>
254
+ {evaluation.error && (
255
+ <Text type='danger'> (Error: {evaluation.error})</Text>
256
+ )}
257
+ </div>
258
+ ))}
259
+ </Panel>
260
+
261
+ <Panel
262
+ header={`Validation Rules (${activeRules.validation.length} active)`}
263
+ key='validation'
264
+ extra={
265
+ <Badge
266
+ count={activeRules.validation.length}
267
+ style={{ backgroundColor: '#52c41a' }}
268
+ />
269
+ }
270
+ >
271
+ {evaluations
272
+ .filter(e => e.pillar === 'Validation')
273
+ .map((evaluation, idx) => (
274
+ <div key={idx} style={{ marginBottom: 8 }}>
275
+ <Tag color={evaluation.active ? 'green' : 'default'}>
276
+ {evaluation.active ? '✓' : '○'}
277
+ </Tag>
278
+ <Text>{evaluation.ruleName}</Text>
279
+ {evaluation.error && (
280
+ <Text type='danger'> (Error: {evaluation.error})</Text>
281
+ )}
282
+ </div>
283
+ ))}
284
+ </Panel>
285
+ </Collapse>
286
+ </Space>
287
+ </Card>
288
+ );
289
+ };
290
+
291
+ export default function FormDevToolsExample() {
292
+ const [formData, setFormData] = useState({});
293
+ const [devToolsEnabled, setDevToolsEnabled] = useState(true);
294
+ const [logToConsole, setLogToConsole] = useState(false);
295
+ const [captureHistory, setCaptureHistory] = useState(false);
296
+
297
+ // Apply conditional logic
298
+ const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
299
+ const conditionalSchema = useConditionalSchema(
300
+ schemaRules,
301
+ baseSchema,
302
+ formData
303
+ );
304
+ const customValidate = useConditionalValidation(validationRules, formData);
305
+
306
+ // Use DevTools
307
+ const devTools = useFormDevTools(
308
+ formData,
309
+ uiRules,
310
+ schemaRules,
311
+ validationRules,
312
+ {
313
+ enabled: devToolsEnabled,
314
+ logToConsole,
315
+ captureHistory,
316
+ historySize: 10,
317
+ }
318
+ );
319
+
320
+ const handleChange = (e: IChangeEvent) => {
321
+ setFormData(e.formData);
322
+ };
323
+
324
+ return (
325
+ <div style={{ padding: '24px', maxWidth: '1200px', margin: '0 auto' }}>
326
+ <Card title='DevTools Settings' style={{ marginBottom: 24 }}>
327
+ <Space>
328
+ <label>
329
+ <Switch checked={devToolsEnabled} onChange={setDevToolsEnabled} />
330
+ <Text style={{ marginLeft: 8 }}>Enable DevTools</Text>
331
+ </label>
332
+ <label>
333
+ <Switch checked={logToConsole} onChange={setLogToConsole} />
334
+ <Text style={{ marginLeft: 8 }}>Log to Console</Text>
335
+ </label>
336
+ <label>
337
+ <Switch checked={captureHistory} onChange={setCaptureHistory} />
338
+ <Text style={{ marginLeft: 8 }}>Capture History</Text>
339
+ </label>
340
+ </Space>
341
+ </Card>
342
+
343
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24 }}>
344
+ <div>
345
+ <Form
346
+ schema={conditionalSchema}
347
+ uiSchema={conditionalUiSchema}
348
+ formData={formData}
349
+ onChange={handleChange}
350
+ validator={validator}
351
+ customValidate={customValidate}
352
+ />
353
+ </div>
354
+
355
+ <div>
356
+ {devToolsEnabled && <DevToolsPanel devTools={devTools} />}
357
+
358
+ {captureHistory && devTools.history.length > 0 && (
359
+ <Card title='History' style={{ marginTop: 24 }}>
360
+ <Text>History entries: {devTools.history.length}</Text>
361
+ <div style={{ maxHeight: 200, overflow: 'auto', marginTop: 8 }}>
362
+ {devTools.history.map((entry, idx) => (
363
+ <div
364
+ key={idx}
365
+ style={{
366
+ marginBottom: 8,
367
+ padding: 8,
368
+ background: '#f0f0f0',
369
+ }}
370
+ >
371
+ <Text type='secondary'>
372
+ {new Date(entry.timestamp).toLocaleTimeString()}
373
+ </Text>
374
+ <pre style={{ fontSize: 12, margin: 0 }}>
375
+ {JSON.stringify(entry.formData, null, 2)}
376
+ </pre>
377
+ </div>
378
+ ))}
379
+ </div>
380
+ </Card>
381
+ )}
382
+ </div>
383
+ </div>
384
+ </div>
385
+ );
386
+ }