@radix-ng/primitives 1.0.7 → 1.0.9

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 (54) hide show
  1. package/fesm2022/radix-ng-primitives-autocomplete.mjs +43 -9
  2. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-checkbox.mjs +89 -8
  4. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-combobox.mjs +43 -9
  6. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-core.mjs +208 -2
  8. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  9. package/fesm2022/radix-ng-primitives-date-field.mjs +50 -9
  10. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  11. package/fesm2022/radix-ng-primitives-editable.mjs +31 -5
  12. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  13. package/fesm2022/radix-ng-primitives-field.mjs +265 -46
  14. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
  15. package/fesm2022/radix-ng-primitives-form.mjs +110 -26
  16. package/fesm2022/radix-ng-primitives-form.mjs.map +1 -1
  17. package/fesm2022/radix-ng-primitives-input.mjs +16 -6
  18. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
  19. package/fesm2022/radix-ng-primitives-number-field.mjs +32 -7
  20. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  21. package/fesm2022/radix-ng-primitives-presence.mjs +4 -2
  22. package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
  23. package/fesm2022/radix-ng-primitives-radio.mjs +18 -4
  24. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  25. package/fesm2022/radix-ng-primitives-select.mjs +26 -8
  26. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  27. package/fesm2022/radix-ng-primitives-signal-forms.mjs +184 -0
  28. package/fesm2022/radix-ng-primitives-signal-forms.mjs.map +1 -0
  29. package/fesm2022/radix-ng-primitives-slider.mjs +24 -8
  30. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  31. package/fesm2022/radix-ng-primitives-switch.mjs +29 -5
  32. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  33. package/fesm2022/radix-ng-primitives-time-field.mjs +49 -9
  34. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  35. package/fesm2022/radix-ng-primitives-toggle-group.mjs +23 -12
  36. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  37. package/package.json +11 -1
  38. package/types/radix-ng-primitives-autocomplete.d.ts +21 -2
  39. package/types/radix-ng-primitives-checkbox.d.ts +53 -4
  40. package/types/radix-ng-primitives-combobox.d.ts +51 -2
  41. package/types/radix-ng-primitives-core.d.ts +206 -3
  42. package/types/radix-ng-primitives-date-field.d.ts +27 -2
  43. package/types/radix-ng-primitives-editable.d.ts +15 -2
  44. package/types/radix-ng-primitives-field.d.ts +136 -33
  45. package/types/radix-ng-primitives-form.d.ts +115 -21
  46. package/types/radix-ng-primitives-input.d.ts +8 -1
  47. package/types/radix-ng-primitives-number-field.d.ts +14 -3
  48. package/types/radix-ng-primitives-radio.d.ts +7 -2
  49. package/types/radix-ng-primitives-select.d.ts +11 -3
  50. package/types/radix-ng-primitives-signal-forms.d.ts +97 -0
  51. package/types/radix-ng-primitives-slider.d.ts +9 -7
  52. package/types/radix-ng-primitives-switch.d.ts +15 -2
  53. package/types/radix-ng-primitives-time-field.d.ts +26 -2
  54. package/types/radix-ng-primitives-toggle-group.d.ts +6 -4
@@ -8,7 +8,7 @@ import * as i1$2 from '@radix-ng/primitives/dismissable-layer';
8
8
  import { RdxFloatingInsideElement, RdxDismiss } from '@radix-ng/primitives/dismissable-layer';
9
9
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
10
10
  import * as i2 from '@radix-ng/primitives/core';
11
- import { createFloatingRootContext, rdxDevWarning, isItemEqualToValue, itemToStringLabel, createCancelableChangeEventDetails, provideFloatingTree, provideFloatingRootContext, setupInternalBackdrop, injectId, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useAnchoredScrollLock, RdxFloatingNodeRegistration, rdxDevError } from '@radix-ng/primitives/core';
11
+ import { RdxFormUiControlBase, createFloatingRootContext, rdxDevWarning, isItemEqualToValue, itemToStringLabel, createCancelableChangeEventDetails, provideFloatingTree, provideFloatingRootContext, setupInternalBackdrop, injectId, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useAnchoredScrollLock, RdxFloatingNodeRegistration, rdxDevError } from '@radix-ng/primitives/core';
12
12
  import { injectDirection } from '@radix-ng/primitives/direction-provider';
13
13
  import { injectFieldRootContext } from '@radix-ng/primitives/field';
14
14
 
@@ -142,6 +142,9 @@ const context = () => {
142
142
  setInputValue: (value) => root.setQuery(value),
143
143
  openAndHighlight: (edge, reason, event) => root.openAndHighlight(edge, reason, event),
144
144
  navigateByKeyboard: (direction, event) => root.navigateByKeyboard(direction, event),
145
+ invalidState: root.invalidState,
146
+ touchedState: root.touchedState,
147
+ dirtyState: root.dirtyState,
145
148
  select: (item, event) => root.handleSelect(item, event),
146
149
  selectIndex: (index, event) => root.selectIndex(index, event),
147
150
  selectHighlighted: (event) => root.selectHighlighted(event),
@@ -171,7 +174,7 @@ function coerceAutoHighlight(value) {
171
174
  *
172
175
  * @group Components
173
176
  */
174
- class RdxAutocompleteRoot {
177
+ class RdxAutocompleteRoot extends RdxFormUiControlBase {
175
178
  // --- engine-backed surface read by the parts / tests ---
176
179
  get listId() {
177
180
  return this.engine.listId;
@@ -231,6 +234,7 @@ class RdxAutocompleteRoot {
231
234
  return this.engine.triggerElement;
232
235
  }
233
236
  constructor() {
237
+ super();
234
238
  this.injector = inject(Injector);
235
239
  /** Per-popup floating root context (ADR 0015) — `open` / `triggers` / reference for the dismissal engine. */
236
240
  this.floatingContext = createFloatingRootContext({
@@ -332,6 +336,12 @@ class RdxAutocompleteRoot {
332
336
  ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
333
337
  this.requiredState = computed(() => this.required(), /* @ts-ignore */
334
338
  ...(ngDevMode ? [{ debugName: "requiredState" }] : /* istanbul ignore next */ []));
339
+ /** @ignore */
340
+ this.invalidState = this.formUi.invalidState;
341
+ /** @ignore */
342
+ this.touchedState = this.formUi.touchedState;
343
+ /** @ignore */
344
+ this.dirtyState = this.formUi.dirtyState;
335
345
  this.preventUnmountOnClose = signal(false, /* @ts-ignore */
336
346
  ...(ngDevMode ? [{ debugName: "preventUnmountOnClose" }] : /* istanbul ignore next */ []));
337
347
  this.present = computed(() => this.open() || this.preventUnmountOnClose(), /* @ts-ignore */
@@ -603,8 +613,12 @@ class RdxAutocompleteRoot {
603
613
  focusInput() {
604
614
  this.engine.focusInput();
605
615
  }
616
+ /** @ignore Bridge the CVA `onTouched` so `markAsTouched()` also notifies Reactive/template forms. */
617
+ formUiTouchTarget() {
618
+ return { markAsTouched: () => this.onTouched?.() };
619
+ }
606
620
  markAsTouched() {
607
- this.onTouched?.();
621
+ this.formUi.markAsTouched();
608
622
  }
609
623
  commitValue(value, reason, event = new Event('autocomplete.value-change')) {
610
624
  // Mirror combobox's guarded commit: never mutate the value while disabled or read-only
@@ -618,6 +632,7 @@ class RdxAutocompleteRoot {
618
632
  return;
619
633
  }
620
634
  this.value.set(value);
635
+ this.formUi.markDirty();
621
636
  this.onChange?.(value);
622
637
  }
623
638
  createOpenChangeEvent(open, reason, event) {
@@ -661,7 +676,7 @@ class RdxAutocompleteRoot {
661
676
  // New floating foundation (ADR 0015/0017) — the dismissal capability reads this shared context.
662
677
  provideFloatingTree(),
663
678
  provideFloatingRootContext(() => inject(RdxAutocompleteRoot).floatingContext)
664
- ], exportAs: ["rdxAutocompleteRoot"], hostDirectives: [{ directive: i1$1.RdxPopper }], ngImport: i0 }); }
679
+ ], exportAs: ["rdxAutocompleteRoot"], usesInheritance: true, hostDirectives: [{ directive: i1$1.RdxPopper }], ngImport: i0 }); }
665
680
  }
666
681
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxAutocompleteRoot, decorators: [{
667
682
  type: Directive,
@@ -882,12 +897,27 @@ class RdxAutocompleteInput {
882
897
  // `aria-autocomplete` tokens. Must reflect the static mechanism, not the transient inline preview.
883
898
  this.ariaAutocomplete = computed(() => this.root.mode(), /* @ts-ignore */
884
899
  ...(ngDevMode ? [{ debugName: "ariaAutocomplete" }] : /* istanbul ignore next */ []));
885
- this.invalidState = computed(() => this.invalid() || Boolean(this.fieldRootContext?.invalidState()), /* @ts-ignore */
900
+ this.invalidState = computed(() => this.invalid() || this.root.invalidState() || Boolean(this.fieldRootContext?.invalidState()), /* @ts-ignore */
886
901
  ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
902
+ /**
903
+ * Tri-state *displayed* validity: the enclosing Field's gated `validState` when inside a `rdxFieldRoot`
904
+ * (so a field whose `validationMode` defers display (e.g. `onBlur`) keeps the input neutral until revealed), else the input's
905
+ * own binary invalidity.
906
+ */
907
+ this.displayValid = computed(() => this.fieldRootContext
908
+ ? this.fieldRootContext.validState()
909
+ : this.invalid() || this.root.invalidState()
910
+ ? false
911
+ : true, /* @ts-ignore */
912
+ ...(ngDevMode ? [{ debugName: "displayValid" }] : /* istanbul ignore next */ []));
887
913
  this.disabledState = computed(() => this.root.disabledState() || Boolean(this.fieldRootContext?.disabledState()), /* @ts-ignore */
888
914
  ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
889
915
  this.requiredState = computed(() => this.root.requiredState() || Boolean(this.fieldRootContext?.requiredState()), /* @ts-ignore */
890
916
  ...(ngDevMode ? [{ debugName: "requiredState" }] : /* istanbul ignore next */ []));
917
+ this.touchedState = computed(() => this.root.touchedState() || Boolean(this.fieldRootContext?.touchedState()), /* @ts-ignore */
918
+ ...(ngDevMode ? [{ debugName: "touchedState" }] : /* istanbul ignore next */ []));
919
+ this.dirtyState = computed(() => this.root.dirtyState() || Boolean(this.fieldRootContext?.dirtyState()), /* @ts-ignore */
920
+ ...(ngDevMode ? [{ debugName: "dirtyState" }] : /* istanbul ignore next */ []));
891
921
  this.filledState = computed(() => Boolean(this.root.value()) || Boolean(this.fieldRootContext?.filledState()), /* @ts-ignore */
892
922
  ...(ngDevMode ? [{ debugName: "filledState" }] : /* istanbul ignore next */ []));
893
923
  this.focusedState = computed(() => Boolean(this.fieldRootContext?.focusedState()), /* @ts-ignore */
@@ -959,6 +989,8 @@ class RdxAutocompleteInput {
959
989
  onBlur() {
960
990
  this.fieldRootContext?.setFocused(false);
961
991
  this.fieldRootContext?.setTouched(true);
992
+ // Notify Signal Forms (touched model + touch output) the control was touched.
993
+ this.root.markAsTouched();
962
994
  }
963
995
  onKeydown(event) {
964
996
  if (event.isComposing || this.composing) {
@@ -1060,7 +1092,7 @@ class RdxAutocompleteInput {
1060
1092
  }
1061
1093
  }
1062
1094
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxAutocompleteInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1063
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxAutocompleteInput, isStandalone: true, selector: "input[rdxAutocompleteInput]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "combobox", "autocomplete": "off" }, listeners: { "input": "onInput($event)", "click": "onClick($event)", "focus": "onFocus()", "blur": "onBlur()", "keydown": "onKeydown($event)", "compositionstart": "composing = true", "compositionend": "onCompositionEnd($event)" }, properties: { "attr.aria-autocomplete": "ariaAutocomplete()", "attr.id": "id()", "attr.aria-haspopup": "root.grid() ? \"grid\" : \"listbox\"", "attr.aria-expanded": "root.open()", "attr.aria-controls": "root.listId", "attr.aria-labelledby": "root.labelId()", "attr.aria-activedescendant": "root.activeId()", "attr.aria-describedby": "describedBy()", "attr.aria-invalid": "invalidState() ? \"true\" : undefined", "attr.aria-required": "requiredState() ? \"true\" : undefined", "attr.aria-disabled": "disabledState() ? \"true\" : undefined", "attr.disabled": "disabledState() ? \"\" : undefined", "attr.readonly": "root.readOnly() ? \"\" : undefined", "attr.required": "requiredState() ? \"\" : undefined", "value": "root.displayValue()", "attr.data-popup-open": "dataAttr(root.open())", "attr.data-list-empty": "dataAttr(root.visibleCount() === 0)", "attr.data-invalid": "dataAttr(invalidState())", "attr.data-valid": "dataAttr(!invalidState())", "attr.data-disabled": "dataAttr(disabledState())", "attr.data-required": "dataAttr(requiredState())", "attr.data-filled": "dataAttr(filledState())", "attr.data-focused": "dataAttr(focusedState())" } }, exportAs: ["rdxAutocompleteInput"], hostDirectives: [{ directive: i1$1.RdxPopperAnchor }, { directive: i1$2.RdxFloatingInsideElement }], ngImport: i0 }); }
1095
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxAutocompleteInput, isStandalone: true, selector: "input[rdxAutocompleteInput]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "combobox", "autocomplete": "off" }, listeners: { "input": "onInput($event)", "click": "onClick($event)", "focus": "onFocus()", "blur": "onBlur()", "keydown": "onKeydown($event)", "compositionstart": "composing = true", "compositionend": "onCompositionEnd($event)" }, properties: { "attr.aria-autocomplete": "ariaAutocomplete()", "attr.id": "id()", "attr.aria-haspopup": "root.grid() ? \"grid\" : \"listbox\"", "attr.aria-expanded": "root.open()", "attr.aria-controls": "root.listId", "attr.aria-labelledby": "root.labelId()", "attr.aria-activedescendant": "root.activeId()", "attr.aria-describedby": "describedBy()", "attr.aria-invalid": "displayValid() === false ? \"true\" : undefined", "attr.aria-required": "requiredState() ? \"true\" : undefined", "attr.aria-disabled": "disabledState() ? \"true\" : undefined", "attr.disabled": "disabledState() ? \"\" : undefined", "attr.readonly": "root.readOnly() ? \"\" : undefined", "attr.required": "requiredState() ? \"\" : undefined", "value": "root.displayValue()", "attr.data-popup-open": "dataAttr(root.open())", "attr.data-list-empty": "dataAttr(root.visibleCount() === 0)", "attr.data-invalid": "dataAttr(displayValid() === false)", "attr.data-valid": "dataAttr(displayValid() === true)", "attr.data-disabled": "dataAttr(disabledState())", "attr.data-required": "dataAttr(requiredState())", "attr.data-touched": "dataAttr(touchedState())", "attr.data-dirty": "dataAttr(dirtyState())", "attr.data-filled": "dataAttr(filledState())", "attr.data-focused": "dataAttr(focusedState())" } }, exportAs: ["rdxAutocompleteInput"], hostDirectives: [{ directive: i1$1.RdxPopperAnchor }, { directive: i1$2.RdxFloatingInsideElement }], ngImport: i0 }); }
1064
1096
  }
1065
1097
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxAutocompleteInput, decorators: [{
1066
1098
  type: Directive,
@@ -1079,7 +1111,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1079
1111
  '[attr.aria-labelledby]': 'root.labelId()',
1080
1112
  '[attr.aria-activedescendant]': 'root.activeId()',
1081
1113
  '[attr.aria-describedby]': 'describedBy()',
1082
- '[attr.aria-invalid]': 'invalidState() ? "true" : undefined',
1114
+ '[attr.aria-invalid]': 'displayValid() === false ? "true" : undefined',
1083
1115
  '[attr.aria-required]': 'requiredState() ? "true" : undefined',
1084
1116
  '[attr.aria-disabled]': 'disabledState() ? "true" : undefined',
1085
1117
  '[attr.disabled]': 'disabledState() ? "" : undefined',
@@ -1088,10 +1120,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1088
1120
  '[value]': 'root.displayValue()',
1089
1121
  '[attr.data-popup-open]': 'dataAttr(root.open())',
1090
1122
  '[attr.data-list-empty]': 'dataAttr(root.visibleCount() === 0)',
1091
- '[attr.data-invalid]': 'dataAttr(invalidState())',
1092
- '[attr.data-valid]': 'dataAttr(!invalidState())',
1123
+ '[attr.data-invalid]': 'dataAttr(displayValid() === false)',
1124
+ '[attr.data-valid]': 'dataAttr(displayValid() === true)',
1093
1125
  '[attr.data-disabled]': 'dataAttr(disabledState())',
1094
1126
  '[attr.data-required]': 'dataAttr(requiredState())',
1127
+ '[attr.data-touched]': 'dataAttr(touchedState())',
1128
+ '[attr.data-dirty]': 'dataAttr(dirtyState())',
1095
1129
  '[attr.data-filled]': 'dataAttr(filledState())',
1096
1130
  '[attr.data-focused]': 'dataAttr(focusedState())',
1097
1131
  '(input)': 'onInput($event)',