juxscript 1.1.63 → 1.1.65
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/index.d.ts +2 -0
- package/index.d.ts.map +1 -1
- package/index.js +2 -0
- package/lib/components/blueprint.d.ts +47 -0
- package/lib/components/blueprint.d.ts.map +1 -0
- package/lib/components/blueprint.js +330 -0
- package/lib/components/blueprint.ts +432 -0
- package/lib/components/container.d.ts +17 -0
- package/lib/components/container.d.ts.map +1 -1
- package/lib/components/container.js +101 -2
- package/lib/components/container.ts +117 -2
- package/package.json +3 -2
package/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { alert } from './lib/components/alert.js';
|
|
2
2
|
import { app } from './lib/components/app.js';
|
|
3
3
|
import { badge } from './lib/components/badge.js';
|
|
4
|
+
import { blueprint } from './lib/components/blueprint.js';
|
|
4
5
|
import { button } from './lib/components/button.js';
|
|
5
6
|
import { card } from './lib/components/card.js';
|
|
6
7
|
import { chart } from './lib/components/chart.js';
|
|
@@ -49,6 +50,7 @@ export declare const jux: {
|
|
|
49
50
|
alert: typeof alert;
|
|
50
51
|
app: typeof app;
|
|
51
52
|
badge: typeof badge;
|
|
53
|
+
blueprint: typeof blueprint;
|
|
52
54
|
button: typeof button;
|
|
53
55
|
card: typeof card;
|
|
54
56
|
chart: typeof chart;
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
package/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { alert } from './lib/components/alert.js';
|
|
2
2
|
import { app } from './lib/components/app.js';
|
|
3
3
|
import { badge } from './lib/components/badge.js';
|
|
4
|
+
import { blueprint } from './lib/components/blueprint.js';
|
|
4
5
|
import { button } from './lib/components/button.js';
|
|
5
6
|
import { card } from './lib/components/card.js';
|
|
6
7
|
import { chart } from './lib/components/chart.js';
|
|
@@ -52,6 +53,7 @@ export const jux = {
|
|
|
52
53
|
alert,
|
|
53
54
|
app,
|
|
54
55
|
badge,
|
|
56
|
+
blueprint,
|
|
55
57
|
button,
|
|
56
58
|
card,
|
|
57
59
|
chart,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BaseComponent, BaseState } from './base/BaseComponent.js';
|
|
2
|
+
export interface BlueprintOptions {
|
|
3
|
+
componentId?: string;
|
|
4
|
+
showClasses?: boolean;
|
|
5
|
+
showIds?: boolean;
|
|
6
|
+
showTags?: boolean;
|
|
7
|
+
expandAll?: boolean;
|
|
8
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
9
|
+
interactive?: boolean;
|
|
10
|
+
style?: string;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
interface BlueprintState extends BaseState {
|
|
14
|
+
componentId: string;
|
|
15
|
+
showClasses: boolean;
|
|
16
|
+
showIds: boolean;
|
|
17
|
+
showTags: boolean;
|
|
18
|
+
expandAll: boolean;
|
|
19
|
+
theme: string;
|
|
20
|
+
interactive: boolean;
|
|
21
|
+
selectedNode: string | null;
|
|
22
|
+
hoveredNode: string | null;
|
|
23
|
+
}
|
|
24
|
+
export declare class Blueprint extends BaseComponent<BlueprintState> {
|
|
25
|
+
private _domStructure;
|
|
26
|
+
constructor(id: string, options?: BlueprintOptions);
|
|
27
|
+
protected getTriggerEvents(): readonly string[];
|
|
28
|
+
protected getCallbackEvents(): readonly string[];
|
|
29
|
+
componentId(value: string): this;
|
|
30
|
+
showClasses(value: boolean): this;
|
|
31
|
+
showIds(value: boolean): this;
|
|
32
|
+
showTags(value: boolean): this;
|
|
33
|
+
expandAll(value: boolean): this;
|
|
34
|
+
theme(value: 'light' | 'dark' | 'auto'): this;
|
|
35
|
+
interactive(value: boolean): this;
|
|
36
|
+
selectNode(path: string): this;
|
|
37
|
+
private _analyzeDOMStructure;
|
|
38
|
+
private _renderNode;
|
|
39
|
+
private _renderTree;
|
|
40
|
+
update(prop: string, value: any): void;
|
|
41
|
+
private _rebuildTree;
|
|
42
|
+
private _updateNodeStates;
|
|
43
|
+
render(targetId?: string | HTMLElement | BaseComponent<any>): this;
|
|
44
|
+
}
|
|
45
|
+
export declare function blueprint(id: string, options?: BlueprintOptions): Blueprint;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=blueprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["blueprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAOnE,MAAM,WAAW,gBAAgB;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,cAAe,SAAQ,SAAS;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAWD,qBAAa,SAAU,SAAQ,aAAa,CAAC,cAAc,CAAC;IACxD,OAAO,CAAC,aAAa,CAAiB;gBAE1B,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAgBtD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAQhD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKhC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKjC,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK7B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK9B,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK/B,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI;IAK7C,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKjC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAU9B,OAAO,CAAC,oBAAoB;IAmC5B,OAAO,CAAC,WAAW;IAyFnB,OAAO,CAAC,WAAW;IAwBnB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAiCtC,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,iBAAiB;IAiBzB,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CA8FrE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS,CAE/E"}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
+
import { registry } from './registry.js';
|
|
3
|
+
// Event definitions
|
|
4
|
+
const TRIGGER_EVENTS = [];
|
|
5
|
+
const CALLBACK_EVENTS = ['select', 'hover'];
|
|
6
|
+
export class Blueprint extends BaseComponent {
|
|
7
|
+
constructor(id, options = {}) {
|
|
8
|
+
super(id, {
|
|
9
|
+
componentId: options.componentId ?? '',
|
|
10
|
+
showClasses: options.showClasses ?? true,
|
|
11
|
+
showIds: options.showIds ?? true,
|
|
12
|
+
showTags: options.showTags ?? true,
|
|
13
|
+
expandAll: options.expandAll ?? true,
|
|
14
|
+
theme: options.theme ?? 'auto',
|
|
15
|
+
interactive: options.interactive ?? true,
|
|
16
|
+
selectedNode: null,
|
|
17
|
+
hoveredNode: null,
|
|
18
|
+
style: options.style ?? '',
|
|
19
|
+
class: options.class ?? ''
|
|
20
|
+
});
|
|
21
|
+
this._domStructure = [];
|
|
22
|
+
}
|
|
23
|
+
getTriggerEvents() {
|
|
24
|
+
return TRIGGER_EVENTS;
|
|
25
|
+
}
|
|
26
|
+
getCallbackEvents() {
|
|
27
|
+
return CALLBACK_EVENTS;
|
|
28
|
+
}
|
|
29
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
30
|
+
* FLUENT API
|
|
31
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
32
|
+
componentId(value) {
|
|
33
|
+
this.state.componentId = value;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
showClasses(value) {
|
|
37
|
+
this.state.showClasses = value;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
showIds(value) {
|
|
41
|
+
this.state.showIds = value;
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
showTags(value) {
|
|
45
|
+
this.state.showTags = value;
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
expandAll(value) {
|
|
49
|
+
this.state.expandAll = value;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
theme(value) {
|
|
53
|
+
this.state.theme = value;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
interactive(value) {
|
|
57
|
+
this.state.interactive = value;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
selectNode(path) {
|
|
61
|
+
this.state.selectedNode = path;
|
|
62
|
+
this._triggerCallback('select', path);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
66
|
+
* DOM ANALYSIS
|
|
67
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
68
|
+
_analyzeDOMStructure(componentId) {
|
|
69
|
+
const component = registry.get(componentId);
|
|
70
|
+
if (!component || !component.container) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const rootElement = component.container.querySelector(`#${componentId}`);
|
|
74
|
+
if (!rootElement) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const extractNode = (element, depth = 0, path = '0') => {
|
|
78
|
+
const node = {
|
|
79
|
+
tag: element.tagName.toLowerCase(),
|
|
80
|
+
classes: Array.from(element.classList),
|
|
81
|
+
id: element.id || null,
|
|
82
|
+
depth,
|
|
83
|
+
path,
|
|
84
|
+
children: []
|
|
85
|
+
};
|
|
86
|
+
Array.from(element.children).forEach((child, index) => {
|
|
87
|
+
node.children.push(extractNode(child, depth + 1, `${path}.${index}`));
|
|
88
|
+
});
|
|
89
|
+
return node;
|
|
90
|
+
};
|
|
91
|
+
return [extractNode(rootElement)];
|
|
92
|
+
}
|
|
93
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
94
|
+
* RENDER NODE
|
|
95
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
96
|
+
_renderNode(node, isLast = false, prefix = '') {
|
|
97
|
+
const { showClasses, showIds, showTags, interactive, selectedNode, hoveredNode } = this.state;
|
|
98
|
+
const nodeEl = document.createElement('div');
|
|
99
|
+
nodeEl.className = 'jux-blueprint-node';
|
|
100
|
+
nodeEl.dataset.path = node.path;
|
|
101
|
+
if (selectedNode === node.path) {
|
|
102
|
+
nodeEl.classList.add('jux-blueprint-node-selected');
|
|
103
|
+
}
|
|
104
|
+
if (hoveredNode === node.path) {
|
|
105
|
+
nodeEl.classList.add('jux-blueprint-node-hovered');
|
|
106
|
+
}
|
|
107
|
+
// Tree line
|
|
108
|
+
const lineEl = document.createElement('span');
|
|
109
|
+
lineEl.className = 'jux-blueprint-line';
|
|
110
|
+
const connector = isLast ? '└─' : '├─';
|
|
111
|
+
const indent = prefix + connector;
|
|
112
|
+
lineEl.textContent = indent;
|
|
113
|
+
nodeEl.appendChild(lineEl);
|
|
114
|
+
// Node content
|
|
115
|
+
const contentEl = document.createElement('span');
|
|
116
|
+
contentEl.className = 'jux-blueprint-content';
|
|
117
|
+
// Tag
|
|
118
|
+
if (showTags) {
|
|
119
|
+
const tagEl = document.createElement('span');
|
|
120
|
+
tagEl.className = 'jux-blueprint-tag';
|
|
121
|
+
tagEl.textContent = `<${node.tag}>`;
|
|
122
|
+
contentEl.appendChild(tagEl);
|
|
123
|
+
}
|
|
124
|
+
// ID
|
|
125
|
+
if (showIds && node.id) {
|
|
126
|
+
const idEl = document.createElement('span');
|
|
127
|
+
idEl.className = 'jux-blueprint-id';
|
|
128
|
+
idEl.textContent = `#${node.id}`;
|
|
129
|
+
contentEl.appendChild(idEl);
|
|
130
|
+
}
|
|
131
|
+
// Classes
|
|
132
|
+
if (showClasses && node.classes.length > 0) {
|
|
133
|
+
const classesEl = document.createElement('span');
|
|
134
|
+
classesEl.className = 'jux-blueprint-classes';
|
|
135
|
+
node.classes.forEach((cls, index) => {
|
|
136
|
+
const classEl = document.createElement('span');
|
|
137
|
+
classEl.className = 'jux-blueprint-class';
|
|
138
|
+
classEl.textContent = `.${cls}`;
|
|
139
|
+
classesEl.appendChild(classEl);
|
|
140
|
+
if (index < node.classes.length - 1) {
|
|
141
|
+
classesEl.appendChild(document.createTextNode(' '));
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
contentEl.appendChild(classesEl);
|
|
145
|
+
}
|
|
146
|
+
nodeEl.appendChild(contentEl);
|
|
147
|
+
// Interactive behavior
|
|
148
|
+
if (interactive) {
|
|
149
|
+
nodeEl.style.cursor = 'pointer';
|
|
150
|
+
nodeEl.addEventListener('click', (e) => {
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
this.state.selectedNode = node.path;
|
|
153
|
+
this._triggerCallback('select', node);
|
|
154
|
+
});
|
|
155
|
+
nodeEl.addEventListener('mouseenter', () => {
|
|
156
|
+
this.state.hoveredNode = node.path;
|
|
157
|
+
this._triggerCallback('hover', node);
|
|
158
|
+
});
|
|
159
|
+
nodeEl.addEventListener('mouseleave', () => {
|
|
160
|
+
if (this.state.hoveredNode === node.path) {
|
|
161
|
+
this.state.hoveredNode = null;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return nodeEl;
|
|
166
|
+
}
|
|
167
|
+
_renderTree(nodes, prefix = '') {
|
|
168
|
+
const treeEl = document.createElement('div');
|
|
169
|
+
treeEl.className = 'jux-blueprint-tree';
|
|
170
|
+
nodes.forEach((node, index) => {
|
|
171
|
+
const isLast = index === nodes.length - 1;
|
|
172
|
+
const nodeEl = this._renderNode(node, isLast, prefix);
|
|
173
|
+
treeEl.appendChild(nodeEl);
|
|
174
|
+
// Render children
|
|
175
|
+
if (node.children.length > 0) {
|
|
176
|
+
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
177
|
+
const childrenEl = this._renderTree(node.children, childPrefix);
|
|
178
|
+
treeEl.appendChild(childrenEl);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
return treeEl;
|
|
182
|
+
}
|
|
183
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
184
|
+
* REACTIVE UPDATE
|
|
185
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
186
|
+
update(prop, value) {
|
|
187
|
+
super.update(prop, value);
|
|
188
|
+
if (!this.container)
|
|
189
|
+
return;
|
|
190
|
+
const blueprint = this.container.querySelector(`#${this._id}`);
|
|
191
|
+
if (!blueprint)
|
|
192
|
+
return;
|
|
193
|
+
switch (prop) {
|
|
194
|
+
case 'componentId':
|
|
195
|
+
this._domStructure = this._analyzeDOMStructure(value);
|
|
196
|
+
this._rebuildTree();
|
|
197
|
+
break;
|
|
198
|
+
case 'showClasses':
|
|
199
|
+
case 'showIds':
|
|
200
|
+
case 'showTags':
|
|
201
|
+
case 'expandAll':
|
|
202
|
+
case 'interactive':
|
|
203
|
+
this._rebuildTree();
|
|
204
|
+
break;
|
|
205
|
+
case 'theme':
|
|
206
|
+
blueprint.dataset.theme = value;
|
|
207
|
+
break;
|
|
208
|
+
case 'selectedNode':
|
|
209
|
+
case 'hoveredNode':
|
|
210
|
+
this._updateNodeStates();
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
_rebuildTree() {
|
|
215
|
+
if (!this.container)
|
|
216
|
+
return;
|
|
217
|
+
const treeContainer = this.container.querySelector('.jux-blueprint-tree-container');
|
|
218
|
+
if (!treeContainer)
|
|
219
|
+
return;
|
|
220
|
+
treeContainer.innerHTML = '';
|
|
221
|
+
if (this._domStructure.length > 0) {
|
|
222
|
+
const treeEl = this._renderTree(this._domStructure);
|
|
223
|
+
treeContainer.appendChild(treeEl);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
const emptyEl = document.createElement('div');
|
|
227
|
+
emptyEl.className = 'jux-blueprint-empty';
|
|
228
|
+
emptyEl.textContent = 'No component selected or component not found';
|
|
229
|
+
treeContainer.appendChild(emptyEl);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
_updateNodeStates() {
|
|
233
|
+
if (!this.container)
|
|
234
|
+
return;
|
|
235
|
+
const nodes = this.container.querySelectorAll('.jux-blueprint-node');
|
|
236
|
+
nodes.forEach(node => {
|
|
237
|
+
const el = node;
|
|
238
|
+
const path = el.dataset.path;
|
|
239
|
+
el.classList.toggle('jux-blueprint-node-selected', path === this.state.selectedNode);
|
|
240
|
+
el.classList.toggle('jux-blueprint-node-hovered', path === this.state.hoveredNode);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
244
|
+
* RENDER
|
|
245
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
246
|
+
render(targetId) {
|
|
247
|
+
const container = this._setupContainer(targetId);
|
|
248
|
+
const { componentId, theme, style, class: className } = this.state;
|
|
249
|
+
// Analyze structure if componentId is provided
|
|
250
|
+
if (componentId) {
|
|
251
|
+
this._domStructure = this._analyzeDOMStructure(componentId);
|
|
252
|
+
}
|
|
253
|
+
// Create wrapper
|
|
254
|
+
const wrapper = document.createElement('div');
|
|
255
|
+
wrapper.className = 'jux-blueprint';
|
|
256
|
+
wrapper.id = this._id;
|
|
257
|
+
wrapper.dataset.theme = theme;
|
|
258
|
+
if (className)
|
|
259
|
+
wrapper.className += ` ${className}`;
|
|
260
|
+
if (style)
|
|
261
|
+
wrapper.setAttribute('style', style);
|
|
262
|
+
// Header
|
|
263
|
+
const header = document.createElement('div');
|
|
264
|
+
header.className = 'jux-blueprint-header';
|
|
265
|
+
const title = document.createElement('h3');
|
|
266
|
+
title.className = 'jux-blueprint-title';
|
|
267
|
+
title.textContent = componentId ? `Blueprint: ${componentId}` : 'Component Blueprint';
|
|
268
|
+
header.appendChild(title);
|
|
269
|
+
// Controls
|
|
270
|
+
const controls = document.createElement('div');
|
|
271
|
+
controls.className = 'jux-blueprint-controls';
|
|
272
|
+
const toggles = [
|
|
273
|
+
{ prop: 'showTags', label: 'Tags' },
|
|
274
|
+
{ prop: 'showIds', label: 'IDs' },
|
|
275
|
+
{ prop: 'showClasses', label: 'Classes' }
|
|
276
|
+
];
|
|
277
|
+
toggles.forEach(({ prop, label }) => {
|
|
278
|
+
const toggleEl = document.createElement('label');
|
|
279
|
+
toggleEl.className = 'jux-blueprint-toggle';
|
|
280
|
+
const checkbox = document.createElement('input');
|
|
281
|
+
checkbox.type = 'checkbox';
|
|
282
|
+
checkbox.checked = this.state[prop];
|
|
283
|
+
checkbox.addEventListener('change', () => {
|
|
284
|
+
this.state[prop] = checkbox.checked;
|
|
285
|
+
});
|
|
286
|
+
toggleEl.appendChild(checkbox);
|
|
287
|
+
toggleEl.appendChild(document.createTextNode(` ${label}`));
|
|
288
|
+
controls.appendChild(toggleEl);
|
|
289
|
+
});
|
|
290
|
+
header.appendChild(controls);
|
|
291
|
+
wrapper.appendChild(header);
|
|
292
|
+
// Tree container
|
|
293
|
+
const treeContainer = document.createElement('div');
|
|
294
|
+
treeContainer.className = 'jux-blueprint-tree-container';
|
|
295
|
+
if (this._domStructure.length > 0) {
|
|
296
|
+
const treeEl = this._renderTree(this._domStructure);
|
|
297
|
+
treeContainer.appendChild(treeEl);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
const emptyEl = document.createElement('div');
|
|
301
|
+
emptyEl.className = 'jux-blueprint-empty';
|
|
302
|
+
emptyEl.textContent = componentId
|
|
303
|
+
? `Component "${componentId}" not found or not rendered`
|
|
304
|
+
: 'Set componentId to analyze a component';
|
|
305
|
+
treeContainer.appendChild(emptyEl);
|
|
306
|
+
}
|
|
307
|
+
wrapper.appendChild(treeContainer);
|
|
308
|
+
// Legend
|
|
309
|
+
const legend = document.createElement('div');
|
|
310
|
+
legend.className = 'jux-blueprint-legend';
|
|
311
|
+
legend.innerHTML = `
|
|
312
|
+
<span class="jux-blueprint-legend-item">
|
|
313
|
+
<span class="jux-blueprint-tag"><tag></span> HTML Element
|
|
314
|
+
</span>
|
|
315
|
+
<span class="jux-blueprint-legend-item">
|
|
316
|
+
<span class="jux-blueprint-id">#id</span> Element ID
|
|
317
|
+
</span>
|
|
318
|
+
<span class="jux-blueprint-legend-item">
|
|
319
|
+
<span class="jux-blueprint-class">.class</span> CSS Class
|
|
320
|
+
</span>
|
|
321
|
+
`;
|
|
322
|
+
wrapper.appendChild(legend);
|
|
323
|
+
this._wireStandardEvents(wrapper);
|
|
324
|
+
container.appendChild(wrapper);
|
|
325
|
+
return this;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
export function blueprint(id, options = {}) {
|
|
329
|
+
return new Blueprint(id, options);
|
|
330
|
+
}
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import { BaseComponent, BaseState } from './base/BaseComponent.js';
|
|
2
|
+
import { registry } from './registry.js';
|
|
3
|
+
|
|
4
|
+
// Event definitions
|
|
5
|
+
const TRIGGER_EVENTS = [] as const;
|
|
6
|
+
const CALLBACK_EVENTS = ['select', 'hover'] as const;
|
|
7
|
+
|
|
8
|
+
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;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
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;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface DOMNode {
|
|
33
|
+
tag: string;
|
|
34
|
+
classes: string[];
|
|
35
|
+
id: string | null;
|
|
36
|
+
depth: number;
|
|
37
|
+
path: string;
|
|
38
|
+
children: DOMNode[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
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
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected getTriggerEvents(): readonly string[] {
|
|
61
|
+
return TRIGGER_EVENTS;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected getCallbackEvents(): readonly string[] {
|
|
65
|
+
return CALLBACK_EVENTS;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
69
|
+
* FLUENT API
|
|
70
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
71
|
+
|
|
72
|
+
componentId(value: string): this {
|
|
73
|
+
this.state.componentId = value;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
showClasses(value: boolean): this {
|
|
78
|
+
this.state.showClasses = value;
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
showIds(value: boolean): this {
|
|
83
|
+
this.state.showIds = value;
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
showTags(value: boolean): this {
|
|
88
|
+
this.state.showTags = value;
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
expandAll(value: boolean): this {
|
|
93
|
+
this.state.expandAll = value;
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
theme(value: 'light' | 'dark' | 'auto'): this {
|
|
98
|
+
this.state.theme = value;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interactive(value: boolean): this {
|
|
103
|
+
this.state.interactive = value;
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
selectNode(path: string): this {
|
|
108
|
+
this.state.selectedNode = path;
|
|
109
|
+
this._triggerCallback('select', path);
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
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
|
+
}
|
|
240
|
+
|
|
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
|
+
});
|
|
257
|
+
|
|
258
|
+
return treeEl;
|
|
259
|
+
}
|
|
260
|
+
|
|
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
|
+
}
|
|
297
|
+
|
|
298
|
+
private _rebuildTree(): void {
|
|
299
|
+
if (!this.container) return;
|
|
300
|
+
|
|
301
|
+
const treeContainer = this.container.querySelector('.jux-blueprint-tree-container');
|
|
302
|
+
if (!treeContainer) return;
|
|
303
|
+
|
|
304
|
+
treeContainer.innerHTML = '';
|
|
305
|
+
|
|
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
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
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;
|
|
324
|
+
|
|
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
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
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
|
+
});
|
|
385
|
+
|
|
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"><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
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export function blueprint(id: string, options: BlueprintOptions = {}): Blueprint {
|
|
431
|
+
return new Blueprint(id, options);
|
|
432
|
+
}
|
|
@@ -3,6 +3,7 @@ import { BaseComponent } from './base/BaseComponent.js';
|
|
|
3
3
|
* Container options
|
|
4
4
|
*/
|
|
5
5
|
export interface ContainerOptions {
|
|
6
|
+
content?: string | BaseComponent<any> | Array<string | BaseComponent<any>>;
|
|
6
7
|
class?: string;
|
|
7
8
|
style?: string;
|
|
8
9
|
direction?: 'row' | 'column';
|
|
@@ -14,6 +15,7 @@ export interface ContainerOptions {
|
|
|
14
15
|
* Container state
|
|
15
16
|
*/
|
|
16
17
|
type ContainerState = {
|
|
18
|
+
content: string | BaseComponent<any> | Array<string | BaseComponent<any>>;
|
|
17
19
|
class: string;
|
|
18
20
|
style: string;
|
|
19
21
|
direction?: 'row' | 'column';
|
|
@@ -25,14 +27,29 @@ type ContainerState = {
|
|
|
25
27
|
* Container component - a simple div container for grouping elements
|
|
26
28
|
*/
|
|
27
29
|
export declare class Container extends BaseComponent<ContainerState> {
|
|
30
|
+
private _containerElement;
|
|
28
31
|
constructor(id: string, options?: ContainerOptions);
|
|
29
32
|
protected getTriggerEvents(): readonly string[];
|
|
30
33
|
protected getCallbackEvents(): readonly string[];
|
|
31
34
|
update(prop: string, value: any): void;
|
|
35
|
+
private _updateContent;
|
|
36
|
+
content(value: string | BaseComponent<any> | Array<string | BaseComponent<any>>): this;
|
|
32
37
|
direction(value: 'row' | 'column'): this;
|
|
33
38
|
gap(value: string | number): this;
|
|
34
39
|
align(value: 'start' | 'center' | 'end' | 'stretch'): this;
|
|
35
40
|
justify(value: 'start' | 'center' | 'end' | 'space-between' | 'space-around' | 'space-evenly'): this;
|
|
41
|
+
/**
|
|
42
|
+
* Add content to container (supports strings and BaseComponents)
|
|
43
|
+
*/
|
|
44
|
+
addContent(content: string | BaseComponent<any> | Array<string | BaseComponent<any>>): this;
|
|
45
|
+
/**
|
|
46
|
+
* Clear all content from container
|
|
47
|
+
*/
|
|
48
|
+
clearContent(): this;
|
|
49
|
+
/**
|
|
50
|
+
* Get the container element ID for rendering child components
|
|
51
|
+
*/
|
|
52
|
+
contentId(): string;
|
|
36
53
|
render(targetId?: string | HTMLElement | BaseComponent<any>): this;
|
|
37
54
|
}
|
|
38
55
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAMxD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,GAAG,cAAc,CAAC;CAC1F;AAED;;GAEG;AACH,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAU,SAAQ,aAAa,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAMxD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,GAAG,cAAc,CAAC;CAC1F;AAED;;GAEG;AACH,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAU,SAAQ,aAAa,CAAC,cAAc,CAAC;IAC1D,OAAO,CAAC,iBAAiB,CAA4B;gBAEzC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAYtD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAIhD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IA6BtC,OAAO,CAAC,cAAc;IAgCtB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI;IAKtF,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,GAAG,IAAI;IAKxC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKjC,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,IAAI;IAK1D,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,eAAe,GAAG,cAAc,GAAG,cAAc,GAAG,IAAI;IAKpG;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI;IAuB3F;;OAEG;IACH,YAAY,IAAI,IAAI;IAOpB;;OAEG;IACH,SAAS,IAAI,MAAM;IAQnB,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CA4DnE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS,CAE/E"}
|
|
@@ -8,6 +8,7 @@ const CALLBACK_EVENTS = ['click'];
|
|
|
8
8
|
export class Container extends BaseComponent {
|
|
9
9
|
constructor(id, options = {}) {
|
|
10
10
|
super(id, {
|
|
11
|
+
content: options.content ?? [],
|
|
11
12
|
class: options.class ?? '',
|
|
12
13
|
style: options.style ?? '',
|
|
13
14
|
direction: options.direction,
|
|
@@ -15,6 +16,7 @@ export class Container extends BaseComponent {
|
|
|
15
16
|
align: options.align,
|
|
16
17
|
justify: options.justify
|
|
17
18
|
});
|
|
19
|
+
this._containerElement = null;
|
|
18
20
|
}
|
|
19
21
|
getTriggerEvents() {
|
|
20
22
|
return TRIGGER_EVENTS;
|
|
@@ -23,7 +25,45 @@ export class Container extends BaseComponent {
|
|
|
23
25
|
return CALLBACK_EVENTS;
|
|
24
26
|
}
|
|
25
27
|
update(prop, value) {
|
|
26
|
-
|
|
28
|
+
super.update(prop, value);
|
|
29
|
+
if (!this._containerElement)
|
|
30
|
+
return;
|
|
31
|
+
switch (prop) {
|
|
32
|
+
case 'content':
|
|
33
|
+
this._updateContent(value);
|
|
34
|
+
break;
|
|
35
|
+
case 'direction':
|
|
36
|
+
this._containerElement.style.flexDirection = value;
|
|
37
|
+
break;
|
|
38
|
+
case 'gap':
|
|
39
|
+
const gapValue = typeof value === 'number' ? `${value}px` : value;
|
|
40
|
+
this._containerElement.style.gap = gapValue;
|
|
41
|
+
break;
|
|
42
|
+
case 'align':
|
|
43
|
+
this._containerElement.style.alignItems = value;
|
|
44
|
+
break;
|
|
45
|
+
case 'justify':
|
|
46
|
+
this._containerElement.style.justifyContent = value;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
_updateContent(value) {
|
|
51
|
+
if (!this._containerElement)
|
|
52
|
+
return;
|
|
53
|
+
this._containerElement.innerHTML = '';
|
|
54
|
+
const items = Array.isArray(value) ? value : [value];
|
|
55
|
+
items.forEach(item => {
|
|
56
|
+
if (typeof item === 'string') {
|
|
57
|
+
const tempDiv = document.createElement('div');
|
|
58
|
+
tempDiv.innerHTML = item;
|
|
59
|
+
while (tempDiv.firstChild) {
|
|
60
|
+
this._containerElement.appendChild(tempDiv.firstChild);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (item instanceof BaseComponent) {
|
|
64
|
+
item.render(this._containerElement);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
27
67
|
}
|
|
28
68
|
/* -------------------------
|
|
29
69
|
* Fluent API
|
|
@@ -35,6 +75,10 @@ export class Container extends BaseComponent {
|
|
|
35
75
|
// - visible(), show(), hide()
|
|
36
76
|
// - attr(), attrs(), removeAttr()
|
|
37
77
|
// - disabled(), enable(), disable()
|
|
78
|
+
content(value) {
|
|
79
|
+
this.state.content = value;
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
38
82
|
direction(value) {
|
|
39
83
|
this.state.direction = value;
|
|
40
84
|
return this;
|
|
@@ -51,14 +95,53 @@ export class Container extends BaseComponent {
|
|
|
51
95
|
this.state.justify = value;
|
|
52
96
|
return this;
|
|
53
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Add content to container (supports strings and BaseComponents)
|
|
100
|
+
*/
|
|
101
|
+
addContent(content) {
|
|
102
|
+
if (!this._containerElement) {
|
|
103
|
+
console.warn('[Container] Container element not found');
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
const items = Array.isArray(content) ? content : [content];
|
|
107
|
+
items.forEach(item => {
|
|
108
|
+
if (typeof item === 'string') {
|
|
109
|
+
const tempDiv = document.createElement('div');
|
|
110
|
+
tempDiv.innerHTML = item;
|
|
111
|
+
while (tempDiv.firstChild) {
|
|
112
|
+
this._containerElement.appendChild(tempDiv.firstChild);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (item instanceof BaseComponent) {
|
|
116
|
+
item.render(this._containerElement);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Clear all content from container
|
|
123
|
+
*/
|
|
124
|
+
clearContent() {
|
|
125
|
+
if (this._containerElement) {
|
|
126
|
+
this._containerElement.innerHTML = '';
|
|
127
|
+
}
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the container element ID for rendering child components
|
|
132
|
+
*/
|
|
133
|
+
contentId() {
|
|
134
|
+
return `${this._id}-content`;
|
|
135
|
+
}
|
|
54
136
|
/* -------------------------
|
|
55
137
|
* Render
|
|
56
138
|
* ------------------------- */
|
|
57
139
|
render(targetId) {
|
|
58
140
|
const container = this._setupContainer(targetId);
|
|
59
|
-
const { class: className, style, direction, gap, align, justify } = this.state;
|
|
141
|
+
const { content, class: className, style, direction, gap, align, justify } = this.state;
|
|
60
142
|
const div = document.createElement('div');
|
|
61
143
|
div.id = this._id;
|
|
144
|
+
this._containerElement = div;
|
|
62
145
|
// Always include jux-container class, append custom classes
|
|
63
146
|
div.className = className ? `jux-container ${className}` : 'jux-container';
|
|
64
147
|
// Only apply flex styles if any flex properties are set
|
|
@@ -83,6 +166,22 @@ export class Container extends BaseComponent {
|
|
|
83
166
|
if (computedStyle) {
|
|
84
167
|
div.setAttribute('style', computedStyle);
|
|
85
168
|
}
|
|
169
|
+
// Render initial content
|
|
170
|
+
if (content) {
|
|
171
|
+
const items = Array.isArray(content) ? content : [content];
|
|
172
|
+
items.forEach(item => {
|
|
173
|
+
if (typeof item === 'string') {
|
|
174
|
+
const tempDiv = document.createElement('div');
|
|
175
|
+
tempDiv.innerHTML = item;
|
|
176
|
+
while (tempDiv.firstChild) {
|
|
177
|
+
div.appendChild(tempDiv.firstChild);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else if (item instanceof BaseComponent) {
|
|
181
|
+
item.render(div);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
86
185
|
this._wireStandardEvents(div);
|
|
87
186
|
container.appendChild(div);
|
|
88
187
|
return this;
|
|
@@ -8,6 +8,7 @@ const CALLBACK_EVENTS = ['click'] as const;
|
|
|
8
8
|
* Container options
|
|
9
9
|
*/
|
|
10
10
|
export interface ContainerOptions {
|
|
11
|
+
content?: string | BaseComponent<any> | Array<string | BaseComponent<any>>;
|
|
11
12
|
class?: string;
|
|
12
13
|
style?: string;
|
|
13
14
|
direction?: 'row' | 'column';
|
|
@@ -20,6 +21,7 @@ export interface ContainerOptions {
|
|
|
20
21
|
* Container state
|
|
21
22
|
*/
|
|
22
23
|
type ContainerState = {
|
|
24
|
+
content: string | BaseComponent<any> | Array<string | BaseComponent<any>>;
|
|
23
25
|
class: string;
|
|
24
26
|
style: string;
|
|
25
27
|
direction?: 'row' | 'column';
|
|
@@ -32,8 +34,11 @@ type ContainerState = {
|
|
|
32
34
|
* Container component - a simple div container for grouping elements
|
|
33
35
|
*/
|
|
34
36
|
export class Container extends BaseComponent<ContainerState> {
|
|
37
|
+
private _containerElement: HTMLElement | null = null;
|
|
38
|
+
|
|
35
39
|
constructor(id: string, options: ContainerOptions = {}) {
|
|
36
40
|
super(id, {
|
|
41
|
+
content: options.content ?? [],
|
|
37
42
|
class: options.class ?? '',
|
|
38
43
|
style: options.style ?? '',
|
|
39
44
|
direction: options.direction,
|
|
@@ -52,7 +57,52 @@ export class Container extends BaseComponent<ContainerState> {
|
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
update(prop: string, value: any): void {
|
|
55
|
-
|
|
60
|
+
super.update(prop, value);
|
|
61
|
+
|
|
62
|
+
if (!this._containerElement) return;
|
|
63
|
+
|
|
64
|
+
switch (prop) {
|
|
65
|
+
case 'content':
|
|
66
|
+
this._updateContent(value);
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
case 'direction':
|
|
70
|
+
this._containerElement.style.flexDirection = value;
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case 'gap':
|
|
74
|
+
const gapValue = typeof value === 'number' ? `${value}px` : value;
|
|
75
|
+
this._containerElement.style.gap = gapValue;
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'align':
|
|
79
|
+
this._containerElement.style.alignItems = value;
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
case 'justify':
|
|
83
|
+
this._containerElement.style.justifyContent = value;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private _updateContent(value: string | BaseComponent<any> | Array<string | BaseComponent<any>>): void {
|
|
89
|
+
if (!this._containerElement) return;
|
|
90
|
+
|
|
91
|
+
this._containerElement.innerHTML = '';
|
|
92
|
+
|
|
93
|
+
const items = Array.isArray(value) ? value : [value];
|
|
94
|
+
|
|
95
|
+
items.forEach(item => {
|
|
96
|
+
if (typeof item === 'string') {
|
|
97
|
+
const tempDiv = document.createElement('div');
|
|
98
|
+
tempDiv.innerHTML = item;
|
|
99
|
+
while (tempDiv.firstChild) {
|
|
100
|
+
this._containerElement!.appendChild(tempDiv.firstChild);
|
|
101
|
+
}
|
|
102
|
+
} else if (item instanceof BaseComponent) {
|
|
103
|
+
item.render(this._containerElement!);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
56
106
|
}
|
|
57
107
|
|
|
58
108
|
/* -------------------------
|
|
@@ -67,6 +117,11 @@ export class Container extends BaseComponent<ContainerState> {
|
|
|
67
117
|
// - attr(), attrs(), removeAttr()
|
|
68
118
|
// - disabled(), enable(), disable()
|
|
69
119
|
|
|
120
|
+
content(value: string | BaseComponent<any> | Array<string | BaseComponent<any>>): this {
|
|
121
|
+
this.state.content = value;
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
|
|
70
125
|
direction(value: 'row' | 'column'): this {
|
|
71
126
|
this.state.direction = value;
|
|
72
127
|
return this;
|
|
@@ -87,6 +142,49 @@ export class Container extends BaseComponent<ContainerState> {
|
|
|
87
142
|
return this;
|
|
88
143
|
}
|
|
89
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Add content to container (supports strings and BaseComponents)
|
|
147
|
+
*/
|
|
148
|
+
addContent(content: string | BaseComponent<any> | Array<string | BaseComponent<any>>): this {
|
|
149
|
+
if (!this._containerElement) {
|
|
150
|
+
console.warn('[Container] Container element not found');
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const items = Array.isArray(content) ? content : [content];
|
|
155
|
+
|
|
156
|
+
items.forEach(item => {
|
|
157
|
+
if (typeof item === 'string') {
|
|
158
|
+
const tempDiv = document.createElement('div');
|
|
159
|
+
tempDiv.innerHTML = item;
|
|
160
|
+
while (tempDiv.firstChild) {
|
|
161
|
+
this._containerElement!.appendChild(tempDiv.firstChild);
|
|
162
|
+
}
|
|
163
|
+
} else if (item instanceof BaseComponent) {
|
|
164
|
+
item.render(this._containerElement!);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Clear all content from container
|
|
173
|
+
*/
|
|
174
|
+
clearContent(): this {
|
|
175
|
+
if (this._containerElement) {
|
|
176
|
+
this._containerElement.innerHTML = '';
|
|
177
|
+
}
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get the container element ID for rendering child components
|
|
183
|
+
*/
|
|
184
|
+
contentId(): string {
|
|
185
|
+
return `${this._id}-content`;
|
|
186
|
+
}
|
|
187
|
+
|
|
90
188
|
/* -------------------------
|
|
91
189
|
* Render
|
|
92
190
|
* ------------------------- */
|
|
@@ -94,10 +192,11 @@ export class Container extends BaseComponent<ContainerState> {
|
|
|
94
192
|
render(targetId?: string | HTMLElement | BaseComponent<any>): this {
|
|
95
193
|
const container = this._setupContainer(targetId);
|
|
96
194
|
|
|
97
|
-
const { class: className, style, direction, gap, align, justify } = this.state;
|
|
195
|
+
const { content, class: className, style, direction, gap, align, justify } = this.state;
|
|
98
196
|
|
|
99
197
|
const div = document.createElement('div');
|
|
100
198
|
div.id = this._id;
|
|
199
|
+
this._containerElement = div;
|
|
101
200
|
|
|
102
201
|
// Always include jux-container class, append custom classes
|
|
103
202
|
div.className = className ? `jux-container ${className}` : 'jux-container';
|
|
@@ -128,6 +227,22 @@ export class Container extends BaseComponent<ContainerState> {
|
|
|
128
227
|
div.setAttribute('style', computedStyle);
|
|
129
228
|
}
|
|
130
229
|
|
|
230
|
+
// Render initial content
|
|
231
|
+
if (content) {
|
|
232
|
+
const items = Array.isArray(content) ? content : [content];
|
|
233
|
+
items.forEach(item => {
|
|
234
|
+
if (typeof item === 'string') {
|
|
235
|
+
const tempDiv = document.createElement('div');
|
|
236
|
+
tempDiv.innerHTML = item;
|
|
237
|
+
while (tempDiv.firstChild) {
|
|
238
|
+
div.appendChild(tempDiv.firstChild);
|
|
239
|
+
}
|
|
240
|
+
} else if (item instanceof BaseComponent) {
|
|
241
|
+
item.render(div);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
131
246
|
this._wireStandardEvents(div);
|
|
132
247
|
|
|
133
248
|
container.appendChild(div);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juxscript",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.65",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A JavaScript UX authorship platform",
|
|
6
6
|
"main": "index.js",
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"build": "tsc",
|
|
48
48
|
"dev": "tsc --watch",
|
|
49
49
|
"prepublishOnly": "npm run build",
|
|
50
|
-
"postpublish": "echo '✅ Published successfully. Verify with: npm info juxscript'"
|
|
50
|
+
"postpublish": "echo '✅ Published successfully. Verify with: npm info juxscript'",
|
|
51
|
+
"scan-css": "node scripts/scan-css-classes.js"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
53
54
|
"acorn": "^8.15.0",
|