juxscript 1.0.132 → 1.1.2

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 (133) hide show
  1. package/README.md +1 -32
  2. package/bin/cli.js +4 -2
  3. package/index.d.ts +200 -0
  4. package/index.js +96 -22
  5. package/juxconfig.example.js +58 -63
  6. package/lib/components/alert.ts +200 -0
  7. package/lib/components/app.ts +247 -0
  8. package/lib/components/badge.ts +101 -0
  9. package/lib/components/base/BaseComponent.ts +421 -0
  10. package/lib/components/base/FormInput.ts +227 -0
  11. package/lib/components/button.ts +178 -0
  12. package/lib/components/card.ts +173 -0
  13. package/lib/components/chart.ts +231 -0
  14. package/lib/components/checkbox.ts +242 -0
  15. package/lib/components/code.ts +123 -0
  16. package/lib/components/container.ts +140 -0
  17. package/lib/components/data.ts +135 -0
  18. package/lib/components/datepicker.ts +234 -0
  19. package/lib/components/dialog.ts +172 -0
  20. package/lib/components/divider.ts +100 -0
  21. package/lib/components/dropdown.ts +186 -0
  22. package/lib/components/element.ts +267 -0
  23. package/lib/components/fileupload.ts +309 -0
  24. package/lib/components/grid.ts +291 -0
  25. package/lib/components/guard.ts +92 -0
  26. package/lib/components/heading.ts +96 -0
  27. package/lib/components/helpers.ts +41 -0
  28. package/lib/components/hero.ts +224 -0
  29. package/lib/components/icon.ts +178 -0
  30. package/lib/components/icons.ts +464 -0
  31. package/lib/components/include.ts +410 -0
  32. package/lib/components/input.ts +457 -0
  33. package/lib/components/list.ts +419 -0
  34. package/lib/components/loading.ts +100 -0
  35. package/lib/components/menu.ts +275 -0
  36. package/lib/components/modal.ts +284 -0
  37. package/lib/components/nav.ts +257 -0
  38. package/lib/components/paragraph.ts +97 -0
  39. package/lib/components/progress.ts +159 -0
  40. package/lib/components/radio.ts +278 -0
  41. package/lib/components/req.ts +303 -0
  42. package/lib/components/script.ts +41 -0
  43. package/lib/components/select.ts +252 -0
  44. package/lib/components/sidebar.ts +275 -0
  45. package/lib/components/style.ts +41 -0
  46. package/lib/components/switch.ts +246 -0
  47. package/lib/components/table.ts +1249 -0
  48. package/lib/components/tabs.ts +250 -0
  49. package/lib/components/theme-toggle.ts +293 -0
  50. package/lib/components/tooltip.ts +144 -0
  51. package/lib/components/view.ts +190 -0
  52. package/lib/components/write.ts +272 -0
  53. package/lib/globals.d.ts +19 -5
  54. package/lib/layouts/default.css +260 -0
  55. package/lib/layouts/figma.css +334 -0
  56. package/lib/reactivity/state.ts +78 -0
  57. package/lib/utils/{fetch.js → fetch.ts} +206 -81
  58. package/machinery/ast.js +347 -0
  59. package/machinery/build.js +466 -0
  60. package/machinery/compiler3.js +6 -66
  61. package/machinery/config.js +6 -93
  62. package/machinery/doc-generator.js +136 -0
  63. package/machinery/imports.js +155 -0
  64. package/machinery/server.js +166 -0
  65. package/machinery/ts-shim.js +46 -0
  66. package/machinery/watcher.js +162 -50
  67. package/package.json +9 -30
  68. package/create/index.jux +0 -77
  69. package/create/layout.jux +0 -18
  70. package/create/style.css +0 -57
  71. package/create/themes/assets/jux.svg +0 -34
  72. package/create/themes/base.css +0 -197
  73. package/create/themes/base2.css +0 -54
  74. package/create/themes/layouts/base.jux +0 -16
  75. package/create/themes/layouts/base_marketing.jux +0 -0
  76. package/create/themes/layouts/base_saas.jux +0 -0
  77. package/lib/componentsv2/base/BaseEngine.d.ts +0 -112
  78. package/lib/componentsv2/base/BaseEngine.js +0 -279
  79. package/lib/componentsv2/base/BaseSkin.d.ts +0 -74
  80. package/lib/componentsv2/base/BaseSkin.js +0 -130
  81. package/lib/componentsv2/base/Neighborhood.d.ts +0 -22
  82. package/lib/componentsv2/base/Neighborhood.js +0 -56
  83. package/lib/componentsv2/base/OptionsContract.d.ts +0 -20
  84. package/lib/componentsv2/base/OptionsContract.js +0 -107
  85. package/lib/componentsv2/base/State.d.ts +0 -18
  86. package/lib/componentsv2/base/State.js +0 -68
  87. package/lib/componentsv2/element/Element.d.ts +0 -30
  88. package/lib/componentsv2/element/Element.js +0 -50
  89. package/lib/componentsv2/element/ElementEngine.d.ts +0 -59
  90. package/lib/componentsv2/element/ElementEngine.js +0 -118
  91. package/lib/componentsv2/element/ElementSkin.d.ts +0 -10
  92. package/lib/componentsv2/element/ElementSkin.js +0 -56
  93. package/lib/componentsv2/element/structure.css +0 -261
  94. package/lib/componentsv2/grid/Grid.d.ts +0 -13
  95. package/lib/componentsv2/grid/Grid.js +0 -27
  96. package/lib/componentsv2/grid/GridEngine.d.ts +0 -77
  97. package/lib/componentsv2/grid/GridEngine.js +0 -153
  98. package/lib/componentsv2/grid/GridSkin.d.ts +0 -11
  99. package/lib/componentsv2/grid/GridSkin.js +0 -84
  100. package/lib/componentsv2/grid/structure.css +0 -27
  101. package/lib/componentsv2/input/Input.d.ts +0 -6
  102. package/lib/componentsv2/input/Input.js +0 -21
  103. package/lib/componentsv2/input/InputEngine.d.ts +0 -70
  104. package/lib/componentsv2/input/InputEngine.js +0 -143
  105. package/lib/componentsv2/input/InputSkin.d.ts +0 -11
  106. package/lib/componentsv2/input/InputSkin.js +0 -89
  107. package/lib/componentsv2/input/structure.css +0 -47
  108. package/lib/componentsv2/list/List.d.ts +0 -49
  109. package/lib/componentsv2/list/List.js +0 -105
  110. package/lib/componentsv2/list/ListEngine.d.ts +0 -121
  111. package/lib/componentsv2/list/ListEngine.js +0 -322
  112. package/lib/componentsv2/list/ListSkin.d.ts +0 -20
  113. package/lib/componentsv2/list/ListSkin.js +0 -345
  114. package/lib/componentsv2/list/structure.css +0 -359
  115. package/lib/componentsv2/plugins/ClientSQLitePlugin.d.ts +0 -21
  116. package/lib/componentsv2/plugins/ClientSQLitePlugin.js +0 -130
  117. package/lib/componentsv2/plugins/IndexedDBPlugin.d.ts +0 -18
  118. package/lib/componentsv2/plugins/IndexedDBPlugin.js +0 -75
  119. package/lib/componentsv2/plugins/LocalStoragePlugin.d.ts +0 -20
  120. package/lib/componentsv2/plugins/LocalStoragePlugin.js +0 -65
  121. package/lib/componentsv2/plugins/ServerSQLitePlugin.d.ts +0 -25
  122. package/lib/componentsv2/plugins/ServerSQLitePlugin.js +0 -70
  123. package/lib/componentsv2/stubs/ComponentComposition.ts.stub +0 -32
  124. package/lib/componentsv2/stubs/ComponentEngine.ts.stub +0 -36
  125. package/lib/componentsv2/stubs/ComponentSkin.ts.stub +0 -35
  126. package/lib/componentsv2/stubs/ComponentStructure.css.d.ts.stub +0 -2
  127. package/lib/componentsv2/stubs/ComponentStructure.css.stub +0 -13
  128. package/lib/utils/fetch.d.ts +0 -176
  129. package/machinery/serve.js +0 -255
  130. package/types/css.d.ts +0 -10
  131. /package/{create/themes/layouts/base_blog.jux → machinery/bundleAssets.js} +0 -0
  132. /package/{create/themes/layouts/base_docs.jux → machinery/bundleJux.js} +0 -0
  133. /package/{create/themes/layouts/base_login.jux → machinery/bundleVendors.js} +0 -0
@@ -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
+ }
@@ -0,0 +1,123 @@
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+
3
+ // Event definitions
4
+ const TRIGGER_EVENTS = [] as const;
5
+ const CALLBACK_EVENTS = [] as const;
6
+
7
+ export interface CodeOptions {
8
+ code?: string;
9
+ language?: string;
10
+ style?: string;
11
+ class?: string;
12
+ }
13
+
14
+ type CodeState = {
15
+ code: string;
16
+ language: string;
17
+ style: string;
18
+ class: string;
19
+ };
20
+
21
+ export class Code extends BaseComponent<CodeState> {
22
+ constructor(id: string, options: CodeOptions = {}) {
23
+ super(id, {
24
+ code: options.code ?? '',
25
+ language: options.language ?? 'javascript',
26
+ style: options.style ?? '',
27
+ class: options.class ?? ''
28
+ });
29
+ }
30
+
31
+ protected getTriggerEvents(): readonly string[] {
32
+ return TRIGGER_EVENTS;
33
+ }
34
+
35
+ protected getCallbackEvents(): readonly string[] {
36
+ return CALLBACK_EVENTS;
37
+ }
38
+
39
+ /* ═════════════════════════════════════════════════════════════════
40
+ * FLUENT API
41
+ * ═════════════════════════════════════════════════════════════════ */
42
+
43
+ // ✅ Inherited from BaseComponent
44
+
45
+ code(value: string): this {
46
+ this.state.code = value;
47
+ return this;
48
+ }
49
+
50
+ language(value: string): this {
51
+ this.state.language = value;
52
+ return this;
53
+ }
54
+
55
+ /* ═════════════════════════════════════════════════════════════════
56
+ * RENDER
57
+ * ═════════════════════════════════════════════════════════════════ */
58
+
59
+ render(targetId?: string): this {
60
+ const container = this._setupContainer(targetId);
61
+
62
+ const { code, language, style, class: className } = this.state;
63
+
64
+ const wrapper = document.createElement('div');
65
+ wrapper.className = 'jux-code';
66
+ wrapper.id = this._id;
67
+ if (className) wrapper.className += ` ${className}`;
68
+ if (style) wrapper.setAttribute('style', style);
69
+
70
+ const pre = document.createElement('pre');
71
+ const codeEl = document.createElement('code');
72
+ codeEl.className = `language-${language}`;
73
+ codeEl.textContent = code;
74
+ pre.appendChild(codeEl);
75
+ wrapper.appendChild(pre);
76
+
77
+ this._wireStandardEvents(wrapper);
78
+
79
+ // Wire sync bindings
80
+ this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
81
+ if (property === 'code') {
82
+ const transform = toComponent || ((v: any) => String(v));
83
+
84
+ stateObj.subscribe((val: any) => {
85
+ const transformed = transform(val);
86
+ codeEl.textContent = transformed;
87
+ this.state.code = transformed;
88
+
89
+ if ((window as any).Prism) {
90
+ (window as any).Prism.highlightElement(codeEl);
91
+ }
92
+ });
93
+ }
94
+ else if (property === 'language') {
95
+ const transform = toComponent || ((v: any) => String(v));
96
+
97
+ stateObj.subscribe((val: any) => {
98
+ const transformed = transform(val);
99
+ codeEl.className = `language-${transformed}`;
100
+ this.state.language = transformed;
101
+
102
+ if ((window as any).Prism) {
103
+ (window as any).Prism.highlightElement(codeEl);
104
+ }
105
+ });
106
+ }
107
+ });
108
+
109
+ container.appendChild(wrapper);
110
+
111
+ requestAnimationFrame(() => {
112
+ if ((window as any).Prism) {
113
+ (window as any).Prism.highlightElement(codeEl);
114
+ }
115
+ });
116
+
117
+ return this;
118
+ }
119
+ }
120
+
121
+ export function code(id: string, options: CodeOptions = {}): Code {
122
+ return new Code(id, options);
123
+ }
@@ -0,0 +1,140 @@
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+
3
+ // Event definitions - Container is a layout wrapper that can be clicked
4
+ const TRIGGER_EVENTS = [] as const;
5
+ const CALLBACK_EVENTS = ['click'] as const;
6
+
7
+ /**
8
+ * Container options
9
+ */
10
+ export interface ContainerOptions {
11
+ class?: string;
12
+ style?: string;
13
+ direction?: 'row' | 'column';
14
+ gap?: string | number;
15
+ align?: 'start' | 'center' | 'end' | 'stretch';
16
+ justify?: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly';
17
+ }
18
+
19
+ /**
20
+ * Container state
21
+ */
22
+ type ContainerState = {
23
+ class: string;
24
+ style: string;
25
+ direction?: 'row' | 'column';
26
+ gap?: string | number;
27
+ align?: string;
28
+ justify?: string;
29
+ };
30
+
31
+ /**
32
+ * Container component - a simple div container for grouping elements
33
+ */
34
+ export class Container extends BaseComponent<ContainerState> {
35
+ constructor(id: string, options: ContainerOptions = {}) {
36
+ super(id, {
37
+ class: options.class ?? '',
38
+ style: options.style ?? '',
39
+ direction: options.direction,
40
+ gap: options.gap,
41
+ align: options.align,
42
+ justify: options.justify
43
+ });
44
+ }
45
+
46
+ protected getTriggerEvents(): readonly string[] {
47
+ return TRIGGER_EVENTS;
48
+ }
49
+
50
+ protected getCallbackEvents(): readonly string[] {
51
+ return CALLBACK_EVENTS;
52
+ }
53
+
54
+ /* -------------------------
55
+ * Fluent API
56
+ * ------------------------- */
57
+
58
+ // ✅ Inherited from BaseComponent:
59
+ // - style(), class()
60
+ // - bind(), sync(), renderTo()
61
+ // - addClass(), removeClass(), toggleClass()
62
+ // - visible(), show(), hide()
63
+ // - attr(), attrs(), removeAttr()
64
+ // - disabled(), enable(), disable()
65
+
66
+ direction(value: 'row' | 'column'): this {
67
+ this.state.direction = value;
68
+ return this;
69
+ }
70
+
71
+ gap(value: string | number): this {
72
+ this.state.gap = value;
73
+ return this;
74
+ }
75
+
76
+ align(value: 'start' | 'center' | 'end' | 'stretch'): this {
77
+ this.state.align = value;
78
+ return this;
79
+ }
80
+
81
+ justify(value: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'): this {
82
+ this.state.justify = value;
83
+ return this;
84
+ }
85
+
86
+ /* -------------------------
87
+ * Render
88
+ * ------------------------- */
89
+
90
+ render(targetId?: string): this {
91
+ const container = this._setupContainer(targetId);
92
+
93
+ const { class: className, style, direction, gap, align, justify } = this.state;
94
+
95
+ const div = document.createElement('div');
96
+ div.id = this._id;
97
+
98
+ // Always include jux-container class, append custom classes
99
+ div.className = className ? `jux-container ${className}` : 'jux-container';
100
+
101
+ // Only apply flex styles if any flex properties are set
102
+ const usesFlexbox = direction || gap !== undefined || align || justify;
103
+
104
+ let computedStyle = style;
105
+
106
+ if (usesFlexbox) {
107
+ const flexStyles: string[] = ['display: flex', 'width: auto'];
108
+
109
+ if (direction) flexStyles.push(`flex-direction: ${direction}`);
110
+
111
+ if (gap !== undefined) {
112
+ const gapValue = typeof gap === 'number' ? `${gap}px` : gap;
113
+ flexStyles.push(`gap: ${gapValue}`);
114
+ }
115
+ // Only set align-items if explicitly specified
116
+ if (align) flexStyles.push(`align-items: ${align}`);
117
+ // Only set justify-content if explicitly specified
118
+ if (justify) flexStyles.push(`justify-content: ${justify}`);
119
+
120
+ computedStyle = `${flexStyles.join('; ')}; ${style}`.trim();
121
+ }
122
+
123
+ if (computedStyle) {
124
+ div.setAttribute('style', computedStyle);
125
+ }
126
+
127
+ this._wireStandardEvents(div);
128
+
129
+ container.appendChild(div);
130
+
131
+ return this;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Factory helper
137
+ */
138
+ export function container(id: string, options: ContainerOptions = {}): Container {
139
+ return new Container(id, options);
140
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Data component - SQL query execution
3
+ * Note: No id needed - this is a data-only component
4
+ *
5
+ * Usage:
6
+ * const posts = jux.data('SELECT * FROM posts WHERE id = ?', [1]);
7
+ * await posts.execute();
8
+ * console.log(posts.data);
9
+ */
10
+
11
+ export interface DataOptions {
12
+ sql: string;
13
+ params?: any[];
14
+ apiUrl?: string;
15
+ }
16
+
17
+ export class Data {
18
+ private _data: any[] = [];
19
+ private _executed: boolean = false;
20
+ private _loading: boolean = false;
21
+ private _sql: string;
22
+ private _params: any[];
23
+ private _apiUrl: string;
24
+
25
+ constructor(sql: string, params: any[] = [], apiUrl: string = '/api/query') {
26
+ this._sql = sql;
27
+ this._params = params;
28
+ this._apiUrl = apiUrl;
29
+ }
30
+
31
+ /* -------------------------
32
+ * Getters
33
+ * ------------------------- */
34
+
35
+ get data(): any[] {
36
+ return this._data;
37
+ }
38
+
39
+ get executed(): boolean {
40
+ return this._executed;
41
+ }
42
+
43
+ get loading(): boolean {
44
+ return this._loading;
45
+ }
46
+
47
+ get sql(): string {
48
+ return this._sql;
49
+ }
50
+
51
+ set sql(value: string) {
52
+ this._sql = value;
53
+ }
54
+
55
+ /* -------------------------
56
+ * Methods
57
+ * ------------------------- */
58
+
59
+ async execute(): Promise<void> {
60
+ if (this._executed) return;
61
+ this._loading = true;
62
+
63
+ try {
64
+ const response = await fetch(this._apiUrl, {
65
+ method: 'POST',
66
+ headers: {
67
+ 'Content-Type': 'application/json'
68
+ },
69
+ body: JSON.stringify({
70
+ sql: this._sql,
71
+ params: this._params
72
+ })
73
+ });
74
+
75
+ if (!response.ok) {
76
+ throw new Error(`API error: ${response.statusText}`);
77
+ }
78
+
79
+ const result = await response.json();
80
+ this._data = result.data || [];
81
+ this._executed = true;
82
+ } catch (error) {
83
+ console.error('Data execution error:', error);
84
+ this._data = [];
85
+ throw error;
86
+ } finally {
87
+ this._loading = false;
88
+ }
89
+ }
90
+
91
+ reset(): void {
92
+ this._executed = false;
93
+ this._data = [];
94
+ this._loading = false;
95
+ }
96
+
97
+ // Remove id requirement, make it optional for caching
98
+ static async fetch<T = any>(url: string, options?: RequestInit): Promise<T> {
99
+ const response = await fetch(url, options);
100
+ if (!response.ok) {
101
+ throw new Error(`API error: ${response.statusText}`);
102
+ }
103
+ return response.json();
104
+ }
105
+
106
+ // ✅ Static methods - no ID needed
107
+ static async post<T = any>(url: string, body: any, options?: RequestInit): Promise<T> {
108
+ const response = await fetch(url, options);
109
+ if (!response.ok) {
110
+ throw new Error(`API error: ${response.statusText}`);
111
+ }
112
+ return response.json();
113
+ }
114
+
115
+ static async put<T = any>(url: string, body: any, options?: RequestInit): Promise<T> {
116
+ const response = await fetch(url, options);
117
+ if (!response.ok) {
118
+ throw new Error(`API error: ${response.statusText}`);
119
+ }
120
+ return response.json();
121
+ }
122
+
123
+ static async delete<T = any>(url: string, options?: RequestInit): Promise<T> {
124
+ const response = await fetch(url, options);
125
+ if (!response.ok) {
126
+ throw new Error(`API error: ${response.statusText}`);
127
+ }
128
+ return response.json();
129
+ }
130
+ }
131
+
132
+ // ✅ No ID needed - pure data fetching
133
+ export function data<T = any>(url: string, options?: RequestInit): Promise<T> {
134
+ return Data.fetch<T>(url, options);
135
+ }