juxscript 1.1.97 → 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:10:28.490Z",
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,18 +250,56 @@ export class JuxCompiler {
250
250
  const sourceSnapshot = {};
251
251
 
252
252
  const juxImports = new Set();
253
+ const layoutImports = new Set();
254
+ let needsJuxFacade = false; // ✅ Track if jux facade is needed
255
+
253
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
+
272
+ // Regular juxscript imports
254
273
  for (const match of m.content.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]juxscript['"]/g)) {
255
274
  match[1].split(',').map(s => s.trim()).forEach(imp => {
256
- if (imp) juxImports.add(imp);
275
+ if (imp) {
276
+ if (imp === 'VStack' || imp === 'HStack' || imp === 'ZStack') {
277
+ layoutImports.add(imp);
278
+ } else {
279
+ juxImports.add(imp);
280
+ }
281
+ }
257
282
  });
258
283
  }
259
284
  });
260
285
 
286
+ // ✅ Import layouts
287
+ if (layoutImports.size > 0) {
288
+ entry += `import { ${[...layoutImports].sort().join(', ')} } from 'juxscript';\n`;
289
+ }
290
+
291
+ // ✅ Import regular components
261
292
  if (juxImports.size > 0) {
262
293
  entry += `import { ${[...juxImports].sort().join(', ')} } from 'juxscript';\n\n`;
263
294
  }
264
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
+
302
+ // Data and shared modules
265
303
  dataModules.forEach(m => {
266
304
  entry += `import * as ${this.sanitizeName(m.name)}Data from './jux/${m.file}';\n`;
267
305
  });
@@ -273,8 +311,18 @@ export class JuxCompiler {
273
311
  dataModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Data);\n`);
274
312
  sharedModules.forEach(m => entry += `Object.assign(window, ${this.sanitizeName(m.name)}Shared);\n`);
275
313
 
314
+ // ✅ Expose layouts to window (critical for nested usage)
315
+ if (layoutImports.size > 0) {
316
+ entry += `\n// Expose layout components\n`;
317
+ layoutImports.forEach(layout => {
318
+ entry += `window.${layout} = ${layout};\n`;
319
+ });
320
+ }
321
+
322
+ // ✅ Expose regular components
276
323
  if (juxImports.size > 0) {
277
- entry += `\nObject.assign(window, { ${[...juxImports].join(', ')} });\n`;
324
+ entry += `\n// Expose components\n`;
325
+ entry += `Object.assign(window, { ${[...juxImports].join(', ')} });\n`;
278
326
  }
279
327
 
280
328
  entry += `\n// --- VIEW FUNCTIONS ---\n`;
@@ -582,52 +630,43 @@ navigate(location.pathname);
582
630
  target: 'es2020',
583
631
  sourcemap: true,
584
632
  external: [],
585
- // ✅ Tell esbuild how to resolve .jux files
586
- loader: {
633
+ // ✅ Tell esbuild how to resolve .jux filesharset="UTF-8">
634
+ loader: { <meta name="viewport" content="width=device-width, initial-scale=1.0">
587
635
  '.jux': 'js' // Treat .jux files as JavaScript
588
- }
636
+ } <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/juxscript@latest/lib/layouts/default.css">
589
637
  });
590
638
 
591
- // Generate index.html
639
+ // Generate index.html <div id="app"></div>
592
640
  console.log('📄 Generating index.html...');
593
641
  const indexHtml = `<!DOCTYPE html>
594
642
  <html lang="en">
595
- <head>
596
- <meta charset="UTF-8">
597
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
598
- <title>JUX App</title>
599
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/juxscript@latest/lib/layouts/default.css">
600
- </head>
601
- <body>
602
- <div id="app"></div>
603
- <script type="module" src="/bundle.js"></script>
604
643
  </body>
605
- </html>`;
644
+ </html>`;this.distDir, 'index.html'), indexHtml);
606
645
 
607
646
  fs.writeFileSync(path.join(this.distDir, 'index.html'), indexHtml);
608
-
609
- // Copy public folder
610
647
  this.copyPublicFolder();
611
-
612
- // 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));
613
652
  const snapshotPath = path.join(this.distDir, '__jux_sources.json');
614
- fs.writeFileSync(snapshotPath, JSON.stringify(this._sourceSnapshot, null, 2));
615
-
616
- 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}`);
617
656
  console.log(`\n✅ Build complete in ${endTime - startTime}ms`);
618
657
  console.log(` Output: ${this.distDir}`);
619
658
 
620
659
  this.reportValidationIssues();
621
660
 
622
- return {
623
- success: true,
624
- errors: this._validationIssues || [],
661
+ return {ionIssues || [],
662
+ success: true, distDir: this.distDir
663
+ errors: this._validationIssues || [], };
625
664
  distDir: this.distDir
626
- };
627
-
665
+ }; } catch (err) {
666
+ console.error('❌ Build failed:', err.message);
628
667
  } catch (err) {
629
- console.error('❌ Build failed:', err.message);
630
- console.error(err.stack);
668
+ console.error('❌ Build failed:', err.message); return {
669
+ console.error(err.stack); false,
631
670
  return {
632
671
  success: false,
633
672
  errors: [err.message]
@@ -636,77 +675,86 @@ navigate(location.pathname);
636
675
  }
637
676
 
638
677
  /**
639
- * Copy public folder contents to dist
640
- */
641
- copyPublicFolder() {
642
- // ✅ 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
643
682
  const publicSrc = this.paths.public
644
683
  ? this.paths.public
645
- : path.resolve(process.cwd(), this.publicDir);
684
+ : path.resolve(process.cwd(), this.publicDir);ync(publicSrc)) {
646
685
 
647
686
  if (!fs.existsSync(publicSrc)) {
648
687
  return; // No public folder, skip
649
- }
688
+ } console.log('📦 Copying public assets...');
650
689
 
651
690
  console.log('📦 Copying public assets...');
652
-
653
- try {
691
+ this._copyDirRecursive(publicSrc, this.distDir, 0);
692
+ try {ed');
654
693
  this._copyDirRecursive(publicSrc, this.distDir, 0);
655
- console.log('✅ Public assets copied');
694
+ console.log('✅ Public assets copied'); console.warn('⚠️ Error copying public folder:', err.message);
656
695
  } catch (err) {
657
696
  console.warn('⚠️ Error copying public folder:', err.message);
658
697
  }
659
- }
698
+ } /**
660
699
 
661
700
  /**
662
- * Recursively copy directory contents
663
- */
701
+ * Recursively copy directory contents _copyDirRecursive(src, dest, depth = 0) {
702
+ */(src, { withFileTypes: true });
664
703
  _copyDirRecursive(src, dest, depth = 0) {
665
704
  const entries = fs.readdirSync(src, { withFileTypes: true });
666
705
 
667
- entries.forEach(entry => {
706
+ entries.forEach(entry => {ntry.name.startsWith('.')) return;
668
707
  // Skip hidden files and directories
669
- if (entry.name.startsWith('.')) return;
670
-
708
+ if (entry.name.startsWith('.')) return;cPath = path.join(src, entry.name);
709
+ path.join(dest, entry.name);
671
710
  const srcPath = path.join(src, entry.name);
672
- const destPath = path.join(dest, entry.name);
711
+ const destPath = path.join(dest, entry.name); if (entry.isDirectory()) {
673
712
 
674
- if (entry.isDirectory()) {
675
- // Create directory and recurse
713
+ if (entry.isDirectory()) {(destPath)) {
714
+ // Create directory and recurserue });
676
715
  if (!fs.existsSync(destPath)) {
677
- fs.mkdirSync(destPath, { recursive: true });
678
- }
679
- this._copyDirRecursive(srcPath, destPath, depth + 1);
680
- } 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);
681
720
  // Copy file
682
- fs.copyFileSync(srcPath, destPath);
683
-
684
- // Log files at root level only
685
- if (depth === 0) {
686
- 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}`);
687
726
  const icon = this._getFileIcon(ext);
688
727
  console.log(` ${icon} ${entry.name}`);
689
728
  }
690
729
  }
691
730
  });
692
731
  }
693
-
732
+ ype
694
733
  /**
695
734
  * Get icon for file type
696
735
  */
697
- _getFileIcon(ext) {
736
+ _getFileIcon(ext) {,
698
737
  const icons = {
699
738
  '.html': '📄',
700
- '.css': '🎨',
701
- '.js': '📜',
702
- '.json': '📋',
739
+ '.css': '🎨',,
740
+ '.js': '📜',',
741
+ '.json': '📋', '.jpg': '🖼️',
703
742
  '.png': '🖼️',
704
- '.jpg': '🖼️',
705
- '.jpeg': '🖼️',
706
- '.gif': '🖼️',
707
- '.svg': '🎨',
708
- '.ico': '🔖',
709
- '.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': '🔤',
710
758
  '.woff2': '🔤',
711
759
  '.ttf': '🔤',
712
760
  '.eot': '🔤'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.97",
3
+ "version": "1.1.100",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",