@ticatec/uniface-flexi-module 0.0.2

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 (136) hide show
  1. package/FLEXICRITERIASET_GUIDE.md +1559 -0
  2. package/FLEXICRITERIASET_GUIDE_CN.md +1133 -0
  3. package/FLEXIDATATABLE_GUIDE.md +1650 -0
  4. package/FLEXIDATATABLE_GUIDE_CN.md +1650 -0
  5. package/FLEXIFORM_GUIDE.md +1068 -0
  6. package/FLEXIFORM_GUIDE_CN.md +1068 -0
  7. package/FLEXI_CONTEXT_GUIDE_CN.md +172 -0
  8. package/MODULE_LOADER_CN.md +228 -0
  9. package/README.md +307 -0
  10. package/README_CN.md +51 -0
  11. package/SANDBOX_CN.md +201 -0
  12. package/dist/FlexiContext.d.ts +28 -0
  13. package/dist/FlexiContext.js +45 -0
  14. package/dist/ModuleLoader.d.ts +41 -0
  15. package/dist/ModuleLoader.js +55 -0
  16. package/dist/Sandbox.d.ts +33 -0
  17. package/dist/Sandbox.js +101 -0
  18. package/dist/criteria-panel/CriteriaFieldsPanel.svelte +26 -0
  19. package/dist/criteria-panel/CriteriaFieldsPanel.svelte.d.ts +22 -0
  20. package/dist/criteria-panel/components/CascadeSelectSearchField.svelte +10 -0
  21. package/dist/criteria-panel/components/CascadeSelectSearchField.svelte.d.ts +25 -0
  22. package/dist/criteria-panel/components/DateRangeField.svelte +11 -0
  23. package/dist/criteria-panel/components/DateRangeField.svelte.d.ts +25 -0
  24. package/dist/criteria-panel/components/DateSearchField.svelte +10 -0
  25. package/dist/criteria-panel/components/DateSearchField.svelte.d.ts +24 -0
  26. package/dist/criteria-panel/components/DateTimeSearchField.svelte +10 -0
  27. package/dist/criteria-panel/components/DateTimeSearchField.svelte.d.ts +24 -0
  28. package/dist/criteria-panel/components/InputOptionSelectSearchField.svelte +9 -0
  29. package/dist/criteria-panel/components/InputOptionSelectSearchField.svelte.d.ts +24 -0
  30. package/dist/criteria-panel/components/NumberRangeField.svelte +11 -0
  31. package/dist/criteria-panel/components/NumberRangeField.svelte.d.ts +25 -0
  32. package/dist/criteria-panel/components/NumberSearchField.svelte +9 -0
  33. package/dist/criteria-panel/components/NumberSearchField.svelte.d.ts +24 -0
  34. package/dist/criteria-panel/components/OptionMultiSelectSearchField.svelte +9 -0
  35. package/dist/criteria-panel/components/OptionMultiSelectSearchField.svelte.d.ts +24 -0
  36. package/dist/criteria-panel/components/OptionSelectSearchField.svelte +9 -0
  37. package/dist/criteria-panel/components/OptionSelectSearchField.svelte.d.ts +24 -0
  38. package/dist/criteria-panel/components/SearchField.svelte +14 -0
  39. package/dist/criteria-panel/components/SearchField.svelte.d.ts +33 -0
  40. package/dist/criteria-panel/components/TextSearchField.svelte +9 -0
  41. package/dist/criteria-panel/components/TextSearchField.svelte.d.ts +24 -0
  42. package/dist/criteria-panel/components/UnknownCriteriaField.svelte +9 -0
  43. package/dist/criteria-panel/components/UnknownCriteriaField.svelte.d.ts +24 -0
  44. package/dist/criteria-panel/index.d.ts +6 -0
  45. package/dist/criteria-panel/index.js +6 -0
  46. package/dist/criteria-panel/lib/CriteriaComponentBuilder.d.ts +19 -0
  47. package/dist/criteria-panel/lib/CriteriaComponentBuilder.js +31 -0
  48. package/dist/criteria-panel/lib/CriteriaFieldBuilder.d.ts +1 -0
  49. package/dist/criteria-panel/lib/CriteriaFieldBuilder.js +127 -0
  50. package/dist/criteria-panel/lib/FlexiCriteriaField.d.ts +38 -0
  51. package/dist/criteria-panel/lib/FlexiCriteriaField.js +31 -0
  52. package/dist/criteria-panel/lib/FlexiCriteriaSet.d.ts +24 -0
  53. package/dist/criteria-panel/lib/FlexiCriteriaSet.js +48 -0
  54. package/dist/flexi-datatable/FlexiDataTable.d.ts +111 -0
  55. package/dist/flexi-datatable/FlexiDataTable.js +90 -0
  56. package/dist/flexi-datatable/index.d.ts +2 -0
  57. package/dist/flexi-datatable/index.js +2 -0
  58. package/dist/flexi-form/FlexiCompound.d.ts +34 -0
  59. package/dist/flexi-form/FlexiCompound.js +84 -0
  60. package/dist/flexi-form/FlexiFormDialog.svelte +24 -0
  61. package/dist/flexi-form/FlexiFormDialog.svelte.d.ts +21 -0
  62. package/dist/flexi-form/FlexiFormPage.svelte +26 -0
  63. package/dist/flexi-form/FlexiFormPage.svelte.d.ts +25 -0
  64. package/dist/flexi-form/Schema.d.ts +6 -0
  65. package/dist/flexi-form/Schema.js +1 -0
  66. package/dist/flexi-form/components/BreakLine.svelte +1 -0
  67. package/dist/flexi-form/components/BreakLine.svelte.d.ts +26 -0
  68. package/dist/flexi-form/components/CardTitleBar.svelte +18 -0
  69. package/dist/flexi-form/components/CardTitleBar.svelte.d.ts +22 -0
  70. package/dist/flexi-form/components/CascadeOptionSelectField.svelte +13 -0
  71. package/dist/flexi-form/components/CascadeOptionSelectField.svelte.d.ts +24 -0
  72. package/dist/flexi-form/components/CellFieldBuilder.d.ts +1 -0
  73. package/dist/flexi-form/components/CellFieldBuilder.js +178 -0
  74. package/dist/flexi-form/components/DateField.svelte +12 -0
  75. package/dist/flexi-form/components/DateField.svelte.d.ts +24 -0
  76. package/dist/flexi-form/components/DateTimeField.svelte +13 -0
  77. package/dist/flexi-form/components/DateTimeField.svelte.d.ts +24 -0
  78. package/dist/flexi-form/components/InputOptionSelectField.svelte +13 -0
  79. package/dist/flexi-form/components/InputOptionSelectField.svelte.d.ts +24 -0
  80. package/dist/flexi-form/components/MemoField.svelte +12 -0
  81. package/dist/flexi-form/components/MemoField.svelte.d.ts +24 -0
  82. package/dist/flexi-form/components/NumberField.svelte +12 -0
  83. package/dist/flexi-form/components/NumberField.svelte.d.ts +24 -0
  84. package/dist/flexi-form/components/OptionsMultiSelectField.svelte +13 -0
  85. package/dist/flexi-form/components/OptionsMultiSelectField.svelte.d.ts +24 -0
  86. package/dist/flexi-form/components/OptionsSelectField.svelte +13 -0
  87. package/dist/flexi-form/components/OptionsSelectField.svelte.d.ts +24 -0
  88. package/dist/flexi-form/components/TextField.svelte +12 -0
  89. package/dist/flexi-form/components/TextField.svelte.d.ts +24 -0
  90. package/dist/flexi-form/components/UnitNumberField.svelte +12 -0
  91. package/dist/flexi-form/components/UnitNumberField.svelte.d.ts +24 -0
  92. package/dist/flexi-form/components/UnknownTypeField.svelte +5 -0
  93. package/dist/flexi-form/components/UnknownTypeField.svelte.d.ts +18 -0
  94. package/dist/flexi-form/containers/FlexiPanel.svelte +13 -0
  95. package/dist/flexi-form/containers/FlexiPanel.svelte.d.ts +33 -0
  96. package/dist/flexi-form/flexi_card/FlexiCard.d.ts +64 -0
  97. package/dist/flexi-form/flexi_card/FlexiCard.js +66 -0
  98. package/dist/flexi-form/flexi_card/FlexiCardPanel.svelte +57 -0
  99. package/dist/flexi-form/flexi_card/FlexiCardPanel.svelte.d.ts +22 -0
  100. package/dist/flexi-form/flexi_composite/FlexiComposite.d.ts +50 -0
  101. package/dist/flexi-form/flexi_composite/FlexiComposite.js +26 -0
  102. package/dist/flexi-form/flexi_composite/FlexiCompositePanel.svelte +42 -0
  103. package/dist/flexi-form/flexi_composite/FlexiCompositePanel.svelte.d.ts +25 -0
  104. package/dist/flexi-form/flexi_composite/README.md +50 -0
  105. package/dist/flexi-form/flexi_datasheet/FlexiDataSheet.d.ts +4 -0
  106. package/dist/flexi-form/flexi_datasheet/FlexiDataSheet.js +2 -0
  107. package/dist/flexi-form/flexi_field/FlexiField.d.ts +76 -0
  108. package/dist/flexi-form/flexi_field/FlexiField.js +128 -0
  109. package/dist/flexi-form/flexi_field/FlexiFieldCell.svelte +35 -0
  110. package/dist/flexi-form/flexi_field/FlexiFieldCell.svelte.d.ts +25 -0
  111. package/dist/flexi-form/flexi_field/UnknownField.d.ts +3 -0
  112. package/dist/flexi-form/flexi_field/UnknownField.js +3 -0
  113. package/dist/flexi-form/flexi_form/FlexiForm.d.ts +127 -0
  114. package/dist/flexi-form/flexi_form/FlexiForm.js +160 -0
  115. package/dist/flexi-form/flexi_form/FlexiFormPanel.svelte +57 -0
  116. package/dist/flexi-form/flexi_form/FlexiFormPanel.svelte.d.ts +25 -0
  117. package/dist/flexi-form/index.d.ts +11 -0
  118. package/dist/flexi-form/index.js +11 -0
  119. package/dist/flexi-form/lib/ComponentBuilder.d.ts +15 -0
  120. package/dist/flexi-form/lib/ComponentBuilder.js +31 -0
  121. package/dist/flexi-form/lib/index.d.ts +5 -0
  122. package/dist/flexi-form/lib/index.js +2 -0
  123. package/dist/flexi-form/lib/types.d.ts +7 -0
  124. package/dist/flexi-form/lib/types.js +6 -0
  125. package/dist/flexi-form/lib/utils.d.ts +10 -0
  126. package/dist/flexi-form/lib/utils.js +48 -0
  127. package/dist/i18n-res/i18nRes.d.ts +2 -0
  128. package/dist/i18n-res/i18nRes.js +8 -0
  129. package/dist/i18n-res/index.d.ts +2 -0
  130. package/dist/i18n-res/index.js +2 -0
  131. package/dist/index.d.ts +5 -0
  132. package/dist/index.js +5 -0
  133. package/dist/uniface-flexi-module.css +46 -0
  134. package/dist/utils.d.ts +4 -0
  135. package/dist/utils.js +8 -0
  136. package/package.json +135 -0
@@ -0,0 +1,1068 @@
1
+ # FlexiForm Complete Usage Guide
2
+
3
+ A comprehensive guide to using FlexiForm for building dynamic, flexible forms in Svelte applications.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Overview](#overview)
8
+ 2. [Basic Usage](#basic-usage)
9
+ 3. [Schema Structure](#schema-structure)
10
+ 4. [Field Types](#field-types)
11
+ 5. [Layout and Arrangement](#layout-and-arrangement)
12
+ 6. [Dynamic Form Generation](#dynamic-form-generation)
13
+ 7. [Events and Interactions](#events-and-interactions)
14
+ 8. [Extending FlexiForm](#extending-flexiform)
15
+ 9. [Advanced Patterns](#advanced-patterns)
16
+ 10. [Best Practices](#best-practices)
17
+
18
+ ## Overview
19
+
20
+ FlexiForm is a powerful form framework that generates dynamic forms from JSON schemas. It supports multiple layout modes, various field types, and provides extensible architecture for custom components.
21
+
22
+ ### Key Features
23
+
24
+ - **Dynamic Schema-Based**: Generate forms from JSON configurations
25
+ - **Flexible Layouts**: Support for flex and grid layouts
26
+ - **Extensible Field Types**: Built-in fields + custom field support
27
+ - **Event System**: Rich event handling for form interactions
28
+ - **Type Safety**: Full TypeScript support
29
+ - **Modular Architecture**: Plug-and-play components
30
+
31
+ ## Basic Usage
32
+
33
+ ### 1. Installation and Setup
34
+
35
+ ```typescript
36
+ import { FlexiFormPage, registerFormFieldBuilder } from '@ticatec/uniface-flexi-form/flexi-form';
37
+ import '@ticatec/uniface-flexi-form/uniface-flexi-form.css';
38
+
39
+ // Register all field builders (required)
40
+ registerFormFieldBuilder();
41
+ ```
42
+
43
+ ### 2. Simple Form Example
44
+
45
+ ```svelte
46
+ <!-- SimpleForm.svelte -->
47
+ <script lang="ts">
48
+ import { FlexiFormPage } from '@ticatec/uniface-flexi-form/flexi-form';
49
+
50
+ // Form data
51
+ let userData = {
52
+ name: '',
53
+ email: '',
54
+ age: null,
55
+ birthDate: null
56
+ };
57
+
58
+ // Form schema
59
+ const userFormSchema = {
60
+ mode: 'flex',
61
+ arrangement: 'vertical',
62
+ variant: 'outlined',
63
+ elements: {
64
+ 'user-info': {
65
+ title: 'User Information',
66
+ fields: [
67
+ {
68
+ type: 'text-editor',
69
+ keyField: 'name',
70
+ name: 'name',
71
+ label: 'Full Name',
72
+ required: true,
73
+ props: {
74
+ placeholder: 'Enter your full name'
75
+ }
76
+ },
77
+ {
78
+ type: 'text-editor',
79
+ keyField: 'email',
80
+ name: 'email',
81
+ label: 'Email Address',
82
+ required: true,
83
+ props: {
84
+ type: 'email',
85
+ placeholder: 'Enter your email'
86
+ }
87
+ },
88
+ {
89
+ type: 'number-editor',
90
+ keyField: 'age',
91
+ name: 'age',
92
+ label: 'Age',
93
+ props: {
94
+ min: 0,
95
+ max: 120
96
+ }
97
+ },
98
+ {
99
+ type: 'date-picker',
100
+ keyField: 'birthDate',
101
+ name: 'birthDate',
102
+ label: 'Birth Date'
103
+ }
104
+ ]
105
+ }
106
+ }
107
+ };
108
+
109
+ function handleFormChange(event) {
110
+ console.log('Form data changed:', event.detail);
111
+ }
112
+
113
+ function handleSubmit() {
114
+ console.log('Submitting form:', userData);
115
+ }
116
+ </script>
117
+
118
+ <FlexiFormPage
119
+ schema={userFormSchema}
120
+ bind:data={userData}
121
+ on:change={handleFormChange}
122
+ />
123
+
124
+ <button on:click={handleSubmit}>Submit</button>
125
+ ```
126
+
127
+ ## Schema Structure
128
+
129
+ ### FlexiFormSchema
130
+
131
+ ```typescript
132
+ interface FlexiFormSchema {
133
+ mode?: 'flex' | 'grid'; // Layout mode
134
+ arrangement?: 'vertical' | 'horizontal'; // Layout direction
135
+ props?: any; // Layout-specific properties
136
+ elements: FormElements; // Form cards/sections
137
+ label$style?: string; // Label styling
138
+ variant?: 'filled' | 'outlined' | ''; // Field variant
139
+ actions?: Array<FormActionSchema>; // Form actions
140
+ style?: string; // Custom CSS
141
+ }
142
+
143
+ type FormElements = {
144
+ [cardKey: string]: FlexiCardSchema | FlexiBlockSchema;
145
+ }
146
+ ```
147
+
148
+ ### FlexiCardSchema
149
+
150
+ ```typescript
151
+ interface FlexiCardSchema {
152
+ title?: string; // Card title
153
+ mode?: 'flex' | 'grid'; // Card layout mode
154
+ props?: any; // Layout properties
155
+ arrangement?: 'vertical' | 'horizontal'; // Layout direction
156
+ fields: Array<FlexiFieldSchema>; // Fields in this card
157
+ readonly?: boolean; // Make card readonly
158
+ disabled?: boolean; // Disable card
159
+ variant?: 'filled' | 'outlined' | ''; // Field variant
160
+ foldable?: boolean; // Allow collapsing
161
+ actions?: Array<ActionSchema>; // Card actions
162
+ }
163
+ ```
164
+
165
+ ### FlexiCompositeSchema
166
+
167
+ ```typescript
168
+ interface FlexiCompositeSchema {
169
+ type?: 'block'; // Type identifier for blocks
170
+ title?: string; // Block title
171
+ fields: Array<FlexiFieldSchema>; // Fields in this composite block
172
+ readonly?: boolean; // Make block readonly
173
+ disabled?: boolean; // Disable block
174
+ label$style?: string; // Label styling
175
+ variant?: string; // Field variant
176
+ mode?: LayoutMode; // Layout mode
177
+ props?: any; // Layout properties
178
+ arrangement?: Arrangement; // Layout direction
179
+ }
180
+ ```
181
+
182
+ ### FlexiCompositeFieldSchema
183
+
184
+ ```typescript
185
+ interface FlexiCompositeFieldSchema {
186
+ type: "composite"; // Type identifier
187
+ keyField?: string; // Data binding path for nested object
188
+ name: string; // Field name
189
+ composite: string; // Reference to composite schema name
190
+ cell?: any; // Cell properties
191
+ }
192
+ ```
193
+
194
+ ### FlexiFieldSchema
195
+
196
+ ```typescript
197
+ interface FlexiFieldSchema {
198
+ type: string; // Field type identifier
199
+ keyField: string; // Data binding path
200
+ name: string; // Field name
201
+ label: string; // Display label
202
+ dictName?: string; // Dictionary for options
203
+ variant?: 'filled' | 'outlined' | ''; // Field variant
204
+ readonly?: boolean; // Field readonly state
205
+ disabled?: boolean; // Field disabled state
206
+ required?: boolean; // Field required state
207
+ events?: Record<string, string>; // Event handlers
208
+ cell?: any; // Cell properties
209
+ props?: any; // Field-specific properties
210
+ }
211
+ ```
212
+
213
+ ## Field Types
214
+
215
+ ### Built-in Field Types
216
+
217
+ | Type | Description | Common Props |
218
+ |------|-------------|--------------|
219
+ | `text-editor` | Single-line text input | `placeholder`, `maxLength`, `pattern` |
220
+ | `memo-editor` | Multi-line text area | `rows`, `placeholder`, `maxLength` |
221
+ | `number-editor` | Numeric input | `min`, `max`, `step`, `precision` |
222
+ | `unit-number-editor` | Number input with units | `min`, `max`, `step`, `unit`, `precision` |
223
+ | `date-picker` | Date selection | `min`, `max`, `format` |
224
+ | `datetime-picker` | Date and time selection | `min`, `max`, `format` |
225
+ | `options-selector` | Single select dropdown | `dictName`, `placeholder` |
226
+ | `options-multi-selector` | Multi-select dropdown | `dictName`, `maxSelections` |
227
+ | `cascade-options-selector` | Cascading select | `dictName`, `levels` |
228
+ | `input-options-selector` | Searchable select | `getOptions`, `minLength` |
229
+ | `-` | Break line/separator | Visual separator |
230
+
231
+ ### Field Examples
232
+
233
+ ```typescript
234
+ // Text field with validation
235
+ {
236
+ type: 'text-editor',
237
+ keyField: 'username',
238
+ name: 'username',
239
+ label: 'Username',
240
+ required: true,
241
+ props: {
242
+ placeholder: 'Enter username',
243
+ maxLength: 50,
244
+ pattern: '^[a-zA-Z0-9_]+$'
245
+ }
246
+ }
247
+
248
+ // Number field with range
249
+ {
250
+ type: 'number-editor',
251
+ keyField: 'price',
252
+ name: 'price',
253
+ label: 'Price',
254
+ props: {
255
+ min: 0,
256
+ max: 9999.99,
257
+ step: 0.01,
258
+ prefix: '$'
259
+ }
260
+ }
261
+
262
+ // Date field with constraints
263
+ {
264
+ type: 'date-picker',
265
+ keyField: 'startDate',
266
+ name: 'startDate',
267
+ label: 'Start Date',
268
+ props: {
269
+ min: '2024-01-01',
270
+ max: '2024-12-31',
271
+ format: 'YYYY-MM-DD'
272
+ }
273
+ }
274
+
275
+ // Select field with dictionary
276
+ {
277
+ type: 'options-selector',
278
+ keyField: 'category',
279
+ name: 'category',
280
+ label: 'Category',
281
+ dictName: 'product-categories',
282
+ required: true,
283
+ props: {
284
+ placeholder: 'Select a category'
285
+ }
286
+ }
287
+ ```
288
+
289
+ ## Layout and Arrangement
290
+
291
+ ### Flex Layout
292
+
293
+ ```typescript
294
+ const flexFormSchema = {
295
+ mode: 'flex',
296
+ arrangement: 'vertical',
297
+ elements: {
298
+ 'main-card': {
299
+ mode: 'flex',
300
+ arrangement: 'horizontal', // Fields side by side
301
+ fields: [
302
+ {
303
+ type: 'text-editor',
304
+ keyField: 'firstName',
305
+ name: 'firstName',
306
+ label: 'First Name',
307
+ cell: { flex: 1 } // Equal width
308
+ },
309
+ {
310
+ type: 'text-editor',
311
+ keyField: 'lastName',
312
+ name: 'lastName',
313
+ label: 'Last Name',
314
+ cell: { flex: 1 } // Equal width
315
+ }
316
+ ]
317
+ }
318
+ }
319
+ };
320
+ ```
321
+
322
+ ### Grid Layout
323
+
324
+ ```typescript
325
+ const gridFormSchema = {
326
+ mode: 'grid',
327
+ props: {
328
+ columns: 3, // 3-column grid
329
+ gap: '16px'
330
+ },
331
+ elements: {
332
+ 'contact-info': {
333
+ mode: 'grid',
334
+ props: {
335
+ columns: 2 // 2-column grid for this card
336
+ },
337
+ fields: [
338
+ {
339
+ type: 'text-editor',
340
+ keyField: 'email',
341
+ name: 'email',
342
+ label: 'Email',
343
+ cell: { colspan: 2 } // Span 2 columns
344
+ },
345
+ {
346
+ type: 'text-editor',
347
+ keyField: 'phone',
348
+ name: 'phone',
349
+ label: 'Phone'
350
+ },
351
+ {
352
+ type: 'text-editor',
353
+ keyField: 'mobile',
354
+ name: 'mobile',
355
+ label: 'Mobile'
356
+ }
357
+ ]
358
+ }
359
+ }
360
+ };
361
+ ```
362
+
363
+ ## Dynamic Form Generation
364
+
365
+ ### Loading Schema from API
366
+
367
+ ```typescript
368
+ class DynamicFormLoader {
369
+ async loadFormSchema(formId: string): Promise<FlexiFormSchema> {
370
+ const response = await fetch(`/api/forms/${formId}/schema`);
371
+ return await response.json();
372
+ }
373
+
374
+ async loadFormData(formId: string, recordId?: string): Promise<any> {
375
+ const url = recordId
376
+ ? `/api/forms/${formId}/data/${recordId}`
377
+ : `/api/forms/${formId}/data/new`;
378
+ const response = await fetch(url);
379
+ return await response.json();
380
+ }
381
+ }
382
+
383
+ // Usage in component
384
+ const loader = new DynamicFormLoader();
385
+ let formSchema = null;
386
+ let formData = {};
387
+
388
+ onMount(async () => {
389
+ formSchema = await loader.loadFormSchema('user-profile');
390
+ formData = await loader.loadFormData('user-profile', userId);
391
+ });
392
+ ```
393
+
394
+ ### Schema Builder Pattern
395
+
396
+ ```typescript
397
+ class FormSchemaBuilder {
398
+ private schema: FlexiFormSchema;
399
+
400
+ constructor() {
401
+ this.schema = {
402
+ mode: 'flex',
403
+ arrangement: 'vertical',
404
+ variant: 'outlined',
405
+ elements: {}
406
+ };
407
+ }
408
+
409
+ addCard(key: string, title?: string): CardBuilder {
410
+ const cardBuilder = new CardBuilder(title);
411
+ this.schema.elements[key] = cardBuilder.getSchema();
412
+ return cardBuilder;
413
+ }
414
+
415
+ setLayout(mode: 'flex' | 'grid', arrangement?: 'vertical' | 'horizontal') {
416
+ this.schema.mode = mode;
417
+ this.schema.arrangement = arrangement;
418
+ return this;
419
+ }
420
+
421
+ build(): FlexiFormSchema {
422
+ return this.schema;
423
+ }
424
+ }
425
+
426
+ class CardBuilder {
427
+ private card: FlexiCardSchema;
428
+
429
+ constructor(title?: string) {
430
+ this.card = {
431
+ title,
432
+ fields: []
433
+ };
434
+ }
435
+
436
+ addTextField(keyField: string, label: string, options: any = {}) {
437
+ this.card.fields.push({
438
+ type: 'text-editor',
439
+ keyField,
440
+ name: keyField,
441
+ label,
442
+ ...options
443
+ });
444
+ return this;
445
+ }
446
+
447
+ addSelectField(keyField: string, label: string, dictName: string, options: any = {}) {
448
+ this.card.fields.push({
449
+ type: 'options-selector',
450
+ keyField,
451
+ name: keyField,
452
+ label,
453
+ dictName,
454
+ ...options
455
+ });
456
+ return this;
457
+ }
458
+
459
+ getSchema(): FlexiCardSchema {
460
+ return this.card;
461
+ }
462
+ }
463
+
464
+ // Usage
465
+ const schema = new FormSchemaBuilder()
466
+ .setLayout('flex', 'vertical')
467
+ .addCard('personal', 'Personal Information')
468
+ .addTextField('name', 'Full Name', { required: true })
469
+ .addTextField('email', 'Email', { required: true })
470
+ .addSelectField('country', 'Country', 'countries')
471
+ .build();
472
+ ```
473
+
474
+ ## Events and Interactions
475
+
476
+ ### Form-Level Events
477
+
478
+ ```svelte
479
+ <script>
480
+ function handleFormChange(event) {
481
+ const { data, field, value } = event.detail;
482
+ console.log(`Field ${field} changed to:`, value);
483
+ console.log('Full form data:', data);
484
+ }
485
+
486
+ function handleFormSubmit(event) {
487
+ const { data, isValid } = event.detail;
488
+ if (isValid) {
489
+ submitForm(data);
490
+ } else {
491
+ console.error('Form validation failed');
492
+ }
493
+ }
494
+
495
+ function handleFormReset(event) {
496
+ console.log('Form was reset');
497
+ }
498
+ </script>
499
+
500
+ <FlexiFormPage
501
+ {schema}
502
+ bind:data={formData}
503
+ on:change={handleFormChange}
504
+ on:submit={handleFormSubmit}
505
+ on:reset={handleFormReset}
506
+ />
507
+ ```
508
+
509
+ ### Field-Level Events
510
+
511
+ ```typescript
512
+ // In schema definition
513
+ {
514
+ type: 'text-editor',
515
+ keyField: 'email',
516
+ name: 'email',
517
+ label: 'Email',
518
+ events: {
519
+ change: 'onEmailChange',
520
+ blur: 'onEmailBlur',
521
+ focus: 'onEmailFocus'
522
+ }
523
+ }
524
+
525
+ // In form implementation class
526
+ class UserForm extends FlexiForm {
527
+ onEmailChange(value: string) {
528
+ console.log('Email changed:', value);
529
+ // Trigger email validation
530
+ this.validateEmail(value);
531
+ }
532
+
533
+ onEmailBlur(event: FocusEvent) {
534
+ console.log('Email field lost focus');
535
+ // Check for duplicate email
536
+ this.checkEmailUniqueness(event.target.value);
537
+ }
538
+
539
+ onEmailFocus(event: FocusEvent) {
540
+ console.log('Email field gained focus');
541
+ }
542
+ }
543
+ ```
544
+
545
+ ## Extending FlexiForm
546
+
547
+ ### Creating Custom Field Types
548
+
549
+ #### 1. Create Custom Field Component
550
+
551
+ ```svelte
552
+ <!-- CustomRatingField.svelte -->
553
+ <script lang="ts">
554
+ import type FlexiField from "$lib/flexi-form/flexi_field/FlexiField";
555
+
556
+ export let field: FlexiField;
557
+ export let readonly: boolean;
558
+ export let disabled: boolean;
559
+ export let variant: "" | "outlined" | "filled";
560
+
561
+ $: props = field.props;
562
+ $: data = field.data;
563
+ $: events = field.events;
564
+
565
+ const maxRating = props.maxRating || 5;
566
+ let currentRating = data[field.keyField] || 0;
567
+
568
+ function setRating(rating: number) {
569
+ if (readonly || disabled) return;
570
+ currentRating = rating;
571
+ data[field.keyField] = rating;
572
+ events.change?.(rating);
573
+ }
574
+ </script>
575
+
576
+ <div class="rating-field" class:readonly class:disabled>
577
+ <label>{field.label}</label>
578
+ <div class="stars">
579
+ {#each Array(maxRating) as _, i}
580
+ <button
581
+ type="button"
582
+ class="star"
583
+ class:filled={i < currentRating}
584
+ {disabled}
585
+ on:click={() => setRating(i + 1)}
586
+ >
587
+
588
+ </button>
589
+ {/each}
590
+ </div>
591
+ </div>
592
+
593
+ <style>
594
+ .rating-field {
595
+ display: flex;
596
+ flex-direction: column;
597
+ gap: 8px;
598
+ }
599
+
600
+ .stars {
601
+ display: flex;
602
+ gap: 4px;
603
+ }
604
+
605
+ .star {
606
+ background: none;
607
+ border: none;
608
+ font-size: 24px;
609
+ cursor: pointer;
610
+ opacity: 0.3;
611
+ transition: opacity 0.2s;
612
+ }
613
+
614
+ .star.filled {
615
+ opacity: 1;
616
+ }
617
+
618
+ .star:hover:not(:disabled) {
619
+ opacity: 0.7;
620
+ }
621
+
622
+ .readonly .star, .disabled .star {
623
+ cursor: default;
624
+ }
625
+ </style>
626
+ ```
627
+
628
+ #### 2. Register Custom Field Type
629
+
630
+ ```typescript
631
+ import { ComponentBuilder } from '@ticatec/uniface-flexi-form/flexi-form/lib/ComponentBuilder';
632
+ import CustomRatingField from './CustomRatingField.svelte';
633
+
634
+ // Create builder function
635
+ const buildRatingField = (schema: FlexiFieldSchema, dictLoader: DictionaryLoader) => {
636
+ return {
637
+ component: CustomRatingField,
638
+ props: schema.props
639
+ };
640
+ };
641
+
642
+ // Register the custom field type
643
+ const componentBuilder = ComponentBuilder.getInstance();
644
+ componentBuilder.register('rating-field', buildRatingField);
645
+
646
+ // Use in schema
647
+ const schemaWithRating = {
648
+ mode: 'flex',
649
+ elements: {
650
+ 'review-card': {
651
+ title: 'Product Review',
652
+ fields: [
653
+ {
654
+ type: 'rating-field',
655
+ keyField: 'rating',
656
+ name: 'rating',
657
+ label: 'Product Rating',
658
+ props: {
659
+ maxRating: 5
660
+ }
661
+ },
662
+ {
663
+ type: 'memo-editor',
664
+ keyField: 'comment',
665
+ name: 'comment',
666
+ label: 'Review Comment'
667
+ }
668
+ ]
669
+ }
670
+ }
671
+ };
672
+ ```
673
+
674
+ ### Creating Custom Form Classes
675
+
676
+ ```typescript
677
+ import FlexiForm, { type FlexiFormSchema } from '@ticatec/uniface-flexi-form/flexi-form';
678
+
679
+ class UserProfileForm extends FlexiForm {
680
+ private userId: string;
681
+
682
+ constructor(userId: string, data: any, schema: FlexiFormSchema) {
683
+ super(data, schema);
684
+ this.userId = userId;
685
+ }
686
+
687
+ protected buildCards(): void {
688
+ // Custom card building logic
689
+ for (const [key, element] of Object.entries(this.elements)) {
690
+ if (element.type === 'block') {
691
+ // Handle block elements
692
+ } else {
693
+ // Create card with custom logic
694
+ const card = new CustomUserCard(this, element as FlexiCardSchema);
695
+ this.cards.push(card);
696
+ }
697
+ }
698
+ }
699
+
700
+ getTitle(): string {
701
+ return `User Profile - ${this.data.name || 'New User'}`;
702
+ }
703
+
704
+ // Custom methods
705
+ async saveProfile(): Promise<void> {
706
+ try {
707
+ await fetch(`/api/users/${this.userId}`, {
708
+ method: 'PUT',
709
+ headers: { 'Content-Type': 'application/json' },
710
+ body: JSON.stringify(this.data)
711
+ });
712
+ console.log('Profile saved successfully');
713
+ } catch (error) {
714
+ console.error('Failed to save profile:', error);
715
+ }
716
+ }
717
+
718
+ validateEmail(email: string): boolean {
719
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
720
+ return emailRegex.test(email);
721
+ }
722
+
723
+ // Event handlers (called from field events)
724
+ onEmailChange(value: string): void {
725
+ if (!this.validateEmail(value)) {
726
+ console.warn('Invalid email format');
727
+ }
728
+ }
729
+
730
+ onPhoneChange(value: string): void {
731
+ // Format phone number
732
+ const formatted = this.formatPhoneNumber(value);
733
+ this.data.phone = formatted;
734
+ this.invalidate(); // Trigger UI update
735
+ }
736
+
737
+ private formatPhoneNumber(phone: string): string {
738
+ const cleaned = phone.replace(/\D/g, '');
739
+ if (cleaned.length === 10) {
740
+ return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`;
741
+ }
742
+ return phone;
743
+ }
744
+ }
745
+
746
+ // Usage
747
+ const userForm = new UserProfileForm(userId, userData, formSchema);
748
+ await userForm.initialize();
749
+ ```
750
+
751
+ ## Advanced Patterns
752
+
753
+ ### Conditional Field Display
754
+
755
+ ```typescript
756
+ class ConditionalFormCard extends FlexiCard {
757
+ protected createFromSchema(): void {
758
+ super.createFromSchema();
759
+
760
+ // Add conditional logic
761
+ this.setupConditionalFields();
762
+ }
763
+
764
+ private setupConditionalFields(): void {
765
+ const typeField = this.field['userType'];
766
+ const companyField = this.field['company'];
767
+
768
+ if (typeField && companyField) {
769
+ typeField.events.change = (value: string) => {
770
+ companyField.visibility = value === 'business'
771
+ ? Visibility.View
772
+ : Visibility.Hidden;
773
+ this.invalidate();
774
+ };
775
+ }
776
+ }
777
+ }
778
+ ```
779
+
780
+ ### Dynamic Field Dependencies
781
+
782
+ ```typescript
783
+ class DependentFieldsHandler {
784
+ private form: FlexiForm;
785
+ private dependencies: Map<string, string[]> = new Map();
786
+
787
+ constructor(form: FlexiForm) {
788
+ this.form = form;
789
+ this.setupDependencies();
790
+ }
791
+
792
+ setupDependencies(): void {
793
+ // Define field dependencies
794
+ this.dependencies.set('country', ['state', 'city']);
795
+ this.dependencies.set('state', ['city']);
796
+
797
+ // Setup change handlers
798
+ for (const [parentField, dependentFields] of this.dependencies) {
799
+ this.setupFieldHandler(parentField, dependentFields);
800
+ }
801
+ }
802
+
803
+ private setupFieldHandler(parentField: string, dependentFields: string[]): void {
804
+ const field = this.findField(parentField);
805
+ if (field) {
806
+ const originalHandler = field.events.change;
807
+ field.events.change = async (value: any) => {
808
+ // Call original handler
809
+ originalHandler?.(value);
810
+
811
+ // Update dependent fields
812
+ await this.updateDependentFields(parentField, value, dependentFields);
813
+ };
814
+ }
815
+ }
816
+
817
+ private async updateDependentFields(
818
+ parentField: string,
819
+ value: any,
820
+ dependentFields: string[]
821
+ ): Promise<void> {
822
+ for (const fieldName of dependentFields) {
823
+ const field = this.findField(fieldName);
824
+ if (field) {
825
+ // Clear current value
826
+ this.form.data[fieldName] = null;
827
+
828
+ // Load new options
829
+ const options = await this.loadOptions(fieldName, { [parentField]: value });
830
+ field.props.options = options;
831
+ }
832
+ }
833
+
834
+ this.form.invalidate();
835
+ }
836
+
837
+ private async loadOptions(fieldName: string, context: any): Promise<any[]> {
838
+ const response = await fetch(`/api/options/${fieldName}`, {
839
+ method: 'POST',
840
+ headers: { 'Content-Type': 'application/json' },
841
+ body: JSON.stringify(context)
842
+ });
843
+ return await response.json();
844
+ }
845
+
846
+ private findField(fieldName: string): FlexiField | null {
847
+ for (const card of this.form.cards) {
848
+ if (card.field[fieldName]) {
849
+ return card.field[fieldName];
850
+ }
851
+ }
852
+ return null;
853
+ }
854
+ }
855
+ ```
856
+
857
+ ### Form Wizard Pattern
858
+
859
+ ```typescript
860
+ class WizardFormManager {
861
+ private steps: FlexiFormSchema[] = [];
862
+ private currentStep = 0;
863
+ private stepData: any[] = [];
864
+
865
+ constructor(steps: FlexiFormSchema[]) {
866
+ this.steps = steps;
867
+ this.stepData = new Array(steps.length).fill({});
868
+ }
869
+
870
+ getCurrentSchema(): FlexiFormSchema {
871
+ return this.steps[this.currentStep];
872
+ }
873
+
874
+ getCurrentData(): any {
875
+ return this.stepData[this.currentStep];
876
+ }
877
+
878
+ nextStep(): boolean {
879
+ if (this.currentStep < this.steps.length - 1) {
880
+ this.currentStep++;
881
+ return true;
882
+ }
883
+ return false;
884
+ }
885
+
886
+ previousStep(): boolean {
887
+ if (this.currentStep > 0) {
888
+ this.currentStep--;
889
+ return true;
890
+ }
891
+ return false;
892
+ }
893
+
894
+ getFinalData(): any {
895
+ return this.stepData.reduce((acc, stepData) => ({
896
+ ...acc,
897
+ ...stepData
898
+ }), {});
899
+ }
900
+
901
+ isFirstStep(): boolean {
902
+ return this.currentStep === 0;
903
+ }
904
+
905
+ isLastStep(): boolean {
906
+ return this.currentStep === this.steps.length - 1;
907
+ }
908
+
909
+ getProgress(): number {
910
+ return ((this.currentStep + 1) / this.steps.length) * 100;
911
+ }
912
+ }
913
+
914
+ // Usage in Svelte component
915
+ let wizardManager = new WizardFormManager([
916
+ personalInfoSchema,
917
+ contactInfoSchema,
918
+ preferencesSchema
919
+ ]);
920
+
921
+ function handleNext() {
922
+ if (wizardManager.nextStep()) {
923
+ currentSchema = wizardManager.getCurrentSchema();
924
+ currentData = wizardManager.getCurrentData();
925
+ } else {
926
+ // Submit final form
927
+ submitWizardData(wizardManager.getFinalData());
928
+ }
929
+ }
930
+ ```
931
+
932
+ ## Best Practices
933
+
934
+ ### 1. Schema Organization
935
+
936
+ ```typescript
937
+ // Good: Organized schema structure
938
+ const FORM_SCHEMAS = {
939
+ userProfile: {
940
+ mode: 'flex',
941
+ arrangement: 'vertical',
942
+ elements: {
943
+ personal: USER_PERSONAL_CARD,
944
+ contact: USER_CONTACT_CARD,
945
+ preferences: USER_PREFERENCES_CARD
946
+ }
947
+ }
948
+ };
949
+
950
+ const USER_PERSONAL_CARD: FlexiCardSchema = {
951
+ title: 'Personal Information',
952
+ fields: [
953
+ FIELDS.firstName,
954
+ FIELDS.lastName,
955
+ FIELDS.birthDate
956
+ ]
957
+ };
958
+
959
+ const FIELDS = {
960
+ firstName: {
961
+ type: 'text-editor',
962
+ keyField: 'firstName',
963
+ name: 'firstName',
964
+ label: 'First Name',
965
+ required: true,
966
+ props: { maxLength: 50 }
967
+ },
968
+ // ... more field definitions
969
+ };
970
+ ```
971
+
972
+ ### 2. Type Safety
973
+
974
+ ```typescript
975
+ // Define strict types for your schemas
976
+ interface UserFormData {
977
+ firstName: string;
978
+ lastName: string;
979
+ email: string;
980
+ birthDate: Date;
981
+ preferences: UserPreferences;
982
+ }
983
+
984
+ interface UserPreferences {
985
+ newsletter: boolean;
986
+ language: string;
987
+ timezone: string;
988
+ }
989
+
990
+ // Use typed form creation
991
+ class TypedUserForm extends FlexiForm {
992
+ protected data: UserFormData;
993
+
994
+ constructor(data: UserFormData, schema: FlexiFormSchema) {
995
+ super(data, schema);
996
+ }
997
+
998
+ getData(): UserFormData {
999
+ return this.data;
1000
+ }
1001
+ }
1002
+ ```
1003
+
1004
+ ### 3. Error Handling
1005
+
1006
+ ```typescript
1007
+ class FormErrorHandler {
1008
+ static handleFormError(error: any, form: FlexiForm): void {
1009
+ if (error.validationErrors) {
1010
+ this.displayValidationErrors(error.validationErrors, form);
1011
+ } else if (error.networkError) {
1012
+ this.displayNetworkError(error.networkError);
1013
+ } else {
1014
+ this.displayGenericError(error);
1015
+ }
1016
+ }
1017
+
1018
+ private static displayValidationErrors(
1019
+ errors: Record<string, string>,
1020
+ form: FlexiForm
1021
+ ): void {
1022
+ for (const [fieldName, errorMessage] of Object.entries(errors)) {
1023
+ const field = this.findFieldInForm(fieldName, form);
1024
+ if (field) {
1025
+ field.error = errorMessage;
1026
+ }
1027
+ }
1028
+ form.invalidate();
1029
+ }
1030
+ }
1031
+ ```
1032
+
1033
+ ### 4. Performance Optimization
1034
+
1035
+ ```typescript
1036
+ // Lazy load large forms
1037
+ class LazyFormLoader {
1038
+ private formCache = new Map<string, FlexiFormSchema>();
1039
+
1040
+ async loadForm(formId: string): Promise<FlexiFormSchema> {
1041
+ if (this.formCache.has(formId)) {
1042
+ return this.formCache.get(formId)!;
1043
+ }
1044
+
1045
+ const schema = await fetch(`/api/forms/${formId}`).then(r => r.json());
1046
+ this.formCache.set(formId, schema);
1047
+ return schema;
1048
+ }
1049
+
1050
+ preloadForms(formIds: string[]): void {
1051
+ formIds.forEach(id => this.loadForm(id));
1052
+ }
1053
+ }
1054
+
1055
+ // Debounce form changes
1056
+ function createDebouncedFormHandler(delay = 300) {
1057
+ let timeoutId: number;
1058
+
1059
+ return function(handler: Function) {
1060
+ return function(...args: any[]) {
1061
+ clearTimeout(timeoutId);
1062
+ timeoutId = setTimeout(() => handler(...args), delay);
1063
+ };
1064
+ };
1065
+ }
1066
+ ```
1067
+
1068
+ This comprehensive guide provides everything needed to effectively use and extend FlexiForm in your applications. The examples progress from basic usage to advanced patterns, showing how to build sophisticated form systems with FlexiForm's flexible architecture.