juxscript 1.0.19 → 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 +92 -60
- 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 +697 -274
- 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 +105 -53
- 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 +54 -91
- 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/package.json +1 -2
|
@@ -21,21 +21,6 @@ type ParagraphState = {
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Paragraph component - semantic paragraph element
|
|
24
|
-
*
|
|
25
|
-
* Usage:
|
|
26
|
-
* jux.paragraph('intro', { text: 'Welcome to JUX' }).render('#app');
|
|
27
|
-
* jux.paragraph('description').text('A simple framework').render('#app');
|
|
28
|
-
*
|
|
29
|
-
* // With state binding
|
|
30
|
-
* jux.paragraph('counter')
|
|
31
|
-
* .text('Count: 0')
|
|
32
|
-
* .bind('text', count, (val) => `Count: ${val}`)
|
|
33
|
-
* .render('#app');
|
|
34
|
-
*
|
|
35
|
-
* // With sync (one-way for paragraph)
|
|
36
|
-
* jux.paragraph('display')
|
|
37
|
-
* .sync('text', count, (val) => `Count: ${val}`)
|
|
38
|
-
* .render('#app');
|
|
39
24
|
*/
|
|
40
25
|
export class Paragraph {
|
|
41
26
|
state: ParagraphState;
|
|
@@ -43,11 +28,14 @@ export class Paragraph {
|
|
|
43
28
|
_id: string;
|
|
44
29
|
id: string;
|
|
45
30
|
|
|
46
|
-
// Store bind
|
|
47
|
-
private _bindings: Array<{ event: string, handler: Function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
31
|
+
// CRITICAL: Store bind/sync instructions for deferred wiring
|
|
32
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
33
|
+
private _syncBindings: Array<{
|
|
34
|
+
property: string,
|
|
35
|
+
stateObj: State<any>,
|
|
36
|
+
toState?: Function,
|
|
37
|
+
toComponent?: Function
|
|
38
|
+
}> = [];
|
|
51
39
|
|
|
52
40
|
constructor(id: string, options: ParagraphOptions = {}) {
|
|
53
41
|
this._id = id;
|
|
@@ -80,36 +68,26 @@ export class Paragraph {
|
|
|
80
68
|
}
|
|
81
69
|
|
|
82
70
|
/**
|
|
83
|
-
* Bind event
|
|
71
|
+
* Bind event handler (stores for wiring in render)
|
|
72
|
+
* DOM events only: click, mouseenter, etc.
|
|
84
73
|
*/
|
|
85
|
-
bind(
|
|
86
|
-
|
|
87
|
-
// Event binding
|
|
88
|
-
this._bindings.push({ event: property, handler: source });
|
|
89
|
-
} else {
|
|
90
|
-
// Validate it's a State object
|
|
91
|
-
if (!source || typeof source.subscribe !== 'function') {
|
|
92
|
-
throw new Error(`Paragraph.bind: Expected a State object, got ${typeof source}. Did you pass 'state.value' instead of 'state'?`);
|
|
93
|
-
}
|
|
94
|
-
// State binding
|
|
95
|
-
this._bindings.push({ event: property, handler: () => { }, stateObj: source, transform });
|
|
96
|
-
}
|
|
74
|
+
bind(event: string, handler: Function): this {
|
|
75
|
+
this._bindings.push({ event, handler });
|
|
97
76
|
return this;
|
|
98
77
|
}
|
|
99
78
|
|
|
100
79
|
/**
|
|
101
|
-
* Sync with state (one-way
|
|
80
|
+
* Sync with state (one-way: State → Component)
|
|
102
81
|
*
|
|
103
82
|
* @param property - Component property to sync ('text', 'class', 'style')
|
|
104
83
|
* @param stateObj - State object to sync with
|
|
105
|
-
* @param transform - Optional transform function
|
|
84
|
+
* @param transform - Optional transform function from state to component
|
|
106
85
|
*/
|
|
107
|
-
sync(property: string, stateObj: State<any>,
|
|
108
|
-
// Validate it's a State object
|
|
86
|
+
sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
|
|
109
87
|
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
110
|
-
throw new Error(`Paragraph.sync: Expected a State object
|
|
88
|
+
throw new Error(`Paragraph.sync: Expected a State object for property "${property}"`);
|
|
111
89
|
}
|
|
112
|
-
this._syncBindings.push({ property, stateObj,
|
|
90
|
+
this._syncBindings.push({ property, stateObj, toState, toComponent });
|
|
113
91
|
return this;
|
|
114
92
|
}
|
|
115
93
|
|
|
@@ -117,77 +95,62 @@ export class Paragraph {
|
|
|
117
95
|
* Render
|
|
118
96
|
* ------------------------- */
|
|
119
97
|
|
|
120
|
-
render(targetId?: string
|
|
98
|
+
render(targetId?: string): this {
|
|
99
|
+
// === 1. SETUP: Get or create container ===
|
|
121
100
|
let container: HTMLElement;
|
|
122
|
-
|
|
123
101
|
if (targetId) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const target = document.querySelector(targetId);
|
|
128
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
129
|
-
throw new Error(`Paragraph: Target element "${targetId}" not found`);
|
|
130
|
-
}
|
|
131
|
-
container = target;
|
|
102
|
+
const target = document.querySelector(targetId);
|
|
103
|
+
if (!target || !(target instanceof HTMLElement)) {
|
|
104
|
+
throw new Error(`Paragraph: Target "${targetId}" not found`);
|
|
132
105
|
}
|
|
106
|
+
container = target;
|
|
133
107
|
} else {
|
|
134
108
|
container = getOrCreateContainer(this._id);
|
|
135
109
|
}
|
|
136
|
-
|
|
137
110
|
this.container = container;
|
|
138
|
-
const { text, class: className, style } = this.state;
|
|
139
|
-
|
|
140
|
-
const p = document.createElement('p');
|
|
141
|
-
p.id = this._id;
|
|
142
|
-
p.textContent = text;
|
|
143
111
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
112
|
+
// === 2. PREPARE: Destructure state ===
|
|
113
|
+
const { text, style, class: className } = this.state;
|
|
147
114
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
115
|
+
// === 3. BUILD: Create DOM elements ===
|
|
116
|
+
const paragraph = document.createElement('p');
|
|
117
|
+
paragraph.className = 'jux-paragraph';
|
|
118
|
+
paragraph.id = this._id;
|
|
119
|
+
paragraph.textContent = text;
|
|
120
|
+
if (className) paragraph.className += ` ${className}`;
|
|
121
|
+
if (style) paragraph.setAttribute('style', style);
|
|
151
122
|
|
|
152
|
-
|
|
123
|
+
// === 4. WIRE: Attach event listeners and sync bindings ===
|
|
153
124
|
|
|
154
|
-
// Wire
|
|
155
|
-
this._bindings.forEach(({ event, handler
|
|
156
|
-
|
|
157
|
-
// State binding - subscribe to state changes
|
|
158
|
-
stateObj.subscribe((val: any) => {
|
|
159
|
-
const transformed = transform ? transform(val) : val;
|
|
160
|
-
if (event === 'text') {
|
|
161
|
-
p.textContent = transformed;
|
|
162
|
-
this.state.text = transformed;
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
} else {
|
|
166
|
-
// Event binding
|
|
167
|
-
p.addEventListener(event, handler as EventListener);
|
|
168
|
-
}
|
|
125
|
+
// Wire custom bindings from .bind() calls
|
|
126
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
127
|
+
paragraph.addEventListener(event, handler as EventListener);
|
|
169
128
|
});
|
|
170
129
|
|
|
171
|
-
// Wire
|
|
172
|
-
this._syncBindings.forEach(({ property, stateObj,
|
|
173
|
-
|
|
174
|
-
const
|
|
130
|
+
// Wire sync bindings from .sync() calls
|
|
131
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
132
|
+
if (property === 'text') {
|
|
133
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
175
134
|
|
|
176
|
-
|
|
177
|
-
|
|
135
|
+
stateObj.subscribe((val: any) => {
|
|
136
|
+
const transformed = transformToComponent(val);
|
|
137
|
+
paragraph.textContent = transformed;
|
|
178
138
|
this.state.text = transformed;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
this.state.class = transformed;
|
|
182
|
-
} else if (property === 'style') {
|
|
183
|
-
p.setAttribute('style', transformed);
|
|
184
|
-
this.state.style = transformed;
|
|
185
|
-
}
|
|
186
|
-
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
187
141
|
});
|
|
188
142
|
|
|
143
|
+
// === 5. RENDER: Append to DOM and finalize ===
|
|
144
|
+
container.appendChild(paragraph);
|
|
189
145
|
return this;
|
|
190
146
|
}
|
|
147
|
+
|
|
148
|
+
renderTo(juxComponent: any): this {
|
|
149
|
+
if (!juxComponent?._id) {
|
|
150
|
+
throw new Error('Paragraph.renderTo: Invalid component');
|
|
151
|
+
}
|
|
152
|
+
return this.render(`#${juxComponent._id}`);
|
|
153
|
+
}
|
|
191
154
|
}
|
|
192
155
|
|
|
193
156
|
/**
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { getOrCreateContainer } from './helpers.js';
|
|
2
2
|
import { State } from '../reactivity/state.js';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Progress component options
|
|
6
|
-
*/
|
|
7
4
|
export interface ProgressOptions {
|
|
8
5
|
value?: number;
|
|
9
6
|
max?: number;
|
|
@@ -17,9 +14,6 @@ export interface ProgressOptions {
|
|
|
17
14
|
class?: string;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
/**
|
|
21
|
-
* Progress component state
|
|
22
|
-
*/
|
|
23
17
|
type ProgressState = {
|
|
24
18
|
value: number;
|
|
25
19
|
max: number;
|
|
@@ -33,28 +27,20 @@ type ProgressState = {
|
|
|
33
27
|
class: string;
|
|
34
28
|
};
|
|
35
29
|
|
|
36
|
-
/**
|
|
37
|
-
* Progress component - Progress bar for loading/completion
|
|
38
|
-
*
|
|
39
|
-
* Usage:
|
|
40
|
-
* jux.progress('upload', {
|
|
41
|
-
* value: 45,
|
|
42
|
-
* max: 100,
|
|
43
|
-
* label: 'Uploading...',
|
|
44
|
-
* showPercentage: true,
|
|
45
|
-
* animated: true
|
|
46
|
-
* }).render('#app');
|
|
47
|
-
*
|
|
48
|
-
* // Update progress
|
|
49
|
-
* const prog = jux.progress('upload').render('#app');
|
|
50
|
-
* prog.value(75);
|
|
51
|
-
*/
|
|
52
30
|
export class Progress {
|
|
53
31
|
state: ProgressState;
|
|
54
32
|
container: HTMLElement | null = null;
|
|
55
33
|
_id: string;
|
|
56
34
|
id: string;
|
|
57
|
-
|
|
35
|
+
|
|
36
|
+
// CRITICAL: Store bind/sync instructions for deferred wiring
|
|
37
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
38
|
+
private _syncBindings: Array<{
|
|
39
|
+
property: string,
|
|
40
|
+
stateObj: State<any>,
|
|
41
|
+
toState?: Function,
|
|
42
|
+
toComponent?: Function
|
|
43
|
+
}> = [];
|
|
58
44
|
|
|
59
45
|
constructor(id: string, options: ProgressOptions = {}) {
|
|
60
46
|
this._id = id;
|
|
@@ -79,31 +65,29 @@ export class Progress {
|
|
|
79
65
|
* ------------------------- */
|
|
80
66
|
|
|
81
67
|
value(value: number): this {
|
|
82
|
-
this.state.value =
|
|
83
|
-
this.
|
|
68
|
+
this.state.value = value;
|
|
69
|
+
this._updateProgress();
|
|
84
70
|
return this;
|
|
85
71
|
}
|
|
86
72
|
|
|
87
73
|
max(value: number): this {
|
|
88
74
|
this.state.max = value;
|
|
75
|
+
this._updateProgress();
|
|
89
76
|
return this;
|
|
90
77
|
}
|
|
91
78
|
|
|
92
79
|
label(value: string): this {
|
|
93
80
|
this.state.label = value;
|
|
94
|
-
this._updateElement();
|
|
95
81
|
return this;
|
|
96
82
|
}
|
|
97
83
|
|
|
98
84
|
showPercentage(value: boolean): this {
|
|
99
85
|
this.state.showPercentage = value;
|
|
100
|
-
this._updateElement();
|
|
101
86
|
return this;
|
|
102
87
|
}
|
|
103
88
|
|
|
104
89
|
variant(value: 'default' | 'success' | 'warning' | 'error' | 'info'): this {
|
|
105
90
|
this.state.variant = value;
|
|
106
|
-
this._updateElement();
|
|
107
91
|
return this;
|
|
108
92
|
}
|
|
109
93
|
|
|
@@ -114,13 +98,11 @@ export class Progress {
|
|
|
114
98
|
|
|
115
99
|
striped(value: boolean): this {
|
|
116
100
|
this.state.striped = value;
|
|
117
|
-
this._updateElement();
|
|
118
101
|
return this;
|
|
119
102
|
}
|
|
120
103
|
|
|
121
104
|
animated(value: boolean): this {
|
|
122
105
|
this.state.animated = value;
|
|
123
|
-
this._updateElement();
|
|
124
106
|
return this;
|
|
125
107
|
}
|
|
126
108
|
|
|
@@ -134,131 +116,125 @@ export class Progress {
|
|
|
134
116
|
return this;
|
|
135
117
|
}
|
|
136
118
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
this._boundState = stateObj;
|
|
142
|
-
|
|
143
|
-
stateObj.subscribe((val) => {
|
|
144
|
-
this.value(val);
|
|
145
|
-
});
|
|
119
|
+
bind(event: string, handler: Function): this {
|
|
120
|
+
this._bindings.push({ event, handler });
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
146
123
|
|
|
124
|
+
sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
|
|
125
|
+
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
126
|
+
throw new Error(`Progress.sync: Expected a State object for property "${property}"`);
|
|
127
|
+
}
|
|
128
|
+
this._syncBindings.push({ property, stateObj, toState, toComponent });
|
|
147
129
|
return this;
|
|
148
130
|
}
|
|
149
131
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
132
|
+
getPercentage(): number {
|
|
133
|
+
return Math.round((this.state.value / this.state.max) * 100);
|
|
134
|
+
}
|
|
153
135
|
|
|
154
|
-
private
|
|
155
|
-
const wrapper = document.getElementById(this._id);
|
|
136
|
+
private _updateProgress(): void {
|
|
156
137
|
const bar = document.getElementById(`${this._id}-bar`);
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
// If element has a value attribute set externally (e.g., by bindValue), sync state
|
|
160
|
-
if (wrapper && wrapper.hasAttribute('data-value')) {
|
|
161
|
-
const externalValue = parseFloat(wrapper.getAttribute('data-value') || '0');
|
|
162
|
-
this.state.value = externalValue;
|
|
163
|
-
}
|
|
138
|
+
const percentageEl = document.getElementById(`${this._id}-percentage`);
|
|
164
139
|
|
|
165
140
|
if (bar) {
|
|
166
|
-
const percentage =
|
|
141
|
+
const percentage = this.getPercentage();
|
|
167
142
|
bar.style.width = `${percentage}%`;
|
|
168
|
-
bar.setAttribute('aria-valuenow', this.state.value.toString());
|
|
169
|
-
bar.className = `jux-progress-bar jux-progress-bar-${this.state.variant}`;
|
|
170
143
|
|
|
171
|
-
if (
|
|
172
|
-
|
|
144
|
+
if (percentageEl) {
|
|
145
|
+
percentageEl.textContent = `${percentage}%`;
|
|
173
146
|
}
|
|
174
|
-
if (this.state.animated) {
|
|
175
|
-
bar.classList.add('jux-progress-bar-animated');
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (labelEl) {
|
|
180
|
-
const percentage = Math.round((this.state.value / this.state.max) * 100);
|
|
181
|
-
const text = this.state.showPercentage
|
|
182
|
-
? `${this.state.label} ${percentage}%`.trim()
|
|
183
|
-
: this.state.label;
|
|
184
|
-
labelEl.textContent = text;
|
|
185
147
|
}
|
|
186
148
|
}
|
|
187
149
|
|
|
188
|
-
getPercentage(): number {
|
|
189
|
-
return Math.round((this.state.value / this.state.max) * 100);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
150
|
/* -------------------------
|
|
193
151
|
* Render
|
|
194
152
|
* ------------------------- */
|
|
195
153
|
|
|
196
154
|
render(targetId?: string): this {
|
|
155
|
+
// === 1. SETUP: Get or create container ===
|
|
197
156
|
let container: HTMLElement;
|
|
198
|
-
|
|
199
157
|
if (targetId) {
|
|
200
158
|
const target = document.querySelector(targetId);
|
|
201
159
|
if (!target || !(target instanceof HTMLElement)) {
|
|
202
|
-
throw new Error(`Progress: Target
|
|
160
|
+
throw new Error(`Progress: Target "${targetId}" not found`);
|
|
203
161
|
}
|
|
204
162
|
container = target;
|
|
205
163
|
} else {
|
|
206
164
|
container = getOrCreateContainer(this._id);
|
|
207
165
|
}
|
|
208
|
-
|
|
209
166
|
this.container = container;
|
|
210
|
-
const { value, max, label, showPercentage, variant, size, striped, animated, style, class: className } = this.state;
|
|
211
167
|
|
|
168
|
+
// === 2. PREPARE: Destructure state ===
|
|
169
|
+
const { value, max, variant, showPercentage, label, style, class: className } = this.state;
|
|
170
|
+
const percentage = Math.min(100, Math.max(0, (value / max) * 100));
|
|
171
|
+
|
|
172
|
+
// === 3. BUILD: Create DOM elements ===
|
|
212
173
|
const wrapper = document.createElement('div');
|
|
213
|
-
wrapper.className =
|
|
174
|
+
wrapper.className = 'jux-progress';
|
|
214
175
|
wrapper.id = this._id;
|
|
176
|
+
if (className) wrapper.className += ` ${className}`;
|
|
177
|
+
if (style) wrapper.setAttribute('style', style);
|
|
215
178
|
|
|
216
|
-
if (
|
|
217
|
-
wrapper.className += ` ${className}`;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (style) {
|
|
221
|
-
wrapper.setAttribute('style', style);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Label
|
|
225
|
-
if (label || showPercentage) {
|
|
179
|
+
if (showPercentage || label) {
|
|
226
180
|
const labelEl = document.createElement('div');
|
|
227
181
|
labelEl.className = 'jux-progress-label';
|
|
228
|
-
labelEl.
|
|
229
|
-
const percentage = Math.round((value / max) * 100);
|
|
230
|
-
const text = showPercentage
|
|
231
|
-
? `${label} ${percentage}%`.trim()
|
|
232
|
-
: label;
|
|
233
|
-
labelEl.textContent = text;
|
|
182
|
+
labelEl.textContent = label || `${Math.round(percentage)}%`;
|
|
234
183
|
wrapper.appendChild(labelEl);
|
|
235
184
|
}
|
|
236
185
|
|
|
237
|
-
// Progress track
|
|
238
186
|
const track = document.createElement('div');
|
|
239
187
|
track.className = 'jux-progress-track';
|
|
240
188
|
|
|
241
|
-
// Progress bar
|
|
242
189
|
const bar = document.createElement('div');
|
|
243
190
|
bar.className = `jux-progress-bar jux-progress-bar-${variant}`;
|
|
244
191
|
bar.id = `${this._id}-bar`;
|
|
245
|
-
bar.setAttribute('role', 'progressbar');
|
|
246
|
-
bar.setAttribute('aria-valuenow', value.toString());
|
|
247
|
-
bar.setAttribute('aria-valuemin', '0');
|
|
248
|
-
bar.setAttribute('aria-valuemax', max.toString());
|
|
249
|
-
|
|
250
|
-
const percentage = (value / max) * 100;
|
|
251
192
|
bar.style.width = `${percentage}%`;
|
|
252
193
|
|
|
253
|
-
if (striped) {
|
|
254
|
-
bar.classList.add('jux-progress-bar-striped');
|
|
255
|
-
}
|
|
256
|
-
if (animated) {
|
|
257
|
-
bar.classList.add('jux-progress-bar-animated');
|
|
258
|
-
}
|
|
259
|
-
|
|
260
194
|
track.appendChild(bar);
|
|
261
195
|
wrapper.appendChild(track);
|
|
196
|
+
|
|
197
|
+
// === 4. WIRE: Attach event listeners and sync bindings ===
|
|
198
|
+
|
|
199
|
+
// Wire custom bindings from .bind() calls
|
|
200
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
201
|
+
wrapper.addEventListener(event, handler as EventListener);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Wire sync bindings from .sync() calls
|
|
205
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
206
|
+
if (property === 'value') {
|
|
207
|
+
const transformToComponent = toComponent || ((v: any) => Number(v));
|
|
208
|
+
|
|
209
|
+
stateObj.subscribe((val: any) => {
|
|
210
|
+
const transformed = transformToComponent(val);
|
|
211
|
+
this.state.value = transformed;
|
|
212
|
+
const newPercentage = Math.min(100, Math.max(0, (transformed / this.state.max) * 100));
|
|
213
|
+
bar.style.width = `${newPercentage}%`;
|
|
214
|
+
|
|
215
|
+
if (showPercentage && !label) {
|
|
216
|
+
const labelEl = wrapper.querySelector('.jux-progress-label');
|
|
217
|
+
if (labelEl) {
|
|
218
|
+
labelEl.textContent = `${Math.round(newPercentage)}%`;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
else if (property === 'label') {
|
|
224
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
225
|
+
|
|
226
|
+
stateObj.subscribe((val: any) => {
|
|
227
|
+
const transformed = transformToComponent(val);
|
|
228
|
+
const labelEl = wrapper.querySelector('.jux-progress-label');
|
|
229
|
+
if (labelEl) {
|
|
230
|
+
labelEl.textContent = transformed;
|
|
231
|
+
}
|
|
232
|
+
this.state.label = transformed;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// === 5. RENDER: Append to DOM and finalize ===
|
|
262
238
|
container.appendChild(wrapper);
|
|
263
239
|
return this;
|
|
264
240
|
}
|