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.
- package/lib/components/blueprint.d.ts +17 -24
- package/lib/components/blueprint.d.ts.map +1 -1
- package/lib/components/blueprint.js +217 -220
- package/lib/components/blueprint.ts +375 -386
- package/package.json +1 -1
|
@@ -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', '
|
|
5
|
+
const CALLBACK_EVENTS = ['select', 'expand'] as const;
|
|
7
6
|
|
|
8
7
|
export interface BlueprintOptions {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
223
|
+
classSection.appendChild(classList);
|
|
224
|
+
body.appendChild(classSection);
|
|
225
|
+
}
|
|
67
226
|
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
244
|
+
body.appendChild(structSection);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
86
247
|
|
|
87
|
-
|
|
88
|
-
this.state.showTags = value;
|
|
89
|
-
return this;
|
|
248
|
+
card.appendChild(body);
|
|
90
249
|
}
|
|
91
250
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return this;
|
|
95
|
-
}
|
|
251
|
+
return card;
|
|
252
|
+
}
|
|
96
253
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
254
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
255
|
+
* REACTIVE UPDATE
|
|
256
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
101
257
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return this;
|
|
105
|
-
}
|
|
258
|
+
update(prop: string, value: any): void {
|
|
259
|
+
super.update(prop, value);
|
|
106
260
|
|
|
107
|
-
|
|
108
|
-
this.state.selectedNode = path;
|
|
109
|
-
this._triggerCallback('select', path);
|
|
110
|
-
return this;
|
|
111
|
-
}
|
|
261
|
+
if (!this.container) return;
|
|
112
262
|
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
276
|
+
case 'theme':
|
|
277
|
+
blueprint.dataset.theme = value;
|
|
278
|
+
break;
|
|
259
279
|
}
|
|
280
|
+
}
|
|
260
281
|
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
299
|
-
|
|
285
|
+
const contentContainer = this.container.querySelector('.jux-blueprint-content');
|
|
286
|
+
if (!contentContainer) return;
|
|
300
287
|
|
|
301
|
-
|
|
302
|
-
if (!treeContainer) return;
|
|
288
|
+
contentContainer.innerHTML = '';
|
|
303
289
|
|
|
304
|
-
|
|
290
|
+
const { classData, filter } = this.state;
|
|
305
291
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
|
|
326
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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"><tag></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
|
-
|
|
420
|
+
return new Blueprint(id, options);
|
|
432
421
|
}
|