juxscript 1.0.18 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/alert.ts +124 -128
- package/lib/components/areachart.ts +169 -287
- package/lib/components/areachartsmooth.ts +2 -2
- package/lib/components/badge.ts +63 -72
- package/lib/components/barchart.ts +120 -48
- package/lib/components/button.ts +99 -101
- package/lib/components/card.ts +97 -121
- package/lib/components/chart-types.ts +159 -0
- package/lib/components/chart-utils.ts +160 -0
- package/lib/components/chart.ts +628 -48
- package/lib/components/checkbox.ts +137 -51
- package/lib/components/code.ts +89 -75
- package/lib/components/container.ts +1 -1
- package/lib/components/datepicker.ts +93 -78
- package/lib/components/dialog.ts +163 -130
- package/lib/components/divider.ts +111 -193
- package/lib/components/docs-data.json +711 -264
- package/lib/components/doughnutchart.ts +125 -57
- package/lib/components/dropdown.ts +172 -85
- package/lib/components/element.ts +66 -61
- package/lib/components/fileupload.ts +142 -171
- package/lib/components/heading.ts +64 -21
- package/lib/components/hero.ts +109 -34
- package/lib/components/icon.ts +247 -0
- package/lib/components/icons.ts +174 -0
- package/lib/components/include.ts +77 -2
- package/lib/components/input.ts +174 -125
- package/lib/components/list.ts +120 -79
- package/lib/components/menu.ts +97 -2
- package/lib/components/modal.ts +144 -63
- package/lib/components/nav.ts +153 -52
- package/lib/components/paragraph.ts +78 -28
- package/lib/components/progress.ts +83 -107
- package/lib/components/radio.ts +151 -52
- package/lib/components/select.ts +110 -102
- package/lib/components/sidebar.ts +148 -105
- package/lib/components/switch.ts +124 -125
- package/lib/components/table.ts +214 -137
- package/lib/components/tabs.ts +194 -113
- package/lib/components/theme-toggle.ts +38 -7
- package/lib/components/tooltip.ts +207 -47
- package/lib/jux.ts +24 -5
- package/lib/reactivity/state.ts +13 -299
- package/package.json +1 -2
package/lib/components/alert.ts
CHANGED
|
@@ -1,94 +1,63 @@
|
|
|
1
1
|
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
+
import { State } from '../reactivity/state.js';
|
|
3
|
+
import { renderIcon } from './icons.js';
|
|
2
4
|
|
|
3
|
-
/**
|
|
4
|
-
* Alert component options
|
|
5
|
-
*/
|
|
6
5
|
export interface AlertOptions {
|
|
7
6
|
message?: string;
|
|
8
|
-
|
|
9
|
-
title?: string;
|
|
7
|
+
type?: 'info' | 'success' | 'warning' | 'error';
|
|
10
8
|
dismissible?: boolean;
|
|
11
|
-
onDismiss?: () => void;
|
|
12
9
|
icon?: string;
|
|
13
10
|
style?: string;
|
|
14
11
|
class?: string;
|
|
15
12
|
}
|
|
16
13
|
|
|
17
|
-
/**
|
|
18
|
-
* Alert component state
|
|
19
|
-
*/
|
|
20
14
|
type AlertState = {
|
|
21
15
|
message: string;
|
|
22
|
-
|
|
23
|
-
title: string;
|
|
16
|
+
type: string;
|
|
24
17
|
dismissible: boolean;
|
|
25
18
|
icon: string;
|
|
19
|
+
visible: boolean;
|
|
26
20
|
style: string;
|
|
27
21
|
class: string;
|
|
28
22
|
};
|
|
29
23
|
|
|
30
|
-
/**
|
|
31
|
-
* Alert component - Status messages and notifications
|
|
32
|
-
*
|
|
33
|
-
* Usage:
|
|
34
|
-
* jux.alert('my-alert', {
|
|
35
|
-
* variant: 'success',
|
|
36
|
-
* title: 'Success!',
|
|
37
|
-
* message: 'Your changes have been saved.',
|
|
38
|
-
* dismissible: true
|
|
39
|
-
* }).render('#app');
|
|
40
|
-
*
|
|
41
|
-
* Variants: info, success, warning, error
|
|
42
|
-
*/
|
|
43
24
|
export class Alert {
|
|
44
25
|
state: AlertState;
|
|
45
26
|
container: HTMLElement | null = null;
|
|
46
27
|
_id: string;
|
|
47
28
|
id: string;
|
|
48
|
-
|
|
29
|
+
|
|
30
|
+
// CRITICAL: Store bind/sync instructions for deferred wiring
|
|
31
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
32
|
+
private _syncBindings: Array<{
|
|
33
|
+
property: string,
|
|
34
|
+
stateObj: State<any>,
|
|
35
|
+
toState?: Function,
|
|
36
|
+
toComponent?: Function
|
|
37
|
+
}> = [];
|
|
49
38
|
|
|
50
39
|
constructor(id: string, options: AlertOptions = {}) {
|
|
51
40
|
this._id = id;
|
|
52
41
|
this.id = id;
|
|
53
|
-
this._onDismiss = options.onDismiss;
|
|
54
|
-
|
|
55
|
-
// Default icons per variant
|
|
56
|
-
const defaultIcons = {
|
|
57
|
-
info: 'ℹ️',
|
|
58
|
-
success: '✅',
|
|
59
|
-
warning: '⚠️',
|
|
60
|
-
error: '❌'
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const variant = options.variant ?? 'info';
|
|
64
42
|
|
|
65
43
|
this.state = {
|
|
66
44
|
message: options.message ?? '',
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
45
|
+
type: options.type ?? 'info',
|
|
46
|
+
dismissible: options.dismissible ?? true,
|
|
47
|
+
icon: options.icon ?? '',
|
|
48
|
+
visible: true,
|
|
71
49
|
style: options.style ?? '',
|
|
72
50
|
class: options.class ?? ''
|
|
73
51
|
};
|
|
74
52
|
}
|
|
75
53
|
|
|
76
|
-
/* -------------------------
|
|
77
|
-
* Fluent API
|
|
78
|
-
* ------------------------- */
|
|
79
|
-
|
|
80
54
|
message(value: string): this {
|
|
81
55
|
this.state.message = value;
|
|
82
56
|
return this;
|
|
83
57
|
}
|
|
84
58
|
|
|
85
|
-
|
|
86
|
-
this.state.
|
|
87
|
-
return this;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
title(value: string): this {
|
|
91
|
-
this.state.title = value;
|
|
59
|
+
type(value: 'info' | 'success' | 'warning' | 'error'): this {
|
|
60
|
+
this.state.type = value;
|
|
92
61
|
return this;
|
|
93
62
|
}
|
|
94
63
|
|
|
@@ -112,129 +81,156 @@ export class Alert {
|
|
|
112
81
|
return this;
|
|
113
82
|
}
|
|
114
83
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const element = document.getElementById(this._id);
|
|
124
|
-
if (element) {
|
|
125
|
-
element.style.opacity = '0';
|
|
126
|
-
element.style.transform = 'translateY(-10px)';
|
|
127
|
-
setTimeout(() => {
|
|
128
|
-
element.remove();
|
|
129
|
-
if (this._onDismiss) {
|
|
130
|
-
this._onDismiss();
|
|
131
|
-
}
|
|
132
|
-
}, 200);
|
|
84
|
+
bind(event: string, handler: Function): this {
|
|
85
|
+
this._bindings.push({ event, handler });
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
|
|
90
|
+
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
91
|
+
throw new Error(`Alert.sync: Expected a State object for property "${property}"`);
|
|
133
92
|
}
|
|
93
|
+
this._syncBindings.push({ property, stateObj, toState, toComponent });
|
|
94
|
+
return this;
|
|
134
95
|
}
|
|
135
96
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
97
|
+
show(): this {
|
|
98
|
+
this.state.visible = true;
|
|
99
|
+
const alertEl = document.getElementById(this._id);
|
|
100
|
+
if (alertEl) {
|
|
101
|
+
alertEl.style.display = 'flex';
|
|
102
|
+
}
|
|
141
103
|
return this;
|
|
142
104
|
}
|
|
143
105
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
106
|
+
hide(): this {
|
|
107
|
+
this.state.visible = false;
|
|
108
|
+
const alertEl = document.getElementById(this._id);
|
|
109
|
+
if (alertEl) {
|
|
110
|
+
alertEl.style.display = 'none';
|
|
111
|
+
}
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
147
114
|
|
|
148
115
|
render(targetId?: string): this {
|
|
116
|
+
// === 1. SETUP: Get or create container ===
|
|
149
117
|
let container: HTMLElement;
|
|
150
|
-
|
|
151
118
|
if (targetId) {
|
|
152
119
|
const target = document.querySelector(targetId);
|
|
153
120
|
if (!target || !(target instanceof HTMLElement)) {
|
|
154
|
-
throw new Error(`Alert: Target
|
|
121
|
+
throw new Error(`Alert: Target "${targetId}" not found`);
|
|
155
122
|
}
|
|
156
123
|
container = target;
|
|
157
124
|
} else {
|
|
158
125
|
container = getOrCreateContainer(this._id);
|
|
159
126
|
}
|
|
160
|
-
|
|
161
127
|
this.container = container;
|
|
162
|
-
const { message, variant, title, dismissible, icon, style, class: className } = this.state;
|
|
163
128
|
|
|
129
|
+
// === 2. PREPARE: Destructure state and check sync flags ===
|
|
130
|
+
const { message, type, dismissible, icon, style, class: className } = this.state;
|
|
131
|
+
const hasVisibleSync = this._syncBindings.some(b => b.property === 'visible');
|
|
132
|
+
|
|
133
|
+
// === 3. BUILD: Create DOM elements ===
|
|
164
134
|
const alert = document.createElement('div');
|
|
165
|
-
alert.className = `jux-alert jux-alert-${
|
|
135
|
+
alert.className = `jux-alert jux-alert-${type}`;
|
|
166
136
|
alert.id = this._id;
|
|
167
|
-
alert.
|
|
137
|
+
if (className) alert.className += ` ${className}`;
|
|
138
|
+
if (style) alert.setAttribute('style', style);
|
|
168
139
|
|
|
169
|
-
if (className) {
|
|
170
|
-
alert.className += ` ${className}`;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (style) {
|
|
174
|
-
alert.setAttribute('style', style);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Icon
|
|
178
140
|
if (icon) {
|
|
179
141
|
const iconEl = document.createElement('span');
|
|
180
142
|
iconEl.className = 'jux-alert-icon';
|
|
181
|
-
iconEl.
|
|
143
|
+
iconEl.appendChild(renderIcon(icon));
|
|
182
144
|
alert.appendChild(iconEl);
|
|
183
145
|
}
|
|
184
146
|
|
|
185
|
-
// Content
|
|
186
147
|
const content = document.createElement('div');
|
|
187
148
|
content.className = 'jux-alert-content';
|
|
149
|
+
content.textContent = message;
|
|
150
|
+
alert.appendChild(content);
|
|
188
151
|
|
|
189
|
-
if (
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (message) {
|
|
197
|
-
const messageEl = document.createElement('div');
|
|
198
|
-
messageEl.className = 'jux-alert-message';
|
|
199
|
-
messageEl.textContent = message;
|
|
200
|
-
content.appendChild(messageEl);
|
|
152
|
+
if (dismissible) {
|
|
153
|
+
const closeBtn = document.createElement('button');
|
|
154
|
+
closeBtn.className = 'jux-alert-close';
|
|
155
|
+
closeBtn.innerHTML = '×';
|
|
156
|
+
alert.appendChild(closeBtn);
|
|
201
157
|
}
|
|
202
158
|
|
|
203
|
-
|
|
159
|
+
// === 4. WIRE: Attach event listeners and sync bindings ===
|
|
204
160
|
|
|
205
|
-
//
|
|
206
|
-
if (dismissible) {
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
dismissBtn.addEventListener('click', () => this.dismiss());
|
|
212
|
-
alert.appendChild(dismissBtn);
|
|
161
|
+
// Default dismiss behavior (only if NOT using sync)
|
|
162
|
+
if (!hasVisibleSync && dismissible) {
|
|
163
|
+
const closeBtn = alert.querySelector('.jux-alert-close');
|
|
164
|
+
closeBtn?.addEventListener('click', () => {
|
|
165
|
+
alert.remove();
|
|
166
|
+
});
|
|
213
167
|
}
|
|
214
168
|
|
|
169
|
+
// Wire custom bindings from .bind() calls
|
|
170
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
171
|
+
alert.addEventListener(event, handler as EventListener);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Wire sync bindings from .sync() calls
|
|
175
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
176
|
+
if (property === 'message') {
|
|
177
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
178
|
+
|
|
179
|
+
stateObj.subscribe((val: any) => {
|
|
180
|
+
const transformed = transformToComponent(val);
|
|
181
|
+
content.textContent = transformed;
|
|
182
|
+
this.state.message = transformed;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else if (property === 'visible') {
|
|
186
|
+
const transformToState = toState || ((v: any) => Boolean(v));
|
|
187
|
+
const transformToComponent = toComponent || ((v: any) => Boolean(v));
|
|
188
|
+
|
|
189
|
+
let isUpdating = false;
|
|
190
|
+
|
|
191
|
+
// State → Component
|
|
192
|
+
stateObj.subscribe((val: any) => {
|
|
193
|
+
if (isUpdating) return;
|
|
194
|
+
const transformed = transformToComponent(val);
|
|
195
|
+
alert.style.display = transformed ? 'flex' : 'none';
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Component → State (close button)
|
|
199
|
+
if (dismissible) {
|
|
200
|
+
const closeBtn = alert.querySelector('.jux-alert-close');
|
|
201
|
+
closeBtn?.addEventListener('click', () => {
|
|
202
|
+
if (isUpdating) return;
|
|
203
|
+
isUpdating = true;
|
|
204
|
+
|
|
205
|
+
alert.style.display = 'none';
|
|
206
|
+
stateObj.set(transformToState(false));
|
|
207
|
+
|
|
208
|
+
setTimeout(() => { isUpdating = false; }, 0);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// === 5. RENDER: Append to DOM and finalize ===
|
|
215
215
|
container.appendChild(alert);
|
|
216
|
+
|
|
217
|
+
requestAnimationFrame(() => {
|
|
218
|
+
if ((window as any).lucide) {
|
|
219
|
+
(window as any).lucide.createIcons();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
216
223
|
return this;
|
|
217
224
|
}
|
|
218
225
|
|
|
219
|
-
/**
|
|
220
|
-
* Render to another Jux component's container
|
|
221
|
-
*/
|
|
222
226
|
renderTo(juxComponent: any): this {
|
|
223
|
-
if (!juxComponent
|
|
224
|
-
throw new Error('Alert.renderTo: Invalid component
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
228
|
-
throw new Error('Alert.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
227
|
+
if (!juxComponent?._id) {
|
|
228
|
+
throw new Error('Alert.renderTo: Invalid component');
|
|
229
229
|
}
|
|
230
|
-
|
|
231
230
|
return this.render(`#${juxComponent._id}`);
|
|
232
231
|
}
|
|
233
232
|
}
|
|
234
233
|
|
|
235
|
-
/**
|
|
236
|
-
* Factory helper
|
|
237
|
-
*/
|
|
238
234
|
export function alert(id: string, options: AlertOptions = {}): Alert {
|
|
239
235
|
return new Alert(id, options);
|
|
240
236
|
}
|