juxscript 1.0.19 → 1.0.21
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/bin/cli.js +121 -72
- package/lib/components/alert.ts +212 -165
- package/lib/components/badge.ts +93 -103
- package/lib/components/base/BaseComponent.ts +397 -0
- package/lib/components/base/FormInput.ts +322 -0
- package/lib/components/button.ts +63 -122
- package/lib/components/card.ts +109 -155
- package/lib/components/charts/areachart.ts +315 -0
- package/lib/components/charts/barchart.ts +421 -0
- package/lib/components/charts/doughnutchart.ts +263 -0
- package/lib/components/charts/lib/BaseChart.ts +402 -0
- package/lib/components/charts/lib/chart-types.ts +159 -0
- package/lib/components/charts/lib/chart-utils.ts +160 -0
- package/lib/components/charts/lib/chart.ts +707 -0
- package/lib/components/checkbox.ts +264 -127
- package/lib/components/code.ts +75 -108
- package/lib/components/container.ts +113 -130
- package/lib/components/data.ts +37 -5
- package/lib/components/datepicker.ts +195 -147
- package/lib/components/dialog.ts +187 -157
- package/lib/components/divider.ts +85 -191
- package/lib/components/docs-data.json +544 -2027
- package/lib/components/dropdown.ts +178 -136
- package/lib/components/element.ts +227 -171
- package/lib/components/fileupload.ts +285 -228
- package/lib/components/guard.ts +92 -0
- package/lib/components/heading.ts +46 -69
- package/lib/components/helpers.ts +13 -6
- package/lib/components/hero.ts +107 -95
- package/lib/components/icon.ts +160 -0
- package/lib/components/icons.ts +175 -0
- package/lib/components/include.ts +153 -5
- package/lib/components/input.ts +174 -374
- package/lib/components/kpicard.ts +16 -16
- package/lib/components/list.ts +378 -240
- package/lib/components/loading.ts +142 -211
- package/lib/components/menu.ts +103 -97
- package/lib/components/modal.ts +138 -144
- package/lib/components/nav.ts +169 -90
- package/lib/components/paragraph.ts +49 -150
- package/lib/components/progress.ts +118 -200
- package/lib/components/radio.ts +297 -149
- package/lib/components/script.ts +19 -87
- package/lib/components/select.ts +184 -186
- package/lib/components/sidebar.ts +152 -140
- package/lib/components/style.ts +19 -82
- package/lib/components/switch.ts +258 -188
- package/lib/components/table.ts +1117 -170
- package/lib/components/tabs.ts +162 -145
- package/lib/components/theme-toggle.ts +108 -169
- package/lib/components/tooltip.ts +86 -157
- package/lib/components/write.ts +108 -127
- package/lib/jux.ts +86 -41
- package/machinery/build.js +466 -0
- package/machinery/compiler.js +354 -105
- package/machinery/server.js +23 -100
- package/machinery/watcher.js +153 -130
- package/package.json +1 -2
- package/presets/base.css +1166 -0
- package/presets/notion.css +2 -1975
- package/lib/adapters/base-adapter.js +0 -35
- package/lib/adapters/index.js +0 -33
- package/lib/adapters/mysql-adapter.js +0 -65
- package/lib/adapters/postgres-adapter.js +0 -70
- package/lib/adapters/sqlite-adapter.js +0 -56
- package/lib/components/areachart.ts +0 -1246
- package/lib/components/areachartsmooth.ts +0 -1380
- package/lib/components/barchart.ts +0 -1250
- package/lib/components/chart.ts +0 -127
- package/lib/components/doughnutchart.ts +0 -1191
- package/lib/components/footer.ts +0 -165
- package/lib/components/header.ts +0 -187
- package/lib/components/layout.ts +0 -239
- package/lib/components/main.ts +0 -137
- package/lib/layouts/default.jux +0 -8
- package/lib/layouts/figma.jux +0 -0
- /package/lib/{themes → components/charts/lib}/charts.js +0 -0
|
@@ -1,65 +1,47 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
+
|
|
3
|
+
// Event definitions
|
|
4
|
+
const TRIGGER_EVENTS = [] as const;
|
|
5
|
+
const CALLBACK_EVENTS = [] as const;
|
|
2
6
|
|
|
3
|
-
/**
|
|
4
|
-
* Tooltip component options
|
|
5
|
-
*/
|
|
6
7
|
export interface TooltipOptions {
|
|
7
8
|
text?: string;
|
|
8
9
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
9
|
-
trigger?: 'hover' | 'click' | 'focus';
|
|
10
10
|
style?: string;
|
|
11
11
|
class?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Tooltip component state
|
|
16
|
-
*/
|
|
17
14
|
type TooltipState = {
|
|
18
15
|
text: string;
|
|
19
16
|
position: string;
|
|
20
|
-
trigger: string;
|
|
21
17
|
style: string;
|
|
22
18
|
class: string;
|
|
23
19
|
};
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* Usage:
|
|
29
|
-
* // Attach to existing element
|
|
30
|
-
* jux.tooltip('help-tip', {
|
|
31
|
-
* text: 'This is helpful information',
|
|
32
|
-
* position: 'top'
|
|
33
|
-
* }).attachTo('#help-icon');
|
|
34
|
-
*
|
|
35
|
-
* // Or render with content
|
|
36
|
-
* jux.button('info').label('ℹ️').render('#app');
|
|
37
|
-
* jux.tooltip('info-tip').text('More info').attachTo('#info');
|
|
38
|
-
*/
|
|
39
|
-
export class Tooltip {
|
|
40
|
-
state: TooltipState;
|
|
41
|
-
container: HTMLElement | null = null;
|
|
42
|
-
_id: string;
|
|
43
|
-
id: string;
|
|
44
|
-
private _targetElement: HTMLElement | null = null;
|
|
45
|
-
private _tooltipElement: HTMLElement | null = null;
|
|
21
|
+
export class Tooltip extends BaseComponent<TooltipState> {
|
|
22
|
+
private _tooltip: HTMLElement | null = null;
|
|
23
|
+
private _target: HTMLElement | null = null;
|
|
46
24
|
|
|
47
25
|
constructor(id: string, options: TooltipOptions = {}) {
|
|
48
|
-
|
|
49
|
-
this.id = id;
|
|
50
|
-
|
|
51
|
-
this.state = {
|
|
26
|
+
super(id, {
|
|
52
27
|
text: options.text ?? '',
|
|
53
28
|
position: options.position ?? 'top',
|
|
54
|
-
trigger: options.trigger ?? 'hover',
|
|
55
29
|
style: options.style ?? '',
|
|
56
30
|
class: options.class ?? ''
|
|
57
|
-
};
|
|
31
|
+
});
|
|
58
32
|
}
|
|
59
33
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
34
|
+
protected getTriggerEvents(): readonly string[] {
|
|
35
|
+
return TRIGGER_EVENTS;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected getCallbackEvents(): readonly string[] {
|
|
39
|
+
return CALLBACK_EVENTS;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
43
|
+
* FLUENT API
|
|
44
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
63
45
|
|
|
64
46
|
text(value: string): this {
|
|
65
47
|
this.state.text = value;
|
|
@@ -71,133 +53,57 @@ export class Tooltip {
|
|
|
71
53
|
return this;
|
|
72
54
|
}
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return this;
|
|
77
|
-
}
|
|
56
|
+
attachTo(target: HTMLElement | null): this {
|
|
57
|
+
if (!target) return this;
|
|
78
58
|
|
|
79
|
-
|
|
80
|
-
this.
|
|
59
|
+
this._target = target;
|
|
60
|
+
this._setupTooltip();
|
|
81
61
|
return this;
|
|
82
62
|
}
|
|
83
63
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/* -------------------------
|
|
90
|
-
* Methods
|
|
91
|
-
* ------------------------- */
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Attach tooltip to an element
|
|
95
|
-
*/
|
|
96
|
-
attachTo(target: string | HTMLElement | any): this {
|
|
97
|
-
let targetElement: HTMLElement | null = null;
|
|
98
|
-
|
|
99
|
-
if (typeof target === 'string') {
|
|
100
|
-
// String selector
|
|
101
|
-
const el = document.querySelector(target);
|
|
102
|
-
if (!el || !(el instanceof HTMLElement)) {
|
|
103
|
-
throw new Error(`Tooltip: Target element "${target}" not found`);
|
|
104
|
-
}
|
|
105
|
-
targetElement = el;
|
|
106
|
-
} else if (target instanceof HTMLElement) {
|
|
107
|
-
// Direct HTMLElement
|
|
108
|
-
targetElement = target;
|
|
109
|
-
} else if (target && target.container) {
|
|
110
|
-
// Jux component with container
|
|
111
|
-
targetElement = target.container;
|
|
112
|
-
} else if (target && target._id) {
|
|
113
|
-
// Jux component with _id
|
|
114
|
-
const el = document.getElementById(target._id);
|
|
115
|
-
if (!el) {
|
|
116
|
-
throw new Error(`Tooltip: Target element with id "${target._id}" not found`);
|
|
117
|
-
}
|
|
118
|
-
targetElement = el;
|
|
119
|
-
} else {
|
|
120
|
-
throw new Error('Tooltip: Invalid target element');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
this._targetElement = targetElement;
|
|
124
|
-
this._createTooltip();
|
|
125
|
-
this._attachEventListeners();
|
|
64
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
65
|
+
* RENDER
|
|
66
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
126
67
|
|
|
127
|
-
|
|
128
|
-
|
|
68
|
+
private _setupTooltip(): void {
|
|
69
|
+
if (!this._target) return;
|
|
129
70
|
|
|
130
|
-
show(): void {
|
|
131
|
-
if (this._tooltipElement) {
|
|
132
|
-
this._tooltipElement.classList.add('jux-tooltip-visible');
|
|
133
|
-
this._positionTooltip();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
hide(): void {
|
|
138
|
-
if (this._tooltipElement) {
|
|
139
|
-
this._tooltipElement.classList.remove('jux-tooltip-visible');
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/* -------------------------
|
|
144
|
-
* Helpers
|
|
145
|
-
* ------------------------- */
|
|
146
|
-
|
|
147
|
-
private _createTooltip(): void {
|
|
148
71
|
const { text, position, style, class: className } = this.state;
|
|
149
72
|
|
|
73
|
+
// Create tooltip element
|
|
150
74
|
const tooltip = document.createElement('div');
|
|
151
75
|
tooltip.className = `jux-tooltip jux-tooltip-${position}`;
|
|
152
76
|
tooltip.id = this._id;
|
|
153
|
-
tooltip.
|
|
77
|
+
if (className) tooltip.className += ` ${className}`;
|
|
78
|
+
if (style) tooltip.setAttribute('style', style);
|
|
154
79
|
tooltip.textContent = text;
|
|
80
|
+
tooltip.style.display = 'none';
|
|
81
|
+
this._tooltip = tooltip;
|
|
155
82
|
|
|
156
|
-
|
|
157
|
-
tooltip.className += ` ${className}`;
|
|
158
|
-
}
|
|
83
|
+
document.body.appendChild(tooltip);
|
|
159
84
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
85
|
+
// Show on hover
|
|
86
|
+
this._target.addEventListener('mouseenter', () => {
|
|
87
|
+
this._show();
|
|
88
|
+
});
|
|
163
89
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
90
|
+
this._target.addEventListener('mouseleave', () => {
|
|
91
|
+
this._hide();
|
|
92
|
+
});
|
|
167
93
|
|
|
168
|
-
|
|
169
|
-
if (!this._targetElement) return;
|
|
170
|
-
|
|
171
|
-
const { trigger } = this.state;
|
|
172
|
-
|
|
173
|
-
if (trigger === 'hover') {
|
|
174
|
-
this._targetElement.addEventListener('mouseenter', () => this.show());
|
|
175
|
-
this._targetElement.addEventListener('mouseleave', () => this.hide());
|
|
176
|
-
} else if (trigger === 'click') {
|
|
177
|
-
this._targetElement.addEventListener('click', () => {
|
|
178
|
-
if (this._tooltipElement?.classList.contains('jux-tooltip-visible')) {
|
|
179
|
-
this.hide();
|
|
180
|
-
} else {
|
|
181
|
-
this.show();
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
} else if (trigger === 'focus') {
|
|
185
|
-
this._targetElement.addEventListener('focus', () => this.show());
|
|
186
|
-
this._targetElement.addEventListener('blur', () => this.hide());
|
|
187
|
-
}
|
|
94
|
+
this._injectTooltipStyles();
|
|
188
95
|
}
|
|
189
96
|
|
|
190
|
-
private
|
|
191
|
-
if (!this.
|
|
97
|
+
private _show(): void {
|
|
98
|
+
if (!this._tooltip || !this._target) return;
|
|
192
99
|
|
|
193
|
-
const targetRect = this.
|
|
194
|
-
const tooltipRect = this.
|
|
195
|
-
const { position } = this.state;
|
|
100
|
+
const targetRect = this._target.getBoundingClientRect();
|
|
101
|
+
const tooltipRect = this._tooltip.getBoundingClientRect();
|
|
196
102
|
|
|
197
103
|
let top = 0;
|
|
198
104
|
let left = 0;
|
|
199
105
|
|
|
200
|
-
switch (position) {
|
|
106
|
+
switch (this.state.position) {
|
|
201
107
|
case 'top':
|
|
202
108
|
top = targetRect.top - tooltipRect.height - 8;
|
|
203
109
|
left = targetRect.left + (targetRect.width - tooltipRect.width) / 2;
|
|
@@ -216,26 +122,49 @@ export class Tooltip {
|
|
|
216
122
|
break;
|
|
217
123
|
}
|
|
218
124
|
|
|
219
|
-
this.
|
|
220
|
-
this.
|
|
125
|
+
this._tooltip.style.top = `${top}px`;
|
|
126
|
+
this._tooltip.style.left = `${left}px`;
|
|
127
|
+
this._tooltip.style.display = 'block';
|
|
221
128
|
}
|
|
222
129
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
130
|
+
private _hide(): void {
|
|
131
|
+
if (this._tooltip) {
|
|
132
|
+
this._tooltip.style.display = 'none';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
226
135
|
|
|
227
136
|
render(targetId?: string): this {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
throw new Error('Tooltip requires a target element. Use attachTo(selector) instead.');
|
|
137
|
+
// Tooltips don't render to containers, they attach to targets
|
|
138
|
+
console.warn('Tooltip: Use .attachTo(element) instead of .render()');
|
|
139
|
+
return this;
|
|
232
140
|
}
|
|
233
141
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
142
|
+
private _injectTooltipStyles(): void {
|
|
143
|
+
const styleId = 'jux-tooltip-styles';
|
|
144
|
+
if (document.getElementById(styleId)) return;
|
|
145
|
+
|
|
146
|
+
const style = document.createElement('style');
|
|
147
|
+
style.id = styleId;
|
|
148
|
+
style.textContent = `
|
|
149
|
+
.jux-tooltip {
|
|
150
|
+
position: fixed;
|
|
151
|
+
background: #1f2937;
|
|
152
|
+
color: white;
|
|
153
|
+
padding: 6px 12px;
|
|
154
|
+
border-radius: 6px;
|
|
155
|
+
font-size: 12px;
|
|
156
|
+
white-space: nowrap;
|
|
157
|
+
z-index: 10000;
|
|
158
|
+
pointer-events: none;
|
|
159
|
+
animation: jux-tooltip-fade 0.2s;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@keyframes jux-tooltip-fade {
|
|
163
|
+
from { opacity: 0; }
|
|
164
|
+
to { opacity: 1; }
|
|
165
|
+
}
|
|
166
|
+
`;
|
|
167
|
+
document.head.appendChild(style);
|
|
239
168
|
}
|
|
240
169
|
}
|
|
241
170
|
|
package/lib/components/write.ts
CHANGED
|
@@ -22,18 +22,10 @@ export interface WriteOptions {
|
|
|
22
22
|
* jux.write('Content').render('#container');
|
|
23
23
|
*
|
|
24
24
|
* // Write HTML
|
|
25
|
-
* jux.write('<strong>Bold text</strong>'
|
|
25
|
+
* jux.write('<strong>Bold text</strong>').html(true).render('#container');
|
|
26
26
|
*
|
|
27
27
|
* // Write with styling
|
|
28
|
-
* jux.write('Styled text'
|
|
29
|
-
* tagType: 'p',
|
|
30
|
-
* className: 'highlight',
|
|
31
|
-
* style: 'color: red;'
|
|
32
|
-
* }).render('#container');
|
|
33
|
-
*
|
|
34
|
-
* // Append multiple writes
|
|
35
|
-
* jux.write('First').render('#container');
|
|
36
|
-
* jux.write('Second').render('#container');
|
|
28
|
+
* jux.write('Styled text').style('color: red;').render('#container');
|
|
37
29
|
*/
|
|
38
30
|
export class Write {
|
|
39
31
|
private content: string;
|
|
@@ -50,54 +42,63 @@ export class Write {
|
|
|
50
42
|
...options
|
|
51
43
|
};
|
|
52
44
|
}
|
|
45
|
+
|
|
46
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
47
|
+
* FLUENT API
|
|
48
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
49
|
+
|
|
53
50
|
/**
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
* Set HTML mode (treat content as HTML)
|
|
52
|
+
*/
|
|
56
53
|
html(enabled: boolean = true): this {
|
|
57
54
|
this.options.html = enabled;
|
|
58
55
|
return this;
|
|
59
56
|
}
|
|
57
|
+
|
|
60
58
|
/**
|
|
61
|
-
*
|
|
62
|
-
* Falls back to body if no target specified
|
|
59
|
+
* Set tag type
|
|
63
60
|
*/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
70
|
-
console.warn(`Write: Target element "${selector}" not found`);
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
61
|
+
tagType(value: string): this {
|
|
62
|
+
this.options.tagType = value;
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
73
65
|
|
|
74
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Set CSS class
|
|
68
|
+
*/
|
|
69
|
+
className(value: string): this {
|
|
70
|
+
this.options.className = value;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Set inline styles
|
|
76
|
+
*/
|
|
77
|
+
style(value: string): this {
|
|
78
|
+
this.options.style = value;
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Set custom attributes
|
|
84
|
+
*/
|
|
85
|
+
attrs(attributes: Record<string, string>): this {
|
|
86
|
+
this.options.attributes = attributes;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
90
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
91
|
+
* RENDER METHODS
|
|
92
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Render content to target element
|
|
96
|
+
*/
|
|
97
|
+
render(targetSelector?: string): this {
|
|
98
|
+
const target = this._getTarget(targetSelector);
|
|
99
|
+
if (!target) return this;
|
|
99
100
|
|
|
100
|
-
|
|
101
|
+
const element = this._createElement();
|
|
101
102
|
target.appendChild(element);
|
|
102
103
|
|
|
103
104
|
return this;
|
|
@@ -107,19 +108,14 @@ export class Write {
|
|
|
107
108
|
* Replace target content (clear first, then render)
|
|
108
109
|
*/
|
|
109
110
|
replace(targetSelector?: string): this {
|
|
110
|
-
const
|
|
111
|
-
|
|
111
|
+
const target = this._getTarget(targetSelector);
|
|
112
|
+
if (!target) return this;
|
|
112
113
|
|
|
113
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
114
|
-
console.warn(`Write: Target element "${selector}" not found`);
|
|
115
|
-
return this;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Clear existing content
|
|
119
114
|
target.innerHTML = '';
|
|
115
|
+
const element = this._createElement();
|
|
116
|
+
target.appendChild(element);
|
|
120
117
|
|
|
121
|
-
|
|
122
|
-
return this.render(selector);
|
|
118
|
+
return this;
|
|
123
119
|
}
|
|
124
120
|
|
|
125
121
|
/**
|
|
@@ -127,35 +123,12 @@ export class Write {
|
|
|
127
123
|
*/
|
|
128
124
|
before(targetSelector: string): this {
|
|
129
125
|
const target = document.querySelector(targetSelector);
|
|
130
|
-
|
|
131
126
|
if (!target || !(target instanceof HTMLElement)) {
|
|
132
127
|
console.warn(`Write: Target element "${targetSelector}" not found`);
|
|
133
128
|
return this;
|
|
134
129
|
}
|
|
135
130
|
|
|
136
|
-
const element =
|
|
137
|
-
|
|
138
|
-
if (this.options.html) {
|
|
139
|
-
element.innerHTML = this.content;
|
|
140
|
-
} else {
|
|
141
|
-
element.textContent = this.content;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (this.options.className) {
|
|
145
|
-
element.className = this.options.className;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (this.options.style) {
|
|
149
|
-
element.setAttribute('style', this.options.style);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (this.options.attributes) {
|
|
153
|
-
Object.entries(this.options.attributes).forEach(([key, value]) => {
|
|
154
|
-
element.setAttribute(key, value);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Insert before target
|
|
131
|
+
const element = this._createElement();
|
|
159
132
|
target.parentNode?.insertBefore(element, target);
|
|
160
133
|
|
|
161
134
|
return this;
|
|
@@ -166,35 +139,12 @@ export class Write {
|
|
|
166
139
|
*/
|
|
167
140
|
after(targetSelector: string): this {
|
|
168
141
|
const target = document.querySelector(targetSelector);
|
|
169
|
-
|
|
170
142
|
if (!target || !(target instanceof HTMLElement)) {
|
|
171
143
|
console.warn(`Write: Target element "${targetSelector}" not found`);
|
|
172
144
|
return this;
|
|
173
145
|
}
|
|
174
146
|
|
|
175
|
-
const element =
|
|
176
|
-
|
|
177
|
-
if (this.options.html) {
|
|
178
|
-
element.innerHTML = this.content;
|
|
179
|
-
} else {
|
|
180
|
-
element.textContent = this.content;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (this.options.className) {
|
|
184
|
-
element.className = this.options.className;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (this.options.style) {
|
|
188
|
-
element.setAttribute('style', this.options.style);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (this.options.attributes) {
|
|
192
|
-
Object.entries(this.options.attributes).forEach(([key, value]) => {
|
|
193
|
-
element.setAttribute(key, value);
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Insert after target
|
|
147
|
+
const element = this._createElement();
|
|
198
148
|
target.parentNode?.insertBefore(element, target.nextSibling);
|
|
199
149
|
|
|
200
150
|
return this;
|
|
@@ -204,87 +154,118 @@ export class Write {
|
|
|
204
154
|
* Prepend to target (insert as first child)
|
|
205
155
|
*/
|
|
206
156
|
prepend(targetSelector?: string): this {
|
|
207
|
-
const
|
|
208
|
-
|
|
157
|
+
const target = this._getTarget(targetSelector);
|
|
158
|
+
if (!target) return this;
|
|
159
|
+
|
|
160
|
+
const element = this._createElement();
|
|
161
|
+
target.insertBefore(element, target.firstChild);
|
|
162
|
+
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Append to target (alias for render)
|
|
168
|
+
*/
|
|
169
|
+
append(targetSelector?: string): this {
|
|
170
|
+
return this.render(targetSelector);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
174
|
+
* PRIVATE HELPERS
|
|
175
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
176
|
+
|
|
177
|
+
private _getTarget(selector?: string): HTMLElement | null {
|
|
178
|
+
const targetSelector = selector || 'body';
|
|
179
|
+
const target = document.querySelector(targetSelector);
|
|
209
180
|
|
|
210
181
|
if (!target || !(target instanceof HTMLElement)) {
|
|
211
|
-
console.warn(`Write: Target element "${
|
|
212
|
-
return
|
|
182
|
+
console.warn(`Write: Target element "${targetSelector}" not found`);
|
|
183
|
+
return null;
|
|
213
184
|
}
|
|
214
185
|
|
|
186
|
+
return target;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private _createElement(): HTMLElement {
|
|
215
190
|
const element = document.createElement(this.options.tagType!);
|
|
216
191
|
|
|
192
|
+
// Set content (text or HTML)
|
|
217
193
|
if (this.options.html) {
|
|
218
194
|
element.innerHTML = this.content;
|
|
219
195
|
} else {
|
|
220
196
|
element.textContent = this.content;
|
|
221
197
|
}
|
|
222
198
|
|
|
199
|
+
// Apply className
|
|
223
200
|
if (this.options.className) {
|
|
224
201
|
element.className = this.options.className;
|
|
225
202
|
}
|
|
226
203
|
|
|
204
|
+
// Apply inline styles
|
|
227
205
|
if (this.options.style) {
|
|
228
206
|
element.setAttribute('style', this.options.style);
|
|
229
207
|
}
|
|
230
208
|
|
|
209
|
+
// Apply custom attributes
|
|
231
210
|
if (this.options.attributes) {
|
|
232
211
|
Object.entries(this.options.attributes).forEach(([key, value]) => {
|
|
233
212
|
element.setAttribute(key, value);
|
|
234
213
|
});
|
|
235
214
|
}
|
|
236
215
|
|
|
237
|
-
|
|
238
|
-
target.insertBefore(element, target.firstChild);
|
|
239
|
-
|
|
240
|
-
return this;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Append to target (alias for render)
|
|
245
|
-
*/
|
|
246
|
-
append(targetSelector?: string): this {
|
|
247
|
-
return this.render(targetSelector);
|
|
216
|
+
return element;
|
|
248
217
|
}
|
|
249
218
|
}
|
|
250
219
|
|
|
251
220
|
/**
|
|
252
|
-
* Factory function
|
|
221
|
+
* Factory function - simple, no overloads
|
|
253
222
|
*/
|
|
254
223
|
export function write(content: string, options: WriteOptions = {}): Write {
|
|
255
224
|
return new Write(content, options);
|
|
256
225
|
}
|
|
257
226
|
|
|
227
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
228
|
+
* SHORTHAND HELPERS
|
|
229
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
230
|
+
|
|
258
231
|
/**
|
|
259
|
-
*
|
|
232
|
+
* Write text (explicit)
|
|
260
233
|
*/
|
|
261
|
-
|
|
262
|
-
// Write text (alias)
|
|
263
234
|
export function writeText(content: string, options: Omit<WriteOptions, 'html'> = {}): Write {
|
|
264
235
|
return new Write(content, { ...options, html: false });
|
|
265
236
|
}
|
|
266
237
|
|
|
267
|
-
|
|
238
|
+
/**
|
|
239
|
+
* Write HTML (explicit)
|
|
240
|
+
*/
|
|
268
241
|
export function writeHtml(content: string, options: Omit<WriteOptions, 'html'> = {}): Write {
|
|
269
242
|
return new Write(content, { ...options, html: true });
|
|
270
243
|
}
|
|
271
244
|
|
|
272
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Write paragraph
|
|
247
|
+
*/
|
|
273
248
|
export function writeParagraph(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
|
|
274
249
|
return new Write(content, { ...options, tagType: 'p' });
|
|
275
250
|
}
|
|
276
251
|
|
|
277
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Write heading
|
|
254
|
+
*/
|
|
278
255
|
export function writeHeading(content: string, level: 1 | 2 | 3 | 4 | 5 | 6 = 2, options: Omit<WriteOptions, 'tagType'> = {}): Write {
|
|
279
256
|
return new Write(content, { ...options, tagType: `h${level}` });
|
|
280
257
|
}
|
|
281
258
|
|
|
282
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Write span
|
|
261
|
+
*/
|
|
283
262
|
export function writeSpan(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
|
|
284
263
|
return new Write(content, { ...options, tagType: 'span' });
|
|
285
264
|
}
|
|
286
265
|
|
|
287
|
-
|
|
266
|
+
/**
|
|
267
|
+
* Write div (explicit)
|
|
268
|
+
*/
|
|
288
269
|
export function writeDiv(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
|
|
289
270
|
return new Write(content, { ...options, tagType: 'div' });
|
|
290
271
|
}
|