@acorex/platform 20.3.0-next.8 → 20.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/common/index.d.ts +643 -367
  2. package/core/index.d.ts +91 -66
  3. package/fesm2022/acorex-platform-auth.mjs +19 -19
  4. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-common-common-settings.provider-9OHien_H.mjs +47 -0
  6. package/fesm2022/acorex-platform-common-common-settings.provider-9OHien_H.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-common.mjs +678 -252
  8. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-core.mjs +173 -161
  10. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-domain.mjs +16 -16
  12. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-builder.mjs +1946 -1947
  14. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  15. package/fesm2022/acorex-platform-layout-components.mjs +1902 -904
  16. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-layout-designer.mjs +96 -89
  18. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-entity-create-entity.command-DyXF9zAh.mjs +52 -0
  20. package/fesm2022/acorex-platform-layout-entity-create-entity.command-DyXF9zAh.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-layout-entity.mjs +2237 -1834
  22. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-views.mjs +64 -62
  24. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  25. package/fesm2022/acorex-platform-layout-widget-core.mjs +2758 -0
  26. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -0
  27. package/fesm2022/{acorex-platform-widgets-button-widget-designer.component-C2Qn1YAW.mjs → acorex-platform-layout-widgets-button-widget-designer.component-C_3IWNkj.mjs} +6 -6
  28. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-C_3IWNkj.mjs.map +1 -0
  29. package/fesm2022/{acorex-platform-widgets-extra-properties-schema-widget-edit.component-D9mf08rU.mjs → acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-CJltEgut.mjs} +5 -5
  30. package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-CJltEgut.mjs.map +1 -0
  31. package/fesm2022/{acorex-platform-widgets-extra-properties-schema-widget-view.component-D6GQ-eyr.mjs → acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-pM-TIuk0.mjs} +5 -5
  32. package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-pM-TIuk0.mjs.map +1 -0
  33. package/fesm2022/{acorex-platform-widgets-extra-properties-values-widget-edit.component-DVbIdVZ6.mjs → acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-BqI96-fU.mjs} +5 -5
  34. package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-BqI96-fU.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-widgets-extra-properties-values-widget-view.component-D-aM64Hu.mjs → acorex-platform-layout-widgets-extra-properties-values-widget-view.component-C-AhenaM.mjs} +5 -5
  36. package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-view.component-C-AhenaM.mjs.map +1 -0
  37. package/fesm2022/{acorex-platform-widgets-extra-properties-widget-edit.component-em2-aU8E.mjs → acorex-platform-layout-widgets-extra-properties-widget-edit.component-DCAya5ne.mjs} +5 -5
  38. package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-edit.component-DCAya5ne.mjs.map +1 -0
  39. package/fesm2022/{acorex-platform-widgets-extra-properties-widget-view.component-BeuIofdr.mjs → acorex-platform-layout-widgets-extra-properties-widget-view.component-D-PnBqLb.mjs} +5 -5
  40. package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-view.component-D-PnBqLb.mjs.map +1 -0
  41. package/fesm2022/{acorex-platform-widgets-file-list-popup.component-rW2RD35f.mjs → acorex-platform-layout-widgets-file-list-popup.component-DuuFHWvB.mjs} +10 -10
  42. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-DuuFHWvB.mjs.map +1 -0
  43. package/fesm2022/{acorex-platform-widgets-page-widget-designer.component-DNvnQ4Mc.mjs → acorex-platform-layout-widgets-page-widget-designer.component-Bss0xUcu.mjs} +8 -8
  44. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-Bss0xUcu.mjs.map +1 -0
  45. package/fesm2022/{acorex-platform-widgets-tabular-data-edit-popup.component-CPVRbE8B.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-Cy9mHnNP.mjs} +14 -14
  46. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Cy9mHnNP.mjs.map +1 -0
  47. package/fesm2022/{acorex-platform-widgets-tabular-data-view-popup.component-Dmg5DdX8.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-DznLtuer.mjs} +6 -5
  48. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-DznLtuer.mjs.map +1 -0
  49. package/fesm2022/{acorex-platform-widgets-text-block-widget-designer.component-yADN3Xji.mjs → acorex-platform-layout-widgets-text-block-widget-designer.component-ndOUSFi9.mjs} +6 -7
  50. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-ndOUSFi9.mjs.map +1 -0
  51. package/fesm2022/{acorex-platform-widgets.mjs → acorex-platform-layout-widgets.mjs} +9918 -8826
  52. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -0
  53. package/fesm2022/acorex-platform-native.mjs +7 -7
  54. package/fesm2022/acorex-platform-native.mjs.map +1 -1
  55. package/fesm2022/acorex-platform-runtime.mjs +40 -40
  56. package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
  57. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-B1PT6FtZ.mjs +115 -0
  58. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-B1PT6FtZ.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-rdKxuMC_.mjs +742 -0
  60. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-rdKxuMC_.mjs.map +1 -0
  61. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-4g19A3eI.mjs +101 -0
  62. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-4g19A3eI.mjs.map +1 -0
  63. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-BExtm1JE.mjs → acorex-platform-themes-default-entity-master-single-view.component-B8gx5cG7.mjs} +17 -17
  64. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-B8gx5cG7.mjs.map +1 -0
  65. package/fesm2022/{acorex-platform-themes-default-error-401.component-DrO1PEOH.mjs → acorex-platform-themes-default-error-401.component-CcvGfdhu.mjs} +4 -4
  66. package/fesm2022/{acorex-platform-themes-default-error-401.component-DrO1PEOH.mjs.map → acorex-platform-themes-default-error-401.component-CcvGfdhu.mjs.map} +1 -1
  67. package/fesm2022/{acorex-platform-themes-default-error-404.component-DqVq0oHX.mjs → acorex-platform-themes-default-error-404.component-4-CaEsnV.mjs} +4 -4
  68. package/fesm2022/{acorex-platform-themes-default-error-404.component-DqVq0oHX.mjs.map → acorex-platform-themes-default-error-404.component-4-CaEsnV.mjs.map} +1 -1
  69. package/fesm2022/{acorex-platform-themes-default-error-offline.component-Bt2PTL7_.mjs → acorex-platform-themes-default-error-offline.component-BNecbFEj.mjs} +4 -4
  70. package/fesm2022/{acorex-platform-themes-default-error-offline.component-Bt2PTL7_.mjs.map → acorex-platform-themes-default-error-offline.component-BNecbFEj.mjs.map} +1 -1
  71. package/fesm2022/acorex-platform-themes-default.mjs +117 -51
  72. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  73. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-BgEh06Tn.mjs → acorex-platform-themes-shared-icon-chooser-view.component-Dc_Txe32.mjs} +5 -5
  74. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-Dc_Txe32.mjs.map +1 -0
  75. package/fesm2022/{acorex-platform-themes-shared-settings.provider-CXiRmniv.mjs → acorex-platform-themes-shared-settings.provider-DY2xFnrv.mjs} +9 -9
  76. package/fesm2022/acorex-platform-themes-shared-settings.provider-DY2xFnrv.mjs.map +1 -0
  77. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-column.component-AeOQxjbS.mjs → acorex-platform-themes-shared-theme-color-chooser-column.component-hgWLhhle.mjs} +5 -5
  78. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-hgWLhhle.mjs.map +1 -0
  79. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-view.component-DEVzRd6-.mjs → acorex-platform-themes-shared-theme-color-chooser-view.component-CY3JZK_W.mjs} +5 -5
  80. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-CY3JZK_W.mjs.map +1 -0
  81. package/fesm2022/acorex-platform-themes-shared.mjs +217 -75
  82. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  83. package/fesm2022/acorex-platform-workflow.mjs +27 -39
  84. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  85. package/layout/builder/README.md +1577 -3
  86. package/layout/builder/index.d.ts +735 -813
  87. package/layout/components/index.d.ts +384 -433
  88. package/layout/designer/index.d.ts +8 -6
  89. package/layout/entity/index.d.ts +953 -338
  90. package/layout/views/index.d.ts +13 -14
  91. package/layout/widget-core/README.md +4 -0
  92. package/layout/widget-core/index.d.ts +959 -0
  93. package/layout/widgets/README.md +4 -0
  94. package/{widgets → layout/widgets}/index.d.ts +768 -655
  95. package/package.json +14 -10
  96. package/themes/shared/index.d.ts +2 -2
  97. package/workflow/index.d.ts +3 -173
  98. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-BXbkGGei.mjs +0 -115
  99. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-BXbkGGei.mjs.map +0 -1
  100. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-gQIK6PIx.mjs +0 -706
  101. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-gQIK6PIx.mjs.map +0 -1
  102. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Bp1JLsj1.mjs +0 -101
  103. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Bp1JLsj1.mjs.map +0 -1
  104. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-BExtm1JE.mjs.map +0 -1
  105. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BgEh06Tn.mjs.map +0 -1
  106. package/fesm2022/acorex-platform-themes-shared-settings.provider-CXiRmniv.mjs.map +0 -1
  107. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-AeOQxjbS.mjs.map +0 -1
  108. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DEVzRd6-.mjs.map +0 -1
  109. package/fesm2022/acorex-platform-widgets-button-widget-designer.component-C2Qn1YAW.mjs.map +0 -1
  110. package/fesm2022/acorex-platform-widgets-checkbox-widget-column.component-CzEFmKWG.mjs +0 -84
  111. package/fesm2022/acorex-platform-widgets-checkbox-widget-column.component-CzEFmKWG.mjs.map +0 -1
  112. package/fesm2022/acorex-platform-widgets-checkbox-widget-designer.component-BXPrXy-h.mjs +0 -55
  113. package/fesm2022/acorex-platform-widgets-checkbox-widget-designer.component-BXPrXy-h.mjs.map +0 -1
  114. package/fesm2022/acorex-platform-widgets-checkbox-widget-view.component-KYCQ2qTJ.mjs +0 -92
  115. package/fesm2022/acorex-platform-widgets-checkbox-widget-view.component-KYCQ2qTJ.mjs.map +0 -1
  116. package/fesm2022/acorex-platform-widgets-color-box-widget-designer.component-BVZ7lWm9.mjs +0 -55
  117. package/fesm2022/acorex-platform-widgets-color-box-widget-designer.component-BVZ7lWm9.mjs.map +0 -1
  118. package/fesm2022/acorex-platform-widgets-extra-properties-schema-widget-edit.component-D9mf08rU.mjs.map +0 -1
  119. package/fesm2022/acorex-platform-widgets-extra-properties-schema-widget-view.component-D6GQ-eyr.mjs.map +0 -1
  120. package/fesm2022/acorex-platform-widgets-extra-properties-values-widget-edit.component-DVbIdVZ6.mjs.map +0 -1
  121. package/fesm2022/acorex-platform-widgets-extra-properties-values-widget-view.component-D-aM64Hu.mjs.map +0 -1
  122. package/fesm2022/acorex-platform-widgets-extra-properties-widget-edit.component-em2-aU8E.mjs.map +0 -1
  123. package/fesm2022/acorex-platform-widgets-extra-properties-widget-view.component-BeuIofdr.mjs.map +0 -1
  124. package/fesm2022/acorex-platform-widgets-file-list-popup.component-rW2RD35f.mjs.map +0 -1
  125. package/fesm2022/acorex-platform-widgets-page-widget-designer.component-DNvnQ4Mc.mjs.map +0 -1
  126. package/fesm2022/acorex-platform-widgets-rich-text-popup.component-Cydlpsat.mjs +0 -40
  127. package/fesm2022/acorex-platform-widgets-rich-text-popup.component-Cydlpsat.mjs.map +0 -1
  128. package/fesm2022/acorex-platform-widgets-tabular-data-edit-popup.component-CPVRbE8B.mjs.map +0 -1
  129. package/fesm2022/acorex-platform-widgets-tabular-data-view-popup.component-Dmg5DdX8.mjs.map +0 -1
  130. package/fesm2022/acorex-platform-widgets-text-block-widget-designer.component-yADN3Xji.mjs.map +0 -1
  131. package/fesm2022/acorex-platform-widgets.mjs.map +0 -1
  132. package/widgets/README.md +0 -4
@@ -1,4 +1,1578 @@
1
- # @acorex/platform/layout
1
+ # ACoreX Layout Builder System
2
2
 
3
- Secondary entry point of `@acorex/platform`. It can be used by importing from `@acorex/platform/layout`.
4
-
3
+ The **Layout Builder** is a powerful, fluent API system for creating dynamic layouts, pages, forms, and dialogs in the ACoreX Platform. It replaces the older Dynamic Form Builder with a more flexible, type-safe, and comprehensive solution.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Key Features](#key-features)
9
+ - [Architecture](#architecture)
10
+ - [Getting Started](#getting-started)
11
+ - [Core Concepts](#core-concepts)
12
+ - [Container Types](#container-types)
13
+ - [Form Fields](#form-fields)
14
+ - [Widget Types](#widget-types)
15
+ - [Dialog Builder](#dialog-builder)
16
+ - [Layout Inheritance](#layout-inheritance)
17
+ - [Complete Examples](#complete-examples)
18
+ - [Migration Guide](#migration-guide)
19
+ - [Best Practices](#best-practices)
20
+ - [API Reference](#api-reference)
21
+
22
+ ---
23
+
24
+ ## Overview
25
+
26
+ The Layout Builder provides a fluent, delegate-based API for creating complex layouts without manually constructing configuration objects. It supports:
27
+
28
+ - **🎨 Multiple Container Types**: Flex, Grid, Panel, Page, Tabset, Fieldset
29
+ - **📝 Rich Form Capabilities**: Form fields with automatic path generation
30
+ - **🔄 Property Inheritance**: Automatic propagation of mode, readonly, disabled, and visibility
31
+ - **💬 Dialog Support**: Built-in dialog builder with action management
32
+ - **✅ Type Safety**: Full TypeScript support with IntelliSense
33
+ - **🎯 Fluent API**: Readable, chainable method calls with delegate pattern
34
+ - **🔧 Widget Support**: 15+ built-in widgets plus custom widget support
35
+
36
+ ### Comparison: Layout Builder vs Dynamic Form Builder
37
+
38
+ | Feature | Dynamic Form Builder | Layout Builder |
39
+ |----------------------------------|----------------------|----------------|
40
+ | **Purpose** | Forms only | Full layouts + forms |
41
+ | **Container Types** | Groups only | Flex, Grid, Panel, Page, Tabset, Fieldset |
42
+ | **Nesting** | Limited | Unlimited nesting |
43
+ | **Layout Control** | Basic | Advanced (Flexbox, Grid) |
44
+ | **Property Inheritance** | No | Yes (mode, readonly, disabled) |
45
+ | **Dialog Support** | Separate service | Built-in |
46
+ | **Widget Path Management** | Manual | Automatic |
47
+ | **Custom Widgets** | Limited | Full support |
48
+
49
+ ---
50
+
51
+ ## Key Features
52
+
53
+ ### 1. Fluent API with Delegate Pattern
54
+
55
+ ```typescript
56
+ builder.flex(flexContainer => {
57
+ flexContainer
58
+ .setDirection('column')
59
+ .setGap('16px')
60
+ .formField('First Name', field => {
61
+ field.path('firstName');
62
+ field.textBox({ placeholder: 'Enter name' });
63
+ });
64
+ });
65
+ ```
66
+
67
+ ### 2. Automatic Property Inheritance
68
+
69
+ ```typescript
70
+ // Parent container sets mode to 'view'
71
+ builder.flex(flexContainer => {
72
+ flexContainer.mode('view'); // All children inherit 'view' mode
73
+
74
+ flexContainer.formField('Name', field => {
75
+ field.textBox(); // Automatically in 'view' mode
76
+ });
77
+ });
78
+ ```
79
+
80
+ ### 3. Automatic Path Generation
81
+
82
+ ```typescript
83
+ // Path is automatically generated from field name
84
+ flexContainer.formField('First Name', field => {
85
+ field.textBox(); // path = 'first_name' (auto-generated)
86
+ });
87
+
88
+ // Or explicitly set path
89
+ flexContainer.formField('First Name', field => {
90
+ field.path('user.firstName');
91
+ field.textBox();
92
+ });
93
+ ```
94
+
95
+ ### 4. Nested Containers
96
+
97
+ ```typescript
98
+ builder.flex(mainContainer => {
99
+ mainContainer
100
+ .setDirection('column')
101
+ .panel(panel => {
102
+ panel
103
+ .setCaption('Personal Info')
104
+ .flex(innerFlex => {
105
+ innerFlex.setDirection('row');
106
+ // Add widgets...
107
+ });
108
+ });
109
+ });
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Architecture
115
+
116
+ ### Service Structure
117
+
118
+ ```
119
+ AXPLayoutBuilderService
120
+ └── create() → ILayoutBuilder
121
+ ├── flex()
122
+ ├── grid()
123
+ ├── panel()
124
+ ├── page()
125
+ ├── tabset()
126
+ ├── fieldset()
127
+ ├── dialog()
128
+ └── build() → AXPWidgetNode
129
+ ```
130
+
131
+ ### Builder Hierarchy
132
+
133
+ ```
134
+ BaseContainerBuilder (Abstract)
135
+ ├── LayoutContainerMixin
136
+ │ ├── ChildContainerMixin
137
+ │ │ └── WidgetContainerMixin
138
+ │ │ ├── FlexContainerBuilder
139
+ │ │ ├── GridContainerBuilder
140
+ │ │ ├── PanelContainerBuilder
141
+ │ │ ├── PageContainerBuilder
142
+ │ │ ├── TabsetContainerBuilder
143
+ │ │ └── ListWidgetBuilder
144
+ │ └── FieldsetContainerBuilder
145
+ │ └── FormFieldBuilder
146
+ └── DialogContainerBuilder
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Getting Started
152
+
153
+ ### Installation
154
+
155
+ The Layout Builder is part of the `@acorex/platform/layout/builder` package:
156
+
157
+ ```typescript
158
+ import { AXPLayoutBuilderService, AXPLayoutRendererComponent } from '@acorex/platform/layout/builder';
159
+ ```
160
+
161
+ ### Basic Setup
162
+
163
+ **Component:**
164
+
165
+ ```typescript
166
+ import { Component, OnInit, inject, signal } from '@angular/core';
167
+ import { AXPLayoutBuilderService, AXPLayoutRendererComponent } from '@acorex/platform/layout/builder';
168
+ import { AXPWidgetNode } from '@acorex/platform/layout/widget-core';
169
+
170
+ @Component({
171
+ selector: 'app-my-page',
172
+ standalone: true,
173
+ imports: [AXPLayoutRendererComponent],
174
+ template: `
175
+ <axp-layout-renderer
176
+ [layout]="layoutDefinition()"
177
+ [(context)]="formContext">
178
+ </axp-layout-renderer>
179
+ `
180
+ })
181
+ export class MyPageComponent implements OnInit {
182
+ private readonly layoutBuilder = inject(AXPLayoutBuilderService);
183
+
184
+ layoutDefinition = signal<AXPWidgetNode | undefined>(undefined);
185
+ formContext = signal<any>({
186
+ firstName: '',
187
+ lastName: '',
188
+ email: ''
189
+ });
190
+
191
+ ngOnInit() {
192
+ this.buildLayout();
193
+ }
194
+
195
+ buildLayout() {
196
+ const builder = this.layoutBuilder.create();
197
+
198
+ builder.flex(flexContainer => {
199
+ flexContainer
200
+ .setDirection('column')
201
+ .setGap('16px')
202
+ .formField('First Name', field => {
203
+ field.path('firstName');
204
+ field.textBox({ placeholder: 'Enter first name' });
205
+ })
206
+ .formField('Last Name', field => {
207
+ field.path('lastName');
208
+ field.textBox({ placeholder: 'Enter last name' });
209
+ });
210
+ });
211
+
212
+ this.layoutDefinition.set(builder.build());
213
+ }
214
+ }
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Core Concepts
220
+
221
+ ### 1. Containers
222
+
223
+ Containers hold other containers or widgets. They provide layout structure.
224
+
225
+ **Available Containers:**
226
+ - **Flex**: Flexbox layout (row/column)
227
+ - **Grid**: CSS Grid layout
228
+ - **Panel**: Card-like container with header
229
+ - **Page**: Full page layout
230
+ - **Tabset**: Tabbed container
231
+ - **Fieldset**: Form section with title/description
232
+
233
+ ### 2. Form Fields
234
+
235
+ Form fields are special containers that wrap a single widget and provide a label.
236
+
237
+ ```typescript
238
+ .formField('Label', field => {
239
+ field.path('fieldPath'); // Data binding path
240
+ field.textBox({ ... }); // Widget type
241
+ });
242
+ ```
243
+
244
+ ### 3. Widgets
245
+
246
+ Widgets are the actual input/display components. They are always contained within form fields or containers.
247
+
248
+ ### 4. Inheritance Context
249
+
250
+ Properties like `mode`, `readonly`, `disabled`, and `visible` are automatically inherited from parent containers.
251
+
252
+ ```typescript
253
+ builder.flex(flexContainer => {
254
+ flexContainer.mode('view'); // Set parent mode
255
+ flexContainer.readonly(true); // All children readonly
256
+
257
+ flexContainer.formField('Name', field => {
258
+ field.textBox(); // Automatically: mode='view', readonly=true
259
+ });
260
+ });
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Container Types
266
+
267
+ ### 1. Flex Container
268
+
269
+ **Purpose**: Flexbox layout for responsive row/column arrangements.
270
+
271
+ ```typescript
272
+ builder.flex(flexContainer => {
273
+ flexContainer
274
+ .setDirection('column') // row | column | row-reverse | column-reverse
275
+ .setJustifyContent('center') // flex-start | flex-end | center | space-between | space-around
276
+ .setAlignItems('stretch') // flex-start | flex-end | center | baseline | stretch
277
+ .setGap('16px') // Gap between items
278
+ .setWrap('wrap') // nowrap | wrap | wrap-reverse
279
+ .setPadding('20px')
280
+ .setMargin('10px')
281
+ .setBackgroundColor('#f5f5f5');
282
+ });
283
+ ```
284
+
285
+ ### 2. Grid Container
286
+
287
+ **Purpose**: CSS Grid layout for complex 2D layouts.
288
+
289
+ ```typescript
290
+ builder.grid(gridContainer => {
291
+ gridContainer
292
+ .setColumns(3) // Number of columns
293
+ .setRows(2) // Number of rows
294
+ .setGap('16px') // Gap between cells
295
+ .setJustifyItems('center') // start | end | center | stretch
296
+ .setAlignItems('center') // start | end | center | stretch
297
+ .setAutoFlow('row') // row | column | row dense | column dense
298
+ .setPadding('20px')
299
+ .setBackgroundColor('#ffffff');
300
+ });
301
+ ```
302
+
303
+ ### 3. Panel Container
304
+
305
+ **Purpose**: Card-like container with optional header, icon, and styling.
306
+
307
+ ```typescript
308
+ builder.panel(panel => {
309
+ panel
310
+ .setCaption('User Information') // Header text
311
+ .setIcon('fa-user') // Header icon
312
+ .setLook('outline') // solid | fill | outline | flat | none
313
+ .setShowHeader(true) // Show/hide header
314
+ .setCollapsed(false) // Collapsed state
315
+ .formField('Name', field => {
316
+ field.textBox();
317
+ });
318
+ });
319
+ ```
320
+
321
+ ### 4. Page Container
322
+
323
+ **Purpose**: Full page layout with theme support.
324
+
325
+ ```typescript
326
+ builder.page(page => {
327
+ page
328
+ .setBackgroundColor('#f9f9f9')
329
+ .setTheme({ id: 'light-theme' })
330
+ .setHasHeader(true)
331
+ .setHasFooter(true)
332
+ .setDirection('ltr')
333
+ .flex(content => {
334
+ // Page content
335
+ });
336
+ });
337
+ ```
338
+
339
+ ### 5. Tabset Container
340
+
341
+ **Purpose**: Tabbed interface for organizing content.
342
+
343
+ ```typescript
344
+ builder.tabset(tabset => {
345
+ tabset
346
+ .setLook('with-line') // with-line | with-line-color | pills | pills-color
347
+ .setOrientation('horizontal') // vertical | horizontal
348
+ .setActiveIndex(0) // Initial active tab
349
+ .panel(tab1 => {
350
+ tab1.setCaption('Tab 1');
351
+ // Tab 1 content
352
+ })
353
+ .panel(tab2 => {
354
+ tab2.setCaption('Tab 2');
355
+ // Tab 2 content
356
+ });
357
+ });
358
+ ```
359
+
360
+ ### 6. Fieldset Container
361
+
362
+ **Purpose**: Form section with title, description, and multi-column support.
363
+
364
+ ```typescript
365
+ builder.fieldset(fieldset => {
366
+ fieldset
367
+ .setTitle('Personal Information')
368
+ .setDescription('Please provide your personal details')
369
+ .setIcon('fa-user')
370
+ .setCols(2) // Number of columns for fields
371
+ .setCollapsible(true)
372
+ .setIsOpen(true)
373
+ .setLook('fieldset') // fieldset | card | group
374
+ .setShowHeader(true)
375
+ .formField('First Name', field => {
376
+ field.textBox();
377
+ })
378
+ .formField('Last Name', field => {
379
+ field.textBox();
380
+ });
381
+ });
382
+ ```
383
+
384
+ ---
385
+
386
+ ## Form Fields
387
+
388
+ Form fields wrap widgets and provide labels, descriptions, and path management.
389
+
390
+ ### Basic Form Field
391
+
392
+ ```typescript
393
+ .formField('Field Label', field => {
394
+ field.path('dataPath');
395
+ field.textBox({ placeholder: 'Enter value' });
396
+ });
397
+ ```
398
+
399
+ ### Form Field Methods
400
+
401
+ ```typescript
402
+ .formField('Email', field => {
403
+ field.path('user.email'); // Data binding path
404
+ field.setLabel('Email Address'); // Custom label
405
+ field.setShowLabel(true); // Show/hide label
406
+ field.mode('edit'); // edit | view
407
+ field.visible(true); // boolean | expression string
408
+ field.readonly(false); // boolean | expression string
409
+ field.disabled(false); // boolean | expression string
410
+ field.textBox({
411
+ placeholder: 'email@example.com',
412
+ prefix: '📧'
413
+ });
414
+ });
415
+ ```
416
+
417
+ ### Auto-Generated Paths
418
+
419
+ ```typescript
420
+ // Path from field name
421
+ .formField('First Name', field => {
422
+ field.textBox(); // path = 'first_name'
423
+ });
424
+
425
+ // Path from explicit name
426
+ .formField('Full Name', field => {
427
+ field.name('fullName');
428
+ field.textBox(); // path = 'fullName'
429
+ });
430
+
431
+ // Path from explicit path
432
+ .formField('Name', field => {
433
+ field.path('user.profile.fullName');
434
+ field.textBox(); // path = 'user.profile.fullName'
435
+ });
436
+ ```
437
+
438
+ ---
439
+
440
+ ## Widget Types
441
+
442
+ ### Text Input Widgets
443
+
444
+ #### 1. Text Box
445
+
446
+ ```typescript
447
+ field.textBox({
448
+ placeholder: 'Enter text',
449
+ maxLength: 100,
450
+ minLength: 3,
451
+ prefix: '🔍',
452
+ suffix: '.com',
453
+ clearButton: true
454
+ });
455
+ ```
456
+
457
+ #### 2. Large Text Box
458
+
459
+ ```typescript
460
+ field.largeTextBox({
461
+ placeholder: 'Enter description',
462
+ rows: 4,
463
+ maxLength: 1000,
464
+ resizable: true
465
+ });
466
+ ```
467
+
468
+ #### 3. Rich Text Editor
469
+
470
+ ```typescript
471
+ field.richText({
472
+ placeholder: 'Enter formatted text',
473
+ toolbar: ['bold', 'italic', 'underline', 'link']
474
+ });
475
+ ```
476
+
477
+ #### 4. Password Box
478
+
479
+ ```typescript
480
+ field.passwordBox({
481
+ placeholder: 'Enter password',
482
+ revealToggle: true,
483
+ maxLength: 50
484
+ });
485
+ ```
486
+
487
+ ### Selection Widgets
488
+
489
+ #### 5. Select Box
490
+
491
+ ```typescript
492
+ field.selectBox({
493
+ dataSource: ['Option 1', 'Option 2', 'Option 3'],
494
+ placeholder: 'Select option',
495
+ multiple: false,
496
+ searchable: true,
497
+ clearButton: true
498
+ });
499
+
500
+ // With objects
501
+ field.selectBox({
502
+ dataSource: [
503
+ { id: 1, name: 'Option 1' },
504
+ { id: 2, name: 'Option 2' }
505
+ ],
506
+ valueField: 'id',
507
+ textField: 'name',
508
+ placeholder: 'Select option'
509
+ });
510
+ ```
511
+
512
+ #### 6. Lookup Box
513
+
514
+ ```typescript
515
+ field.lookupBox({
516
+ entity: 'human-capital-management.employee',
517
+ multiple: true,
518
+ expose: [
519
+ { source: 'id', target: 'employeeIds.{id}' },
520
+ { source: 'title', target: 'employeeNames.{title}' }
521
+ ],
522
+ searchable: true,
523
+ allowClear: true
524
+ });
525
+ ```
526
+
527
+ #### 7. Selection List
528
+
529
+ ```typescript
530
+ field.selectionList({
531
+ dataSource: ['Option A', 'Option B', 'Option C'],
532
+ multiple: true,
533
+ searchable: false
534
+ });
535
+ ```
536
+
537
+ ### Numeric Widgets
538
+
539
+ #### 8. Number Box
540
+
541
+ ```typescript
542
+ field.numberBox({
543
+ min: 0,
544
+ max: 100,
545
+ step: 1,
546
+ format: '#,##0.00',
547
+ placeholder: 'Enter number'
548
+ });
549
+ ```
550
+
551
+ ### Date/Time Widgets
552
+
553
+ #### 9. Date Time Box
554
+
555
+ ```typescript
556
+ field.dateTimeBox({
557
+ type: 'date', // date | time | datetime
558
+ format: 'YYYY-MM-DD',
559
+ min: '2024-01-01',
560
+ max: '2024-12-31',
561
+ clearButton: true,
562
+ placeholder: 'Select date'
563
+ });
564
+ ```
565
+
566
+ ### Boolean Widgets
567
+
568
+ #### 10. Toggle Switch
569
+
570
+ ```typescript
571
+ field.toggleSwitch({
572
+ label: 'Enable notifications',
573
+ trueText: 'On',
574
+ falseText: 'Off'
575
+ });
576
+ ```
577
+
578
+ ### Color Widgets
579
+
580
+ #### 11. Color Box
581
+
582
+ ```typescript
583
+ field.colorBox({
584
+ format: 'hex', // hex | rgb | hsl
585
+ showAlpha: true,
586
+ showPalette: true
587
+ });
588
+ ```
589
+
590
+ ### Custom Widgets
591
+
592
+ ```typescript
593
+ field.customWidget('signature-pad', {
594
+ width: 400,
595
+ height: 200,
596
+ backgroundColor: '#ffffff'
597
+ });
598
+ ```
599
+
600
+ ---
601
+
602
+ ## Dialog Builder
603
+
604
+ The Layout Builder includes built-in dialog support with automatic form rendering and action management.
605
+
606
+ ### Basic Dialog
607
+
608
+ ```typescript
609
+ async showDialog() {
610
+ const dialogRef = await this.layoutBuilder
611
+ .create()
612
+ .dialog(dialog => {
613
+ dialog
614
+ .setTitle('User Information')
615
+ .setSize('md') // sm | md | lg | xl
616
+ .setCloseButton(false)
617
+ .setContext({ firstName: '', lastName: '' })
618
+ .content(layoutBuilder => {
619
+ layoutBuilder.flex(flex => {
620
+ flex
621
+ .setDirection('column')
622
+ .setGap('16px')
623
+ .formField('First Name', field => {
624
+ field.path('firstName');
625
+ field.textBox({ placeholder: 'Enter first name' });
626
+ })
627
+ .formField('Last Name', field => {
628
+ field.path('lastName');
629
+ field.textBox({ placeholder: 'Enter last name' });
630
+ });
631
+ });
632
+ })
633
+ .setActions(actions => {
634
+ actions
635
+ .cancel('@general:actions.cancel.title')
636
+ .submit('@general:actions.submit.title');
637
+ });
638
+ })
639
+ .show();
640
+
641
+ // Wait for user interaction
642
+ const formData = dialogRef.context();
643
+ const action = dialogRef.action();
644
+
645
+ if (action === 'submit') {
646
+ console.log('Form data:', formData);
647
+ }
648
+
649
+ dialogRef.close();
650
+ }
651
+ ```
652
+
653
+ ### Dialog with Custom Actions
654
+
655
+ ```typescript
656
+ .setActions(actions => {
657
+ actions
658
+ .cancel('@general:actions.cancel.title')
659
+ .submit('@general:actions.save.title')
660
+ .custom({
661
+ title: 'Save Draft',
662
+ icon: 'fa-save',
663
+ color: 'secondary',
664
+ command: { name: 'save-draft' }
665
+ });
666
+ })
667
+ ```
668
+
669
+ ### Dialog with Nested Containers
670
+
671
+ ```typescript
672
+ .content(layoutBuilder => {
673
+ layoutBuilder.flex(mainFlex => {
674
+ mainFlex
675
+ .setDirection('column')
676
+ .setGap('20px')
677
+ .fieldset(fieldset => {
678
+ fieldset
679
+ .setTitle('Personal Info')
680
+ .setCols(2)
681
+ .formField('First Name', field => {
682
+ field.textBox();
683
+ })
684
+ .formField('Last Name', field => {
685
+ field.textBox();
686
+ });
687
+ })
688
+ .panel(panel => {
689
+ panel
690
+ .setCaption('Additional Info')
691
+ .formField('Phone', field => {
692
+ field.textBox();
693
+ });
694
+ });
695
+ });
696
+ })
697
+ ```
698
+
699
+ ---
700
+
701
+ ## Layout Inheritance
702
+
703
+ Properties are automatically inherited from parent containers to children.
704
+
705
+ ### Inherited Properties
706
+
707
+ - **mode**: `edit` | `view`
708
+ - **readonly**: `boolean | expression string`
709
+ - **disabled**: `boolean | expression string`
710
+ - **visible**: `boolean | expression string`
711
+ - **direction**: `rtl` | `ltr`
712
+
713
+ ### Example: Parent Sets Mode
714
+
715
+ ```typescript
716
+ builder.flex(mainContainer => {
717
+ mainContainer.mode('view'); // All children inherit 'view' mode
718
+
719
+ mainContainer.formField('Name', field => {
720
+ field.textBox(); // mode = 'view'
721
+ });
722
+
723
+ mainContainer.formField('Email', field => {
724
+ field.mode('edit'); // Override: mode = 'edit'
725
+ field.textBox();
726
+ });
727
+ });
728
+ ```
729
+
730
+ ### Example: Readonly Inheritance
731
+
732
+ ```typescript
733
+ builder.fieldset(fieldset => {
734
+ fieldset.readonly(true); // All fields readonly
735
+
736
+ fieldset.formField('First Name', field => {
737
+ field.textBox(); // readonly = true
738
+ });
739
+
740
+ fieldset.formField('Last Name', field => {
741
+ field.readonly(false); // Override: readonly = false
742
+ field.textBox();
743
+ });
744
+ });
745
+ ```
746
+
747
+ ### Example: Conditional Visibility
748
+
749
+ ```typescript
750
+ builder.flex(container => {
751
+ container.visible("context.eval('user.role') === 'admin'");
752
+
753
+ container.formField('Admin Settings', field => {
754
+ field.textBox(); // Only visible when user.role === 'admin'
755
+ });
756
+ });
757
+ ```
758
+
759
+ ---
760
+
761
+ ## Complete Examples
762
+
763
+ ### Example 1: Registration Form
764
+
765
+ ```typescript
766
+ buildLayout() {
767
+ const builder = this.layoutBuilder.create();
768
+
769
+ builder.flex(mainContainer => {
770
+ mainContainer
771
+ .mode('edit')
772
+ .setDirection('column')
773
+ .setGap('20px');
774
+
775
+ // Personal Information
776
+ mainContainer.fieldset(personalFieldset => {
777
+ personalFieldset
778
+ .setTitle('Personal Information')
779
+ .setDescription('Please provide your basic personal details')
780
+ .setIcon('fa-light fa-user')
781
+ .setCols(2)
782
+ .formField('First Name', field => {
783
+ field.path('firstName');
784
+ field.textBox({
785
+ placeholder: 'Enter your first name',
786
+ validations: [{ rule: 'required', message: 'First name is required' }]
787
+ });
788
+ })
789
+ .formField('Last Name', field => {
790
+ field.path('lastName');
791
+ field.textBox({ placeholder: 'Enter your last name' });
792
+ })
793
+ .formField('Email Address', field => {
794
+ field.path('email');
795
+ field.textBox({
796
+ placeholder: 'Enter your email address',
797
+ prefix: '📧'
798
+ });
799
+ })
800
+ .formField('Phone Number', field => {
801
+ field.path('phone');
802
+ field.textBox({
803
+ placeholder: 'Enter your phone number',
804
+ prefix: '📱'
805
+ });
806
+ })
807
+ .formField('Date of Birth', field => {
808
+ field.path('birthDate');
809
+ field.dateTimeBox({
810
+ type: 'date',
811
+ placeholder: 'Select your birth date'
812
+ });
813
+ })
814
+ .formField('Gender', field => {
815
+ field.path('gender');
816
+ field.selectBox({
817
+ placeholder: 'Select your gender',
818
+ dataSource: ['Male', 'Female', 'Other', 'Prefer not to say']
819
+ });
820
+ });
821
+ });
822
+
823
+ // Address Information
824
+ mainContainer.fieldset(addressFieldset => {
825
+ addressFieldset
826
+ .setTitle('Address Information')
827
+ .setDescription('Your current residential address')
828
+ .setIcon('fa-light fa-home')
829
+ .setCols(2)
830
+ .formField('Street Address', field => {
831
+ field.path('address.street');
832
+ field.textBox({ placeholder: 'Enter your street address' });
833
+ })
834
+ .formField('City', field => {
835
+ field.path('address.city');
836
+ field.textBox({ placeholder: 'Enter your city' });
837
+ })
838
+ .formField('State/Province', field => {
839
+ field.path('address.state');
840
+ field.selectBox({
841
+ placeholder: 'Select your state/province',
842
+ dataSource: ['California', 'New York', 'Texas', 'Florida']
843
+ });
844
+ })
845
+ .formField('ZIP/Postal Code', field => {
846
+ field.path('address.zip');
847
+ field.textBox({ placeholder: 'Enter ZIP/postal code' });
848
+ });
849
+ });
850
+
851
+ // Account Security
852
+ mainContainer.fieldset(securityFieldset => {
853
+ securityFieldset
854
+ .setTitle('Account Security')
855
+ .setDescription('Set up your account credentials')
856
+ .setIcon('fa-light fa-lock')
857
+ .setCols(2)
858
+ .formField('Password', field => {
859
+ field.path('password');
860
+ field.passwordBox({
861
+ placeholder: 'Enter password (min 8 characters)',
862
+ revealToggle: true
863
+ });
864
+ })
865
+ .formField('Confirm Password', field => {
866
+ field.path('confirmPassword');
867
+ field.passwordBox({
868
+ placeholder: 'Confirm your password',
869
+ revealToggle: true
870
+ });
871
+ })
872
+ .formField('Two-Factor Authentication', field => {
873
+ field.path('twoFactorEnabled');
874
+ field.toggleSwitch({
875
+ label: 'Enable Two-Factor Authentication'
876
+ });
877
+ });
878
+ });
879
+ });
880
+
881
+ return builder.build();
882
+ }
883
+ ```
884
+
885
+ ### Example 2: Dashboard with Tabs
886
+
887
+ ```typescript
888
+ buildDashboard() {
889
+ const builder = this.layoutBuilder.create();
890
+
891
+ builder.page(page => {
892
+ page
893
+ .setBackgroundColor('#f5f5f5')
894
+ .setHasHeader(true)
895
+ .tabset(tabset => {
896
+ tabset
897
+ .setLook('pills')
898
+ .setOrientation('horizontal')
899
+
900
+ // Overview Tab
901
+ .panel(overviewTab => {
902
+ overviewTab
903
+ .setCaption('Overview')
904
+ .setIcon('fa-chart-line')
905
+ .flex(content => {
906
+ content
907
+ .setDirection('row')
908
+ .setGap('20px')
909
+ .panel(statsPanel => {
910
+ statsPanel.setCaption('Statistics');
911
+ // Add stats widgets
912
+ });
913
+ });
914
+ })
915
+
916
+ // Settings Tab
917
+ .panel(settingsTab => {
918
+ settingsTab
919
+ .setCaption('Settings')
920
+ .setIcon('fa-cog')
921
+ .fieldset(fieldset => {
922
+ fieldset
923
+ .setTitle('User Preferences')
924
+ .setCols(2)
925
+ .formField('Language', field => {
926
+ field.selectBox({
927
+ dataSource: ['English', 'Spanish', 'French']
928
+ });
929
+ })
930
+ .formField('Theme', field => {
931
+ field.selectBox({
932
+ dataSource: ['Light', 'Dark', 'Auto']
933
+ });
934
+ });
935
+ });
936
+ });
937
+ });
938
+ });
939
+
940
+ return builder.build();
941
+ }
942
+ ```
943
+
944
+ ### Example 3: Dialog with Nested Panels
945
+
946
+ ```typescript
947
+ async showComplexDialog() {
948
+ const dialogRef = await this.layoutBuilder
949
+ .create()
950
+ .dialog(dialog => {
951
+ dialog
952
+ .setTitle('Project Configuration')
953
+ .setSize('xl')
954
+ .setContext({
955
+ projectName: '',
956
+ category: '',
957
+ members: [],
958
+ startDate: null
959
+ })
960
+ .content(layoutBuilder => {
961
+ layoutBuilder.flex(mainFlex => {
962
+ mainFlex
963
+ .setDirection('column')
964
+ .setGap('20px')
965
+
966
+ // Project Details Panel
967
+ .panel(detailsPanel => {
968
+ detailsPanel
969
+ .setCaption('Project Details')
970
+ .setIcon('fa-folder')
971
+ .fieldset(fieldset => {
972
+ fieldset
973
+ .setCols(2)
974
+ .formField('Project Name', field => {
975
+ field.path('projectName');
976
+ field.textBox({
977
+ placeholder: 'Enter project name',
978
+ validations: [{ rule: 'required' }]
979
+ });
980
+ })
981
+ .formField('Category', field => {
982
+ field.path('category');
983
+ field.lookupBox({
984
+ entity: 'project-management.category',
985
+ multiple: false
986
+ });
987
+ });
988
+ });
989
+ })
990
+
991
+ // Team Panel
992
+ .panel(teamPanel => {
993
+ teamPanel
994
+ .setCaption('Team Members')
995
+ .setIcon('fa-users')
996
+ .formField('Members', field => {
997
+ field.path('members');
998
+ field.lookupBox({
999
+ entity: 'human-capital-management.employee',
1000
+ multiple: true,
1001
+ expose: [
1002
+ { source: 'id', target: 'memberIds.{id}' },
1003
+ { source: 'title', target: 'memberNames.{title}' }
1004
+ ]
1005
+ });
1006
+ });
1007
+ })
1008
+
1009
+ // Timeline Panel
1010
+ .panel(timelinePanel => {
1011
+ timelinePanel
1012
+ .setCaption('Timeline')
1013
+ .setIcon('fa-calendar')
1014
+ .fieldset(fieldset => {
1015
+ fieldset
1016
+ .setCols(2)
1017
+ .formField('Start Date', field => {
1018
+ field.path('startDate');
1019
+ field.dateTimeBox({
1020
+ type: 'date',
1021
+ format: 'YYYY-MM-DD'
1022
+ });
1023
+ })
1024
+ .formField('End Date', field => {
1025
+ field.path('endDate');
1026
+ field.dateTimeBox({
1027
+ type: 'date',
1028
+ format: 'YYYY-MM-DD'
1029
+ });
1030
+ });
1031
+ });
1032
+ });
1033
+ });
1034
+ })
1035
+ .setActions(actions => {
1036
+ actions
1037
+ .cancel('@general:actions.cancel.title')
1038
+ .submit('@general:actions.create.title');
1039
+ });
1040
+ })
1041
+ .show();
1042
+
1043
+ const formData = dialogRef.context();
1044
+ const action = dialogRef.action();
1045
+
1046
+ if (action === 'submit') {
1047
+ console.log('Project data:', formData);
1048
+ }
1049
+
1050
+ dialogRef.close();
1051
+ }
1052
+ ```
1053
+
1054
+ ---
1055
+
1056
+ ## Migration Guide
1057
+
1058
+ ### From Dynamic Form Builder to Layout Builder
1059
+
1060
+ #### Before (Dynamic Form Builder)
1061
+
1062
+ ```typescript
1063
+ import { AXPDynamicFormBuilderService } from '@acorex/platform/layout/components';
1064
+
1065
+ async showDialog() {
1066
+ const dialog = this.formBuilder.dialog();
1067
+ const dialogRef = await dialog
1068
+ .title('@user:edit.title')
1069
+ .size('lg')
1070
+ .group('basic-info', group => {
1071
+ group
1072
+ .field('firstName', field => {
1073
+ field.title('@user:firstName');
1074
+ field.textBox({ required: true, maxLength: 50 });
1075
+ })
1076
+ .field('lastName', field => {
1077
+ field.title('@user:lastName');
1078
+ field.textBox({ required: true, maxLength: 50 });
1079
+ });
1080
+ })
1081
+ .context(user)
1082
+ .actions(actions => {
1083
+ actions
1084
+ .cancel('@general:actions.cancel.title')
1085
+ .submit('@general:actions.save.title');
1086
+ })
1087
+ .show();
1088
+
1089
+ const formData = dialogRef.context();
1090
+ const action = dialogRef.action();
1091
+ }
1092
+ ```
1093
+
1094
+ #### After (Layout Builder)
1095
+
1096
+ ```typescript
1097
+ import { AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
1098
+
1099
+ async showDialog() {
1100
+ const dialogRef = await this.layoutBuilder
1101
+ .create()
1102
+ .dialog(dialog => {
1103
+ dialog
1104
+ .setTitle('@user:edit.title')
1105
+ .setSize('lg')
1106
+ .setContext(user)
1107
+ .content(layoutBuilder => {
1108
+ layoutBuilder.flex(flex => {
1109
+ flex
1110
+ .setDirection('column')
1111
+ .setGap('16px')
1112
+ .formField('@user:firstName', field => {
1113
+ field.path('firstName');
1114
+ field.textBox({
1115
+ placeholder: 'Enter first name',
1116
+ validations: [{ rule: 'required' }, { rule: 'maxLength', options: { value: 50 } }]
1117
+ });
1118
+ })
1119
+ .formField('@user:lastName', field => {
1120
+ field.path('lastName');
1121
+ field.textBox({
1122
+ placeholder: 'Enter last name',
1123
+ validations: [{ rule: 'required' }, { rule: 'maxLength', options: { value: 50 } }]
1124
+ });
1125
+ });
1126
+ });
1127
+ })
1128
+ .setActions(actions => {
1129
+ actions
1130
+ .cancel('@general:actions.cancel.title')
1131
+ .submit('@general:actions.save.title');
1132
+ });
1133
+ })
1134
+ .show();
1135
+
1136
+ const formData = dialogRef.context();
1137
+ const action = dialogRef.action();
1138
+ }
1139
+ ```
1140
+
1141
+ ### Key Migration Changes
1142
+
1143
+ | Dynamic Form Builder | Layout Builder |
1144
+ |-----------------------------------|----------------|
1145
+ | `.group(name, delegate)` | `.flex(delegate)` or `.fieldset(delegate)` |
1146
+ | `.field(path, delegate)` | `.formField(label, delegate)` + `field.path(path)` |
1147
+ | `.title(text)` | `.setTitle(text)` or `.setCaption(text)` |
1148
+ | `.mode(mode)` | `.mode(mode)` (same, but inherited) |
1149
+ | Direct widget methods | Same widget methods |
1150
+ | `.actions(delegate)` | `.setActions(delegate)` |
1151
+ | `.show()` | `.show()` (same) |
1152
+
1153
+ ### Component Migration Example
1154
+
1155
+ **Before:**
1156
+
1157
+ ```typescript
1158
+ export class SignatureComponent {
1159
+ private readonly formBuilder = inject(AXPDynamicFormBuilderService);
1160
+
1161
+ async showPopup() {
1162
+ const ref = await this.formBuilder
1163
+ .dialog()
1164
+ .title('Signature')
1165
+ .size('lg')
1166
+ .group('signature', group => {
1167
+ group
1168
+ .mode('view')
1169
+ .field('preview', field => {
1170
+ field
1171
+ .options({ readonly: true })
1172
+ .widget('image', { src: url, width: '100%' });
1173
+ });
1174
+ })
1175
+ .actions(a => a.cancel('@general:actions.cancel.title'))
1176
+ .show();
1177
+ ref.close();
1178
+ }
1179
+ }
1180
+ ```
1181
+
1182
+ **After:**
1183
+
1184
+ ```typescript
1185
+ export class SignatureComponent {
1186
+ private readonly layoutBuilder = inject(AXPLayoutBuilderService);
1187
+
1188
+ async showPopup() {
1189
+ const ref = await this.layoutBuilder
1190
+ .create()
1191
+ .dialog(dialog => {
1192
+ dialog
1193
+ .setTitle('Signature')
1194
+ .setSize('lg')
1195
+ .content(layoutBuilder => {
1196
+ layoutBuilder.flex(flex => {
1197
+ flex
1198
+ .mode('view')
1199
+ .formField('Preview', field => {
1200
+ field
1201
+ .readonly(true)
1202
+ .customWidget('image', {
1203
+ src: url,
1204
+ width: '100%',
1205
+ height: 'auto',
1206
+ objectFit: 'contain'
1207
+ });
1208
+ });
1209
+ });
1210
+ })
1211
+ .setActions(actions => {
1212
+ actions.cancel('@general:actions.cancel.title');
1213
+ });
1214
+ })
1215
+ .show();
1216
+ ref.close();
1217
+ }
1218
+ }
1219
+ ```
1220
+
1221
+ ---
1222
+
1223
+ ## Best Practices
1224
+
1225
+ ### 1. Use Descriptive Names
1226
+
1227
+ ```typescript
1228
+ // ✅ Good
1229
+ .formField('User Email Address', field => {
1230
+ field.path('user.email');
1231
+ });
1232
+
1233
+ // ❌ Bad
1234
+ .formField('Email', field => {
1235
+ field.path('e');
1236
+ });
1237
+ ```
1238
+
1239
+ ### 2. Group Related Fields
1240
+
1241
+ ```typescript
1242
+ // ✅ Good
1243
+ .fieldset(fieldset => {
1244
+ fieldset
1245
+ .setTitle('Contact Information')
1246
+ .setCols(2)
1247
+ .formField('Email', field => { ... })
1248
+ .formField('Phone', field => { ... });
1249
+ });
1250
+
1251
+ // ❌ Bad - scattered fields
1252
+ .formField('Email', field => { ... })
1253
+ .formField('Name', field => { ... })
1254
+ .formField('Phone', field => { ... })
1255
+ .formField('Address', field => { ... });
1256
+ ```
1257
+
1258
+ ### 3. Use Property Inheritance
1259
+
1260
+ ```typescript
1261
+ // ✅ Good
1262
+ .fieldset(fieldset => {
1263
+ fieldset.mode('view'); // All fields inherit view mode
1264
+ fieldset.formField('Name', field => field.textBox());
1265
+ fieldset.formField('Email', field => field.textBox());
1266
+ });
1267
+
1268
+ // ❌ Bad - repetitive
1269
+ .fieldset(fieldset => {
1270
+ fieldset.formField('Name', field => {
1271
+ field.mode('view');
1272
+ field.textBox();
1273
+ });
1274
+ fieldset.formField('Email', field => {
1275
+ field.mode('view');
1276
+ field.textBox();
1277
+ });
1278
+ });
1279
+ ```
1280
+
1281
+ ### 4. Provide Clear Placeholders
1282
+
1283
+ ```typescript
1284
+ // ✅ Good
1285
+ .formField('Email', field => {
1286
+ field.textBox({
1287
+ placeholder: 'Enter your email address (e.g., user@example.com)'
1288
+ });
1289
+ });
1290
+
1291
+ // ❌ Bad
1292
+ .formField('Email', field => {
1293
+ field.textBox();
1294
+ });
1295
+ ```
1296
+
1297
+ ### 5. Use Appropriate Container Types
1298
+
1299
+ ```typescript
1300
+ // ✅ Good - Use flex for simple vertical/horizontal layouts
1301
+ .flex(container => {
1302
+ container.setDirection('column');
1303
+ });
1304
+
1305
+ // ✅ Good - Use grid for complex 2D layouts
1306
+ .grid(container => {
1307
+ container.setColumns(3).setRows(2);
1308
+ });
1309
+
1310
+ // ❌ Bad - Using grid for simple vertical layout
1311
+ .grid(container => {
1312
+ container.setColumns(1); // Just use flex!
1313
+ });
1314
+ ```
1315
+
1316
+ ### 6. Set Validation Rules
1317
+
1318
+ ```typescript
1319
+ // ✅ Good
1320
+ .formField('Email', field => {
1321
+ field.textBox({
1322
+ validations: [
1323
+ { rule: 'required', message: 'Email is required' },
1324
+ { rule: 'email', message: 'Invalid email format' }
1325
+ ]
1326
+ });
1327
+ });
1328
+
1329
+ // ❌ Bad - No validation
1330
+ .formField('Email', field => {
1331
+ field.textBox();
1332
+ });
1333
+ ```
1334
+
1335
+ ### 7. Use Translation Keys
1336
+
1337
+ ```typescript
1338
+ // ✅ Good
1339
+ .setTitle('@module:section.title')
1340
+ .formField('@module:field.label', field => { ... })
1341
+
1342
+ // ❌ Bad - Hardcoded text
1343
+ .setTitle('User Information')
1344
+ .formField('Name', field => { ... })
1345
+ ```
1346
+
1347
+ ### 8. Proper Dialog Context Management
1348
+
1349
+ ```typescript
1350
+ // ✅ Good
1351
+ async showDialog(user: User) {
1352
+ const dialogRef = await this.layoutBuilder
1353
+ .create()
1354
+ .dialog(dialog => {
1355
+ dialog.setContext({ ...user }); // Clone the object
1356
+ // ... rest of dialog setup
1357
+ })
1358
+ .show();
1359
+
1360
+ const formData = dialogRef.context();
1361
+ const action = dialogRef.action();
1362
+
1363
+ if (action === 'submit') {
1364
+ await this.saveUser(formData);
1365
+ }
1366
+
1367
+ dialogRef.close();
1368
+ }
1369
+
1370
+ // ❌ Bad - Mutating original object
1371
+ async showDialog(user: User) {
1372
+ const dialogRef = await this.layoutBuilder
1373
+ .create()
1374
+ .dialog(dialog => {
1375
+ dialog.setContext(user); // Direct reference!
1376
+ })
1377
+ .show();
1378
+ }
1379
+ ```
1380
+
1381
+ ### 9. Organize Complex Forms with Fieldsets
1382
+
1383
+ ```typescript
1384
+ // ✅ Good
1385
+ .flex(mainContainer => {
1386
+ mainContainer
1387
+ .fieldset(personalInfo => {
1388
+ personalInfo.setTitle('Personal Information');
1389
+ // Personal fields
1390
+ })
1391
+ .fieldset(contactInfo => {
1392
+ contactInfo.setTitle('Contact Information');
1393
+ // Contact fields
1394
+ })
1395
+ .fieldset(preferences => {
1396
+ preferences.setTitle('Preferences');
1397
+ // Preference fields
1398
+ });
1399
+ });
1400
+ ```
1401
+
1402
+ ### 10. Handle Dialog Actions Properly
1403
+
1404
+ ```typescript
1405
+ // ✅ Good
1406
+ const dialogRef = await dialog.show();
1407
+ const formData = dialogRef.context();
1408
+ const action = dialogRef.action();
1409
+
1410
+ if (action === 'cancel') {
1411
+ return { cancelled: true };
1412
+ }
1413
+
1414
+ if (action === 'submit') {
1415
+ const isValid = await this.validate(formData);
1416
+ if (isValid) {
1417
+ await this.save(formData);
1418
+ return { cancelled: false, data: formData };
1419
+ }
1420
+ }
1421
+
1422
+ dialogRef.close();
1423
+
1424
+ // ❌ Bad - No action handling
1425
+ const dialogRef = await dialog.show();
1426
+ await this.save(dialogRef.context());
1427
+ dialogRef.close();
1428
+ ```
1429
+
1430
+ ---
1431
+
1432
+ ## API Reference
1433
+
1434
+ ### AXPLayoutBuilderService
1435
+
1436
+ ```typescript
1437
+ class AXPLayoutBuilderService {
1438
+ create(): ILayoutBuilder;
1439
+ }
1440
+ ```
1441
+
1442
+ ### ILayoutBuilder
1443
+
1444
+ ```typescript
1445
+ interface ILayoutBuilder {
1446
+ flex(delegate: (container: IFlexContainerBuilder) => void): ILayoutBuilder;
1447
+ grid(delegate: (container: IGridContainerBuilder) => void): ILayoutBuilder;
1448
+ panel(delegate: (container: IPanelContainerBuilder) => void): ILayoutBuilder;
1449
+ page(delegate: (container: IPageContainerBuilder) => void): ILayoutBuilder;
1450
+ tabset(delegate: (container: ITabsetContainerBuilder) => void): ILayoutBuilder;
1451
+ fieldset(delegate: (container: IFieldsetContainerBuilder) => void): ILayoutBuilder;
1452
+ dialog(delegate: (container: IDialogBuilder) => void): IDialogBuilder;
1453
+ formField(label: string, delegate?: (field: IFormFieldBuilder) => void): ILayoutBuilder;
1454
+ build(): AXPWidgetNode;
1455
+ }
1456
+ ```
1457
+
1458
+ ### Container Builders
1459
+
1460
+ All container builders extend the base interface:
1461
+
1462
+ ```typescript
1463
+ interface IBaseContainerBuilder<TContainer> {
1464
+ name(name: string): TContainer;
1465
+ path(path: string): TContainer;
1466
+ mode(mode: 'edit' | 'view'): TContainer;
1467
+ visible(condition: boolean | string): TContainer;
1468
+ disabled(condition: boolean | string): TContainer;
1469
+ readonly(condition: boolean | string): TContainer;
1470
+ direction(direction: 'rtl' | 'ltr'): TContainer;
1471
+ build(): AXPWidgetNode;
1472
+ }
1473
+ ```
1474
+
1475
+ ### Form Field Builder
1476
+
1477
+ ```typescript
1478
+ interface IFormFieldBuilder extends IBaseContainerBuilder<IFormFieldBuilder> {
1479
+ setLabel(label: string): IFormFieldBuilder;
1480
+ setShowLabel(showLabel: boolean): IFormFieldBuilder;
1481
+
1482
+ // Widget methods
1483
+ textBox(options?: TextBoxOptions): IFormFieldBuilder;
1484
+ largeTextBox(options?: LargeTextBoxOptions): IFormFieldBuilder;
1485
+ richText(options?: RichTextOptions): IFormFieldBuilder;
1486
+ passwordBox(options?: PasswordBoxOptions): IFormFieldBuilder;
1487
+ numberBox(options?: NumberBoxOptions): IFormFieldBuilder;
1488
+ selectBox(options: SelectBoxOptions): IFormFieldBuilder;
1489
+ lookupBox(options: LookupBoxOptions): IFormFieldBuilder;
1490
+ selectionList(options: SelectionListOptions): IFormFieldBuilder;
1491
+ dateTimeBox(options?: DateTimeBoxOptions): IFormFieldBuilder;
1492
+ toggleSwitch(options?: ToggleSwitchOptions): IFormFieldBuilder;
1493
+ colorBox(options?: ColorBoxOptions): IFormFieldBuilder;
1494
+ customWidget<T>(type: string, options?: T): IFormFieldBuilder;
1495
+ }
1496
+ ```
1497
+
1498
+ ### Dialog Builder
1499
+
1500
+ ```typescript
1501
+ interface IDialogBuilder {
1502
+ setTitle(title: string): IDialogBuilder;
1503
+ setMessage(message?: string): IDialogBuilder;
1504
+ setSize(size: 'sm' | 'md' | 'lg' | 'xl'): IDialogBuilder;
1505
+ setCloseButton(closeButton: boolean): IDialogBuilder;
1506
+ setContext(context: any): IDialogBuilder;
1507
+ content(delegate: (layoutBuilder: IFlexContainerBuilder) => void): IDialogBuilder;
1508
+ setActions(delegate?: (actions: IActionBuilder) => void): IDialogBuilder;
1509
+ show(): Promise<AXPDialogRef>;
1510
+ }
1511
+ ```
1512
+
1513
+ ### Action Builder
1514
+
1515
+ ```typescript
1516
+ interface IActionBuilder {
1517
+ cancel(text?: string): IActionBuilder;
1518
+ submit(text?: string): IActionBuilder;
1519
+ custom(action: AXPActionMenuItem): IActionBuilder;
1520
+ }
1521
+ ```
1522
+
1523
+ ### Dialog Reference
1524
+
1525
+ ```typescript
1526
+ interface AXPDialogRef {
1527
+ context(): any;
1528
+ action(): string;
1529
+ setLoading(loading: boolean): void;
1530
+ close(): void;
1531
+ }
1532
+ ```
1533
+
1534
+ ### Layout Renderer Component
1535
+
1536
+ ```typescript
1537
+ @Component({
1538
+ selector: 'axp-layout-renderer'
1539
+ })
1540
+ class AXPLayoutRendererComponent {
1541
+ @Input() layout: AXPWidgetNode | AXPDynamicFormDefinition;
1542
+ @Input() context: any;
1543
+ @Input() look: 'fieldset' | 'card' | 'group';
1544
+ @Input() mode: 'edit' | 'view';
1545
+
1546
+ @Output() contextInitiated: EventEmitter<any>;
1547
+ @Output() validityChange: EventEmitter<boolean>;
1548
+
1549
+ getContext(): any;
1550
+ updateContext(context: any): void;
1551
+ getWidgetTree(): AXPWidgetNode | null;
1552
+ validate(): Promise<AXValidationSummary>;
1553
+ clear(): void;
1554
+ reset(): void;
1555
+ }
1556
+ ```
1557
+
1558
+ ---
1559
+
1560
+ ## Summary
1561
+
1562
+ The **ACoreX Layout Builder** is a comprehensive solution for building dynamic, type-safe layouts in the ACoreX Platform. It provides:
1563
+
1564
+ ✅ **Fluent API** with delegate pattern for readable code
1565
+ ✅ **Multiple container types** for flexible layouts
1566
+ ✅ **Automatic property inheritance** for consistent behavior
1567
+ ✅ **Built-in dialog support** with action management
1568
+ ✅ **15+ widgets** with custom widget support
1569
+ ✅ **Type safety** with full TypeScript support
1570
+ ✅ **Automatic path generation** for form fields
1571
+
1572
+ For more examples and advanced usage, refer to the [examples directory](./examples/) or contact the platform team.
1573
+
1574
+ ---
1575
+
1576
+ **Last Updated**: October 2025
1577
+ **Version**: 1.0.0
1578
+ **Maintainer**: ACoreX Platform Team