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/switch.ts
CHANGED
|
@@ -1,63 +1,48 @@
|
|
|
1
1
|
import { getOrCreateContainer } from './helpers.js';
|
|
2
2
|
import { State } from '../reactivity/state.js';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Switch component options
|
|
6
|
-
*/
|
|
7
4
|
export interface SwitchOptions {
|
|
8
|
-
label?: string;
|
|
9
5
|
checked?: boolean;
|
|
10
|
-
|
|
6
|
+
label?: string;
|
|
11
7
|
name?: string;
|
|
12
|
-
|
|
8
|
+
disabled?: boolean;
|
|
13
9
|
style?: string;
|
|
14
10
|
class?: string;
|
|
15
11
|
}
|
|
16
12
|
|
|
17
|
-
/**
|
|
18
|
-
* Switch component state
|
|
19
|
-
*/
|
|
20
13
|
type SwitchState = {
|
|
21
|
-
label: string;
|
|
22
14
|
checked: boolean;
|
|
23
|
-
|
|
15
|
+
label: string;
|
|
24
16
|
name: string;
|
|
17
|
+
disabled: boolean;
|
|
25
18
|
style: string;
|
|
26
19
|
class: string;
|
|
27
20
|
};
|
|
28
21
|
|
|
29
|
-
/**
|
|
30
|
-
* Switch component - Toggle switch for boolean values
|
|
31
|
-
*
|
|
32
|
-
* Usage:
|
|
33
|
-
* jux.switch('notifications', {
|
|
34
|
-
* label: 'Enable notifications',
|
|
35
|
-
* checked: true,
|
|
36
|
-
* onChange: (checked) => console.log(checked)
|
|
37
|
-
* }).render('#settings');
|
|
38
|
-
*
|
|
39
|
-
* // Two-way binding
|
|
40
|
-
* const notificationsState = state(true);
|
|
41
|
-
* jux.switch('notifications').label('Notifications').bind(notificationsState).render('#settings');
|
|
42
|
-
*/
|
|
43
22
|
export class Switch {
|
|
44
23
|
state: SwitchState;
|
|
45
24
|
container: HTMLElement | null = null;
|
|
46
25
|
_id: string;
|
|
47
26
|
id: string;
|
|
48
|
-
|
|
49
|
-
|
|
27
|
+
|
|
28
|
+
// CRITICAL: Store bind/sync instructions for deferred wiring
|
|
29
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
30
|
+
private _syncBindings: Array<{
|
|
31
|
+
property: string,
|
|
32
|
+
stateObj: State<any>,
|
|
33
|
+
toState?: Function,
|
|
34
|
+
toComponent?: Function
|
|
35
|
+
}> = [];
|
|
50
36
|
|
|
51
37
|
constructor(id: string, options: SwitchOptions = {}) {
|
|
52
38
|
this._id = id;
|
|
53
39
|
this.id = id;
|
|
54
|
-
this._onChange = options.onChange;
|
|
55
40
|
|
|
56
41
|
this.state = {
|
|
57
|
-
label: options.label ?? '',
|
|
58
42
|
checked: options.checked ?? false,
|
|
59
|
-
|
|
43
|
+
label: options.label ?? '',
|
|
60
44
|
name: options.name ?? id,
|
|
45
|
+
disabled: options.disabled ?? false,
|
|
61
46
|
style: options.style ?? '',
|
|
62
47
|
class: options.class ?? ''
|
|
63
48
|
};
|
|
@@ -67,20 +52,14 @@ export class Switch {
|
|
|
67
52
|
* Fluent API
|
|
68
53
|
* ------------------------- */
|
|
69
54
|
|
|
70
|
-
label(value: string): this {
|
|
71
|
-
this.state.label = value;
|
|
72
|
-
return this;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
55
|
checked(value: boolean): this {
|
|
76
56
|
this.state.checked = value;
|
|
77
57
|
this._updateElement();
|
|
78
58
|
return this;
|
|
79
59
|
}
|
|
80
60
|
|
|
81
|
-
|
|
82
|
-
this.state.
|
|
83
|
-
this._updateElement();
|
|
61
|
+
label(value: string): this {
|
|
62
|
+
this.state.label = value;
|
|
84
63
|
return this;
|
|
85
64
|
}
|
|
86
65
|
|
|
@@ -89,6 +68,12 @@ export class Switch {
|
|
|
89
68
|
return this;
|
|
90
69
|
}
|
|
91
70
|
|
|
71
|
+
disabled(value: boolean): this {
|
|
72
|
+
this.state.disabled = value;
|
|
73
|
+
this._updateElement();
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
92
77
|
style(value: string): this {
|
|
93
78
|
this.state.style = value;
|
|
94
79
|
return this;
|
|
@@ -99,26 +84,28 @@ export class Switch {
|
|
|
99
84
|
return this;
|
|
100
85
|
}
|
|
101
86
|
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Bind event handler (stores for wiring in render)
|
|
89
|
+
* DOM events only: change, click, etc.
|
|
90
|
+
*/
|
|
91
|
+
bind(event: string, handler: Function): this {
|
|
92
|
+
this._bindings.push({ event, handler });
|
|
104
93
|
return this;
|
|
105
94
|
}
|
|
106
95
|
|
|
107
96
|
/**
|
|
108
|
-
* Two-way
|
|
97
|
+
* Two-way sync with state (stores for wiring in render)
|
|
98
|
+
*
|
|
99
|
+
* @param property - Component property to sync ('checked', 'label', 'disabled')
|
|
100
|
+
* @param stateObj - State object to sync with
|
|
101
|
+
* @param toState - Optional transform function when going from component to state
|
|
102
|
+
* @param toComponent - Optional transform function when going from state to component
|
|
109
103
|
*/
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.state.checked = val;
|
|
116
|
-
this._updateElement();
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Update state when switch changes
|
|
120
|
-
this.onChange((checked) => stateObj.set(checked));
|
|
121
|
-
|
|
104
|
+
sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
|
|
105
|
+
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
106
|
+
throw new Error(`Switch.sync: Expected a State object for property "${property}"`);
|
|
107
|
+
}
|
|
108
|
+
this._syncBindings.push({ property, stateObj, toState, toComponent });
|
|
122
109
|
return this;
|
|
123
110
|
}
|
|
124
111
|
|
|
@@ -128,69 +115,44 @@ export class Switch {
|
|
|
128
115
|
|
|
129
116
|
private _updateElement(): void {
|
|
130
117
|
const input = document.getElementById(`${this._id}-input`) as HTMLInputElement;
|
|
131
|
-
const track = document.getElementById(`${this._id}-track`);
|
|
132
|
-
|
|
133
118
|
if (input) {
|
|
134
119
|
input.checked = this.state.checked;
|
|
135
120
|
input.disabled = this.state.disabled;
|
|
136
121
|
}
|
|
137
|
-
|
|
138
|
-
if (track) {
|
|
139
|
-
if (this.state.checked) {
|
|
140
|
-
track.classList.add('jux-switch-track-checked');
|
|
141
|
-
} else {
|
|
142
|
-
track.classList.remove('jux-switch-track-checked');
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Toggle the switch
|
|
149
|
-
*/
|
|
150
|
-
toggle(): this {
|
|
151
|
-
this.state.checked = !this.state.checked;
|
|
152
|
-
this._updateElement();
|
|
153
|
-
if (this._onChange) {
|
|
154
|
-
this._onChange(this.state.checked);
|
|
155
|
-
}
|
|
156
|
-
return this;
|
|
157
122
|
}
|
|
158
123
|
|
|
159
|
-
|
|
124
|
+
getValue(): boolean {
|
|
160
125
|
return this.state.checked;
|
|
161
126
|
}
|
|
162
127
|
|
|
163
128
|
/* -------------------------
|
|
164
|
-
* Render
|
|
129
|
+
* Render (5-Step Pattern)
|
|
165
130
|
* ------------------------- */
|
|
166
131
|
|
|
167
132
|
render(targetId?: string): this {
|
|
133
|
+
// === 1. SETUP: Get or create container ===
|
|
168
134
|
let container: HTMLElement;
|
|
169
|
-
|
|
170
135
|
if (targetId) {
|
|
171
136
|
const target = document.querySelector(targetId);
|
|
172
137
|
if (!target || !(target instanceof HTMLElement)) {
|
|
173
|
-
throw new Error(`Switch: Target
|
|
138
|
+
throw new Error(`Switch: Target "${targetId}" not found`);
|
|
174
139
|
}
|
|
175
140
|
container = target;
|
|
176
141
|
} else {
|
|
177
142
|
container = getOrCreateContainer(this._id);
|
|
178
143
|
}
|
|
179
|
-
|
|
180
144
|
this.container = container;
|
|
181
|
-
const { label, checked, disabled, name, style, class: className } = this.state;
|
|
182
145
|
|
|
146
|
+
// === 2. PREPARE: Destructure state and check sync flags ===
|
|
147
|
+
const { checked, label, disabled, name, style, class: className } = this.state;
|
|
148
|
+
const hasCheckedSync = this._syncBindings.some(b => b.property === 'checked');
|
|
149
|
+
|
|
150
|
+
// === 3. BUILD: Create DOM elements ===
|
|
183
151
|
const wrapper = document.createElement('div');
|
|
184
152
|
wrapper.className = 'jux-switch';
|
|
185
153
|
wrapper.id = this._id;
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
wrapper.className += ` ${className}`;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (style) {
|
|
192
|
-
wrapper.setAttribute('style', style);
|
|
193
|
-
}
|
|
154
|
+
if (className) wrapper.className += ` ${className}`;
|
|
155
|
+
if (style) wrapper.setAttribute('style', style);
|
|
194
156
|
|
|
195
157
|
const labelEl = document.createElement('label');
|
|
196
158
|
labelEl.className = 'jux-switch-label';
|
|
@@ -202,48 +164,84 @@ export class Switch {
|
|
|
202
164
|
input.name = name;
|
|
203
165
|
input.checked = checked;
|
|
204
166
|
input.disabled = disabled;
|
|
205
|
-
input.setAttribute('role', 'switch');
|
|
206
|
-
|
|
207
|
-
input.addEventListener('change', (e) => {
|
|
208
|
-
const target = e.target as HTMLInputElement;
|
|
209
|
-
this.state.checked = target.checked;
|
|
210
|
-
|
|
211
|
-
const track = document.getElementById(`${this._id}-track`);
|
|
212
|
-
if (track) {
|
|
213
|
-
if (target.checked) {
|
|
214
|
-
track.classList.add('jux-switch-track-checked');
|
|
215
|
-
} else {
|
|
216
|
-
track.classList.remove('jux-switch-track-checked');
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
167
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
const track = document.createElement('span');
|
|
226
|
-
track.className = 'jux-switch-track';
|
|
227
|
-
track.id = `${this._id}-track`;
|
|
228
|
-
if (checked) {
|
|
229
|
-
track.classList.add('jux-switch-track-checked');
|
|
230
|
-
}
|
|
168
|
+
const slider = document.createElement('span');
|
|
169
|
+
slider.className = 'jux-switch-slider';
|
|
231
170
|
|
|
232
|
-
const
|
|
233
|
-
|
|
171
|
+
const text = document.createElement('span');
|
|
172
|
+
text.className = 'jux-switch-text';
|
|
173
|
+
text.textContent = label;
|
|
234
174
|
|
|
235
|
-
track.appendChild(thumb);
|
|
236
175
|
labelEl.appendChild(input);
|
|
237
|
-
labelEl.appendChild(
|
|
176
|
+
labelEl.appendChild(slider);
|
|
177
|
+
if (label) labelEl.appendChild(text);
|
|
178
|
+
wrapper.appendChild(labelEl);
|
|
179
|
+
|
|
180
|
+
// === 4. WIRE: Attach event listeners and sync bindings ===
|
|
238
181
|
|
|
239
|
-
if
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
182
|
+
// Default behavior (only if NOT using sync)
|
|
183
|
+
if (!hasCheckedSync) {
|
|
184
|
+
input.addEventListener('change', () => {
|
|
185
|
+
this.state.checked = input.checked;
|
|
186
|
+
});
|
|
244
187
|
}
|
|
245
188
|
|
|
246
|
-
|
|
189
|
+
// Wire custom bindings from .bind() calls
|
|
190
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
191
|
+
wrapper.addEventListener(event, handler as EventListener);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Wire sync bindings from .sync() calls
|
|
195
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
196
|
+
if (property === 'checked') {
|
|
197
|
+
const transformToState = toState || ((v: any) => Boolean(v));
|
|
198
|
+
const transformToComponent = toComponent || ((v: any) => Boolean(v));
|
|
199
|
+
|
|
200
|
+
let isUpdating = false;
|
|
201
|
+
|
|
202
|
+
// State → Component
|
|
203
|
+
stateObj.subscribe((val: any) => {
|
|
204
|
+
if (isUpdating) return;
|
|
205
|
+
const transformed = transformToComponent(val);
|
|
206
|
+
if (input.checked !== transformed) {
|
|
207
|
+
input.checked = transformed;
|
|
208
|
+
this.state.checked = transformed;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Component → State
|
|
213
|
+
input.addEventListener('change', () => {
|
|
214
|
+
if (isUpdating) return;
|
|
215
|
+
isUpdating = true;
|
|
216
|
+
|
|
217
|
+
const transformed = transformToState(input.checked);
|
|
218
|
+
this.state.checked = input.checked;
|
|
219
|
+
stateObj.set(transformed);
|
|
220
|
+
|
|
221
|
+
setTimeout(() => { isUpdating = false; }, 0);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
else if (property === 'label') {
|
|
225
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
226
|
+
|
|
227
|
+
stateObj.subscribe((val: any) => {
|
|
228
|
+
const transformed = transformToComponent(val);
|
|
229
|
+
text.textContent = transformed;
|
|
230
|
+
this.state.label = transformed;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
else if (property === 'disabled') {
|
|
234
|
+
const transformToComponent = toComponent || ((v: any) => Boolean(v));
|
|
235
|
+
|
|
236
|
+
stateObj.subscribe((val: any) => {
|
|
237
|
+
const transformed = transformToComponent(val);
|
|
238
|
+
input.disabled = transformed;
|
|
239
|
+
this.state.disabled = transformed;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// === 5. RENDER: Append to DOM and finalize ===
|
|
247
245
|
container.appendChild(wrapper);
|
|
248
246
|
return this;
|
|
249
247
|
}
|
|
@@ -258,4 +256,5 @@ export class Switch {
|
|
|
258
256
|
|
|
259
257
|
export function switchComponent(id: string, options: SwitchOptions = {}): Switch {
|
|
260
258
|
return new Switch(id, options);
|
|
261
|
-
}
|
|
259
|
+
}
|
|
260
|
+
|