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/input.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getOrCreateContainer } from './helpers.js';
|
|
2
2
|
import { State } from '../reactivity/state.js';
|
|
3
|
+
import { renderIcon } from './icons.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Input component options
|
|
@@ -9,6 +10,7 @@ export interface InputOptions {
|
|
|
9
10
|
value?: string;
|
|
10
11
|
placeholder?: string;
|
|
11
12
|
label?: string;
|
|
13
|
+
icon?: string;
|
|
12
14
|
required?: boolean;
|
|
13
15
|
disabled?: boolean;
|
|
14
16
|
name?: string;
|
|
@@ -19,7 +21,6 @@ export interface InputOptions {
|
|
|
19
21
|
minLength?: number;
|
|
20
22
|
maxLength?: number;
|
|
21
23
|
pattern?: string;
|
|
22
|
-
onChange?: (value: string) => void;
|
|
23
24
|
onValidate?: (value: string) => boolean | string;
|
|
24
25
|
style?: string;
|
|
25
26
|
class?: string;
|
|
@@ -33,6 +34,7 @@ type InputState = {
|
|
|
33
34
|
value: string;
|
|
34
35
|
placeholder: string;
|
|
35
36
|
label: string;
|
|
37
|
+
icon: string;
|
|
36
38
|
required: boolean;
|
|
37
39
|
disabled: boolean;
|
|
38
40
|
name: string;
|
|
@@ -48,44 +50,22 @@ type InputState = {
|
|
|
48
50
|
errorMessage?: string;
|
|
49
51
|
};
|
|
50
52
|
|
|
51
|
-
/**
|
|
52
|
-
* Input component - text input and textarea with validation
|
|
53
|
-
*
|
|
54
|
-
* Usage:
|
|
55
|
-
* jux.input('username')
|
|
56
|
-
* .label('Username')
|
|
57
|
-
* .placeholder('Enter username')
|
|
58
|
-
* .required(true)
|
|
59
|
-
* .minLength(3)
|
|
60
|
-
* .maxLength(20)
|
|
61
|
-
* .render('#form');
|
|
62
|
-
*
|
|
63
|
-
* jux.input('age')
|
|
64
|
-
* .type('number')
|
|
65
|
-
* .min(0)
|
|
66
|
-
* .max(120)
|
|
67
|
-
* .step(1)
|
|
68
|
-
* .render('#form');
|
|
69
|
-
*
|
|
70
|
-
* jux.input('bio')
|
|
71
|
-
* .type('textarea')
|
|
72
|
-
* .rows(5)
|
|
73
|
-
* .maxLength(500)
|
|
74
|
-
* .render('#form');
|
|
75
|
-
*/
|
|
76
53
|
export class Input {
|
|
77
54
|
state: InputState;
|
|
78
55
|
container: HTMLElement | null = null;
|
|
79
56
|
_id: string;
|
|
80
57
|
id: string;
|
|
81
|
-
private _onChange?: (value: string) => void;
|
|
82
58
|
private _onValidate?: (value: string) => boolean | string;
|
|
83
|
-
|
|
59
|
+
|
|
60
|
+
// Store bind() instructions (DOM events only)
|
|
61
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
62
|
+
|
|
63
|
+
// Store sync() instructions (state synchronization)
|
|
64
|
+
private _syncBindings: Array<{ property: string, stateObj: State<any>, toState?: Function, toComponent?: Function }> = [];
|
|
84
65
|
|
|
85
66
|
constructor(id: string, options: InputOptions = {}) {
|
|
86
67
|
this._id = id;
|
|
87
68
|
this.id = id;
|
|
88
|
-
this._onChange = options.onChange;
|
|
89
69
|
this._onValidate = options.onValidate;
|
|
90
70
|
|
|
91
71
|
this.state = {
|
|
@@ -93,6 +73,7 @@ export class Input {
|
|
|
93
73
|
value: options.value ?? '',
|
|
94
74
|
placeholder: options.placeholder ?? '',
|
|
95
75
|
label: options.label ?? '',
|
|
76
|
+
icon: options.icon ?? '',
|
|
96
77
|
required: options.required ?? false,
|
|
97
78
|
disabled: options.disabled ?? false,
|
|
98
79
|
name: options.name ?? id,
|
|
@@ -133,6 +114,11 @@ export class Input {
|
|
|
133
114
|
return this;
|
|
134
115
|
}
|
|
135
116
|
|
|
117
|
+
icon(value: string): this {
|
|
118
|
+
this.state.icon = value;
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
|
|
136
122
|
required(value: boolean): this {
|
|
137
123
|
this.state.required = value;
|
|
138
124
|
return this;
|
|
@@ -154,54 +140,36 @@ export class Input {
|
|
|
154
140
|
return this;
|
|
155
141
|
}
|
|
156
142
|
|
|
157
|
-
/**
|
|
158
|
-
* Minimum value for number inputs
|
|
159
|
-
*/
|
|
160
143
|
min(value: number): this {
|
|
161
144
|
this.state.min = value;
|
|
162
145
|
this._updateElement();
|
|
163
146
|
return this;
|
|
164
147
|
}
|
|
165
148
|
|
|
166
|
-
/**
|
|
167
|
-
* Maximum value for number inputs
|
|
168
|
-
*/
|
|
169
149
|
max(value: number): this {
|
|
170
150
|
this.state.max = value;
|
|
171
151
|
this._updateElement();
|
|
172
152
|
return this;
|
|
173
153
|
}
|
|
174
154
|
|
|
175
|
-
/**
|
|
176
|
-
* Step value for number inputs
|
|
177
|
-
*/
|
|
178
155
|
step(value: number): this {
|
|
179
156
|
this.state.step = value;
|
|
180
157
|
this._updateElement();
|
|
181
158
|
return this;
|
|
182
159
|
}
|
|
183
160
|
|
|
184
|
-
/**
|
|
185
|
-
* Minimum length for text inputs
|
|
186
|
-
*/
|
|
187
161
|
minLength(value: number): this {
|
|
188
162
|
this.state.minLength = value;
|
|
189
163
|
this._updateElement();
|
|
190
164
|
return this;
|
|
191
165
|
}
|
|
192
166
|
|
|
193
|
-
/**
|
|
194
|
-
* Maximum length for text inputs
|
|
195
|
-
*/
|
|
196
167
|
maxLength(value: number): this {
|
|
197
168
|
this.state.maxLength = value;
|
|
198
169
|
this._updateElement();
|
|
199
170
|
return this;
|
|
200
171
|
}
|
|
201
172
|
|
|
202
|
-
/**
|
|
203
|
-
* Pattern validation for text inputs (regex)
|
|
204
|
-
*/
|
|
205
173
|
pattern(value: string): this {
|
|
206
174
|
this.state.pattern = value;
|
|
207
175
|
this._updateElement();
|
|
@@ -218,35 +186,33 @@ export class Input {
|
|
|
218
186
|
return this;
|
|
219
187
|
}
|
|
220
188
|
|
|
221
|
-
|
|
222
|
-
this.
|
|
189
|
+
onValidate(handler: (value: string) => boolean | string): this {
|
|
190
|
+
this._onValidate = handler;
|
|
223
191
|
return this;
|
|
224
192
|
}
|
|
225
193
|
|
|
226
194
|
/**
|
|
227
|
-
*
|
|
228
|
-
*
|
|
195
|
+
* Bind event handler (stores for wiring in render)
|
|
196
|
+
* DOM events only: input, change, blur, focus, etc.
|
|
229
197
|
*/
|
|
230
|
-
|
|
231
|
-
this.
|
|
198
|
+
bind(event: string, handler: Function): this {
|
|
199
|
+
this._bindings.push({ event, handler });
|
|
232
200
|
return this;
|
|
233
201
|
}
|
|
234
202
|
|
|
235
203
|
/**
|
|
236
|
-
* Two-way
|
|
204
|
+
* Two-way sync with state (stores for wiring in render)
|
|
205
|
+
*
|
|
206
|
+
* @param property - Component property to sync ('value', 'label', etc)
|
|
207
|
+
* @param stateObj - State object to sync with
|
|
208
|
+
* @param toState - Optional transform function when going from component to state
|
|
209
|
+
* @param toComponent - Optional transform function when going from state to component
|
|
237
210
|
*/
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
this.state.value = val;
|
|
244
|
-
this._updateElement();
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Update state on input change
|
|
248
|
-
this.onChange((value) => stateObj.set(value));
|
|
249
|
-
|
|
211
|
+
sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
|
|
212
|
+
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
213
|
+
throw new Error(`Input.sync: Expected a State object for property "${property}"`);
|
|
214
|
+
}
|
|
215
|
+
this._syncBindings.push({ property, stateObj, toState, toComponent });
|
|
250
216
|
return this;
|
|
251
217
|
}
|
|
252
218
|
|
|
@@ -257,12 +223,10 @@ export class Input {
|
|
|
257
223
|
private _validate(value: string): boolean | string {
|
|
258
224
|
const { required, type, min, max, minLength, maxLength, pattern } = this.state;
|
|
259
225
|
|
|
260
|
-
// Required check
|
|
261
226
|
if (required && !value.trim()) {
|
|
262
227
|
return 'This field is required';
|
|
263
228
|
}
|
|
264
229
|
|
|
265
|
-
// Number validation
|
|
266
230
|
if (type === 'number' && value) {
|
|
267
231
|
const numValue = Number(value);
|
|
268
232
|
|
|
@@ -279,7 +243,6 @@ export class Input {
|
|
|
279
243
|
}
|
|
280
244
|
}
|
|
281
245
|
|
|
282
|
-
// Text length validation
|
|
283
246
|
if ((type === 'text' || type === 'textarea') && value) {
|
|
284
247
|
if (minLength !== undefined && value.length < minLength) {
|
|
285
248
|
return `Must be at least ${minLength} characters`;
|
|
@@ -290,7 +253,6 @@ export class Input {
|
|
|
290
253
|
}
|
|
291
254
|
}
|
|
292
255
|
|
|
293
|
-
// Pattern validation
|
|
294
256
|
if (pattern && value) {
|
|
295
257
|
const regex = new RegExp(pattern);
|
|
296
258
|
if (!regex.test(value)) {
|
|
@@ -298,7 +260,6 @@ export class Input {
|
|
|
298
260
|
}
|
|
299
261
|
}
|
|
300
262
|
|
|
301
|
-
// Custom validation
|
|
302
263
|
if (this._onValidate) {
|
|
303
264
|
const result = this._onValidate(value);
|
|
304
265
|
if (result !== true) {
|
|
@@ -341,9 +302,6 @@ export class Input {
|
|
|
341
302
|
this.state.errorMessage = undefined;
|
|
342
303
|
}
|
|
343
304
|
|
|
344
|
-
/**
|
|
345
|
-
* Manually validate the current value
|
|
346
|
-
*/
|
|
347
305
|
validate(): boolean {
|
|
348
306
|
const result = this._validate(this.state.value);
|
|
349
307
|
|
|
@@ -400,8 +358,8 @@ export class Input {
|
|
|
400
358
|
* ------------------------- */
|
|
401
359
|
|
|
402
360
|
render(targetId?: string): this {
|
|
361
|
+
// === 1. SETUP: Get container ===
|
|
403
362
|
let container: HTMLElement;
|
|
404
|
-
|
|
405
363
|
if (targetId) {
|
|
406
364
|
const target = document.querySelector(targetId);
|
|
407
365
|
if (!target || !(target instanceof HTMLElement)) {
|
|
@@ -411,40 +369,52 @@ export class Input {
|
|
|
411
369
|
} else {
|
|
412
370
|
container = getOrCreateContainer(this._id);
|
|
413
371
|
}
|
|
414
|
-
|
|
415
372
|
this.container = container;
|
|
416
|
-
const { type, value, placeholder, label, required, disabled, name, rows, min, max, step, minLength, maxLength, pattern, style, class: className } = this.state;
|
|
417
373
|
|
|
374
|
+
// === 2. PREPARE: Destructure state and check bindings ===
|
|
375
|
+
const {
|
|
376
|
+
type, value, placeholder, label, icon, required, disabled, name, rows,
|
|
377
|
+
min, max, step, minLength, maxLength, pattern, style, class: className
|
|
378
|
+
} = this.state;
|
|
379
|
+
|
|
380
|
+
const hasValueSync = this._syncBindings.some(binding => binding.property === 'value');
|
|
381
|
+
|
|
382
|
+
// === 3. BUILD: Create all DOM elements ===
|
|
418
383
|
const wrapper = document.createElement('div');
|
|
419
384
|
wrapper.className = 'jux-input';
|
|
420
385
|
wrapper.id = this._id;
|
|
421
|
-
|
|
422
|
-
if (
|
|
423
|
-
wrapper.className += ` ${className}`;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
if (style) {
|
|
427
|
-
wrapper.setAttribute('style', style);
|
|
428
|
-
}
|
|
386
|
+
if (className) wrapper.className += ` ${className}`;
|
|
387
|
+
if (style) wrapper.setAttribute('style', style);
|
|
429
388
|
|
|
430
389
|
// Label
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
390
|
+
const labelEl = document.createElement('label');
|
|
391
|
+
labelEl.className = 'jux-input-label';
|
|
392
|
+
labelEl.htmlFor = `${this._id}-input`;
|
|
393
|
+
labelEl.textContent = label;
|
|
394
|
+
if (required) {
|
|
395
|
+
const requiredSpan = document.createElement('span');
|
|
396
|
+
requiredSpan.className = 'jux-input-required';
|
|
397
|
+
requiredSpan.textContent = ' *';
|
|
398
|
+
labelEl.appendChild(requiredSpan);
|
|
399
|
+
}
|
|
400
|
+
if (label) wrapper.appendChild(labelEl);
|
|
401
|
+
|
|
402
|
+
// Input container
|
|
403
|
+
const inputContainer = document.createElement('div');
|
|
404
|
+
inputContainer.className = 'jux-input-container';
|
|
405
|
+
if (icon) inputContainer.classList.add('jux-input-with-icon');
|
|
406
|
+
|
|
407
|
+
// Icon
|
|
408
|
+
if (icon) {
|
|
409
|
+
const iconEl = document.createElement('span');
|
|
410
|
+
iconEl.className = 'jux-input-icon';
|
|
411
|
+
const iconElement = renderIcon(icon);
|
|
412
|
+
iconEl.appendChild(iconElement);
|
|
413
|
+
inputContainer.appendChild(iconEl);
|
|
443
414
|
}
|
|
444
415
|
|
|
445
|
-
// Input/Textarea
|
|
416
|
+
// Input/Textarea element
|
|
446
417
|
let inputEl: HTMLInputElement | HTMLTextAreaElement;
|
|
447
|
-
|
|
448
418
|
if (type === 'textarea') {
|
|
449
419
|
inputEl = document.createElement('textarea');
|
|
450
420
|
inputEl.rows = rows;
|
|
@@ -454,14 +424,12 @@ export class Input {
|
|
|
454
424
|
inputEl = document.createElement('input');
|
|
455
425
|
inputEl.type = type;
|
|
456
426
|
|
|
457
|
-
// Number-specific attributes
|
|
458
427
|
if (type === 'number') {
|
|
459
428
|
if (min !== undefined) inputEl.min = String(min);
|
|
460
429
|
if (max !== undefined) inputEl.max = String(max);
|
|
461
430
|
if (step !== undefined) inputEl.step = String(step);
|
|
462
431
|
}
|
|
463
432
|
|
|
464
|
-
// Text-specific attributes
|
|
465
433
|
if (type === 'text' || type === 'email' || type === 'tel' || type === 'url') {
|
|
466
434
|
if (minLength !== undefined) inputEl.minLength = minLength;
|
|
467
435
|
if (maxLength !== undefined) inputEl.maxLength = maxLength;
|
|
@@ -477,52 +445,108 @@ export class Input {
|
|
|
477
445
|
inputEl.required = required;
|
|
478
446
|
inputEl.disabled = disabled;
|
|
479
447
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const target = e.target as HTMLInputElement | HTMLTextAreaElement;
|
|
483
|
-
this.state.value = target.value;
|
|
484
|
-
|
|
485
|
-
// Clear error on input
|
|
486
|
-
this._clearError();
|
|
487
|
-
|
|
488
|
-
if (this._onChange) {
|
|
489
|
-
this._onChange(target.value);
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
// Blur event for validation
|
|
494
|
-
inputEl.addEventListener('blur', () => {
|
|
495
|
-
this.validate();
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
wrapper.appendChild(inputEl);
|
|
448
|
+
inputContainer.appendChild(inputEl);
|
|
449
|
+
wrapper.appendChild(inputContainer);
|
|
499
450
|
|
|
500
|
-
// Error
|
|
451
|
+
// Error element
|
|
501
452
|
const errorEl = document.createElement('div');
|
|
502
453
|
errorEl.className = 'jux-input-error';
|
|
503
454
|
errorEl.id = `${this._id}-error`;
|
|
504
455
|
errorEl.style.display = 'none';
|
|
505
456
|
wrapper.appendChild(errorEl);
|
|
506
457
|
|
|
507
|
-
// Character counter
|
|
458
|
+
// Character counter
|
|
508
459
|
if (maxLength && (type === 'text' || type === 'textarea')) {
|
|
509
460
|
const counterEl = document.createElement('div');
|
|
510
461
|
counterEl.className = 'jux-input-counter';
|
|
511
462
|
counterEl.id = `${this._id}-counter`;
|
|
512
463
|
counterEl.textContent = `${value.length}/${maxLength}`;
|
|
464
|
+
wrapper.appendChild(counterEl);
|
|
513
465
|
|
|
466
|
+
// Wire counter immediately
|
|
514
467
|
inputEl.addEventListener('input', () => {
|
|
515
468
|
counterEl.textContent = `${inputEl.value.length}/${maxLength}`;
|
|
516
469
|
});
|
|
470
|
+
}
|
|
517
471
|
|
|
518
|
-
|
|
472
|
+
// === 4. WIRE: Add event listeners ===
|
|
473
|
+
|
|
474
|
+
// Default input handler (only if NOT using sync)
|
|
475
|
+
if (!hasValueSync) {
|
|
476
|
+
inputEl.addEventListener('input', () => {
|
|
477
|
+
this.state.value = inputEl.value;
|
|
478
|
+
this._clearError();
|
|
479
|
+
});
|
|
519
480
|
}
|
|
520
481
|
|
|
521
|
-
|
|
482
|
+
// Always add blur validation
|
|
483
|
+
inputEl.addEventListener('blur', () => {
|
|
484
|
+
this.validate();
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// Wire up custom event bindings (from .bind() calls)
|
|
488
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
489
|
+
wrapper.addEventListener(event, handler as EventListener);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// Wire up sync bindings (from .sync() calls)
|
|
493
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
494
|
+
if (property === 'value') {
|
|
495
|
+
// Default transforms
|
|
496
|
+
const transformToState = toState || ((v: string) => {
|
|
497
|
+
return type === 'number' ? (parseInt(v) || 0) : v;
|
|
498
|
+
});
|
|
499
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
500
|
+
|
|
501
|
+
let isUpdating = false;
|
|
502
|
+
|
|
503
|
+
// State → Input (when state changes, update input)
|
|
504
|
+
stateObj.subscribe((val: any) => {
|
|
505
|
+
if (isUpdating) return;
|
|
506
|
+
const transformed = transformToComponent(val);
|
|
507
|
+
if (inputEl.value !== transformed) {
|
|
508
|
+
inputEl.value = transformed;
|
|
509
|
+
this.state.value = transformed;
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Input → State (when input changes, update state)
|
|
514
|
+
inputEl.addEventListener('input', () => {
|
|
515
|
+
if (isUpdating) return;
|
|
516
|
+
isUpdating = true;
|
|
517
|
+
|
|
518
|
+
const transformed = transformToState(inputEl.value);
|
|
519
|
+
this.state.value = inputEl.value;
|
|
520
|
+
this._clearError();
|
|
521
|
+
|
|
522
|
+
stateObj.set(transformed);
|
|
523
|
+
|
|
524
|
+
setTimeout(() => { isUpdating = false; }, 0);
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
else if (property === 'label') {
|
|
528
|
+
// Sync label (one-way: state → component)
|
|
529
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
530
|
+
|
|
531
|
+
stateObj.subscribe((val: any) => {
|
|
532
|
+
const transformed = transformToComponent(val);
|
|
533
|
+
labelEl.textContent = transformed;
|
|
534
|
+
this.state.label = transformed;
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
});
|
|
522
538
|
|
|
523
|
-
//
|
|
539
|
+
// === 5. RENDER: Append to DOM and finalize ===
|
|
540
|
+
container.appendChild(wrapper);
|
|
524
541
|
this._injectDefaultStyles();
|
|
525
542
|
|
|
543
|
+
// Trigger Lucide icon rendering
|
|
544
|
+
requestAnimationFrame(() => {
|
|
545
|
+
if ((window as any).lucide) {
|
|
546
|
+
(window as any).lucide.createIcons();
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
526
550
|
return this;
|
|
527
551
|
}
|
|
528
552
|
|
|
@@ -537,6 +561,31 @@ export class Input {
|
|
|
537
561
|
margin-bottom: 16px;
|
|
538
562
|
}
|
|
539
563
|
|
|
564
|
+
.jux-input-container {
|
|
565
|
+
position: relative;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.jux-input-with-icon .jux-input-element {
|
|
569
|
+
padding-left: 40px;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.jux-input-icon {
|
|
573
|
+
position: absolute;
|
|
574
|
+
left: 12px;
|
|
575
|
+
top: 50%;
|
|
576
|
+
transform: translateY(-50%);
|
|
577
|
+
display: flex;
|
|
578
|
+
align-items: center;
|
|
579
|
+
justify-content: center;
|
|
580
|
+
color: #6b7280;
|
|
581
|
+
pointer-events: none;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.jux-input-icon svg {
|
|
585
|
+
width: 18px;
|
|
586
|
+
height: 18px;
|
|
587
|
+
}
|
|
588
|
+
|
|
540
589
|
.jux-input-label {
|
|
541
590
|
display: block;
|
|
542
591
|
margin-bottom: 6px;
|