juxscript 1.0.3 → 1.0.4
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/README.md +37 -92
- package/bin/cli.js +57 -56
- package/lib/components/alert.ts +240 -0
- package/lib/components/app.ts +216 -82
- package/lib/components/badge.ts +164 -0
- package/lib/components/button.ts +188 -53
- package/lib/components/card.ts +75 -61
- package/lib/components/chart.ts +17 -15
- package/lib/components/checkbox.ts +228 -0
- package/lib/components/code.ts +66 -152
- package/lib/components/container.ts +104 -208
- package/lib/components/data.ts +1 -3
- package/lib/components/datepicker.ts +226 -0
- package/lib/components/dialog.ts +258 -0
- package/lib/components/docs-data.json +1697 -388
- package/lib/components/dropdown.ts +244 -0
- package/lib/components/element.ts +271 -0
- package/lib/components/fileupload.ts +319 -0
- package/lib/components/footer.ts +37 -18
- package/lib/components/header.ts +53 -33
- package/lib/components/heading.ts +119 -0
- package/lib/components/helpers.ts +34 -0
- package/lib/components/hero.ts +57 -31
- package/lib/components/include.ts +292 -0
- package/lib/components/input.ts +166 -78
- package/lib/components/layout.ts +144 -18
- package/lib/components/list.ts +83 -74
- package/lib/components/loading.ts +263 -0
- package/lib/components/main.ts +43 -17
- package/lib/components/menu.ts +108 -24
- package/lib/components/modal.ts +50 -21
- package/lib/components/nav.ts +60 -18
- package/lib/components/paragraph.ts +111 -0
- package/lib/components/progress.ts +276 -0
- package/lib/components/radio.ts +236 -0
- package/lib/components/req.ts +300 -0
- package/lib/components/script.ts +33 -74
- package/lib/components/select.ts +247 -0
- package/lib/components/sidebar.ts +86 -36
- package/lib/components/style.ts +47 -70
- package/lib/components/switch.ts +261 -0
- package/lib/components/table.ts +47 -24
- package/lib/components/tabs.ts +105 -63
- package/lib/components/theme-toggle.ts +361 -0
- package/lib/components/token-calculator.ts +380 -0
- package/lib/components/tooltip.ts +244 -0
- package/lib/components/view.ts +36 -20
- package/lib/components/write.ts +284 -0
- package/lib/globals.d.ts +21 -0
- package/lib/jux.ts +172 -68
- package/lib/presets/notion.css +521 -0
- package/lib/presets/notion.jux +27 -0
- package/lib/reactivity/state.ts +364 -0
- package/machinery/compiler.js +126 -38
- package/machinery/generators/html.js +2 -3
- package/machinery/server.js +2 -2
- package/package.json +29 -3
- package/lib/components/import.ts +0 -430
- package/lib/components/node.ts +0 -200
- package/lib/components/reactivity.js +0 -104
- package/lib/components/theme.ts +0 -97
- package/lib/layouts/notion.css +0 -258
- package/lib/styles/base-theme.css +0 -186
- package/lib/styles/dark-theme.css +0 -144
- package/lib/styles/light-theme.css +0 -144
- package/lib/styles/tokens/dark.css +0 -86
- package/lib/styles/tokens/light.css +0 -86
- package/lib/templates/index.juxt +0 -33
- package/lib/themes/dark.css +0 -86
- package/lib/themes/light.css +0 -86
- /package/lib/{styles → presets}/global.css +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dropdown menu item
|
|
5
|
+
*/
|
|
6
|
+
export interface DropdownItem {
|
|
7
|
+
label: string;
|
|
8
|
+
value?: string;
|
|
9
|
+
icon?: string;
|
|
10
|
+
click?: () => void;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
divider?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Dropdown component options
|
|
17
|
+
*/
|
|
18
|
+
export interface DropdownOptions {
|
|
19
|
+
items?: DropdownItem[];
|
|
20
|
+
trigger?: string;
|
|
21
|
+
position?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
|
|
22
|
+
style?: string;
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Dropdown component state
|
|
28
|
+
*/
|
|
29
|
+
type DropdownState = {
|
|
30
|
+
items: DropdownItem[];
|
|
31
|
+
trigger: string;
|
|
32
|
+
position: string;
|
|
33
|
+
style: string;
|
|
34
|
+
class: string;
|
|
35
|
+
isOpen: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Dropdown Menu component - Context menus, action menus
|
|
40
|
+
*
|
|
41
|
+
* Usage:
|
|
42
|
+
* jux.dropdown('actions', {
|
|
43
|
+
* trigger: 'Actions ▾',
|
|
44
|
+
* position: 'bottom-right',
|
|
45
|
+
* items: [
|
|
46
|
+
* { label: 'Edit', icon: '✏️', click: () => console.log('Edit') },
|
|
47
|
+
* { label: 'Delete', icon: '🗑️', click: () => console.log('Delete') },
|
|
48
|
+
* { divider: true },
|
|
49
|
+
* { label: 'Archive', click: () => console.log('Archive') }
|
|
50
|
+
* ]
|
|
51
|
+
* }).render('#toolbar');
|
|
52
|
+
*
|
|
53
|
+
* // Control programmatically
|
|
54
|
+
* const dd = jux.dropdown('my-dropdown').render();
|
|
55
|
+
* dd.open();
|
|
56
|
+
* dd.close();
|
|
57
|
+
*/
|
|
58
|
+
export class Dropdown {
|
|
59
|
+
state: DropdownState;
|
|
60
|
+
container: HTMLElement | null = null;
|
|
61
|
+
_id: string;
|
|
62
|
+
id: string;
|
|
63
|
+
|
|
64
|
+
constructor(id: string, options: DropdownOptions = {}) {
|
|
65
|
+
this._id = id;
|
|
66
|
+
this.id = id;
|
|
67
|
+
|
|
68
|
+
this.state = {
|
|
69
|
+
items: options.items ?? [],
|
|
70
|
+
trigger: options.trigger ?? 'Menu',
|
|
71
|
+
position: options.position ?? 'bottom-left',
|
|
72
|
+
style: options.style ?? '',
|
|
73
|
+
class: options.class ?? '',
|
|
74
|
+
isOpen: false
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* -------------------------
|
|
79
|
+
* Fluent API
|
|
80
|
+
* ------------------------- */
|
|
81
|
+
|
|
82
|
+
items(value: DropdownItem[]): this {
|
|
83
|
+
this.state.items = value;
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
addItem(item: DropdownItem): this {
|
|
88
|
+
this.state.items = [...this.state.items, item];
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
trigger(value: string): this {
|
|
93
|
+
this.state.trigger = value;
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
position(value: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'): this {
|
|
98
|
+
this.state.position = value;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
style(value: string): this {
|
|
103
|
+
this.state.style = value;
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
class(value: string): this {
|
|
108
|
+
this.state.class = value;
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* -------------------------
|
|
113
|
+
* Methods
|
|
114
|
+
* ------------------------- */
|
|
115
|
+
|
|
116
|
+
open(): void {
|
|
117
|
+
this.state.isOpen = true;
|
|
118
|
+
const menu = document.getElementById(`${this._id}-menu`);
|
|
119
|
+
if (menu) {
|
|
120
|
+
menu.classList.add('jux-dropdown-menu-open');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
close(): void {
|
|
125
|
+
this.state.isOpen = false;
|
|
126
|
+
const menu = document.getElementById(`${this._id}-menu`);
|
|
127
|
+
if (menu) {
|
|
128
|
+
menu.classList.remove('jux-dropdown-menu-open');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
toggle(): void {
|
|
133
|
+
if (this.state.isOpen) {
|
|
134
|
+
this.close();
|
|
135
|
+
} else {
|
|
136
|
+
this.open();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* -------------------------
|
|
141
|
+
* Render
|
|
142
|
+
* ------------------------- */
|
|
143
|
+
|
|
144
|
+
render(targetId?: string): this {
|
|
145
|
+
let container: HTMLElement;
|
|
146
|
+
|
|
147
|
+
if (targetId) {
|
|
148
|
+
const target = document.querySelector(targetId);
|
|
149
|
+
if (!target || !(target instanceof HTMLElement)) {
|
|
150
|
+
throw new Error(`Dropdown: Target element "${targetId}" not found`);
|
|
151
|
+
}
|
|
152
|
+
container = target;
|
|
153
|
+
} else {
|
|
154
|
+
container = getOrCreateContainer(this._id);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.container = container;
|
|
158
|
+
const { items, trigger, position, style, class: className } = this.state;
|
|
159
|
+
|
|
160
|
+
// Wrapper
|
|
161
|
+
const wrapper = document.createElement('div');
|
|
162
|
+
wrapper.className = 'jux-dropdown';
|
|
163
|
+
wrapper.id = this._id;
|
|
164
|
+
|
|
165
|
+
if (className) {
|
|
166
|
+
wrapper.className += ` ${className}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (style) {
|
|
170
|
+
wrapper.setAttribute('style', style);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Trigger button
|
|
174
|
+
const triggerBtn = document.createElement('button');
|
|
175
|
+
triggerBtn.className = 'jux-dropdown-trigger';
|
|
176
|
+
triggerBtn.textContent = trigger;
|
|
177
|
+
triggerBtn.addEventListener('click', (e) => {
|
|
178
|
+
e.stopPropagation();
|
|
179
|
+
this.toggle();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Menu
|
|
183
|
+
const menu = document.createElement('div');
|
|
184
|
+
menu.className = `jux-dropdown-menu jux-dropdown-menu-${position}`;
|
|
185
|
+
menu.id = `${this._id}-menu`;
|
|
186
|
+
|
|
187
|
+
items.forEach(item => {
|
|
188
|
+
if (item.divider) {
|
|
189
|
+
const divider = document.createElement('div');
|
|
190
|
+
divider.className = 'jux-dropdown-divider';
|
|
191
|
+
menu.appendChild(divider);
|
|
192
|
+
} else {
|
|
193
|
+
const menuItem = document.createElement('button');
|
|
194
|
+
menuItem.className = 'jux-dropdown-item';
|
|
195
|
+
menuItem.disabled = item.disabled ?? false;
|
|
196
|
+
|
|
197
|
+
if (item.icon) {
|
|
198
|
+
const icon = document.createElement('span');
|
|
199
|
+
icon.className = 'jux-dropdown-item-icon';
|
|
200
|
+
icon.textContent = item.icon;
|
|
201
|
+
menuItem.appendChild(icon);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const label = document.createElement('span');
|
|
205
|
+
label.className = 'jux-dropdown-item-label';
|
|
206
|
+
label.textContent = item.label;
|
|
207
|
+
menuItem.appendChild(label);
|
|
208
|
+
|
|
209
|
+
menuItem.addEventListener('click', () => {
|
|
210
|
+
if (item.click) {
|
|
211
|
+
item.click();
|
|
212
|
+
}
|
|
213
|
+
this.close();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
menu.appendChild(menuItem);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
wrapper.appendChild(triggerBtn);
|
|
221
|
+
wrapper.appendChild(menu);
|
|
222
|
+
container.appendChild(wrapper);
|
|
223
|
+
|
|
224
|
+
// Close on outside click
|
|
225
|
+
document.addEventListener('click', (e) => {
|
|
226
|
+
if (!wrapper.contains(e.target as Node)) {
|
|
227
|
+
this.close();
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
renderTo(juxComponent: any): this {
|
|
235
|
+
if (!juxComponent?._id) {
|
|
236
|
+
throw new Error('Dropdown.renderTo: Invalid component');
|
|
237
|
+
}
|
|
238
|
+
return this.render(`#${juxComponent._id}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function dropdown(id: string, options: DropdownOptions = {}): Dropdown {
|
|
243
|
+
return new Dropdown(id, options);
|
|
244
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Element component options
|
|
5
|
+
*/
|
|
6
|
+
export interface ElementOptions {
|
|
7
|
+
tagType?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
text?: string;
|
|
10
|
+
innerHTML?: string;
|
|
11
|
+
attributes?: Record<string, string>;
|
|
12
|
+
styles?: Record<string, string>;
|
|
13
|
+
style?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Element component state
|
|
19
|
+
*/
|
|
20
|
+
type ElementState = {
|
|
21
|
+
tagType: string;
|
|
22
|
+
className: string;
|
|
23
|
+
text: string;
|
|
24
|
+
innerHTML: string;
|
|
25
|
+
attributes: Record<string, string>;
|
|
26
|
+
styles: Record<string, string>;
|
|
27
|
+
style: string;
|
|
28
|
+
class: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Element component - Create arbitrary HTML elements
|
|
33
|
+
*
|
|
34
|
+
* Usage:
|
|
35
|
+
* const div = jux.element('myDiv', { tagType: 'div', className: 'container' });
|
|
36
|
+
* div.render('#target');
|
|
37
|
+
*
|
|
38
|
+
* const span = jux.element('mySpan')
|
|
39
|
+
* .tagType('span')
|
|
40
|
+
* .className('highlight')
|
|
41
|
+
* .text('Hello World')
|
|
42
|
+
* .render();
|
|
43
|
+
*
|
|
44
|
+
* For reactive content, use State bindings after render:
|
|
45
|
+
* const count = state(0);
|
|
46
|
+
* jux.element('counter').render('#app');
|
|
47
|
+
* count.bindText('counter', (val) => `Count: ${val}`);
|
|
48
|
+
*/
|
|
49
|
+
export class Element {
|
|
50
|
+
state: ElementState;
|
|
51
|
+
container: HTMLElement | null = null;
|
|
52
|
+
_id: string;
|
|
53
|
+
id: string;
|
|
54
|
+
|
|
55
|
+
constructor(id: string, options: ElementOptions = {}) {
|
|
56
|
+
this._id = id;
|
|
57
|
+
this.id = id;
|
|
58
|
+
|
|
59
|
+
this.state = {
|
|
60
|
+
tagType: options.tagType ?? 'div',
|
|
61
|
+
className: options.className ?? '',
|
|
62
|
+
text: options.text ?? '',
|
|
63
|
+
innerHTML: options.innerHTML ?? '',
|
|
64
|
+
attributes: options.attributes ?? {},
|
|
65
|
+
styles: options.styles ?? {},
|
|
66
|
+
style: options.style ?? '',
|
|
67
|
+
class: options.class ?? ''
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* -------------------------
|
|
72
|
+
* Fluent API
|
|
73
|
+
* ------------------------- */
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Set the HTML tag type
|
|
77
|
+
*/
|
|
78
|
+
tagType(value: string): Element {
|
|
79
|
+
this.state.tagType = value;
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Set the className
|
|
85
|
+
*/
|
|
86
|
+
className(value: string): Element {
|
|
87
|
+
this.state.className = value;
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Add a class to existing classes
|
|
93
|
+
*/
|
|
94
|
+
addClass(value: string): Element {
|
|
95
|
+
const classes = this.state.className.split(' ').filter(c => c);
|
|
96
|
+
if (!classes.includes(value)) {
|
|
97
|
+
classes.push(value);
|
|
98
|
+
this.state.className = classes.join(' ');
|
|
99
|
+
}
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Remove a class from existing classes
|
|
105
|
+
*/
|
|
106
|
+
removeClass(value: string): Element {
|
|
107
|
+
const classes = this.state.className.split(' ').filter(c => c && c !== value);
|
|
108
|
+
this.state.className = classes.join(' ');
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Set text content (static)
|
|
114
|
+
*/
|
|
115
|
+
text(value: string): Element {
|
|
116
|
+
this.state.text = value;
|
|
117
|
+
this.state.innerHTML = '';
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Set HTML content (static, clears text)
|
|
123
|
+
*/
|
|
124
|
+
innerHTML(value: string): Element {
|
|
125
|
+
this.state.innerHTML = value;
|
|
126
|
+
this.state.text = '';
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Set a single attribute
|
|
132
|
+
*/
|
|
133
|
+
attr(name: string, value: string): Element {
|
|
134
|
+
this.state.attributes = { ...this.state.attributes, [name]: value };
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Set multiple attributes
|
|
140
|
+
*/
|
|
141
|
+
attrs(attributes: Record<string, string>): Element {
|
|
142
|
+
this.state.attributes = { ...this.state.attributes, ...attributes };
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Set inline styles
|
|
148
|
+
* @param propertyOrValue - CSS property name, style string, or style object
|
|
149
|
+
* @param value - CSS value (when first arg is property name)
|
|
150
|
+
*/
|
|
151
|
+
style(propertyOrValue: string | Record<string, string>, value?: string): Element {
|
|
152
|
+
if (typeof propertyOrValue === 'string' && value !== undefined) {
|
|
153
|
+
// Called as .style('color', 'red')
|
|
154
|
+
this.state.styles = { ...this.state.styles, [propertyOrValue]: value };
|
|
155
|
+
} else if (typeof propertyOrValue === 'string') {
|
|
156
|
+
// Called as .style('color: red; background: blue')
|
|
157
|
+
this.state.style = propertyOrValue;
|
|
158
|
+
} else if (typeof propertyOrValue === 'object') {
|
|
159
|
+
// Called as .style({ color: 'red', background: 'blue' })
|
|
160
|
+
this.state.styles = { ...this.state.styles, ...propertyOrValue };
|
|
161
|
+
}
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Set multiple styles as object
|
|
167
|
+
*/
|
|
168
|
+
styles(styles: Record<string, string>): Element {
|
|
169
|
+
this.state.styles = { ...this.state.styles, ...styles };
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Add additional classes (fluent alternative to className)
|
|
175
|
+
*/
|
|
176
|
+
class(value: string): Element {
|
|
177
|
+
this.state.class = value;
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* -------------------------
|
|
182
|
+
* Render
|
|
183
|
+
* ------------------------- */
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Render the element to a target element
|
|
187
|
+
* @param targetId - CSS selector for target element (optional, defaults to body)
|
|
188
|
+
*/
|
|
189
|
+
render(targetId?: string): Element {
|
|
190
|
+
let container: HTMLElement;
|
|
191
|
+
|
|
192
|
+
if (targetId) {
|
|
193
|
+
const target = document.querySelector(targetId);
|
|
194
|
+
if (!target || !(target instanceof HTMLElement)) {
|
|
195
|
+
throw new Error(`Element: Target element "${targetId}" not found`);
|
|
196
|
+
}
|
|
197
|
+
container = target;
|
|
198
|
+
} else {
|
|
199
|
+
container = getOrCreateContainer(this._id);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this.container = container;
|
|
203
|
+
const { tagType, className, text, innerHTML, attributes, styles, style, class: classValue } = this.state;
|
|
204
|
+
|
|
205
|
+
// Create the element
|
|
206
|
+
const element = document.createElement(tagType);
|
|
207
|
+
|
|
208
|
+
// Set ID to component ID
|
|
209
|
+
element.id = this._id;
|
|
210
|
+
|
|
211
|
+
// Set className
|
|
212
|
+
if (className) {
|
|
213
|
+
element.className = className;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (classValue) {
|
|
217
|
+
element.className = element.className ? `${element.className} ${classValue}` : classValue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Set content (innerHTML takes precedence over text)
|
|
221
|
+
if (innerHTML) {
|
|
222
|
+
element.innerHTML = innerHTML;
|
|
223
|
+
} else if (text) {
|
|
224
|
+
element.textContent = text;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Set attributes
|
|
228
|
+
Object.entries(attributes).forEach(([name, value]) => {
|
|
229
|
+
element.setAttribute(name, value);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Set styles (object)
|
|
233
|
+
Object.entries(styles).forEach(([property, value]) => {
|
|
234
|
+
element.style.setProperty(property, value);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Set style (string)
|
|
238
|
+
if (style) {
|
|
239
|
+
element.setAttribute('style', style);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
container.appendChild(element);
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Render to another Jux component's container
|
|
248
|
+
*
|
|
249
|
+
* Usage:
|
|
250
|
+
* const container = jux.element('myContainer');
|
|
251
|
+
* const child = jux.element('myChild').renderTo(container);
|
|
252
|
+
*/
|
|
253
|
+
renderTo(juxComponent: Element): Element {
|
|
254
|
+
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
255
|
+
throw new Error('Element.renderTo: Invalid component - not an object');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
259
|
+
throw new Error('Element.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return this.render(`#${juxComponent._id}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Factory helper
|
|
268
|
+
*/
|
|
269
|
+
export function element(id: string, options: ElementOptions = {}): Element {
|
|
270
|
+
return new Element(id, options);
|
|
271
|
+
}
|