juxscript 1.0.2 → 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
package/lib/components/button.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { getOrCreateContainer } from './helpers.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Button component options
|
|
6
5
|
*/
|
|
7
6
|
export interface ButtonOptions {
|
|
8
7
|
label?: string;
|
|
9
|
-
|
|
8
|
+
icon?: string;
|
|
9
|
+
click?: (e: Event) => void;
|
|
10
|
+
variant?: 'primary' | 'secondary' | 'danger' | 'success' | 'warning' | 'info' | 'link' | string;
|
|
11
|
+
size?: 'small' | 'medium' | 'large';
|
|
10
12
|
disabled?: boolean;
|
|
11
|
-
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
iconPosition?: 'left' | 'right';
|
|
15
|
+
fullWidth?: boolean;
|
|
16
|
+
type?: 'button' | 'submit' | 'reset';
|
|
17
|
+
style?: string;
|
|
18
|
+
class?: string;
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
/**
|
|
@@ -16,55 +23,153 @@ export interface ButtonOptions {
|
|
|
16
23
|
*/
|
|
17
24
|
type ButtonState = {
|
|
18
25
|
label: string;
|
|
26
|
+
icon: string;
|
|
27
|
+
click: ((e: Event) => void) | null;
|
|
19
28
|
variant: string;
|
|
29
|
+
size: string;
|
|
20
30
|
disabled: boolean;
|
|
21
|
-
|
|
31
|
+
loading: boolean;
|
|
32
|
+
iconPosition: string;
|
|
33
|
+
fullWidth: boolean;
|
|
34
|
+
type: 'button' | 'submit' | 'reset';
|
|
35
|
+
style: string;
|
|
36
|
+
class: string;
|
|
22
37
|
};
|
|
23
38
|
|
|
24
39
|
/**
|
|
25
40
|
* Button component
|
|
26
41
|
*
|
|
27
42
|
* Usage:
|
|
28
|
-
*
|
|
29
|
-
*
|
|
43
|
+
* jux.button('myButton').label('Click Me').click(() => console.log('hi')).render();
|
|
44
|
+
*
|
|
45
|
+
* // Or with options
|
|
46
|
+
* jux.button('myButton', { label: 'Click Me', click: () => console.log('hi') }).render();
|
|
30
47
|
*/
|
|
31
|
-
export class Button
|
|
32
|
-
state
|
|
48
|
+
export class Button {
|
|
49
|
+
state: ButtonState;
|
|
33
50
|
container: HTMLElement | null = null;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
_id: string;
|
|
52
|
+
id: string;
|
|
53
|
+
|
|
54
|
+
constructor(id: string, options?: ButtonOptions) {
|
|
55
|
+
this._id = id;
|
|
56
|
+
this.id = id;
|
|
57
|
+
|
|
58
|
+
const opts = options || {};
|
|
59
|
+
|
|
60
|
+
this.state = {
|
|
61
|
+
label: opts.label ?? 'Button',
|
|
62
|
+
icon: opts.icon ?? '',
|
|
63
|
+
click: opts.click ?? null,
|
|
64
|
+
variant: opts.variant ?? 'primary',
|
|
65
|
+
size: opts.size ?? 'medium',
|
|
66
|
+
disabled: opts.disabled ?? false,
|
|
67
|
+
loading: opts.loading ?? false,
|
|
68
|
+
iconPosition: opts.iconPosition ?? 'left',
|
|
69
|
+
fullWidth: opts.fullWidth ?? false,
|
|
70
|
+
type: opts.type ?? 'button',
|
|
71
|
+
style: opts.style ?? '',
|
|
72
|
+
class: opts.class ?? ''
|
|
73
|
+
};
|
|
45
74
|
}
|
|
46
75
|
|
|
47
76
|
/* -------------------------
|
|
48
77
|
* Fluent API
|
|
49
78
|
* ------------------------- */
|
|
50
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Set button label text
|
|
82
|
+
*/
|
|
51
83
|
label(value: string): this {
|
|
52
84
|
this.state.label = value;
|
|
53
85
|
return this;
|
|
54
86
|
}
|
|
55
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Set button icon (emoji or text)
|
|
90
|
+
*/
|
|
91
|
+
icon(value: string): this {
|
|
92
|
+
this.state.icon = value;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Set click handler
|
|
98
|
+
*/
|
|
99
|
+
click(callback: (e: Event) => void): this {
|
|
100
|
+
this.state.click = callback;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Set button variant/style
|
|
106
|
+
*/
|
|
56
107
|
variant(value: string): this {
|
|
57
108
|
this.state.variant = value;
|
|
58
109
|
return this;
|
|
59
110
|
}
|
|
60
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Set button size
|
|
114
|
+
*/
|
|
115
|
+
size(value: 'small' | 'medium' | 'large'): this {
|
|
116
|
+
this.state.size = value;
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Set disabled state
|
|
122
|
+
*/
|
|
61
123
|
disabled(value: boolean): this {
|
|
62
124
|
this.state.disabled = value;
|
|
63
125
|
return this;
|
|
64
126
|
}
|
|
65
127
|
|
|
66
|
-
|
|
67
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Set loading state
|
|
130
|
+
*/
|
|
131
|
+
loading(value: boolean): this {
|
|
132
|
+
this.state.loading = value;
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Set icon position (left or right)
|
|
138
|
+
*/
|
|
139
|
+
iconPosition(value: 'left' | 'right'): this {
|
|
140
|
+
this.state.iconPosition = value;
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Set full width
|
|
146
|
+
*/
|
|
147
|
+
fullWidth(value: boolean): this {
|
|
148
|
+
this.state.fullWidth = value;
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Set button type attribute
|
|
154
|
+
*/
|
|
155
|
+
type(value: 'button' | 'submit' | 'reset'): this {
|
|
156
|
+
this.state.type = value;
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Set inline style
|
|
162
|
+
*/
|
|
163
|
+
style(value: string): this {
|
|
164
|
+
this.state.style = value;
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Set additional CSS classes
|
|
170
|
+
*/
|
|
171
|
+
class(value: string): this {
|
|
172
|
+
this.state.class = value;
|
|
68
173
|
return this;
|
|
69
174
|
}
|
|
70
175
|
|
|
@@ -72,65 +177,95 @@ export class Button extends Reactive {
|
|
|
72
177
|
* Render
|
|
73
178
|
* ------------------------- */
|
|
74
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Render button to target element
|
|
182
|
+
*/
|
|
75
183
|
render(targetId?: string): this {
|
|
76
184
|
let container: HTMLElement;
|
|
77
|
-
|
|
185
|
+
|
|
78
186
|
if (targetId) {
|
|
79
|
-
// Use provided targetId - must exist
|
|
80
187
|
const target = document.querySelector(targetId);
|
|
81
188
|
if (!target || !(target instanceof HTMLElement)) {
|
|
82
189
|
throw new Error(`Button: Target element "${targetId}" not found`);
|
|
83
190
|
}
|
|
84
191
|
container = target;
|
|
85
192
|
} else {
|
|
86
|
-
|
|
87
|
-
container = getOrCreateContainer(this._componentId) as HTMLElement;
|
|
193
|
+
container = getOrCreateContainer(this._id);
|
|
88
194
|
}
|
|
89
|
-
|
|
195
|
+
|
|
90
196
|
this.container = container;
|
|
91
|
-
const { label, variant, disabled,
|
|
92
|
-
|
|
93
|
-
// Create button element
|
|
197
|
+
const { label, icon, click, variant, size, disabled, loading, iconPosition, fullWidth, type, style, class: className } = this.state;
|
|
198
|
+
|
|
94
199
|
const button = document.createElement('button');
|
|
95
|
-
button.className = `jux-button jux-button-${variant}`;
|
|
96
|
-
button.id = this.
|
|
97
|
-
button.
|
|
98
|
-
button.disabled = disabled;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
200
|
+
button.className = `jux-button jux-button-${variant} jux-button-${size}`;
|
|
201
|
+
button.id = this._id;
|
|
202
|
+
button.type = type;
|
|
203
|
+
button.disabled = disabled || loading;
|
|
204
|
+
|
|
205
|
+
if (fullWidth) {
|
|
206
|
+
button.classList.add('jux-button-full-width');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (loading) {
|
|
210
|
+
button.classList.add('jux-button-loading');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (className) {
|
|
214
|
+
button.className += ` ${className}`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (style) {
|
|
218
|
+
button.setAttribute('style', style);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Build button content
|
|
222
|
+
if (icon && iconPosition === 'left') {
|
|
223
|
+
const iconEl = document.createElement('span');
|
|
224
|
+
iconEl.className = 'jux-button-icon jux-button-icon-left';
|
|
225
|
+
iconEl.textContent = icon;
|
|
226
|
+
button.appendChild(iconEl);
|
|
103
227
|
}
|
|
104
|
-
|
|
228
|
+
|
|
229
|
+
const labelEl = document.createElement('span');
|
|
230
|
+
labelEl.className = 'jux-button-label';
|
|
231
|
+
labelEl.textContent = loading ? 'Loading...' : label;
|
|
232
|
+
button.appendChild(labelEl);
|
|
233
|
+
|
|
234
|
+
if (icon && iconPosition === 'right') {
|
|
235
|
+
const iconEl = document.createElement('span');
|
|
236
|
+
iconEl.className = 'jux-button-icon jux-button-icon-right';
|
|
237
|
+
iconEl.textContent = icon;
|
|
238
|
+
button.appendChild(iconEl);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Event binding - click handler
|
|
242
|
+
if (click) {
|
|
243
|
+
button.addEventListener('click', click);
|
|
244
|
+
}
|
|
245
|
+
|
|
105
246
|
container.appendChild(button);
|
|
106
247
|
return this;
|
|
107
248
|
}
|
|
108
249
|
|
|
109
250
|
/**
|
|
110
251
|
* Render to another Jux component's container
|
|
111
|
-
*
|
|
112
|
-
* Usage:
|
|
113
|
-
* const container = jux.node('myContainer');
|
|
114
|
-
* const btn = jux.button('myBtn').renderTo(container);
|
|
115
252
|
*/
|
|
116
|
-
renderTo(juxComponent:
|
|
117
|
-
// Verify it's a Jux component (has _componentId from Reactive base)
|
|
253
|
+
renderTo(juxComponent: this): this {
|
|
118
254
|
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
119
255
|
throw new Error('Button.renderTo: Invalid component - not an object');
|
|
120
256
|
}
|
|
121
|
-
|
|
122
|
-
if (!juxComponent.
|
|
123
|
-
throw new Error('Button.renderTo: Invalid component - missing
|
|
257
|
+
|
|
258
|
+
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
259
|
+
throw new Error('Button.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
124
260
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return this.render(`#${juxComponent._componentId}`);
|
|
261
|
+
|
|
262
|
+
return this.render(`#${juxComponent._id}`);
|
|
128
263
|
}
|
|
129
264
|
}
|
|
130
265
|
|
|
131
266
|
/**
|
|
132
267
|
* Factory helper
|
|
133
268
|
*/
|
|
134
|
-
export function button(
|
|
135
|
-
return new Button(
|
|
269
|
+
export function button(id: string, options?: ButtonOptions): Button {
|
|
270
|
+
return new Button(id, options);
|
|
136
271
|
}
|
package/lib/components/card.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Action configuration for card actions
|
|
5
|
-
*/
|
|
6
|
-
export interface CardAction {
|
|
7
|
-
label: string;
|
|
8
|
-
onClick: () => void;
|
|
9
|
-
variant?: 'primary' | 'secondary' | 'danger';
|
|
10
|
-
}
|
|
1
|
+
import { getOrCreateContainer } from './helpers.js';
|
|
11
2
|
|
|
12
3
|
/**
|
|
13
4
|
* Card component options
|
|
@@ -17,8 +8,9 @@ export interface CardOptions {
|
|
|
17
8
|
subtitle?: string;
|
|
18
9
|
content?: string;
|
|
19
10
|
image?: string;
|
|
20
|
-
actions?: CardAction[];
|
|
21
11
|
variant?: 'default' | 'elevated' | 'outlined';
|
|
12
|
+
style?: string;
|
|
13
|
+
class?: string;
|
|
22
14
|
}
|
|
23
15
|
|
|
24
16
|
/**
|
|
@@ -29,8 +21,10 @@ type CardState = {
|
|
|
29
21
|
subtitle: string;
|
|
30
22
|
content: string;
|
|
31
23
|
image: string;
|
|
32
|
-
actions: CardAction[];
|
|
33
24
|
variant: string;
|
|
25
|
+
style: string;
|
|
26
|
+
class: string;
|
|
27
|
+
hasActions: boolean;
|
|
34
28
|
};
|
|
35
29
|
|
|
36
30
|
/**
|
|
@@ -42,23 +36,31 @@ type CardState = {
|
|
|
42
36
|
* content: 'Card content here'
|
|
43
37
|
* });
|
|
44
38
|
* card.render();
|
|
39
|
+
*
|
|
40
|
+
* // Add actions (any components)
|
|
41
|
+
* jux.button('view-btn').label('View').render('#myCard-actions');
|
|
42
|
+
* jux.button('delete-btn').label('Delete').render('#myCard-actions');
|
|
45
43
|
*/
|
|
46
|
-
export class Card
|
|
47
|
-
state
|
|
44
|
+
export class Card {
|
|
45
|
+
state: CardState;
|
|
48
46
|
container: HTMLElement | null = null;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.
|
|
47
|
+
_id: string;
|
|
48
|
+
id: string;
|
|
49
|
+
|
|
50
|
+
constructor(id: string, options: CardOptions = {}) {
|
|
51
|
+
this._id = id;
|
|
52
|
+
this.id = id;
|
|
53
|
+
|
|
54
|
+
this.state = {
|
|
55
55
|
title: options.title ?? '',
|
|
56
56
|
subtitle: options.subtitle ?? '',
|
|
57
57
|
content: options.content ?? '',
|
|
58
58
|
image: options.image ?? '',
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
variant: options.variant ?? 'default',
|
|
60
|
+
style: options.style ?? '',
|
|
61
|
+
class: options.class ?? '',
|
|
62
|
+
hasActions: false
|
|
63
|
+
};
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
/* -------------------------
|
|
@@ -85,13 +87,27 @@ export class Card extends Reactive {
|
|
|
85
87
|
return this;
|
|
86
88
|
}
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
this.state.
|
|
90
|
+
variant(value: string): this {
|
|
91
|
+
this.state.variant = value;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
style(value: string): this {
|
|
96
|
+
this.state.style = value;
|
|
90
97
|
return this;
|
|
91
98
|
}
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
this.state.
|
|
100
|
+
class(value: string): this {
|
|
101
|
+
this.state.class = value;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Enable actions region
|
|
107
|
+
* Creates an empty actions container that components can render into
|
|
108
|
+
*/
|
|
109
|
+
withActions(): this {
|
|
110
|
+
this.state.hasActions = true;
|
|
95
111
|
return this;
|
|
96
112
|
}
|
|
97
113
|
|
|
@@ -101,7 +117,7 @@ export class Card extends Reactive {
|
|
|
101
117
|
|
|
102
118
|
render(targetId?: string): this {
|
|
103
119
|
let container: HTMLElement;
|
|
104
|
-
|
|
120
|
+
|
|
105
121
|
if (targetId) {
|
|
106
122
|
const target = document.querySelector(targetId);
|
|
107
123
|
if (!target || !(target instanceof HTMLElement)) {
|
|
@@ -109,17 +125,25 @@ export class Card extends Reactive {
|
|
|
109
125
|
}
|
|
110
126
|
container = target;
|
|
111
127
|
} else {
|
|
112
|
-
container = getOrCreateContainer(this.
|
|
128
|
+
container = getOrCreateContainer(this._id);
|
|
113
129
|
}
|
|
114
|
-
|
|
130
|
+
|
|
115
131
|
this.container = container;
|
|
116
|
-
const { title, subtitle, content, image,
|
|
117
|
-
|
|
132
|
+
const { title, subtitle, content, image, variant, style, class: className, hasActions } = this.state;
|
|
133
|
+
|
|
118
134
|
// Create card element
|
|
119
135
|
const card = document.createElement('div');
|
|
120
136
|
card.className = `jux-card jux-card-${variant}`;
|
|
121
|
-
card.id = this.
|
|
122
|
-
|
|
137
|
+
card.id = this._id;
|
|
138
|
+
|
|
139
|
+
if (className) {
|
|
140
|
+
card.className += ` ${className}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (style) {
|
|
144
|
+
card.setAttribute('style', style);
|
|
145
|
+
}
|
|
146
|
+
|
|
123
147
|
// Image
|
|
124
148
|
if (image) {
|
|
125
149
|
const img = document.createElement('img');
|
|
@@ -128,11 +152,11 @@ export class Card extends Reactive {
|
|
|
128
152
|
img.alt = title || 'Card image';
|
|
129
153
|
card.appendChild(img);
|
|
130
154
|
}
|
|
131
|
-
|
|
155
|
+
|
|
132
156
|
// Content wrapper
|
|
133
157
|
const cardBody = document.createElement('div');
|
|
134
158
|
cardBody.className = 'jux-card-body';
|
|
135
|
-
|
|
159
|
+
|
|
136
160
|
// Title
|
|
137
161
|
if (title) {
|
|
138
162
|
const titleEl = document.createElement('h3');
|
|
@@ -140,7 +164,7 @@ export class Card extends Reactive {
|
|
|
140
164
|
titleEl.textContent = title;
|
|
141
165
|
cardBody.appendChild(titleEl);
|
|
142
166
|
}
|
|
143
|
-
|
|
167
|
+
|
|
144
168
|
// Subtitle
|
|
145
169
|
if (subtitle) {
|
|
146
170
|
const subtitleEl = document.createElement('p');
|
|
@@ -148,7 +172,7 @@ export class Card extends Reactive {
|
|
|
148
172
|
subtitleEl.textContent = subtitle;
|
|
149
173
|
cardBody.appendChild(subtitleEl);
|
|
150
174
|
}
|
|
151
|
-
|
|
175
|
+
|
|
152
176
|
// Content
|
|
153
177
|
if (content) {
|
|
154
178
|
const contentEl = document.createElement('p');
|
|
@@ -156,27 +180,17 @@ export class Card extends Reactive {
|
|
|
156
180
|
contentEl.textContent = content;
|
|
157
181
|
cardBody.appendChild(contentEl);
|
|
158
182
|
}
|
|
159
|
-
|
|
183
|
+
|
|
160
184
|
card.appendChild(cardBody);
|
|
161
|
-
|
|
162
|
-
// Actions
|
|
163
|
-
if (
|
|
185
|
+
|
|
186
|
+
// Actions region (empty container for user to populate)
|
|
187
|
+
if (hasActions) {
|
|
164
188
|
const actionsEl = document.createElement('div');
|
|
165
189
|
actionsEl.className = 'jux-card-actions';
|
|
166
|
-
|
|
167
|
-
actions.forEach(action => {
|
|
168
|
-
const btn = document.createElement('button');
|
|
169
|
-
btn.className = `jux-button jux-button-${action.variant || 'primary'}`;
|
|
170
|
-
btn.textContent = action.label;
|
|
171
|
-
actionsEl.appendChild(btn);
|
|
172
|
-
|
|
173
|
-
// Event binding
|
|
174
|
-
btn.addEventListener('click', action.onClick);
|
|
175
|
-
});
|
|
176
|
-
|
|
190
|
+
actionsEl.id = `${this._id}-actions`;
|
|
177
191
|
card.appendChild(actionsEl);
|
|
178
192
|
}
|
|
179
|
-
|
|
193
|
+
|
|
180
194
|
container.appendChild(card);
|
|
181
195
|
return this;
|
|
182
196
|
}
|
|
@@ -188,18 +202,18 @@ export class Card extends Reactive {
|
|
|
188
202
|
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
189
203
|
throw new Error('Card.renderTo: Invalid component - not an object');
|
|
190
204
|
}
|
|
191
|
-
|
|
192
|
-
if (!juxComponent.
|
|
193
|
-
throw new Error('Card.renderTo: Invalid component - missing
|
|
205
|
+
|
|
206
|
+
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
207
|
+
throw new Error('Card.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
194
208
|
}
|
|
195
|
-
|
|
196
|
-
return this.render(`#${juxComponent.
|
|
209
|
+
|
|
210
|
+
return this.render(`#${juxComponent._id}`);
|
|
197
211
|
}
|
|
198
212
|
}
|
|
199
213
|
|
|
200
214
|
/**
|
|
201
215
|
* Factory helper
|
|
202
216
|
*/
|
|
203
|
-
export function card(
|
|
204
|
-
return new Card(
|
|
217
|
+
export function card(id: string, options: CardOptions = {}): Card {
|
|
218
|
+
return new Card(id, options);
|
|
205
219
|
}
|
package/lib/components/chart.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getOrCreateContainer } from './helpers.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Chart component options
|
|
@@ -31,19 +31,21 @@ type ChartState = {
|
|
|
31
31
|
* });
|
|
32
32
|
* chart.render();
|
|
33
33
|
*/
|
|
34
|
-
export class Chart
|
|
35
|
-
state
|
|
34
|
+
export class Chart {
|
|
35
|
+
state: ChartState;
|
|
36
36
|
container: HTMLElement | null = null;
|
|
37
|
+
_id: string;
|
|
38
|
+
id: string;
|
|
37
39
|
|
|
38
|
-
constructor(
|
|
39
|
-
|
|
40
|
-
this.
|
|
40
|
+
constructor(id: string, options: ChartOptions = {}) {
|
|
41
|
+
this._id = id;
|
|
42
|
+
this.id = id;
|
|
41
43
|
|
|
42
|
-
this.state =
|
|
44
|
+
this.state = {
|
|
43
45
|
type: options.type ?? 'bar',
|
|
44
46
|
data: options.data ?? {},
|
|
45
47
|
options: options.options ?? {}
|
|
46
|
-
}
|
|
48
|
+
};
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/* -------------------------
|
|
@@ -79,14 +81,14 @@ export class Chart extends Reactive {
|
|
|
79
81
|
}
|
|
80
82
|
container = target;
|
|
81
83
|
} else {
|
|
82
|
-
container = getOrCreateContainer(this.
|
|
84
|
+
container = getOrCreateContainer(this._id);
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
this.container = container;
|
|
86
88
|
const { type, data } = this.state;
|
|
87
89
|
|
|
88
90
|
const canvas = document.createElement('canvas');
|
|
89
|
-
canvas.id = this.
|
|
91
|
+
canvas.id = this._id;
|
|
90
92
|
canvas.className = 'jux-chart';
|
|
91
93
|
|
|
92
94
|
// Placeholder rendering (would integrate with Chart.js or similar)
|
|
@@ -109,17 +111,17 @@ export class Chart extends Reactive {
|
|
|
109
111
|
throw new Error('Chart.renderTo: Invalid component - not an object');
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
if (!juxComponent.
|
|
113
|
-
throw new Error('Chart.renderTo: Invalid component - missing
|
|
114
|
+
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
115
|
+
throw new Error('Chart.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
114
116
|
}
|
|
115
117
|
|
|
116
|
-
return this.render(`#${juxComponent.
|
|
118
|
+
return this.render(`#${juxComponent._id}`);
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
/**
|
|
121
123
|
* Factory helper
|
|
122
124
|
*/
|
|
123
|
-
export function chart(
|
|
124
|
-
return new Chart(
|
|
125
|
+
export function chart(id: string, options: ChartOptions = {}): Chart {
|
|
126
|
+
return new Chart(id, options);
|
|
125
127
|
}
|