@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,757 @@
1
+ /*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
2
+ componyx.UI.editor_modules = componyx.UI.editor_modules || {};
3
+
4
+ /**
5
+ * TableDialog module — manages the Table Settings dialog.
6
+ * @class TableDialog
7
+ * @memberof componyx.UI.editor_modules
8
+ * @ignore
9
+ */
10
+ componyx.UI.editor_modules.TableDialog = class TableDialog
11
+ {
12
+ #dialog = null;
13
+ #editor;
14
+ #cf;
15
+ #context = null;
16
+
17
+ // Category elements
18
+ #categoriesEl = null;
19
+ #settingsViewEl = null;
20
+
21
+ // Change tracking flags (reset on each dialog open)
22
+ #tableChanged = { borderWidth: false, borderStyle: false, borderColor: false };
23
+ #rowChanged = { borderStyle: false, borderColor: false };
24
+ #cellChanged = { borderWidth: false, borderStyle: false, borderColor: false };
25
+
26
+ // Table fields
27
+ #tableWidth = null;
28
+ #tableAlignment = null;
29
+ #tableBorderWidth = null;
30
+ #tableBorderStyle = null;
31
+ #tableBorderColorBtn = null;
32
+ #tableBgColorBtn = null;
33
+ #tableCellSpacing = null;
34
+ #tableCellPadding = null;
35
+ #tableCaption = null;
36
+
37
+ // Row fields
38
+ #rowIsHeader = null;
39
+ #rowIsFooter = null;
40
+ #rowHeight = null;
41
+ #rowBgColorBtn = null;
42
+ #rowBorderStyle = null;
43
+ #rowBorderColorBtn = null;
44
+
45
+ // Cell fields
46
+ #cellWidth = null;
47
+ #cellHeight = null;
48
+ #cellVerticalAlign = null;
49
+ #cellHorizontalAlign = null;
50
+ #cellBgColorBtn = null;
51
+ #cellBorderWidth = null;
52
+ #cellBorderStyle = null;
53
+ #cellBorderColorBtn = null;
54
+
55
+ #selectedCssClass;
56
+ #panelCssClass;
57
+ #fieldHalfCssClass;
58
+
59
+ constructor(editor)
60
+ {
61
+ this.#editor = editor;
62
+ this.#cf = editor.componentFactory;
63
+ this.utility = editor.utility;
64
+ this.#selectedCssClass = editor.getCssClass(this.#editor.classOption.TABLE_SELECTED);
65
+ this.#panelCssClass = editor.getCssClass(this.#editor.classOption.TABLE_DIALOG_PANEL);
66
+ this.#fieldHalfCssClass = editor.getCssClass(editor.classOption.FIELD_HALF);
67
+ }
68
+
69
+ get dialog() { return this.#dialog; }
70
+
71
+ show(context)
72
+ {
73
+ this.#context = context;
74
+ this.#dialog?.show();
75
+ }
76
+
77
+ create()
78
+ {
79
+ const editor = this.#editor,
80
+ cf = this.#cf,
81
+ id = 'TableDialog',
82
+ header = $lib.element('', '', '', editor.labels.tableDialogHeader || 'Table Settings'),
83
+ content = $lib.element();
84
+
85
+ this.#categoriesEl = $lib.element('', '', '', '', { class: editor.getCssClass(editor.classOption.TABLE_DIALOG_CATEGORIES) });
86
+ this.#settingsViewEl = $lib.element('', '', '', '', { class: editor.getCssClass(editor.classOption.TABLE_DIALOG_VIEW) });
87
+
88
+ // Build category navigation — Table / Row / Cell
89
+ const categories = [
90
+ { label: editor.labels.tableDialogCategoryTable, fn: () => this.#showCategory('table') },
91
+ { label: editor.labels.tableDialogCategoryRow, fn: () => this.#showCategory('row') },
92
+ { label: editor.labels.tableDialogCategoryCell, fn: () => this.#showCategory('cell') },
93
+ ];
94
+
95
+ $lib.each(categories, cat =>
96
+ {
97
+ $lib.element(this.#categoriesEl, '', 'a', cat.label, { class: editor.getCssClass(editor.classOption.TABLE_DIALOG_CATEGORY) }, { onclick: cat.fn });
98
+ });
99
+
100
+ // Build all three setting panels (hidden by default)
101
+ this.#settingsViewEl.append(
102
+ this.#buildTablePanel(),
103
+ this.#buildRowPanel(),
104
+ this.#buildCellPanel()
105
+ );
106
+
107
+ content.append(this.#categoriesEl, this.#settingsViewEl);
108
+
109
+ this.#dialog = cf.createDialog(id, editor.tableDialogId, {
110
+ cssClass: editor.getCssClass(editor.classOption.TABLE_DIALOG),
111
+ header,
112
+ content,
113
+ onShowComplete: () => this.#onShowComplete(),
114
+ onConfirm: () => this.#onConfirm(),
115
+ onHide: () =>
116
+ {
117
+ const cell = this.#context.cell;
118
+
119
+ cell.classList.remove(this.#selectedCssClass);
120
+
121
+ if (!cell.className)
122
+ cell.removeAttribute('class');
123
+
124
+ this.#context = null;
125
+ }
126
+ });
127
+ }
128
+
129
+ // ── Category navigation ───────────────────────────────────────────────────
130
+
131
+ #showCategory(name)
132
+ {
133
+ this.#settingsViewEl.querySelectorAll(`.${this.#panelCssClass}`).forEach(panel =>
134
+ {
135
+ panel.style.display = panel.dataset.category === name ? '' : 'none';
136
+ });
137
+
138
+ const activeCssClass = this.#editor.getCssClass(this.#editor.classOption.ACTIVE);
139
+
140
+ $lib.each(this.#categoriesEl.children, (link, index) =>
141
+ {
142
+ const cats = ['table', 'row', 'cell'];
143
+ link.classList.toggle(activeCssClass, cats[index] === name);
144
+ });
145
+ }
146
+
147
+ // ── Panel builders ────────────────────────────────────────────────────────
148
+
149
+ #buildTablePanel()
150
+ {
151
+ const editor = this.#editor,
152
+ cf = this.#cf,
153
+ panel = this.#createPanel('table'),
154
+ labels = editor.labels,
155
+ td = editor.tableDialog,
156
+ temp = $lib.element();
157
+
158
+ const fields = {};
159
+
160
+ // Width — full width on its own row
161
+ this.#tableWidth = $lib.element('', '', 'input', '', { type: 'text', id: this.#getId('tableWidth') });
162
+ fields.width = cf.createFormField('fldTableWidth', td.tableWidthFormFieldId, temp, { label: labels.tableWidth, field: this.#tableWidth }).element;
163
+
164
+ // Cell spacing / Cell padding — paired
165
+ this.#tableCellSpacing = $lib.element('', '', 'input', '', { type: 'text', id: this.#getId('tableCellSpacing') });
166
+ fields.cellSpacing = cf.createFormField('fldTableCellSpacing', td.tableCellSpacingFormFieldId, temp, { label: labels.tableCellSpacing, field: this.#tableCellSpacing, cssClass: this.#fieldHalfCssClass }).element;
167
+
168
+ this.#tableCellPadding = $lib.element('', '', 'input', '', { type: 'text', id: this.#getId('tableCellPadding') });
169
+ fields.cellPadding = cf.createFormField('fldTableCellPadding', td.tableCellPaddingFormFieldId, temp, { label: labels.tableCellPadding, field: this.#tableCellPadding, cssClass: this.#fieldHalfCssClass }).element;
170
+
171
+ // Alignment
172
+ this.#tableAlignment = this.#createComboBox('tableAlignment', td.horizontalAlignComboBoxId, [
173
+ { value: 'left', text: labels.alignLeft },
174
+ { value: 'center', text: labels.alignCenter },
175
+ { value: 'right', text: labels.alignRight },
176
+ ]);
177
+ fields.alignment = cf.createFormField('fldTableAlignment', td.tableAlignFormFieldId, temp, { label: labels.tableAlignment, field: this.#tableAlignment.element }).element;
178
+
179
+ // Border width / Border style — paired
180
+ this.#tableBorderWidth = $lib.element({ tag: 'input', props: { type: 'text', id: this.#getId('tableBorderWidth'), oninput: () => { this.#tableChanged.borderWidth = true; } } });
181
+ fields.borderWidth = cf.createFormField('fldTableBorderWidth', td.tableBorderWidthFormFieldId, temp, { label: labels.borderWidth, field: this.#tableBorderWidth, cssClass: this.#fieldHalfCssClass }).element;
182
+
183
+ this.#tableBorderStyle = this.#createComboBox('tableBorderStyle', td.borderStyleComboBoxId, [
184
+ { value: 'none', text: labels.borderStyleNone },
185
+ { value: 'solid', text: labels.borderStyleSolid },
186
+ { value: 'dashed', text: labels.borderStyleDashed },
187
+ { value: 'dotted', text: labels.borderStyleDotted },
188
+ { value: 'double', text: labels.borderStyleDouble },
189
+ ], () => { this.#tableChanged.borderStyle = true; });
190
+ fields.borderStyle = cf.createFormField('fldTableBorderStyle', td.tableBorderStyleFormFieldId, temp, { label: labels.borderStyle, field: this.#tableBorderStyle.element, cssClass: this.#fieldHalfCssClass }).element;
191
+
192
+ // Border color / Background color
193
+ this.#tableBorderColorBtn = this.#createColorButton('tableBorderColor', td.tableBorderColorFormFieldId, td.borderColorButtonId, temp, labels.borderColor, true, () => { this.#tableChanged.borderColor = true; });
194
+ fields.borderColor = temp.lastElementChild;
195
+
196
+ this.#tableBgColorBtn = this.#createColorButton('tableBgColor', td.tableBgColorFormFieldId, td.bgColorButtonId, temp, labels.bgColor, true);
197
+ fields.bgColor = temp.lastElementChild;
198
+
199
+ // Caption
200
+ this.#tableCaption = $lib.element('', '', 'input', '', { type: 'text', id: this.#getId('tableCaption') });
201
+ fields.caption = cf.createFormField('fldTableCaption', td.tableCaptionFormFieldId, temp, { label: labels.caption, field: this.#tableCaption }).element;
202
+
203
+ const defaultTemplate = '{width}{cellSpacing}{cellPadding}{alignment}{borderWidth}{borderStyle}{borderColor}{bgColor}{caption}',
204
+ templateId = 'TableDialogTable',
205
+ defaultTemplateId = 'Default' + templateId;
206
+
207
+ if (!editor.hasTemplate(templateId))
208
+ {
209
+ editor.addTemplate(defaultTemplateId, defaultTemplate, false);
210
+ editor.applyTemplate(panel, defaultTemplateId, fields);
211
+ }
212
+ else
213
+ editor.applyTemplate(panel, templateId, fields);
214
+
215
+ return panel;
216
+ }
217
+
218
+ #buildRowPanel()
219
+ {
220
+ const editor = this.#editor,
221
+ cf = this.#cf,
222
+ panel = this.#createPanel('row'),
223
+ labels = editor.labels,
224
+ td = editor.tableDialog,
225
+ temp = $lib.element();
226
+
227
+ const fields = {};
228
+
229
+ // Header / Footer toggles — paired
230
+ this.#rowIsHeader = $lib.element('', '', 'input', '', { type: 'checkbox', id: this.#getId('rowIsHeader') });
231
+ fields.isHeader = cf.createFormField('fldRowIsHeader', td.rowIsHeaderFormFieldId, temp, { label: labels.headerRow, field: this.#rowIsHeader, switch: true, labelDisplay: 5 }).element;
232
+
233
+ this.#rowIsFooter = $lib.element('', '', 'input', '', { type: 'checkbox', id: this.#getId('rowIsFooter') });
234
+ fields.isFooter = cf.createFormField('fldRowIsFooter', td.rowIsFooterFormFieldId, temp, { label: labels.footerRow, field: this.#rowIsFooter, switch: true, labelDisplay: 5 }).element;
235
+
236
+ // Height — full width on its own
237
+ this.#rowHeight = $lib.element('', '', 'input', '', { type: 'text', id: this.#getId('rowHeight') });
238
+ fields.height = cf.createFormField('fldRowHeight', td.rowHeightFormFieldId, temp, { label: labels.rowHeight, field: this.#rowHeight }).element;
239
+
240
+ // Border style — paired with height
241
+ this.#rowBorderStyle = this.#createComboBox('rowBorderStyle', td.borderStyleComboBoxId, [
242
+ { value: 'none', text: labels.borderStyleNone },
243
+ { value: 'solid', text: labels.borderStyleSolid },
244
+ { value: 'dashed', text: labels.borderStyleDashed },
245
+ { value: 'dotted', text: labels.borderStyleDotted },
246
+ { value: 'double', text: labels.borderStyleDouble },
247
+ ], () => { this.#rowChanged.borderStyle = true; });
248
+ fields.borderStyle = cf.createFormField('fldRowBorderStyle', td.rowBorderStyleFormFieldId, temp, { label: labels.borderStyle, field: this.#rowBorderStyle.element }).element;
249
+
250
+ // Border color / Background color — full width each
251
+ this.#rowBorderColorBtn = this.#createColorButton('rowBorderColor', td.rowBorderColorFormFieldId, td.borderColorButtonId, temp, labels.borderColor, true, () => { this.#rowChanged.borderColor = true; });
252
+ fields.borderColor = temp.lastElementChild;
253
+
254
+ this.#rowBgColorBtn = this.#createColorButton('rowBgColor', td.rowBgColorFormFieldId, td.bgColorButtonId, temp, labels.bgColor, true);
255
+ fields.bgColor = temp.lastElementChild;
256
+
257
+ const defaultTemplate = '{isHeader}{isFooter}{height}{borderStyle}{borderColor}{bgColor}',
258
+ templateId = 'TableDialogRow',
259
+ defaultTemplateId = 'Default' + templateId;
260
+
261
+ if (!editor.hasTemplate(templateId))
262
+ {
263
+ editor.addTemplate(defaultTemplateId, defaultTemplate, false);
264
+ editor.applyTemplate(panel, defaultTemplateId, fields);
265
+ }
266
+ else
267
+ editor.applyTemplate(panel, templateId, fields);
268
+
269
+ return panel;
270
+ }
271
+
272
+ #buildCellPanel()
273
+ {
274
+ const editor = this.#editor,
275
+ cf = this.#cf,
276
+ panel = this.#createPanel('cell'),
277
+ labels = editor.labels,
278
+ td = editor.tableDialog,
279
+ temp = $lib.element();
280
+
281
+ const fields = {};
282
+
283
+ // Width / Height — paired
284
+ this.#cellWidth = $lib.element('', '', 'input', '', { type: 'text', id: this.#getId('cellWidth') });
285
+ fields.width = cf.createFormField('fldCellWidth', td.cellWidthFormFieldId, temp, { label: labels.cellWidth, field: this.#cellWidth, cssClass: this.#fieldHalfCssClass }).element;
286
+
287
+ this.#cellHeight = $lib.element('', '', 'input', '', { type: 'text', id: this.#getId('cellHeight') });
288
+ fields.height = cf.createFormField('fldCellHeight', td.cellHeightFormFieldId, temp, { label: labels.cellHeight, field: this.#cellHeight, cssClass: this.#fieldHalfCssClass }).element;
289
+
290
+ // Vertical / Horizontal align — paired
291
+ this.#cellVerticalAlign = this.#createComboBox('cellVerticalAlign', td.verticalAlignComboBoxId, [
292
+ { value: 'top', text: labels.alignTop },
293
+ { value: 'middle', text: labels.alignMiddle },
294
+ { value: 'bottom', text: labels.alignBottom },
295
+ ]);
296
+ fields.verticalAlign = cf.createFormField('fldCellVerticalAlign', td.cellVerticalAlignFormFieldId, temp, { label: labels.verticalAlign, field: this.#cellVerticalAlign.element, cssClass: this.#fieldHalfCssClass }).element;
297
+
298
+ this.#cellHorizontalAlign = this.#createComboBox('cellHorizontalAlign', td.horizontalAlignComboBoxId, [
299
+ { value: 'left', text: labels.alignLeft },
300
+ { value: 'center', text: labels.alignCenter },
301
+ { value: 'right', text: labels.alignRight },
302
+ ]);
303
+ fields.horizontalAlign = cf.createFormField('fldCellHorizontalAlign', td.cellHorizontalAlignFormFieldId, temp, { label: labels.horizontalAlign, field: this.#cellHorizontalAlign.element, cssClass: this.#fieldHalfCssClass }).element;
304
+
305
+ // Border width / Border style — paired
306
+ this.#cellBorderWidth = $lib.element({ tag: 'input', props: { type: 'text', id: this.#getId('cellBorderWidth'), oninput: () => { this.#cellChanged.borderWidth = true; } } });
307
+ fields.borderWidth = cf.createFormField('fldCellBorderWidth', td.cellBorderWidthFormFieldId, temp, { label: labels.borderWidth, field: this.#cellBorderWidth, cssClass: this.#fieldHalfCssClass }).element;
308
+
309
+ this.#cellBorderStyle = this.#createComboBox('cellBorderStyle', td.borderStyleComboBoxId, [
310
+ { value: 'none', text: labels.borderStyleNone },
311
+ { value: 'solid', text: labels.borderStyleSolid },
312
+ { value: 'dashed', text: labels.borderStyleDashed },
313
+ { value: 'dotted', text: labels.borderStyleDotted },
314
+ { value: 'double', text: labels.borderStyleDouble },
315
+ ], () => { this.#cellChanged.borderStyle = true; });
316
+ fields.borderStyle = cf.createFormField('fldCellBorderStyle', td.cellBorderStyleFormFieldId, temp, { label: labels.borderStyle, field: this.#cellBorderStyle.element, cssClass: this.#fieldHalfCssClass }).element;
317
+
318
+ // Border color / Background color — full width each
319
+ this.#cellBorderColorBtn = this.#createColorButton('cellBorderColor', td.cellBorderColorFormFieldId, td.borderColorButtonId, temp, labels.borderColor, true, () => { this.#cellChanged.borderColor = true; });
320
+ fields.borderColor = temp.lastElementChild;
321
+
322
+ this.#cellBgColorBtn = this.#createColorButton('cellBgColor', td.cellBgColorFormFieldId, td.bgColorButtonId, temp, labels.bgColor, true);
323
+ fields.bgColor = temp.lastElementChild;
324
+
325
+ const defaultTemplate = '{width}{height}{verticalAlign}{horizontalAlign}{borderWidth}{borderStyle}{padding}{borderColor}{bgColor}',
326
+ templateId = 'TableDialogCell',
327
+ defaultTemplateId = 'Default' + templateId;
328
+
329
+ if (!editor.hasTemplate(templateId))
330
+ {
331
+ editor.addTemplate(defaultTemplateId, defaultTemplate, false);
332
+ editor.applyTemplate(panel, defaultTemplateId, fields);
333
+ }
334
+ else
335
+ editor.applyTemplate(panel, templateId, fields);
336
+
337
+ return panel;
338
+ }
339
+
340
+ // ── Show / Confirm ────────────────────────────────────────────────────────
341
+
342
+ #onShowComplete()
343
+ {
344
+ const { cell, table } = this.#context || {},
345
+ row = cell.closest('tr');
346
+
347
+ cell.classList.add(this.#selectedCssClass);
348
+
349
+ // make sure all fields in all panels can be detected by box
350
+ this.#settingsViewEl.querySelectorAll(`.${this.#panelCssClass}`).forEach(panel =>
351
+ {
352
+ panel.style.display = '';
353
+ });
354
+
355
+ this.#dialog.getBox().update(); // creates focusable field list, first field will be focused after this event
356
+
357
+ this.#showCategory('cell');
358
+
359
+ if (table)
360
+ this.#loadTableValues(table, cell);
361
+
362
+ if (row)
363
+ this.#loadRowValues(row);
364
+
365
+ if (cell)
366
+ this.#loadCellValues(cell);
367
+
368
+ this.#resetFlags();
369
+ }
370
+
371
+ #resetFlags()
372
+ {
373
+ this.#tableChanged = { borderWidth: false, borderStyle: false, borderColor: false };
374
+ this.#rowChanged = { borderStyle: false, borderColor: false };
375
+ this.#cellChanged = { borderWidth: false, borderStyle: false, borderColor: false, padding: false };
376
+ }
377
+
378
+ #onConfirm()
379
+ {
380
+ let { cell, table } = this.#context || {},
381
+ row = cell?.closest('tr');
382
+
383
+ if (table)
384
+ this.#applyTableValues(table);
385
+
386
+ if (row)
387
+ this.#applyRowValues(row, table);
388
+
389
+ cell = this.#context.cell; // cell can change if switched to/from header row
390
+
391
+ if (cell)
392
+ this.#applyCellValues(cell);
393
+
394
+ this.#editor.table.updateContextCell(cell);
395
+ this.#editor.history.addItem();
396
+ }
397
+
398
+ // ── Load current values into fields ───────────────────────────────────────
399
+
400
+ #loadTableValues(table, cell)
401
+ {
402
+ this.#tableWidth.value = table.style.width || '';
403
+ this.#tableCellSpacing.value = table.style.borderSpacing || '';
404
+ this.#tableCellPadding.value = cell.style.padding || '';
405
+ this.#tableCaption.value = table.querySelector('caption')?.textContent || '';
406
+
407
+ if (this.#tableBgColorBtn)
408
+ this.#tableBgColorBtn.setRGBA(...this.#resolveColor(table.style.backgroundColor));
409
+
410
+ this.#tableBorderWidth.value = table.style.borderWidth || '';
411
+ this.#tableBorderStyle.setValue(table.style.borderStyle || '');
412
+
413
+ if (this.#tableBorderColorBtn)
414
+ this.#tableBorderColorBtn.setRGBA(...this.#resolveColor(table.style.borderColor));
415
+
416
+ // Alignment — infer from margin/float
417
+ if (table.style.marginLeft === 'auto' && table.style.marginRight === 'auto')
418
+ this.#tableAlignment.setValue('center');
419
+ else if (table.style.marginLeft === 'auto')
420
+ this.#tableAlignment.setValue('right');
421
+ else if (table.style.float)
422
+ this.#tableAlignment.setValue(table.style.float);
423
+ else
424
+ this.#tableAlignment.setValue('');
425
+ }
426
+
427
+ #loadRowValues(row)
428
+ {
429
+ this.#rowIsHeader.checked = row.parentElement?.tagName === 'THEAD';
430
+ this.#rowIsFooter.checked = row.parentElement?.tagName === 'TFOOT';
431
+ this.#rowHeight.value = row.style.height || '';
432
+
433
+ if (this.#rowBgColorBtn)
434
+ this.#rowBgColorBtn.setRGBA(...this.#resolveColor(row.style.backgroundColor));
435
+
436
+ this.#rowBorderStyle.setValue(row.style.borderStyle || '');
437
+
438
+ if (this.#rowBorderColorBtn)
439
+ this.#rowBorderColorBtn.setRGBA(...this.#resolveColor(row.style.borderColor));
440
+ }
441
+
442
+ #loadCellValues(cell)
443
+ {
444
+ this.#cellWidth.value = cell.style.width || '';
445
+ this.#cellHeight.value = cell.style.height || '';
446
+ this.#cellBorderWidth.value = cell.style.borderWidth || '';
447
+
448
+ this.#cellVerticalAlign.setValue(cell.style.verticalAlign || '');
449
+ this.#cellHorizontalAlign.setValue(cell.style.textAlign || '');
450
+ this.#cellBorderStyle.setValue(cell.style.borderStyle || '');
451
+
452
+ if (this.#cellBgColorBtn)
453
+ this.#cellBgColorBtn.setRGBA(...this.#resolveColor(cell.style.backgroundColor));
454
+
455
+ if (this.#cellBorderColorBtn)
456
+ this.#cellBorderColorBtn.setRGBA(...this.#resolveColor(cell.style.borderColor));
457
+ }
458
+
459
+ // ── Apply values to DOM ───────────────────────────────────────────────────
460
+
461
+ #applyTableValues(table)
462
+ {
463
+ // Width
464
+ this.#setStyle(table, 'width', this.#tableWidth.value);
465
+
466
+ // Background color
467
+ this.#setStyle(table, 'backgroundColor', this.#colorValue(this.#tableBgColorBtn));
468
+
469
+ // Cell spacing
470
+ if (this.#tableCellSpacing.value)
471
+ {
472
+ table.style.borderCollapse = 'separate';
473
+ this.#setStyle(table, 'borderSpacing', $lib.unit(this.#tableCellSpacing.value));
474
+ }
475
+ else
476
+ {
477
+ table.style.borderCollapse = 'collapse';
478
+ table.style.removeProperty('border-spacing');
479
+ }
480
+
481
+ // Border — stored on table for read-back
482
+ const borderWidth = this.#tableBorderWidth.value ? $lib.unit(this.#tableBorderWidth.value) : '',
483
+ borderStyle = this.#tableBorderStyle.getValue() || '',
484
+ borderColor = this.#colorValue(this.#tableBorderColorBtn);
485
+
486
+ this.#setStyle(table, 'borderWidth', borderWidth);
487
+ this.#setStyle(table, 'borderStyle', borderStyle);
488
+ this.#setStyle(table, 'borderColor', borderColor);
489
+
490
+ // Cell padding and border — applied to cells respecting cell-level overrides
491
+ const cellPadding = this.#tableCellPadding.value ? $lib.unit(this.#tableCellPadding.value) : '',
492
+ currentCell = this.#context?.cell;
493
+
494
+ table.querySelectorAll('td, th').forEach(cell =>
495
+ {
496
+ const isCurrentCell = cell === currentCell;
497
+
498
+ if (this.#tableChanged.borderWidth && (!isCurrentCell || !this.#cellChanged.borderWidth))
499
+ this.#setStyle(cell, 'borderWidth', borderWidth);
500
+
501
+ if (this.#tableChanged.borderStyle && (!isCurrentCell || !this.#cellChanged.borderStyle))
502
+ this.#setStyle(cell, 'borderStyle', borderStyle);
503
+
504
+ if (this.#tableChanged.borderColor && (!isCurrentCell || !this.#cellChanged.borderColor))
505
+ this.#setStyle(cell, 'borderColor', borderColor);
506
+
507
+ this.#setStyle(cell, 'padding', cellPadding);
508
+ });
509
+
510
+ // Alignment
511
+ table.style.removeProperty('float');
512
+ table.style.removeProperty('margin-left');
513
+ table.style.removeProperty('margin-right');
514
+ switch (this.#tableAlignment.getValue())
515
+ {
516
+ case 'center': table.style.marginLeft = table.style.marginRight = 'auto'; break;
517
+ case 'right': table.style.marginLeft = 'auto'; break;
518
+ case 'left': table.style.float = 'left'; break;
519
+ }
520
+
521
+ // Caption
522
+ let caption = table.querySelector('caption');
523
+ if (this.#tableCaption.value)
524
+ {
525
+ if (!caption)
526
+ {
527
+ caption = table.ownerDocument.createElement('caption');
528
+ table.insertBefore(caption, table.firstChild);
529
+ }
530
+ caption.textContent = this.#tableCaption.value;
531
+ }
532
+ else if (caption)
533
+ {
534
+ caption.remove();
535
+ }
536
+ }
537
+
538
+ #applyRowValues(row, table)
539
+ {
540
+ const doc = table.ownerDocument;
541
+ const tbody = table.querySelector('tbody') || this.#ensureSection(table, 'tbody');
542
+
543
+ // Header toggle
544
+ if (this.#rowIsHeader.checked && row.parentElement?.tagName !== 'THEAD')
545
+ {
546
+ let thead = table.querySelector('thead');
547
+ if (!thead) { thead = doc.createElement('thead'); table.insertBefore(thead, table.firstChild); }
548
+ this.#convertCells(row, 'TH');
549
+ thead.appendChild(row);
550
+ }
551
+ else if (!this.#rowIsHeader.checked && row.parentElement?.tagName === 'THEAD')
552
+ {
553
+ const thead = row.parentElement;
554
+ this.#convertCells(row, 'TD');
555
+ tbody.insertBefore(row, tbody.firstChild);
556
+ if (!thead.children.length) thead.remove();
557
+ }
558
+ // Footer toggle
559
+ if (this.#rowIsFooter.checked && row.parentElement?.tagName !== 'TFOOT')
560
+ {
561
+ let tfoot = table.querySelector('tfoot');
562
+ if (!tfoot) { tfoot = doc.createElement('tfoot'); table.appendChild(tfoot); }
563
+ this.#convertCells(row, 'TD');
564
+ tfoot.insertBefore(row, tfoot.firstChild);
565
+ }
566
+ else if (!this.#rowIsFooter.checked && row.parentElement?.tagName === 'TFOOT')
567
+ {
568
+ const tfoot = row.parentElement;
569
+ tbody.appendChild(row);
570
+ this.#convertCells(row, 'TD');
571
+ if (!tfoot.children.length) tfoot.remove();
572
+ }
573
+
574
+ this.#setStyle(row, 'height', this.#rowHeight.value ? $lib.unit(this.#rowHeight.value) : '');
575
+ this.#setStyle(row, 'backgroundColor', this.#colorValue(this.#rowBgColorBtn));
576
+
577
+ // Row border — applied to row and its cells respecting cell-level overrides
578
+ const borderStyle = this.#rowBorderStyle.getValue() || '',
579
+ borderColor = this.#colorValue(this.#rowBorderColorBtn),
580
+ currentCell = this.#context?.cell;
581
+
582
+ if (this.#rowChanged.borderStyle)
583
+ this.#setStyle(row, 'borderStyle', borderStyle);
584
+
585
+ if (this.#rowChanged.borderColor)
586
+ this.#setStyle(row, 'borderColor', borderColor);
587
+
588
+ row.querySelectorAll('td, th').forEach(cell =>
589
+ {
590
+ const isCurrentCell = cell === currentCell;
591
+
592
+ if (this.#rowChanged.borderStyle && (!isCurrentCell || !this.#cellChanged.borderStyle))
593
+ this.#setStyle(cell, 'borderStyle', borderStyle);
594
+
595
+ if (this.#rowChanged.borderColor && (!isCurrentCell || !this.#cellChanged.borderColor))
596
+ this.#setStyle(cell, 'borderColor', borderColor);
597
+ });
598
+ }
599
+
600
+ #applyCellValues(cell)
601
+ {
602
+ this.#setStyle(cell, 'width', this.#cellWidth.value ? $lib.unit(this.#cellWidth.value) : '');
603
+ this.#setStyle(cell, 'height', this.#cellHeight.value ? $lib.unit(this.#cellHeight.value) : '');
604
+ this.#setStyle(cell, 'verticalAlign', this.#cellVerticalAlign.getValue());
605
+ this.#setStyle(cell, 'textAlign', this.#cellHorizontalAlign.getValue());
606
+ this.#setStyle(cell, 'backgroundColor', this.#colorValue(this.#cellBgColorBtn));
607
+
608
+ if (this.#cellChanged.borderWidth)
609
+ this.#setStyle(cell, 'borderWidth', this.#cellBorderWidth.value ? $lib.unit(this.#cellBorderWidth.value) : '');
610
+
611
+ if (this.#cellChanged.borderStyle)
612
+ this.#setStyle(cell, 'borderStyle', this.#cellBorderStyle.getValue());
613
+
614
+ if (this.#cellChanged.borderColor)
615
+ this.#setStyle(cell, 'borderColor', this.#colorValue(this.#cellBorderColorBtn));
616
+
617
+ }
618
+
619
+ // ── Helpers ───────────────────────────────────────────────────────────────
620
+
621
+ #convertCells(row, tagName)
622
+ {
623
+ const doc = row.ownerDocument,
624
+ { cell, table } = this.#context || {};
625
+
626
+ for (const c of [...row.cells])
627
+ {
628
+ if (c.nodeName !== tagName)
629
+ {
630
+ const newCell = doc.createElement(tagName);
631
+ newCell.innerHTML = c.innerHTML;
632
+ for (const attr of c.attributes)
633
+ newCell.setAttribute(attr.name, attr.value);
634
+
635
+ c.replaceWith(newCell);
636
+
637
+ if (c === cell)
638
+ this.#context.cell = newCell;
639
+ }
640
+ }
641
+ }
642
+
643
+ #colorValue(btn)
644
+ {
645
+ const value = btn?.getValue();
646
+ if (!value)
647
+ return '';
648
+
649
+ return `rgba(${value})`;
650
+ }
651
+
652
+ #setStyle(el, prop, value)
653
+ {
654
+ if (value)
655
+ el.style[prop] = value;
656
+ else
657
+ el.style.removeProperty(prop.replace(/([A-Z])/g, '-$1').toLowerCase());
658
+ }
659
+
660
+ #createPanel(category)
661
+ {
662
+ const panel = $lib.element({ attrs: { 'data-category': category, class: this.#panelCssClass } });
663
+ panel.style.display = 'none';
664
+ return panel;
665
+ }
666
+
667
+ #createComboBox(id, cloneId, options, onChanged = null)
668
+ {
669
+ const itemList = [];
670
+
671
+ $lib.each(options, opt =>
672
+ {
673
+ itemList.push({ id: opt.value, value: opt.value, text: opt.text });
674
+ });
675
+
676
+ const combo = this.#cf.createComboBox(id, cloneId, null, { itemList });
677
+
678
+ if (onChanged)
679
+ {
680
+ combo.events.onItemSelect.priorityAdd(onChanged);
681
+ combo.events.onItemDeselect.priorityAdd(onChanged);
682
+ }
683
+
684
+ return combo;
685
+ }
686
+
687
+ #createColorButton(id, formfieldCloneId, cloneId, container, label, halfField = false, onChanged = null)
688
+ {
689
+ this.#editor;
690
+ const cf = this.#cf;
691
+
692
+ const colorButton = cf.createColorButton(id, cloneId, container,
693
+ {
694
+ command: () =>
695
+ {
696
+ const swatchBox = cf.colorSwatchBox;
697
+ if (!swatchBox)
698
+ return;
699
+
700
+ if (swatchBox.showing)
701
+ {
702
+ // Second call — swatch confirmed a color, __color was set by #selectSwatchColor
703
+ const button = colorButton.getButton();
704
+
705
+ if (!button.__color)
706
+ colorButton.setRGBA(255, 0, 0, 0);
707
+ else
708
+ colorButton.setRGBA(...this.#resolveColor(button.__color));
709
+
710
+ if (onChanged)
711
+ onChanged();
712
+ }
713
+ else
714
+ {
715
+ // First call — register as active target and open swatch
716
+ cf.setActiveExpandButton(colorButton.getButton());
717
+ swatchBox.expander = colorButton.element;
718
+ swatchBox.show();
719
+ }
720
+ }
721
+ });
722
+
723
+ cf.createFormField('fld_' + id, formfieldCloneId, container, { label, field: colorButton.element, cssClass: halfField ? this.#fieldHalfCssClass : undefined });
724
+
725
+ return colorButton;
726
+ }
727
+
728
+ #resolveColor(value)
729
+ {
730
+ if (!value)
731
+ return [255, 0, 0, 0]; // alpha 0 = no color
732
+
733
+ const temp = $lib.element({ container: document.body });
734
+ temp.style.color = value;
735
+ const computed = getComputedStyle(temp).color; // "rgb(r, g, b)" or "rgba(r, g, b, a)"
736
+ temp.remove();
737
+ return computed.match(/[\d.]+/g);
738
+ }
739
+
740
+ #ensureSection(table, tag)
741
+ {
742
+ let section = table.querySelector(tag);
743
+ if (!section)
744
+ {
745
+ section = table.ownerDocument.createElement(tag);
746
+ const tfoot = table.querySelector('tfoot');
747
+ tag === 'tbody' && tfoot ? table.insertBefore(section, tfoot) : table.appendChild(section);
748
+ }
749
+ return section;
750
+ }
751
+
752
+ #getId(id) { return this.utility.getId(id); }
753
+ };
754
+
755
+ var TableDialog = componyx.UI.editor_modules.TableDialog;
756
+
757
+ export { TableDialog as default };