juxscript 1.1.98 → 1.1.100

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "totalComponents": 63,
3
- "generatedAt": "2026-02-12T20:48:20.139Z",
3
+ "generatedAt": "2026-02-12T21:08:33.192Z",
4
4
  "components": [
5
5
  {
6
6
  "file": "alert.js",
@@ -0,0 +1,77 @@
1
+ import { BaseComponent } from './BaseComponent.js';
2
+ /**
3
+ * Base state interface for all form inputs
4
+ */
5
+ export interface FormInputState extends Record<string, any> {
6
+ label: string;
7
+ required: boolean;
8
+ disabled: boolean;
9
+ name: string;
10
+ style: string;
11
+ class: string;
12
+ errorMessage?: string;
13
+ }
14
+ /**
15
+ * Abstract base class for all form input components
16
+ * Extends BaseComponent with form-specific functionality
17
+ */
18
+ export declare abstract class FormInput<TState extends FormInputState> extends BaseComponent<TState> {
19
+ protected _inputElement: HTMLElement | null;
20
+ protected _labelElement: HTMLLabelElement | null;
21
+ protected _errorElement: HTMLElement | null;
22
+ protected _onValidate?: (value: any) => boolean | string;
23
+ protected _hasBeenValidated: boolean;
24
+ /**
25
+ * Get the current value of the input
26
+ */
27
+ abstract getValue(): any;
28
+ /**
29
+ * Set the value of the input
30
+ */
31
+ abstract setValue(value: any): this;
32
+ /**
33
+ * Build the actual input element (input, select, textarea, etc.)
34
+ */
35
+ protected abstract _buildInputElement(): HTMLElement;
36
+ /**
37
+ * Validate the current value
38
+ */
39
+ protected abstract _validateValue(value: any): boolean | string;
40
+ label(value: string): this;
41
+ required(value: boolean): this;
42
+ name(value: string): this;
43
+ onValidate(handler: (value: any) => boolean | string): this;
44
+ /**
45
+ * Validate the current value and show/hide errors
46
+ */
47
+ validate(): boolean;
48
+ /**
49
+ * Check if current value is valid without showing errors
50
+ */
51
+ isValid(): boolean;
52
+ /**
53
+ * Show error message
54
+ */
55
+ protected _showError(message: string): void;
56
+ /**
57
+ * Clear error message
58
+ */
59
+ protected _clearError(): void;
60
+ /**
61
+ * Build label element with auto-generated text from ID if not provided
62
+ */
63
+ protected _renderLabel(): HTMLLabelElement;
64
+ /**
65
+ * Build error element
66
+ */
67
+ protected _renderError(): HTMLElement;
68
+ /**
69
+ * Default update implementation for form inputs
70
+ */
71
+ update(prop: string, value: any): void;
72
+ /**
73
+ * Wire up two-way sync for value property
74
+ */
75
+ protected _wireFormSync(inputElement: HTMLElement, eventName?: string): void;
76
+ }
77
+ //# sourceMappingURL=FormInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormInput.d.ts","sourceRoot":"","sources":["FormInput.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,8BAAsB,SAAS,CAAC,MAAM,SAAS,cAAc,CAAE,SAAQ,aAAa,CAAC,MAAM,CAAC;IACxF,SAAS,CAAC,aAAa,EAAE,WAAW,GAAG,IAAI,CAAQ;IACnD,SAAS,CAAC,aAAa,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IACxD,SAAS,CAAC,aAAa,EAAE,WAAW,GAAG,IAAI,CAAQ;IACnD,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,GAAG,MAAM,CAAC;IACzD,SAAS,CAAC,iBAAiB,EAAE,OAAO,CAAS;IAM7C;;OAEG;IACH,QAAQ,CAAC,QAAQ,IAAI,GAAG;IAExB;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAEnC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,kBAAkB,IAAI,WAAW;IAEpD;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,GAAG,MAAM;IAM/D,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ1B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK9B,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzB,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,GAAG,MAAM,GAAG,IAAI;IAS3D;;OAEG;IACH,QAAQ,IAAI,OAAO;IAcnB;;OAEG;IACH,OAAO,IAAI,OAAO;IAKlB;;OAEG;IACH,SAAS,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAa3C;;OAEG;IACH,SAAS,CAAC,WAAW,IAAI,IAAI;IAiB7B;;OAEG;IACH,SAAS,CAAC,YAAY,IAAI,gBAAgB;IAqB1C;;OAEG;IACH,SAAS,CAAC,YAAY,IAAI,WAAW;IAUrC;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAItC;;OAEG;IACH,SAAS,CAAC,aAAa,CAAC,YAAY,EAAE,WAAW,EAAE,SAAS,GAAE,MAAgB,GAAG,IAAI;CA8CxF"}
@@ -0,0 +1,171 @@
1
+ import { BaseComponent } from './BaseComponent.js';
2
+ import { formatIdAsLabel } from '../../utils/formatId.js'; // ✅ Import utility
3
+ /**
4
+ * Abstract base class for all form input components
5
+ * Extends BaseComponent with form-specific functionality
6
+ */
7
+ export class FormInput extends BaseComponent {
8
+ constructor() {
9
+ super(...arguments);
10
+ this._inputElement = null;
11
+ this._labelElement = null;
12
+ this._errorElement = null;
13
+ this._hasBeenValidated = false; // NEW: Track if user has submitted/validated
14
+ }
15
+ /* ═════════════════════════════════════════════════════════════════
16
+ * COMMON FORM INPUT API
17
+ * ═════════════════════════════════════════════════════════════════ */
18
+ label(value) {
19
+ this.state.label = value;
20
+ if (this._labelElement) {
21
+ this._labelElement.textContent = value;
22
+ }
23
+ return this;
24
+ }
25
+ required(value) {
26
+ this.state.required = value;
27
+ return this;
28
+ }
29
+ name(value) {
30
+ this.state.name = value;
31
+ return this;
32
+ }
33
+ onValidate(handler) {
34
+ this._onValidate = handler;
35
+ return this;
36
+ }
37
+ /* ═════════════════════════════════════════════════════════════════
38
+ * VALIDATION
39
+ * ═════════════════════════════════════════════════════════════════ */
40
+ /**
41
+ * Validate the current value and show/hide errors
42
+ */
43
+ validate() {
44
+ this._hasBeenValidated = true; // Mark as validated
45
+ const value = this.getValue();
46
+ const result = this._validateValue(value);
47
+ if (result === true) {
48
+ this._clearError();
49
+ return true;
50
+ }
51
+ else {
52
+ this._showError(result);
53
+ return false;
54
+ }
55
+ }
56
+ /**
57
+ * Check if current value is valid without showing errors
58
+ */
59
+ isValid() {
60
+ const value = this.getValue();
61
+ return this._validateValue(value) === true;
62
+ }
63
+ /**
64
+ * Show error message
65
+ */
66
+ _showError(message) {
67
+ if (this._errorElement) {
68
+ this._errorElement.textContent = message;
69
+ this._errorElement.style.display = 'block';
70
+ }
71
+ if (this._inputElement) {
72
+ this._inputElement.classList.add('jux-input-invalid');
73
+ }
74
+ this.state.errorMessage = message;
75
+ }
76
+ /**
77
+ * Clear error message
78
+ */
79
+ _clearError() {
80
+ if (this._errorElement) {
81
+ this._errorElement.textContent = '';
82
+ this._errorElement.style.display = 'none';
83
+ }
84
+ if (this._inputElement) {
85
+ this._inputElement.classList.remove('jux-input-invalid');
86
+ }
87
+ this.state.errorMessage = undefined;
88
+ }
89
+ /* ═════════════════════════════════════════════════════════════════
90
+ * COMMON RENDER HELPERS
91
+ * ═════════════════════════════════════════════════════════════════ */
92
+ /**
93
+ * Build label element with auto-generated text from ID if not provided
94
+ */
95
+ _renderLabel() {
96
+ const { label, required } = this.state;
97
+ const labelEl = document.createElement('label');
98
+ labelEl.className = 'jux-input-label';
99
+ labelEl.htmlFor = `${this._id}-input`;
100
+ // ✅ Use provided label or auto-generate from ID
101
+ labelEl.textContent = label || formatIdAsLabel(this._id);
102
+ if (required) {
103
+ const requiredSpan = document.createElement('span');
104
+ requiredSpan.className = 'jux-input-required';
105
+ requiredSpan.textContent = ' *';
106
+ labelEl.appendChild(requiredSpan);
107
+ }
108
+ this._labelElement = labelEl;
109
+ return labelEl;
110
+ }
111
+ /**
112
+ * Build error element
113
+ */
114
+ _renderError() {
115
+ const errorEl = document.createElement('div');
116
+ errorEl.className = 'jux-input-error';
117
+ errorEl.id = `${this._id}-error`;
118
+ errorEl.style.display = 'none';
119
+ this._errorElement = errorEl;
120
+ return errorEl;
121
+ }
122
+ /**
123
+ * Default update implementation for form inputs
124
+ */
125
+ update(prop, value) {
126
+ // No reactive updates needed - form inputs handle their own state
127
+ }
128
+ /**
129
+ * Wire up two-way sync for value property
130
+ */
131
+ _wireFormSync(inputElement, eventName = 'input') {
132
+ const valueSync = this._syncBindings.find(b => b.property === 'value');
133
+ if (valueSync) {
134
+ const { stateObj, toState, toComponent } = valueSync;
135
+ // Default transforms
136
+ const transformToState = toState || ((v) => v);
137
+ const transformToComponent = toComponent || ((v) => v);
138
+ let isUpdating = false;
139
+ // State → Component
140
+ stateObj.subscribe((val) => {
141
+ if (isUpdating)
142
+ return;
143
+ const transformed = transformToComponent(val);
144
+ this.setValue(transformed);
145
+ });
146
+ // Component → State
147
+ inputElement.addEventListener(eventName, () => {
148
+ if (isUpdating)
149
+ return;
150
+ isUpdating = true;
151
+ const value = this.getValue();
152
+ const transformed = transformToState(value);
153
+ this._clearError();
154
+ stateObj.set(transformed);
155
+ setTimeout(() => { isUpdating = false; }, 0);
156
+ });
157
+ }
158
+ else {
159
+ // Default behavior without sync
160
+ inputElement.addEventListener(eventName, () => {
161
+ this._clearError();
162
+ });
163
+ }
164
+ // Only validate on blur IF the field has been validated before (e.g., after submit)
165
+ inputElement.addEventListener('blur', () => {
166
+ if (this._hasBeenValidated) {
167
+ this.validate();
168
+ }
169
+ });
170
+ }
171
+ }
@@ -0,0 +1,237 @@
1
+ import { BaseComponent } from './BaseComponent.js';
2
+ import { formatIdAsLabel } from '../../utils/formatId.js'; // ✅ Import utility
3
+
4
+ /**
5
+ * Base state interface for all form inputs
6
+ */
7
+ export interface FormInputState extends Record<string, any> {
8
+ label: string;
9
+ required: boolean;
10
+ disabled: boolean;
11
+ name: string;
12
+ style: string;
13
+ class: string;
14
+ errorMessage?: string;
15
+ }
16
+
17
+ /**
18
+ * Abstract base class for all form input components
19
+ * Extends BaseComponent with form-specific functionality
20
+ */
21
+ export abstract class FormInput<TState extends FormInputState> extends BaseComponent<TState> {
22
+ protected _inputElement: HTMLElement | null = null;
23
+ protected _labelElement: HTMLLabelElement | null = null;
24
+ protected _errorElement: HTMLElement | null = null;
25
+ protected _onValidate?: (value: any) => boolean | string;
26
+ protected _hasBeenValidated: boolean = false; // NEW: Track if user has submitted/validated
27
+
28
+ /* ═════════════════════════════════════════════════════════════════
29
+ * ABSTRACT METHODS (Child must implement)
30
+ * ═════════════════════════════════════════════════════════════════ */
31
+
32
+ /**
33
+ * Get the current value of the input
34
+ */
35
+ abstract getValue(): any;
36
+
37
+ /**
38
+ * Set the value of the input
39
+ */
40
+ abstract setValue(value: any): this;
41
+
42
+ /**
43
+ * Build the actual input element (input, select, textarea, etc.)
44
+ */
45
+ protected abstract _buildInputElement(): HTMLElement;
46
+
47
+ /**
48
+ * Validate the current value
49
+ */
50
+ protected abstract _validateValue(value: any): boolean | string;
51
+
52
+ /* ═════════════════════════════════════════════════════════════════
53
+ * COMMON FORM INPUT API
54
+ * ═════════════════════════════════════════════════════════════════ */
55
+
56
+ label(value: string): this {
57
+ this.state.label = value;
58
+ if (this._labelElement) {
59
+ this._labelElement.textContent = value;
60
+ }
61
+ return this;
62
+ }
63
+
64
+ required(value: boolean): this {
65
+ this.state.required = value;
66
+ return this;
67
+ }
68
+
69
+ name(value: string): this {
70
+ this.state.name = value;
71
+ return this;
72
+ }
73
+
74
+ onValidate(handler: (value: any) => boolean | string): this {
75
+ this._onValidate = handler;
76
+ return this;
77
+ }
78
+
79
+ /* ═════════════════════════════════════════════════════════════════
80
+ * VALIDATION
81
+ * ═════════════════════════════════════════════════════════════════ */
82
+
83
+ /**
84
+ * Validate the current value and show/hide errors
85
+ */
86
+ validate(): boolean {
87
+ this._hasBeenValidated = true; // Mark as validated
88
+ const value = this.getValue();
89
+ const result = this._validateValue(value);
90
+
91
+ if (result === true) {
92
+ this._clearError();
93
+ return true;
94
+ } else {
95
+ this._showError(result as string);
96
+ return false;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Check if current value is valid without showing errors
102
+ */
103
+ isValid(): boolean {
104
+ const value = this.getValue();
105
+ return this._validateValue(value) === true;
106
+ }
107
+
108
+ /**
109
+ * Show error message
110
+ */
111
+ protected _showError(message: string): void {
112
+ if (this._errorElement) {
113
+ this._errorElement.textContent = message;
114
+ this._errorElement.style.display = 'block';
115
+ }
116
+
117
+ if (this._inputElement) {
118
+ this._inputElement.classList.add('jux-input-invalid');
119
+ }
120
+
121
+ this.state.errorMessage = message;
122
+ }
123
+
124
+ /**
125
+ * Clear error message
126
+ */
127
+ protected _clearError(): void {
128
+ if (this._errorElement) {
129
+ this._errorElement.textContent = '';
130
+ this._errorElement.style.display = 'none';
131
+ }
132
+
133
+ if (this._inputElement) {
134
+ this._inputElement.classList.remove('jux-input-invalid');
135
+ }
136
+
137
+ this.state.errorMessage = undefined;
138
+ }
139
+
140
+ /* ═════════════════════════════════════════════════════════════════
141
+ * COMMON RENDER HELPERS
142
+ * ═════════════════════════════════════════════════════════════════ */
143
+
144
+ /**
145
+ * Build label element with auto-generated text from ID if not provided
146
+ */
147
+ protected _renderLabel(): HTMLLabelElement {
148
+ const { label, required } = this.state;
149
+
150
+ const labelEl = document.createElement('label');
151
+ labelEl.className = 'jux-input-label';
152
+ labelEl.htmlFor = `${this._id}-input`;
153
+
154
+ // ✅ Use provided label or auto-generate from ID
155
+ labelEl.textContent = label || formatIdAsLabel(this._id);
156
+
157
+ if (required) {
158
+ const requiredSpan = document.createElement('span');
159
+ requiredSpan.className = 'jux-input-required';
160
+ requiredSpan.textContent = ' *';
161
+ labelEl.appendChild(requiredSpan);
162
+ }
163
+
164
+ this._labelElement = labelEl;
165
+ return labelEl;
166
+ }
167
+
168
+ /**
169
+ * Build error element
170
+ */
171
+ protected _renderError(): HTMLElement {
172
+ const errorEl = document.createElement('div');
173
+ errorEl.className = 'jux-input-error';
174
+ errorEl.id = `${this._id}-error`;
175
+ errorEl.style.display = 'none';
176
+
177
+ this._errorElement = errorEl;
178
+ return errorEl;
179
+ }
180
+
181
+ /**
182
+ * Default update implementation for form inputs
183
+ */
184
+ update(prop: string, value: any): void {
185
+ // No reactive updates needed - form inputs handle their own state
186
+ }
187
+
188
+ /**
189
+ * Wire up two-way sync for value property
190
+ */
191
+ protected _wireFormSync(inputElement: HTMLElement, eventName: string = 'input'): void {
192
+ const valueSync = this._syncBindings.find(b => b.property === 'value');
193
+
194
+ if (valueSync) {
195
+ const { stateObj, toState, toComponent } = valueSync;
196
+
197
+ // Default transforms
198
+ const transformToState = toState || ((v: any) => v);
199
+ const transformToComponent = toComponent || ((v: any) => v);
200
+
201
+ let isUpdating = false;
202
+
203
+ // State → Component
204
+ stateObj.subscribe((val: any) => {
205
+ if (isUpdating) return;
206
+ const transformed = transformToComponent(val);
207
+ this.setValue(transformed);
208
+ });
209
+
210
+ // Component → State
211
+ inputElement.addEventListener(eventName, () => {
212
+ if (isUpdating) return;
213
+ isUpdating = true;
214
+
215
+ const value = this.getValue();
216
+ const transformed = transformToState(value);
217
+ this._clearError();
218
+
219
+ stateObj.set(transformed);
220
+
221
+ setTimeout(() => { isUpdating = false; }, 0);
222
+ });
223
+ } else {
224
+ // Default behavior without sync
225
+ inputElement.addEventListener(eventName, () => {
226
+ this._clearError();
227
+ });
228
+ }
229
+
230
+ // Only validate on blur IF the field has been validated before (e.g., after submit)
231
+ inputElement.addEventListener('blur', () => {
232
+ if (this._hasBeenValidated) {
233
+ this.validate();
234
+ }
235
+ });
236
+ }
237
+ }
@@ -26,6 +26,7 @@ interface StackState extends BaseState {
26
26
  export declare abstract class BaseStack extends BaseComponent<StackState> {
27
27
  protected abstract baseClassName: string;
28
28
  private _inlineStyles;
29
+ _container: HTMLElement | null;
29
30
  constructor(id: string, children: Record<string, any> | any[], options?: StackOptions);
30
31
  protected getTriggerEvents(): readonly string[];
31
32
  protected getCallbackEvents(): readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"BaseStack.d.ts","sourceRoot":"","sources":["BaseStack.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseStack.d.ts","sourceRoot":"","sources":["BaseStack.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,7 @@ export class BaseStack extends BaseComponent {
26
26
  childStyles: undefined
27
27
  });
28
28
  this._inlineStyles = new Map();
29
+ this._container = null; // ✅ Make public for nesting
29
30
  }
30
31
  getTriggerEvents() {
31
32
  return TRIGGER_EVENTS;
@@ -184,22 +185,24 @@ export class BaseStack extends BaseComponent {
184
185
  }
185
186
  renderChild(child, index) {
186
187
  let childElement;
187
- // String - wrap in div
188
- if (typeof child === 'string') {
189
- childElement = document.createElement('div');
190
- childElement.textContent = child;
188
+ // Priority 1: Check if it's a Stack (has _container property)
189
+ if (child && typeof child === 'object' && '_container' in child && child._container instanceof HTMLElement) {
190
+ childElement = child._container;
191
191
  }
192
- // JUX Component - render it
193
- else if (child && typeof child.render === 'function' && 'container' in child) {
194
- const tempContainer = document.createElement('div');
195
- child.render(tempContainer);
196
- childElement = child.container || tempContainer.firstElementChild;
192
+ // ✅ Priority 2: Check if it's a JUX Component (has container property)
193
+ else if (child && typeof child === 'object' && 'container' in child && child.container instanceof HTMLElement) {
194
+ childElement = child.container;
197
195
  }
198
- // DOM Element - use directly
196
+ // Priority 3: Check if it's already a DOM element
199
197
  else if (child instanceof HTMLElement) {
200
198
  childElement = child;
201
199
  }
202
- // Fallback
200
+ // ✅ Priority 4: String - wrap in div
201
+ else if (typeof child === 'string') {
202
+ childElement = document.createElement('div');
203
+ childElement.innerHTML = child;
204
+ }
205
+ // ✅ Fallback - stringify
203
206
  else {
204
207
  childElement = document.createElement('div');
205
208
  childElement.textContent = String(child);
@@ -241,6 +244,8 @@ export class BaseStack extends BaseComponent {
241
244
  wrapper.appendChild(childEl);
242
245
  });
243
246
  this._wireStandardEvents(wrapper);
247
+ // ✅ Store wrapper as _container for nesting
248
+ this._container = wrapper;
244
249
  container.appendChild(wrapper);
245
250
  return this;
246
251
  }
@@ -34,6 +34,7 @@ interface StackState extends BaseState {
34
34
  export abstract class BaseStack extends BaseComponent<StackState> {
35
35
  protected abstract baseClassName: string;
36
36
  private _inlineStyles: Map<string, string> = new Map();
37
+ public _container: HTMLElement | null = null; // ✅ Make public for nesting
37
38
 
38
39
  constructor(id: string, children: Record<string, any> | any[], options: StackOptions = {}) {
39
40
  const childArray = Array.isArray(children)
@@ -244,22 +245,24 @@ export abstract class BaseStack extends BaseComponent<StackState> {
244
245
  protected renderChild(child: any, index: number): HTMLElement {
245
246
  let childElement: HTMLElement;
246
247
 
247
- // String - wrap in div
248
- if (typeof child === 'string') {
249
- childElement = document.createElement('div');
250
- childElement.textContent = child;
248
+ // Priority 1: Check if it's a Stack (has _container property)
249
+ if (child && typeof child === 'object' && '_container' in child && child._container instanceof HTMLElement) {
250
+ childElement = child._container;
251
251
  }
252
- // JUX Component - render it
253
- else if (child && typeof child.render === 'function' && 'container' in child) {
254
- const tempContainer = document.createElement('div');
255
- child.render(tempContainer);
256
- childElement = child.container || tempContainer.firstElementChild as HTMLElement;
252
+ // ✅ Priority 2: Check if it's a JUX Component (has container property)
253
+ else if (child && typeof child === 'object' && 'container' in child && child.container instanceof HTMLElement) {
254
+ childElement = child.container;
257
255
  }
258
- // DOM Element - use directly
256
+ // Priority 3: Check if it's already a DOM element
259
257
  else if (child instanceof HTMLElement) {
260
258
  childElement = child;
261
259
  }
262
- // Fallback
260
+ // ✅ Priority 4: String - wrap in div
261
+ else if (typeof child === 'string') {
262
+ childElement = document.createElement('div');
263
+ childElement.innerHTML = child;
264
+ }
265
+ // ✅ Fallback - stringify
263
266
  else {
264
267
  childElement = document.createElement('div');
265
268
  childElement.textContent = String(child);
@@ -313,6 +316,10 @@ export abstract class BaseStack extends BaseComponent<StackState> {
313
316
  });
314
317
 
315
318
  this._wireStandardEvents(wrapper);
319
+
320
+ // ✅ Store wrapper as _container for nesting
321
+ this._container = wrapper;
322
+
316
323
  container.appendChild(wrapper);
317
324
 
318
325
  return this;
@@ -1 +1 @@
1
- {"version":3,"file":"HStack.d.ts","sourceRoot":"","sources":["HStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEzD,qBAAa,MAAO,SAAQ,SAAS;IACjC,SAAS,CAAC,aAAa,SAAgB;gBAE3B,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB;IAIzF,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM;CAY3F;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAE5G"}
1
+ {"version":3,"file":"HStack.d.ts","sourceRoot":"","sources":["HStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEzD,qBAAa,MAAO,SAAQ,SAAS;IACjC,SAAS,CAAC,aAAa,SAAgB;gBAE3B,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB;IAIzF,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM;CAa3F;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAE5G"}
@@ -7,8 +7,9 @@ export class HStack extends BaseStack {
7
7
  static create(children, options = {}) {
8
8
  const id = `hstack-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
9
9
  const instance = new HStack(id, children, options);
10
+ // ✅ Auto-render to 'app' on next tick
10
11
  queueMicrotask(() => {
11
- if (!instance.container) {
12
+ if (!instance._container) {
12
13
  instance.render('app');
13
14
  }
14
15
  });
@@ -11,8 +11,9 @@ export class HStack extends BaseStack {
11
11
  const id = `hstack-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
12
12
  const instance = new HStack(id, children, options);
13
13
 
14
+ // ✅ Auto-render to 'app' on next tick
14
15
  queueMicrotask(() => {
15
- if (!instance.container) {
16
+ if (!instance._container) {
16
17
  instance.render('app');
17
18
  }
18
19
  });
@@ -10,9 +10,9 @@ export class VStack extends BaseStack {
10
10
  static create(children, options = {}) {
11
11
  const id = `vstack-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
12
12
  const instance = new VStack(id, children, options);
13
- // ✅ Auto-render after microtask
13
+ // ✅ Auto-render to 'app' on next tick
14
14
  queueMicrotask(() => {
15
- if (!instance.container) {
15
+ if (!instance._container) {
16
16
  instance.render('app');
17
17
  }
18
18
  });
@@ -14,9 +14,9 @@ export class VStack extends BaseStack {
14
14
  const id = `vstack-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
15
15
  const instance = new VStack(id, children, options);
16
16
 
17
- // ✅ Auto-render after microtask
17
+ // ✅ Auto-render to 'app' on next tick
18
18
  queueMicrotask(() => {
19
- if (!instance.container) {
19
+ if (!instance._container) {
20
20
  instance.render('app');
21
21
  }
22
22
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ZStack.d.ts","sourceRoot":"","sources":["ZStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEzD,qBAAa,MAAO,SAAQ,SAAS;IACjC,SAAS,CAAC,aAAa,SAAgB;gBAE3B,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB;IAIzF,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM;CAY3F;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAE5G"}
1
+ {"version":3,"file":"ZStack.d.ts","sourceRoot":"","sources":["ZStack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEzD,qBAAa,MAAO,SAAQ,SAAS;IACjC,SAAS,CAAC,aAAa,SAAgB;gBAE3B,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB;IAIzF,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM;CAa3F;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAE5G"}
@@ -7,8 +7,9 @@ export class ZStack extends BaseStack {
7
7
  static create(children, options = {}) {
8
8
  const id = `zstack-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
9
9
  const instance = new ZStack(id, children, options);
10
+ // ✅ Auto-render to 'app' on next tick
10
11
  queueMicrotask(() => {
11
- if (!instance.container) {
12
+ if (!instance._container) {
12
13
  instance.render('app');
13
14
  }
14
15
  });
@@ -11,8 +11,9 @@ export class ZStack extends BaseStack {
11
11
  const id = `zstack-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
12
12
  const instance = new ZStack(id, children, options);
13
13
 
14
+ // ✅ Auto-render to 'app' on next tick
14
15
  queueMicrotask(() => {
15
- if (!instance.container) {
16
+ if (!instance._container) {
16
17
  instance.render('app');
17
18
  }
18
19
  });
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=VStack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VStack.d.ts","sourceRoot":"","sources":["VStack.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ "use strict";
File without changes
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=ZStack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZStack.d.ts","sourceRoot":"","sources":["ZStack.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ "use strict";
File without changes
@@ -250,15 +250,29 @@ export class JuxCompiler {
250
250
  const sourceSnapshot = {};
251
251
 
252
252
  const juxImports = new Set();
253
- const layoutImports = new Set(); // ✅ Track layout imports separately
253
+ const layoutImports = new Set();
254
+ let needsJuxFacade = false; // ✅ Track if jux facade is needed
254
255
 
255
- // Scan for imports
256
256
  [...views, ...dataModules, ...sharedModules].forEach(m => {
257
+ // ✅ Detect jux.something() usage
258
+ if (/jux\s*\.\s*\w+\s*\(/g.test(m.content)) {
259
+ needsJuxFacade = true;
260
+ }
261
+
262
+ // Detect layout usage (both import and direct usage)
263
+ const layoutPattern = /(VStack|HStack|ZStack)\s*\.\s*create/g;
264
+ const layoutMatches = m.content.match(layoutPattern);
265
+ if (layoutMatches) {
266
+ layoutMatches.forEach(match => {
267
+ const layoutName = match.split('.')[0].trim();
268
+ layoutImports.add(layoutName);
269
+ });
270
+ }
271
+
257
272
  // Regular juxscript imports
258
273
  for (const match of m.content.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]juxscript['"]/g)) {
259
274
  match[1].split(',').map(s => s.trim()).forEach(imp => {
260
275
  if (imp) {
261
- // ✅ Separate layout imports
262
276
  if (imp === 'VStack' || imp === 'HStack' || imp === 'ZStack') {
263
277
  layoutImports.add(imp);
264
278
  } else {
@@ -269,7 +283,7 @@ export class JuxCompiler {
269
283
  }
270
284
  });
271
285
 
272
- // ✅ Import layouts separately
286
+ // ✅ Import layouts
273
287
  if (layoutImports.size > 0) {
274
288
  entry += `import { ${[...layoutImports].sort().join(', ')} } from 'juxscript';\n`;
275
289
  }
@@ -279,6 +293,12 @@ export class JuxCompiler {
279
293
  entry += `import { ${[...juxImports].sort().join(', ')} } from 'juxscript';\n\n`;
280
294
  }
281
295
 
296
+ // ✅ Import jux facade if needed
297
+ if (needsJuxFacade) {
298
+ entry += `import { jux } from 'juxscript';\n`;
299
+ entry += `window.jux = jux;\n`;
300
+ }
301
+
282
302
  // Data and shared modules
283
303
  dataModules.forEach(m => {
284
304
  entry += `import * as ${this.sanitizeName(m.name)}Data from './jux/${m.file}';\n`;
@@ -291,7 +311,7 @@ export class JuxCompiler {
291
311
  dataModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Data);\n`);
292
312
  sharedModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Shared);\n`);
293
313
 
294
- // ✅ Expose layouts to window
314
+ // ✅ Expose layouts to window (critical for nested usage)
295
315
  if (layoutImports.size > 0) {
296
316
  entry += `\n// Expose layout components\n`;
297
317
  layoutImports.forEach(layout => {
@@ -610,52 +630,43 @@ navigate(location.pathname);
610
630
  target: 'es2020',
611
631
  sourcemap: true,
612
632
  external: [],
613
- // ✅ Tell esbuild how to resolve .jux files
614
- loader: {
633
+ // ✅ Tell esbuild how to resolve .jux filesharset="UTF-8">
634
+ loader: { <meta name="viewport" content="width=device-width, initial-scale=1.0">
615
635
  '.jux': 'js' // Treat .jux files as JavaScript
616
- }
636
+ } <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/juxscript@latest/lib/layouts/default.css">
617
637
  });
618
638
 
619
- // Generate index.html
639
+ // Generate index.html <div id="app"></div>
620
640
  console.log('📄 Generating index.html...');
621
641
  const indexHtml = `<!DOCTYPE html>
622
642
  <html lang="en">
623
- <head>
624
- <meta charset="UTF-8">
625
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
626
- <title>JUX App</title>
627
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/juxscript@latest/lib/layouts/default.css">
628
- </head>
629
- <body>
630
- <div id="app"></div>
631
- <script type="module" src="/bundle.js"></script>
632
643
  </body>
633
- </html>`;
644
+ </html>`;this.distDir, 'index.html'), indexHtml);
634
645
 
635
646
  fs.writeFileSync(path.join(this.distDir, 'index.html'), indexHtml);
636
-
637
- // Copy public folder
638
647
  this.copyPublicFolder();
639
-
640
- // Write source snapshot for error overlay
648
+ // Copy public folder
649
+ this.copyPublicFolder(); // Write source snapshot for error overlay
650
+ apshotPath = path.join(this.distDir, '__jux_sources.json');
651
+ // Write source snapshot for error overlaynapshotPath, JSON.stringify(this._sourceSnapshot, null, 2));
641
652
  const snapshotPath = path.join(this.distDir, '__jux_sources.json');
642
- fs.writeFileSync(snapshotPath, JSON.stringify(this._sourceSnapshot, null, 2));
643
-
644
- const endTime = Date.now();
653
+ fs.writeFileSync(snapshotPath, JSON.stringify(this._sourceSnapshot, null, 2)););
654
+ nsole.log(`\n✅ Build complete in ${endTime - startTime}ms`);
655
+ const endTime = Date.now(); console.log(` Output: ${this.distDir}`);
645
656
  console.log(`\n✅ Build complete in ${endTime - startTime}ms`);
646
657
  console.log(` Output: ${this.distDir}`);
647
658
 
648
659
  this.reportValidationIssues();
649
660
 
650
- return {
651
- success: true,
652
- errors: this._validationIssues || [],
661
+ return {ionIssues || [],
662
+ success: true, distDir: this.distDir
663
+ errors: this._validationIssues || [], };
653
664
  distDir: this.distDir
654
- };
655
-
665
+ }; } catch (err) {
666
+ console.error('❌ Build failed:', err.message);
656
667
  } catch (err) {
657
- console.error('❌ Build failed:', err.message);
658
- console.error(err.stack);
668
+ console.error('❌ Build failed:', err.message); return {
669
+ console.error(err.stack); false,
659
670
  return {
660
671
  success: false,
661
672
  errors: [err.message]
@@ -664,77 +675,86 @@ navigate(location.pathname);
664
675
  }
665
676
 
666
677
  /**
667
- * Copy public folder contents to dist
668
- */
669
- copyPublicFolder() {
670
- // ✅ Use configured public path or resolve from paths object
678
+ * Copy public folder contents to dist copyPublicFolder() {
679
+ */from paths object
680
+ copyPublicFolder() { const publicSrc = this.paths.public
681
+ // ✅ Use configured public path or resolve from paths object this.paths.public
671
682
  const publicSrc = this.paths.public
672
683
  ? this.paths.public
673
- : path.resolve(process.cwd(), this.publicDir);
684
+ : path.resolve(process.cwd(), this.publicDir);ync(publicSrc)) {
674
685
 
675
686
  if (!fs.existsSync(publicSrc)) {
676
687
  return; // No public folder, skip
677
- }
688
+ } console.log('📦 Copying public assets...');
678
689
 
679
690
  console.log('📦 Copying public assets...');
680
-
681
- try {
691
+ this._copyDirRecursive(publicSrc, this.distDir, 0);
692
+ try {ed');
682
693
  this._copyDirRecursive(publicSrc, this.distDir, 0);
683
- console.log('✅ Public assets copied');
694
+ console.log('✅ Public assets copied'); console.warn('⚠️ Error copying public folder:', err.message);
684
695
  } catch (err) {
685
696
  console.warn('⚠️ Error copying public folder:', err.message);
686
697
  }
687
- }
698
+ } /**
688
699
 
689
700
  /**
690
- * Recursively copy directory contents
691
- */
701
+ * Recursively copy directory contents _copyDirRecursive(src, dest, depth = 0) {
702
+ */(src, { withFileTypes: true });
692
703
  _copyDirRecursive(src, dest, depth = 0) {
693
704
  const entries = fs.readdirSync(src, { withFileTypes: true });
694
705
 
695
- entries.forEach(entry => {
706
+ entries.forEach(entry => {ntry.name.startsWith('.')) return;
696
707
  // Skip hidden files and directories
697
- if (entry.name.startsWith('.')) return;
698
-
708
+ if (entry.name.startsWith('.')) return;cPath = path.join(src, entry.name);
709
+ path.join(dest, entry.name);
699
710
  const srcPath = path.join(src, entry.name);
700
- const destPath = path.join(dest, entry.name);
711
+ const destPath = path.join(dest, entry.name); if (entry.isDirectory()) {
701
712
 
702
- if (entry.isDirectory()) {
703
- // Create directory and recurse
713
+ if (entry.isDirectory()) {(destPath)) {
714
+ // Create directory and recurserue });
704
715
  if (!fs.existsSync(destPath)) {
705
- fs.mkdirSync(destPath, { recursive: true });
706
- }
707
- this._copyDirRecursive(srcPath, destPath, depth + 1);
708
- } else {
716
+ fs.mkdirSync(destPath, { recursive: true });pth + 1);
717
+ }e {
718
+ this._copyDirRecursive(srcPath, destPath, depth + 1); // Copy file
719
+ } else { fs.copyFileSync(srcPath, destPath);
709
720
  // Copy file
710
- fs.copyFileSync(srcPath, destPath);
711
-
712
- // Log files at root level only
713
- if (depth === 0) {
714
- const ext = path.extname(entry.name);
721
+ fs.copyFileSync(srcPath, destPath); // Log files at root level only
722
+ if (depth === 0) {
723
+ // Log files at root level only = path.extname(entry.name);
724
+ if (depth === 0) { const icon = this._getFileIcon(ext);
725
+ const ext = path.extname(entry.name);sole.log(` ${icon} ${entry.name}`);
715
726
  const icon = this._getFileIcon(ext);
716
727
  console.log(` ${icon} ${entry.name}`);
717
728
  }
718
729
  }
719
730
  });
720
731
  }
721
-
732
+ ype
722
733
  /**
723
734
  * Get icon for file type
724
735
  */
725
- _getFileIcon(ext) {
736
+ _getFileIcon(ext) {,
726
737
  const icons = {
727
738
  '.html': '📄',
728
- '.css': '🎨',
729
- '.js': '📜',
730
- '.json': '📋',
739
+ '.css': '🎨',,
740
+ '.js': '📜',',
741
+ '.json': '📋', '.jpg': '🖼️',
731
742
  '.png': '🖼️',
732
- '.jpg': '🖼️',
733
- '.jpeg': '🖼️',
734
- '.gif': '🖼️',
735
- '.svg': '🎨',
736
- '.ico': '🔖',
737
- '.woff': '🔤',
743
+ '.jpg': '🖼️', '.gif': '🖼️',
744
+ '.jpeg': '🖼️', '.svg': '🎨',
745
+ '.gif': '🖼️', '.ico': '🔖',
746
+
747
+
748
+
749
+
750
+
751
+
752
+
753
+
754
+
755
+
756
+
757
+ } } return icons[ext.toLowerCase()] || '📦'; }; '.eot': '🔤' '.ttf': '🔤', '.woff2': '🔤', '.woff': '🔤', '.ico': '🔖', '.svg': '🎨', '.woff': '🔤',
738
758
  '.woff2': '🔤',
739
759
  '.ttf': '🔤',
740
760
  '.eot': '🔤'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.98",
3
+ "version": "1.1.100",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",