juxscript 1.1.239 → 1.1.243

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/tag.ts +68 -0
  4. package/lib/styles/shadcn.css +20 -7
  5. package/lib/utils/idgen.ts +6 -0
  6. package/package.json +5 -6
  7. package/index.d.ts +0 -239
  8. package/index.d.ts.map +0 -1
  9. package/lib/components/alert.d.ts +0 -36
  10. package/lib/components/alert.d.ts.map +0 -1
  11. package/lib/components/alert.js +0 -172
  12. package/lib/components/alert.ts +0 -219
  13. package/lib/components/app.d.ts +0 -89
  14. package/lib/components/app.d.ts.map +0 -1
  15. package/lib/components/app.js +0 -175
  16. package/lib/components/app.ts +0 -247
  17. package/lib/components/badge.d.ts +0 -26
  18. package/lib/components/badge.d.ts.map +0 -1
  19. package/lib/components/badge.js +0 -91
  20. package/lib/components/badge.ts +0 -118
  21. package/lib/components/base/Animations.d.ts +0 -36
  22. package/lib/components/base/Animations.d.ts.map +0 -1
  23. package/lib/components/base/Animations.js +0 -70
  24. package/lib/components/base/Animations.ts +0 -112
  25. package/lib/components/base/BaseComponent.d.ts +0 -294
  26. package/lib/components/base/BaseComponent.d.ts.map +0 -1
  27. package/lib/components/base/BaseComponent.js +0 -735
  28. package/lib/components/base/BaseComponent.ts +0 -884
  29. package/lib/components/base/FormInput.d.ts +0 -77
  30. package/lib/components/base/FormInput.d.ts.map +0 -1
  31. package/lib/components/base/FormInput.js +0 -171
  32. package/lib/components/base/FormInput.ts +0 -237
  33. package/lib/components/blueprint.d.ts +0 -40
  34. package/lib/components/blueprint.d.ts.map +0 -1
  35. package/lib/components/blueprint.js +0 -327
  36. package/lib/components/button.d.ts +0 -70
  37. package/lib/components/button.d.ts.map +0 -1
  38. package/lib/components/button.js +0 -177
  39. package/lib/components/button.ts +0 -237
  40. package/lib/components/card.d.ts +0 -35
  41. package/lib/components/card.d.ts.map +0 -1
  42. package/lib/components/card.js +0 -130
  43. package/lib/components/card.ts +0 -177
  44. package/lib/components/chart.d.ts +0 -49
  45. package/lib/components/chart.d.ts.map +0 -1
  46. package/lib/components/chart.js +0 -205
  47. package/lib/components/chart.ts +0 -254
  48. package/lib/components/checkbox.d.ts +0 -33
  49. package/lib/components/checkbox.d.ts.map +0 -1
  50. package/lib/components/checkbox.js +0 -202
  51. package/lib/components/checkbox.ts +0 -260
  52. package/lib/components/code.d.ts +0 -52
  53. package/lib/components/code.d.ts.map +0 -1
  54. package/lib/components/code.js +0 -201
  55. package/lib/components/code.ts +0 -260
  56. package/lib/components/container.d.ts +0 -60
  57. package/lib/components/container.d.ts.map +0 -1
  58. package/lib/components/container.js +0 -195
  59. package/lib/components/container.ts +0 -259
  60. package/lib/components/data.d.ts +0 -36
  61. package/lib/components/data.d.ts.map +0 -1
  62. package/lib/components/data.js +0 -110
  63. package/lib/components/data.ts +0 -135
  64. package/lib/components/dataframe/DataFrameSource.d.ts +0 -118
  65. package/lib/components/dataframe/DataFrameSource.d.ts.map +0 -1
  66. package/lib/components/dataframe/DataFrameSource.js +0 -421
  67. package/lib/components/dataframe/DataFrameSource.ts +0 -532
  68. package/lib/components/dataframe/ImportSettingsModal.d.ts +0 -60
  69. package/lib/components/dataframe/ImportSettingsModal.d.ts.map +0 -1
  70. package/lib/components/dataframe/ImportSettingsModal.js +0 -442
  71. package/lib/components/dataframe/ImportSettingsModal.ts +0 -531
  72. package/lib/components/dataframe.d.ts +0 -110
  73. package/lib/components/dataframe.d.ts.map +0 -1
  74. package/lib/components/dataframe.js +0 -470
  75. package/lib/components/datepicker.d.ts +0 -40
  76. package/lib/components/datepicker.d.ts.map +0 -1
  77. package/lib/components/datepicker.js +0 -193
  78. package/lib/components/datepicker.ts +0 -251
  79. package/lib/components/dialog.d.ts +0 -39
  80. package/lib/components/dialog.d.ts.map +0 -1
  81. package/lib/components/dialog.js +0 -131
  82. package/lib/components/dialog.ts +0 -178
  83. package/lib/components/divider.d.ts +0 -31
  84. package/lib/components/divider.d.ts.map +0 -1
  85. package/lib/components/divider.js +0 -72
  86. package/lib/components/divider.ts +0 -104
  87. package/lib/components/dropdown-menu.d.ts +0 -42
  88. package/lib/components/dropdown-menu.d.ts.map +0 -1
  89. package/lib/components/dropdown-menu.js +0 -177
  90. package/lib/components/dropdown-menu.ts +0 -214
  91. package/lib/components/dropdown.d.ts +0 -41
  92. package/lib/components/dropdown.d.ts.map +0 -1
  93. package/lib/components/dropdown.js +0 -136
  94. package/lib/components/dropdown.ts +0 -188
  95. package/lib/components/element.d.ts +0 -51
  96. package/lib/components/element.d.ts.map +0 -1
  97. package/lib/components/element.js +0 -209
  98. package/lib/components/element.ts +0 -271
  99. package/lib/components/event-chain.d.ts +0 -9
  100. package/lib/components/event-chain.d.ts.map +0 -1
  101. package/lib/components/event-chain.js +0 -33
  102. package/lib/components/fileupload.d.ts +0 -98
  103. package/lib/components/fileupload.d.ts.map +0 -1
  104. package/lib/components/fileupload.js +0 -351
  105. package/lib/components/fileupload.ts +0 -449
  106. package/lib/components/grid.d.ts +0 -88
  107. package/lib/components/grid.d.ts.map +0 -1
  108. package/lib/components/grid.js +0 -208
  109. package/lib/components/grid.ts +0 -295
  110. package/lib/components/heading.d.ts +0 -25
  111. package/lib/components/heading.d.ts.map +0 -1
  112. package/lib/components/heading.js +0 -83
  113. package/lib/components/heading.ts +0 -113
  114. package/lib/components/helpers.d.ts +0 -9
  115. package/lib/components/helpers.d.ts.map +0 -1
  116. package/lib/components/helpers.js +0 -30
  117. package/lib/components/helpers.ts +0 -41
  118. package/lib/components/hero.d.ts +0 -60
  119. package/lib/components/hero.d.ts.map +0 -1
  120. package/lib/components/hero.js +0 -239
  121. package/lib/components/hero.ts +0 -302
  122. package/lib/components/history/StateHistory.d.ts +0 -91
  123. package/lib/components/history/StateHistory.d.ts.map +0 -1
  124. package/lib/components/history/StateHistory.js +0 -154
  125. package/lib/components/history/StateHistory.ts +0 -200
  126. package/lib/components/icon.d.ts +0 -36
  127. package/lib/components/icon.d.ts.map +0 -1
  128. package/lib/components/icon.js +0 -135
  129. package/lib/components/icon.ts +0 -182
  130. package/lib/components/icons.d.ts +0 -25
  131. package/lib/components/icons.d.ts.map +0 -1
  132. package/lib/components/icons.js +0 -440
  133. package/lib/components/icons.ts +0 -464
  134. package/lib/components/image.d.ts +0 -42
  135. package/lib/components/image.d.ts.map +0 -1
  136. package/lib/components/image.js +0 -204
  137. package/lib/components/image.ts +0 -260
  138. package/lib/components/include.d.ts +0 -86
  139. package/lib/components/include.d.ts.map +0 -1
  140. package/lib/components/include.js +0 -238
  141. package/lib/components/include.ts +0 -281
  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
- }