@componyx/ui 0.0.0-stage → 1.0.4

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 (169) hide show
  1. package/AppSkin/AppSkin.css +1268 -0
  2. package/AppSkin/AppSkin.js +2748 -0
  3. package/AppSkin/Themes/Default.css +241 -0
  4. package/Base/Base.js +3407 -0
  5. package/Base/ComponentIcons.css +66 -0
  6. package/Base/Font/ComponentIcons.woff +0 -0
  7. package/Base/Font/UI.woff +0 -0
  8. package/Base/Library.js +8432 -0
  9. package/Base/NavigationManager.js +462 -0
  10. package/Base/ResponsiveTemplates.js +371 -0
  11. package/Base/Sanitizer.js +288 -0
  12. package/Base/Themes/Default.css +255 -0
  13. package/Base/UI.css +618 -0
  14. package/Base/UIResourcePath.js +6 -0
  15. package/Bindary/Bindary.js +2011 -0
  16. package/Bindary/Modules/Connection.js +216 -0
  17. package/Bindary/Modules/Core.js +854 -0
  18. package/Bindary/Modules/DataUpdater.js +351 -0
  19. package/Bindary/Modules/ExpressionEngine.js +347 -0
  20. package/Bindary/Modules/ProxyManager.js +482 -0
  21. package/Bindary/Modules/Renderer.js +605 -0
  22. package/Bindary/Modules/Router.js +291 -0
  23. package/Bindary/Modules/Scroller.js +89 -0
  24. package/Bindary/Modules/Template.js +1273 -0
  25. package/BookmarkSpy/BookmarkSpy.js +430 -0
  26. package/Box/Box.css +36 -0
  27. package/Box/Box.js +1685 -0
  28. package/Button/Button.css +470 -0
  29. package/Button/Button.js +1398 -0
  30. package/Button/Themes/Default.css +200 -0
  31. package/Carousel/Carousel.css +273 -0
  32. package/Carousel/Carousel.js +607 -0
  33. package/ColorButton/ColorButton.css +39 -0
  34. package/ColorButton/ColorButton.js +336 -0
  35. package/ColorPicker/ColorPicker.css +359 -0
  36. package/ColorPicker/ColorPicker.js +972 -0
  37. package/ComboBox/ComboBox.css +357 -0
  38. package/ComboBox/ComboBox.js +2342 -0
  39. package/ComboBox/Themes/Default.css +23 -0
  40. package/DataPager/DataPager.css +72 -0
  41. package/DataPager/DataPager.js +674 -0
  42. package/DatePicker/DatePicker.css +227 -0
  43. package/DatePicker/DatePicker.js +1531 -0
  44. package/DatePicker/Themes/Default.css +26 -0
  45. package/Dialog/Dialog.css +106 -0
  46. package/Dialog/Dialog.js +572 -0
  47. package/Dialog/Themes/Default.css +10 -0
  48. package/Editor/Data/Emoji.json +8285 -0
  49. package/Editor/Data/Special.json +1042 -0
  50. package/Editor/Editor.css +1562 -0
  51. package/Editor/Editor.js +4058 -0
  52. package/Editor/Font/Editor.woff +0 -0
  53. package/Editor/Modules/ComponentFactory.js +565 -0
  54. package/Editor/Modules/ContentManager.js +748 -0
  55. package/Editor/Modules/DialogManager.js +1234 -0
  56. package/Editor/Modules/EventManager.js +458 -0
  57. package/Editor/Modules/Format.js +950 -0
  58. package/Editor/Modules/History.js +191 -0
  59. package/Editor/Modules/LayoutState.js +379 -0
  60. package/Editor/Modules/ListManager.js +296 -0
  61. package/Editor/Modules/MenuManager.js +388 -0
  62. package/Editor/Modules/NodeManager.js +681 -0
  63. package/Editor/Modules/ParagraphButtons.js +122 -0
  64. package/Editor/Modules/Resizer.js +176 -0
  65. package/Editor/Modules/SelectionRange.js +564 -0
  66. package/Editor/Modules/SourceViewBuilder.js +212 -0
  67. package/Editor/Modules/SymbolPicker.js +208 -0
  68. package/Editor/Modules/Table.js +2327 -0
  69. package/Editor/Modules/TableDialog.js +757 -0
  70. package/Editor/Modules/Toolbar.js +375 -0
  71. package/Editor/Modules/TooltipMenu.js +125 -0
  72. package/Editor/Modules/Utility.js +131 -0
  73. package/Editor/Themes/Default.css +204 -0
  74. package/FileUpload/FileUpload.css +156 -0
  75. package/FileUpload/FileUpload.js +898 -0
  76. package/FileUpload/Themes/Default.css +17 -0
  77. package/Form/Form.css +1934 -0
  78. package/Form/Form.js +4943 -0
  79. package/Form/Modules/ActionManager.js +368 -0
  80. package/Form/Modules/BuildPanelManager.js +319 -0
  81. package/Form/Modules/ComponentFactory.js +468 -0
  82. package/Form/Modules/ComponentPanel.js +265 -0
  83. package/Form/Modules/ConfigPanelManager.js +685 -0
  84. package/Form/Modules/DataBinder.js +602 -0
  85. package/Form/Modules/DataObserver.js +717 -0
  86. package/Form/Modules/Draggable.js +756 -0
  87. package/Form/Modules/OptionsPanel.js +342 -0
  88. package/Form/Modules/Renderer.js +1889 -0
  89. package/Form/Modules/RuleEngine.js +585 -0
  90. package/Form/Modules/RulesPanel.js +1158 -0
  91. package/Form/Modules/Types.js +1709 -0
  92. package/Form/Modules/ValidationManager.js +194 -0
  93. package/Form/Themes/Default.css +216 -0
  94. package/FormField/FormField.css +468 -0
  95. package/FormField/FormField.js +576 -0
  96. package/FormField/Themes/Default.css +99 -0
  97. package/Grid/Grid.css +544 -0
  98. package/Grid/Grid.js +3761 -0
  99. package/Grid/Themes/Default.css +43 -0
  100. package/LICENSE +126 -0
  101. package/MaskedTextBox/MaskedTextBox.css +28 -0
  102. package/MaskedTextBox/MaskedTextBox.js +642 -0
  103. package/MaskedTextBox/Themes/Default.css +6 -0
  104. package/Menu/Menu.css +328 -0
  105. package/Menu/Menu.js +2386 -0
  106. package/Menu/Themes/Default.css +24 -0
  107. package/NumericBox/NumericBox.css +120 -0
  108. package/NumericBox/NumericBox.js +984 -0
  109. package/PanelBar/PanelBar.css +123 -0
  110. package/PanelBar/PanelBar.js +505 -0
  111. package/PanelBar/Themes/Default.css +51 -0
  112. package/PieChart/PieChart.css +95 -0
  113. package/PieChart/PieChart.js +820 -0
  114. package/PieChart/Themes/Default.css +9 -0
  115. package/README.md +178 -3
  116. package/Slider/Slider.css +348 -0
  117. package/Slider/Slider.js +1306 -0
  118. package/Slider/Themes/Default.css +44 -0
  119. package/TabStrip/TabStrip.css +52 -0
  120. package/TabStrip/TabStrip.js +362 -0
  121. package/TabStrip/Themes/Default.css +35 -0
  122. package/TimePicker/Themes/Default.css +30 -0
  123. package/TimePicker/TimePicker.css +253 -0
  124. package/TimePicker/TimePicker.js +1485 -0
  125. package/TooltipManager/Themes/Default.css +21 -0
  126. package/TooltipManager/TooltipManager.css +227 -0
  127. package/TooltipManager/TooltipManager.js +791 -0
  128. package/Validator/Validator.css +54 -0
  129. package/Validator/Validator.js +2508 -0
  130. package/componyx.html-data.json +619 -0
  131. package/index.js +27 -0
  132. package/package.json +43 -4
  133. package/types/AppSkin.d.ts +842 -0
  134. package/types/Base.d.ts +1034 -0
  135. package/types/Bindary.d.ts +1196 -0
  136. package/types/BookmarkSpy.d.ts +112 -0
  137. package/types/Box.d.ts +374 -0
  138. package/types/Button.d.ts +455 -0
  139. package/types/Carousel.d.ts +136 -0
  140. package/types/ColorButton.d.ts +120 -0
  141. package/types/ColorPicker.d.ts +225 -0
  142. package/types/ComboBox.d.ts +507 -0
  143. package/types/DataPager.d.ts +152 -0
  144. package/types/DatePicker.d.ts +354 -0
  145. package/types/Dialog.d.ts +160 -0
  146. package/types/Editor.d.ts +1289 -0
  147. package/types/FileUpload.d.ts +206 -0
  148. package/types/Form.d.ts +3078 -0
  149. package/types/FormField.d.ts +204 -0
  150. package/types/Grid.d.ts +911 -0
  151. package/types/Library.d.ts +1899 -0
  152. package/types/MaskedTextBox.d.ts +91 -0
  153. package/types/Menu.d.ts +577 -0
  154. package/types/NumericBox.d.ts +310 -0
  155. package/types/PanelBar.d.ts +223 -0
  156. package/types/PieChart.d.ts +224 -0
  157. package/types/ResponsiveTemplates.d.ts +108 -0
  158. package/types/Slider.d.ts +227 -0
  159. package/types/TabStrip.d.ts +185 -0
  160. package/types/TimePicker.d.ts +265 -0
  161. package/types/TooltipManager.d.ts +178 -0
  162. package/types/UI.d.ts +29 -0
  163. package/types/Validator.d.ts +653 -0
  164. package/types/index.d.ts +59 -0
  165. package/ui.css +11783 -0
  166. package/ui.js +79477 -0
  167. package/ui.min.css +138 -0
  168. package/ui.min.js +264 -0
  169. package/ui.min.js.map +1 -0
@@ -0,0 +1,585 @@
1
+ /*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
2
+ componyx.UI.form_modules = componyx.UI.form_modules || {};
3
+
4
+ const Operators = {
5
+ equal: (a, b) =>
6
+ {
7
+ if (Array.isArray(a) && Array.isArray(b))
8
+ return a.length === b.length && a.every(x => b.includes(x));
9
+ if (Array.isArray(a))
10
+ return a.includes(b);
11
+ if (Array.isArray(b))
12
+ return b.includes(a);
13
+ return a === b;
14
+ },
15
+
16
+ not_equal: (a, b) =>
17
+ {
18
+ if (Array.isArray(a) && Array.isArray(b))
19
+ return !(a.length === b.length && a.every(x => b.includes(x)));
20
+ if (Array.isArray(a))
21
+ return !a.includes(b);
22
+ if (Array.isArray(b))
23
+ return !b.includes(a);
24
+ return a !== b;
25
+ },
26
+
27
+ greater_than: (a, b) =>
28
+ {
29
+ if (Array.isArray(a)) return a.length > b;
30
+ return a > b;
31
+ },
32
+
33
+ greater_than_or_equal: (a, b) =>
34
+ {
35
+ if (Array.isArray(a)) return a.length >= b;
36
+ return a >= b;
37
+ },
38
+
39
+ less_than: (a, b) =>
40
+ {
41
+ if (Array.isArray(a)) return a.length < b;
42
+ return a < b;
43
+ },
44
+
45
+ less_than_or_equal: (a, b) =>
46
+ {
47
+ if (Array.isArray(a)) return a.length <= b;
48
+ return a <= b;
49
+ },
50
+
51
+ contains: (a, b) =>
52
+ {
53
+ if (Array.isArray(a) && Array.isArray(b))
54
+ return b.some(x => a.includes(x)); // any overlap
55
+ if (Array.isArray(a))
56
+ return a.includes(b);
57
+ if (typeof a === 'string')
58
+ return a.indexOf(b) !== -1;
59
+ return false;
60
+ },
61
+
62
+ not_contains: (a, b) =>
63
+ {
64
+ if (Array.isArray(a) && Array.isArray(b))
65
+ return b.every(x => !a.includes(x)); // no overlap
66
+ if (Array.isArray(a))
67
+ return !a.includes(b);
68
+ if (typeof a === 'string')
69
+ return a.indexOf(b) === -1;
70
+ return false;
71
+ },
72
+
73
+ starts_with: (a, b) => typeof a === 'string' && a.startsWith(b),
74
+ ends_with: (a, b) => typeof a === 'string' && a.endsWith(b),
75
+
76
+ empty: (a) =>
77
+ {
78
+ if (Array.isArray(a)) return a.length === 0;
79
+ return a == null || a === '';
80
+ },
81
+
82
+ not_empty: (a) =>
83
+ {
84
+ if (Array.isArray(a)) return a.length > 0;
85
+ return !(a == null || a === '');
86
+ }
87
+ };
88
+
89
+ componyx.UI.form_modules.RuleEngine = class RuleEngine
90
+ {
91
+ #fields;
92
+ #isExecuting = false;
93
+ #hiddenFieldCache;
94
+ #dependencyMap;
95
+ #originalFieldStates = new Map();
96
+ #visible = { on: 'show', off: 'hide' };
97
+ #readOnly = { on: 'readOnly', off: 'editable' };
98
+ #disabled = { on: 'disable', off: 'enable' };
99
+ #required = { on: 'required', off: 'optional' };
100
+
101
+ constructor(form)
102
+ {
103
+ this.form = form;
104
+ this.dataBinder = form.dataBinder;
105
+ this.InputTypeOption = form.constructor.InputTypeOption;
106
+ this.TriggerOption = form.constructor.RuleCaseTriggerOption;
107
+ this.RunVisibilityOption = form.constructor.RuleCaseRunVisibilityOption;
108
+ this.ComparisonOperatorOption = form.constructor.RuleComparisonOperatorOption;
109
+ this.ActionOption = form.constructor.RuleActionOption;
110
+ }
111
+
112
+ destroy()
113
+ {
114
+ this.#restoreFields();
115
+ }
116
+
117
+ /**
118
+ * Evaluates all rules for all fields/fieldsets and executes actions for matching rule cases.
119
+ * @param {componyx.UI.Form.RuleCaseTriggerOption} caseTrigger The trigger causing this evaluation (e.g., INIT, SHOW_SECTION, SUBMIT).
120
+ */
121
+ initRules(trigger = this.TriggerOption.INIT)
122
+ {
123
+ if (!this.#dependencyMap)
124
+ this.#buildDependencyMap();
125
+
126
+ this.#hiddenFieldCache = {};
127
+ this.form.getFields().forEach(field => this.#applyRulesChain(field, trigger));
128
+ }
129
+
130
+ /**
131
+ * Evaluates the field/fieldset rules and executes actions for each rule case if conditions are met.
132
+ * @param {componyx.UI.Form.Field} changedField The field for which the rule actions are applied.
133
+ * @param {componyx.UI.Form.RuleCaseTriggerOption} caseTrigger The trigger that caused this evaluation.
134
+ */
135
+ applyRules(changedField, trigger = this.TriggerOption.CHANGE)
136
+ {
137
+ this.#applyRulesChain(changedField, trigger);
138
+ }
139
+
140
+ /**
141
+ * Updates the temporary rule-driven state of a field.
142
+ * @param {Object} field - The form field whose rule-driven state should be updated.
143
+ * @param {string} key - The property name being updated (e.g., 'readOnly', 'value').
144
+ * @param {*} newValue - The new value to store for this rule-driven property.
145
+ * @param {boolean} manualChange - A value indicating if this value was set manually.
146
+ */
147
+ updateRuleDrivenState(field, key, newValue, manualChange = false)
148
+ {
149
+ const entry = this.#originalFieldStates.get(field.id);
150
+ if (!entry) return;
151
+
152
+ entry.ruleState ??= {};
153
+ entry.ruleState[key] = newValue;
154
+ entry.manualChange = manualChange;
155
+ }
156
+
157
+ /**
158
+ * Build the dependency map where each field holds a set of its dependend fields stored by field id.
159
+ * @private
160
+ */
161
+ #buildDependencyMap()
162
+ {
163
+ this.#dependencyMap = new Map();
164
+
165
+ this.form.getFields().forEach(field =>
166
+ {
167
+ const targetId = field.id;
168
+ (field.ruleCases || []).forEach(ruleCase =>
169
+ {
170
+ (ruleCase.ruleGroups || []).forEach(group =>
171
+ {
172
+ (group.rules || []).forEach(rule =>
173
+ {
174
+ const sourceId = rule.sourceId;
175
+ if (!this.#dependencyMap.has(sourceId))
176
+ {
177
+ this.#dependencyMap.set(sourceId, new Set());
178
+ }
179
+ this.#dependencyMap.get(sourceId).add(targetId);
180
+ });
181
+ });
182
+ });
183
+ });
184
+ }
185
+
186
+ #restoreFields()
187
+ {
188
+ const fields = this.form.getFields();
189
+
190
+ for (const [fieldId] of this.#originalFieldStates)
191
+ {
192
+ const field = this.form.getField(fieldId, null, fields);
193
+ if (!field) continue;
194
+
195
+ this.#restoreOriginalState(field, true);
196
+ }
197
+
198
+ this.#originalFieldStates = new Map();
199
+ }
200
+
201
+ /**
202
+ * Evaluates the field/fieldset rules and executes actions for each rule case if conditions are met.
203
+ * @private
204
+ * @param {Field} changedField - The field or fieldset to evaluate.
205
+ * @param {string} trigger - The current lifecycle trigger ('init' or 'change').
206
+ */
207
+ async #applyRulesChain(changedField, trigger)
208
+ {
209
+ if (this.#isExecuting)
210
+ return;
211
+
212
+ this.#isExecuting = true;
213
+
214
+ try
215
+ {
216
+ this.#fields = this.form.getFields();
217
+
218
+ const visited = new Set(),
219
+ queue = [...(this.#dependencyMap.get(changedField.id) || []), ...this.#getUnconditionalFields(trigger)];
220
+
221
+ while (queue.length > 0)
222
+ {
223
+ const currentId = queue.shift();
224
+ if (visited.has(currentId)) continue;
225
+ visited.add(currentId);
226
+
227
+ const currentField = this.form.getField(currentId, null, this.#fields);
228
+ if (!currentField) continue;
229
+
230
+ this.#restoreOriginalState(currentField);
231
+
232
+ if (this.form.ended) return; // If form ended, stop executing rules and actions
233
+
234
+ const section = this.form.getParentSection(currentField);
235
+
236
+ // Process each ruleCase sequentially
237
+ const ruleCases = currentField.ruleCases || [];
238
+ for (const ruleCase of ruleCases)
239
+ {
240
+ if (section && !section.visible && ruleCase.runVisibility !== this.RunVisibilityOption.SECTION_HIDDEN)
241
+ continue;
242
+
243
+ if (!ruleCase.trigger || ruleCase.trigger === this.TriggerOption.ALWAYS || ruleCase.trigger === trigger)
244
+ {
245
+ const isHidden = this.#isFieldHidden(currentField);
246
+ if (isHidden && (ruleCase.runVisibility !== this.RunVisibilityOption.HIDDEN && ruleCase.runVisibility !== this.RunVisibilityOption.SECTION_HIDDEN))
247
+ continue;
248
+
249
+ const caseMatched = this.#evaluateCase(ruleCase);
250
+ if (!caseMatched)
251
+ continue;
252
+
253
+ const actions = ruleCase.actions || [];
254
+ for (const action of actions)
255
+ {
256
+ const target = action.targetFieldSetId ? this.form.getFieldSet(action.targetFieldSetId) : currentField,
257
+ runtime = this.form.runtimeActions[action.runtimeActionId];
258
+
259
+ this.#storeFieldState(currentField, action);
260
+ const result = await this.form.executeAction(action, target); // await confirmation if mustAwait
261
+
262
+ if (runtime?.mustAwait && result && result.success === false)
263
+ break; // skip remaining actions in this ruleCase
264
+ }
265
+ }
266
+ }
267
+
268
+ // Add all dependents of currentField to the queue
269
+ (this.#dependencyMap.get(currentId) || []).forEach(depId =>
270
+ {
271
+ if (!visited.has(depId))
272
+ queue.push(depId);
273
+ });
274
+ }
275
+ }
276
+ finally
277
+ {
278
+ this.#isExecuting = false;
279
+ }
280
+ }
281
+
282
+ #getUnconditionalFields(trigger)
283
+ {
284
+ const result = [];
285
+
286
+ this.#fields.forEach(field =>
287
+ {
288
+ const cases = field.ruleCases || [];
289
+ cases.forEach(ruleCase =>
290
+ {
291
+ const hasNoRules = !ruleCase.ruleGroups || ruleCase.ruleGroups.length === 0,
292
+ hasActions = ruleCase.actions && ruleCase.actions.length > 0,
293
+ triggerMatches = !ruleCase.trigger || ruleCase.trigger === this.TriggerOption.ALWAYS || ruleCase.trigger === trigger;
294
+
295
+ if (hasNoRules && hasActions && triggerMatches)
296
+ {
297
+ result.push(field.id);
298
+ }
299
+ });
300
+ });
301
+
302
+ return result;
303
+ }
304
+
305
+ #storeFieldState(field, action)
306
+ {
307
+ const relevantActions = [this.#visible.on, this.#visible.off, this.#readOnly.on, this.#readOnly.off,
308
+ this.#disabled.on, this.#disabled.off, this.#required.on, this.#required.off, 'value', 'expression', 'feedback'];
309
+
310
+ if (!relevantActions.includes(action?.runtimeActionId)) return; // nothing to store
311
+
312
+ const fields = action?.targetFieldSetId
313
+ ? this.form.getFields(this.form.getFieldSet(action.targetFieldSetId)) || []
314
+ : [field];
315
+
316
+ const props = {};
317
+ if ([this.#required.on, this.#required.off].includes(action.runtimeActionId)) props.required = true;
318
+ if ([this.#visible.on, this.#visible.off].includes(action.runtimeActionId)) props.visible = true;
319
+ if ([this.#readOnly.on, this.#readOnly.off].includes(action.runtimeActionId)) props.readOnly = true;
320
+ if ([this.#disabled.on, this.#disabled.off].includes(action.runtimeActionId)) props.disabled = true;
321
+
322
+ fields.forEach(f =>
323
+ {
324
+ const entry = this.#originalFieldStates.get(f.id) || { original: {}, affectedByFieldSet: null };
325
+
326
+ Object.keys(props).forEach(prop =>
327
+ {
328
+ if (!(prop in entry.original))
329
+ entry.original[prop] = f[prop];
330
+ });
331
+
332
+ if (action.runtimeActionId == 'feedback' && !('feedback' in entry.original))
333
+ {
334
+ const ruleFeedbackEl = this.#getFeedbackElement(f);
335
+
336
+ if (ruleFeedbackEl)
337
+ entry.original.feedback = ruleFeedbackEl.textContent;
338
+ }
339
+
340
+ if (!('value' in entry.original))
341
+ {
342
+ entry.original.value = this.dataBinder.getModelValue(f);
343
+ }
344
+
345
+ // Store the state of field options (disabled state of options)
346
+ if (f.options && Array.isArray(f.options))
347
+ {
348
+ entry.original.options = f.options.map(opt => ({
349
+ id: opt.id,
350
+ disabled: opt.disabled
351
+ }));
352
+ }
353
+
354
+ if (action.targetFieldSetId)
355
+ entry.affectedByFieldSet = action.targetFieldSetId;
356
+
357
+ this.#originalFieldStates.set(f.id, entry);
358
+ });
359
+ }
360
+
361
+ #restoreOriginalState(field, ignoreManualFlag)
362
+ {
363
+ const entry = this.#originalFieldStates.get(field.id);
364
+ if (!entry) return;
365
+
366
+ const restore = f =>
367
+ {
368
+ const entry = this.#originalFieldStates.get(f.id);
369
+ if (!entry) return;
370
+
371
+ const { value, feedback, options, ...switchStates } = entry.original;
372
+
373
+ if ('value' in entry.original && ignoreManualFlag || !entry.manualChange)
374
+ {
375
+ if (Operators.equal(entry.ruleState?.value, this.dataBinder.getModelValue(f)))
376
+ this.form.dataBinder.setModelValue(f, entry.original.value); // only restore if it holds the value set by the action
377
+ }
378
+
379
+ if (('feedback' in entry.original))
380
+ {
381
+ const ruleFeedbackEl = this.#getFeedbackElement(f);
382
+
383
+ if (ruleFeedbackEl && entry.ruleState?.feedback === ruleFeedbackEl.textContent)
384
+ ruleFeedbackEl.textContent = entry.original.feedback; // only restore if it holds the feedback msg set by the action
385
+ }
386
+
387
+ // Restore options (disabled state for each option)
388
+ if (options && Array.isArray(options))
389
+ {
390
+ f.options.forEach(opt =>
391
+ {
392
+ const storedOpt = options.find(o => o.id === opt.id);
393
+ if (storedOpt)
394
+ {
395
+ opt.disabled = storedOpt.disabled;
396
+ }
397
+ });
398
+ }
399
+
400
+ if (!$lib.isEmpty(switchStates))
401
+ {
402
+ Object.assign(f, switchStates);
403
+ this.form.updateField(f);
404
+ }
405
+ };
406
+
407
+ if (entry.affectedByFieldSet)
408
+ {
409
+ const siblings = this.form.getFields(this.form.getFieldSet(entry.affectedByFieldSet)) || [];
410
+ siblings.forEach(restore);
411
+ }
412
+ else
413
+ {
414
+ restore(field);
415
+ }
416
+ }
417
+
418
+ #getFeedbackElement(field)
419
+ {
420
+ return field.element.querySelector('.' + this.form.getCssClass(this.form.classOption.RULE_FEEDBACK));
421
+ }
422
+
423
+ #isFieldHidden(field)
424
+ {
425
+ if (field.id in this.#hiddenFieldCache)
426
+ return this.#hiddenFieldCache[field.id];
427
+
428
+ let current = field,
429
+ hidden = false;
430
+
431
+ while (current)
432
+ {
433
+ if (current.visible === false)
434
+ {
435
+ hidden = true;
436
+ break;
437
+ }
438
+ if (current.type === 'Section') break;
439
+
440
+ current = current.parent;
441
+ }
442
+
443
+ this.#hiddenFieldCache[field.id] = hidden;
444
+ return hidden;
445
+ }
446
+
447
+ /**
448
+ * Loops through all ruleGroups in a case and evaluates them to see if they match.
449
+ * @private
450
+ * @param {Object} ruleCase
451
+ * @returns {boolean} A value indicating if the rule case matches.
452
+ */
453
+ #evaluateCase(ruleCase)
454
+ {
455
+ if (!ruleCase.ruleGroups || ruleCase.ruleGroups.length === 0) return true;
456
+
457
+ let result;
458
+ const groups = ruleCase.ruleGroups;
459
+
460
+ for (let index = 0; index < groups.length; index++)
461
+ {
462
+ const matched = this.#evaluateGroup(groups[index]);
463
+
464
+ if (index === 0)
465
+ {
466
+ result = matched;
467
+ }
468
+ else
469
+ {
470
+ const op = groups[index - 1].logicalOperator || 'AND';
471
+ result = (op === 'OR') ? (result || matched) : (result && matched);
472
+ }
473
+ }
474
+
475
+ return result;
476
+ }
477
+
478
+ /**
479
+ * Loops through rules in a group and returns true if the group matches.
480
+ * @private
481
+ * @param {Object} group
482
+ * @returns {boolean} A value indicating if the group matches.
483
+ */
484
+ #evaluateGroup(group)
485
+ {
486
+ if (!group?.rules || group.rules.length === 0) return false;
487
+
488
+ let result;
489
+ const rules = group.rules;
490
+
491
+ for (let index = 0; index < rules.length; index++)
492
+ {
493
+ const matched = this.#evaluateRule(rules[index]);
494
+
495
+ if (index === 0)
496
+ {
497
+ result = matched;
498
+ }
499
+ else
500
+ {
501
+ // operator that combines rules[i-1] with rules[i]
502
+ const op = rules[index - 1].logicalOperator || 'AND';
503
+ result = (op === 'OR') ? (result || matched) : (result && matched);
504
+ }
505
+ }
506
+
507
+ return result;
508
+ }
509
+
510
+ /**
511
+ * Evaluates a single rule and returns true if the rule matches.
512
+ * @private
513
+ * @param {Object} rule
514
+ * @returns {boolean} A value indicating if the rule matches.
515
+ */
516
+ #evaluateRule(rule)
517
+ {
518
+ if (!rule?.sourceId || !rule.comparisonOperator)
519
+ return false;
520
+
521
+ const target = this.#getRuleTarget(rule);
522
+ if (!target)
523
+ return false;
524
+
525
+ const isValueOperator = Operators.hasOwnProperty(rule.comparisonOperator);
526
+ let valueToCheck;
527
+
528
+ if (isValueOperator)
529
+ {
530
+ if (target.inputType === this.InputTypeOption.CHECKBOX || target.inputType === this.InputTypeOption.SWITCH)
531
+ {
532
+ const arrVal = this.dataBinder.getModelValue(target);
533
+
534
+ if (Array.isArray(arrVal))
535
+ {
536
+ if (target.componentSettings?.range)
537
+ {
538
+ valueToCheck = rule.isRangeEnd ? arrVal[1] : arrVal[0];
539
+ }
540
+ else
541
+ valueToCheck = arrVal;
542
+ }
543
+ else
544
+ {
545
+ valueToCheck = (target.options || []).filter(opt =>
546
+ {
547
+ return !!this.dataBinder.getModelValue(opt); // make boolean of result
548
+ }).map(opt => (opt.value !== undefined ? opt.value : (opt.name || null))).filter(v => v !== null); // first filter all checked options, then either take the value (if set), otherwise the name
549
+ }
550
+ }
551
+ else
552
+ {
553
+ valueToCheck = this.dataBinder.getModelValue(target);
554
+ }
555
+
556
+ const operatorFn = Operators[rule.comparisonOperator];
557
+ return operatorFn ? operatorFn(valueToCheck, rule.value) : false;
558
+ }
559
+ else
560
+ {
561
+ const propMap = {
562
+ [this.ComparisonOperatorOption.VISIBLE]: ['visible', true],
563
+ [this.ComparisonOperatorOption.DISABLED]: ['disabled', true],
564
+ [this.ComparisonOperatorOption.REQUIRED]: ['required', true],
565
+ [this.ComparisonOperatorOption.HIDDEN]: ['visible', false],
566
+ [this.ComparisonOperatorOption.ENABLED]: ['disabled', false],
567
+ [this.ComparisonOperatorOption.OPTIONAL]: ['required', false],
568
+ };
569
+
570
+ const [propertyName, expectedValue] = propMap[rule.comparisonOperator];
571
+ return target[propertyName] === expectedValue;
572
+ }
573
+ }
574
+
575
+ #getRuleTarget(rule)
576
+ {
577
+ if (!rule?.sourceId) return null;
578
+
579
+ return this.form.getField(rule.sourceId, null, this.#fields) || null;
580
+ }
581
+ };
582
+
583
+ var RuleEngine = componyx.UI.form_modules.RuleEngine;
584
+
585
+ export { RuleEngine as default };