juxscript 1.1.240 → 1.1.244

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 (297) hide show
  1. package/index.js +7 -137
  2. package/lib/components/dataframe.ts +0 -569
  3. package/lib/components/include.ts +229 -229
  4. package/lib/components/tag.ts +68 -0
  5. package/lib/styles/shadcn.css +17 -5
  6. package/lib/utils/idgen.ts +6 -0
  7. package/package.json +5 -6
  8. package/index.d.ts +0 -239
  9. package/index.d.ts.map +0 -1
  10. package/lib/components/alert.d.ts +0 -36
  11. package/lib/components/alert.d.ts.map +0 -1
  12. package/lib/components/alert.js +0 -172
  13. package/lib/components/alert.ts +0 -219
  14. package/lib/components/app.d.ts +0 -89
  15. package/lib/components/app.d.ts.map +0 -1
  16. package/lib/components/app.js +0 -175
  17. package/lib/components/app.ts +0 -247
  18. package/lib/components/badge.d.ts +0 -26
  19. package/lib/components/badge.d.ts.map +0 -1
  20. package/lib/components/badge.js +0 -91
  21. package/lib/components/badge.ts +0 -118
  22. package/lib/components/base/Animations.d.ts +0 -36
  23. package/lib/components/base/Animations.d.ts.map +0 -1
  24. package/lib/components/base/Animations.js +0 -70
  25. package/lib/components/base/Animations.ts +0 -112
  26. package/lib/components/base/BaseComponent.d.ts +0 -294
  27. package/lib/components/base/BaseComponent.d.ts.map +0 -1
  28. package/lib/components/base/BaseComponent.js +0 -735
  29. package/lib/components/base/BaseComponent.ts +0 -884
  30. package/lib/components/base/FormInput.d.ts +0 -77
  31. package/lib/components/base/FormInput.d.ts.map +0 -1
  32. package/lib/components/base/FormInput.js +0 -171
  33. package/lib/components/base/FormInput.ts +0 -237
  34. package/lib/components/blueprint.d.ts +0 -40
  35. package/lib/components/blueprint.d.ts.map +0 -1
  36. package/lib/components/blueprint.js +0 -327
  37. package/lib/components/button.d.ts +0 -70
  38. package/lib/components/button.d.ts.map +0 -1
  39. package/lib/components/button.js +0 -177
  40. package/lib/components/button.ts +0 -237
  41. package/lib/components/card.d.ts +0 -35
  42. package/lib/components/card.d.ts.map +0 -1
  43. package/lib/components/card.js +0 -130
  44. package/lib/components/card.ts +0 -177
  45. package/lib/components/chart.d.ts +0 -49
  46. package/lib/components/chart.d.ts.map +0 -1
  47. package/lib/components/chart.js +0 -205
  48. package/lib/components/chart.ts +0 -254
  49. package/lib/components/checkbox.d.ts +0 -33
  50. package/lib/components/checkbox.d.ts.map +0 -1
  51. package/lib/components/checkbox.js +0 -202
  52. package/lib/components/checkbox.ts +0 -260
  53. package/lib/components/code.d.ts +0 -52
  54. package/lib/components/code.d.ts.map +0 -1
  55. package/lib/components/code.js +0 -201
  56. package/lib/components/code.ts +0 -260
  57. package/lib/components/container.d.ts +0 -60
  58. package/lib/components/container.d.ts.map +0 -1
  59. package/lib/components/container.js +0 -195
  60. package/lib/components/container.ts +0 -259
  61. package/lib/components/data.d.ts +0 -36
  62. package/lib/components/data.d.ts.map +0 -1
  63. package/lib/components/data.js +0 -110
  64. package/lib/components/data.ts +0 -135
  65. package/lib/components/dataframe/DataFrameSource.d.ts +0 -118
  66. package/lib/components/dataframe/DataFrameSource.d.ts.map +0 -1
  67. package/lib/components/dataframe/DataFrameSource.js +0 -421
  68. package/lib/components/dataframe/DataFrameSource.ts +0 -532
  69. package/lib/components/dataframe/ImportSettingsModal.d.ts +0 -60
  70. package/lib/components/dataframe/ImportSettingsModal.d.ts.map +0 -1
  71. package/lib/components/dataframe/ImportSettingsModal.js +0 -442
  72. package/lib/components/dataframe/ImportSettingsModal.ts +0 -531
  73. package/lib/components/dataframe.d.ts +0 -110
  74. package/lib/components/dataframe.d.ts.map +0 -1
  75. package/lib/components/dataframe.js +0 -470
  76. package/lib/components/datepicker.d.ts +0 -40
  77. package/lib/components/datepicker.d.ts.map +0 -1
  78. package/lib/components/datepicker.js +0 -193
  79. package/lib/components/datepicker.ts +0 -251
  80. package/lib/components/dialog.d.ts +0 -39
  81. package/lib/components/dialog.d.ts.map +0 -1
  82. package/lib/components/dialog.js +0 -131
  83. package/lib/components/dialog.ts +0 -178
  84. package/lib/components/divider.d.ts +0 -31
  85. package/lib/components/divider.d.ts.map +0 -1
  86. package/lib/components/divider.js +0 -72
  87. package/lib/components/divider.ts +0 -104
  88. package/lib/components/dropdown-menu.d.ts +0 -42
  89. package/lib/components/dropdown-menu.d.ts.map +0 -1
  90. package/lib/components/dropdown-menu.js +0 -177
  91. package/lib/components/dropdown-menu.ts +0 -214
  92. package/lib/components/dropdown.d.ts +0 -41
  93. package/lib/components/dropdown.d.ts.map +0 -1
  94. package/lib/components/dropdown.js +0 -136
  95. package/lib/components/dropdown.ts +0 -188
  96. package/lib/components/element.d.ts +0 -51
  97. package/lib/components/element.d.ts.map +0 -1
  98. package/lib/components/element.js +0 -209
  99. package/lib/components/element.ts +0 -271
  100. package/lib/components/event-chain.d.ts +0 -9
  101. package/lib/components/event-chain.d.ts.map +0 -1
  102. package/lib/components/event-chain.js +0 -33
  103. package/lib/components/fileupload.d.ts +0 -98
  104. package/lib/components/fileupload.d.ts.map +0 -1
  105. package/lib/components/fileupload.js +0 -351
  106. package/lib/components/fileupload.ts +0 -449
  107. package/lib/components/grid.d.ts +0 -88
  108. package/lib/components/grid.d.ts.map +0 -1
  109. package/lib/components/grid.js +0 -208
  110. package/lib/components/grid.ts +0 -295
  111. package/lib/components/heading.d.ts +0 -25
  112. package/lib/components/heading.d.ts.map +0 -1
  113. package/lib/components/heading.js +0 -83
  114. package/lib/components/heading.ts +0 -113
  115. package/lib/components/helpers.d.ts +0 -9
  116. package/lib/components/helpers.d.ts.map +0 -1
  117. package/lib/components/helpers.js +0 -30
  118. package/lib/components/helpers.ts +0 -41
  119. package/lib/components/hero.d.ts +0 -60
  120. package/lib/components/hero.d.ts.map +0 -1
  121. package/lib/components/hero.js +0 -239
  122. package/lib/components/hero.ts +0 -302
  123. package/lib/components/history/StateHistory.d.ts +0 -91
  124. package/lib/components/history/StateHistory.d.ts.map +0 -1
  125. package/lib/components/history/StateHistory.js +0 -154
  126. package/lib/components/history/StateHistory.ts +0 -200
  127. package/lib/components/icon.d.ts +0 -36
  128. package/lib/components/icon.d.ts.map +0 -1
  129. package/lib/components/icon.js +0 -135
  130. package/lib/components/icon.ts +0 -182
  131. package/lib/components/icons.d.ts +0 -25
  132. package/lib/components/icons.d.ts.map +0 -1
  133. package/lib/components/icons.js +0 -440
  134. package/lib/components/icons.ts +0 -464
  135. package/lib/components/image.d.ts +0 -42
  136. package/lib/components/image.d.ts.map +0 -1
  137. package/lib/components/image.js +0 -204
  138. package/lib/components/image.ts +0 -260
  139. package/lib/components/include.d.ts +0 -86
  140. package/lib/components/include.d.ts.map +0 -1
  141. package/lib/components/include.js +0 -238
  142. package/lib/components/input.d.ts +0 -85
  143. package/lib/components/input.d.ts.map +0 -1
  144. package/lib/components/input.js +0 -362
  145. package/lib/components/input.ts +0 -473
  146. package/lib/components/layer.d.ts +0 -72
  147. package/lib/components/layer.d.ts.map +0 -1
  148. package/lib/components/layer.js +0 -219
  149. package/lib/components/layer.ts +0 -304
  150. package/lib/components/link.d.ts +0 -41
  151. package/lib/components/link.d.ts.map +0 -1
  152. package/lib/components/link.js +0 -216
  153. package/lib/components/link.ts +0 -268
  154. package/lib/components/list.d.ts +0 -83
  155. package/lib/components/list.d.ts.map +0 -1
  156. package/lib/components/list.js +0 -314
  157. package/lib/components/list.ts +0 -423
  158. package/lib/components/loading.d.ts +0 -25
  159. package/lib/components/loading.d.ts.map +0 -1
  160. package/lib/components/loading.js +0 -76
  161. package/lib/components/loading.ts +0 -104
  162. package/lib/components/menu.d.ts +0 -38
  163. package/lib/components/menu.d.ts.map +0 -1
  164. package/lib/components/menu.js +0 -205
  165. package/lib/components/menu.ts +0 -279
  166. package/lib/components/modal.d.ts +0 -97
  167. package/lib/components/modal.d.ts.map +0 -1
  168. package/lib/components/modal.js +0 -463
  169. package/lib/components/modal.ts +0 -576
  170. package/lib/components/nav.d.ts +0 -46
  171. package/lib/components/nav.d.ts.map +0 -1
  172. package/lib/components/nav.js +0 -193
  173. package/lib/components/nav.ts +0 -261
  174. package/lib/components/paragraph.d.ts +0 -30
  175. package/lib/components/paragraph.d.ts.map +0 -1
  176. package/lib/components/paragraph.js +0 -93
  177. package/lib/components/paragraph.ts +0 -123
  178. package/lib/components/pen.d.ts +0 -125
  179. package/lib/components/pen.d.ts.map +0 -1
  180. package/lib/components/pen.js +0 -443
  181. package/lib/components/pen.ts +0 -567
  182. package/lib/components/progress.d.ts +0 -40
  183. package/lib/components/progress.d.ts.map +0 -1
  184. package/lib/components/progress.js +0 -116
  185. package/lib/components/progress.ts +0 -163
  186. package/lib/components/radio.d.ts +0 -43
  187. package/lib/components/radio.d.ts.map +0 -1
  188. package/lib/components/radio.js +0 -226
  189. package/lib/components/radio.ts +0 -303
  190. package/lib/components/registry.d.ts +0 -34
  191. package/lib/components/registry.d.ts.map +0 -1
  192. package/lib/components/registry.js +0 -163
  193. package/lib/components/registry.ts +0 -193
  194. package/lib/components/req.d.ts +0 -155
  195. package/lib/components/req.d.ts.map +0 -1
  196. package/lib/components/req.js +0 -253
  197. package/lib/components/req.ts +0 -303
  198. package/lib/components/script.d.ts +0 -14
  199. package/lib/components/script.d.ts.map +0 -1
  200. package/lib/components/script.js +0 -33
  201. package/lib/components/script.ts +0 -41
  202. package/lib/components/select.d.ts +0 -42
  203. package/lib/components/select.d.ts.map +0 -1
  204. package/lib/components/select.js +0 -209
  205. package/lib/components/select.ts +0 -281
  206. package/lib/components/sidebar.d.ts +0 -59
  207. package/lib/components/sidebar.d.ts.map +0 -1
  208. package/lib/components/sidebar.js +0 -298
  209. package/lib/components/sidebar.ts +0 -395
  210. package/lib/components/stack/BaseStack.d.ts +0 -65
  211. package/lib/components/stack/BaseStack.d.ts.map +0 -1
  212. package/lib/components/stack/BaseStack.js +0 -274
  213. package/lib/components/stack/BaseStack.ts +0 -328
  214. package/lib/components/stack/HStack.d.ts +0 -18
  215. package/lib/components/stack/HStack.d.ts.map +0 -1
  216. package/lib/components/stack/HStack.js +0 -22
  217. package/lib/components/stack/HStack.ts +0 -25
  218. package/lib/components/stack/VStack.d.ts +0 -19
  219. package/lib/components/stack/VStack.d.ts.map +0 -1
  220. package/lib/components/stack/VStack.js +0 -23
  221. package/lib/components/stack/VStack.ts +0 -26
  222. package/lib/components/stack/ZStack.d.ts +0 -18
  223. package/lib/components/stack/ZStack.d.ts.map +0 -1
  224. package/lib/components/stack/ZStack.js +0 -22
  225. package/lib/components/stack/ZStack.ts +0 -25
  226. package/lib/components/style.d.ts +0 -14
  227. package/lib/components/style.d.ts.map +0 -1
  228. package/lib/components/style.js +0 -33
  229. package/lib/components/style.ts +0 -41
  230. package/lib/components/switch.d.ts +0 -34
  231. package/lib/components/switch.d.ts.map +0 -1
  232. package/lib/components/switch.js +0 -209
  233. package/lib/components/switch.ts +0 -272
  234. package/lib/components/table.d.ts +0 -137
  235. package/lib/components/table.d.ts.map +0 -1
  236. package/lib/components/table.js +0 -1019
  237. package/lib/components/table.ts +0 -1225
  238. package/lib/components/tabs.d.ts +0 -53
  239. package/lib/components/tabs.d.ts.map +0 -1
  240. package/lib/components/tabs.js +0 -275
  241. package/lib/components/tabs.ts +0 -349
  242. package/lib/components/theme-toggle.d.ts +0 -45
  243. package/lib/components/theme-toggle.d.ts.map +0 -1
  244. package/lib/components/theme-toggle.js +0 -218
  245. package/lib/components/theme-toggle.ts +0 -297
  246. package/lib/components/tooltip.d.ts +0 -31
  247. package/lib/components/tooltip.d.ts.map +0 -1
  248. package/lib/components/tooltip.js +0 -112
  249. package/lib/components/tooltip.ts +0 -148
  250. package/lib/components/watcher.d.ts +0 -195
  251. package/lib/components/watcher.d.ts.map +0 -1
  252. package/lib/components/watcher.js +0 -241
  253. package/lib/components/watcher.ts +0 -261
  254. package/lib/components/write.d.ts +0 -107
  255. package/lib/components/write.d.ts.map +0 -1
  256. package/lib/components/write.js +0 -222
  257. package/lib/components/write.ts +0 -272
  258. package/lib/data/DataPipeline.d.ts +0 -113
  259. package/lib/data/DataPipeline.d.ts.map +0 -1
  260. package/lib/data/DataPipeline.js +0 -359
  261. package/lib/data/DataPipeline.ts +0 -452
  262. package/lib/facades/dataframe.jux +0 -0
  263. package/lib/globals.d.ts +0 -21
  264. package/lib/reactivity/state.d.ts +0 -36
  265. package/lib/reactivity/state.d.ts.map +0 -1
  266. package/lib/reactivity/state.js +0 -67
  267. package/lib/reactivity/state.ts +0 -78
  268. package/lib/storage/DataFrame.d.ts +0 -284
  269. package/lib/storage/DataFrame.d.ts.map +0 -1
  270. package/lib/storage/DataFrame.js +0 -1022
  271. package/lib/storage/DataFrame.ts +0 -1195
  272. package/lib/storage/DataFrameSource.d.ts +0 -158
  273. package/lib/storage/DataFrameSource.d.ts.map +0 -1
  274. package/lib/storage/DataFrameSource.js +0 -409
  275. package/lib/storage/DataFrameSource.ts +0 -556
  276. package/lib/storage/FileStorage.d.ts +0 -53
  277. package/lib/storage/FileStorage.d.ts.map +0 -1
  278. package/lib/storage/FileStorage.js +0 -80
  279. package/lib/storage/FileStorage.ts +0 -95
  280. package/lib/storage/IndexedDBDriver.d.ts +0 -75
  281. package/lib/storage/IndexedDBDriver.d.ts.map +0 -1
  282. package/lib/storage/IndexedDBDriver.js +0 -177
  283. package/lib/storage/IndexedDBDriver.ts +0 -226
  284. package/lib/storage/TabularDriver.d.ts +0 -118
  285. package/lib/storage/TabularDriver.d.ts.map +0 -1
  286. package/lib/storage/TabularDriver.js +0 -731
  287. package/lib/storage/TabularDriver.ts +0 -874
  288. package/lib/utils/codeparser.d.ts +0 -29
  289. package/lib/utils/codeparser.d.ts.map +0 -1
  290. package/lib/utils/codeparser.js +0 -409
  291. package/lib/utils/fetch.d.ts +0 -176
  292. package/lib/utils/fetch.d.ts.map +0 -1
  293. package/lib/utils/fetch.js +0 -427
  294. package/lib/utils/formatId.d.ts +0 -16
  295. package/lib/utils/formatId.d.ts.map +0 -1
  296. package/lib/utils/formatId.js +0 -27
  297. package/lib/utils/path-resolver.js +0 -23
@@ -1,1019 +0,0 @@
1
- /* ═════════════════════════════════════════════════════════════════
2
- * SECTION 1: DEFINITIONS
3
- * Type definitions, constants, interfaces
4
- * ═════════════════════════════════════════════════════════════════ */
5
- import { BaseComponent } from './base/BaseComponent.js';
6
- // Event definitions
7
- const TRIGGER_EVENTS = [
8
- 'rowClick', 'rowHover', 'cellClick',
9
- 'selected', 'deselected', 'selectionChange'
10
- ];
11
- const CALLBACK_EVENTS = [
12
- 'sortChange', 'filterChange', 'pageChange', 'dataChange'
13
- ];
14
- /* ═════════════════════════════════════════════════════════════════
15
- * SECTION 2: CONSTRUCTOR & STORAGE
16
- * Class declaration, instance variables, initialization
17
- * ═════════════════════════════════════════════════════════════════ */
18
- export class Table extends BaseComponent {
19
- constructor(id, options = {}) {
20
- const normalizedColumns = (options.columns ?? []).map(col => typeof col === 'string' ? { key: col, label: col } : col);
21
- // Initialize base with state
22
- super(id, {
23
- columns: normalizedColumns,
24
- rows: options.rows ?? [],
25
- computedColumns: new Map(), // ✨ NEW: Initialize empty Map
26
- headers: options.headers ?? true,
27
- striped: options.striped ?? false,
28
- hoverable: options.hoverable ?? false,
29
- bordered: options.bordered ?? false,
30
- compact: options.compact ?? false,
31
- sortable: options.sortable ?? false,
32
- filterable: options.filterable ?? false,
33
- paginated: options.paginated ?? false,
34
- rowsPerPage: options.rowsPerPage ?? 10,
35
- currentPage: 1,
36
- sortColumn: null,
37
- sortDirection: 'asc',
38
- filterText: '',
39
- selectable: options.selectable ?? false,
40
- multiSelect: options.multiSelect ?? false,
41
- showCheckboxes: options.showCheckboxes ?? false,
42
- showBulkCheckbox: options.showBulkCheckbox ?? false,
43
- selectionTrigger: options.selectionTrigger ?? 'row',
44
- selectedIndexes: new Set(),
45
- style: options.style ?? '',
46
- class: options.class ?? '',
47
- rowIdField: options.rowIdField,
48
- selectionBehavior: options.selectionBehavior ?? 'clear'
49
- });
50
- this._tableElement = null;
51
- // ✅ DEBUG: Log what was passed vs what was stored
52
- console.log(`[Table ${id}] constructor options.filterable:`, options.filterable);
53
- console.log(`[Table ${id}] this.state.filterable:`, this.state.filterable);
54
- }
55
- /* ═════════════════════════════════════════════════════════════════
56
- * ABSTRACT METHOD IMPLEMENTATIONS
57
- * ═════════════════════════════════════════════════════════════════ */
58
- getTriggerEvents() {
59
- return TRIGGER_EVENTS;
60
- }
61
- getCallbackEvents() {
62
- return CALLBACK_EVENTS;
63
- }
64
- /* ═════════════════════════════════════════════════════════════════
65
- * SECTION 3: FLUENT API
66
- * ═════════════════════════════════════════════════════════════════ */
67
- // ✅ Inherited from BaseComponent: style(), class(), bind(), sync(), renderTo()
68
- // Configuration methods
69
- columns(value) {
70
- this.state.columns = value.map(col => typeof col === 'string' ? { key: col, label: col } : col);
71
- // ✅ Rebuild header if table already rendered
72
- if (this._tableElement) {
73
- const oldThead = this._tableElement.querySelector('thead');
74
- if (oldThead)
75
- oldThead.remove();
76
- if (this.state.headers) {
77
- const newThead = this._buildTableHeader();
78
- this._tableElement.insertBefore(newThead, this._tableElement.firstChild);
79
- }
80
- }
81
- return this;
82
- }
83
- rows(value) {
84
- const previousRows = this.state.rows;
85
- const hadSelections = this.state.selectedIndexes.size > 0;
86
- // Handle selections based on behavior
87
- if (this.state.selectionBehavior === 'preserve' && this.state.rowIdField) {
88
- this._preserveSelections(previousRows, value);
89
- }
90
- else {
91
- this.state.selectedIndexes.clear();
92
- }
93
- this.state.rows = value;
94
- // Auto-reset pagination if current page is now invalid
95
- if (this.state.paginated) {
96
- const totalPages = Math.ceil(value.length / this.state.rowsPerPage);
97
- if (this.state.currentPage > totalPages && totalPages > 0) {
98
- this.state.currentPage = totalPages;
99
- }
100
- if (totalPages === 0) {
101
- this.state.currentPage = 1;
102
- }
103
- }
104
- this._updateTable();
105
- // Fire callbacks
106
- this._triggerCallback('dataChange', value, previousRows);
107
- if (hadSelections && this._triggerHandlers.has('selectionChange')) {
108
- this._triggerHandlers.get('selectionChange')(this.getSelectedRows(), this.getSelectedIndexes(), new CustomEvent('dataChange'));
109
- }
110
- return this;
111
- }
112
- /**
113
- * Add a computed column that evaluates dynamically at render time
114
- *
115
- * @param key - Unique key for the column
116
- * @param label - Display label in header
117
- * @param compute - Function to compute value from row data
118
- * @param render - Optional custom renderer for the computed value
119
- * @param width - Optional column width
120
- */
121
- computedColumn(key, label, compute, render, width) {
122
- // Store computed column definition
123
- this.state.computedColumns.set(key, {
124
- key,
125
- label,
126
- compute,
127
- render,
128
- width
129
- });
130
- // Add to columns list if not already present
131
- const existingColumn = this.state.columns.find(col => col.key === key);
132
- if (!existingColumn) {
133
- this.state.columns.push({
134
- key,
135
- label,
136
- width,
137
- computed: true,
138
- render: render
139
- });
140
- }
141
- // If already rendered, update table
142
- if (this._tableElement) {
143
- const tbody = this._tableElement.querySelector('tbody');
144
- const thead = this._tableElement.querySelector('thead');
145
- if (tbody && thead) {
146
- // Rebuild header with new column
147
- const table = this._tableElement;
148
- table.innerHTML = '';
149
- const newThead = this._buildTableHeader();
150
- table.appendChild(newThead);
151
- const newTbody = document.createElement('tbody');
152
- this._renderTableBody(newTbody);
153
- table.appendChild(newTbody);
154
- // Re-wire events
155
- this._wireTriggerEvents(newTbody);
156
- }
157
- }
158
- return this;
159
- }
160
- /**
161
- * Remove a computed column
162
- */
163
- removeComputedColumn(key) {
164
- this.state.computedColumns.delete(key);
165
- this.state.columns = this.state.columns.filter(col => col.key !== key);
166
- if (this._tableElement) {
167
- const tbody = this._tableElement.querySelector('tbody');
168
- const thead = this._tableElement.querySelector('thead');
169
- if (tbody && thead) {
170
- const table = this._tableElement;
171
- table.innerHTML = '';
172
- const newThead = this._buildTableHeader();
173
- table.appendChild(newThead);
174
- const newTbody = document.createElement('tbody');
175
- this._renderTableBody(newTbody);
176
- table.appendChild(newTbody);
177
- this._wireTriggerEvents(newTbody);
178
- }
179
- }
180
- return this;
181
- }
182
- // Visual options
183
- headers(value) {
184
- this.state.headers = value;
185
- return this;
186
- }
187
- striped(value) {
188
- this.state.striped = value;
189
- return this;
190
- }
191
- hoverable(value) {
192
- this.state.hoverable = value;
193
- return this;
194
- }
195
- bordered(value) {
196
- this.state.bordered = value;
197
- return this;
198
- }
199
- compact(value) {
200
- this.state.compact = value;
201
- return this;
202
- }
203
- // Feature toggles
204
- sortable(value) {
205
- this.state.sortable = value;
206
- return this;
207
- }
208
- filterable(value) {
209
- this.state.filterable = value;
210
- return this;
211
- }
212
- paginated(value) {
213
- this.state.paginated = value;
214
- return this;
215
- }
216
- rowsPerPage(value) {
217
- this.state.rowsPerPage = value;
218
- return this;
219
- }
220
- // Selection configuration
221
- selectable(value) {
222
- this.state.selectable = value;
223
- return this;
224
- }
225
- multiSelect(value) {
226
- this.state.multiSelect = value;
227
- if (value) {
228
- this.state.selectable = true; // multi-select implies selectable
229
- }
230
- return this;
231
- }
232
- showCheckboxes(value) {
233
- this.state.showCheckboxes = value;
234
- // If already rendered, update immediately
235
- if (this._tableElement) {
236
- const tbody = this._tableElement.querySelector('tbody');
237
- const thead = this._tableElement.querySelector('thead');
238
- if (tbody && thead) {
239
- // Update header
240
- this._updateHeaderCheckbox(thead);
241
- // Re-render body with/without checkboxes
242
- this._renderTableBody(tbody);
243
- // Update bulk checkbox state
244
- this._updateBulkCheckboxState();
245
- }
246
- }
247
- return this;
248
- }
249
- showBulkCheckbox(value) {
250
- this.state.showBulkCheckbox = value;
251
- if (value) {
252
- this.state.multiSelect = true;
253
- this.state.selectable = true;
254
- this.state.showCheckboxes = true;
255
- }
256
- // If already rendered, update immediately
257
- if (this._tableElement) {
258
- const thead = this._tableElement.querySelector('thead');
259
- const tbody = this._tableElement.querySelector('tbody');
260
- if (thead && tbody) {
261
- this._updateHeaderCheckbox(thead);
262
- this._renderTableBody(tbody);
263
- this._updateBulkCheckboxState();
264
- }
265
- }
266
- return this;
267
- }
268
- selectionTrigger(value) {
269
- this.state.selectionTrigger = value;
270
- // If already rendered, update cursor style
271
- if (this._tableElement) {
272
- const tbody = this._tableElement.querySelector('tbody');
273
- if (tbody) {
274
- if (this.state.selectionTrigger === 'row' && this.state.selectable) {
275
- tbody.style.cursor = 'pointer';
276
- }
277
- else {
278
- tbody.style.cursor = '';
279
- }
280
- }
281
- }
282
- return this;
283
- }
284
- // Selection actions
285
- selectAll() {
286
- this.state.rows.forEach((_, index) => {
287
- this.state.selectedIndexes.add(index);
288
- });
289
- this._updateRowSelectionUI();
290
- // Fire selectionChange
291
- if (this._triggerHandlers.has('selectionChange')) {
292
- this._triggerHandlers.get('selectionChange')(this.getSelectedRows(), this.getSelectedIndexes(), new CustomEvent('bulkSelect'));
293
- }
294
- return this;
295
- }
296
- deselectAll() {
297
- this.state.selectedIndexes.clear();
298
- this._updateRowSelectionUI();
299
- // Fire selectionChange
300
- if (this._triggerHandlers.has('selectionChange')) {
301
- this._triggerHandlers.get('selectionChange')([], [], new CustomEvent('bulkDeselect'));
302
- }
303
- return this;
304
- }
305
- clearSelection() {
306
- this.state.selectedIndexes.clear();
307
- this._updateRowSelectionUI();
308
- return this;
309
- }
310
- selectRows(indexes) {
311
- if (!this.state.multiSelect) {
312
- this.state.selectedIndexes.clear();
313
- }
314
- indexes.forEach(index => {
315
- if (index >= 0 && index < this.state.rows.length) {
316
- this.state.selectedIndexes.add(index);
317
- }
318
- });
319
- this._updateRowSelectionUI();
320
- return this;
321
- }
322
- deselectRows(indexes) {
323
- indexes.forEach(index => this.state.selectedIndexes.delete(index));
324
- this._updateRowSelectionUI();
325
- return this;
326
- }
327
- // Public utilities
328
- getSelectedIndexes() {
329
- return Array.from(this.state.selectedIndexes);
330
- }
331
- getSelectedRows() {
332
- return Array.from(this.state.selectedIndexes).map(index => this.state.rows[index]);
333
- }
334
- /* ═════════════════════════════════════════════════════════════════
335
- * SECTION 5: RENDER LIFECYCLE
336
- * Main render method and BUILD phase helpers
337
- * ═════════════════════════════════════════════════════════════════ */
338
- update(prop, value) {
339
- // No reactive updates needed
340
- }
341
- render(targetId) {
342
- const container = this._setupContainer(targetId);
343
- const wrapper = this._buildWrapper();
344
- const table = this._buildTable(wrapper);
345
- const tbody = table.querySelector('tbody');
346
- this._wireAllEvents(wrapper, tbody);
347
- container.appendChild(wrapper);
348
- this._tableElement = table;
349
- return this;
350
- }
351
- // Step 1: SETUP
352
- _setupContainer(targetId) {
353
- let container;
354
- if (targetId) {
355
- if (typeof targetId === 'string') {
356
- const target = document.querySelector(targetId);
357
- if (!target || !(target instanceof HTMLElement)) {
358
- throw new Error(`Table: Target "${targetId}" not found`);
359
- }
360
- container = target;
361
- }
362
- else if (targetId instanceof HTMLElement) {
363
- container = targetId;
364
- }
365
- else if (targetId instanceof BaseComponent) {
366
- // Find component's container by ID
367
- const target = document.getElementById(targetId._id);
368
- if (!target) {
369
- throw new Error(`Table: Target component "${targetId._id}" not found`);
370
- }
371
- container = target;
372
- }
373
- else {
374
- throw new Error(`Table: Invalid target type`);
375
- }
376
- }
377
- else {
378
- // Inline getOrCreateContainer functionality
379
- let element = document.getElementById(this._id);
380
- if (!element) {
381
- element = document.createElement('div');
382
- element.id = this._id;
383
- document.body.appendChild(element);
384
- }
385
- container = element;
386
- }
387
- this.container = container;
388
- return container;
389
- }
390
- // Step 2: BUILD wrapper
391
- _buildWrapper() {
392
- const { style, class: className } = this.state;
393
- const wrapper = document.createElement('div');
394
- wrapper.className = 'jux-table-wrapper';
395
- wrapper.id = this._id;
396
- if (className)
397
- wrapper.className += ` ${className}`;
398
- if (style)
399
- wrapper.setAttribute('style', style);
400
- if (this.state.selectable) {
401
- const selectionStyles = document.createElement('style');
402
- selectionStyles.textContent = `
403
- .jux-table-row-selected { background-color: #e3f2fd !important; }
404
- .jux-table-row-selected:hover { background-color: #bbdefb !important; }
405
- `;
406
- wrapper.appendChild(selectionStyles);
407
- }
408
- // ✅ Only show filter if filterable AND there are rows
409
- if (this.state.filterable && this.state.rows.length > 0) {
410
- wrapper.appendChild(this._buildFilterInput());
411
- }
412
- return wrapper;
413
- }
414
- _buildFilterInput() {
415
- const input = document.createElement('input');
416
- input.type = 'text';
417
- input.placeholder = 'Filter...';
418
- input.className = 'jux-table-filter';
419
- input.id = `${this._id}-filter`;
420
- input.style.cssText = 'margin-bottom: 10px; padding: 5px; width: 100%; box-sizing: border-box;';
421
- input.addEventListener('input', (e) => {
422
- const target = e.target;
423
- this.state.filterText = target.value;
424
- const tbody = this._tableElement?.querySelector('tbody');
425
- if (tbody) {
426
- this._renderTableBody(tbody);
427
- const wrapper = this._tableElement?.closest('.jux-table-wrapper');
428
- if (wrapper && this.state.paginated) {
429
- this.state.currentPage = 1;
430
- this._updatePagination(wrapper, tbody);
431
- }
432
- }
433
- this._triggerCallback('filterChange', target.value, e);
434
- });
435
- return input;
436
- }
437
- // Step 3: BUILD table
438
- _buildTable(wrapper) {
439
- const { striped, hoverable, bordered } = this.state;
440
- const table = document.createElement('table');
441
- table.className = 'jux-table';
442
- if (striped)
443
- table.classList.add('jux-table-striped');
444
- if (hoverable)
445
- table.classList.add('jux-table-hoverable');
446
- if (bordered)
447
- table.classList.add('jux-table-bordered');
448
- // Build and append header
449
- if (this.state.headers) {
450
- const thead = this._buildTableHeader();
451
- table.appendChild(thead);
452
- }
453
- // Build and append body
454
- const tbody = document.createElement('tbody');
455
- this._renderTableBody(tbody);
456
- table.appendChild(tbody);
457
- wrapper.appendChild(table);
458
- return table;
459
- }
460
- _buildTableHeader() {
461
- const thead = document.createElement('thead');
462
- const headerRow = document.createElement('tr');
463
- // Add bulk checkbox or empty checkbox column
464
- if (this.state.showBulkCheckbox) {
465
- const bulkTh = document.createElement('th');
466
- bulkTh.style.width = '40px';
467
- bulkTh.style.textAlign = 'center';
468
- const bulkCheckbox = document.createElement('input');
469
- bulkCheckbox.type = 'checkbox';
470
- bulkCheckbox.className = 'jux-bulk-checkbox';
471
- bulkCheckbox.style.cursor = 'pointer';
472
- bulkCheckbox.title = 'Select all';
473
- bulkCheckbox.addEventListener('change', (e) => {
474
- if (bulkCheckbox.checked) {
475
- this.selectAll();
476
- }
477
- else {
478
- this.deselectAll();
479
- }
480
- });
481
- bulkTh.appendChild(bulkCheckbox);
482
- headerRow.insertBefore(bulkTh, headerRow.firstChild);
483
- }
484
- else if (this.state.showCheckboxes) {
485
- const checkboxTh = document.createElement('th');
486
- checkboxTh.style.width = '40px';
487
- headerRow.insertBefore(checkboxTh, headerRow.firstChild);
488
- }
489
- // Add column headers with optional sorts
490
- this.state.columns.forEach(col => {
491
- const th = this._buildColumnHeader(col);
492
- headerRow.appendChild(th);
493
- });
494
- thead.appendChild(headerRow);
495
- thead.appendChild(headerRow);
496
- return thead;
497
- }
498
- _buildBulkCheckboxCell() {
499
- const bulkTh = document.createElement('th');
500
- bulkTh.style.width = '40px';
501
- bulkTh.style.textAlign = 'center';
502
- const bulkCheckbox = document.createElement('input');
503
- bulkCheckbox.type = 'checkbox';
504
- bulkCheckbox.className = 'jux-bulk-checkbox';
505
- bulkCheckbox.style.cursor = 'pointer';
506
- bulkCheckbox.title = 'Select all';
507
- bulkCheckbox.addEventListener('change', (e) => {
508
- if (bulkCheckbox.checked) {
509
- this.selectAll();
510
- }
511
- else {
512
- this.deselectAll();
513
- }
514
- });
515
- bulkTh.appendChild(bulkCheckbox);
516
- return bulkTh;
517
- }
518
- _buildColumnHeader(col) {
519
- const th = document.createElement('th');
520
- th.textContent = col.label;
521
- th.setAttribute('data-column-key', col.key);
522
- if (col.width)
523
- th.style.width = col.width;
524
- if (this.state.sortable) {
525
- th.style.cursor = 'pointer';
526
- th.style.userSelect = 'none';
527
- th.addEventListener('click', (e) => {
528
- if (this.state.sortColumn === col.key) {
529
- this.state.sortDirection = this.state.sortDirection === 'asc' ? 'desc' : 'asc';
530
- }
531
- else {
532
- this.state.sortColumn = col.key;
533
- this.state.sortDirection = 'asc';
534
- }
535
- const tbody = this._tableElement.querySelector('tbody');
536
- this._renderTableBody(tbody);
537
- const headerRow = th.parentElement;
538
- headerRow.querySelectorAll('th[data-column-key]').forEach(h => {
539
- h.textContent = h.textContent?.replace(' ▲', '').replace(' ▼', '') || '';
540
- });
541
- th.textContent = col.label + (this.state.sortDirection === 'asc' ? ' ▲' : ' ▼');
542
- this._triggerCallback('sortChange', col.key, this.state.sortDirection, e);
543
- });
544
- }
545
- return th;
546
- }
547
- _wireAllEvents(wrapper, tbody) {
548
- this._wireTriggerEvents(tbody);
549
- if (this.state.paginated) {
550
- this._updatePagination(wrapper, tbody);
551
- }
552
- this._wireStandardEvents(wrapper);
553
- this._wireSyncBindings(wrapper, tbody);
554
- }
555
- /* ═════════════════════════════════════════════════════════════════
556
- * SECTION 6: EVENT WIRING
557
- * ═════════════════════════════════════════════════════════════════ */
558
- _wireTriggerEvents(tbody) {
559
- if (this._triggerHandlers.has('rowClick')) {
560
- const handler = this._triggerHandlers.get('rowClick');
561
- tbody.addEventListener('click', (e) => {
562
- const tr = e.target.closest('tr');
563
- if (tr && tbody.contains(tr)) {
564
- const rowIndex = Array.from(tbody.children).indexOf(tr);
565
- let rows = this._getFilteredRows();
566
- rows = this._getSortedRows(rows);
567
- rows = this._getPaginatedRows(rows);
568
- const rowData = rows[rowIndex];
569
- if (rowData)
570
- handler(rowData, rowIndex, e);
571
- }
572
- });
573
- tbody.style.cursor = 'pointer';
574
- }
575
- if (this._triggerHandlers.has('cellClick')) {
576
- const handler = this._triggerHandlers.get('cellClick');
577
- tbody.addEventListener('click', (e) => {
578
- const td = e.target.closest('td');
579
- if (td) {
580
- const tr = td.closest('tr');
581
- if (tr && tbody.contains(tr)) {
582
- const rowIndex = Array.from(tbody.children).indexOf(tr);
583
- const cellIndex = Array.from(tr.children).indexOf(td);
584
- let rows = this._getFilteredRows();
585
- rows = this._getSortedRows(rows);
586
- rows = this._getPaginatedRows(rows);
587
- const rowData = rows[rowIndex];
588
- const columnKey = this.state.columns[cellIndex]?.key;
589
- const cellValue = rowData?.[columnKey];
590
- if (rowData && columnKey) {
591
- handler(cellValue, rowData, columnKey, rowIndex, cellIndex, e);
592
- }
593
- }
594
- }
595
- });
596
- }
597
- if (this.state.selectable) {
598
- tbody.addEventListener('click', (e) => {
599
- const target = e.target;
600
- const isCheckboxClick = target.tagName === 'INPUT' && target.getAttribute('type') === 'checkbox';
601
- if (this.state.selectionTrigger === 'checkbox' && !isCheckboxClick)
602
- return;
603
- if (this.state.selectionTrigger === 'row' && isCheckboxClick) {
604
- e.preventDefault();
605
- const checkbox = target;
606
- checkbox.checked = !checkbox.checked;
607
- }
608
- const tr = target.closest('tr');
609
- if (tr && tbody.contains(tr)) {
610
- const visualRowIndex = Array.from(tbody.children).indexOf(tr);
611
- let rows = this._getFilteredRows();
612
- rows = this._getSortedRows(rows);
613
- const actualRowIndex = this.state.rows.indexOf(rows[(this.state.currentPage - 1) * this.state.rowsPerPage + visualRowIndex]);
614
- if (actualRowIndex === -1)
615
- return;
616
- const wasSelected = this.state.selectedIndexes.has(actualRowIndex);
617
- if (this.state.multiSelect) {
618
- if (wasSelected) {
619
- this.state.selectedIndexes.delete(actualRowIndex);
620
- tr.classList.remove('jux-table-row-selected');
621
- const checkbox = tr.querySelector('input[type="checkbox"]');
622
- if (checkbox)
623
- checkbox.checked = false;
624
- if (this._triggerHandlers.has('deselected')) {
625
- this._triggerHandlers.get('deselected')(this.state.rows[actualRowIndex], actualRowIndex, e);
626
- }
627
- }
628
- else {
629
- this.state.selectedIndexes.add(actualRowIndex);
630
- tr.classList.add('jux-table-row-selected');
631
- const checkbox = tr.querySelector('input[type="checkbox"]');
632
- if (checkbox)
633
- checkbox.checked = true;
634
- if (this._triggerHandlers.has('selected')) {
635
- this._triggerHandlers.get('selected')(this.state.rows[actualRowIndex], actualRowIndex, e);
636
- }
637
- }
638
- }
639
- else {
640
- const previousSelection = Array.from(this.state.selectedIndexes);
641
- this.state.selectedIndexes.clear();
642
- tbody.querySelectorAll('tr').forEach(row => {
643
- row.classList.remove('jux-table-row-selected');
644
- const checkbox = row.querySelector('input[type="checkbox"]');
645
- if (checkbox)
646
- checkbox.checked = false;
647
- });
648
- if (!wasSelected) {
649
- this.state.selectedIndexes.add(actualRowIndex);
650
- tr.classList.add('jux-table-row-selected');
651
- const checkbox = tr.querySelector('input[type="checkbox"]');
652
- if (checkbox)
653
- checkbox.checked = true;
654
- if (this._triggerHandlers.has('selected')) {
655
- this._triggerHandlers.get('selected')(this.state.rows[actualRowIndex], actualRowIndex, e);
656
- }
657
- }
658
- if (this._triggerHandlers.has('deselected') && previousSelection.length > 0) {
659
- previousSelection.forEach(idx => {
660
- if (idx !== actualRowIndex) {
661
- this._triggerHandlers.get('deselected')(this.state.rows[idx], idx, e);
662
- }
663
- });
664
- }
665
- }
666
- this._updateBulkCheckboxState();
667
- if (this._triggerHandlers.has('selectionChange')) {
668
- this._triggerHandlers.get('selectionChange')(this.getSelectedRows(), this.getSelectedIndexes(), e);
669
- }
670
- }
671
- });
672
- if (this.state.selectionTrigger === 'row') {
673
- tbody.style.cursor = 'pointer';
674
- }
675
- }
676
- }
677
- _wireSyncBindings(wrapper, tbody) {
678
- this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
679
- if (property === 'rows' || property === 'data') {
680
- this._wireSyncRows(stateObj, toComponent, tbody, wrapper);
681
- }
682
- else if (property === 'columns') {
683
- this._wireSyncColumns(stateObj, toComponent, wrapper, tbody);
684
- }
685
- });
686
- }
687
- _wireSyncRows(stateObj, toComponent, tbody, wrapper) {
688
- const transform = toComponent || ((v) => v);
689
- stateObj.subscribe((val) => {
690
- const previousRows = this.state.rows;
691
- const transformed = transform(val);
692
- const hadSelections = this.state.selectedIndexes.size > 0;
693
- if (this.state.selectionBehavior === 'preserve' && this.state.rowIdField) {
694
- this._preserveSelections(previousRows, transformed);
695
- }
696
- else {
697
- const isAppend = transformed.length > previousRows.length;
698
- if (isAppend) {
699
- const maxValidIndex = Math.min(previousRows.length, transformed.length) - 1;
700
- const validSelections = new Set();
701
- this.state.selectedIndexes.forEach(idx => {
702
- if (idx <= maxValidIndex)
703
- validSelections.add(idx);
704
- });
705
- this.state.selectedIndexes = validSelections;
706
- }
707
- else {
708
- this.state.selectedIndexes.clear();
709
- }
710
- }
711
- this.state.rows = transformed;
712
- if (this.state.paginated) {
713
- const totalPages = Math.ceil(transformed.length / this.state.rowsPerPage);
714
- if (this.state.currentPage > totalPages && totalPages > 0) {
715
- this.state.currentPage = totalPages;
716
- }
717
- if (totalPages === 0) {
718
- this.state.currentPage = 1;
719
- }
720
- }
721
- this._renderTableBody(tbody);
722
- if (this.state.paginated) {
723
- this._updatePagination(wrapper, tbody);
724
- }
725
- this._triggerCallback('dataChange', transformed, previousRows);
726
- if (hadSelections || this.state.selectedIndexes.size > 0) {
727
- this._triggerHandlers.get('selectionChange')?.(this.getSelectedRows(), this.getSelectedIndexes(), new CustomEvent('dataChange'));
728
- }
729
- });
730
- }
731
- _wireSyncColumns(stateObj, toComponent, wrapper, tbody) {
732
- const transform = toComponent || ((v) => v);
733
- stateObj.subscribe((val) => {
734
- const transformed = transform(val);
735
- this.state.columns = transformed;
736
- const table = this._tableElement;
737
- table.innerHTML = '';
738
- const thead = this._buildTableHeader();
739
- table.appendChild(thead);
740
- const newTbody = document.createElement('tbody');
741
- this._renderTableBody(newTbody);
742
- table.appendChild(newTbody);
743
- this._wireTriggerEvents(newTbody);
744
- if (this.state.paginated) {
745
- this._updatePagination(wrapper, newTbody);
746
- }
747
- });
748
- }
749
- /* ═════════════════════════════════════════════════════════════════
750
- * SECTION 7: INTERNAL HELPERS
751
- * ═════════════════════════════════════════════════════════════════ */
752
- _getFilteredRows() {
753
- if (!this.state.filterText)
754
- return this.state.rows;
755
- const searchText = this.state.filterText.toLowerCase();
756
- return this.state.rows.filter(row => {
757
- return this.state.columns.some(col => {
758
- const value = row[col.key];
759
- return String(value).toLowerCase().includes(searchText);
760
- });
761
- });
762
- }
763
- _getSortedRows(rows) {
764
- if (!this.state.sortColumn)
765
- return rows;
766
- const computedDef = this.state.computedColumns.get(this.state.sortColumn);
767
- const sorted = [...rows].sort((a, b) => {
768
- let aVal;
769
- let bVal;
770
- if (computedDef) {
771
- const aIndex = this.state.rows.indexOf(a);
772
- const bIndex = this.state.rows.indexOf(b);
773
- aVal = computedDef.compute(a, aIndex);
774
- bVal = computedDef.compute(b, bIndex);
775
- }
776
- else {
777
- aVal = a[this.state.sortColumn];
778
- bVal = b[this.state.sortColumn];
779
- }
780
- if (aVal === bVal)
781
- return 0;
782
- if (aVal == null)
783
- return 1;
784
- if (bVal == null)
785
- return -1;
786
- const comparison = aVal < bVal ? -1 : 1;
787
- return this.state.sortDirection === 'asc' ? comparison : -comparison;
788
- });
789
- return sorted;
790
- }
791
- _getPaginatedRows(rows) {
792
- if (!this.state.paginated)
793
- return rows;
794
- const start = (this.state.currentPage - 1) * this.state.rowsPerPage;
795
- const end = start + this.state.rowsPerPage;
796
- return rows.slice(start, end);
797
- }
798
- _updateTable() {
799
- if (!this._tableElement)
800
- return;
801
- const tbody = this._tableElement.querySelector('tbody');
802
- if (!tbody)
803
- return;
804
- this._renderTableBody(tbody);
805
- const wrapper = this._tableElement.closest('.jux-table-wrapper');
806
- if (wrapper && this.state.paginated) {
807
- this._updatePagination(wrapper, tbody);
808
- }
809
- // ✅ Show/hide filter based on whether there's data
810
- if (wrapper && this.state.filterable) {
811
- let filterInput = wrapper.querySelector('.jux-table-filter');
812
- if (this.state.rows.length > 0) {
813
- // Show filter - create if doesn't exist
814
- if (!filterInput) {
815
- filterInput = this._buildFilterInput();
816
- // Insert at the beginning of wrapper, before the table
817
- wrapper.insertBefore(filterInput, wrapper.firstChild);
818
- }
819
- }
820
- else {
821
- // Hide filter - remove if exists
822
- if (filterInput) {
823
- filterInput.remove();
824
- }
825
- }
826
- }
827
- }
828
- _renderTableBody(tbody) {
829
- tbody.innerHTML = '';
830
- let rows = this._getFilteredRows();
831
- rows = this._getSortedRows(rows);
832
- rows = this._getPaginatedRows(rows);
833
- rows.forEach((row) => {
834
- const tr = document.createElement('tr');
835
- const actualRowIndex = this.state.rows.indexOf(row);
836
- const isSelected = this.state.selectedIndexes.has(actualRowIndex);
837
- if (isSelected) {
838
- tr.classList.add('jux-table-row-selected');
839
- }
840
- if (this.state.showCheckboxes) {
841
- const checkboxTd = document.createElement('td');
842
- checkboxTd.style.width = '40px';
843
- checkboxTd.style.textAlign = 'center';
844
- const checkbox = document.createElement('input');
845
- checkbox.type = 'checkbox';
846
- checkbox.checked = isSelected;
847
- checkbox.style.cursor = 'pointer';
848
- checkboxTd.appendChild(checkbox);
849
- tr.appendChild(checkboxTd);
850
- }
851
- this.state.columns.forEach(col => {
852
- const td = document.createElement('td');
853
- const computedDef = this.state.computedColumns.get(col.key);
854
- let cellValue;
855
- let rendered;
856
- if (computedDef) {
857
- cellValue = computedDef.compute(row, actualRowIndex);
858
- if (computedDef.render) {
859
- rendered = computedDef.render(cellValue, row, actualRowIndex);
860
- }
861
- else {
862
- rendered = cellValue != null ? String(cellValue) : '';
863
- }
864
- }
865
- else {
866
- cellValue = row[col.key];
867
- if (col.render) {
868
- rendered = col.render(cellValue, row);
869
- }
870
- else {
871
- rendered = cellValue != null ? String(cellValue) : '';
872
- }
873
- }
874
- if (typeof rendered === 'string') {
875
- td.innerHTML = rendered;
876
- }
877
- else {
878
- td.appendChild(rendered);
879
- }
880
- tr.appendChild(td);
881
- });
882
- tbody.appendChild(tr);
883
- });
884
- }
885
- _updateHeaderCheckbox(thead) {
886
- const headerRow = thead.querySelector('tr');
887
- if (!headerRow)
888
- return;
889
- const existingCheckboxThs = headerRow.querySelectorAll('th:first-child');
890
- existingCheckboxThs.forEach(th => {
891
- if (th.querySelector('.jux-bulk-checkbox') || th.textContent === '') {
892
- th.remove();
893
- }
894
- });
895
- if (this.state.showBulkCheckbox) {
896
- const bulkTh = this._buildBulkCheckboxCell();
897
- headerRow.insertBefore(bulkTh, headerRow.firstChild);
898
- }
899
- else if (this.state.showCheckboxes) {
900
- const checkboxTh = document.createElement('th');
901
- checkboxTh.style.width = '40px';
902
- headerRow.insertBefore(checkboxTh, headerRow.firstChild);
903
- }
904
- }
905
- _updateBulkCheckboxState() {
906
- if (!this.state.showBulkCheckbox || !this._tableElement)
907
- return;
908
- const bulkCheckbox = this._tableElement.querySelector('.jux-bulk-checkbox');
909
- if (!bulkCheckbox)
910
- return;
911
- const totalRows = this.state.rows.length;
912
- const selectedRows = this.state.selectedIndexes.size;
913
- if (selectedRows === 0) {
914
- bulkCheckbox.checked = false;
915
- bulkCheckbox.indeterminate = false;
916
- }
917
- else if (selectedRows === totalRows) {
918
- bulkCheckbox.checked = true;
919
- bulkCheckbox.indeterminate = false;
920
- }
921
- else {
922
- bulkCheckbox.checked = false;
923
- bulkCheckbox.indeterminate = true;
924
- }
925
- }
926
- _updateRowSelectionUI() {
927
- if (!this._tableElement)
928
- return;
929
- const tbody = this._tableElement.querySelector('tbody');
930
- if (!tbody)
931
- return;
932
- let rows = this._getFilteredRows();
933
- rows = this._getSortedRows(rows);
934
- const pageRows = this._getPaginatedRows(rows);
935
- Array.from(tbody.children).forEach((tr, visualIndex) => {
936
- const pageRowData = pageRows[visualIndex];
937
- const actualRowIndex = this.state.rows.indexOf(pageRowData);
938
- const isSelected = this.state.selectedIndexes.has(actualRowIndex);
939
- if (isSelected) {
940
- tr.classList.add('jux-table-row-selected');
941
- }
942
- else {
943
- tr.classList.remove('jux-table-row-selected');
944
- }
945
- const checkbox = tr.querySelector('input[type="checkbox"]');
946
- if (checkbox) {
947
- checkbox.checked = isSelected;
948
- }
949
- });
950
- this._updateBulkCheckboxState();
951
- }
952
- _preserveSelections(previousRows, newRows) {
953
- if (!this.state.rowIdField || this.state.selectionBehavior === 'clear') {
954
- this.state.selectedIndexes.clear();
955
- return;
956
- }
957
- const selectedIds = new Set();
958
- Array.from(this.state.selectedIndexes).forEach(index => {
959
- const row = previousRows[index];
960
- if (row && row[this.state.rowIdField] != null) {
961
- selectedIds.add(row[this.state.rowIdField]);
962
- }
963
- });
964
- this.state.selectedIndexes.clear();
965
- newRows.forEach((row, index) => {
966
- const rowId = row[this.state.rowIdField];
967
- if (rowId != null && selectedIds.has(rowId)) {
968
- this.state.selectedIndexes.add(index);
969
- }
970
- });
971
- }
972
- _updatePagination(wrapper, tbody) {
973
- const existingPagination = wrapper.querySelector('.jux-table-pagination');
974
- if (existingPagination)
975
- existingPagination.remove();
976
- let rows = this._getFilteredRows();
977
- rows = this._getSortedRows(rows);
978
- const totalPages = Math.ceil(rows.length / this.state.rowsPerPage);
979
- if (totalPages <= 1)
980
- return;
981
- const pagination = document.createElement('div');
982
- pagination.className = 'jux-table-pagination';
983
- pagination.style.cssText = 'margin-top: 10px; display: flex; gap: 5px; justify-content: center;';
984
- const prevBtn = document.createElement('button');
985
- prevBtn.textContent = 'Previous';
986
- prevBtn.disabled = this.state.currentPage === 1;
987
- prevBtn.addEventListener('click', (e) => {
988
- if (this.state.currentPage > 1) {
989
- const previousPage = this.state.currentPage;
990
- this.state.currentPage--;
991
- this._renderTableBody(tbody);
992
- this._updatePagination(wrapper, tbody);
993
- this._triggerCallback('pageChange', this.state.currentPage, previousPage, e);
994
- }
995
- });
996
- pagination.appendChild(prevBtn);
997
- const pageInfo = document.createElement('span');
998
- pageInfo.textContent = `Page ${this.state.currentPage} of ${totalPages}`;
999
- pageInfo.style.cssText = 'display: flex; align-items: center; padding: 0 10px;';
1000
- pagination.appendChild(pageInfo);
1001
- const nextBtn = document.createElement('button');
1002
- nextBtn.textContent = 'Next';
1003
- nextBtn.disabled = this.state.currentPage === totalPages;
1004
- nextBtn.addEventListener('click', (e) => {
1005
- if (this.state.currentPage < totalPages) {
1006
- const previousPage = this.state.currentPage;
1007
- this.state.currentPage++;
1008
- this._renderTableBody(tbody);
1009
- this._updatePagination(wrapper, tbody);
1010
- this._triggerCallback('pageChange', this.state.currentPage, previousPage, e);
1011
- }
1012
- });
1013
- pagination.appendChild(nextBtn);
1014
- wrapper.appendChild(pagination);
1015
- }
1016
- }
1017
- export function table(id, options = {}) {
1018
- return new Table(id, options);
1019
- }