juxscript 1.0.19 → 1.0.21

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.
Files changed (77) hide show
  1. package/bin/cli.js +121 -72
  2. package/lib/components/alert.ts +212 -165
  3. package/lib/components/badge.ts +93 -103
  4. package/lib/components/base/BaseComponent.ts +397 -0
  5. package/lib/components/base/FormInput.ts +322 -0
  6. package/lib/components/button.ts +63 -122
  7. package/lib/components/card.ts +109 -155
  8. package/lib/components/charts/areachart.ts +315 -0
  9. package/lib/components/charts/barchart.ts +421 -0
  10. package/lib/components/charts/doughnutchart.ts +263 -0
  11. package/lib/components/charts/lib/BaseChart.ts +402 -0
  12. package/lib/components/charts/lib/chart-types.ts +159 -0
  13. package/lib/components/charts/lib/chart-utils.ts +160 -0
  14. package/lib/components/charts/lib/chart.ts +707 -0
  15. package/lib/components/checkbox.ts +264 -127
  16. package/lib/components/code.ts +75 -108
  17. package/lib/components/container.ts +113 -130
  18. package/lib/components/data.ts +37 -5
  19. package/lib/components/datepicker.ts +195 -147
  20. package/lib/components/dialog.ts +187 -157
  21. package/lib/components/divider.ts +85 -191
  22. package/lib/components/docs-data.json +544 -2027
  23. package/lib/components/dropdown.ts +178 -136
  24. package/lib/components/element.ts +227 -171
  25. package/lib/components/fileupload.ts +285 -228
  26. package/lib/components/guard.ts +92 -0
  27. package/lib/components/heading.ts +46 -69
  28. package/lib/components/helpers.ts +13 -6
  29. package/lib/components/hero.ts +107 -95
  30. package/lib/components/icon.ts +160 -0
  31. package/lib/components/icons.ts +175 -0
  32. package/lib/components/include.ts +153 -5
  33. package/lib/components/input.ts +174 -374
  34. package/lib/components/kpicard.ts +16 -16
  35. package/lib/components/list.ts +378 -240
  36. package/lib/components/loading.ts +142 -211
  37. package/lib/components/menu.ts +103 -97
  38. package/lib/components/modal.ts +138 -144
  39. package/lib/components/nav.ts +169 -90
  40. package/lib/components/paragraph.ts +49 -150
  41. package/lib/components/progress.ts +118 -200
  42. package/lib/components/radio.ts +297 -149
  43. package/lib/components/script.ts +19 -87
  44. package/lib/components/select.ts +184 -186
  45. package/lib/components/sidebar.ts +152 -140
  46. package/lib/components/style.ts +19 -82
  47. package/lib/components/switch.ts +258 -188
  48. package/lib/components/table.ts +1117 -170
  49. package/lib/components/tabs.ts +162 -145
  50. package/lib/components/theme-toggle.ts +108 -169
  51. package/lib/components/tooltip.ts +86 -157
  52. package/lib/components/write.ts +108 -127
  53. package/lib/jux.ts +86 -41
  54. package/machinery/build.js +466 -0
  55. package/machinery/compiler.js +354 -105
  56. package/machinery/server.js +23 -100
  57. package/machinery/watcher.js +153 -130
  58. package/package.json +1 -2
  59. package/presets/base.css +1166 -0
  60. package/presets/notion.css +2 -1975
  61. package/lib/adapters/base-adapter.js +0 -35
  62. package/lib/adapters/index.js +0 -33
  63. package/lib/adapters/mysql-adapter.js +0 -65
  64. package/lib/adapters/postgres-adapter.js +0 -70
  65. package/lib/adapters/sqlite-adapter.js +0 -56
  66. package/lib/components/areachart.ts +0 -1246
  67. package/lib/components/areachartsmooth.ts +0 -1380
  68. package/lib/components/barchart.ts +0 -1250
  69. package/lib/components/chart.ts +0 -127
  70. package/lib/components/doughnutchart.ts +0 -1191
  71. package/lib/components/footer.ts +0 -165
  72. package/lib/components/header.ts +0 -187
  73. package/lib/components/layout.ts +0 -239
  74. package/lib/components/main.ts +0 -137
  75. package/lib/layouts/default.jux +0 -8
  76. package/lib/layouts/figma.jux +0 -0
  77. /package/lib/{themes → components/charts/lib}/charts.js +0 -0
@@ -1,14 +1,16 @@
1
- import { getOrCreateContainer } from './helpers.js';
2
- import { State } from '../reactivity/state.js';
1
+ import { FormInput, FormInputState } from './base/FormInput.js';
2
+ import { renderIcon } from './icons.js';
3
+
4
+ // Event definitions
5
+ const TRIGGER_EVENTS = [] as const;
6
+ const CALLBACK_EVENTS = ['change', 'input'] as const;
3
7
 
4
- /**
5
- * Input component options
6
- */
7
8
  export interface InputOptions {
8
9
  type?: string;
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,23 +21,16 @@ 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;
26
27
  }
27
28
 
28
- /**
29
- * Input component state
30
- */
31
- type InputState = {
29
+ interface InputState extends FormInputState {
32
30
  type: string;
33
31
  value: string;
34
32
  placeholder: string;
35
- label: string;
36
- required: boolean;
37
- disabled: boolean;
38
- name: string;
33
+ icon: string;
39
34
  rows: number;
40
35
  min?: number;
41
36
  max?: number;
@@ -43,39 +38,16 @@ type InputState = {
43
38
  minLength?: number;
44
39
  maxLength?: number;
45
40
  pattern?: string;
46
- style: string;
47
- class: string;
48
- errorMessage?: string;
49
- };
50
-
51
- /**
52
- * Input component
53
- */
54
- export class Input {
55
- state: InputState;
56
- container: HTMLElement | null = null;
57
- _id: string;
58
- id: string;
59
- private _onChange?: (value: string) => void;
60
- private _onValidate?: (value: string) => boolean | string;
61
-
62
- // Store bind() instructions
63
- private _bindings: Array<{ event: string, handler: Function }> = [];
64
-
65
- // Store sync() instructions
66
- private _syncBindings: Array<{ property: string, stateObj: State<any>, toState?: Function, toComponent?: Function }> = [];
41
+ }
67
42
 
43
+ export class Input extends FormInput<InputState> {
68
44
  constructor(id: string, options: InputOptions = {}) {
69
- this._id = id;
70
- this.id = id;
71
- this._onChange = options.onChange;
72
- this._onValidate = options.onValidate;
73
-
74
- this.state = {
45
+ super(id, {
75
46
  type: options.type ?? 'text',
76
47
  value: options.value ?? '',
77
48
  placeholder: options.placeholder ?? '',
78
49
  label: options.label ?? '',
50
+ icon: options.icon ?? '',
79
51
  required: options.required ?? false,
80
52
  disabled: options.disabled ?? false,
81
53
  name: options.name ?? id,
@@ -87,13 +59,33 @@ export class Input {
87
59
  maxLength: options.maxLength,
88
60
  pattern: options.pattern,
89
61
  style: options.style ?? '',
90
- class: options.class ?? ''
91
- };
62
+ class: options.class ?? '',
63
+ errorMessage: undefined
64
+ });
65
+
66
+ if (options.onValidate) {
67
+ this._onValidate = options.onValidate;
68
+ }
69
+ }
70
+
71
+ protected getTriggerEvents(): readonly string[] {
72
+ return TRIGGER_EVENTS;
73
+ }
74
+
75
+ protected getCallbackEvents(): readonly string[] {
76
+ return CALLBACK_EVENTS;
92
77
  }
93
78
 
94
- /* -------------------------
95
- * Fluent API
96
- * ------------------------- */
79
+ /* ═════════════════════════════════════════════════════════════════
80
+ * FLUENT API
81
+ * ═════════════════════════════════════════════════════════════════ */
82
+
83
+ // ✅ Inherited from FormInput/BaseComponent:
84
+ // - label(), required(), name(), onValidate()
85
+ // - validate(), isValid()
86
+ // - style(), class()
87
+ // - bind(), sync(), renderTo()
88
+ // - disabled(), enable(), disable()
97
89
 
98
90
  type(value: string): this {
99
91
  this.state.type = value;
@@ -101,9 +93,7 @@ export class Input {
101
93
  }
102
94
 
103
95
  value(value: string | number): this {
104
- this.state.value = String(value);
105
- this._updateElement();
106
- return this;
96
+ return this.setValue(String(value));
107
97
  }
108
98
 
109
99
  placeholder(value: string): this {
@@ -111,24 +101,8 @@ export class Input {
111
101
  return this;
112
102
  }
113
103
 
114
- label(value: string): this {
115
- this.state.label = value;
116
- return this;
117
- }
118
-
119
- required(value: boolean): this {
120
- this.state.required = value;
121
- return this;
122
- }
123
-
124
- disabled(value: boolean): this {
125
- this.state.disabled = value;
126
- this._updateElement();
127
- return this;
128
- }
129
-
130
- name(value: string): this {
131
- this.state.name = value;
104
+ icon(value: string): this {
105
+ this.state.icon = value;
132
106
  return this;
133
107
  }
134
108
 
@@ -139,86 +113,56 @@ export class Input {
139
113
 
140
114
  min(value: number): this {
141
115
  this.state.min = value;
142
- this._updateElement();
143
116
  return this;
144
117
  }
145
118
 
146
119
  max(value: number): this {
147
120
  this.state.max = value;
148
- this._updateElement();
149
121
  return this;
150
122
  }
151
123
 
152
124
  step(value: number): this {
153
125
  this.state.step = value;
154
- this._updateElement();
155
126
  return this;
156
127
  }
157
128
 
158
129
  minLength(value: number): this {
159
130
  this.state.minLength = value;
160
- this._updateElement();
161
131
  return this;
162
132
  }
163
133
 
164
134
  maxLength(value: number): this {
165
135
  this.state.maxLength = value;
166
- this._updateElement();
167
136
  return this;
168
137
  }
169
138
 
170
139
  pattern(value: string): this {
171
140
  this.state.pattern = value;
172
- this._updateElement();
173
- return this;
174
- }
175
-
176
- style(value: string): this {
177
- this.state.style = value;
178
141
  return this;
179
142
  }
180
143
 
181
- class(value: string): this {
182
- this.state.class = value;
183
- return this;
184
- }
185
-
186
- onChange(handler: (value: string) => void): this {
187
- this._onChange = handler;
188
- return this;
189
- }
144
+ /* ═════════════════════════════════════════════════════════════════
145
+ * FORM INPUT IMPLEMENTATION
146
+ * ═════════════════════════════════════════════════════════════════ */
190
147
 
191
- onValidate(handler: (value: string) => boolean | string): this {
192
- this._onValidate = handler;
193
- return this;
148
+ getValue(): string {
149
+ return this.state.value;
194
150
  }
195
151
 
196
- /**
197
- * Bind event handler (stores for wiring in render)
198
- */
199
- bind(event: string, handler: Function): this {
200
- this._bindings.push({ event, handler });
152
+ setValue(value: string): this {
153
+ this.state.value = value;
154
+ if (this._inputElement) {
155
+ (this._inputElement as HTMLInputElement | HTMLTextAreaElement).value = value;
156
+ }
201
157
  return this;
202
158
  }
203
159
 
204
- /**
205
- * Two-way sync with state (stores for wiring in render)
206
- *
207
- * @param property - Component property to sync ('value', 'label', etc)
208
- * @param stateObj - State object to sync with
209
- * @param toState - Optional transform function when going from component to state
210
- * @param toComponent - Optional transform function when going from state to component
211
- */
212
- sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
213
- this._syncBindings.push({ property, stateObj, toState, toComponent });
214
- return this;
160
+ getNumericValue(): number | null {
161
+ const num = Number(this.state.value);
162
+ return isNaN(num) ? null : num;
215
163
  }
216
164
 
217
- /* -------------------------
218
- * Validation
219
- * ------------------------- */
220
-
221
- private _validate(value: string): boolean | string {
165
+ protected _validateValue(value: string): boolean | string {
222
166
  const { required, type, min, max, minLength, maxLength, pattern } = this.state;
223
167
 
224
168
  if (required && !value.trim()) {
@@ -268,137 +212,9 @@ export class Input {
268
212
  return true;
269
213
  }
270
214
 
271
- private _showError(message: string): void {
272
- const errorEl = document.getElementById(`${this._id}-error`);
273
- const inputEl = document.getElementById(`${this._id}-input`);
274
-
275
- if (errorEl) {
276
- errorEl.textContent = message;
277
- errorEl.style.display = 'block';
278
- }
279
-
280
- if (inputEl) {
281
- inputEl.classList.add('jux-input-invalid');
282
- }
283
-
284
- this.state.errorMessage = message;
285
- }
286
-
287
- private _clearError(): void {
288
- const errorEl = document.getElementById(`${this._id}-error`);
289
- const inputEl = document.getElementById(`${this._id}-input`);
290
-
291
- if (errorEl) {
292
- errorEl.textContent = '';
293
- errorEl.style.display = 'none';
294
- }
295
-
296
- if (inputEl) {
297
- inputEl.classList.remove('jux-input-invalid');
298
- }
299
-
300
- this.state.errorMessage = undefined;
301
- }
302
-
303
- validate(): boolean {
304
- const result = this._validate(this.state.value);
305
-
306
- if (result === true) {
307
- this._clearError();
308
- return true;
309
- } else {
310
- this._showError(result as string);
311
- return false;
312
- }
313
- }
314
-
315
- /* -------------------------
316
- * Helpers
317
- * ------------------------- */
318
-
319
- private _updateElement(): void {
320
- const input = document.getElementById(`${this._id}-input`) as HTMLInputElement | HTMLTextAreaElement;
321
- if (input) {
322
- input.value = this.state.value;
323
- input.disabled = this.state.disabled;
324
-
325
- if (input instanceof HTMLInputElement) {
326
- if (this.state.min !== undefined) input.min = String(this.state.min);
327
- if (this.state.max !== undefined) input.max = String(this.state.max);
328
- if (this.state.step !== undefined) input.step = String(this.state.step);
329
- if (this.state.minLength !== undefined) input.minLength = this.state.minLength;
330
- if (this.state.maxLength !== undefined) input.maxLength = this.state.maxLength;
331
- if (this.state.pattern) input.pattern = this.state.pattern;
332
- }
333
-
334
- if (input instanceof HTMLTextAreaElement) {
335
- if (this.state.minLength !== undefined) input.minLength = this.state.minLength;
336
- if (this.state.maxLength !== undefined) input.maxLength = this.state.maxLength;
337
- }
338
- }
339
- }
340
-
341
- getValue(): string {
342
- return this.state.value;
343
- }
344
-
345
- getNumericValue(): number | null {
346
- const num = Number(this.state.value);
347
- return isNaN(num) ? null : num;
348
- }
349
-
350
- isValid(): boolean {
351
- return this._validate(this.state.value) === true;
352
- }
353
-
354
- /* -------------------------
355
- * Render
356
- * ------------------------- */
357
-
358
- render(targetId?: string): this {
359
- let container: HTMLElement;
215
+ protected _buildInputElement(): HTMLElement {
216
+ const { type, value, placeholder, required, disabled, name, rows, min, max, step, minLength, maxLength, pattern } = this.state;
360
217
 
361
- if (targetId) {
362
- const target = document.querySelector(targetId);
363
- if (!target || !(target instanceof HTMLElement)) {
364
- throw new Error(`Input: Target element "${targetId}" not found`);
365
- }
366
- container = target;
367
- } else {
368
- container = getOrCreateContainer(this._id);
369
- }
370
-
371
- this.container = container;
372
- const { type, value, placeholder, label, required, disabled, name, rows, min, max, step, minLength, maxLength, pattern, style, class: className } = this.state;
373
-
374
- const wrapper = document.createElement('div');
375
- wrapper.className = 'jux-input';
376
- wrapper.id = this._id;
377
-
378
- if (className) {
379
- wrapper.className += ` ${className}`;
380
- }
381
-
382
- if (style) {
383
- wrapper.setAttribute('style', style);
384
- }
385
-
386
- // Label
387
- const labelEl = document.createElement('label');
388
- labelEl.className = 'jux-input-label';
389
- labelEl.htmlFor = `${this._id}-input`;
390
- labelEl.textContent = label;
391
- if (required) {
392
- const requiredSpan = document.createElement('span');
393
- requiredSpan.className = 'jux-input-required';
394
- requiredSpan.textContent = ' *';
395
- labelEl.appendChild(requiredSpan);
396
- }
397
- if (label) {
398
- wrapper.appendChild(labelEl);
399
- }
400
-
401
- // Input/Textarea
402
218
  let inputEl: HTMLInputElement | HTMLTextAreaElement;
403
219
 
404
220
  if (type === 'textarea') {
@@ -431,173 +247,157 @@ export class Input {
431
247
  inputEl.required = required;
432
248
  inputEl.disabled = disabled;
433
249
 
434
- // Input event handler - from onChange option
435
- inputEl.addEventListener('input', (e) => {
436
- const target = e.target as HTMLInputElement | HTMLTextAreaElement;
437
- this.state.value = target.value;
250
+ return inputEl;
251
+ }
438
252
 
439
- this._clearError();
253
+ /* ═════════════════════════════════════════════════════════════════
254
+ * RENDER
255
+ * ═════════════════════════════════════════════════════════════════ */
440
256
 
441
- if (this._onChange) {
442
- this._onChange(target.value);
443
- }
444
- });
257
+ render(targetId?: string): this {
258
+ const container = this._setupContainer(targetId);
445
259
 
446
- inputEl.addEventListener('blur', () => {
447
- this.validate();
448
- });
260
+ const { icon, maxLength, type, style, class: className } = this.state;
261
+
262
+ // Build wrapper
263
+ const wrapper = document.createElement('div');
264
+ wrapper.className = 'jux-input';
265
+ wrapper.id = this._id;
266
+ if (className) wrapper.className += ` ${className}`;
267
+ if (style) wrapper.setAttribute('style', style);
449
268
 
450
- wrapper.appendChild(inputEl);
269
+ // Label
270
+ if (this.state.label) {
271
+ wrapper.appendChild(this._renderLabel());
272
+ }
451
273
 
452
- // Error message element
453
- const errorEl = document.createElement('div');
454
- errorEl.className = 'jux-input-error';
455
- errorEl.id = `${this._id}-error`;
456
- errorEl.style.display = 'none';
457
- wrapper.appendChild(errorEl);
274
+ // Input container
275
+ const inputContainer = document.createElement('div');
276
+ inputContainer.className = 'jux-input-container';
277
+ if (icon) inputContainer.classList.add('jux-input-with-icon');
278
+
279
+ // Icon
280
+ if (icon) {
281
+ const iconEl = document.createElement('span');
282
+ iconEl.className = 'jux-input-icon';
283
+ iconEl.appendChild(renderIcon(icon));
284
+ inputContainer.appendChild(iconEl);
285
+ }
458
286
 
459
- // Character counter for maxLength
287
+ // Input element
288
+ const inputEl = this._buildInputElement();
289
+ this._inputElement = inputEl;
290
+ inputContainer.appendChild(inputEl);
291
+ wrapper.appendChild(inputContainer);
292
+
293
+ // Error element
294
+ wrapper.appendChild(this._renderError());
295
+
296
+ // Character counter
460
297
  if (maxLength && (type === 'text' || type === 'textarea')) {
461
298
  const counterEl = document.createElement('div');
462
299
  counterEl.className = 'jux-input-counter';
463
300
  counterEl.id = `${this._id}-counter`;
464
- counterEl.textContent = `${value.length}/${maxLength}`;
301
+ counterEl.textContent = `${this.state.value.length}/${maxLength}`;
302
+ wrapper.appendChild(counterEl);
465
303
 
466
304
  inputEl.addEventListener('input', () => {
467
- counterEl.textContent = `${inputEl.value.length}/${maxLength}`;
305
+ const input = inputEl as HTMLInputElement | HTMLTextAreaElement;
306
+ counterEl.textContent = `${input.value.length}/${maxLength}`;
307
+ this.state.value = input.value;
308
+
309
+ // 🎯 Fire the input callback event
310
+ this._triggerCallback('input', input.value);
468
311
  });
312
+ } else {
313
+ // Fire input event even without counter
314
+ inputEl.addEventListener('input', () => {
315
+ const input = inputEl as HTMLInputElement | HTMLTextAreaElement;
316
+ this.state.value = input.value;
469
317
 
470
- wrapper.appendChild(counterEl);
318
+ // 🎯 Fire the input callback event
319
+ this._triggerCallback('input', input.value);
320
+ });
471
321
  }
472
322
 
473
- container.appendChild(wrapper);
474
- this._injectDefaultStyles();
475
-
476
- // === Wire up event bindings ===
477
- this._bindings.forEach(({ event, handler }) => {
478
- wrapper.addEventListener(event, handler as EventListener);
323
+ // Fire change event on blur
324
+ inputEl.addEventListener('change', () => {
325
+ const input = inputEl as HTMLInputElement | HTMLTextAreaElement;
326
+ // 🎯 Fire the change callback event
327
+ this._triggerCallback('change', input.value);
479
328
  });
480
329
 
481
- // === Wire up sync bindings (TWO-WAY) ===
482
- this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
483
- if (property === 'value') {
484
- // Default transforms
485
- const transformToState = toState || ((v: string) => {
486
- return type === 'number' ? (parseInt(v) || 0) : v;
487
- });
488
- const transformToComponent = toComponent || ((v: any) => String(v));
489
-
490
- let isUpdating = false;
491
-
492
- // State → Input (when state changes, update input)
493
- stateObj.subscribe((val: any) => {
494
- if (isUpdating) return;
495
- const transformed = transformToComponent(val);
496
- if (inputEl.value !== transformed) {
497
- inputEl.value = transformed;
498
- this.state.value = transformed;
499
- }
500
- });
501
-
502
- // Input → State (when input changes, update state)
503
- inputEl.addEventListener('input', () => {
504
- if (isUpdating) return;
505
- isUpdating = true;
506
- const transformed = transformToState(inputEl.value);
507
- stateObj.set(transformed);
508
- setTimeout(() => { isUpdating = false; }, 0);
509
- });
510
- }
511
- else if (property === 'label') {
512
- // Sync label
513
- const transformToComponent = toComponent || ((v: any) => String(v));
514
-
515
- stateObj.subscribe((val: any) => {
516
- const transformed = transformToComponent(val);
517
- labelEl.textContent = transformed;
518
- this.state.label = transformed;
519
- });
330
+ // Wire events
331
+ this._wireStandardEvents(wrapper);
332
+ this._wireFormSync(inputEl, 'input');
333
+
334
+ // Sync label changes
335
+ const labelSync = this._syncBindings.find(b => b.property === 'label');
336
+ if (labelSync) {
337
+ const transform = labelSync.toComponent || ((v: any) => String(v));
338
+ labelSync.stateObj.subscribe((val: any) => {
339
+ this.label(transform(val));
340
+ });
341
+ }
342
+
343
+ container.appendChild(wrapper);
344
+ this._injectFormStyles();
345
+
346
+ requestAnimationFrame(() => {
347
+ if ((window as any).lucide) {
348
+ (window as any).lucide.createIcons();
520
349
  }
521
350
  });
522
351
 
523
352
  return this;
524
353
  }
354
+ }
525
355
 
526
- private _injectDefaultStyles(): void {
527
- const styleId = 'jux-input-styles';
528
- if (document.getElementById(styleId)) return;
529
-
530
- const style = document.createElement('style');
531
- style.id = styleId;
532
- style.textContent = `
533
- .jux-input {
534
- margin-bottom: 16px;
535
- }
356
+ export function input(id: string, options: InputOptions = {}): Input {
357
+ return new Input(id, options);
358
+ }
536
359
 
537
- .jux-input-label {
538
- display: block;
539
- margin-bottom: 6px;
540
- font-weight: 500;
541
- color: #374151;
542
- }
360
+ // Shorter, consistent naming
361
+ export function text(id: string, options?: Omit<InputOptions, 'type'>): Input {
362
+ return new Input(id, { ...options, type: 'text' });
363
+ }
543
364
 
544
- .jux-input-required {
545
- color: #ef4444;
546
- }
365
+ export function number(id: string, options?: Omit<InputOptions, 'type'>): Input {
366
+ return new Input(id, { ...options, type: 'number' });
367
+ }
547
368
 
548
- .jux-input-element {
549
- width: 100%;
550
- padding: 8px 12px;
551
- border: 1px solid #d1d5db;
552
- border-radius: 6px;
553
- font-size: 14px;
554
- transition: border-color 0.2s;
555
- box-sizing: border-box;
556
- }
369
+ export function email(id: string, options?: Omit<InputOptions, 'type'>): Input {
370
+ return new Input(id, { ...options, type: 'email' });
371
+ }
557
372
 
558
- .jux-input-element:focus {
559
- outline: none;
560
- border-color: #3b82f6;
561
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
562
- }
373
+ export function password(id: string, options?: Omit<InputOptions, 'type'>): Input {
374
+ return new Input(id, { ...options, type: 'password' });
375
+ }
563
376
 
564
- .jux-input-element:disabled {
565
- background-color: #f3f4f6;
566
- cursor: not-allowed;
567
- }
377
+ export function tel(id: string, options?: Omit<InputOptions, 'type'>): Input {
378
+ return new Input(id, { ...options, type: 'tel' });
379
+ }
568
380
 
569
- .jux-input-element.jux-input-invalid {
570
- border-color: #ef4444;
571
- }
381
+ export function url(id: string, options?: Omit<InputOptions, 'type'>): Input {
382
+ return new Input(id, { ...options, type: 'url' });
383
+ }
572
384
 
573
- .jux-input-element.jux-input-invalid:focus {
574
- box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
575
- }
385
+ export function textarea(id: string, options?: Omit<InputOptions, 'type'>): Input {
386
+ return new Input(id, { ...options, type: 'textarea' });
387
+ }
576
388
 
577
- .jux-input-error {
578
- color: #ef4444;
579
- font-size: 12px;
580
- margin-top: 4px;
581
- }
389
+ export function range(id: string, options?: Omit<InputOptions, 'type'>): Input {
390
+ return new Input(id, { ...options, type: 'range' });
391
+ }
582
392
 
583
- .jux-input-counter {
584
- text-align: right;
585
- font-size: 12px;
586
- color: #6b7280;
587
- margin-top: 4px;
588
- }
589
- `;
590
- document.head.appendChild(style);
591
- }
393
+ export function date(id: string, options?: Omit<InputOptions, 'type'>): Input {
394
+ return new Input(id, { ...options, type: 'date' });
395
+ }
592
396
 
593
- renderTo(juxComponent: any): this {
594
- if (!juxComponent?._id) {
595
- throw new Error('Input.renderTo: Invalid component');
596
- }
597
- return this.render(`#${juxComponent._id}`);
598
- }
397
+ export function time(id: string, options?: Omit<InputOptions, 'type'>): Input {
398
+ return new Input(id, { ...options, type: 'time' });
599
399
  }
600
400
 
601
- export function input(id: string, options: InputOptions = {}): Input {
602
- return new Input(id, options);
401
+ export function color(id: string, options?: Omit<InputOptions, 'type'>): Input {
402
+ return new Input(id, { ...options, type: 'color' });
603
403
  }