juxscript 1.0.3 → 1.0.5

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 (73) hide show
  1. package/README.md +37 -92
  2. package/bin/cli.js +57 -56
  3. package/lib/components/alert.ts +240 -0
  4. package/lib/components/app.ts +216 -82
  5. package/lib/components/badge.ts +164 -0
  6. package/lib/components/barchart.ts +1248 -0
  7. package/lib/components/button.ts +188 -53
  8. package/lib/components/card.ts +75 -61
  9. package/lib/components/chart.ts +17 -15
  10. package/lib/components/checkbox.ts +199 -0
  11. package/lib/components/code.ts +66 -152
  12. package/lib/components/container.ts +104 -208
  13. package/lib/components/data.ts +1 -3
  14. package/lib/components/datepicker.ts +226 -0
  15. package/lib/components/dialog.ts +258 -0
  16. package/lib/components/docs-data.json +1969 -423
  17. package/lib/components/dropdown.ts +244 -0
  18. package/lib/components/element.ts +271 -0
  19. package/lib/components/fileupload.ts +319 -0
  20. package/lib/components/footer.ts +37 -18
  21. package/lib/components/header.ts +53 -33
  22. package/lib/components/heading.ts +119 -0
  23. package/lib/components/helpers.ts +34 -0
  24. package/lib/components/hero.ts +57 -31
  25. package/lib/components/include.ts +292 -0
  26. package/lib/components/input.ts +508 -77
  27. package/lib/components/layout.ts +144 -18
  28. package/lib/components/list.ts +83 -74
  29. package/lib/components/loading.ts +263 -0
  30. package/lib/components/main.ts +43 -17
  31. package/lib/components/menu.ts +108 -24
  32. package/lib/components/modal.ts +50 -21
  33. package/lib/components/nav.ts +60 -18
  34. package/lib/components/paragraph.ts +111 -0
  35. package/lib/components/progress.ts +276 -0
  36. package/lib/components/radio.ts +236 -0
  37. package/lib/components/req.ts +300 -0
  38. package/lib/components/script.ts +33 -74
  39. package/lib/components/select.ts +280 -0
  40. package/lib/components/sidebar.ts +87 -37
  41. package/lib/components/style.ts +47 -70
  42. package/lib/components/switch.ts +261 -0
  43. package/lib/components/table.ts +47 -24
  44. package/lib/components/tabs.ts +105 -63
  45. package/lib/components/theme-toggle.ts +361 -0
  46. package/lib/components/token-calculator.ts +380 -0
  47. package/lib/components/tooltip.ts +244 -0
  48. package/lib/components/view.ts +36 -20
  49. package/lib/components/write.ts +284 -0
  50. package/lib/globals.d.ts +21 -0
  51. package/lib/jux.ts +178 -68
  52. package/lib/presets/notion.css +521 -0
  53. package/lib/presets/notion.jux +27 -0
  54. package/lib/reactivity/state.ts +364 -0
  55. package/lib/themes/charts.js +126 -0
  56. package/machinery/compiler.js +126 -38
  57. package/machinery/generators/html.js +2 -3
  58. package/machinery/server.js +2 -2
  59. package/package.json +29 -3
  60. package/lib/components/import.ts +0 -430
  61. package/lib/components/node.ts +0 -200
  62. package/lib/components/reactivity.js +0 -104
  63. package/lib/components/theme.ts +0 -97
  64. package/lib/layouts/notion.css +0 -258
  65. package/lib/styles/base-theme.css +0 -186
  66. package/lib/styles/dark-theme.css +0 -144
  67. package/lib/styles/light-theme.css +0 -144
  68. package/lib/styles/tokens/dark.css +0 -86
  69. package/lib/styles/tokens/light.css +0 -86
  70. package/lib/templates/index.juxt +0 -33
  71. package/lib/themes/dark.css +0 -86
  72. package/lib/themes/light.css +0 -86
  73. /package/lib/{styles → presets}/global.css +0 -0
@@ -1,16 +1,28 @@
1
- import { Reactive, getOrCreateContainer } from './reactivity.js';
1
+ import { getOrCreateContainer } from './helpers.js';
2
+ import { State } from '../reactivity/state.js';
2
3
 
3
4
  /**
4
5
  * Input component options
5
6
  */
6
7
  export interface InputOptions {
7
- type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search';
8
- label?: string;
9
- placeholder?: string;
8
+ type?: string;
10
9
  value?: string;
10
+ placeholder?: string;
11
+ label?: string;
11
12
  required?: boolean;
12
13
  disabled?: boolean;
14
+ name?: string;
15
+ rows?: number;
16
+ min?: number;
17
+ max?: number;
18
+ step?: number;
19
+ minLength?: number;
20
+ maxLength?: number;
21
+ pattern?: string;
13
22
  onChange?: (value: string) => void;
23
+ onValidate?: (value: string) => boolean | string;
24
+ style?: string;
25
+ class?: string;
14
26
  }
15
27
 
16
28
  /**
@@ -18,43 +30,82 @@ export interface InputOptions {
18
30
  */
19
31
  type InputState = {
20
32
  type: string;
21
- label: string;
22
- placeholder: string;
23
33
  value: string;
34
+ placeholder: string;
35
+ label: string;
24
36
  required: boolean;
25
37
  disabled: boolean;
26
- onChange: ((value: string) => void) | null;
38
+ name: string;
39
+ rows: number;
40
+ min?: number;
41
+ max?: number;
42
+ step?: number;
43
+ minLength?: number;
44
+ maxLength?: number;
45
+ pattern?: string;
46
+ style: string;
47
+ class: string;
48
+ errorMessage?: string;
27
49
  };
28
50
 
29
51
  /**
30
- * Input component
52
+ * Input component - text input and textarea with validation
31
53
  *
32
54
  * Usage:
33
- * const input = jux.input('myInput', {
34
- * label: 'Email',
35
- * type: 'email',
36
- * placeholder: 'Enter your email',
37
- * onChange: (value) => console.log(value)
38
- * });
39
- * input.render();
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');
40
75
  */
41
- export class Input extends Reactive {
42
- state!: InputState;
76
+ export class Input {
77
+ state: InputState;
43
78
  container: HTMLElement | null = null;
44
-
45
- constructor(componentId: string, options: InputOptions = {}) {
46
- super();
47
- this._setComponentId(componentId);
48
-
49
- this.state = this._createReactiveState({
79
+ _id: string;
80
+ id: string;
81
+ private _onChange?: (value: string) => void;
82
+ private _onValidate?: (value: string) => boolean | string;
83
+ private _boundState?: State<string>;
84
+
85
+ constructor(id: string, options: InputOptions = {}) {
86
+ this._id = id;
87
+ this.id = id;
88
+ this._onChange = options.onChange;
89
+ this._onValidate = options.onValidate;
90
+
91
+ this.state = {
50
92
  type: options.type ?? 'text',
51
- label: options.label ?? '',
52
- placeholder: options.placeholder ?? '',
53
93
  value: options.value ?? '',
94
+ placeholder: options.placeholder ?? '',
95
+ label: options.label ?? '',
54
96
  required: options.required ?? false,
55
97
  disabled: options.disabled ?? false,
56
- onChange: options.onChange ?? null
57
- }) as InputState;
98
+ name: options.name ?? id,
99
+ rows: options.rows ?? 3,
100
+ min: options.min,
101
+ max: options.max,
102
+ step: options.step,
103
+ minLength: options.minLength,
104
+ maxLength: options.maxLength,
105
+ pattern: options.pattern,
106
+ style: options.style ?? '',
107
+ class: options.class ?? ''
108
+ };
58
109
  }
59
110
 
60
111
  /* -------------------------
@@ -66,8 +117,9 @@ export class Input extends Reactive {
66
117
  return this;
67
118
  }
68
119
 
69
- label(value: string): this {
70
- this.state.label = value;
120
+ value(value: string | number): this {
121
+ this.state.value = String(value);
122
+ this._updateElement();
71
123
  return this;
72
124
  }
73
125
 
@@ -76,8 +128,8 @@ export class Input extends Reactive {
76
128
  return this;
77
129
  }
78
130
 
79
- value(value: string): this {
80
- this.state.value = value;
131
+ label(value: string): this {
132
+ this.state.label = value;
81
133
  return this;
82
134
  }
83
135
 
@@ -88,21 +140,268 @@ export class Input extends Reactive {
88
140
 
89
141
  disabled(value: boolean): this {
90
142
  this.state.disabled = value;
143
+ this._updateElement();
144
+ return this;
145
+ }
146
+
147
+ name(value: string): this {
148
+ this.state.name = value;
149
+ return this;
150
+ }
151
+
152
+ rows(value: number): this {
153
+ this.state.rows = value;
154
+ return this;
155
+ }
156
+
157
+ /**
158
+ * Minimum value for number inputs
159
+ */
160
+ min(value: number): this {
161
+ this.state.min = value;
162
+ this._updateElement();
163
+ return this;
164
+ }
165
+
166
+ /**
167
+ * Maximum value for number inputs
168
+ */
169
+ max(value: number): this {
170
+ this.state.max = value;
171
+ this._updateElement();
172
+ return this;
173
+ }
174
+
175
+ /**
176
+ * Step value for number inputs
177
+ */
178
+ step(value: number): this {
179
+ this.state.step = value;
180
+ this._updateElement();
181
+ return this;
182
+ }
183
+
184
+ /**
185
+ * Minimum length for text inputs
186
+ */
187
+ minLength(value: number): this {
188
+ this.state.minLength = value;
189
+ this._updateElement();
190
+ return this;
191
+ }
192
+
193
+ /**
194
+ * Maximum length for text inputs
195
+ */
196
+ maxLength(value: number): this {
197
+ this.state.maxLength = value;
198
+ this._updateElement();
91
199
  return this;
92
200
  }
93
201
 
94
- onChange(callback: (value: string) => void): this {
95
- this.state.onChange = callback;
202
+ /**
203
+ * Pattern validation for text inputs (regex)
204
+ */
205
+ pattern(value: string): this {
206
+ this.state.pattern = value;
207
+ this._updateElement();
208
+ return this;
209
+ }
210
+
211
+ style(value: string): this {
212
+ this.state.style = value;
213
+ return this;
214
+ }
215
+
216
+ class(value: string): this {
217
+ this.state.class = value;
218
+ return this;
219
+ }
220
+
221
+ onChange(handler: (value: string) => void): this {
222
+ this._onChange = handler;
223
+ return this;
224
+ }
225
+
226
+ /**
227
+ * Custom validation function
228
+ * Should return true if valid, or an error message string if invalid
229
+ */
230
+ onValidate(handler: (value: string) => boolean | string): this {
231
+ this._onValidate = handler;
232
+ return this;
233
+ }
234
+
235
+ /**
236
+ * Two-way binding to state
237
+ */
238
+ bind(stateObj: State<string>): this {
239
+ this._boundState = stateObj;
240
+
241
+ // Subscribe to state changes
242
+ stateObj.subscribe((val) => {
243
+ this.state.value = val;
244
+ this._updateElement();
245
+ });
246
+
247
+ // Update state on input change
248
+ this.onChange((value) => stateObj.set(value));
249
+
96
250
  return this;
97
251
  }
98
252
 
253
+ /* -------------------------
254
+ * Validation
255
+ * ------------------------- */
256
+
257
+ private _validate(value: string): boolean | string {
258
+ const { required, type, min, max, minLength, maxLength, pattern } = this.state;
259
+
260
+ // Required check
261
+ if (required && !value.trim()) {
262
+ return 'This field is required';
263
+ }
264
+
265
+ // Number validation
266
+ if (type === 'number' && value) {
267
+ const numValue = Number(value);
268
+
269
+ if (isNaN(numValue)) {
270
+ return 'Must be a valid number';
271
+ }
272
+
273
+ if (min !== undefined && numValue < min) {
274
+ return `Must be at least ${min}`;
275
+ }
276
+
277
+ if (max !== undefined && numValue > max) {
278
+ return `Must be at most ${max}`;
279
+ }
280
+ }
281
+
282
+ // Text length validation
283
+ if ((type === 'text' || type === 'textarea') && value) {
284
+ if (minLength !== undefined && value.length < minLength) {
285
+ return `Must be at least ${minLength} characters`;
286
+ }
287
+
288
+ if (maxLength !== undefined && value.length > maxLength) {
289
+ return `Must be at most ${maxLength} characters`;
290
+ }
291
+ }
292
+
293
+ // Pattern validation
294
+ if (pattern && value) {
295
+ const regex = new RegExp(pattern);
296
+ if (!regex.test(value)) {
297
+ return 'Invalid format';
298
+ }
299
+ }
300
+
301
+ // Custom validation
302
+ if (this._onValidate) {
303
+ const result = this._onValidate(value);
304
+ if (result !== true) {
305
+ return result || 'Invalid value';
306
+ }
307
+ }
308
+
309
+ return true;
310
+ }
311
+
312
+ private _showError(message: string): void {
313
+ const errorEl = document.getElementById(`${this._id}-error`);
314
+ const inputEl = document.getElementById(`${this._id}-input`);
315
+
316
+ if (errorEl) {
317
+ errorEl.textContent = message;
318
+ errorEl.style.display = 'block';
319
+ }
320
+
321
+ if (inputEl) {
322
+ inputEl.classList.add('jux-input-invalid');
323
+ }
324
+
325
+ this.state.errorMessage = message;
326
+ }
327
+
328
+ private _clearError(): void {
329
+ const errorEl = document.getElementById(`${this._id}-error`);
330
+ const inputEl = document.getElementById(`${this._id}-input`);
331
+
332
+ if (errorEl) {
333
+ errorEl.textContent = '';
334
+ errorEl.style.display = 'none';
335
+ }
336
+
337
+ if (inputEl) {
338
+ inputEl.classList.remove('jux-input-invalid');
339
+ }
340
+
341
+ this.state.errorMessage = undefined;
342
+ }
343
+
344
+ /**
345
+ * Manually validate the current value
346
+ */
347
+ validate(): boolean {
348
+ const result = this._validate(this.state.value);
349
+
350
+ if (result === true) {
351
+ this._clearError();
352
+ return true;
353
+ } else {
354
+ this._showError(result as string);
355
+ return false;
356
+ }
357
+ }
358
+
359
+ /* -------------------------
360
+ * Helpers
361
+ * ------------------------- */
362
+
363
+ private _updateElement(): void {
364
+ const input = document.getElementById(`${this._id}-input`) as HTMLInputElement | HTMLTextAreaElement;
365
+ if (input) {
366
+ input.value = this.state.value;
367
+ input.disabled = this.state.disabled;
368
+
369
+ if (input instanceof HTMLInputElement) {
370
+ if (this.state.min !== undefined) input.min = String(this.state.min);
371
+ if (this.state.max !== undefined) input.max = String(this.state.max);
372
+ if (this.state.step !== undefined) input.step = String(this.state.step);
373
+ if (this.state.minLength !== undefined) input.minLength = this.state.minLength;
374
+ if (this.state.maxLength !== undefined) input.maxLength = this.state.maxLength;
375
+ if (this.state.pattern) input.pattern = this.state.pattern;
376
+ }
377
+
378
+ if (input instanceof HTMLTextAreaElement) {
379
+ if (this.state.minLength !== undefined) input.minLength = this.state.minLength;
380
+ if (this.state.maxLength !== undefined) input.maxLength = this.state.maxLength;
381
+ }
382
+ }
383
+ }
384
+
385
+ getValue(): string {
386
+ return this.state.value;
387
+ }
388
+
389
+ getNumericValue(): number | null {
390
+ const num = Number(this.state.value);
391
+ return isNaN(num) ? null : num;
392
+ }
393
+
394
+ isValid(): boolean {
395
+ return this._validate(this.state.value) === true;
396
+ }
397
+
99
398
  /* -------------------------
100
399
  * Render
101
400
  * ------------------------- */
102
401
 
103
402
  render(targetId?: string): this {
104
403
  let container: HTMLElement;
105
-
404
+
106
405
  if (targetId) {
107
406
  const target = document.querySelector(targetId);
108
407
  if (!target || !(target instanceof HTMLElement)) {
@@ -110,66 +409,198 @@ export class Input extends Reactive {
110
409
  }
111
410
  container = target;
112
411
  } else {
113
- container = getOrCreateContainer(this._componentId) as HTMLElement;
412
+ container = getOrCreateContainer(this._id);
114
413
  }
115
-
414
+
116
415
  this.container = container;
117
- const { type, label, placeholder, value, required, disabled, onChange } = this.state;
118
-
416
+ const { type, value, placeholder, label, required, disabled, name, rows, min, max, step, minLength, maxLength, pattern, style, class: className } = this.state;
417
+
119
418
  const wrapper = document.createElement('div');
120
- wrapper.className = 'jux-input-wrapper';
121
- wrapper.id = this._componentId;
122
-
419
+ wrapper.className = 'jux-input';
420
+ wrapper.id = this._id;
421
+
422
+ if (className) {
423
+ wrapper.className += ` ${className}`;
424
+ }
425
+
426
+ if (style) {
427
+ wrapper.setAttribute('style', style);
428
+ }
429
+
430
+ // Label
123
431
  if (label) {
124
432
  const labelEl = document.createElement('label');
125
433
  labelEl.className = 'jux-input-label';
434
+ labelEl.htmlFor = `${this._id}-input`;
126
435
  labelEl.textContent = label;
127
- labelEl.setAttribute('for', `${this._componentId}-input`);
436
+ if (required) {
437
+ const requiredSpan = document.createElement('span');
438
+ requiredSpan.className = 'jux-input-required';
439
+ requiredSpan.textContent = ' *';
440
+ labelEl.appendChild(requiredSpan);
441
+ }
128
442
  wrapper.appendChild(labelEl);
129
443
  }
130
-
131
- const input = document.createElement('input');
132
- input.id = `${this._componentId}-input`;
133
- input.className = 'jux-input';
134
- input.type = type;
135
- input.placeholder = placeholder;
136
- input.value = value;
137
- input.required = required;
138
- input.disabled = disabled;
139
-
140
- wrapper.appendChild(input);
141
- container.appendChild(wrapper);
142
-
143
- // Event binding - onChange
144
- if (onChange) {
145
- input.addEventListener('input', (e) => {
146
- const target = e.target as HTMLInputElement;
147
- onChange(target.value);
444
+
445
+ // Input/Textarea
446
+ let inputEl: HTMLInputElement | HTMLTextAreaElement;
447
+
448
+ if (type === 'textarea') {
449
+ inputEl = document.createElement('textarea');
450
+ inputEl.rows = rows;
451
+ if (minLength !== undefined) inputEl.minLength = minLength;
452
+ if (maxLength !== undefined) inputEl.maxLength = maxLength;
453
+ } else {
454
+ inputEl = document.createElement('input');
455
+ inputEl.type = type;
456
+
457
+ // Number-specific attributes
458
+ if (type === 'number') {
459
+ if (min !== undefined) inputEl.min = String(min);
460
+ if (max !== undefined) inputEl.max = String(max);
461
+ if (step !== undefined) inputEl.step = String(step);
462
+ }
463
+
464
+ // Text-specific attributes
465
+ if (type === 'text' || type === 'email' || type === 'tel' || type === 'url') {
466
+ if (minLength !== undefined) inputEl.minLength = minLength;
467
+ if (maxLength !== undefined) inputEl.maxLength = maxLength;
468
+ if (pattern) inputEl.pattern = pattern;
469
+ }
470
+ }
471
+
472
+ inputEl.className = 'jux-input-element';
473
+ inputEl.id = `${this._id}-input`;
474
+ inputEl.name = name;
475
+ inputEl.value = value;
476
+ inputEl.placeholder = placeholder;
477
+ inputEl.required = required;
478
+ inputEl.disabled = disabled;
479
+
480
+ // Input event handler
481
+ inputEl.addEventListener('input', (e) => {
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);
499
+
500
+ // Error message element
501
+ const errorEl = document.createElement('div');
502
+ errorEl.className = 'jux-input-error';
503
+ errorEl.id = `${this._id}-error`;
504
+ errorEl.style.display = 'none';
505
+ wrapper.appendChild(errorEl);
506
+
507
+ // Character counter for maxLength
508
+ if (maxLength && (type === 'text' || type === 'textarea')) {
509
+ const counterEl = document.createElement('div');
510
+ counterEl.className = 'jux-input-counter';
511
+ counterEl.id = `${this._id}-counter`;
512
+ counterEl.textContent = `${value.length}/${maxLength}`;
513
+
514
+ inputEl.addEventListener('input', () => {
515
+ counterEl.textContent = `${inputEl.value.length}/${maxLength}`;
148
516
  });
517
+
518
+ wrapper.appendChild(counterEl);
149
519
  }
150
-
520
+
521
+ container.appendChild(wrapper);
522
+
523
+ // Add default styles if not already present
524
+ this._injectDefaultStyles();
525
+
151
526
  return this;
152
527
  }
153
528
 
154
- /**
155
- * Render to another Jux component's container
156
- */
529
+ private _injectDefaultStyles(): void {
530
+ const styleId = 'jux-input-styles';
531
+ if (document.getElementById(styleId)) return;
532
+
533
+ const style = document.createElement('style');
534
+ style.id = styleId;
535
+ style.textContent = `
536
+ .jux-input {
537
+ margin-bottom: 16px;
538
+ }
539
+
540
+ .jux-input-label {
541
+ display: block;
542
+ margin-bottom: 6px;
543
+ font-weight: 500;
544
+ color: #374151;
545
+ }
546
+
547
+ .jux-input-required {
548
+ color: #ef4444;
549
+ }
550
+
551
+ .jux-input-element {
552
+ width: 100%;
553
+ padding: 8px 12px;
554
+ border: 1px solid #d1d5db;
555
+ border-radius: 6px;
556
+ font-size: 14px;
557
+ transition: border-color 0.2s;
558
+ box-sizing: border-box;
559
+ }
560
+
561
+ .jux-input-element:focus {
562
+ outline: none;
563
+ border-color: #3b82f6;
564
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
565
+ }
566
+
567
+ .jux-input-element:disabled {
568
+ background-color: #f3f4f6;
569
+ cursor: not-allowed;
570
+ }
571
+
572
+ .jux-input-element.jux-input-invalid {
573
+ border-color: #ef4444;
574
+ }
575
+
576
+ .jux-input-element.jux-input-invalid:focus {
577
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
578
+ }
579
+
580
+ .jux-input-error {
581
+ color: #ef4444;
582
+ font-size: 12px;
583
+ margin-top: 4px;
584
+ }
585
+
586
+ .jux-input-counter {
587
+ text-align: right;
588
+ font-size: 12px;
589
+ color: #6b7280;
590
+ margin-top: 4px;
591
+ }
592
+ `;
593
+ document.head.appendChild(style);
594
+ }
595
+
157
596
  renderTo(juxComponent: any): this {
158
- if (!juxComponent || typeof juxComponent !== 'object') {
159
- throw new Error('Input.renderTo: Invalid component - not an object');
597
+ if (!juxComponent?._id) {
598
+ throw new Error('Input.renderTo: Invalid component');
160
599
  }
161
-
162
- if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
163
- throw new Error('Input.renderTo: Invalid component - missing _componentId (not a Jux component)');
164
- }
165
-
166
- return this.render(`#${juxComponent._componentId}`);
600
+ return this.render(`#${juxComponent._id}`);
167
601
  }
168
602
  }
169
603
 
170
- /**
171
- * Factory helper
172
- */
173
- export function input(componentId: string, options: InputOptions = {}): Input {
174
- return new Input(componentId, options);
604
+ export function input(id: string, options: InputOptions = {}): Input {
605
+ return new Input(id, options);
175
606
  }