@webiny/app-admin 6.2.0 → 6.3.0-beta.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.
Files changed (143) hide show
  1. package/base/Admin.js +2 -0
  2. package/base/Admin.js.map +1 -1
  3. package/base/Base/DefaultFieldRenderers.js +20 -4
  4. package/base/Base/DefaultFieldRenderers.js.map +1 -1
  5. package/base/Base/DefaultLayoutRenderers.d.ts +2 -0
  6. package/base/Base/DefaultLayoutRenderers.js +11 -0
  7. package/base/Base/DefaultLayoutRenderers.js.map +1 -0
  8. package/base/Base/FieldRenderers/InputRenderer.d.ts +15 -0
  9. package/base/Base/FieldRenderers/{TextRenderer.js → InputRenderer.js} +5 -3
  10. package/base/Base/FieldRenderers/InputRenderer.js.map +1 -0
  11. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.d.ts +25 -0
  12. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js +89 -0
  13. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js.map +1 -0
  14. package/base/Base/FieldRenderers/ObjectRenderer/ObjectListFlatRenderer.d.ts +21 -0
  15. package/base/Base/FieldRenderers/ObjectRenderer/ObjectListFlatRenderer.js +28 -0
  16. package/base/Base/FieldRenderers/ObjectRenderer/ObjectListFlatRenderer.js.map +1 -0
  17. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.d.ts +17 -0
  18. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js +63 -0
  19. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js.map +1 -0
  20. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.d.ts +4 -0
  21. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js +22 -0
  22. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js.map +1 -0
  23. package/base/Base/FieldRenderers/PassthroughRenderer.d.ts +15 -0
  24. package/base/Base/FieldRenderers/PassthroughRenderer.js +29 -0
  25. package/base/Base/FieldRenderers/PassthroughRenderer.js.map +1 -0
  26. package/base/Base/FieldRenderers/SelectRenderer.d.ts +12 -2
  27. package/base/Base/FieldRenderers/SelectRenderer.js +4 -2
  28. package/base/Base/FieldRenderers/SelectRenderer.js.map +1 -1
  29. package/base/Base/FieldRenderers/TextareaRenderer.d.ts +17 -0
  30. package/base/Base/FieldRenderers/TextareaRenderer.js +25 -0
  31. package/base/Base/FieldRenderers/TextareaRenderer.js.map +1 -0
  32. package/base/Base/FieldRenderers/VerticalTabsRenderer.d.ts +5 -0
  33. package/base/Base/FieldRenderers/VerticalTabsRenderer.js +41 -0
  34. package/base/Base/FieldRenderers/VerticalTabsRenderer.js.map +1 -0
  35. package/base/Base.js +2 -1
  36. package/base/Base.js.map +1 -1
  37. package/base/createRootContainer.js +4 -0
  38. package/base/createRootContainer.js.map +1 -1
  39. package/base/providers/UiProviders.js +0 -1
  40. package/base/providers/UiProviders.js.map +1 -1
  41. package/components/Filters/Filters.js.map +1 -1
  42. package/components/IconPicker/components/IconPickerCell.d.ts +1 -1
  43. package/components/IconPicker/components/IconPickerTrigger.d.ts +1 -1
  44. package/components/IconPicker/components/IconPickerTrigger.js.map +1 -1
  45. package/components/LexicalEditor/LexicalEditor.js +7 -1
  46. package/components/LexicalEditor/LexicalEditor.js.map +1 -1
  47. package/components/LexicalEditor/lexicalValueFromHtml.d.ts +7 -0
  48. package/components/LexicalEditor/lexicalValueFromHtml.js +35 -0
  49. package/components/LexicalEditor/lexicalValueFromHtml.js.map +1 -0
  50. package/components/OverlayLayout/components/OverlayHeader.d.ts +1 -1
  51. package/components/OverlayLayout/components/OverlayHeader.js.map +1 -1
  52. package/components/SimpleForm/SimpleForm.d.ts +2 -2
  53. package/components/SimpleForm/SimpleForm.js.map +1 -1
  54. package/config/AdminConfig/Form.d.ts +1 -0
  55. package/config/AdminConfig/Form.js +3 -1
  56. package/config/AdminConfig/Form.js.map +1 -1
  57. package/config/AdminConfig/LayoutRenderer.d.ts +10 -0
  58. package/config/AdminConfig/LayoutRenderer.js +21 -0
  59. package/config/AdminConfig/LayoutRenderer.js.map +1 -0
  60. package/config/AdminConfig/Menu/MenuLink.d.ts +2 -2
  61. package/config/AdminConfig.d.ts +4 -0
  62. package/config/AdminConfig.js +2 -1
  63. package/config/AdminConfig.js.map +1 -1
  64. package/exports/admin.d.ts +2 -0
  65. package/exports/admin.js +2 -0
  66. package/exports/admin.js.map +1 -1
  67. package/features/formModel/Field.js +4 -0
  68. package/features/formModel/Field.js.map +1 -1
  69. package/features/formModel/FieldBuilder.d.ts +18 -2
  70. package/features/formModel/FieldBuilder.js +57 -3
  71. package/features/formModel/FieldBuilder.js.map +1 -1
  72. package/features/formModel/FormModel.d.ts +3 -3
  73. package/features/formModel/FormModel.js +48 -13
  74. package/features/formModel/FormModel.js.map +1 -1
  75. package/features/formModel/FormModel.test.js +383 -0
  76. package/features/formModel/FormModel.test.js.map +1 -1
  77. package/features/formModel/FormModelFactory.d.ts +2 -2
  78. package/features/formModel/FormModelFactory.js.map +1 -1
  79. package/features/formModel/FormView.d.ts +24 -4
  80. package/features/formModel/FormView.js +57 -27
  81. package/features/formModel/FormView.js.map +1 -1
  82. package/features/formModel/ObjectField.d.ts +46 -0
  83. package/features/formModel/ObjectField.js +346 -0
  84. package/features/formModel/ObjectField.js.map +1 -0
  85. package/features/formModel/abstractions.d.ts +88 -19
  86. package/features/formModel/abstractions.js +8 -0
  87. package/features/formModel/abstractions.js.map +1 -1
  88. package/features/formModel/index.d.ts +7 -4
  89. package/features/formModel/index.js +9 -2
  90. package/features/formModel/index.js.map +1 -1
  91. package/features/formModel/renderers.d.ts +6 -0
  92. package/features/formModel/renderers.js +10 -0
  93. package/features/formModel/renderers.js.map +1 -0
  94. package/features/formModel/useLayoutRenderers.d.ts +2 -0
  95. package/features/formModel/useLayoutRenderers.js +19 -0
  96. package/features/formModel/useLayoutRenderers.js.map +1 -0
  97. package/features/tools/ToolPipelineRunner.d.ts +10 -0
  98. package/features/tools/ToolPipelineRunner.js +33 -0
  99. package/features/tools/ToolPipelineRunner.js.map +1 -0
  100. package/features/tools/ToolRegistry.d.ts +12 -0
  101. package/features/tools/ToolRegistry.js +29 -0
  102. package/features/tools/ToolRegistry.js.map +1 -0
  103. package/features/tools/abstractions.d.ts +28 -0
  104. package/features/tools/abstractions.js +6 -0
  105. package/features/tools/abstractions.js.map +1 -0
  106. package/features/tools/feature.d.ts +4 -0
  107. package/features/tools/feature.js +19 -0
  108. package/features/tools/feature.js.map +1 -0
  109. package/features/tools/index.d.ts +2 -0
  110. package/features/tools/index.js +4 -0
  111. package/features/tools/index.js.map +1 -0
  112. package/features/wcp/ReactLicense.d.ts +3 -0
  113. package/features/wcp/ReactLicense.js +9 -0
  114. package/features/wcp/ReactLicense.js.map +1 -1
  115. package/features/wcp/WcpGateway.js +4 -0
  116. package/features/wcp/WcpGateway.js.map +1 -1
  117. package/features/webinySdk/WebinySdk.d.ts +11 -0
  118. package/features/webinySdk/WebinySdk.js +27 -0
  119. package/features/webinySdk/WebinySdk.js.map +1 -0
  120. package/features/webinySdk/abstractions.d.ts +6 -0
  121. package/features/webinySdk/abstractions.js +8 -0
  122. package/features/webinySdk/abstractions.js.map +1 -0
  123. package/features/webinySdk/feature.d.ts +3 -0
  124. package/features/webinySdk/feature.js +16 -0
  125. package/features/webinySdk/feature.js.map +1 -0
  126. package/features/webinySdk/index.d.ts +2 -0
  127. package/features/webinySdk/index.js +4 -0
  128. package/features/webinySdk/index.js.map +1 -0
  129. package/index.d.ts +8 -3
  130. package/index.js +5 -2
  131. package/index.js.map +1 -1
  132. package/package.json +24 -23
  133. package/presentation/textToLexicalTool/TextToLexicalTool.d.ts +27 -0
  134. package/presentation/textToLexicalTool/TextToLexicalTool.js +25 -0
  135. package/presentation/textToLexicalTool/TextToLexicalTool.js.map +1 -0
  136. package/presentation/textToLexicalTool/feature.d.ts +1 -0
  137. package/presentation/textToLexicalTool/feature.js +10 -0
  138. package/presentation/textToLexicalTool/feature.js.map +1 -0
  139. package/presentation/textToLexicalTool/textToLexicalState.d.ts +5 -0
  140. package/presentation/textToLexicalTool/textToLexicalState.js +19 -0
  141. package/presentation/textToLexicalTool/textToLexicalState.js.map +1 -0
  142. package/base/Base/FieldRenderers/TextRenderer.d.ts +0 -5
  143. package/base/Base/FieldRenderers/TextRenderer.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { FormModel } from "./FormModel.js";
2
1
  import { type IFormModelFactory, type IFormModelConfig } from "./abstractions.js";
2
+ import type { IFormModel } from "./abstractions.js";
3
3
  declare class FormModelFactoryImpl implements IFormModelFactory {
4
- create(config: IFormModelConfig): FormModel;
4
+ create<T = Record<string, any>>(config: IFormModelConfig): IFormModel<T>;
5
5
  }
6
6
  export declare const FormModelFactory: typeof FormModelFactoryImpl & {
7
7
  __abstraction: import("@webiny/di").Abstraction<IFormModelFactory>;
@@ -1 +1 @@
1
- {"version":3,"names":["FormModel","FormModelFactory","Abstraction","FormModelFactoryImpl","create","config","createImplementation","implementation","dependencies"],"sources":["FormModelFactory.ts"],"sourcesContent":["import { FormModel } from \"./FormModel.js\";\nimport {\n FormModelFactory as Abstraction,\n type IFormModelFactory,\n type IFormModelConfig\n} from \"./abstractions.js\";\n\nclass FormModelFactoryImpl implements IFormModelFactory {\n create(config: IFormModelConfig): FormModel {\n return new FormModel(config);\n }\n}\n\nexport const FormModelFactory = Abstraction.createImplementation({\n implementation: FormModelFactoryImpl,\n dependencies: []\n});\n"],"mappings":"AAAA,SAASA,SAAS;AAClB,SACIC,gBAAgB,IAAIC,WAAW;AAKnC,MAAMC,oBAAoB,CAA8B;EACpDC,MAAMA,CAACC,MAAwB,EAAa;IACxC,OAAO,IAAIL,SAAS,CAACK,MAAM,CAAC;EAChC;AACJ;AAEA,OAAO,MAAMJ,gBAAgB,GAAGC,WAAW,CAACI,oBAAoB,CAAC;EAC7DC,cAAc,EAAEJ,oBAAoB;EACpCK,YAAY,EAAE;AAClB,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["FormModel","FormModelFactory","Abstraction","FormModelFactoryImpl","create","config","createImplementation","implementation","dependencies"],"sources":["FormModelFactory.ts"],"sourcesContent":["import { FormModel } from \"./FormModel.js\";\nimport {\n FormModelFactory as Abstraction,\n type IFormModelFactory,\n type IFormModelConfig\n} from \"./abstractions.js\";\nimport type { IFormModel } from \"./abstractions.js\";\n\nclass FormModelFactoryImpl implements IFormModelFactory {\n create<T = Record<string, any>>(config: IFormModelConfig): IFormModel<T> {\n return new FormModel(config) as IFormModel<T>;\n }\n}\n\nexport const FormModelFactory = Abstraction.createImplementation({\n implementation: FormModelFactoryImpl,\n dependencies: []\n});\n"],"mappings":"AAAA,SAASA,SAAS;AAClB,SACIC,gBAAgB,IAAIC,WAAW;AAMnC,MAAMC,oBAAoB,CAA8B;EACpDC,MAAMA,CAA0BC,MAAwB,EAAiB;IACrE,OAAO,IAAIL,SAAS,CAACK,MAAM,CAAC;EAChC;AACJ;AAEA,OAAO,MAAMJ,gBAAgB,GAAGC,WAAW,CAACI,oBAAoB,CAAC;EAC7DC,cAAc,EAAEJ,oBAAoB;EACpCK,YAAY,EAAE;AAClB,CAAC,CAAC","ignoreList":[]}
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import type { IFormVM, IFieldVM } from "./abstractions.js";
2
+ import type { IFormVM, LayoutNodeVM, IFieldVM, ITabsNodeVM } from "./abstractions.js";
3
3
  /**
4
4
  * A field renderer component receives a FieldVM and renders the appropriate UI.
5
5
  */
@@ -7,17 +7,37 @@ export type FieldRendererComponent = React.ComponentType<{
7
7
  field: IFieldVM;
8
8
  }>;
9
9
  /**
10
- * Map of renderer keys to React components.
10
+ * Map of renderer keys to React components for fields.
11
11
  * Lookup order: `{type}:{renderer}` → `{type}`.
12
12
  */
13
13
  export type FieldRenderers = Record<string, FieldRendererComponent>;
14
+ /**
15
+ * Map of renderer keys to React components for layout nodes.
16
+ */
17
+ export type LayoutRenderers = Record<string, React.ComponentType<any>>;
18
+ interface FormViewRenderers {
19
+ fieldRenderers: FieldRenderers;
20
+ layoutRenderers: LayoutRenderers;
21
+ }
22
+ declare const useFormViewRenderers: () => FormViewRenderers;
23
+ export { useFormViewRenderers };
14
24
  interface FormViewProps {
15
25
  form: IFormVM;
16
26
  renderers?: FieldRenderers;
27
+ layoutRenderers?: LayoutRenderers;
17
28
  }
18
29
  /**
19
30
  * Generic form view that walks layout nodes and renders fields.
20
31
  * This component is stateless — it reads from the FormVM and delegates to renderers.
21
32
  */
22
- export declare const FormView: React.FunctionComponent<FormViewProps>;
23
- export {};
33
+ export declare const FormView: (({ form, renderers, layoutRenderers }: FormViewProps) => React.JSX.Element) & {
34
+ displayName: string;
35
+ };
36
+ export declare const LayoutNodeRenderer: (({ node }: {
37
+ node: LayoutNodeVM;
38
+ }) => React.JSX.Element | null) & {
39
+ displayName: string;
40
+ };
41
+ export interface TabsNodeRendererProps {
42
+ node: ITabsNodeVM;
43
+ }
@@ -1,63 +1,82 @@
1
- import React from "react";
1
+ import React, { createContext, useContext, useMemo } from "react";
2
2
  import { observer } from "mobx-react-lite";
3
3
  import { useFieldRenderers } from "./useFieldRenderers.js";
4
+ import { useLayoutRenderers } from "./useLayoutRenderers.js";
4
5
 
5
6
  /**
6
7
  * A field renderer component receives a FieldVM and renders the appropriate UI.
7
8
  */
8
9
 
9
10
  /**
10
- * Map of renderer keys to React components.
11
+ * Map of renderer keys to React components for fields.
11
12
  * Lookup order: `{type}:{renderer}` → `{type}`.
12
13
  */
13
14
 
15
+ /**
16
+ * Map of renderer keys to React components for layout nodes.
17
+ */
18
+
19
+ const FormViewRenderersContext = /*#__PURE__*/createContext(null);
20
+ const useFormViewRenderers = () => {
21
+ const ctx = useContext(FormViewRenderersContext);
22
+ if (!ctx) {
23
+ throw new Error("useFormViewRenderers must be used within a FormView.");
24
+ }
25
+ return ctx;
26
+ };
27
+ export { useFormViewRenderers };
14
28
  /**
15
29
  * Generic form view that walks layout nodes and renders fields.
16
30
  * This component is stateless — it reads from the FormVM and delegates to renderers.
17
31
  */
18
32
  export const FormView = observer(function FormView({
19
33
  form,
20
- renderers
34
+ renderers,
35
+ layoutRenderers
21
36
  }) {
22
- const fieldRenderers = useFieldRenderers();
23
- return /*#__PURE__*/React.createElement("div", {
37
+ const defaultFieldRenderers = useFieldRenderers();
38
+ const defaultLayoutRenderers = useLayoutRenderers();
39
+ const value = useMemo(() => ({
40
+ fieldRenderers: renderers ?? defaultFieldRenderers,
41
+ layoutRenderers: layoutRenderers ?? defaultLayoutRenderers
42
+ }), [renderers, defaultFieldRenderers, layoutRenderers, defaultLayoutRenderers]);
43
+ return /*#__PURE__*/React.createElement(FormViewRenderersContext.Provider, {
44
+ value: value
45
+ }, /*#__PURE__*/React.createElement("div", {
24
46
  className: "w-full flex flex-col gap-4"
25
47
  }, form.layout.map((node, index) => /*#__PURE__*/React.createElement(LayoutNodeRenderer, {
26
48
  key: index,
27
- node: node,
28
- renderers: renderers ?? fieldRenderers
29
- })));
49
+ node: node
50
+ }))));
30
51
  });
31
- const LayoutNodeRenderer = observer(function LayoutNodeRenderer({
32
- node,
33
- renderers
52
+ export const LayoutNodeRenderer = observer(function LayoutNodeRenderer({
53
+ node
34
54
  }) {
35
55
  switch (node.type) {
36
56
  case "row":
37
57
  return /*#__PURE__*/React.createElement(RowNodeRenderer, {
38
- node: node,
39
- renderers: renderers
58
+ node: node
40
59
  });
41
60
  case "separator":
42
61
  return /*#__PURE__*/React.createElement(SeparatorNodeRenderer, null);
43
62
  case "tabs":
44
63
  return /*#__PURE__*/React.createElement(TabsNodeRenderer, {
45
- node: node,
46
- renderers: renderers
64
+ node: node
47
65
  });
48
66
  case "element":
49
67
  return /*#__PURE__*/React.createElement(ElementNodeRenderer, {
50
- node: node,
51
- renderers: renderers
68
+ node: node
52
69
  });
53
70
  default:
54
71
  return null;
55
72
  }
56
73
  });
57
74
  const RowNodeRenderer = observer(function RowNodeRenderer({
58
- node,
59
- renderers
75
+ node
60
76
  }) {
77
+ const {
78
+ fieldRenderers
79
+ } = useFormViewRenderers();
61
80
  return /*#__PURE__*/React.createElement("div", {
62
81
  className: "grid grid-cols-12 gap-4"
63
82
  }, node.fields.map(field => {
@@ -67,7 +86,7 @@ const RowNodeRenderer = observer(function RowNodeRenderer({
67
86
  className: `col-span-${span}`
68
87
  }, /*#__PURE__*/React.createElement(FieldRenderer, {
69
88
  field: field,
70
- renderers: renderers
89
+ renderers: fieldRenderers
71
90
  }));
72
91
  }));
73
92
  });
@@ -92,9 +111,19 @@ const SeparatorNodeRenderer = observer(function SeparatorNodeRenderer() {
92
111
  });
93
112
  });
94
113
  const TabsNodeRenderer = observer(function TabsNodeRenderer({
95
- node,
96
- renderers
114
+ node
97
115
  }) {
116
+ const {
117
+ layoutRenderers
118
+ } = useFormViewRenderers();
119
+ if (node.renderer) {
120
+ const CustomRenderer = layoutRenderers[node.renderer];
121
+ if (CustomRenderer) {
122
+ return /*#__PURE__*/React.createElement(CustomRenderer, {
123
+ node: node
124
+ });
125
+ }
126
+ }
98
127
  const activeTab = node.tabs.find(t => t.id === node.activeTabId);
99
128
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
100
129
  className: "flex border-b border-neutral-dimmed"
@@ -113,15 +142,16 @@ const TabsNodeRenderer = observer(function TabsNodeRenderer({
113
142
  className: "flex flex-col gap-4"
114
143
  }, activeTab.layout.map((childNode, index) => /*#__PURE__*/React.createElement(LayoutNodeRenderer, {
115
144
  key: index,
116
- node: childNode,
117
- renderers: renderers
145
+ node: childNode
118
146
  })))));
119
147
  });
120
148
  const ElementNodeRenderer = observer(function ElementNodeRenderer({
121
- node,
122
- renderers
149
+ node
123
150
  }) {
124
- const Renderer = renderers[`element:${node.renderer}`];
151
+ const {
152
+ fieldRenderers
153
+ } = useFormViewRenderers();
154
+ const Renderer = fieldRenderers[`element:${node.renderer}`];
125
155
  if (!Renderer) {
126
156
  if (process.env.NODE_ENV === "development") {
127
157
  console.warn(`[FormView] No renderer found for element "${node.renderer}".`);
@@ -1 +1 @@
1
- {"version":3,"names":["React","observer","useFieldRenderers","FormView","form","renderers","fieldRenderers","createElement","className","layout","map","node","index","LayoutNodeRenderer","key","type","RowNodeRenderer","SeparatorNodeRenderer","TabsNodeRenderer","ElementNodeRenderer","fields","field","span","Math","floor","length","name","FieldRenderer","Renderer","renderer","undefined","process","env","NODE_ENV","console","warn","activeTab","tabs","find","t","id","activeTabId","tab","onClick","setActiveTab","label","hasErrors","description","childNode","props"],"sources":["FormView.tsx"],"sourcesContent":["import React from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport type {\n IFormVM,\n LayoutNodeVM,\n IRowNodeVM,\n IFieldVM,\n ITabsNodeVM,\n IElementNodeVM\n} from \"./abstractions.js\";\nimport { useFieldRenderers } from \"~/features/formModel/useFieldRenderers.js\";\n\n/**\n * A field renderer component receives a FieldVM and renders the appropriate UI.\n */\nexport type FieldRendererComponent = React.ComponentType<{ field: IFieldVM }>;\n\n/**\n * Map of renderer keys to React components.\n * Lookup order: `{type}:{renderer}` → `{type}`.\n */\nexport type FieldRenderers = Record<string, FieldRendererComponent>;\n\ninterface FormViewProps {\n form: IFormVM;\n renderers?: FieldRenderers;\n}\n\n/**\n * Generic form view that walks layout nodes and renders fields.\n * This component is stateless — it reads from the FormVM and delegates to renderers.\n */\nexport const FormView = observer(function FormView({ form, renderers }: FormViewProps) {\n const fieldRenderers = useFieldRenderers();\n\n return (\n <div className=\"w-full flex flex-col gap-4\">\n {form.layout.map((node, index) => (\n <LayoutNodeRenderer\n key={index}\n node={node}\n renderers={renderers ?? fieldRenderers}\n />\n ))}\n </div>\n );\n});\n\ninterface LayoutNodeRendererProps {\n node: LayoutNodeVM;\n renderers: FieldRenderers;\n}\n\nconst LayoutNodeRenderer = observer(function LayoutNodeRenderer({\n node,\n renderers\n}: LayoutNodeRendererProps) {\n switch (node.type) {\n case \"row\":\n return <RowNodeRenderer node={node} renderers={renderers} />;\n case \"separator\":\n return <SeparatorNodeRenderer />;\n case \"tabs\":\n return <TabsNodeRenderer node={node} renderers={renderers} />;\n case \"element\":\n return <ElementNodeRenderer node={node} renderers={renderers} />;\n default:\n return null;\n }\n});\n\ninterface RowNodeRendererProps {\n node: IRowNodeVM;\n renderers: FieldRenderers;\n}\n\nconst RowNodeRenderer = observer(function RowNodeRenderer({\n node,\n renderers\n}: RowNodeRendererProps) {\n return (\n <div className=\"grid grid-cols-12 gap-4\">\n {node.fields.map(field => {\n const span = Math.floor(12 / node.fields.length);\n return (\n <div key={field.name} className={`col-span-${span}`}>\n <FieldRenderer field={field} renderers={renderers} />\n </div>\n );\n })}\n </div>\n );\n});\n\ninterface FieldRendererProps {\n field: IFieldVM;\n renderers: FieldRenderers;\n}\n\nconst FieldRenderer = observer(function FieldRenderer({ field, renderers }: FieldRendererProps) {\n const Renderer = field.renderer ? renderers[field.renderer] : undefined;\n\n if (!Renderer) {\n if (process.env.NODE_ENV === \"development\") {\n console.warn(\n `[FormView] No renderer found for field \"${field.name}\" (renderer: \"${field.renderer || \"none\"}\").`\n );\n }\n return null;\n }\n\n return <Renderer field={field} />;\n});\n\nconst SeparatorNodeRenderer = observer(function SeparatorNodeRenderer() {\n return <hr className=\"border-neutral-dimmed my-2\" />;\n});\n\ninterface TabsNodeRendererProps {\n node: ITabsNodeVM;\n renderers: FieldRenderers;\n}\n\nconst TabsNodeRenderer = observer(function TabsNodeRenderer({\n node,\n renderers\n}: TabsNodeRendererProps) {\n const activeTab = node.tabs.find(t => t.id === node.activeTabId);\n\n return (\n <div>\n <div className=\"flex border-b border-neutral-dimmed\">\n {node.tabs.map(tab => (\n <button\n key={tab.id}\n type=\"button\"\n className={`px-4 py-2 text-sm font-medium border-b-2 ${\n tab.id === node.activeTabId\n ? \"border-primary text-primary\"\n : \"border-transparent text-neutral hover:text-neutral-strong\"\n }`}\n onClick={() => node.setActiveTab(tab.id)}\n >\n {tab.label}\n {tab.hasErrors && <span className=\"ml-1 text-destructive text-xs\">*</span>}\n </button>\n ))}\n </div>\n {activeTab && (\n <div className=\"pt-4\">\n {activeTab.description && (\n <p className=\"text-sm text-neutral mb-4\">{activeTab.description}</p>\n )}\n <div className=\"flex flex-col gap-4\">\n {activeTab.layout.map((childNode, index) => (\n <LayoutNodeRenderer\n key={index}\n node={childNode}\n renderers={renderers}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n );\n});\n\ninterface ElementNodeRendererProps {\n node: IElementNodeVM;\n renderers: FieldRenderers;\n}\n\nconst ElementNodeRenderer = observer(function ElementNodeRenderer({\n node,\n renderers\n}: ElementNodeRendererProps) {\n const Renderer = renderers[`element:${node.renderer}`];\n\n if (!Renderer) {\n if (process.env.NODE_ENV === \"development\") {\n console.warn(`[FormView] No renderer found for element \"${node.renderer}\".`);\n }\n return null;\n }\n\n return <Renderer field={{ ...node.props } as any} />;\n});\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","createContext","useContext","useMemo","observer","useFieldRenderers","useLayoutRenderers","FormViewRenderersContext","useFormViewRenderers","ctx","Error","FormView","form","renderers","layoutRenderers","defaultFieldRenderers","defaultLayoutRenderers","value","fieldRenderers","createElement","Provider","className","layout","map","node","index","LayoutNodeRenderer","key","type","RowNodeRenderer","SeparatorNodeRenderer","TabsNodeRenderer","ElementNodeRenderer","fields","field","span","Math","floor","length","name","FieldRenderer","Renderer","renderer","undefined","process","env","NODE_ENV","console","warn","CustomRenderer","activeTab","tabs","find","t","id","activeTabId","tab","onClick","setActiveTab","label","hasErrors","description","childNode","props"],"sources":["FormView.tsx"],"sourcesContent":["import React, { createContext, useContext, useMemo } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport type {\n IFormVM,\n LayoutNodeVM,\n IRowNodeVM,\n IFieldVM,\n ITabsNodeVM,\n IElementNodeVM\n} from \"./abstractions.js\";\nimport { useFieldRenderers } from \"~/features/formModel/useFieldRenderers.js\";\nimport { useLayoutRenderers } from \"~/features/formModel/useLayoutRenderers.js\";\n\n/**\n * A field renderer component receives a FieldVM and renders the appropriate UI.\n */\nexport type FieldRendererComponent = React.ComponentType<{ field: IFieldVM }>;\n\n/**\n * Map of renderer keys to React components for fields.\n * Lookup order: `{type}:{renderer}` → `{type}`.\n */\nexport type FieldRenderers = Record<string, FieldRendererComponent>;\n\n/**\n * Map of renderer keys to React components for layout nodes.\n */\nexport type LayoutRenderers = Record<string, React.ComponentType<any>>;\n\ninterface FormViewRenderers {\n fieldRenderers: FieldRenderers;\n layoutRenderers: LayoutRenderers;\n}\n\nconst FormViewRenderersContext = createContext<FormViewRenderers | null>(null);\n\nconst useFormViewRenderers = (): FormViewRenderers => {\n const ctx = useContext(FormViewRenderersContext);\n if (!ctx) {\n throw new Error(\"useFormViewRenderers must be used within a FormView.\");\n }\n return ctx;\n};\n\nexport { useFormViewRenderers };\n\ninterface FormViewProps {\n form: IFormVM;\n renderers?: FieldRenderers;\n layoutRenderers?: LayoutRenderers;\n}\n\n/**\n * Generic form view that walks layout nodes and renders fields.\n * This component is stateless — it reads from the FormVM and delegates to renderers.\n */\nexport const FormView = observer(function FormView({\n form,\n renderers,\n layoutRenderers\n}: FormViewProps) {\n const defaultFieldRenderers = useFieldRenderers();\n const defaultLayoutRenderers = useLayoutRenderers();\n\n const value = useMemo(\n () => ({\n fieldRenderers: renderers ?? defaultFieldRenderers,\n layoutRenderers: layoutRenderers ?? defaultLayoutRenderers\n }),\n [renderers, defaultFieldRenderers, layoutRenderers, defaultLayoutRenderers]\n );\n\n return (\n <FormViewRenderersContext.Provider value={value}>\n <div className=\"w-full flex flex-col gap-4\">\n {form.layout.map((node, index) => (\n <LayoutNodeRenderer key={index} node={node} />\n ))}\n </div>\n </FormViewRenderersContext.Provider>\n );\n});\n\nexport const LayoutNodeRenderer = observer(function LayoutNodeRenderer({\n node\n}: {\n node: LayoutNodeVM;\n}) {\n switch (node.type) {\n case \"row\":\n return <RowNodeRenderer node={node} />;\n case \"separator\":\n return <SeparatorNodeRenderer />;\n case \"tabs\":\n return <TabsNodeRenderer node={node} />;\n case \"element\":\n return <ElementNodeRenderer node={node} />;\n default:\n return null;\n }\n});\n\nconst RowNodeRenderer = observer(function RowNodeRenderer({ node }: { node: IRowNodeVM }) {\n const { fieldRenderers } = useFormViewRenderers();\n\n return (\n <div className=\"grid grid-cols-12 gap-4\">\n {node.fields.map(field => {\n const span = Math.floor(12 / node.fields.length);\n return (\n <div key={field.name} className={`col-span-${span}`}>\n <FieldRenderer field={field} renderers={fieldRenderers} />\n </div>\n );\n })}\n </div>\n );\n});\n\ninterface FieldRendererProps {\n field: IFieldVM;\n renderers: FieldRenderers;\n}\n\nconst FieldRenderer = observer(function FieldRenderer({ field, renderers }: FieldRendererProps) {\n const Renderer = field.renderer ? renderers[field.renderer] : undefined;\n\n if (!Renderer) {\n if (process.env.NODE_ENV === \"development\") {\n console.warn(\n `[FormView] No renderer found for field \"${field.name}\" (renderer: \"${field.renderer || \"none\"}\").`\n );\n }\n return null;\n }\n\n return <Renderer field={field} />;\n});\n\nconst SeparatorNodeRenderer = observer(function SeparatorNodeRenderer() {\n return <hr className=\"border-neutral-dimmed my-2\" />;\n});\n\nexport interface TabsNodeRendererProps {\n node: ITabsNodeVM;\n}\n\nconst TabsNodeRenderer = observer(function TabsNodeRenderer({ node }: TabsNodeRendererProps) {\n const { layoutRenderers } = useFormViewRenderers();\n\n if (node.renderer) {\n const CustomRenderer = layoutRenderers[node.renderer];\n if (CustomRenderer) {\n return <CustomRenderer node={node} />;\n }\n }\n\n const activeTab = node.tabs.find(t => t.id === node.activeTabId);\n\n return (\n <div>\n <div className=\"flex border-b border-neutral-dimmed\">\n {node.tabs.map(tab => (\n <button\n key={tab.id}\n type=\"button\"\n className={`px-4 py-2 text-sm font-medium border-b-2 ${\n tab.id === node.activeTabId\n ? \"border-primary text-primary\"\n : \"border-transparent text-neutral hover:text-neutral-strong\"\n }`}\n onClick={() => node.setActiveTab(tab.id)}\n >\n {tab.label}\n {tab.hasErrors && <span className=\"ml-1 text-destructive text-xs\">*</span>}\n </button>\n ))}\n </div>\n {activeTab && (\n <div className=\"pt-4\">\n {activeTab.description && (\n <p className=\"text-sm text-neutral mb-4\">{activeTab.description}</p>\n )}\n <div className=\"flex flex-col gap-4\">\n {activeTab.layout.map((childNode, index) => (\n <LayoutNodeRenderer key={index} node={childNode} />\n ))}\n </div>\n </div>\n )}\n </div>\n );\n});\n\nconst ElementNodeRenderer = observer(function ElementNodeRenderer({\n node\n}: {\n node: IElementNodeVM;\n}) {\n const { fieldRenderers } = useFormViewRenderers();\n const Renderer = fieldRenderers[`element:${node.renderer}`];\n\n if (!Renderer) {\n if (process.env.NODE_ENV === \"development\") {\n console.warn(`[FormView] No renderer found for element \"${node.renderer}\".`);\n }\n return null;\n }\n\n return <Renderer field={{ ...node.props } as any} />;\n});\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,46 @@
1
+ import type { IObjectFieldConfig, IObjectField, IListItemField, IField, IObjectFieldVM, IFieldValidation, IFormModel, FieldTypeMap, BeforeChangeCallback, AfterChangeCallback, AfterSetValueCallback, OnBlurCallback } from "./abstractions.js";
2
+ export declare function isObjectField(field: IField): field is ObjectField;
3
+ /**
4
+ * ObjectField — hierarchical field with children, using composition.
5
+ * Holds a `_base` Field for scalar behavior (callbacks, disabled/hidden, form ref).
6
+ * Adds its own children/items management for object and list modes.
7
+ */
8
+ export declare class ObjectField implements IObjectField {
9
+ readonly config: IObjectFieldConfig;
10
+ private _base;
11
+ private _form;
12
+ private _children;
13
+ private _items;
14
+ constructor(config: IObjectFieldConfig);
15
+ get name(): string;
16
+ get type(): string;
17
+ get visible(): boolean;
18
+ setDisabled(value: boolean): void;
19
+ setVisible(value: boolean): void;
20
+ setForm(form: IFormModel): void;
21
+ setValidation(validation: IFieldValidation): void;
22
+ addBeforeChange(cb: BeforeChangeCallback): void;
23
+ addAfterChange(cb: AfterChangeCallback): void;
24
+ addAfterSetValue(cb: AfterSetValueCallback): void;
25
+ addOnBlur(cb: OnBlurCallback): void;
26
+ blur(): void;
27
+ remove(): void;
28
+ get isList(): boolean;
29
+ get children(): Map<string, IField>;
30
+ get items(): IListItemField[];
31
+ getChild(name: string): IField | undefined;
32
+ getListItemChild(index: number, name: string): IField | undefined;
33
+ getData(): any;
34
+ getValue<T = unknown>(): T;
35
+ setValue(value: unknown): void;
36
+ setValueSilent(value: unknown): void;
37
+ addItem(data?: Record<string, unknown>): void;
38
+ removeItem(index: number): void;
39
+ moveItem(fromIndex: number, toIndex: number): void;
40
+ private _addItemInternal;
41
+ resetValidation(): void;
42
+ as<T extends keyof FieldTypeMap>(type: T): FieldTypeMap[T];
43
+ get vm(): IObjectFieldVM;
44
+ get hasErrors(): boolean;
45
+ validate(): Promise<boolean>;
46
+ }
@@ -0,0 +1,346 @@
1
+ import { makeAutoObservable, runInAction, toJS } from "mobx";
2
+ import { Field } from "./Field.js";
3
+ function createChildFields(childBuilders, form) {
4
+ const children = new Map();
5
+ for (const [name, builder] of Object.entries(childBuilders)) {
6
+ const config = builder.build(name);
7
+ const field = createFieldFromConfig(config, form);
8
+ children.set(name, field);
9
+ }
10
+ return children;
11
+ }
12
+ function createFieldFromConfig(config, form) {
13
+ if (config.childBuilders) {
14
+ const objField = new ObjectField(config);
15
+ if (form) {
16
+ objField.setForm(form);
17
+ }
18
+ return objField;
19
+ }
20
+ const field = new Field(config);
21
+ if (form) {
22
+ field.setForm(form);
23
+ }
24
+ return field;
25
+ }
26
+ function hydrateChildren(children, data) {
27
+ if (!data) {
28
+ return;
29
+ }
30
+ for (const [name, field] of children) {
31
+ if (name in data) {
32
+ field.setValueSilent(data[name]);
33
+ }
34
+ }
35
+ }
36
+ function getChildrenData(children) {
37
+ const data = {};
38
+ for (const [name, field] of children) {
39
+ if (isObjectField(field)) {
40
+ data[name] = toJS(field.getData());
41
+ } else {
42
+ data[name] = toJS(field.getValue());
43
+ }
44
+ }
45
+ return data;
46
+ }
47
+ async function validateChildren(children) {
48
+ let allValid = true;
49
+ for (const [, field] of children) {
50
+ const valid = await field.validate();
51
+ if (!valid) {
52
+ allValid = false;
53
+ }
54
+ }
55
+ return allValid;
56
+ }
57
+ export function isObjectField(field) {
58
+ return field.type === "object";
59
+ }
60
+ let itemKeyCounter = 0;
61
+ /**
62
+ * ObjectField — hierarchical field with children, using composition.
63
+ * Holds a `_base` Field for scalar behavior (callbacks, disabled/hidden, form ref).
64
+ * Adds its own children/items management for object and list modes.
65
+ */
66
+ export class ObjectField {
67
+ _form = null;
68
+ _items = [];
69
+ constructor(config) {
70
+ this.config = config;
71
+ this._base = new Field({
72
+ ...config,
73
+ type: "object",
74
+ renderer: config.renderer ?? "object"
75
+ });
76
+ this._children = createChildFields(config.childBuilders, null);
77
+ makeAutoObservable(this, {
78
+ config: false
79
+ });
80
+ }
81
+
82
+ // --- Forwarded from _base ---
83
+
84
+ get name() {
85
+ return this._base.name;
86
+ }
87
+ get type() {
88
+ return "object";
89
+ }
90
+ get visible() {
91
+ return this._base.visible;
92
+ }
93
+ setDisabled(value) {
94
+ this._base.setDisabled(value);
95
+ }
96
+ setVisible(value) {
97
+ this._base.setVisible(value);
98
+ }
99
+ setForm(form) {
100
+ this._form = form;
101
+ this._base.setForm(form);
102
+ for (const [, field] of this._children) {
103
+ field.setForm(form);
104
+ }
105
+ for (const item of this._items) {
106
+ for (const [, field] of item.children) {
107
+ field.setForm(form);
108
+ }
109
+ }
110
+ }
111
+ setValidation(validation) {
112
+ this._base.setValidation(validation);
113
+ }
114
+ addBeforeChange(cb) {
115
+ this._base.addBeforeChange(cb);
116
+ }
117
+ addAfterChange(cb) {
118
+ this._base.addAfterChange(cb);
119
+ }
120
+ addAfterSetValue(cb) {
121
+ this._base.addAfterSetValue(cb);
122
+ }
123
+ addOnBlur(cb) {
124
+ this._base.addOnBlur(cb);
125
+ }
126
+ blur() {
127
+ this._base.blur();
128
+ }
129
+ remove() {
130
+ this._base.remove();
131
+ }
132
+
133
+ // --- Object-specific ---
134
+
135
+ get isList() {
136
+ return this.config.isList;
137
+ }
138
+ get children() {
139
+ return this._children;
140
+ }
141
+ get items() {
142
+ return this._items.map(item => ({
143
+ key: item.key,
144
+ children: item.children,
145
+ getData: () => getChildrenData(item.children)
146
+ }));
147
+ }
148
+ getChild(name) {
149
+ return this._children.get(name);
150
+ }
151
+ getListItemChild(index, name) {
152
+ const item = this._items[index];
153
+ return item?.children.get(name);
154
+ }
155
+ getData() {
156
+ if (this.config.isList) {
157
+ return this._items.map(item => getChildrenData(item.children));
158
+ }
159
+ return getChildrenData(this._children);
160
+ }
161
+ getValue() {
162
+ return this.getData();
163
+ }
164
+ setValue(value) {
165
+ this.setValueSilent(value);
166
+ }
167
+ setValueSilent(value) {
168
+ if (this.config.isList) {
169
+ this._items = [];
170
+ if (Array.isArray(value)) {
171
+ for (const itemData of value) {
172
+ this._addItemInternal(itemData);
173
+ }
174
+ }
175
+ } else {
176
+ hydrateChildren(this._children, value);
177
+ }
178
+ }
179
+ addItem(data) {
180
+ this._addItemInternal(data);
181
+ }
182
+ removeItem(index) {
183
+ this._items.splice(index, 1);
184
+ }
185
+ moveItem(fromIndex, toIndex) {
186
+ if (fromIndex < 0 || fromIndex >= this._items.length || toIndex < 0 || toIndex >= this._items.length || fromIndex === toIndex) {
187
+ return;
188
+ }
189
+ const [item] = this._items.splice(fromIndex, 1);
190
+ this._items.splice(toIndex, 0, item);
191
+ }
192
+ _addItemInternal(data) {
193
+ const children = createChildFields(this.config.childBuilders, this._form);
194
+ if (data) {
195
+ hydrateChildren(children, data);
196
+ }
197
+ const key = `item_${++itemKeyCounter}`;
198
+ this._items.push({
199
+ key,
200
+ children
201
+ });
202
+ }
203
+ resetValidation() {
204
+ this._base.resetValidation();
205
+ for (const [, field] of this._children) {
206
+ field.resetValidation();
207
+ }
208
+ for (const item of this._items) {
209
+ for (const [, field] of item.children) {
210
+ field.resetValidation();
211
+ }
212
+ }
213
+ }
214
+ as(type) {
215
+ if (type !== "object") {
216
+ throw new Error(`Field "${this.config.name}" is type "object", not "${type}".`);
217
+ }
218
+ return this;
219
+ }
220
+ get vm() {
221
+ const baseVm = this._base.vm;
222
+ return {
223
+ name: baseVm.name,
224
+ type: "object",
225
+ label: baseVm.label,
226
+ help: baseVm.help,
227
+ description: baseVm.description,
228
+ note: baseVm.note,
229
+ placeholder: baseVm.placeholder,
230
+ value: this.getValue(),
231
+ validation: baseVm.validation,
232
+ required: baseVm.required,
233
+ disabled: baseVm.disabled,
234
+ renderer: baseVm.renderer,
235
+ rendererSettings: baseVm.rendererSettings,
236
+ onChange: value => this.setValue(value),
237
+ onBlur: () => this.blur(),
238
+ isList: this.config.isList,
239
+ fields: this.config.isList ? [] : Array.from(this._children.values()).filter(f => f.visible).map(f => f.vm),
240
+ items: this.config.isList ? this._items.map((item, index) => ({
241
+ key: item.key,
242
+ fields: Array.from(item.children.values()).filter(f => f.visible).map(f => f.vm),
243
+ remove: () => this.removeItem(index),
244
+ moveUp: () => this.moveItem(index, index - 1),
245
+ moveDown: () => this.moveItem(index, index + 1)
246
+ })) : [],
247
+ addItem: () => this.addItem(),
248
+ removeItem: index => this.removeItem(index),
249
+ moveItem: (from, to) => this.moveItem(from, to)
250
+ };
251
+ }
252
+ get hasErrors() {
253
+ const checkChildren = children => {
254
+ for (const [, field] of children) {
255
+ if (field.vm.validation.isValid === false) {
256
+ return true;
257
+ }
258
+ if (isObjectField(field) && field.hasErrors) {
259
+ return true;
260
+ }
261
+ }
262
+ return false;
263
+ };
264
+ if (this.config.isList) {
265
+ for (const item of this._items) {
266
+ if (checkChildren(item.children)) {
267
+ return true;
268
+ }
269
+ }
270
+ } else {
271
+ if (checkChildren(this._children)) {
272
+ return true;
273
+ }
274
+ }
275
+ return false;
276
+ }
277
+ async validate() {
278
+ if (this.config.required) {
279
+ if (this.config.isList && this._items.length === 0) {
280
+ this.setValidation({
281
+ isValid: false,
282
+ message: this.config.requiredMessage || "This field is required."
283
+ });
284
+ return false;
285
+ }
286
+ if (!this.config.isList) {
287
+ const data = this.getData();
288
+ const hasAnyValue = Object.values(data).some(v => v !== null && v !== undefined && v !== "");
289
+ if (!hasAnyValue) {
290
+ this.setValidation({
291
+ isValid: false,
292
+ message: this.config.requiredMessage || "This field is required."
293
+ });
294
+ return false;
295
+ }
296
+ }
297
+ }
298
+ if (this.config.isList && this.config.listSchema) {
299
+ const listData = this.getData();
300
+ const result = await this.config.listSchema.safeParseAsync(listData);
301
+ if (!result.success) {
302
+ const firstIssue = result.error.issues[0];
303
+ runInAction(() => {
304
+ this.setValidation({
305
+ isValid: false,
306
+ message: firstIssue?.message || "Invalid value."
307
+ });
308
+ });
309
+ return false;
310
+ }
311
+ }
312
+ if (this.config.schema) {
313
+ const data = this.getData();
314
+ const result = await this.config.schema.safeParseAsync(data);
315
+ if (!result.success) {
316
+ const firstIssue = result.error.issues[0];
317
+ runInAction(() => {
318
+ this.setValidation({
319
+ isValid: false,
320
+ message: firstIssue?.message || "Invalid value."
321
+ });
322
+ });
323
+ return false;
324
+ }
325
+ }
326
+ let allValid = true;
327
+ if (this.config.isList) {
328
+ for (const item of this._items) {
329
+ const valid = await validateChildren(item.children);
330
+ if (!valid) {
331
+ allValid = false;
332
+ }
333
+ }
334
+ } else {
335
+ allValid = await validateChildren(this._children);
336
+ }
337
+ runInAction(() => {
338
+ this.setValidation({
339
+ isValid: allValid
340
+ });
341
+ });
342
+ return allValid;
343
+ }
344
+ }
345
+
346
+ //# sourceMappingURL=ObjectField.js.map