juxscript 1.1.80 → 1.1.81

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 (52) hide show
  1. package/dom-structure-map.json +1 -1
  2. package/index.d.ts +2 -2
  3. package/index.d.ts.map +1 -1
  4. package/index.js +2 -2
  5. package/lib/components/badge.d.ts.map +1 -1
  6. package/lib/components/badge.js +2 -1
  7. package/lib/components/badge.ts +2 -1
  8. package/lib/components/base/BaseComponent.d.ts +55 -1
  9. package/lib/components/base/BaseComponent.d.ts.map +1 -1
  10. package/lib/components/base/BaseComponent.js +168 -2
  11. package/lib/components/base/BaseComponent.ts +203 -3
  12. package/lib/components/checkbox.d.ts +5 -4
  13. package/lib/components/checkbox.d.ts.map +1 -1
  14. package/lib/components/checkbox.js +33 -16
  15. package/lib/components/checkbox.ts +39 -22
  16. package/lib/components/datepicker.d.ts +5 -4
  17. package/lib/components/datepicker.d.ts.map +1 -1
  18. package/lib/components/datepicker.js +31 -16
  19. package/lib/components/datepicker.ts +37 -22
  20. package/lib/components/dropdown.d.ts.map +1 -1
  21. package/lib/components/dropdown.js +2 -1
  22. package/lib/components/dropdown.ts +2 -1
  23. package/lib/components/fileupload.d.ts +6 -6
  24. package/lib/components/fileupload.d.ts.map +1 -1
  25. package/lib/components/fileupload.js +77 -52
  26. package/lib/components/fileupload.ts +88 -58
  27. package/lib/components/input.d.ts +5 -4
  28. package/lib/components/input.d.ts.map +1 -1
  29. package/lib/components/input.js +38 -24
  30. package/lib/components/input.ts +48 -33
  31. package/lib/components/radio.d.ts +5 -4
  32. package/lib/components/radio.d.ts.map +1 -1
  33. package/lib/components/radio.js +37 -14
  34. package/lib/components/radio.ts +40 -16
  35. package/lib/components/select.d.ts +5 -4
  36. package/lib/components/select.d.ts.map +1 -1
  37. package/lib/components/select.js +32 -11
  38. package/lib/components/select.ts +38 -16
  39. package/lib/components/switch.d.ts +5 -4
  40. package/lib/components/switch.d.ts.map +1 -1
  41. package/lib/components/switch.js +34 -11
  42. package/lib/components/switch.ts +42 -16
  43. package/lib/components/watcher.d.ts +195 -0
  44. package/lib/components/watcher.d.ts.map +1 -0
  45. package/lib/components/watcher.js +241 -0
  46. package/lib/components/watcher.ts +261 -0
  47. package/package.json +1 -1
  48. package/lib/components/base/FormInput.d.ts +0 -77
  49. package/lib/components/base/FormInput.d.ts.map +0 -1
  50. package/lib/components/base/FormInput.js +0 -171
  51. package/lib/components/base/FormInput.ts +0 -237
  52. package/lib/components/event-chain.ts +0 -31
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "totalComponents": 63,
3
- "generatedAt": "2026-02-12T02:42:54.671Z",
3
+ "generatedAt": "2026-02-12T04:07:13.437Z",
4
4
  "components": [
5
5
  {
6
6
  "file": "alert.js",
package/index.d.ts CHANGED
@@ -44,7 +44,7 @@ import { renderIcon, renderEmoji } from './lib/components/icons.js';
44
44
  import { state } from './lib/reactivity/state.js';
45
45
  import { registry } from './lib/components/registry.js';
46
46
  import { stateHistory } from './lib/components/history/StateHistory.js';
47
- import { eventChain } from './lib/components/event-chain.js';
47
+ import { watcher } from './lib/components/watcher.js';
48
48
  export { state, registry, stateHistory };
49
49
  export declare const jux: {
50
50
  alert: typeof alert;
@@ -199,6 +199,6 @@ export declare const jux: {
199
199
  renderIcon: typeof renderIcon;
200
200
  renderEmoji: typeof renderEmoji;
201
201
  getOrCreateContainer: typeof getOrCreateContainer;
202
- eventChain: typeof eventChain;
202
+ watcher: typeof watcher;
203
203
  };
204
204
  //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
package/index.js CHANGED
@@ -46,7 +46,7 @@ import { renderIcon, renderEmoji } from './lib/components/icons.js';
46
46
  import { state } from './lib/reactivity/state.js';
47
47
  import { registry } from './lib/components/registry.js'; // ✅ Import registry
48
48
  import { stateHistory } from './lib/components/history/StateHistory.js'; // ✅ Import history
49
- import { eventChain } from './lib/components/event-chain.js';
49
+ import { watcher } from './lib/components/watcher.js';
50
50
  export { state, registry, stateHistory }; // ✅ Export history
51
51
  // Utilities
52
52
  export const jux = {
@@ -97,5 +97,5 @@ export const jux = {
97
97
  renderIcon,
98
98
  renderEmoji,
99
99
  getOrCreateContainer,
100
- eventChain
100
+ watcher
101
101
  };
@@ -1 +1 @@
1
- {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["badge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAMnE,MAAM,WAAW,YAAY;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,UAAU,GAAG,SAAS,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,qBAAa,KAAM,SAAQ,aAAa,CAAC,UAAU,CAAC;gBACpC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB;IAUlD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAIhD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAwBtC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI;IAK1E,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAS1B,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CA8BrE;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,KAAK,CAEnE"}
1
+ {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["badge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAOnE,MAAM,WAAW,YAAY;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,UAAU,GAAG,SAAS,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,qBAAa,KAAM,SAAQ,aAAa,CAAC,UAAU,CAAC;gBACpC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB;IAUlD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAIhD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAwBtC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI;IAK1E,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAS1B,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CA8BrE;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,KAAK,CAEnE"}
@@ -1,11 +1,12 @@
1
1
  import { BaseComponent } from './base/BaseComponent.js';
2
+ import { formatIdAsLabel } from '../utils/formatId.js'; // ✅ Import
2
3
  // Event definitions
3
4
  const TRIGGER_EVENTS = [];
4
5
  const CALLBACK_EVENTS = [];
5
6
  export class Badge extends BaseComponent {
6
7
  constructor(id, options = {}) {
7
8
  super(id, {
8
- label: options.label ?? '',
9
+ label: options.label ?? formatIdAsLabel(id), // ✅ Auto-generate from ID
9
10
  variant: options.variant ?? 'default',
10
11
  pill: options.pill ?? false,
11
12
  style: options.style ?? '',
@@ -1,4 +1,5 @@
1
1
  import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
+ import { formatIdAsLabel } from '../utils/formatId.js'; // ✅ Import
2
3
 
3
4
  // Event definitions
4
5
  const TRIGGER_EVENTS = [] as const;
@@ -21,7 +22,7 @@ type BadgeState = BaseState & {
21
22
  export class Badge extends BaseComponent<BadgeState> {
22
23
  constructor(id: string, options: BadgeOptions = {}) {
23
24
  super(id, {
24
- label: options.label ?? '',
25
+ label: options.label ?? formatIdAsLabel(id), // ✅ Auto-generate from ID
25
26
  variant: options.variant ?? 'default',
26
27
  pill: options.pill ?? false,
27
28
  style: options.style ?? '',
@@ -1,7 +1,6 @@
1
1
  import { State } from '../../reactivity/state.js';
2
2
  /**
3
3
  * Base state interface that ALL component states must extend
4
- * Contains properties managed by BaseComponent
5
4
  */
6
5
  export interface BaseState {
7
6
  visible?: boolean;
@@ -10,6 +9,10 @@ export interface BaseState {
10
9
  class?: string;
11
10
  style?: string;
12
11
  attributes?: Record<string, string>;
12
+ label?: string;
13
+ required?: boolean;
14
+ name?: string;
15
+ errorMessage?: string;
13
16
  }
14
17
  /**
15
18
  * Abstract base class for all JUX components
@@ -38,6 +41,11 @@ export declare abstract class BaseComponent<TState extends BaseState = BaseState
38
41
  protected _triggerHandlers: Map<string, Function>;
39
42
  protected _callbackHandlers: Map<string, Function>;
40
43
  protected _isUpdatingSync: boolean;
44
+ protected _inputElement: HTMLElement | null;
45
+ protected _labelElement: HTMLLabelElement | null;
46
+ protected _errorElement: HTMLElement | null;
47
+ protected _onValidate?: (value: any) => boolean | string;
48
+ protected _hasBeenValidated: boolean;
41
49
  constructor(id: string, initialState: TState);
42
50
  protected abstract getTriggerEvents(): readonly string[];
43
51
  protected abstract getCallbackEvents(): readonly string[];
@@ -169,5 +177,51 @@ export declare abstract class BaseComponent<TState extends BaseState = BaseState
169
177
  * const myState = component.props(); // ❌ Error: props is not a function
170
178
  */
171
179
  get props(): Readonly<TState>;
180
+ /**
181
+ * Set label for form inputs
182
+ */
183
+ label(value: string): this;
184
+ /**
185
+ * Set required state for form inputs
186
+ */
187
+ required(value: boolean): this;
188
+ /**
189
+ * Set name attribute for form inputs
190
+ */
191
+ name(value: string): this;
192
+ /**
193
+ * Set custom validation handler
194
+ */
195
+ onValidate(handler: (value: any) => boolean | string): this;
196
+ /**
197
+ * Validate form input (override in form components)
198
+ */
199
+ validate(): boolean;
200
+ /**
201
+ * Check if form input is valid (override in form components)
202
+ */
203
+ isValid(): boolean;
204
+ /**
205
+ * Get current value (override in form components)
206
+ */
207
+ getValue(): any;
208
+ /**
209
+ * Set current value (override in form components)
210
+ */
211
+ setValue(value: any): this;
212
+ protected _showError(message: string): void;
213
+ protected _clearError(): void;
214
+ /**
215
+ * Build label element for form inputs
216
+ */
217
+ protected _renderLabel(): HTMLLabelElement;
218
+ /**
219
+ * Build error element for form inputs
220
+ */
221
+ protected _renderError(): HTMLElement;
222
+ /**
223
+ * Wire up two-way sync for form inputs
224
+ */
225
+ protected _wireFormSync(inputElement: HTMLElement, eventName?: string): void;
172
226
  }
173
227
  //# sourceMappingURL=BaseComponent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BaseComponent.d.ts","sourceRoot":"","sources":["BaseComponent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseComponent.d.ts","sourceRoot":"","sources":["BaseComponent.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { getOrCreateContainer } from '../helpers.js';
2
2
  import { registry } from '../registry.js';
3
- import { stateHistory } from '../history/StateHistory.js'; // ✅ Import history
3
+ import { stateHistory } from '../history/StateHistory.js';
4
+ import { formatIdAsLabel } from '../../utils/formatId.js'; // ✅ Import utility
4
5
  /**
5
6
  * Abstract base class for all JUX components
6
7
  * Provides common storage, event routing, and lifecycle methods
@@ -18,7 +19,12 @@ export class BaseComponent {
18
19
  this._syncBindings = [];
19
20
  this._triggerHandlers = new Map();
20
21
  this._callbackHandlers = new Map();
21
- this._isUpdatingSync = false; // ✅ Guard flag
22
+ this._isUpdatingSync = false;
23
+ // Form-specific protected properties (only used by form components)
24
+ this._inputElement = null;
25
+ this._labelElement = null;
26
+ this._errorElement = null;
27
+ this._hasBeenValidated = false;
22
28
  this._id = id;
23
29
  this.id = id;
24
30
  const stateWithDefaults = {
@@ -467,4 +473,164 @@ export class BaseComponent {
467
473
  get props() {
468
474
  return this.state;
469
475
  }
476
+ /* ═════════════════════════════════════════════════════════════════
477
+ * FORM INPUT API (Optional - only used by form components)
478
+ * ═════════════════════════════════════════════════════════════════ */
479
+ /**
480
+ * Set label for form inputs
481
+ */
482
+ label(value) {
483
+ this.state.label = value;
484
+ if (this._labelElement) {
485
+ const requiredSpan = this._labelElement.querySelector('.jux-input-required');
486
+ this._labelElement.textContent = value;
487
+ if (requiredSpan)
488
+ this._labelElement.appendChild(requiredSpan);
489
+ }
490
+ return this;
491
+ }
492
+ /**
493
+ * Set required state for form inputs
494
+ */
495
+ required(value) {
496
+ this.state.required = value;
497
+ return this;
498
+ }
499
+ /**
500
+ * Set name attribute for form inputs
501
+ */
502
+ name(value) {
503
+ this.state.name = value;
504
+ return this;
505
+ }
506
+ /**
507
+ * Set custom validation handler
508
+ */
509
+ onValidate(handler) {
510
+ this._onValidate = handler;
511
+ return this;
512
+ }
513
+ /**
514
+ * Validate form input (override in form components)
515
+ */
516
+ validate() {
517
+ console.warn(`${this.constructor.name}.validate() not implemented`);
518
+ return true;
519
+ }
520
+ /**
521
+ * Check if form input is valid (override in form components)
522
+ */
523
+ isValid() {
524
+ console.warn(`${this.constructor.name}.isValid() not implemented`);
525
+ return true;
526
+ }
527
+ /**
528
+ * Get current value (override in form components)
529
+ */
530
+ getValue() {
531
+ console.warn(`${this.constructor.name}.getValue() not implemented`);
532
+ return undefined;
533
+ }
534
+ /**
535
+ * Set current value (override in form components)
536
+ */
537
+ setValue(value) {
538
+ console.warn(`${this.constructor.name}.setValue() not implemented`);
539
+ return this;
540
+ }
541
+ /* ═════════════════════════════════════════════════════════════════
542
+ * FORM VALIDATION HELPERS (Protected - for form components)
543
+ * ═════════════════════════════════════════════════════════════════ */
544
+ _showError(message) {
545
+ if (this._errorElement) {
546
+ this._errorElement.textContent = message;
547
+ this._errorElement.style.display = 'block';
548
+ }
549
+ if (this._inputElement) {
550
+ this._inputElement.classList.add('jux-input-invalid');
551
+ }
552
+ this.state.errorMessage = message;
553
+ }
554
+ _clearError() {
555
+ if (this._errorElement) {
556
+ this._errorElement.textContent = '';
557
+ this._errorElement.style.display = 'none';
558
+ }
559
+ if (this._inputElement) {
560
+ this._inputElement.classList.remove('jux-input-invalid');
561
+ }
562
+ this.state.errorMessage = undefined;
563
+ }
564
+ /**
565
+ * Build label element for form inputs
566
+ */
567
+ _renderLabel() {
568
+ const label = this.state.label || formatIdAsLabel(this._id);
569
+ const required = this.state.required || false;
570
+ const labelEl = document.createElement('label');
571
+ labelEl.className = 'jux-input-label';
572
+ labelEl.htmlFor = `${this._id}-input`;
573
+ labelEl.textContent = label;
574
+ if (required) {
575
+ const requiredSpan = document.createElement('span');
576
+ requiredSpan.className = 'jux-input-required';
577
+ requiredSpan.textContent = ' *';
578
+ labelEl.appendChild(requiredSpan);
579
+ }
580
+ this._labelElement = labelEl;
581
+ return labelEl;
582
+ }
583
+ /**
584
+ * Build error element for form inputs
585
+ */
586
+ _renderError() {
587
+ const errorEl = document.createElement('div');
588
+ errorEl.className = 'jux-input-error';
589
+ errorEl.id = `${this._id}-error`;
590
+ errorEl.style.display = 'none';
591
+ this._errorElement = errorEl;
592
+ return errorEl;
593
+ }
594
+ /**
595
+ * Wire up two-way sync for form inputs
596
+ */
597
+ _wireFormSync(inputElement, eventName = 'input') {
598
+ const valueSync = this._syncBindings.find(b => b.property === 'value');
599
+ if (valueSync) {
600
+ const { stateObj, toState, toComponent } = valueSync;
601
+ const transformToState = toState || ((v) => v);
602
+ const transformToComponent = toComponent || ((v) => v);
603
+ let isUpdating = false;
604
+ // State → Component
605
+ stateObj.subscribe((val) => {
606
+ if (isUpdating)
607
+ return;
608
+ const transformed = transformToComponent(val);
609
+ this.setValue(transformed);
610
+ });
611
+ // Component → State
612
+ inputElement.addEventListener(eventName, () => {
613
+ if (isUpdating)
614
+ return;
615
+ isUpdating = true;
616
+ const value = this.getValue();
617
+ const transformed = transformToState(value);
618
+ this._clearError();
619
+ stateObj.set(transformed);
620
+ setTimeout(() => { isUpdating = false; }, 0);
621
+ });
622
+ }
623
+ else {
624
+ // Default behavior without sync
625
+ inputElement.addEventListener(eventName, () => {
626
+ this._clearError();
627
+ });
628
+ }
629
+ // Only validate on blur IF the field has been validated before
630
+ inputElement.addEventListener('blur', () => {
631
+ if (this._hasBeenValidated) {
632
+ this.validate();
633
+ }
634
+ });
635
+ }
470
636
  }
@@ -1,11 +1,11 @@
1
1
  import { State } from '../../reactivity/state.js';
2
2
  import { getOrCreateContainer } from '../helpers.js';
3
3
  import { registry } from '../registry.js';
4
- import { stateHistory } from '../history/StateHistory.js'; // ✅ Import history
4
+ import { stateHistory } from '../history/StateHistory.js';
5
+ import { formatIdAsLabel } from '../../utils/formatId.js'; // ✅ Import utility
5
6
 
6
7
  /**
7
8
  * Base state interface that ALL component states must extend
8
- * Contains properties managed by BaseComponent
9
9
  */
10
10
  export interface BaseState {
11
11
  visible?: boolean;
@@ -14,6 +14,11 @@ export interface BaseState {
14
14
  class?: string;
15
15
  style?: string;
16
16
  attributes?: Record<string, string>;
17
+ // ✅ Form-specific properties (optional for all components)
18
+ label?: string;
19
+ required?: boolean;
20
+ name?: string;
21
+ errorMessage?: string;
17
22
  }
18
23
 
19
24
  /**
@@ -42,7 +47,14 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
42
47
  }> = [];
43
48
  protected _triggerHandlers: Map<string, Function> = new Map();
44
49
  protected _callbackHandlers: Map<string, Function> = new Map();
45
- protected _isUpdatingSync: boolean = false; // ✅ Guard flag
50
+ protected _isUpdatingSync: boolean = false;
51
+
52
+ // Form-specific protected properties (only used by form components)
53
+ protected _inputElement: HTMLElement | null = null;
54
+ protected _labelElement: HTMLLabelElement | null = null;
55
+ protected _errorElement: HTMLElement | null = null;
56
+ protected _onValidate?: (value: any) => boolean | string;
57
+ protected _hasBeenValidated: boolean = false;
46
58
 
47
59
  constructor(id: string, initialState: TState) {
48
60
  this._id = id;
@@ -563,4 +575,192 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
563
575
  get props(): Readonly<TState> {
564
576
  return this.state as Readonly<TState>;
565
577
  }
578
+
579
+ /* ═════════════════════════════════════════════════════════════════
580
+ * FORM INPUT API (Optional - only used by form components)
581
+ * ═════════════════════════════════════════════════════════════════ */
582
+
583
+ /**
584
+ * Set label for form inputs
585
+ */
586
+ label(value: string): this {
587
+ (this.state as any).label = value;
588
+ if (this._labelElement) {
589
+ const requiredSpan = this._labelElement.querySelector('.jux-input-required');
590
+ this._labelElement.textContent = value;
591
+ if (requiredSpan) this._labelElement.appendChild(requiredSpan);
592
+ }
593
+ return this;
594
+ }
595
+
596
+ /**
597
+ * Set required state for form inputs
598
+ */
599
+ required(value: boolean): this {
600
+ (this.state as any).required = value;
601
+ return this;
602
+ }
603
+
604
+ /**
605
+ * Set name attribute for form inputs
606
+ */
607
+ name(value: string): this {
608
+ (this.state as any).name = value;
609
+ return this;
610
+ }
611
+
612
+ /**
613
+ * Set custom validation handler
614
+ */
615
+ onValidate(handler: (value: any) => boolean | string): this {
616
+ this._onValidate = handler;
617
+ return this;
618
+ }
619
+
620
+ /**
621
+ * Validate form input (override in form components)
622
+ */
623
+ validate(): boolean {
624
+ console.warn(`${this.constructor.name}.validate() not implemented`);
625
+ return true;
626
+ }
627
+
628
+ /**
629
+ * Check if form input is valid (override in form components)
630
+ */
631
+ isValid(): boolean {
632
+ console.warn(`${this.constructor.name}.isValid() not implemented`);
633
+ return true;
634
+ }
635
+
636
+ /**
637
+ * Get current value (override in form components)
638
+ */
639
+ getValue(): any {
640
+ console.warn(`${this.constructor.name}.getValue() not implemented`);
641
+ return undefined;
642
+ }
643
+
644
+ /**
645
+ * Set current value (override in form components)
646
+ */
647
+ setValue(value: any): this {
648
+ console.warn(`${this.constructor.name}.setValue() not implemented`);
649
+ return this;
650
+ }
651
+
652
+ /* ═════════════════════════════════════════════════════════════════
653
+ * FORM VALIDATION HELPERS (Protected - for form components)
654
+ * ═════════════════════════════════════════════════════════════════ */
655
+
656
+ protected _showError(message: string): void {
657
+ if (this._errorElement) {
658
+ this._errorElement.textContent = message;
659
+ this._errorElement.style.display = 'block';
660
+ }
661
+
662
+ if (this._inputElement) {
663
+ this._inputElement.classList.add('jux-input-invalid');
664
+ }
665
+
666
+ (this.state as any).errorMessage = message;
667
+ }
668
+
669
+ protected _clearError(): void {
670
+ if (this._errorElement) {
671
+ this._errorElement.textContent = '';
672
+ this._errorElement.style.display = 'none';
673
+ }
674
+
675
+ if (this._inputElement) {
676
+ this._inputElement.classList.remove('jux-input-invalid');
677
+ }
678
+
679
+ (this.state as any).errorMessage = undefined;
680
+ }
681
+
682
+ /**
683
+ * Build label element for form inputs
684
+ */
685
+ protected _renderLabel(): HTMLLabelElement {
686
+ const label = (this.state as any).label || formatIdAsLabel(this._id);
687
+ const required = (this.state as any).required || false;
688
+
689
+ const labelEl = document.createElement('label');
690
+ labelEl.className = 'jux-input-label';
691
+ labelEl.htmlFor = `${this._id}-input`;
692
+ labelEl.textContent = label;
693
+
694
+ if (required) {
695
+ const requiredSpan = document.createElement('span');
696
+ requiredSpan.className = 'jux-input-required';
697
+ requiredSpan.textContent = ' *';
698
+ labelEl.appendChild(requiredSpan);
699
+ }
700
+
701
+ this._labelElement = labelEl;
702
+ return labelEl;
703
+ }
704
+
705
+ /**
706
+ * Build error element for form inputs
707
+ */
708
+ protected _renderError(): HTMLElement {
709
+ const errorEl = document.createElement('div');
710
+ errorEl.className = 'jux-input-error';
711
+ errorEl.id = `${this._id}-error`;
712
+ errorEl.style.display = 'none';
713
+
714
+ this._errorElement = errorEl;
715
+ return errorEl;
716
+ }
717
+
718
+ /**
719
+ * Wire up two-way sync for form inputs
720
+ */
721
+ protected _wireFormSync(inputElement: HTMLElement, eventName: string = 'input'): void {
722
+ const valueSync = this._syncBindings.find(b => b.property === 'value');
723
+
724
+ if (valueSync) {
725
+ const { stateObj, toState, toComponent } = valueSync;
726
+
727
+ const transformToState = toState || ((v: any) => v);
728
+ const transformToComponent = toComponent || ((v: any) => v);
729
+
730
+ let isUpdating = false;
731
+
732
+ // State → Component
733
+ stateObj.subscribe((val: any) => {
734
+ if (isUpdating) return;
735
+ const transformed = transformToComponent(val);
736
+ this.setValue(transformed);
737
+ });
738
+
739
+ // Component → State
740
+ inputElement.addEventListener(eventName, () => {
741
+ if (isUpdating) return;
742
+ isUpdating = true;
743
+
744
+ const value = this.getValue();
745
+ const transformed = transformToState(value);
746
+ this._clearError();
747
+
748
+ stateObj.set(transformed);
749
+
750
+ setTimeout(() => { isUpdating = false; }, 0);
751
+ });
752
+ } else {
753
+ // Default behavior without sync
754
+ inputElement.addEventListener(eventName, () => {
755
+ this._clearError();
756
+ });
757
+ }
758
+
759
+ // Only validate on blur IF the field has been validated before
760
+ inputElement.addEventListener('blur', () => {
761
+ if (this._hasBeenValidated) {
762
+ this.validate();
763
+ }
764
+ });
765
+ }
566
766
  }
@@ -1,5 +1,4 @@
1
- import { FormInput, FormInputState } from './base/FormInput.js';
2
- import { BaseComponent } from './base/BaseComponent.js';
1
+ import { BaseComponent, BaseState } from './base/BaseComponent.js';
3
2
  export interface CheckboxOptions {
4
3
  checked?: boolean;
5
4
  label?: string;
@@ -11,11 +10,11 @@ export interface CheckboxOptions {
11
10
  class?: string;
12
11
  onValidate?: (checked: boolean) => boolean | string;
13
12
  }
14
- interface CheckboxState extends FormInputState {
13
+ interface CheckboxState extends BaseState {
15
14
  checked: boolean;
16
15
  value: string;
17
16
  }
18
- export declare class Checkbox extends FormInput<CheckboxState> {
17
+ export declare class Checkbox extends BaseComponent<CheckboxState> {
19
18
  constructor(id: string, options?: CheckboxOptions);
20
19
  protected getTriggerEvents(): readonly string[];
21
20
  protected getCallbackEvents(): readonly string[];
@@ -23,6 +22,8 @@ export declare class Checkbox extends FormInput<CheckboxState> {
23
22
  value(value: string): this;
24
23
  getValue(): boolean;
25
24
  setValue(value: boolean): this;
25
+ validate(): boolean;
26
+ isValid(): boolean;
26
27
  protected _validateValue(checked: boolean): boolean | string;
27
28
  protected _buildInputElement(): HTMLElement;
28
29
  render(targetId?: string | HTMLElement | BaseComponent<any>): this;
@@ -1 +1 @@
1
- {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAOxD,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAAC;CACvD;AAED,UAAU,aAAc,SAAQ,cAAc;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,QAAS,SAAQ,SAAS,CAAC,aAAa,CAAC;gBACtC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;IAkBrD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAehD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI7B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAS1B,QAAQ,IAAI,OAAO;IAInB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAQ9B,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM;IAiB5D,SAAS,CAAC,kBAAkB,IAAI,WAAW;IAoB3C,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CAiHrE;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAE5E"}
1
+ {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAQnE,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAAC;CACvD;AAED,UAAU,aAAc,SAAQ,SAAS;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,QAAS,SAAQ,aAAa,CAAC,aAAa,CAAC;gBAC1C,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;IAqBrD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAQhD,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI7B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAS1B,QAAQ,IAAI,OAAO;IAInB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAQ9B,QAAQ,IAAI,OAAO;IAcnB,OAAO,IAAI,OAAO;IAKlB,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM;IAiB5D,SAAS,CAAC,kBAAkB,IAAI,WAAW;IAoB3C,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CAmHrE;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAE5E"}