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,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
- }