juxscript 1.0.56 → 1.0.57

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.
@@ -269,114 +269,8 @@ export class Radio extends FormInput<RadioState> {
269
269
  }
270
270
 
271
271
  container.appendChild(wrapper);
272
- this._injectRadioStyles();
273
-
274
272
  return this;
275
273
  }
276
-
277
- private _injectRadioStyles(): void {
278
- const styleId = 'jux-radio-styles';
279
- if (document.getElementById(styleId)) return;
280
-
281
- const style = document.createElement('style');
282
- style.id = styleId;
283
- style.textContent = `
284
- .jux-radio {
285
- margin-bottom: 12px;
286
- }
287
-
288
- .jux-radio-options {
289
- display: flex;
290
- gap: 8px;
291
- }
292
-
293
- /* ✅ Vertical orientation (default) */
294
- .jux-radio-vertical {
295
- flex-direction: column;
296
- }
297
-
298
- /* ✅ Horizontal orientation */
299
- .jux-radio-horizontal {
300
- flex-direction: row;
301
- flex-wrap: wrap;
302
- }
303
-
304
- .jux-radio-option {
305
- display: inline-flex;
306
- align-items: center;
307
- cursor: pointer;
308
- user-select: none;
309
- position: relative;
310
- }
311
-
312
- .jux-radio-input {
313
- position: absolute;
314
- opacity: 0;
315
- cursor: pointer;
316
- height: 0;
317
- width: 0;
318
- }
319
-
320
- .jux-radio-mark {
321
- position: relative;
322
- height: 20px;
323
- width: 20px;
324
- border: 2px solid #d1d5db;
325
- border-radius: 50%;
326
- background-color: white;
327
- transition: all 0.2s;
328
- display: flex;
329
- align-items: center;
330
- justify-content: center;
331
- margin-right: 8px;
332
- }
333
-
334
- .jux-radio-mark::after {
335
- content: '';
336
- position: absolute;
337
- width: 10px;
338
- height: 10px;
339
- border-radius: 50%;
340
- background-color: white;
341
- opacity: 0;
342
- transition: opacity 0.2s;
343
- }
344
-
345
- .jux-radio-input:checked ~ .jux-radio-mark {
346
- background-color: #3b82f6;
347
- border-color: #3b82f6;
348
- }
349
-
350
- .jux-radio-input:checked ~ .jux-radio-mark::after {
351
- opacity: 1;
352
- }
353
-
354
- .jux-radio-input:disabled ~ .jux-radio-mark {
355
- background-color: #f3f4f6;
356
- border-color: #d1d5db;
357
- cursor: not-allowed;
358
- }
359
-
360
- .jux-radio-input:disabled ~ .jux-radio-label-text {
361
- color: #9ca3af;
362
- cursor: not-allowed;
363
- }
364
-
365
- .jux-radio-input:focus ~ .jux-radio-mark {
366
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
367
- }
368
-
369
- .jux-radio-input.jux-input-invalid ~ .jux-radio-mark {
370
- border-color: #ef4444;
371
- }
372
-
373
- .jux-radio-label-text {
374
- font-size: 14px;
375
- color: #374151;
376
- }
377
- `;
378
- document.head.appendChild(style);
379
- }
380
274
  }
381
275
 
382
276
  export function radio(id: string, options: RadioOptions = {}): Radio {
@@ -239,38 +239,12 @@ export class Select extends FormInput<SelectState> {
239
239
  }
240
240
 
241
241
  container.appendChild(wrapper);
242
- this._injectSelectStyles();
243
- this._injectFormStyles();
242
+
244
243
 
245
244
  return this;
246
245
  }
247
246
 
248
- private _injectSelectStyles(): void {
249
- const styleId = 'jux-select-styles';
250
- if (document.getElementById(styleId)) return;
251
-
252
- const style = document.createElement('style');
253
- style.id = styleId;
254
- style.textContent = `
255
- .jux-select-container {
256
- position: relative;
257
- }
258
-
259
- .jux-select-element {
260
- appearance: none;
261
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b7280' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
262
- background-repeat: no-repeat;
263
- background-position: right 12px center;
264
- padding-right: 36px;
265
- cursor: pointer;
266
- }
267
-
268
- .jux-select-element:disabled {
269
- cursor: not-allowed;
270
- }
271
- `;
272
- document.head.appendChild(style);
273
- }
247
+
274
248
  }
275
249
 
276
250
  export function select(id: string, options: SelectOptions = {}): Select {
@@ -157,80 +157,9 @@ export class Sidebar extends BaseComponent<SidebarState> {
157
157
 
158
158
  container.appendChild(sidebar);
159
159
  this._sidebar = sidebar;
160
- this._injectDefaultStyles();
161
160
 
162
161
  return this;
163
162
  }
164
-
165
- private _injectDefaultStyles(): void {
166
- const styleId = 'jux-sidebar-styles';
167
- if (document.getElementById(styleId)) return;
168
-
169
- const style = document.createElement('style');
170
- style.id = styleId;
171
- style.textContent = `
172
- .jux-sidebar {
173
- position: relative;
174
- height: 100vh;
175
- background: #f9fafb;
176
- border-right: 1px solid #e5e7eb;
177
- transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
178
- overflow: hidden;
179
- }
180
-
181
- .jux-sidebar-right {
182
- border-right: none;
183
- border-left: 1px solid #e5e7eb;
184
- }
185
-
186
- .jux-sidebar-toggle {
187
- position: absolute;
188
- bottom: 20px;
189
- right: 20px;
190
- width: 32px;
191
- height: 32px;
192
- border: none;
193
- background: #3b82f6;
194
- color: white;
195
- border-radius: 6px;
196
- cursor: pointer;
197
- font-size: 14px;
198
- display: flex;
199
- align-items: center;
200
- justify-content: center;
201
- transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
202
- z-index: 100;
203
- box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
204
- }
205
-
206
- .jux-sidebar-toggle:hover {
207
- background: #2563eb;
208
- transform: scale(1.05);
209
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
210
- }
211
-
212
- .jux-sidebar-toggle:active {
213
- transform: scale(0.95);
214
- }
215
-
216
- .jux-sidebar-collapsed {
217
- width: 60px !important;
218
- }
219
-
220
- .jux-sidebar-collapsed > *:not(.jux-sidebar-toggle) {
221
- opacity: 0;
222
- pointer-events: none;
223
- transition: opacity 0.2s ease-out;
224
- }
225
-
226
- .jux-sidebar > *:not(.jux-sidebar-toggle) {
227
- opacity: 1;
228
- pointer-events: auto;
229
- transition: opacity 0.3s ease-in 0.1s;
230
- }
231
- `;
232
- document.head.appendChild(style);
233
- }
234
163
  }
235
164
 
236
165
  export function sidebar(id: string, options: SidebarOptions = {}): Sidebar {
@@ -235,94 +235,9 @@ export class Switch extends FormInput<SwitchState> {
235
235
  }
236
236
 
237
237
  container.appendChild(wrapper);
238
- this._injectSwitchStyles();
239
238
 
240
239
  return this;
241
240
  }
242
-
243
- private _injectSwitchStyles(): void {
244
- const styleId = 'jux-switch-styles';
245
- if (document.getElementById(styleId)) return;
246
-
247
- const style = document.createElement('style');
248
- style.id = styleId;
249
- style.textContent = `
250
- .jux-switch {
251
- margin-bottom: 12px;
252
- }
253
-
254
- .jux-switch-container {
255
- display: inline-flex;
256
- align-items: center;
257
- cursor: pointer;
258
- user-select: none;
259
- position: relative;
260
- }
261
-
262
- .jux-switch-input {
263
- position: absolute;
264
- opacity: 0;
265
- cursor: pointer;
266
- height: 0;
267
- width: 0;
268
- }
269
-
270
- .jux-switch-slider {
271
- position: relative;
272
- width: 48px;
273
- height: 24px;
274
- background-color: #d1d5db;
275
- border-radius: 24px;
276
- transition: background-color 0.3s;
277
- margin-right: 8px;
278
- }
279
-
280
- .jux-switch-slider::before {
281
- content: '';
282
- position: absolute;
283
- height: 18px;
284
- width: 18px;
285
- left: 3px;
286
- top: 3px;
287
- background-color: white;
288
- border-radius: 50%;
289
- transition: transform 0.3s;
290
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
291
- }
292
-
293
- .jux-switch-input:checked ~ .jux-switch-slider {
294
- background-color: #3b82f6;
295
- }
296
-
297
- .jux-switch-input:checked ~ .jux-switch-slider::before {
298
- transform: translateX(24px);
299
- }
300
-
301
- .jux-switch-input:disabled ~ .jux-switch-slider {
302
- background-color: #f3f4f6;
303
- cursor: not-allowed;
304
- }
305
-
306
- .jux-switch-input:disabled ~ .jux-switch-label-text {
307
- color: #9ca3af;
308
- cursor: not-allowed;
309
- }
310
-
311
- .jux-switch-input:focus ~ .jux-switch-slider {
312
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
313
- }
314
-
315
- .jux-switch-input.jux-input-invalid ~ .jux-switch-slider {
316
- box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
317
- }
318
-
319
- .jux-switch-label-text {
320
- font-size: 14px;
321
- color: #374151;
322
- }
323
- `;
324
- document.head.appendChild(style);
325
- }
326
241
  }
327
242
 
328
243
  export function switchComponent(id: string, options: SwitchOptions = {}): Switch {
@@ -91,7 +91,6 @@ export class Tooltip extends BaseComponent<TooltipState> {
91
91
  this._hide();
92
92
  });
93
93
 
94
- this._injectTooltipStyles();
95
94
  }
96
95
 
97
96
  private _show(): void {
@@ -138,34 +137,6 @@ export class Tooltip extends BaseComponent<TooltipState> {
138
137
  console.warn('Tooltip: Use .attachTo(element) instead of .render()');
139
138
  return this;
140
139
  }
141
-
142
- private _injectTooltipStyles(): void {
143
- const styleId = 'jux-tooltip-styles';
144
- if (document.getElementById(styleId)) return;
145
-
146
- const style = document.createElement('style');
147
- style.id = styleId;
148
- style.textContent = `
149
- .jux-tooltip {
150
- position: fixed;
151
- background: #1f2937;
152
- color: white;
153
- padding: 6px 12px;
154
- border-radius: 6px;
155
- font-size: 12px;
156
- white-space: nowrap;
157
- z-index: 10000;
158
- pointer-events: none;
159
- animation: jux-tooltip-fade 0.2s;
160
- }
161
-
162
- @keyframes jux-tooltip-fade {
163
- from { opacity: 0; }
164
- to { opacity: 1; }
165
- }
166
- `;
167
- document.head.appendChild(style);
168
- }
169
140
  }
170
141
 
171
142
  export function tooltip(id: string, options: TooltipOptions = {}): Tooltip {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.56",
3
+ "version": "1.0.57",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",
@@ -50,7 +50,11 @@
50
50
  "scripts": {
51
51
  "build": "tsc",
52
52
  "dev": "tsc --watch",
53
- "prepublishOnly": "npm run build"
53
+ "prepublishOnly": "npm run build",
54
+ "analyze": "node scripts/analyze-components.js",
55
+ "check-styles": "node scripts/check-inline-styles.js",
56
+ "find-inject-styles": "node scripts/find-inject-styles.js",
57
+ "find-jux-classes": "node scripts/find-jux-classes.js"
54
58
  },
55
59
  "dependencies": {
56
60
  "chokidar": "^3.5.3",
@@ -63,6 +67,7 @@
63
67
  "@types/express": "^4.17.17",
64
68
  "@types/node": "^20.0.0",
65
69
  "@types/ws": "^8.5.5",
70
+ "jsdom": "^27.4.0",
66
71
  "typescript": "^5.0.0"
67
72
  }
68
73
  }
@@ -0,0 +1,190 @@
1
+ import { jux, state } from 'juxscript';
2
+ jux.include('layout.css');
3
+
4
+ // ═══════════════════════════════════════════════════════════════════
5
+ // ALL COMPONENTS SHOWCASE - Maximum DOM nodes per component
6
+ // ═══════════════════════════════════════════════════════════════════
7
+
8
+ // Hero - All DOM-generating options
9
+ jux.hero('all-hero', {
10
+ title: 'Hero Title',
11
+ subtitle: 'Hero Subtitle',
12
+ content: 'Hero body content goes here',
13
+ cta: 'Call to Action',
14
+ ctaLink: '#',
15
+ backgroundImage: 'https://via.placeholder.com/1200x400',
16
+ backgroundOverlay: true,
17
+ centered: true
18
+ }).render();
19
+
20
+ jux.alert('all-alert').render();
21
+ jux.badge('all-badge').render();
22
+ jux.button('all-button').render();
23
+ jux.card('all-card').render();
24
+ jux.checkbox('all-checkbox').render();
25
+ jux.code('all-code').render();
26
+ jux.container('all-container').render();
27
+ jux.datepicker('all-datepicker').render();
28
+ jux.dropdown('all-dropdown', {
29
+ items: [{ label: 'Item 1', value: '1' }, { label: 'Item 2', value: '2' }]
30
+ }).render();
31
+ jux.fileupload('all-fileupload').render();
32
+ jux.heading('all-h1').render();
33
+ // jux.icon('all-icon').render();
34
+ jux.input('all-input').render();
35
+ jux.kpicard('all-kpicard').render();
36
+ jux.list('all-list', {
37
+ items: ['Item 1', 'Item 2', 'Item 3']
38
+ }).render();
39
+ jux.loading('all-loading').render();
40
+ jux.menu('all-menu', {
41
+ items: [{ label: 'Home', path: '/' }, { label: 'About', path: '/about' }]
42
+ }).render();
43
+ jux.nav('all-nav', {
44
+ items: [{ label: 'Home', path: '/' }, { label: 'About', path: '/about' }]
45
+ }).render();
46
+ jux.paragraph('all-paragraph').render();
47
+ jux.progress('all-progress').render();
48
+ jux.radio('all-radio', {
49
+ options: [{ label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }]
50
+ }).render();
51
+ jux.select('all-select', {
52
+ options: [{ label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }]
53
+ }).render();
54
+ jux.sidebar('all-sidebar').render();
55
+ jux.switch('all-switch').render();
56
+ jux.table('all-table', {
57
+ columns: ['Name', 'Age'],
58
+ rows: [
59
+ { Name: 'John', Age: '25' },
60
+ { Name: 'Jane', Age: '30' }
61
+ ]
62
+ }).render();
63
+ jux.tabs('all-tabs', {
64
+ tabs: [{ label: 'Tab 1', content: 'Content 1' }, { label: 'Tab 2', content: 'Content 2' }]
65
+ }).render();
66
+
67
+ jux.button('tooltip-trigger').label('Hover for tooltip').render();
68
+ jux.tooltip('all-tooltip', {
69
+ text: 'Tooltip content',
70
+ position: 'top'
71
+ }).render('#tooltip-trigger');
72
+
73
+ jux.view('all-view').render();
74
+
75
+ // ═══════════════════════════════════════════════════════════════════
76
+ // SELF-ANALYSIS: Build component tree from #app
77
+ // ═══════════════════════════════════════════════════════════════════
78
+
79
+ function buildComponentTree(element, depth = 0) {
80
+ const indent = ' '.repeat(depth);
81
+ const tagName = element.tagName.toLowerCase();
82
+ // ✅ Fix: Check if className exists and is a string
83
+ const classes = (element.className && typeof element.className === 'string')
84
+ ? `.${element.className.split(' ').join('.')}`
85
+ : '';
86
+
87
+ let tree = `${indent}${tagName}${classes}\n`;
88
+
89
+ // Recurse through children
90
+ Array.from(element.children).forEach(child => {
91
+ tree += buildComponentTree(child, depth + 1);
92
+ });
93
+
94
+ return tree;
95
+ }
96
+
97
+ function analyzeComponents() {
98
+ const app = document.getElementById('app');
99
+ if (!app) return 'No #app container found';
100
+
101
+ const components = Array.from(app.children);
102
+ const componentTypes = new Map();
103
+
104
+ let analysis = `═══════════════════════════════════════════════════════════════
105
+ COMPONENT TREE ANALYSIS
106
+ ═══════════════════════════════════════════════════════════════
107
+
108
+ Found ${components.length} components in #app
109
+
110
+ `;
111
+
112
+ components.forEach((component, index) => {
113
+ const componentId = component.id || `component-${index}`;
114
+ // ✅ Fix: Safely handle className
115
+ const className = (component.className && typeof component.className === 'string')
116
+ ? component.className
117
+ : '';
118
+ const rootClasses = className.split(' ').filter(c => c);
119
+ const primaryClass = rootClasses.find(c => c.startsWith('jux-')) || rootClasses[0] || 'unknown';
120
+
121
+ const componentName = primaryClass
122
+ .replace(/^jux-/, '')
123
+ .split('-')
124
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
125
+ .join('');
126
+
127
+ // Track component types
128
+ const typeName = primaryClass.replace(/^jux-/, '');
129
+ componentTypes.set(typeName, (componentTypes.get(typeName) || 0) + 1);
130
+
131
+ analysis += `┌─ ${componentName} (${componentId})\n`;
132
+ analysis += '│\n';
133
+
134
+ const tree = buildComponentTree(component);
135
+ tree.split('\n').forEach(line => {
136
+ if (line.trim()) {
137
+ analysis += `│ ${line}\n`;
138
+ }
139
+ });
140
+
141
+ analysis += `└${'─'.repeat(66)}\n\n`;
142
+ });
143
+
144
+ // Add summary
145
+ analysis += `═══════════════════════════════════════════════════════════════
146
+ SUMMARY
147
+ ═══════════════════════════════════════════════════════════════
148
+
149
+ Component Types:
150
+ `;
151
+
152
+ Array.from(componentTypes.entries())
153
+ .sort(([a], [b]) => a.localeCompare(b))
154
+ .forEach(([type, count]) => {
155
+ analysis += ` - ${type}: ${count}\n`;
156
+ });
157
+
158
+ analysis += `\nTotal: ${components.length} components rendered\n`;
159
+
160
+ return analysis;
161
+ }
162
+
163
+ // Wait for DOM to be fully rendered
164
+ setTimeout(() => {
165
+ const analysis = analyzeComponents();
166
+
167
+ // Create analysis display
168
+ jux.container('analysis-container')
169
+ .style('margin-top: 3rem; padding: 2rem; background: #1e1e1e; border-radius: 8px;')
170
+ .render();
171
+
172
+ jux.heading('analysis-title')
173
+ .level(2)
174
+ .text('🔍 Self-Analysis')
175
+ .style('color: #10b981; margin-bottom: 1rem;')
176
+ .render('#analysis-container');
177
+
178
+ jux.code('component-tree')
179
+ .language('text')
180
+ .code(analysis)
181
+ .style('background: #0d1117; color: #c9d1d9; font-size: 0.875rem; max-height: 600px; overflow-y: auto;')
182
+ .render('#analysis-container');
183
+
184
+ jux.paragraph('analysis-note')
185
+ .text('This tree was generated by JavaScript analyzing the DOM structure of this page.')
186
+ .style('margin-top: 1rem; color: #8b949e; font-size: 0.875rem;')
187
+ .render('#analysis-container');
188
+ }, 100);
189
+
190
+