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,735 +0,0 @@
1
- import { getOrCreateContainer } from '../helpers.js';
2
- import { registry } from '../registry.js';
3
- import { stateHistory } from '../history/StateHistory.js';
4
- import { formatIdAsLabel } from '../../utils/formatId.js';
5
- import { applyAnimations } from './Animations.js'; // ✅ Import Animations
6
- /**
7
- * Abstract base class for all JUX components
8
- * Provides common storage, event routing, and lifecycle methods
9
- *
10
- * Children must provide:
11
- * - TRIGGER_EVENTS constant (readonly string[])
12
- * - CALLBACK_EVENTS constant (readonly string[])
13
- * - render() implementation
14
- */
15
- export class BaseComponent {
16
- constructor(id, initialState) {
17
- this.container = null;
18
- // Event & sync storage (populated by bind() and sync())
19
- this._bindings = [];
20
- this._syncBindings = [];
21
- this._triggerHandlers = new Map();
22
- this._callbackHandlers = new Map();
23
- this._isUpdatingSync = false;
24
- // Form-specific protected properties (only used by form components)
25
- this._inputElement = null;
26
- this._labelElement = null;
27
- this._errorElement = null;
28
- this._hasBeenValidated = false;
29
- this._id = id;
30
- this.id = id;
31
- const stateWithDefaults = {
32
- visible: true,
33
- disabled: false,
34
- loading: false,
35
- class: '',
36
- style: '',
37
- attributes: {},
38
- ...initialState
39
- };
40
- this.state = new Proxy(stateWithDefaults, {
41
- set: (target, prop, value) => {
42
- const key = prop;
43
- const oldValue = target[key];
44
- if (oldValue !== value) {
45
- stateHistory.recordStateChange(this._id, prop, oldValue, value);
46
- target[key] = value;
47
- this.update(prop, value);
48
- if (!this._isUpdatingSync) {
49
- this._notifySyncedState(prop, value);
50
- }
51
- }
52
- return true;
53
- }
54
- });
55
- // ✅ Apply ONLY animation extensions
56
- applyAnimations(this);
57
- }
58
- /**
59
- * REACTIVE UPDATE HOOK (PUBLIC, CONCRETE)
60
- * Called automatically when this.state[prop] changes via Proxy.
61
- * Default implementation handles base properties.
62
- * Children can override to add component-specific logic.
63
- */
64
- update(prop, value) {
65
- if (!this.container)
66
- return;
67
- const el = this.container.querySelector(`#${this._id}`);
68
- if (!el)
69
- return;
70
- // Handle base properties
71
- switch (prop) {
72
- case 'visible':
73
- el.style.display = value ? '' : 'none';
74
- break;
75
- case 'class':
76
- const baseClasses = el.className.split(' ').filter(c => c.startsWith('jux-'));
77
- const userClasses = value.split(' ').filter((c) => c);
78
- el.className = [...baseClasses, ...userClasses].join(' ');
79
- break;
80
- case 'style':
81
- el.setAttribute('style', value);
82
- break;
83
- case 'disabled':
84
- el.setAttribute('aria-disabled', String(value));
85
- const inputs = el.querySelectorAll('input, button, select, textarea');
86
- inputs.forEach(input => {
87
- input.disabled = value;
88
- });
89
- break;
90
- case 'loading':
91
- el.classList.toggle('jux-loading', value);
92
- el.setAttribute('aria-busy', String(value));
93
- break;
94
- }
95
- }
96
- /**
97
- * ✨ Notify external State<T> objects when component state changes
98
- * Prevents infinite loops with guard flag
99
- */
100
- _notifySyncedState(prop, value) {
101
- const syncBinding = this._syncBindings.find(b => b.property === prop);
102
- if (syncBinding) {
103
- const { stateObj, toState } = syncBinding;
104
- // Set guard flag to prevent recursion
105
- this._isUpdatingSync = true;
106
- try {
107
- const transformedValue = toState ? toState(value) : value;
108
- stateObj.set(transformedValue);
109
- }
110
- finally {
111
- // Always clear guard flag
112
- this._isUpdatingSync = false;
113
- }
114
- }
115
- }
116
- /* ═════════════════════════════════════════════════════════════════
117
- * COMMON FLUENT API (Inherited by all components)
118
- * ═════════════════════════════════════════════════════════════════ */
119
- /**
120
- * Set component style
121
- */
122
- style(value) {
123
- this.state.style = value; // ✅ Triggers update()
124
- return this;
125
- }
126
- /**
127
- * Set component class
128
- */
129
- class(value) {
130
- this.state.class = value; // ✅ Triggers update()
131
- return this;
132
- }
133
- /* ═════════════════════════════════════════════════════════════════
134
- * CSS CLASS MANAGEMENT
135
- * ═════════════════════════════════════════════════════════════════ */
136
- /**
137
- * Add a CSS class to the component
138
- */
139
- addClass(value) {
140
- const current = this.state.class || '';
141
- const classes = current.split(' ').filter((c) => c && c !== value);
142
- classes.push(value);
143
- this.state.class = classes.join(' '); // ✅ Triggers update()
144
- return this;
145
- }
146
- /**
147
- * Remove a CSS class from the component
148
- */
149
- removeClass(value) {
150
- const current = this.state.class || '';
151
- const classes = current.split(' ').filter((c) => c && c !== value);
152
- this.state.class = classes.join(' '); // ✅ Triggers update()
153
- return this;
154
- }
155
- /**
156
- * Toggle a CSS class on the component
157
- */
158
- toggleClass(value) {
159
- const current = this.state.class || '';
160
- const hasClass = current.split(' ').includes(value);
161
- return hasClass ? this.removeClass(value) : this.addClass(value);
162
- }
163
- /* ═════════════════════════════════════════════════════════════════
164
- * VISIBILITY CONTROL
165
- * ═════════════════════════════════════════════════════════════════ */
166
- /**
167
- * Set component visibility
168
- */
169
- visible(value) {
170
- this.state.visible = value; // ✅ Triggers update()
171
- return this;
172
- }
173
- /**
174
- * Show the component
175
- */
176
- show() {
177
- return this.visible(true);
178
- }
179
- /**
180
- * Hide the component
181
- */
182
- hide() {
183
- return this.visible(false);
184
- }
185
- /**
186
- * Toggle component visibility
187
- */
188
- toggleVisibility() {
189
- const isVisible = this.state.visible ?? true;
190
- return this.visible(!isVisible);
191
- }
192
- /* ═════════════════════════════════════════════════════════════════
193
- * ATTRIBUTE MANAGEMENT
194
- * ═════════════════════════════════════════════════════════════════ */
195
- /**
196
- * Set a single HTML attribute
197
- */
198
- attr(name, value) {
199
- const attrs = this.state.attributes || {};
200
- this.state.attributes = { ...attrs, [name]: value };
201
- if (this.container)
202
- this.container.setAttribute(name, value);
203
- return this;
204
- }
205
- /**
206
- * Set multiple HTML attributes
207
- */
208
- attrs(attributes) {
209
- Object.entries(attributes).forEach(([name, value]) => {
210
- this.attr(name, value);
211
- });
212
- return this;
213
- }
214
- /**
215
- * Remove an HTML attribute
216
- */
217
- removeAttr(name) {
218
- const attrs = this.state.attributes || {};
219
- delete attrs[name];
220
- if (this.container)
221
- this.container.removeAttribute(name);
222
- return this;
223
- }
224
- /* ═════════════════════════════════════════════════════════════════
225
- * DISABLED STATE
226
- * ═════════════════════════════════════════════════════════════════ */
227
- /**
228
- * Set disabled state for interactive elements
229
- */
230
- disabled(value) {
231
- this.state.disabled = value; // ✅ Triggers update()
232
- return this;
233
- }
234
- /**
235
- * Enable the component
236
- */
237
- enable() {
238
- return this.disabled(false);
239
- }
240
- /**
241
- * Disable the component
242
- */
243
- disable() {
244
- return this.disabled(true);
245
- }
246
- /* ═════════════════════════════════════════════════════════════════
247
- * LOADING STATE
248
- * ═════════════════════════════════════════════════════════════════ */
249
- /**
250
- * Set loading state
251
- */
252
- loading(value) {
253
- this.state.loading = value; // ✅ Triggers update()
254
- return this;
255
- }
256
- /* ═════════════════════════════════════════════════════════════════
257
- * FOCUS MANAGEMENT
258
- * ═════════════════════════════════════════════════════════════════ */
259
- /**
260
- * Focus the first focusable element in the component
261
- */
262
- focus() {
263
- if (this.container) {
264
- const focusable = this.container.querySelector('input, button, select, textarea, [tabindex]');
265
- if (focusable)
266
- focusable.focus();
267
- }
268
- return this;
269
- }
270
- /**
271
- * Blur the currently focused element in the component
272
- */
273
- blur() {
274
- if (this.container) {
275
- const focused = this.container.querySelector(':focus');
276
- if (focused)
277
- focused.blur();
278
- }
279
- return this;
280
- }
281
- /* ═════════════════════════════════════════════════════════════════
282
- * DOM MANIPULATION
283
- * ═════════════════════════════════════════════════════════════════ */
284
- /**
285
- * Remove the component from the DOM
286
- */
287
- remove() {
288
- if (this.container) {
289
- this.container.remove();
290
- this.container = null;
291
- // ✅ Unregister when removed
292
- registry.unregister(this._id);
293
- }
294
- return this;
295
- }
296
- /* ═════════════════════════════════════════════════════════════════
297
- * EVENT BINDING (Shared logic)
298
- * ═════════════════════════════════════════════════════════════════ */
299
- bind(event, handler) {
300
- // ✅ Record bind event
301
- stateHistory.recordEvent(this._id, 'bind', event, { handlerName: handler.name });
302
- if (this._isTriggerEvent(event)) {
303
- this._triggerHandlers.set(event, handler);
304
- }
305
- else if (this._isCallbackEvent(event)) {
306
- this._callbackHandlers.set(event, handler);
307
- }
308
- else {
309
- this._bindings.push({ event, handler });
310
- }
311
- return this;
312
- }
313
- sync(property, stateObj, toStateOrTransform, toComponent) {
314
- if (!stateObj || typeof stateObj.subscribe !== 'function') {
315
- throw new Error(`${this.constructor.name}.sync: Expected a State object for property "${property}"`);
316
- }
317
- const actualToState = (toComponent !== undefined) ? toStateOrTransform : undefined;
318
- const actualToComponent = (toComponent !== undefined) ? toComponent : toStateOrTransform;
319
- // ✅ Record sync event
320
- stateHistory.recordEvent(this._id, 'sync', property, {
321
- hasToState: !!actualToState,
322
- hasToComponent: !!actualToComponent
323
- });
324
- this._syncBindings.push({
325
- property,
326
- stateObj,
327
- toState: actualToState,
328
- toComponent: actualToComponent
329
- });
330
- return this;
331
- }
332
- _isTriggerEvent(event) {
333
- return this.getTriggerEvents().includes(event);
334
- }
335
- _isCallbackEvent(event) {
336
- return this.getCallbackEvents().includes(event);
337
- }
338
- _triggerCallback(eventName, ...args) {
339
- // ✅ Record callback event
340
- stateHistory.recordEvent(this._id, 'callback', eventName, { argsCount: args.length });
341
- if (this._callbackHandlers.has(eventName)) {
342
- const handler = this._callbackHandlers.get(eventName);
343
- handler(...args);
344
- }
345
- }
346
- /* ═════════════════════════════════════════════════════════════════
347
- * FLUENT EVENT SHORTHANDS
348
- * ═════════════════════════════════════════════════════════════════ */
349
- /**
350
- * Register a click event handler
351
- */
352
- click(handler) {
353
- return this.bind('click', handler);
354
- }
355
- /**
356
- * Register a double-click event handler
357
- */
358
- dblclick(handler) {
359
- return this.bind('dblclick', handler);
360
- }
361
- /**
362
- * Register a change event handler
363
- */
364
- change(handler) {
365
- return this.bind('change', handler);
366
- }
367
- /**
368
- * Register an input event handler
369
- */
370
- input(handler) {
371
- return this.bind('input', handler);
372
- }
373
- /**
374
- * Register a mouseover event handler
375
- */
376
- hover(handler) {
377
- return this.bind('mouseover', handler);
378
- }
379
- /**
380
- * Register a mouseout event handler
381
- */
382
- mouseout(handler) {
383
- return this.bind('mouseout', handler);
384
- }
385
- /**
386
- * Register a mousedown event handler
387
- */
388
- mousedown(handler) {
389
- return this.bind('mousedown', handler);
390
- }
391
- /**
392
- * Register a mouseup event handler
393
- */
394
- mouseup(handler) {
395
- return this.bind('mouseup', handler);
396
- }
397
- /**
398
- * Register a focus event handler (renamed to onFocus to avoid conflict with focus() method)
399
- */
400
- onFocus(handler) {
401
- return this.bind('focus', handler);
402
- }
403
- /**
404
- * Register a blur event handler (renamed to onBlur to avoid conflict with blur() method)
405
- */
406
- onBlur(handler) {
407
- return this.bind('blur', handler);
408
- }
409
- /**
410
- * Register a keydown event handler
411
- */
412
- keydown(handler) {
413
- return this.bind('keydown', handler);
414
- }
415
- /**
416
- * Register a keyup event handler
417
- */
418
- keyup(handler) {
419
- return this.bind('keyup', handler);
420
- }
421
- /**
422
- * Register a keypress event handler
423
- */
424
- keypress(handler) {
425
- return this.bind('keypress', handler);
426
- }
427
- /**
428
- * Register a submit event handler
429
- */
430
- submit(handler) {
431
- return this.bind('submit', handler);
432
- }
433
- /**
434
- * Register a scroll event handler
435
- */
436
- scroll(handler) {
437
- return this.bind('scroll', handler);
438
- }
439
- /**
440
- * Register a contextmenu (right-click) event handler
441
- */
442
- contextmenu(handler) {
443
- return this.bind('contextmenu', handler);
444
- }
445
- /* ═════════════════════════════════════════════════════════════════
446
- * TIME-TRAVEL DEBUGGING (PUBLIC API)
447
- * ═════════════════════════════════════════════════════════════════ */
448
- /**
449
- * Roll back to previous state
450
- */
451
- rollback() {
452
- const snapshot = stateHistory.moveBack();
453
- if (snapshot && snapshot.componentId === this._id) {
454
- stateHistory.startReplay();
455
- this.state[snapshot.property] = snapshot.oldValue;
456
- stateHistory.endReplay();
457
- }
458
- return this;
459
- }
460
- /**
461
- * Roll forward to next state
462
- */
463
- rollforward() {
464
- const snapshot = stateHistory.moveForward();
465
- if (snapshot && snapshot.componentId === this._id) {
466
- stateHistory.startReplay();
467
- this.state[snapshot.property] = snapshot.newValue;
468
- stateHistory.endReplay();
469
- }
470
- return this;
471
- }
472
- /**
473
- * Get complete timeline of this component's changes
474
- */
475
- timeline() {
476
- const stateChanges = stateHistory.getComponentHistory(this._id);
477
- const events = stateHistory.getComponentEvents(this._id);
478
- return [...stateChanges, ...events].sort((a, b) => a.timestamp - b.timestamp);
479
- }
480
- /**
481
- * Get only state changes for this component
482
- */
483
- stateHistory() {
484
- return stateHistory.getComponentHistory(this._id);
485
- }
486
- /**
487
- * Get only events for this component
488
- */
489
- eventHistory() {
490
- return stateHistory.getComponentEvents(this._id);
491
- }
492
- /* ═════════════════════════════════════════════════════════════════
493
- * COMMON RENDER HELPERS
494
- * ═════════════════════════════════════════════════════════════════ */
495
- _setupContainer(targetId) {
496
- // Determine Target Container Type
497
- let container;
498
- if (targetId) {
499
- if (typeof targetId === 'string') {
500
- const id = targetId.startsWith('#') ? targetId.slice(1) : targetId;
501
- const target = document.getElementById(id);
502
- if (target) {
503
- container = target;
504
- }
505
- else {
506
- console.warn(`[Jux] Target "${targetId}" not found, creating it with graceful fallback`);
507
- container = getOrCreateContainer(id);
508
- }
509
- }
510
- else if (targetId instanceof HTMLElement) {
511
- container = targetId;
512
- }
513
- else if (targetId instanceof BaseComponent) {
514
- container = getOrCreateContainer(targetId._id);
515
- }
516
- else {
517
- throw new Error(`[Jux] Invalid targetId type: ${typeof targetId}`);
518
- }
519
- }
520
- else {
521
- container = getOrCreateContainer(this._id);
522
- }
523
- this.container = container;
524
- // ✅ Auto-register component when container is set up
525
- registry.register(this);
526
- return container;
527
- }
528
- _wireStandardEvents(element) {
529
- this._bindings.forEach(({ event, handler }) => {
530
- element.addEventListener(event, handler);
531
- });
532
- }
533
- /**
534
- * Wire sync subscriptions with guard flag
535
- */
536
- _wireAllSyncs() {
537
- this._syncBindings.forEach(({ property, stateObj, toComponent }) => {
538
- const transform = toComponent || ((v) => v);
539
- const method = this[property];
540
- if (typeof method === 'function') {
541
- // Set initial value
542
- const initialValue = transform(stateObj.value);
543
- this._isUpdatingSync = true;
544
- method.call(this, initialValue);
545
- this._isUpdatingSync = false;
546
- // Subscribe to changes with guard
547
- stateObj.subscribe((val) => {
548
- if (this._isUpdatingSync)
549
- return;
550
- const transformed = transform(val);
551
- this._isUpdatingSync = true;
552
- method.call(this, transformed);
553
- this._isUpdatingSync = false;
554
- });
555
- }
556
- else {
557
- console.warn(`[Jux] ${this.constructor.name}.sync('${property}'): ` +
558
- `No method .${property}() found. Property will not be synced.`);
559
- }
560
- });
561
- }
562
- /* ═════════════════════════════════════════════════════════════════
563
- * PROPS ACCESSOR - Read-only access to component state
564
- * ═════════════════════════════════════════════════════════════════ */
565
- /**
566
- * ✅ Read-only accessor for component state (getter, not a method)
567
- *
568
- * Usage:
569
- * const myState = component.props; // ✅ Correct
570
- * const myState = component.props(); // ❌ Error: props is not a function
571
- */
572
- get props() {
573
- return this.state;
574
- }
575
- /* ═════════════════════════════════════════════════════════════════
576
- * FORM INPUT API (Optional - only used by form components)
577
- * ═════════════════════════════════════════════════════════════════ */
578
- /**
579
- * Set label for form inputs
580
- */
581
- label(value) {
582
- this.state.label = value;
583
- if (this._labelElement) {
584
- const requiredSpan = this._labelElement.querySelector('.jux-input-required');
585
- this._labelElement.textContent = value;
586
- if (requiredSpan)
587
- this._labelElement.appendChild(requiredSpan);
588
- }
589
- return this;
590
- }
591
- /**
592
- * Set required state for form inputs
593
- */
594
- required(value) {
595
- this.state.required = value;
596
- return this;
597
- }
598
- /**
599
- * Set name attribute for form inputs
600
- */
601
- name(value) {
602
- this.state.name = value;
603
- return this;
604
- }
605
- /**
606
- * Set custom validation handler
607
- */
608
- onValidate(handler) {
609
- this._onValidate = handler;
610
- return this;
611
- }
612
- /**
613
- * Validate form input (override in form components)
614
- */
615
- validate() {
616
- console.warn(`${this.constructor.name}.validate() not implemented`);
617
- return true;
618
- }
619
- /**
620
- * Check if form input is valid (override in form components)
621
- */
622
- isValid() {
623
- console.warn(`${this.constructor.name}.isValid() not implemented`);
624
- return true;
625
- }
626
- /**
627
- * Get current value (override in form components)
628
- */
629
- getValue() {
630
- console.warn(`${this.constructor.name}.getValue() not implemented`);
631
- return undefined;
632
- }
633
- /**
634
- * Set current value (override in form components)
635
- */
636
- setValue(value) {
637
- console.warn(`${this.constructor.name}.setValue() not implemented`);
638
- return this;
639
- }
640
- /* ═════════════════════════════════════════════════════════════════
641
- * FORM VALIDATION HELPERS (Protected - for form components)
642
- * ═════════════════════════════════════════════════════════════════ */
643
- _showError(message) {
644
- if (this._errorElement) {
645
- this._errorElement.textContent = message;
646
- this._errorElement.style.display = 'block';
647
- }
648
- if (this._inputElement) {
649
- this._inputElement.classList.add('jux-input-invalid');
650
- }
651
- this.state.errorMessage = message;
652
- }
653
- _clearError() {
654
- if (this._errorElement) {
655
- this._errorElement.textContent = '';
656
- this._errorElement.style.display = 'none';
657
- }
658
- if (this._inputElement) {
659
- this._inputElement.classList.remove('jux-input-invalid');
660
- }
661
- this.state.errorMessage = undefined;
662
- }
663
- /**
664
- * Build label element for form inputs
665
- */
666
- _renderLabel() {
667
- const label = this.state.label || formatIdAsLabel(this._id); // ✅ Auto-generate
668
- const required = this.state.required || false;
669
- const labelEl = document.createElement('label');
670
- labelEl.className = 'jux-input-label';
671
- labelEl.htmlFor = `${this._id}-input`;
672
- labelEl.textContent = label;
673
- if (required) {
674
- const requiredSpan = document.createElement('span');
675
- requiredSpan.className = 'jux-input-required';
676
- requiredSpan.textContent = ' *';
677
- labelEl.appendChild(requiredSpan);
678
- }
679
- this._labelElement = labelEl;
680
- return labelEl;
681
- }
682
- /**
683
- * Build error element for form inputs
684
- */
685
- _renderError() {
686
- const errorEl = document.createElement('div');
687
- errorEl.className = 'jux-input-error';
688
- errorEl.id = `${this._id}-error`;
689
- errorEl.style.display = 'none';
690
- this._errorElement = errorEl;
691
- return errorEl;
692
- }
693
- /**
694
- * Wire up two-way sync for form inputs
695
- */
696
- _wireFormSync(inputElement, eventName = 'input') {
697
- const valueSync = this._syncBindings.find(b => b.property === 'value');
698
- if (valueSync) {
699
- const { stateObj, toState, toComponent } = valueSync;
700
- const transformToState = toState || ((v) => v);
701
- const transformToComponent = toComponent || ((v) => v);
702
- let isUpdating = false;
703
- // State → Component
704
- stateObj.subscribe((val) => {
705
- if (isUpdating)
706
- return;
707
- const transformed = transformToComponent(val);
708
- this.setValue(transformed);
709
- });
710
- // Component → State
711
- inputElement.addEventListener(eventName, () => {
712
- if (isUpdating)
713
- return;
714
- isUpdating = true;
715
- const value = this.getValue();
716
- const transformed = transformToState(value);
717
- this._clearError();
718
- stateObj.set(transformed);
719
- setTimeout(() => { isUpdating = false; }, 0);
720
- });
721
- }
722
- else {
723
- // Default behavior without sync
724
- inputElement.addEventListener(eventName, () => {
725
- this._clearError();
726
- });
727
- }
728
- // Only validate on blur IF the field has been validated before
729
- inputElement.addEventListener('blur', () => {
730
- if (this._hasBeenValidated) {
731
- this.validate();
732
- }
733
- });
734
- }
735
- }