juxscript 1.1.4 → 1.1.6

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 (205) hide show
  1. package/index.d.ts +10 -10
  2. package/index.d.ts.map +1 -0
  3. package/lib/components/alert.d.ts +32 -0
  4. package/lib/components/alert.d.ts.map +1 -0
  5. package/lib/components/alert.js +153 -0
  6. package/lib/components/alert.ts +200 -0
  7. package/lib/components/app.d.ts +89 -0
  8. package/lib/components/app.d.ts.map +1 -0
  9. package/lib/components/app.js +175 -0
  10. package/lib/components/app.ts +247 -0
  11. package/lib/components/badge.d.ts +27 -0
  12. package/lib/components/badge.d.ts.map +1 -0
  13. package/lib/components/badge.js +70 -0
  14. package/lib/components/badge.ts +101 -0
  15. package/lib/components/base/BaseComponent.d.ts +142 -0
  16. package/lib/components/base/BaseComponent.d.ts.map +1 -0
  17. package/lib/components/base/BaseComponent.js +363 -0
  18. package/lib/components/base/BaseComponent.ts +421 -0
  19. package/lib/components/base/FormInput.d.ts +73 -0
  20. package/lib/components/base/FormInput.d.ts.map +1 -0
  21. package/lib/components/base/FormInput.js +163 -0
  22. package/lib/components/base/FormInput.ts +227 -0
  23. package/lib/components/button.d.ts +48 -0
  24. package/lib/components/button.d.ts.map +1 -0
  25. package/lib/components/button.js +121 -0
  26. package/lib/components/button.ts +178 -0
  27. package/lib/components/card.d.ts +34 -0
  28. package/lib/components/card.d.ts.map +1 -0
  29. package/lib/components/card.js +127 -0
  30. package/lib/components/card.ts +173 -0
  31. package/lib/components/chart.d.ts +45 -0
  32. package/lib/components/chart.d.ts.map +1 -0
  33. package/lib/components/chart.js +186 -0
  34. package/lib/components/chart.ts +231 -0
  35. package/lib/components/checkbox.d.ts +31 -0
  36. package/lib/components/checkbox.d.ts.map +1 -0
  37. package/lib/components/checkbox.js +185 -0
  38. package/lib/components/checkbox.ts +242 -0
  39. package/lib/components/code.d.ts +24 -0
  40. package/lib/components/code.d.ts.map +1 -0
  41. package/lib/components/code.js +88 -0
  42. package/lib/components/code.ts +123 -0
  43. package/lib/components/container.d.ts +42 -0
  44. package/lib/components/container.d.ts.map +1 -0
  45. package/lib/components/container.js +93 -0
  46. package/lib/components/container.ts +140 -0
  47. package/lib/components/data.d.ts +36 -0
  48. package/lib/components/data.d.ts.map +1 -0
  49. package/lib/components/data.js +110 -0
  50. package/lib/components/data.ts +135 -0
  51. package/lib/components/datepicker.d.ts +38 -0
  52. package/lib/components/datepicker.d.ts.map +1 -0
  53. package/lib/components/datepicker.js +177 -0
  54. package/lib/components/datepicker.ts +234 -0
  55. package/lib/components/dialog.d.ts +38 -0
  56. package/lib/components/dialog.d.ts.map +1 -0
  57. package/lib/components/dialog.js +126 -0
  58. package/lib/components/dialog.ts +172 -0
  59. package/lib/components/divider.d.ts +30 -0
  60. package/lib/components/divider.d.ts.map +1 -0
  61. package/lib/components/divider.js +69 -0
  62. package/lib/components/divider.ts +100 -0
  63. package/lib/components/dropdown.d.ts +39 -0
  64. package/lib/components/dropdown.d.ts.map +1 -0
  65. package/lib/components/dropdown.js +133 -0
  66. package/lib/components/dropdown.ts +186 -0
  67. package/lib/components/element.d.ts +50 -0
  68. package/lib/components/element.d.ts.map +1 -0
  69. package/lib/components/element.js +206 -0
  70. package/lib/components/element.ts +267 -0
  71. package/lib/components/fileupload.d.ts +40 -0
  72. package/lib/components/fileupload.d.ts.map +1 -0
  73. package/lib/components/fileupload.js +241 -0
  74. package/lib/components/fileupload.ts +309 -0
  75. package/lib/components/grid.d.ts +87 -0
  76. package/lib/components/grid.d.ts.map +1 -0
  77. package/lib/components/grid.js +205 -0
  78. package/lib/components/grid.ts +291 -0
  79. package/lib/components/guard.d.ts +41 -0
  80. package/lib/components/guard.d.ts.map +1 -0
  81. package/lib/components/guard.js +56 -0
  82. package/lib/components/guard.ts +92 -0
  83. package/lib/components/heading.d.ts +24 -0
  84. package/lib/components/heading.d.ts.map +1 -0
  85. package/lib/components/heading.js +67 -0
  86. package/lib/components/heading.ts +96 -0
  87. package/lib/components/helpers.d.ts +9 -0
  88. package/lib/components/helpers.d.ts.map +1 -0
  89. package/lib/components/helpers.js +30 -0
  90. package/lib/components/helpers.ts +41 -0
  91. package/lib/components/hero.d.ts +45 -0
  92. package/lib/components/hero.d.ts.map +1 -0
  93. package/lib/components/hero.js +165 -0
  94. package/lib/components/hero.ts +224 -0
  95. package/lib/components/icon.d.ts +35 -0
  96. package/lib/components/icon.d.ts.map +1 -0
  97. package/lib/components/icon.js +132 -0
  98. package/lib/components/icon.ts +178 -0
  99. package/lib/components/icons.d.ts +25 -0
  100. package/lib/components/icons.d.ts.map +1 -0
  101. package/lib/components/icons.js +440 -0
  102. package/lib/components/icons.ts +464 -0
  103. package/lib/components/include.d.ts +120 -0
  104. package/lib/components/include.d.ts.map +1 -0
  105. package/lib/components/include.js +350 -0
  106. package/lib/components/include.ts +410 -0
  107. package/lib/components/input.d.ts +83 -0
  108. package/lib/components/input.d.ts.map +1 -0
  109. package/lib/components/input.js +348 -0
  110. package/lib/components/input.ts +457 -0
  111. package/lib/components/list.d.ts +82 -0
  112. package/lib/components/list.d.ts.map +1 -0
  113. package/lib/components/list.js +311 -0
  114. package/lib/components/list.ts +419 -0
  115. package/lib/components/loading.d.ts +24 -0
  116. package/lib/components/loading.d.ts.map +1 -0
  117. package/lib/components/loading.js +73 -0
  118. package/lib/components/loading.ts +100 -0
  119. package/lib/components/menu.d.ts +37 -0
  120. package/lib/components/menu.d.ts.map +1 -0
  121. package/lib/components/menu.js +202 -0
  122. package/lib/components/menu.ts +275 -0
  123. package/lib/components/modal.d.ts +51 -0
  124. package/lib/components/modal.d.ts.map +1 -0
  125. package/lib/components/modal.js +227 -0
  126. package/lib/components/modal.ts +284 -0
  127. package/lib/components/nav.d.ts +45 -0
  128. package/lib/components/nav.d.ts.map +1 -0
  129. package/lib/components/nav.js +190 -0
  130. package/lib/components/nav.ts +257 -0
  131. package/lib/components/paragraph.d.ts +21 -0
  132. package/lib/components/paragraph.d.ts.map +1 -0
  133. package/lib/components/paragraph.js +70 -0
  134. package/lib/components/paragraph.ts +97 -0
  135. package/lib/components/progress.d.ts +39 -0
  136. package/lib/components/progress.d.ts.map +1 -0
  137. package/lib/components/progress.js +113 -0
  138. package/lib/components/progress.ts +159 -0
  139. package/lib/components/radio.d.ts +41 -0
  140. package/lib/components/radio.d.ts.map +1 -0
  141. package/lib/components/radio.js +203 -0
  142. package/lib/components/radio.ts +278 -0
  143. package/lib/components/req.d.ts +155 -0
  144. package/lib/components/req.d.ts.map +1 -0
  145. package/lib/components/req.js +253 -0
  146. package/lib/components/req.ts +303 -0
  147. package/lib/components/script.d.ts +14 -0
  148. package/lib/components/script.d.ts.map +1 -0
  149. package/lib/components/script.js +33 -0
  150. package/lib/components/script.ts +41 -0
  151. package/lib/components/select.d.ts +40 -0
  152. package/lib/components/select.d.ts.map +1 -0
  153. package/lib/components/select.js +183 -0
  154. package/lib/components/select.ts +252 -0
  155. package/lib/components/sidebar.d.ts +48 -0
  156. package/lib/components/sidebar.d.ts.map +1 -0
  157. package/lib/components/sidebar.js +207 -0
  158. package/lib/components/sidebar.ts +275 -0
  159. package/lib/components/style.d.ts +14 -0
  160. package/lib/components/style.d.ts.map +1 -0
  161. package/lib/components/style.js +33 -0
  162. package/lib/components/style.ts +41 -0
  163. package/lib/components/switch.d.ts +32 -0
  164. package/lib/components/switch.d.ts.map +1 -0
  165. package/lib/components/switch.js +186 -0
  166. package/lib/components/switch.ts +246 -0
  167. package/lib/components/table.d.ts +137 -0
  168. package/lib/components/table.d.ts.map +1 -0
  169. package/lib/components/table.js +1045 -0
  170. package/lib/components/table.ts +1249 -0
  171. package/lib/components/tabs.d.ts +36 -0
  172. package/lib/components/tabs.d.ts.map +1 -0
  173. package/lib/components/tabs.js +198 -0
  174. package/lib/components/tabs.ts +250 -0
  175. package/lib/components/theme-toggle.d.ts +44 -0
  176. package/lib/components/theme-toggle.d.ts.map +1 -0
  177. package/lib/components/theme-toggle.js +215 -0
  178. package/lib/components/theme-toggle.ts +293 -0
  179. package/lib/components/tooltip.d.ts +30 -0
  180. package/lib/components/tooltip.d.ts.map +1 -0
  181. package/lib/components/tooltip.js +109 -0
  182. package/lib/components/tooltip.ts +144 -0
  183. package/lib/components/view.d.ts +48 -0
  184. package/lib/components/view.d.ts.map +1 -0
  185. package/lib/components/view.js +149 -0
  186. package/lib/components/view.ts +190 -0
  187. package/lib/components/write.d.ts +107 -0
  188. package/lib/components/write.d.ts.map +1 -0
  189. package/lib/components/write.js +222 -0
  190. package/lib/components/write.ts +272 -0
  191. package/lib/layouts/default.css +260 -0
  192. package/lib/layouts/figma.css +334 -0
  193. package/lib/reactivity/state.d.ts +36 -0
  194. package/lib/reactivity/state.d.ts.map +1 -0
  195. package/lib/reactivity/state.js +67 -0
  196. package/lib/reactivity/state.ts +78 -0
  197. package/lib/utils/fetch.d.ts +176 -0
  198. package/lib/utils/fetch.d.ts.map +1 -0
  199. package/lib/utils/fetch.js +427 -0
  200. package/lib/utils/fetch.ts +553 -0
  201. package/machinery/compiler3.js +78 -0
  202. package/machinery/doc-generator.js +136 -0
  203. package/machinery/imports.js +155 -0
  204. package/machinery/ts-shim.js +46 -0
  205. package/package.json +9 -15
@@ -0,0 +1,231 @@
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+ import { Chart, ChartConfiguration, ChartType, ChartOptions, ChartData } from 'chart.js/auto';
3
+
4
+ // filepath: /Users/timkerr/newprojects2025/compile-sqljs/packages/jux/lib/components/chart.ts
5
+
6
+ export interface ChartState {
7
+ type: ChartType;
8
+ data: ChartData;
9
+ options: ChartOptions;
10
+ style?: string;
11
+ class?: string;
12
+ visible?: boolean;
13
+ disabled?: boolean;
14
+ loading?: boolean;
15
+ }
16
+
17
+ /**
18
+ * Chart component using Chart.js
19
+ * Provides fluent API for creating and configuring charts
20
+ */
21
+ export class ChartComponent extends BaseComponent<ChartState> {
22
+ private static readonly TRIGGER_EVENTS: readonly string[] = [];
23
+ private static readonly CALLBACK_EVENTS: readonly string[] = ['render', 'update', 'destroy'];
24
+ private chartInstance: Chart | null = null;
25
+
26
+ constructor(id: string, type: ChartType = 'bar', options?: Partial<ChartState>) {
27
+ super(id, {
28
+ type,
29
+ data: { datasets: [] },
30
+ options: {},
31
+ ...options
32
+ });
33
+ }
34
+
35
+ protected getTriggerEvents(): readonly string[] {
36
+ return ChartComponent.TRIGGER_EVENTS;
37
+ }
38
+
39
+ protected getCallbackEvents(): readonly string[] {
40
+ return ChartComponent.CALLBACK_EVENTS;
41
+ }
42
+
43
+ /* ═════════════════════════════════════════════════════════════════
44
+ * CHART TYPE
45
+ * ═════════════════════════════════════════════════════════════════ */
46
+
47
+ type(value: ChartType): this {
48
+ this.state.type = value;
49
+ if (this.chartInstance) {
50
+ // Destroy and recreate chart with new type
51
+ const canvas = this.chartInstance.canvas;
52
+ this.chartInstance.destroy();
53
+ const config: ChartConfiguration = {
54
+ type: this.state.type,
55
+ data: this.state.data,
56
+ options: this.state.options
57
+ };
58
+ this.chartInstance = new Chart(canvas, config);
59
+ }
60
+ return this;
61
+ }
62
+
63
+ /* ═════════════════════════════════════════════════════════════════
64
+ * DATA CONFIGURATION
65
+ * ═════════════════════════════════════════════════════════════════ */
66
+
67
+ data(value: ChartData): this {
68
+ this.state.data = value;
69
+ if (this.chartInstance) {
70
+ this.chartInstance.data = value;
71
+ this.chartInstance.update();
72
+ }
73
+ return this;
74
+ }
75
+
76
+ labels(value: string[]): this {
77
+ this.state.data.labels = value;
78
+ if (this.chartInstance) {
79
+ this.chartInstance.data.labels = value;
80
+ this.chartInstance.update();
81
+ }
82
+ return this;
83
+ }
84
+
85
+ datasets(value: any[]): this {
86
+ this.state.data.datasets = value;
87
+ if (this.chartInstance) {
88
+ this.chartInstance.data.datasets = value;
89
+ this.chartInstance.update();
90
+ }
91
+ return this;
92
+ }
93
+
94
+ addDataset(dataset: any): this {
95
+ this.state.data.datasets.push(dataset);
96
+ if (this.chartInstance) {
97
+ this.chartInstance.data.datasets.push(dataset);
98
+ this.chartInstance.update();
99
+ }
100
+ return this;
101
+ }
102
+
103
+ /* ═════════════════════════════════════════════════════════════════
104
+ * OPTIONS CONFIGURATION
105
+ * ═════════════════════════════════════════════════════════════════ */
106
+
107
+ options(value: ChartOptions): this {
108
+ this.state.options = { ...this.state.options, ...value };
109
+ if (this.chartInstance) {
110
+ this.chartInstance.options = this.state.options;
111
+ this.chartInstance.update();
112
+ }
113
+ return this;
114
+ }
115
+
116
+ responsive(value: boolean = true): this {
117
+ return this.options({ responsive: value });
118
+ }
119
+
120
+ maintainAspectRatio(value: boolean = true): this {
121
+ return this.options({ maintainAspectRatio: value });
122
+ }
123
+
124
+ title(text: string, display: boolean = true): this {
125
+ return this.options({
126
+ plugins: {
127
+ ...this.state.options.plugins,
128
+ title: { display, text }
129
+ }
130
+ });
131
+ }
132
+
133
+ legend(display: boolean = true, position: 'top' | 'bottom' | 'left' | 'right' = 'top'): this {
134
+ return this.options({
135
+ plugins: {
136
+ ...this.state.options.plugins,
137
+ legend: { display, position }
138
+ }
139
+ });
140
+ }
141
+
142
+ tooltip(enabled: boolean = true): this {
143
+ return this.options({
144
+ plugins: {
145
+ ...this.state.options.plugins,
146
+ tooltip: { enabled }
147
+ }
148
+ });
149
+ }
150
+
151
+ /* ═════════════════════════════════════════════════════════════════
152
+ * CHART OPERATIONS
153
+ * ═════════════════════════════════════════════════════════════════ */
154
+
155
+ update(mode?: 'resize' | 'reset' | 'none' | 'hide' | 'show' | 'default'): this {
156
+ if (this.chartInstance) {
157
+ this.chartInstance.update(mode);
158
+ this._triggerCallback('update', this.chartInstance);
159
+ }
160
+ return this;
161
+ }
162
+
163
+ reset(): this {
164
+ if (this.chartInstance) {
165
+ this.chartInstance.reset();
166
+ }
167
+ return this;
168
+ }
169
+
170
+ destroy(): this {
171
+ if (this.chartInstance) {
172
+ this.chartInstance.destroy();
173
+ this.chartInstance = null;
174
+ this._triggerCallback('destroy');
175
+ }
176
+ return this;
177
+ }
178
+
179
+ getChart(): Chart | null {
180
+ return this.chartInstance;
181
+ }
182
+
183
+ /* ═════════════════════════════════════════════════════════════════
184
+ * RENDER
185
+ * ═════════════════════════════════════════════════════════════════ */
186
+
187
+ render(targetId?: string): this {
188
+ const container = this._setupContainer(targetId);
189
+
190
+ // Destroy existing chart
191
+ if (this.chartInstance) {
192
+ this.chartInstance.destroy();
193
+ }
194
+
195
+ // Clear container and create canvas
196
+ container.innerHTML = '';
197
+ const canvas = document.createElement('canvas');
198
+ canvas.id = `${this._id}-canvas`;
199
+
200
+ // Apply styles and classes
201
+ if (this.state.style) container.setAttribute('style', this.state.style);
202
+ if (this.state.class) container.className = this.state.class;
203
+ if (this.state.visible === false) container.style.display = 'none';
204
+
205
+ container.appendChild(canvas);
206
+
207
+ // Create Chart.js instance
208
+ const config: ChartConfiguration = {
209
+ type: this.state.type,
210
+ data: this.state.data,
211
+ options: this.state.options
212
+ };
213
+
214
+ this.chartInstance = new Chart(canvas, config);
215
+
216
+ // Wire events and syncs
217
+ this._wireStandardEvents(container);
218
+ this._wireAllSyncs();
219
+
220
+ this._triggerCallback('render', this.chartInstance);
221
+
222
+ return this;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Factory function for creating chart components
228
+ */
229
+ export function chart(id: string, type: ChartType = 'bar', options?: Partial<ChartState>): ChartComponent {
230
+ return new ChartComponent(id, type, options);
231
+ }
@@ -0,0 +1,31 @@
1
+ import { FormInput, FormInputState } from './base/FormInput.js';
2
+ export interface CheckboxOptions {
3
+ checked?: boolean;
4
+ label?: string;
5
+ required?: boolean;
6
+ disabled?: boolean;
7
+ name?: string;
8
+ value?: string;
9
+ style?: string;
10
+ class?: string;
11
+ onValidate?: (checked: boolean) => boolean | string;
12
+ }
13
+ interface CheckboxState extends FormInputState {
14
+ checked: boolean;
15
+ value: string;
16
+ }
17
+ export declare class Checkbox extends FormInput<CheckboxState> {
18
+ constructor(id: string, options?: CheckboxOptions);
19
+ protected getTriggerEvents(): readonly string[];
20
+ protected getCallbackEvents(): readonly string[];
21
+ checked(value: boolean): this;
22
+ value(value: string): this;
23
+ getValue(): boolean;
24
+ setValue(value: boolean): this;
25
+ protected _validateValue(checked: boolean): boolean | string;
26
+ protected _buildInputElement(): HTMLElement;
27
+ render(targetId?: string): this;
28
+ }
29
+ export declare function checkbox(id: string, options?: CheckboxOptions): Checkbox;
30
+ export {};
31
+ //# sourceMappingURL=checkbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAOhE,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAAC;CACvD;AAED,UAAU,aAAc,SAAQ,cAAc;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,QAAS,SAAQ,SAAS,CAAC,aAAa,CAAC;gBACtC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;IAkBrD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAehD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI7B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAS1B,QAAQ,IAAI,OAAO;IAInB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAQ9B,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM;IAiB5D,SAAS,CAAC,kBAAkB,IAAI,WAAW;IAoB3C,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;CAiHlC;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAE5E"}
@@ -0,0 +1,185 @@
1
+ import { FormInput } from './base/FormInput.js';
2
+ import { renderIcon } from './icons.js';
3
+ // Event definitions
4
+ const TRIGGER_EVENTS = [];
5
+ const CALLBACK_EVENTS = ['change'];
6
+ export class Checkbox extends FormInput {
7
+ constructor(id, options = {}) {
8
+ super(id, {
9
+ checked: options.checked ?? false,
10
+ value: options.value ?? 'on',
11
+ label: options.label ?? '',
12
+ required: options.required ?? false,
13
+ disabled: options.disabled ?? false,
14
+ name: options.name ?? id,
15
+ style: options.style ?? '',
16
+ class: options.class ?? '',
17
+ errorMessage: undefined
18
+ });
19
+ if (options.onValidate) {
20
+ this._onValidate = options.onValidate;
21
+ }
22
+ }
23
+ getTriggerEvents() {
24
+ return TRIGGER_EVENTS;
25
+ }
26
+ getCallbackEvents() {
27
+ return CALLBACK_EVENTS;
28
+ }
29
+ /* ═════════════════════════════════════════════════════════════════
30
+ * FLUENT API
31
+ * ═════════════════════════════════════════════════════════════════ */
32
+ // ✅ Inherited from FormInput/BaseComponent:
33
+ // - label(), required(), name(), onValidate()
34
+ // - validate(), isValid()
35
+ // - style(), class()
36
+ // - bind(), sync(), renderTo()
37
+ // - disabled(), enable(), disable()
38
+ checked(value) {
39
+ return this.setValue(value);
40
+ }
41
+ value(value) {
42
+ this.state.value = value;
43
+ return this;
44
+ }
45
+ /* ═════════════════════════════════════════════════════════════════
46
+ * FORM INPUT IMPLEMENTATION
47
+ * ═════════════════════════════════════════════════════════════════ */
48
+ getValue() {
49
+ return this.state.checked;
50
+ }
51
+ setValue(value) {
52
+ this.state.checked = value;
53
+ if (this._inputElement) {
54
+ this._inputElement.checked = value;
55
+ }
56
+ return this;
57
+ }
58
+ _validateValue(checked) {
59
+ const { required } = this.state;
60
+ if (required && !checked) {
61
+ return 'This field must be checked';
62
+ }
63
+ if (this._onValidate) {
64
+ const result = this._onValidate(checked);
65
+ if (result !== true) {
66
+ return result || 'Invalid value';
67
+ }
68
+ }
69
+ return true;
70
+ }
71
+ _buildInputElement() {
72
+ const { checked, value, required, disabled, name } = this.state;
73
+ const input = document.createElement('input');
74
+ input.type = 'checkbox';
75
+ input.className = 'jux-checkbox-input';
76
+ input.id = `${this._id}-input`;
77
+ input.name = name;
78
+ input.value = value;
79
+ input.checked = checked;
80
+ input.required = required;
81
+ input.disabled = disabled;
82
+ return input;
83
+ }
84
+ /* ═════════════════════════════════════════════════════════════════
85
+ * RENDER
86
+ * ═════════════════════════════════════════════════════════════════ */
87
+ render(targetId) {
88
+ const container = this._setupContainer(targetId);
89
+ const { style, class: className } = this.state;
90
+ // Build wrapper
91
+ const wrapper = document.createElement('div');
92
+ wrapper.className = 'jux-checkbox';
93
+ wrapper.id = this._id;
94
+ if (className)
95
+ wrapper.className += ` ${className}`;
96
+ if (style)
97
+ wrapper.setAttribute('style', style);
98
+ // Checkbox container
99
+ const checkboxContainer = document.createElement('label');
100
+ checkboxContainer.className = 'jux-checkbox-container';
101
+ checkboxContainer.htmlFor = `${this._id}-input`;
102
+ // Input element
103
+ const inputEl = this._buildInputElement();
104
+ this._inputElement = inputEl;
105
+ checkboxContainer.appendChild(inputEl);
106
+ // Checkmark (custom styled)
107
+ const checkmark = document.createElement('span');
108
+ checkmark.className = 'jux-checkbox-checkmark';
109
+ checkmark.appendChild(renderIcon('check'));
110
+ checkboxContainer.appendChild(checkmark);
111
+ // Label text
112
+ if (this.state.label) {
113
+ const labelText = document.createElement('span');
114
+ labelText.className = 'jux-checkbox-label-text';
115
+ labelText.textContent = this.state.label;
116
+ if (this.state.required) {
117
+ const requiredSpan = document.createElement('span');
118
+ requiredSpan.className = 'jux-input-required';
119
+ requiredSpan.textContent = ' *';
120
+ labelText.appendChild(requiredSpan);
121
+ }
122
+ checkboxContainer.appendChild(labelText);
123
+ }
124
+ wrapper.appendChild(checkboxContainer);
125
+ // Error element
126
+ wrapper.appendChild(this._renderError());
127
+ // Wire events
128
+ this._wireStandardEvents(wrapper);
129
+ // Wire checkbox-specific sync (maps 'checked' to 'value' property for consistency)
130
+ const valueSync = this._syncBindings.find(b => b.property === 'value' || b.property === 'checked');
131
+ if (valueSync) {
132
+ const { stateObj, toState, toComponent } = valueSync;
133
+ const transformToState = toState || ((v) => v);
134
+ const transformToComponent = toComponent || ((v) => Boolean(v));
135
+ let isUpdating = false;
136
+ // State → Component
137
+ stateObj.subscribe((val) => {
138
+ if (isUpdating)
139
+ return;
140
+ const transformed = transformToComponent(val);
141
+ this.setValue(transformed);
142
+ });
143
+ // Component → State
144
+ inputEl.addEventListener('change', () => {
145
+ if (isUpdating)
146
+ return;
147
+ isUpdating = true;
148
+ const checked = inputEl.checked;
149
+ this.state.checked = checked;
150
+ this._clearError();
151
+ const transformed = transformToState(checked);
152
+ stateObj.set(transformed);
153
+ // 🎯 Fire the change callback event
154
+ this._triggerCallback('change', checked);
155
+ setTimeout(() => { isUpdating = false; }, 0);
156
+ });
157
+ }
158
+ else {
159
+ // Default behavior without sync
160
+ inputEl.addEventListener('change', () => {
161
+ this.state.checked = inputEl.checked;
162
+ this._clearError();
163
+ // 🎯 Fire the change callback event
164
+ this._triggerCallback('change', inputEl.checked);
165
+ });
166
+ }
167
+ // Always add blur validation
168
+ inputEl.addEventListener('blur', () => {
169
+ this.validate();
170
+ });
171
+ // Sync label changes
172
+ const labelSync = this._syncBindings.find(b => b.property === 'label');
173
+ if (labelSync) {
174
+ const transform = labelSync.toComponent || ((v) => String(v));
175
+ labelSync.stateObj.subscribe((val) => {
176
+ this.label(transform(val));
177
+ });
178
+ }
179
+ container.appendChild(wrapper);
180
+ return this;
181
+ }
182
+ }
183
+ export function checkbox(id, options = {}) {
184
+ return new Checkbox(id, options);
185
+ }
@@ -0,0 +1,242 @@
1
+ import { FormInput, FormInputState } from './base/FormInput.js';
2
+ import { renderIcon } from './icons.js';
3
+
4
+ // Event definitions
5
+ const TRIGGER_EVENTS = [] as const;
6
+ const CALLBACK_EVENTS = ['change'] as const;
7
+
8
+ export interface CheckboxOptions {
9
+ checked?: boolean;
10
+ label?: string;
11
+ required?: boolean;
12
+ disabled?: boolean;
13
+ name?: string;
14
+ value?: string;
15
+ style?: string;
16
+ class?: string;
17
+ onValidate?: (checked: boolean) => boolean | string;
18
+ }
19
+
20
+ interface CheckboxState extends FormInputState {
21
+ checked: boolean;
22
+ value: string;
23
+ }
24
+
25
+ export class Checkbox extends FormInput<CheckboxState> {
26
+ constructor(id: string, options: CheckboxOptions = {}) {
27
+ super(id, {
28
+ checked: options.checked ?? false,
29
+ value: options.value ?? 'on',
30
+ label: options.label ?? '',
31
+ required: options.required ?? false,
32
+ disabled: options.disabled ?? false,
33
+ name: options.name ?? id,
34
+ style: options.style ?? '',
35
+ class: options.class ?? '',
36
+ errorMessage: undefined
37
+ });
38
+
39
+ if (options.onValidate) {
40
+ this._onValidate = options.onValidate;
41
+ }
42
+ }
43
+
44
+ protected getTriggerEvents(): readonly string[] {
45
+ return TRIGGER_EVENTS;
46
+ }
47
+
48
+ protected getCallbackEvents(): readonly string[] {
49
+ return CALLBACK_EVENTS;
50
+ }
51
+
52
+ /* ═════════════════════════════════════════════════════════════════
53
+ * FLUENT API
54
+ * ═════════════════════════════════════════════════════════════════ */
55
+
56
+ // ✅ Inherited from FormInput/BaseComponent:
57
+ // - label(), required(), name(), onValidate()
58
+ // - validate(), isValid()
59
+ // - style(), class()
60
+ // - bind(), sync(), renderTo()
61
+ // - disabled(), enable(), disable()
62
+
63
+ checked(value: boolean): this {
64
+ return this.setValue(value);
65
+ }
66
+
67
+ value(value: string): this {
68
+ this.state.value = value;
69
+ return this;
70
+ }
71
+
72
+ /* ═════════════════════════════════════════════════════════════════
73
+ * FORM INPUT IMPLEMENTATION
74
+ * ═════════════════════════════════════════════════════════════════ */
75
+
76
+ getValue(): boolean {
77
+ return this.state.checked;
78
+ }
79
+
80
+ setValue(value: boolean): this {
81
+ this.state.checked = value;
82
+ if (this._inputElement) {
83
+ (this._inputElement as HTMLInputElement).checked = value;
84
+ }
85
+ return this;
86
+ }
87
+
88
+ protected _validateValue(checked: boolean): boolean | string {
89
+ const { required } = this.state;
90
+
91
+ if (required && !checked) {
92
+ return 'This field must be checked';
93
+ }
94
+
95
+ if (this._onValidate) {
96
+ const result = this._onValidate(checked);
97
+ if (result !== true) {
98
+ return result || 'Invalid value';
99
+ }
100
+ }
101
+
102
+ return true;
103
+ }
104
+
105
+ protected _buildInputElement(): HTMLElement {
106
+ const { checked, value, required, disabled, name } = this.state;
107
+
108
+ const input = document.createElement('input');
109
+ input.type = 'checkbox';
110
+ input.className = 'jux-checkbox-input';
111
+ input.id = `${this._id}-input`;
112
+ input.name = name;
113
+ input.value = value;
114
+ input.checked = checked;
115
+ input.required = required;
116
+ input.disabled = disabled;
117
+
118
+ return input;
119
+ }
120
+
121
+ /* ═════════════════════════════════════════════════════════════════
122
+ * RENDER
123
+ * ═════════════════════════════════════════════════════════════════ */
124
+
125
+ render(targetId?: string): this {
126
+ const container = this._setupContainer(targetId);
127
+
128
+ const { style, class: className } = this.state;
129
+
130
+ // Build wrapper
131
+ const wrapper = document.createElement('div');
132
+ wrapper.className = 'jux-checkbox';
133
+ wrapper.id = this._id;
134
+ if (className) wrapper.className += ` ${className}`;
135
+ if (style) wrapper.setAttribute('style', style);
136
+
137
+ // Checkbox container
138
+ const checkboxContainer = document.createElement('label');
139
+ checkboxContainer.className = 'jux-checkbox-container';
140
+ checkboxContainer.htmlFor = `${this._id}-input`;
141
+
142
+ // Input element
143
+ const inputEl = this._buildInputElement() as HTMLInputElement;
144
+ this._inputElement = inputEl;
145
+ checkboxContainer.appendChild(inputEl);
146
+
147
+ // Checkmark (custom styled)
148
+ const checkmark = document.createElement('span');
149
+ checkmark.className = 'jux-checkbox-checkmark';
150
+ checkmark.appendChild(renderIcon('check'));
151
+ checkboxContainer.appendChild(checkmark);
152
+
153
+ // Label text
154
+ if (this.state.label) {
155
+ const labelText = document.createElement('span');
156
+ labelText.className = 'jux-checkbox-label-text';
157
+ labelText.textContent = this.state.label;
158
+ if (this.state.required) {
159
+ const requiredSpan = document.createElement('span');
160
+ requiredSpan.className = 'jux-input-required';
161
+ requiredSpan.textContent = ' *';
162
+ labelText.appendChild(requiredSpan);
163
+ }
164
+ checkboxContainer.appendChild(labelText);
165
+ }
166
+
167
+ wrapper.appendChild(checkboxContainer);
168
+
169
+ // Error element
170
+ wrapper.appendChild(this._renderError());
171
+
172
+ // Wire events
173
+ this._wireStandardEvents(wrapper);
174
+
175
+ // Wire checkbox-specific sync (maps 'checked' to 'value' property for consistency)
176
+ const valueSync = this._syncBindings.find(b => b.property === 'value' || b.property === 'checked');
177
+
178
+ if (valueSync) {
179
+ const { stateObj, toState, toComponent } = valueSync;
180
+
181
+ const transformToState = toState || ((v: boolean) => v);
182
+ const transformToComponent = toComponent || ((v: any) => Boolean(v));
183
+
184
+ let isUpdating = false;
185
+
186
+ // State → Component
187
+ stateObj.subscribe((val: any) => {
188
+ if (isUpdating) return;
189
+ const transformed = transformToComponent(val);
190
+ this.setValue(transformed);
191
+ });
192
+
193
+ // Component → State
194
+ inputEl.addEventListener('change', () => {
195
+ if (isUpdating) return;
196
+ isUpdating = true;
197
+
198
+ const checked = inputEl.checked;
199
+ this.state.checked = checked;
200
+ this._clearError();
201
+
202
+ const transformed = transformToState(checked);
203
+ stateObj.set(transformed);
204
+
205
+ // 🎯 Fire the change callback event
206
+ this._triggerCallback('change', checked);
207
+
208
+ setTimeout(() => { isUpdating = false; }, 0);
209
+ });
210
+ } else {
211
+ // Default behavior without sync
212
+ inputEl.addEventListener('change', () => {
213
+ this.state.checked = inputEl.checked;
214
+ this._clearError();
215
+
216
+ // 🎯 Fire the change callback event
217
+ this._triggerCallback('change', inputEl.checked);
218
+ });
219
+ }
220
+
221
+ // Always add blur validation
222
+ inputEl.addEventListener('blur', () => {
223
+ this.validate();
224
+ });
225
+
226
+ // Sync label changes
227
+ const labelSync = this._syncBindings.find(b => b.property === 'label');
228
+ if (labelSync) {
229
+ const transform = labelSync.toComponent || ((v: any) => String(v));
230
+ labelSync.stateObj.subscribe((val: any) => {
231
+ this.label(transform(val));
232
+ });
233
+ }
234
+
235
+ container.appendChild(wrapper);
236
+ return this;
237
+ }
238
+ }
239
+
240
+ export function checkbox(id: string, options: CheckboxOptions = {}): Checkbox {
241
+ return new Checkbox(id, options);
242
+ }