@componyx/ui 0.0.0-stage → 1.0.3

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/DataObserver.js +717 -0
  85. package/Form/Modules/Databinder.js +602 -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,756 @@
1
+ /*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
2
+ componyx.UI.form_modules = componyx.UI.form_modules || {};
3
+ componyx.UI.form_modules.Draggable = class Draggable
4
+ {
5
+ #classOption;
6
+ constructor(form)
7
+ {
8
+ this.form = form;
9
+ this.draggables = new Map();
10
+ this.#classOption = form.classOption;
11
+ }
12
+
13
+ destroy()
14
+ {
15
+ this.draggables.forEach((value) =>
16
+ {
17
+ value.disable();
18
+ });
19
+ }
20
+
21
+ /**
22
+ * @typedef {Object} DragOption
23
+ * @property {HTMLElement} element - Gets the field option row element to be dragged.
24
+ * @property {componyx.UI.Form.Field} field - Gets the corresponding field.
25
+ * @property {componyx.UI.Form.FieldOption} option - Gets the corresponding field option.
26
+ * @property {componyx.UI.PanelBar.Panel} panel - Gets the panel to which the field option belongs.
27
+ */
28
+
29
+ /**
30
+ * Creates a draggable field option.
31
+ * @param {DragOption} dragItem
32
+ */
33
+ createDraggableOption(dragItem)
34
+ {
35
+ let element = dragItem.element,
36
+ dropZone = dragItem.panel.contentElement,
37
+ dragHandle = element.querySelector('.' + this.#getCssClass(this.#classOption.DRAG_HANDLE)),
38
+ onDragStart = this.#dragStartOption.bind(this),
39
+ onDroppable = this.#droppableOption.bind(this),
40
+ onDroppableLeave = this.#droppableOptionLeave.bind(this),
41
+ onDrop = this.#dropOption.bind(this);
42
+
43
+ this.createDraggable(dragItem, dragHandle, this.form.ownerDocument.documentElement, [dropZone], onDragStart, onDroppable, onDroppableLeave, onDrop);
44
+ }
45
+
46
+ /**
47
+ * Creates a draggable build block, field or fieldset.
48
+ * @param {componyx.UI.Form.FieldSet|componyx.UI.Form.Field|componyx.UI.Form.BuildBlock} dragItem
49
+ */
50
+ createDraggableField(dragItem)
51
+ {
52
+ if (this.form.displayMode !== this.form.constructor.DisplayModeOption.BUILD)
53
+ return;
54
+
55
+ let element = dragItem.element,
56
+ isBlock = dragItem.type === 'BuildBlock',
57
+ isSet = dragItem.type === 'FieldSet' || (isBlock && dragItem.blockType == this.form.constructor.BlockTypeOption.FIELD_SET),
58
+ dragHandle = (!isBlock) ? element.querySelector('.' + this.#getCssClass(this.#classOption.DRAG_HANDLE)) : null,
59
+ boundaryZone = this.form.ownerDocument.documentElement,
60
+ getRootZones = () =>
61
+ {
62
+ // Use visible section if available, otherwise fall back to the form pane
63
+ return [Array.from(this.form.querySelectorAll('section')).find(s => s.offsetParent)
64
+ || this.form.querySelector('.' + this.#getCssClass(this.#classOption.FORM_PANE))];
65
+ },
66
+ dropZones = () =>
67
+ {
68
+ return getRootZones().concat(this.#getDropZones((isSet || isBlock) ? element : null));
69
+ },
70
+ onDragStart = (isSet && !isBlock) ? this.#dragStartFieldSet.bind(this) :
71
+ (this.form.isField(dragItem.type)) ? this.#dragStartField.bind(this) : this.#dragStartBuildBlock.bind(this),
72
+ onDroppable = this.#droppableField.bind(this),
73
+ onDroppableLeave = this.#droppableFieldLeave.bind(this),
74
+ onDrop = (isSet) ? this.#dropFieldSet.bind(this) : this.#dropField.bind(this);
75
+
76
+ this.createDraggable(dragItem, dragHandle, boundaryZone, dropZones, onDragStart, onDroppable, onDroppableLeave, onDrop);
77
+ }
78
+
79
+ createDraggable(dragItem, dragHandle, boundaryZone, dropZones, onDragStart, onDroppable, onDroppableLeave, onDrop)
80
+ {
81
+ if (this.draggables.has(dragItem.element))
82
+ {
83
+ this.draggables.get(dragItem.element).disable();
84
+ this.draggables.delete(dragItem.element);
85
+ }
86
+
87
+ let draggable = $lib.draggable(dragItem.element, $base.static.initDragSettings({
88
+ dragGhost: this.#createDragGhost(dragItem.element),
89
+ dragHandle: dragHandle,
90
+ autoGhostSize: false,
91
+ moveOriginal: false,
92
+ ignoreMargins: true,
93
+ scrollX: true,
94
+ scrollY: true,
95
+ boundaryZone: boundaryZone,
96
+ minDragX: 1,
97
+ minDragY: 1,
98
+ dropAcceptMode: 2,
99
+ dragClass: this.#getCssClass(this.#classOption.DRAGGING),
100
+ droppableClass: this.#getCssClass(this.#classOption.DROPPABLE),
101
+ dropZones: dropZones,
102
+ onDragStart: onDragStart,
103
+ onDroppable: onDroppable,
104
+ onDroppableLeave: onDroppableLeave,
105
+ onDrop: onDrop,
106
+ _dragItem: dragItem
107
+ }, this.form.dragSettings));
108
+
109
+ this.draggables.set(dragItem.element, draggable);
110
+ }
111
+
112
+ #getDropZones(excludeEl = null)
113
+ {
114
+ return Array.from(this.form.querySelectorAll('fieldset')).filter(element =>
115
+ element !== excludeEl &&
116
+ element.offsetParent !== null // include visible elements only
117
+ );
118
+ }
119
+
120
+ #createDragGhost(element)
121
+ {
122
+ const container = element.classList.contains(this.#getCssClass(this.#classOption.FIELD_OPTIONS_ITEM))
123
+ ? element.closest('.content') || element
124
+ : element,
125
+ dragGhost = $lib.element(container, null);
126
+
127
+ dragGhost.classList.add(this.#getCssClass(this.#classOption.DRAG_GHOST));
128
+ dragGhost.style.display = 'none';
129
+ return dragGhost;
130
+ }
131
+
132
+ #dragStartOption(args)
133
+ {
134
+ const labelInput = args.element.querySelector('input');
135
+ args.settings.dragGhost.innerHTML = labelInput.value.trim() || this.form.labels.FieldOption;
136
+ args.settings.dragGhost.classList.add(this.#getCssClass(this.#classOption.FIELD_OPTIONS_ITEM_GHOST));
137
+ }
138
+
139
+ #dragStartBuildBlock(args)
140
+ {
141
+ const cloneEl = args.element.cloneNode(true),
142
+ dragGhostClass = this.#getCssClass(this.#classOption.DRAG_GHOST),
143
+ ghostEl = cloneEl.querySelector(`.${dragGhostClass}`),
144
+ buttonEl = cloneEl.querySelector('.button');
145
+
146
+ $lib.remove(buttonEl);
147
+ $lib.remove(ghostEl);
148
+ args.settings.dragGhost.innerHTML = cloneEl.outerHTML;
149
+ }
150
+
151
+ #dragStartField(args)
152
+ {
153
+ const dragItem = args.settings._dragItem,
154
+ buildBlock = this.form.buildBlocks.find((b) =>
155
+ (b.inputType ?? null) === (dragItem.inputType ?? null) &&
156
+ (this.form.constructor.BlockTypeOption.getName(b.blockType) ?? null) === (dragItem.type ?? null) &&
157
+ (b.customType ?? null) === (dragItem.customType ?? null));
158
+
159
+ args.settings.dragGhost.innerHTML = '';
160
+ this.form.buildPanelManager.createBuildBlockElement(buildBlock, args.settings.dragGhost);
161
+ }
162
+
163
+ #dragStartFieldSet(args)
164
+ {
165
+ let set = args.settings._dragItem,
166
+ fieldCount = this.form.getFields(set).length,
167
+ label = set.label || this.form.labels.fieldSet,
168
+ fieldLabel = fieldCount === 1 ? this.form.labels.field : this.form.labels.fields;
169
+
170
+ args.settings.dragGhost.classList.add(this.#getCssClass(this.#classOption.FIELD_SET));
171
+ args.settings.dragGhost.innerHTML = `${label} (${fieldCount} ${fieldLabel})`;
172
+ }
173
+
174
+ #droppableOption(args)
175
+ {
176
+ const dropZone = args.dropZone,
177
+ rows = Array.from(dropZone.querySelectorAll('.' + this.#getCssClass(this.#classOption.FIELD_OPTIONS_ITEM)))
178
+ .filter(el => el !== args.element && !el.classList.contains(this.#getCssClass(this.#classOption.FIELD_OPTIONS_ITEM_NEW))),
179
+ closestElData = this.#getClosestElement(rows, args.settings),
180
+ dropAbove = this.#getDropAbove(),
181
+ dropBelow = this.#getDropBelow();
182
+
183
+ if (!closestElData.el || !closestElData.metrics)
184
+ return;
185
+
186
+ if (args.settings.closestOptionEl && args.settings.closestOptionEl !== closestElData.el)
187
+ this.#clearDroppableOptionStyles(args);
188
+
189
+ args.settings.closestOptionEl = closestElData.el;
190
+ const cl = closestElData.el.classList;
191
+ cl.remove(dropAbove);
192
+ cl.remove(dropBelow);
193
+ cl.add(this.#getCssClass(this.#classOption.DROPPABLE));
194
+
195
+ if (closestElData.metrics.isAbove)
196
+ cl.add(dropAbove);
197
+ else
198
+ cl.add(dropBelow);
199
+ }
200
+
201
+ #droppableField(args)
202
+ {
203
+ let dragItem = args.settings._dragItem,
204
+ isBlock = dragItem.type === 'BuildBlock',
205
+ isSet = dragItem.type === 'FieldSet' || (isBlock && dragItem.blockType == this.form.constructor.BlockTypeOption.FIELD_SET),
206
+ fieldElements = Array.from(args.dropZone.querySelectorAll(isSet ? 'fieldset' : ':scope > .field-pane > .form-field')).filter(el => el !== args.element);
207
+
208
+ if (isSet && args.dropZone.nodeName === 'FIELDSET' && args.dropZone !== args.element)
209
+ fieldElements.push(args.dropZone);
210
+
211
+ if (!this.#allowDrop(dragItem, args.dropZone))
212
+ {
213
+ this.#clearDroppableFieldStyles(args);
214
+ args.dropZone.classList.add(this.#getCssClass(this.#classOption.DROP_DENIED));
215
+ args.settings.dragGhost.classList.add(this.#getCssClass(this.#classOption.DROP_DENIED));
216
+ args.settings._allowDrop = false;
217
+ }
218
+ else
219
+ {
220
+ args.settings._allowDrop = true;
221
+ const closestElData = this.#getClosestElement(fieldElements, args.settings);
222
+
223
+ if (args.settings.closestFieldEl && args.settings.closestFieldEl != closestElData.el)
224
+ this.#clearDroppableFieldStyles(args);
225
+
226
+ if (closestElData.el)
227
+ {
228
+ args.settings.closestFieldEl = closestElData.el;
229
+ this.#showDropIndicator(closestElData.el, closestElData.metrics, isSet);
230
+ }
231
+ else
232
+ args.dropZone.classList.add(this.#getDropInside());
233
+ }
234
+ }
235
+
236
+ #getClosestElement(elements, settings)
237
+ {
238
+ const draggedEl = settings._dragItem.element,
239
+ closestElData = { el: null, metrics: null, distance: Infinity };
240
+
241
+ for (const el of elements)
242
+ {
243
+ if (draggedEl.contains(el)) continue;
244
+
245
+ const metrics = this.#calculateDropMetrics(el, settings.dragGhost);
246
+
247
+ if (metrics.isInsideX && metrics.isInsideY)
248
+ {
249
+ closestElData.el = el;
250
+ closestElData.metrics = metrics;
251
+ break; // perfect match found, no need to check others
252
+ }
253
+
254
+ if (metrics.distance < closestElData.distance)
255
+ {
256
+ closestElData.el = el;
257
+ closestElData.metrics = metrics;
258
+ closestElData.distance = metrics.distance;
259
+ }
260
+ }
261
+ return closestElData;
262
+ }
263
+
264
+ #droppableOptionLeave(args)
265
+ {
266
+ this.#clearDroppableOptionStyles(args);
267
+ }
268
+
269
+ #droppableFieldLeave(args)
270
+ {
271
+ this.#clearDroppableFieldStyles(args);
272
+ }
273
+
274
+ #dropOption(args)
275
+ {
276
+ const draggedEl = args.settings._dragItem.element,
277
+ field = args.settings._dragItem.field,
278
+ option = args.settings._dragItem.option,
279
+ closestOptionEl = args.settings.closestOptionEl,
280
+ cl = closestOptionEl.classList;
281
+
282
+ if (!closestOptionEl || draggedEl === closestOptionEl)
283
+ return;
284
+
285
+ if (cl.contains(this.#getDropAbove()))
286
+ draggedEl.parentElement.insertBefore(draggedEl, closestOptionEl);
287
+ else
288
+ draggedEl.parentElement.insertBefore(draggedEl, closestOptionEl.nextElementSibling);
289
+
290
+ this.#insertOption(option, field, closestOptionEl);
291
+ this.form.updateFieldOptions(field);
292
+ this.#clearDroppableOptionStyles(args);
293
+ }
294
+
295
+ #dropField(args)
296
+ {
297
+ if (!args.settings._allowDrop)
298
+ {
299
+ this.#clearDroppableFieldStyles(args);
300
+ return;
301
+ }
302
+
303
+ let fieldSetEl = args.dropZone,
304
+ fieldSet = this.form.findFieldSetByElement(this.form.fieldSets, fieldSetEl),
305
+ droppedField = args.settings._dragItem,
306
+ closestFieldEl = args.settings.closestFieldEl,
307
+ field;
308
+
309
+ if (droppedField.type === 'BuildBlock') // copy
310
+ {
311
+ field = this.form.createItemFromBuildBlock(droppedField);
312
+ }
313
+ else // move
314
+ {
315
+ field = droppedField;
316
+ this.form.removeField(field); // remove before we re-insert
317
+ }
318
+
319
+ const closestField = (closestFieldEl) ? fieldSet.fields.find(f => f.element === closestFieldEl) : null;
320
+
321
+ this.#insertFieldInFieldSet(field, fieldSet, closestField);
322
+ field = fieldSet.fields.find(f => f.id === field.id); // make sure we get the inserted field proxy object
323
+
324
+ if (closestFieldEl) // Scale if dropping next to the field (left or right)
325
+ {
326
+ const cl = closestFieldEl.classList;
327
+ if (cl.contains(this.#getDropLeft()) || cl.contains(this.#getDropRight()))
328
+ {
329
+ this.form.scaleInlineSiblings(field);
330
+ }
331
+ else if ((!field.width || field.width.endsWith('%')) && (cl.contains(this.#getDropAbove()) || cl.contains(this.#getDropBelow())))
332
+ field.width = '100%';
333
+ }
334
+
335
+ field.selected = true;
336
+ this.form.renderField(field);
337
+ this.form.rebindFieldElements(field.parent);
338
+ this.form.events.onDropField.fire(this, { item: field });
339
+ this.#clearDroppableFieldStyles(args);
340
+
341
+ if (droppedField.type === 'Field')
342
+ this.form.autoNameCandidates.add(field.id);
343
+ }
344
+
345
+ #dropFieldSet(args)
346
+ {
347
+ if (!args.settings._allowDrop)
348
+ {
349
+ this.#clearDroppableFieldStyles(args);
350
+ return;
351
+ }
352
+
353
+ let droppedFieldSet = args.settings._dragItem,
354
+ closestFieldEl = args.settings.closestFieldEl,
355
+ fieldSet;
356
+
357
+ if (droppedFieldSet.type === 'BuildBlock') // copy
358
+ {
359
+ fieldSet = this.form.createItemFromBuildBlock(droppedFieldSet);
360
+ }
361
+ else // move
362
+ {
363
+ fieldSet = droppedFieldSet;
364
+ this.form.removeFieldSet(fieldSet); // remove before we re-insert
365
+ }
366
+
367
+ this.#insertFieldSet(fieldSet, args.dropZone, closestFieldEl);
368
+ fieldSet.selected = true;
369
+ this.form.renderFieldSet(fieldSet);
370
+ this.form.rebindFieldSetLabels(fieldSet.parent);
371
+ this.form.events.onDropField.fire(this, { item: fieldSet });
372
+ this.#clearDroppableFieldStyles(args);
373
+ }
374
+
375
+ #allowDrop(dragItem, dropZone)
376
+ {
377
+ const isField = dragItem.type === 'Field',
378
+ isSet = dragItem.type === 'FieldSet',
379
+ isBlock = dragItem.type === 'BuildBlock',
380
+ targetFS = (' FIELDSET SECTION '.indexOf(` ${dropZone.nodeName} `) > -1)
381
+ ? this.form.findFieldSetByElement(this.form.fieldSets, dropZone)
382
+ : null,
383
+ dropZoneIsRoot = !targetFS || targetFS.type === 'SECTION';
384
+
385
+ if (isField)
386
+ {
387
+ if (dropZoneIsRoot ||
388
+ (targetFS.fieldSets && targetFS.fieldSets.length > 0) ||
389
+ (targetFS.fields.length === 1 && targetFS.fields[0].id === dragItem.id))
390
+ return false
391
+ else
392
+ return true;
393
+ }
394
+
395
+ if (isSet)
396
+ {
397
+ const isRoot = !dragItem.parent || dragItem.parent.type === 'SECTION',
398
+ parentFieldSets = (dragItem.parent) ? dragItem.parent.fieldSets : this.form.fieldSets,
399
+ isOnlyRoot = isRoot && parentFieldSets.length === 1;
400
+
401
+ if (isOnlyRoot ||
402
+ dragItem.element.contains(dropZone) ||
403
+ (dropZoneIsRoot && !dragItem.allowAsRoot) ||
404
+ (targetFS && targetFS.fieldSets?.length === 1 && targetFS.fieldSets[0].id === dragItem.id))
405
+ return false;
406
+
407
+ return true;
408
+ }
409
+
410
+ if (isBlock)
411
+ {
412
+ if ((dropZoneIsRoot && dragItem.blockType !== this.form.constructor.BlockTypeOption.FIELD_SET) ||
413
+ (dropZoneIsRoot && dragItem.fieldSet && !dragItem.fieldSet.allowAsRoot))
414
+ return false;
415
+
416
+ if (dragItem.blockType === this.form.constructor.BlockTypeOption.FIELD &&
417
+ targetFS && targetFS.fieldSets && targetFS.fieldSets.length > 0)
418
+ {
419
+ return false;
420
+ }
421
+ }
422
+
423
+ return true;
424
+ }
425
+
426
+ /**
427
+ * Calculates drop-related metrics for an element.
428
+ * @param {HTMLElement} targetEl - The element being evaluated as a drop target.
429
+ * @param {HTMLElement} dragGhost - The visual representation of the dragged element.
430
+ * @returns {Object} - An object containing positioning and distance metrics.
431
+ * @private
432
+ */
433
+ #calculateDropMetrics(targetEl, dragGhost)
434
+ {
435
+ const rect = targetEl.getBoundingClientRect(),
436
+ margin = $lib.margin(targetEl),
437
+ top = rect.top + margin.top,
438
+ left = rect.left + margin.left,
439
+ bottom = rect.bottom,
440
+ right = rect.right,
441
+ fieldWidth = (rect.right - rect.left) / 2,
442
+ fieldHeight = (rect.bottom - rect.top) / 2,
443
+ fieldCenterX = rect.left + fieldWidth,
444
+ fieldCenterY = rect.top + fieldHeight,
445
+ dragGhostRect = dragGhost.getBoundingClientRect(),
446
+ dragCenterX = dragGhostRect.left + (dragGhostRect.width / 2),
447
+ dragCenterY = dragGhostRect.top + (dragGhostRect.height / 2),
448
+ isInsideX = dragCenterX > left && dragCenterX < right,
449
+ isInsideY = dragCenterY > top && dragCenterY < bottom,
450
+ isAbove = dragCenterY < fieldCenterY,
451
+ isBelow = dragCenterY > fieldCenterY,
452
+ isLeft = isInsideY && dragCenterX < fieldCenterX,
453
+ isRight = isInsideY && dragCenterX > fieldCenterX,
454
+ nearestX = Math.max(left, Math.min(dragCenterX, right)), // Clamp ghost's X to be within element's horizontal bounds.
455
+ nearestY = Math.max(top, Math.min(dragCenterY, bottom)), // Clamp ghost's Y to be within element's vertical bounds.
456
+ // Calculate the Euclidean distance (Pythagoras' theorem, diagonal line) from the ghost center to the nearest point on the element's rectangle. Will be 0 if the ghost is inside the element's bounds.
457
+ // Equivalent to Math.sqrt((nearestX - dragCenterX) ** 2 + (nearestY - dragCenterY) ** 2).
458
+ distance = Math.hypot(nearestX - dragCenterX, nearestY - dragCenterY),
459
+ centerProximityX = Math.abs(dragCenterX - fieldCenterX) / fieldWidth,
460
+ centerProximityY = Math.abs(dragCenterY - fieldCenterY) / fieldHeight,
461
+ isInsideCenter = centerProximityX < 0.3 && centerProximityY < 0.3;
462
+
463
+ return { top, left, bottom, right, fieldCenterX, fieldCenterY, fieldWidth, fieldHeight, dragCenterX, dragCenterY, isInsideX, isInsideY, isAbove, isBelow, isLeft, isRight, isInsideCenter, distance };
464
+ }
465
+
466
+ #showDropIndicator(element, metrics, isFieldSet)
467
+ {
468
+ const dropClasses =
469
+ [
470
+ this.#getDropLeft(),
471
+ this.#getDropRight(),
472
+ this.#getDropBelow(),
473
+ this.#getDropAbove(),
474
+ this.#getDropInside()
475
+ ],
476
+ cl = element.classList;
477
+
478
+ cl.add(this.#getCssClass(this.#classOption.DROPPABLE));
479
+
480
+ let activeClass = "";
481
+
482
+ if (metrics.isInsideCenter && isFieldSet)
483
+ activeClass = this.#getDropInside();
484
+ else if (metrics.isLeft)
485
+ activeClass = this.#getDropLeft();
486
+ else if (metrics.isRight)
487
+ activeClass = this.#getDropRight();
488
+ else if (metrics.isBelow)
489
+ activeClass = this.#getDropBelow();
490
+ else
491
+ activeClass = this.#getDropAbove();
492
+
493
+ dropClasses.forEach((cls) =>
494
+ {
495
+ if (cls !== activeClass)
496
+ cl.remove(cls);
497
+ });
498
+
499
+ cl.add(activeClass);
500
+ }
501
+
502
+ #clearDroppableOptionStyles(args)
503
+ {
504
+ if (!args.settings.closestOptionEl)
505
+ return;
506
+
507
+ const closestOptionEl = args.settings.closestOptionEl,
508
+ cl = closestOptionEl.classList;
509
+
510
+ cl.remove(this.#getCssClass(this.#classOption.DROPPABLE));
511
+ cl.remove(this.#getDropBelow());
512
+ cl.remove(this.#getDropAbove());
513
+ args.settings.closestOptionEl = null;
514
+ }
515
+
516
+ #clearDroppableFieldStyles(args)
517
+ {
518
+ const closestFieldEl = args.settings.closestFieldEl;
519
+
520
+ args.dropZone.classList.remove(this.#getDropInside());
521
+ args.dropZone.classList.remove(this.#getCssClass(this.#classOption.DROP_DENIED));
522
+ args.settings.dragGhost.classList.remove(this.#getCssClass(this.#classOption.DROP_DENIED));
523
+
524
+ if (!closestFieldEl)
525
+ return;
526
+
527
+ const cl = closestFieldEl.classList;
528
+ cl.remove(this.#getCssClass(this.#classOption.DROPPABLE));
529
+ cl.remove(this.#getDropBelow());
530
+ cl.remove(this.#getDropAbove());
531
+ cl.remove(this.#getDropRight());
532
+ cl.remove(this.#getDropLeft());
533
+ cl.remove(this.#getDropInside());
534
+ args.settings.closestFieldEl = null;
535
+ }
536
+
537
+ #insertOption(option, field, closestOptionEl)
538
+ {
539
+ const options = field.options,
540
+ cl = closestOptionEl.classList,
541
+ targetId = closestOptionEl._option.id,
542
+ currentIndex = options.findIndex(opt => String(opt.id) === String(option.id));
543
+
544
+ if (currentIndex > -1)
545
+ options.splice(currentIndex, 1);
546
+
547
+ const targetIndex = options.findIndex(opt => String(opt.id) === targetId),
548
+ insertIndex = cl.contains(this.#getDropAbove()) ? targetIndex : targetIndex + 1;
549
+
550
+ options.splice(insertIndex, 0, option);
551
+ }
552
+
553
+ #insertFieldInFieldSet(field, fieldSet, closestField)
554
+ {
555
+ field.lineBreak;
556
+ const cl = closestField?.element.classList;
557
+
558
+ let lineBreak = false,
559
+ insertIndex = fieldSet.fields.length;
560
+
561
+ if (closestField)
562
+ {
563
+ const closestFieldIndex = fieldSet.fields.findIndex(f => f.id === closestField.id);
564
+
565
+ if (cl.contains(this.#getDropAbove()))
566
+ {
567
+ let firstInRow = closestFieldIndex;
568
+ while (firstInRow > 0 && !fieldSet.fields[firstInRow].lineBreak)
569
+ {
570
+ firstInRow--;
571
+ }
572
+
573
+ insertIndex = firstInRow;
574
+ lineBreak = true;
575
+ this.form.addLineBreak(fieldSet.fields[firstInRow]);
576
+ }
577
+ else if (cl.contains(this.#getDropBelow()))
578
+ {
579
+ let lastInRow = closestFieldIndex + 1;
580
+ while (lastInRow < fieldSet.fields.length && !fieldSet.fields[lastInRow].lineBreak)
581
+ {
582
+ lastInRow++;
583
+ }
584
+
585
+ insertIndex = lastInRow;
586
+ lineBreak = true;
587
+ }
588
+ else if (cl.contains(this.#getDropLeft()))
589
+ {
590
+ insertIndex = closestFieldIndex;
591
+ if (closestField.lineBreak)
592
+ {
593
+ this.form.removeLineBreak(closestField);
594
+ lineBreak = true;
595
+ }
596
+ }
597
+ else if (cl.contains(this.#getDropRight()))
598
+ {
599
+ insertIndex = closestFieldIndex + 1;
600
+ }
601
+ }
602
+
603
+ insertIndex = Math.min(insertIndex, fieldSet.fields.length);
604
+ fieldSet.fields.splice(insertIndex, 0, field);
605
+
606
+ this.form.getField(field.id, fieldSet).lineBreak = lineBreak; // set property on the proxy
607
+ this.form.setParentReferences(fieldSet.fields || this.form.fieldSets, fieldSet);
608
+ }
609
+
610
+ #insertFieldSet(fieldSet, dropZoneEl, closestFieldEl)
611
+ {
612
+ let container, wrapperFieldSet,
613
+ containerFieldSet,
614
+ cl = closestFieldEl?.classList,
615
+ lineBreak = false;
616
+
617
+ const isDropAroundFieldSet = cl && (
618
+ cl.contains(this.#getDropAbove()) ||
619
+ cl.contains(this.#getDropBelow()) ||
620
+ cl.contains(this.#getDropLeft()) ||
621
+ cl.contains(this.#getDropRight())
622
+ );
623
+
624
+ if (dropZoneEl.classList.contains(this.#getCssClass(this.#classOption.FORM_PANE)))
625
+ {
626
+ container = this.form.fieldSets;
627
+ }
628
+ else if (dropZoneEl.tagName === 'SECTION')
629
+ {
630
+ // dropZoneEl is a section, find the matching fieldSet in form.fieldSets
631
+ containerFieldSet = this.form.fieldSets.find(fs => fs.element === dropZoneEl);
632
+ container = containerFieldSet ? (containerFieldSet.fieldSets || (containerFieldSet.fieldSets = [])) : this.form.fieldSets;
633
+ }
634
+ else if (closestFieldEl)
635
+ {
636
+ // dropping around a fieldset, use its parent as container
637
+ const targetEl = isDropAroundFieldSet
638
+ ? closestFieldEl.parentElement.closest('fieldset') || null
639
+ : dropZoneEl;
640
+
641
+ if (targetEl)
642
+ {
643
+ containerFieldSet = this.form.findFieldSetByElement(this.form.fieldSets, targetEl);
644
+ container = containerFieldSet.fieldSets || (containerFieldSet.fieldSets = []);
645
+ }
646
+ else
647
+ container = this.form.fieldSets;
648
+ }
649
+
650
+ let insertIndex = container.length,
651
+ closestFieldSet = closestFieldEl ? container.find(fs => fs.element === closestFieldEl) : null;
652
+
653
+ if (closestFieldSet)
654
+ {
655
+ const closestIndex = container.findIndex(fs => fs.id === closestFieldSet.id);
656
+
657
+ if (cl.contains(this.#getDropAbove()))
658
+ {
659
+ let firstInRow = closestIndex;
660
+ while (firstInRow > 0 && !container[firstInRow].lineBreak)
661
+ {
662
+ firstInRow--;
663
+ }
664
+ insertIndex = firstInRow;
665
+ lineBreak = true;
666
+ this.form.addLineBreak(container[firstInRow]);
667
+ }
668
+ else if (cl.contains(this.#getDropBelow()))
669
+ {
670
+ let lastInRow = closestIndex + 1;
671
+ while (lastInRow < container.length && !container[lastInRow].lineBreak)
672
+ {
673
+ lastInRow++;
674
+ }
675
+ insertIndex = lastInRow;
676
+ lineBreak = true;
677
+ }
678
+ else if (cl.contains(this.#getDropLeft()))
679
+ {
680
+ insertIndex = closestIndex;
681
+ if (closestFieldSet.lineBreak)
682
+ {
683
+ this.form.removeLineBreak(closestFieldSet);
684
+ lineBreak = true;
685
+ }
686
+ }
687
+ else if (cl.contains(this.#getDropRight()))
688
+ {
689
+ insertIndex = closestIndex + 1;
690
+ }
691
+ }
692
+ else if (dropZoneEl.classList.contains(this.#getDropInside()))
693
+ {
694
+ // When dropping inside another FieldSet, ensure that the target FieldSet does not have fields. If it does, wrap those fields into a new FieldSet.
695
+ containerFieldSet = this.form.findFieldSetByElement(this.form.fieldSets, dropZoneEl);
696
+ if (containerFieldSet)
697
+ {
698
+ if (containerFieldSet.fields && containerFieldSet.fields.length > 0)
699
+ {
700
+ wrapperFieldSet = this.form.ensureItemId(new this.form.constructor.FieldSet({ layout: this.form.constructor.FieldSetLayoutOption.NONE }));
701
+ wrapperFieldSet.fields = containerFieldSet.fields;
702
+ containerFieldSet.fields = [];
703
+ containerFieldSet.fieldSets = containerFieldSet.fieldSets || [];
704
+ containerFieldSet.fieldSets.push(wrapperFieldSet);
705
+ }
706
+ container = containerFieldSet.fieldSets || (containerFieldSet.fieldSets = []);
707
+ insertIndex = container.length;
708
+ }
709
+ }
710
+
711
+ container.splice(insertIndex, 0, fieldSet);
712
+ this.form.getFieldSet(fieldSet.id, container).lineBreak = lineBreak; // set property on the proxy
713
+
714
+ this.form.setParentReferences(this.form.fieldSets);
715
+
716
+ if (wrapperFieldSet)
717
+ {
718
+ wrapperFieldSet.fields.forEach(f => this.form.destroyField(f));
719
+ this.form.renderFieldSet(wrapperFieldSet);
720
+ }
721
+ }
722
+
723
+ #getDropAbove()
724
+ {
725
+ return this.#getCssClass(this.#classOption.DROP_ABOVE);
726
+ }
727
+
728
+ #getDropBelow()
729
+ {
730
+ return this.#getCssClass(this.#classOption.DROP_BELOW);
731
+ }
732
+
733
+ #getDropLeft()
734
+ {
735
+ return this.#getCssClass(this.#classOption.DROP_LEFT);
736
+ }
737
+
738
+ #getDropRight()
739
+ {
740
+ return this.#getCssClass(this.#classOption.DROP_RIGHT);
741
+ }
742
+
743
+ #getDropInside()
744
+ {
745
+ return this.#getCssClass(this.#classOption.DROP_INSIDE);
746
+ }
747
+
748
+ #getCssClass(cssClassValue)
749
+ {
750
+ return this.form.getCssClass(cssClassValue);
751
+ }
752
+ };
753
+
754
+ var Draggable = componyx.UI.form_modules.Draggable;
755
+
756
+ export { Draggable as default };