juxscript 1.1.65 → 1.1.67

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,432 +1,421 @@
1
1
  import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
- import { registry } from './registry.js';
3
2
 
4
3
  // Event definitions
5
4
  const TRIGGER_EVENTS = [] as const;
6
- const CALLBACK_EVENTS = ['select', 'hover'] as const;
5
+ const CALLBACK_EVENTS = ['select', 'expand'] as const;
7
6
 
8
7
  export interface BlueprintOptions {
9
- componentId?: string;
10
- showClasses?: boolean;
11
- showIds?: boolean;
12
- showTags?: boolean;
13
- expandAll?: boolean;
14
- theme?: 'light' | 'dark' | 'auto';
15
- interactive?: boolean;
16
- style?: string;
17
- class?: string;
8
+ structureData?: any; // dom-structure-map.json
9
+ classData?: any; // css-classes-inventory.json
10
+ filter?: string; // Filter by component name
11
+ showMethods?: boolean;
12
+ expandAll?: boolean;
13
+ theme?: 'light' | 'dark' | 'auto';
14
+ style?: string;
15
+ class?: string;
18
16
  }
19
17
 
20
18
  interface BlueprintState extends BaseState {
21
- componentId: string;
22
- showClasses: boolean;
23
- showIds: boolean;
24
- showTags: boolean;
25
- expandAll: boolean;
26
- theme: string;
27
- interactive: boolean;
28
- selectedNode: string | null;
29
- hoveredNode: string | null;
19
+ structureData: any;
20
+ classData: any;
21
+ filter: string;
22
+ showMethods: boolean;
23
+ expandAll: boolean;
24
+ theme: string;
25
+ expandedComponents: Set<string>;
30
26
  }
31
27
 
32
- interface DOMNode {
33
- tag: string;
34
- classes: string[];
35
- id: string | null;
36
- depth: number;
37
- path: string;
38
- children: DOMNode[];
28
+ interface ComponentHierarchy {
29
+ tag: string;
30
+ classes: string[];
31
+ id: string | null;
32
+ children?: ComponentHierarchy[];
39
33
  }
40
34
 
41
35
  export class Blueprint extends BaseComponent<BlueprintState> {
42
- private _domStructure: DOMNode[] = [];
43
-
44
- constructor(id: string, options: BlueprintOptions = {}) {
45
- super(id, {
46
- componentId: options.componentId ?? '',
47
- showClasses: options.showClasses ?? true,
48
- showIds: options.showIds ?? true,
49
- showTags: options.showTags ?? true,
50
- expandAll: options.expandAll ?? true,
51
- theme: options.theme ?? 'auto',
52
- interactive: options.interactive ?? true,
53
- selectedNode: null,
54
- hoveredNode: null,
55
- style: options.style ?? '',
56
- class: options.class ?? ''
57
- });
36
+ constructor(id: string, options: BlueprintOptions = {}) {
37
+ super(id, {
38
+ structureData: options.structureData ?? null,
39
+ classData: options.classData ?? null,
40
+ filter: options.filter ?? '',
41
+ showMethods: options.showMethods ?? true,
42
+ expandAll: options.expandAll ?? false,
43
+ theme: options.theme ?? 'auto',
44
+ expandedComponents: new Set<string>(),
45
+ style: options.style ?? '',
46
+ class: options.class ?? ''
47
+ });
48
+ }
49
+
50
+ protected getTriggerEvents(): readonly string[] {
51
+ return TRIGGER_EVENTS;
52
+ }
53
+
54
+ protected getCallbackEvents(): readonly string[] {
55
+ return CALLBACK_EVENTS;
56
+ }
57
+
58
+ /* ═════════════════════════════════════════════════════════════════
59
+ * FLUENT API
60
+ * ═════════════════════════════════════════════════════════════════ */
61
+
62
+ structureData(data: any): this {
63
+ this.state.structureData = data;
64
+ return this;
65
+ }
66
+
67
+ classData(data: any): this {
68
+ this.state.classData = data;
69
+ return this;
70
+ }
71
+
72
+ filter(value: string): this {
73
+ this.state.filter = value;
74
+ return this;
75
+ }
76
+
77
+ showMethods(value: boolean): this {
78
+ this.state.showMethods = value;
79
+ return this;
80
+ }
81
+
82
+ expandAll(value: boolean): this {
83
+ this.state.expandAll = value;
84
+ return this;
85
+ }
86
+
87
+ theme(value: 'light' | 'dark' | 'auto'): this {
88
+ this.state.theme = value;
89
+ return this;
90
+ }
91
+
92
+ toggleComponent(componentFile: string): this {
93
+ if (this.state.expandedComponents.has(componentFile)) {
94
+ this.state.expandedComponents.delete(componentFile);
95
+ } else {
96
+ this.state.expandedComponents.add(componentFile);
58
97
  }
98
+ this._triggerCallback('expand', componentFile);
99
+ return this;
100
+ }
101
+
102
+ /* ═════════════════════════════════════════════════════════════════
103
+ * RENDER HELPERS
104
+ * ═════════════════════════════════════════════════════════════════ */
105
+
106
+ private _renderHierarchy(hierarchy: ComponentHierarchy[], depth: number = 0): HTMLElement {
107
+ const container = document.createElement('div');
108
+ container.className = 'jux-blueprint-hierarchy';
109
+
110
+ hierarchy.forEach((node, index) => {
111
+ const nodeEl = document.createElement('div');
112
+ nodeEl.className = 'jux-blueprint-node';
113
+ nodeEl.style.paddingLeft = `${depth * 20}px`;
114
+
115
+ const isLast = index === hierarchy.length - 1;
116
+ const connector = isLast ? '└─' : '├─';
117
+
118
+ // Tree line
119
+ const lineEl = document.createElement('span');
120
+ lineEl.className = 'jux-blueprint-line';
121
+ lineEl.textContent = connector + ' ';
122
+ nodeEl.appendChild(lineEl);
123
+
124
+ // Tag
125
+ const tagEl = document.createElement('span');
126
+ tagEl.className = 'jux-blueprint-tag';
127
+ tagEl.textContent = `<${node.tag}>`;
128
+ nodeEl.appendChild(tagEl);
129
+
130
+ // ID
131
+ if (node.id) {
132
+ const idEl = document.createElement('span');
133
+ idEl.className = 'jux-blueprint-id';
134
+ idEl.textContent = ` #${node.id}`;
135
+ nodeEl.appendChild(idEl);
136
+ }
137
+
138
+ // Classes
139
+ if (node.classes && node.classes.length > 0) {
140
+ const classesEl = document.createElement('span');
141
+ classesEl.className = 'jux-blueprint-classes';
142
+
143
+ node.classes.forEach(cls => {
144
+ const classEl = document.createElement('span');
145
+ classEl.className = 'jux-blueprint-class';
146
+ classEl.textContent = `.${cls}`;
147
+ classesEl.appendChild(classEl);
148
+ classesEl.appendChild(document.createTextNode(' '));
149
+ });
59
150
 
60
- protected getTriggerEvents(): readonly string[] {
61
- return TRIGGER_EVENTS;
62
- }
151
+ nodeEl.appendChild(classesEl);
152
+ }
153
+
154
+ container.appendChild(nodeEl);
155
+
156
+ // Render children recursively
157
+ if (node.children && node.children.length > 0) {
158
+ container.appendChild(this._renderHierarchy(node.children, depth + 1));
159
+ }
160
+ });
161
+
162
+ return container;
163
+ }
164
+
165
+ private _renderComponentCard(component: any): HTMLElement {
166
+ const { showMethods, expandAll, expandedComponents } = this.state;
167
+ const isExpanded = expandAll || expandedComponents.has(component.file);
168
+
169
+ const card = document.createElement('div');
170
+ card.className = 'jux-blueprint-card';
171
+ if (isExpanded) card.classList.add('jux-blueprint-card-expanded');
172
+
173
+ // Header
174
+ const header = document.createElement('div');
175
+ header.className = 'jux-blueprint-card-header';
176
+ header.addEventListener('click', () => {
177
+ this.toggleComponent(component.file);
178
+ this.update('expandedComponents', this.state.expandedComponents);
179
+ });
180
+
181
+ const title = document.createElement('h4');
182
+ title.className = 'jux-blueprint-card-title';
183
+ title.textContent = component.file;
184
+ header.appendChild(title);
185
+
186
+ const badge = document.createElement('span');
187
+ badge.className = 'jux-blueprint-badge';
188
+ badge.textContent = `${component.classCount} classes`;
189
+ header.appendChild(badge);
190
+
191
+ const toggle = document.createElement('span');
192
+ toggle.className = 'jux-blueprint-toggle-icon';
193
+ toggle.textContent = isExpanded ? '▼' : '▶';
194
+ header.appendChild(toggle);
195
+
196
+ card.appendChild(header);
197
+
198
+ // Body (collapsed by default)
199
+ if (isExpanded) {
200
+ const body = document.createElement('div');
201
+ body.className = 'jux-blueprint-card-body';
202
+
203
+ // Class list
204
+ if (component.allClasses && component.allClasses.length > 0) {
205
+ const classSection = document.createElement('div');
206
+ classSection.className = 'jux-blueprint-section';
207
+
208
+ const classTitle = document.createElement('h5');
209
+ classTitle.className = 'jux-blueprint-section-title';
210
+ classTitle.textContent = 'CSS Classes';
211
+ classSection.appendChild(classTitle);
212
+
213
+ const classList = document.createElement('div');
214
+ classList.className = 'jux-blueprint-class-list';
215
+
216
+ component.allClasses.forEach((cls: string) => {
217
+ const classTag = document.createElement('span');
218
+ classTag.className = 'jux-blueprint-class-tag';
219
+ classTag.textContent = `.${cls}`;
220
+ classList.appendChild(classTag);
221
+ });
63
222
 
64
- protected getCallbackEvents(): readonly string[] {
65
- return CALLBACK_EVENTS;
66
- }
223
+ classSection.appendChild(classList);
224
+ body.appendChild(classSection);
225
+ }
67
226
 
68
- /* ═════════════════════════════════════════════════════════════════
69
- * FLUENT API
70
- * ═════════════════════════════════════════════════════════════════ */
227
+ // DOM Structures
228
+ if (component.domStructures && component.domStructures.length > 0) {
229
+ component.domStructures.forEach((structure: any) => {
230
+ const structSection = document.createElement('div');
231
+ structSection.className = 'jux-blueprint-section';
71
232
 
72
- componentId(value: string): this {
73
- this.state.componentId = value;
74
- return this;
75
- }
233
+ const methodTitle = document.createElement('h5');
234
+ methodTitle.className = 'jux-blueprint-section-title';
235
+ methodTitle.textContent = `Method: ${structure.method}()`;
236
+ structSection.appendChild(methodTitle);
76
237
 
77
- showClasses(value: boolean): this {
78
- this.state.showClasses = value;
79
- return this;
80
- }
238
+ // Render hierarchy if available
239
+ if (structure.hierarchy && structure.hierarchy.length > 0) {
240
+ const hierarchyEl = this._renderHierarchy(structure.hierarchy);
241
+ structSection.appendChild(hierarchyEl);
242
+ }
81
243
 
82
- showIds(value: boolean): this {
83
- this.state.showIds = value;
84
- return this;
85
- }
244
+ body.appendChild(structSection);
245
+ });
246
+ }
86
247
 
87
- showTags(value: boolean): this {
88
- this.state.showTags = value;
89
- return this;
248
+ card.appendChild(body);
90
249
  }
91
250
 
92
- expandAll(value: boolean): this {
93
- this.state.expandAll = value;
94
- return this;
95
- }
251
+ return card;
252
+ }
96
253
 
97
- theme(value: 'light' | 'dark' | 'auto'): this {
98
- this.state.theme = value;
99
- return this;
100
- }
254
+ /* ═════════════════════════════════════════════════════════════════
255
+ * REACTIVE UPDATE
256
+ * ═════════════════════════════════════════════════════════════════ */
101
257
 
102
- interactive(value: boolean): this {
103
- this.state.interactive = value;
104
- return this;
105
- }
258
+ update(prop: string, value: any): void {
259
+ super.update(prop, value);
106
260
 
107
- selectNode(path: string): this {
108
- this.state.selectedNode = path;
109
- this._triggerCallback('select', path);
110
- return this;
111
- }
261
+ if (!this.container) return;
112
262
 
113
- /* ═════════════════════════════════════════════════════════════════
114
- * DOM ANALYSIS
115
- * ═════════════════════════════════════════════════════════════════ */
116
-
117
- private _analyzeDOMStructure(componentId: string): DOMNode[] {
118
- const component = registry.get(componentId);
119
- if (!component || !component.container) {
120
- return [];
121
- }
122
-
123
- const rootElement = component.container.querySelector(`#${componentId}`);
124
- if (!rootElement) {
125
- return [];
126
- }
127
-
128
- const extractNode = (element: Element, depth: number = 0, path: string = '0'): DOMNode => {
129
- const node: DOMNode = {
130
- tag: element.tagName.toLowerCase(),
131
- classes: Array.from(element.classList),
132
- id: element.id || null,
133
- depth,
134
- path,
135
- children: []
136
- };
137
-
138
- Array.from(element.children).forEach((child, index) => {
139
- node.children.push(extractNode(child, depth + 1, `${path}.${index}`));
140
- });
141
-
142
- return node;
143
- };
144
-
145
- return [extractNode(rootElement)];
146
- }
147
-
148
- /* ═════════════════════════════════════════════════════════════════
149
- * RENDER NODE
150
- * ═════════════════════════════════════════════════════════════════ */
151
-
152
- private _renderNode(node: DOMNode, isLast: boolean = false, prefix: string = ''): HTMLElement {
153
- const { showClasses, showIds, showTags, interactive, selectedNode, hoveredNode } = this.state;
154
-
155
- const nodeEl = document.createElement('div');
156
- nodeEl.className = 'jux-blueprint-node';
157
- nodeEl.dataset.path = node.path;
158
-
159
- if (selectedNode === node.path) {
160
- nodeEl.classList.add('jux-blueprint-node-selected');
161
- }
162
- if (hoveredNode === node.path) {
163
- nodeEl.classList.add('jux-blueprint-node-hovered');
164
- }
165
-
166
- // Tree line
167
- const lineEl = document.createElement('span');
168
- lineEl.className = 'jux-blueprint-line';
169
-
170
- const connector = isLast ? '└─' : '├─';
171
- const indent = prefix + connector;
172
- lineEl.textContent = indent;
173
- nodeEl.appendChild(lineEl);
174
-
175
- // Node content
176
- const contentEl = document.createElement('span');
177
- contentEl.className = 'jux-blueprint-content';
178
-
179
- // Tag
180
- if (showTags) {
181
- const tagEl = document.createElement('span');
182
- tagEl.className = 'jux-blueprint-tag';
183
- tagEl.textContent = `<${node.tag}>`;
184
- contentEl.appendChild(tagEl);
185
- }
186
-
187
- // ID
188
- if (showIds && node.id) {
189
- const idEl = document.createElement('span');
190
- idEl.className = 'jux-blueprint-id';
191
- idEl.textContent = `#${node.id}`;
192
- contentEl.appendChild(idEl);
193
- }
194
-
195
- // Classes
196
- if (showClasses && node.classes.length > 0) {
197
- const classesEl = document.createElement('span');
198
- classesEl.className = 'jux-blueprint-classes';
199
-
200
- node.classes.forEach((cls, index) => {
201
- const classEl = document.createElement('span');
202
- classEl.className = 'jux-blueprint-class';
203
- classEl.textContent = `.${cls}`;
204
- classesEl.appendChild(classEl);
205
-
206
- if (index < node.classes.length - 1) {
207
- classesEl.appendChild(document.createTextNode(' '));
208
- }
209
- });
210
-
211
- contentEl.appendChild(classesEl);
212
- }
213
-
214
- nodeEl.appendChild(contentEl);
215
-
216
- // Interactive behavior
217
- if (interactive) {
218
- nodeEl.style.cursor = 'pointer';
219
-
220
- nodeEl.addEventListener('click', (e) => {
221
- e.stopPropagation();
222
- this.state.selectedNode = node.path;
223
- this._triggerCallback('select', node);
224
- });
225
-
226
- nodeEl.addEventListener('mouseenter', () => {
227
- this.state.hoveredNode = node.path;
228
- this._triggerCallback('hover', node);
229
- });
230
-
231
- nodeEl.addEventListener('mouseleave', () => {
232
- if (this.state.hoveredNode === node.path) {
233
- this.state.hoveredNode = null;
234
- }
235
- });
236
- }
237
-
238
- return nodeEl;
239
- }
263
+ const blueprint = this.container.querySelector(`#${this._id}`) as HTMLElement;
264
+ if (!blueprint) return;
240
265
 
241
- private _renderTree(nodes: DOMNode[], prefix: string = ''): HTMLElement {
242
- const treeEl = document.createElement('div');
243
- treeEl.className = 'jux-blueprint-tree';
244
-
245
- nodes.forEach((node, index) => {
246
- const isLast = index === nodes.length - 1;
247
- const nodeEl = this._renderNode(node, isLast, prefix);
248
- treeEl.appendChild(nodeEl);
249
-
250
- // Render children
251
- if (node.children.length > 0) {
252
- const childPrefix = prefix + (isLast ? ' ' : '│ ');
253
- const childrenEl = this._renderTree(node.children, childPrefix);
254
- treeEl.appendChild(childrenEl);
255
- }
256
- });
266
+ switch (prop) {
267
+ case 'structureData':
268
+ case 'classData':
269
+ case 'filter':
270
+ case 'showMethods':
271
+ case 'expandAll':
272
+ case 'expandedComponents':
273
+ this._rebuildContent();
274
+ break;
257
275
 
258
- return treeEl;
276
+ case 'theme':
277
+ blueprint.dataset.theme = value;
278
+ break;
259
279
  }
280
+ }
260
281
 
261
- /* ═════════════════════════════════════════════════════════════════
262
- * REACTIVE UPDATE
263
- * ═════════════════════════════════════════════════════════════════ */
264
-
265
- update(prop: string, value: any): void {
266
- super.update(prop, value);
267
-
268
- if (!this.container) return;
269
-
270
- const blueprint = this.container.querySelector(`#${this._id}`) as HTMLElement;
271
- if (!blueprint) return;
272
-
273
- switch (prop) {
274
- case 'componentId':
275
- this._domStructure = this._analyzeDOMStructure(value);
276
- this._rebuildTree();
277
- break;
278
-
279
- case 'showClasses':
280
- case 'showIds':
281
- case 'showTags':
282
- case 'expandAll':
283
- case 'interactive':
284
- this._rebuildTree();
285
- break;
286
-
287
- case 'theme':
288
- blueprint.dataset.theme = value;
289
- break;
290
-
291
- case 'selectedNode':
292
- case 'hoveredNode':
293
- this._updateNodeStates();
294
- break;
295
- }
296
- }
282
+ private _rebuildContent(): void {
283
+ if (!this.container) return;
297
284
 
298
- private _rebuildTree(): void {
299
- if (!this.container) return;
285
+ const contentContainer = this.container.querySelector('.jux-blueprint-content');
286
+ if (!contentContainer) return;
300
287
 
301
- const treeContainer = this.container.querySelector('.jux-blueprint-tree-container');
302
- if (!treeContainer) return;
288
+ contentContainer.innerHTML = '';
303
289
 
304
- treeContainer.innerHTML = '';
290
+ const { classData, filter } = this.state;
305
291
 
306
- if (this._domStructure.length > 0) {
307
- const treeEl = this._renderTree(this._domStructure);
308
- treeContainer.appendChild(treeEl);
309
- } else {
310
- const emptyEl = document.createElement('div');
311
- emptyEl.className = 'jux-blueprint-empty';
312
- emptyEl.textContent = 'No component selected or component not found';
313
- treeContainer.appendChild(emptyEl);
314
- }
292
+ if (!classData || !classData.components) {
293
+ const emptyEl = document.createElement('div');
294
+ emptyEl.className = 'jux-blueprint-empty';
295
+ emptyEl.textContent = 'No component data loaded. Use .structureData() and .classData() to load JSON files.';
296
+ contentContainer.appendChild(emptyEl);
297
+ return;
315
298
  }
316
299
 
317
- private _updateNodeStates(): void {
318
- if (!this.container) return;
319
-
320
- const nodes = this.container.querySelectorAll('.jux-blueprint-node');
321
- nodes.forEach(node => {
322
- const el = node as HTMLElement;
323
- const path = el.dataset.path;
300
+ // Filter components
301
+ const filteredComponents = classData.components.filter((comp: any) => {
302
+ if (!filter) return true;
303
+ return comp.file.toLowerCase().includes(filter.toLowerCase());
304
+ });
305
+
306
+ // Render component cards
307
+ filteredComponents.forEach((component: any) => {
308
+ const card = this._renderComponentCard(component);
309
+ contentContainer.appendChild(card);
310
+ });
311
+
312
+ // Show count
313
+ const countEl = this.container.querySelector('.jux-blueprint-count');
314
+ if (countEl) {
315
+ countEl.textContent = `Showing ${filteredComponents.length} of ${classData.components.length} components`;
316
+ }
317
+ }
318
+
319
+ /* ═════════════════════════════════════════════════════════════════
320
+ * RENDER
321
+ * ═════════════════════════════════════════════════════════════════ */
322
+
323
+ render(targetId?: string | HTMLElement | BaseComponent<any>): this {
324
+ const container = this._setupContainer(targetId);
325
+ const { theme, style, class: className, classData } = this.state;
326
+
327
+ // Create wrapper
328
+ const wrapper = document.createElement('div');
329
+ wrapper.className = 'jux-blueprint';
330
+ wrapper.id = this._id;
331
+ wrapper.dataset.theme = theme;
332
+ if (className) wrapper.className += ` ${className}`;
333
+ if (style) wrapper.setAttribute('style', style);
334
+
335
+ // Header
336
+ const header = document.createElement('div');
337
+ header.className = 'jux-blueprint-header';
338
+
339
+ const title = document.createElement('h2');
340
+ title.className = 'jux-blueprint-title';
341
+ title.textContent = 'Component Blueprint';
342
+ header.appendChild(title);
343
+
344
+ // Stats
345
+ if (classData) {
346
+ const stats = document.createElement('div');
347
+ stats.className = 'jux-blueprint-stats';
348
+ stats.innerHTML = `
349
+ <span class="jux-blueprint-stat">
350
+ <strong>${classData.totalFiles || 0}</strong> Components
351
+ </span>
352
+ <span class="jux-blueprint-stat">
353
+ <strong>${classData.totalClasses || 0}</strong> Total Classes
354
+ </span>
355
+ `;
356
+ header.appendChild(stats);
357
+ }
324
358
 
325
- el.classList.toggle('jux-blueprint-node-selected', path === this.state.selectedNode);
326
- el.classList.toggle('jux-blueprint-node-hovered', path === this.state.hoveredNode);
327
- });
359
+ wrapper.appendChild(header);
360
+
361
+ // Controls
362
+ const controls = document.createElement('div');
363
+ controls.className = 'jux-blueprint-controls';
364
+
365
+ // Filter input
366
+ const filterInput = document.createElement('input');
367
+ filterInput.type = 'text';
368
+ filterInput.className = 'jux-blueprint-filter';
369
+ filterInput.placeholder = 'Filter components...';
370
+ filterInput.value = this.state.filter;
371
+ filterInput.addEventListener('input', (e) => {
372
+ this.state.filter = (e.target as HTMLInputElement).value;
373
+ });
374
+ controls.appendChild(filterInput);
375
+
376
+ // Expand All toggle
377
+ const expandToggle = document.createElement('button');
378
+ expandToggle.className = 'jux-blueprint-button';
379
+ expandToggle.textContent = 'Expand All';
380
+ expandToggle.addEventListener('click', () => {
381
+ this.state.expandAll = !this.state.expandAll;
382
+ expandToggle.textContent = this.state.expandAll ? 'Collapse All' : 'Expand All';
383
+ });
384
+ controls.appendChild(expandToggle);
385
+
386
+ wrapper.appendChild(controls);
387
+
388
+ // Count
389
+ const count = document.createElement('div');
390
+ count.className = 'jux-blueprint-count';
391
+ count.textContent = classData ? `Showing ${classData.components.length} components` : 'No data loaded';
392
+ wrapper.appendChild(count);
393
+
394
+ // Content container
395
+ const content = document.createElement('div');
396
+ content.className = 'jux-blueprint-content';
397
+
398
+ if (classData && classData.components) {
399
+ classData.components.forEach((component: any) => {
400
+ const card = this._renderComponentCard(component);
401
+ content.appendChild(card);
402
+ });
403
+ } else {
404
+ const emptyEl = document.createElement('div');
405
+ emptyEl.className = 'jux-blueprint-empty';
406
+ emptyEl.textContent = 'No component data loaded. Use .structureData() and .classData() to load JSON files.';
407
+ content.appendChild(emptyEl);
328
408
  }
329
409
 
330
- /* ═════════════════════════════════════════════════════════════════
331
- * RENDER
332
- * ═════════════════════════════════════════════════════════════════ */
333
-
334
- render(targetId?: string | HTMLElement | BaseComponent<any>): this {
335
- const container = this._setupContainer(targetId);
336
- const { componentId, theme, style, class: className } = this.state;
337
-
338
- // Analyze structure if componentId is provided
339
- if (componentId) {
340
- this._domStructure = this._analyzeDOMStructure(componentId);
341
- }
342
-
343
- // Create wrapper
344
- const wrapper = document.createElement('div');
345
- wrapper.className = 'jux-blueprint';
346
- wrapper.id = this._id;
347
- wrapper.dataset.theme = theme;
348
- if (className) wrapper.className += ` ${className}`;
349
- if (style) wrapper.setAttribute('style', style);
350
-
351
- // Header
352
- const header = document.createElement('div');
353
- header.className = 'jux-blueprint-header';
354
-
355
- const title = document.createElement('h3');
356
- title.className = 'jux-blueprint-title';
357
- title.textContent = componentId ? `Blueprint: ${componentId}` : 'Component Blueprint';
358
- header.appendChild(title);
359
-
360
- // Controls
361
- const controls = document.createElement('div');
362
- controls.className = 'jux-blueprint-controls';
363
-
364
- const toggles = [
365
- { prop: 'showTags', label: 'Tags' },
366
- { prop: 'showIds', label: 'IDs' },
367
- { prop: 'showClasses', label: 'Classes' }
368
- ];
369
-
370
- toggles.forEach(({ prop, label }) => {
371
- const toggleEl = document.createElement('label');
372
- toggleEl.className = 'jux-blueprint-toggle';
373
-
374
- const checkbox = document.createElement('input');
375
- checkbox.type = 'checkbox';
376
- checkbox.checked = (this.state as any)[prop];
377
- checkbox.addEventListener('change', () => {
378
- (this.state as any)[prop] = checkbox.checked;
379
- });
380
-
381
- toggleEl.appendChild(checkbox);
382
- toggleEl.appendChild(document.createTextNode(` ${label}`));
383
- controls.appendChild(toggleEl);
384
- });
410
+ wrapper.appendChild(content);
385
411
 
386
- header.appendChild(controls);
387
- wrapper.appendChild(header);
388
-
389
- // Tree container
390
- const treeContainer = document.createElement('div');
391
- treeContainer.className = 'jux-blueprint-tree-container';
392
-
393
- if (this._domStructure.length > 0) {
394
- const treeEl = this._renderTree(this._domStructure);
395
- treeContainer.appendChild(treeEl);
396
- } else {
397
- const emptyEl = document.createElement('div');
398
- emptyEl.className = 'jux-blueprint-empty';
399
- emptyEl.textContent = componentId
400
- ? `Component "${componentId}" not found or not rendered`
401
- : 'Set componentId to analyze a component';
402
- treeContainer.appendChild(emptyEl);
403
- }
404
-
405
- wrapper.appendChild(treeContainer);
406
-
407
- // Legend
408
- const legend = document.createElement('div');
409
- legend.className = 'jux-blueprint-legend';
410
- legend.innerHTML = `
411
- <span class="jux-blueprint-legend-item">
412
- <span class="jux-blueprint-tag">&lt;tag&gt;</span> HTML Element
413
- </span>
414
- <span class="jux-blueprint-legend-item">
415
- <span class="jux-blueprint-id">#id</span> Element ID
416
- </span>
417
- <span class="jux-blueprint-legend-item">
418
- <span class="jux-blueprint-class">.class</span> CSS Class
419
- </span>
420
- `;
421
- wrapper.appendChild(legend);
422
-
423
- this._wireStandardEvents(wrapper);
424
- container.appendChild(wrapper);
425
-
426
- return this;
427
- }
412
+ this._wireStandardEvents(wrapper);
413
+ container.appendChild(wrapper);
414
+
415
+ return this;
416
+ }
428
417
  }
429
418
 
430
419
  export function blueprint(id: string, options: BlueprintOptions = {}): Blueprint {
431
- return new Blueprint(id, options);
420
+ return new Blueprint(id, options);
432
421
  }