ngx-com 0.0.3 → 0.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 (33) hide show
  1. package/fesm2022/ngx-com-components-avatar.mjs +772 -0
  2. package/fesm2022/ngx-com-components-avatar.mjs.map +1 -0
  3. package/fesm2022/ngx-com-components-calendar.mjs +33 -130
  4. package/fesm2022/ngx-com-components-calendar.mjs.map +1 -1
  5. package/fesm2022/ngx-com-components-confirm.mjs +562 -0
  6. package/fesm2022/ngx-com-components-confirm.mjs.map +1 -0
  7. package/fesm2022/ngx-com-components-dropdown.mjs +119 -25
  8. package/fesm2022/ngx-com-components-dropdown.mjs.map +1 -1
  9. package/fesm2022/ngx-com-components-empty-state.mjs +382 -0
  10. package/fesm2022/ngx-com-components-empty-state.mjs.map +1 -0
  11. package/fesm2022/ngx-com-components-form-field.mjs +16 -15
  12. package/fesm2022/ngx-com-components-form-field.mjs.map +1 -1
  13. package/fesm2022/ngx-com-components-item.mjs +578 -0
  14. package/fesm2022/ngx-com-components-item.mjs.map +1 -0
  15. package/fesm2022/ngx-com-components-paginator.mjs +823 -0
  16. package/fesm2022/ngx-com-components-paginator.mjs.map +1 -0
  17. package/fesm2022/ngx-com-components-segmented-control.mjs +538 -0
  18. package/fesm2022/ngx-com-components-segmented-control.mjs.map +1 -0
  19. package/fesm2022/ngx-com-components-spinner.mjs +189 -0
  20. package/fesm2022/ngx-com-components-spinner.mjs.map +1 -0
  21. package/fesm2022/ngx-com-components-tooltip.mjs +625 -0
  22. package/fesm2022/ngx-com-components-tooltip.mjs.map +1 -0
  23. package/package.json +33 -1
  24. package/types/ngx-com-components-avatar.d.ts +409 -0
  25. package/types/ngx-com-components-calendar.d.ts +5 -0
  26. package/types/ngx-com-components-confirm.d.ts +160 -0
  27. package/types/ngx-com-components-dropdown.d.ts +52 -28
  28. package/types/ngx-com-components-empty-state.d.ts +269 -0
  29. package/types/ngx-com-components-item.d.ts +336 -0
  30. package/types/ngx-com-components-paginator.d.ts +265 -0
  31. package/types/ngx-com-components-segmented-control.d.ts +274 -0
  32. package/types/ngx-com-components-spinner.d.ts +120 -0
  33. package/types/ngx-com-components-tooltip.d.ts +200 -0
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, ElementRef, viewChild, input, output, computed, ChangeDetectionStrategy, Component, DestroyRef, signal, TemplateRef, Directive, ViewContainerRef, DOCUMENT, contentChild, linkedSignal } from '@angular/core';
2
+ import { viewChild, input, output, computed, ChangeDetectionStrategy, Component, inject, DestroyRef, signal, TemplateRef, Directive, ElementRef, ViewContainerRef, contentChild, linkedSignal, forwardRef } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { NgTemplateOutlet } from '@angular/common';
5
- import { NgControl } from '@angular/forms';
5
+ import { NgForm, FormGroupDirective, NgControl } from '@angular/forms';
6
+ import { ErrorStateMatcher, FormFieldControl } from 'ngx-com/components/form-field';
6
7
  import { Overlay, OverlayModule } from '@angular/cdk/overlay';
7
8
  import { TemplatePortal } from '@angular/cdk/portal';
8
9
  import { LiveAnnouncer } from '@angular/cdk/a11y';
@@ -62,6 +63,14 @@ const dropdownTriggerVariants = cva([
62
63
  'bg-muted',
63
64
  'hover:bg-muted-hover',
64
65
  ],
66
+ naked: [
67
+ 'border-transparent',
68
+ 'bg-transparent',
69
+ 'shadow-none',
70
+ 'focus:ring-0',
71
+ 'focus:ring-offset-0',
72
+ 'rounded-none',
73
+ ],
65
74
  },
66
75
  size: {
67
76
  sm: ['h-8', 'px-2', 'text-xs', 'gap-1'],
@@ -95,6 +104,24 @@ const dropdownTriggerVariants = cva([
95
104
  variant: 'outline',
96
105
  class: ['border-primary'],
97
106
  },
107
+ // Naked variant should not show ring when open (form-field provides focus styling)
108
+ {
109
+ open: true,
110
+ variant: 'naked',
111
+ class: ['ring-0', 'border-transparent'],
112
+ },
113
+ // Naked variant should not show error border (form-field provides error styling)
114
+ {
115
+ state: 'error',
116
+ variant: 'naked',
117
+ class: ['border-transparent', 'focus:ring-0'],
118
+ },
119
+ // Naked variant should not show success border (form-field provides styling)
120
+ {
121
+ state: 'success',
122
+ variant: 'naked',
123
+ class: ['border-transparent', 'focus:ring-0'],
124
+ },
98
125
  ],
99
126
  defaultVariants: {
100
127
  variant: 'default',
@@ -429,7 +456,6 @@ const dropdownChevronVariants = cva([
429
456
  * `--color-primary-subtle-foreground`, `--color-disabled-foreground`
430
457
  */
431
458
  class ComDropdownOption {
432
- elementRef = inject(ElementRef);
433
459
  /** Reference to the option element for focus management. */
434
460
  optionRef = viewChild('optionElement', ...(ngDevMode ? [{ debugName: "optionRef" }] : []));
435
461
  /** The value this option represents. */
@@ -497,10 +523,6 @@ class ComDropdownOption {
497
523
  this.hover.emit(this.value());
498
524
  }
499
525
  }
500
- /** Gets the host element. */
501
- getHostElement() {
502
- return this.elementRef.nativeElement;
503
- }
504
526
  /** Scrolls this option into view. */
505
527
  scrollIntoView() {
506
528
  this.optionRef()?.nativeElement.scrollIntoView({
@@ -620,7 +642,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
620
642
  * `--color-muted-foreground`, `--radius-overlay`
621
643
  */
622
644
  class ComDropdownPanel {
623
- elementRef = inject(ElementRef);
624
645
  /** Reference to the panel element. */
625
646
  panelRef = viewChild('panelElement', ...(ngDevMode ? [{ debugName: "panelRef" }] : []));
626
647
  /** Reference to the virtual scroll viewport (when enabled). */
@@ -670,10 +691,6 @@ class ComDropdownPanel {
670
691
  trackByFn(_index, option) {
671
692
  return option.id;
672
693
  }
673
- /** Gets the host element. */
674
- getHostElement() {
675
- return this.elementRef.nativeElement;
676
- }
677
694
  /** Scrolls to a specific index. */
678
695
  scrollToIndex(index) {
679
696
  const vp = this.viewport();
@@ -1559,8 +1576,10 @@ class ComDropdown {
1559
1576
  overlay = inject(Overlay);
1560
1577
  viewContainerRef = inject(ViewContainerRef);
1561
1578
  liveAnnouncer = inject(LiveAnnouncer);
1562
- document = inject(DOCUMENT);
1563
- /** Optional NgControl for form integration. */
1579
+ defaultErrorStateMatcher = inject(ErrorStateMatcher);
1580
+ parentForm = inject(NgForm, { optional: true });
1581
+ parentFormGroup = inject(FormGroupDirective, { optional: true });
1582
+ /** NgControl bound to this dropdown (if using reactive forms). */
1564
1583
  ngControl = inject(NgControl, { optional: true, self: true });
1565
1584
  /** Reference to the trigger button element. */
1566
1585
  triggerRef = viewChild.required('triggerElement');
@@ -1627,6 +1646,8 @@ class ComDropdown {
1627
1646
  virtualScrollThreshold = input(VIRTUAL_SCROLL_THRESHOLD, ...(ngDevMode ? [{ debugName: "virtualScrollThreshold" }] : []));
1628
1647
  /** Maximum number of tags to display in multi-select mode. Set to null for no limit. */
1629
1648
  maxVisibleTags = input(2, ...(ngDevMode ? [{ debugName: "maxVisibleTags" }] : []));
1649
+ /** Custom error state matcher for determining when to show errors. */
1650
+ errorStateMatcher = input(...(ngDevMode ? [undefined, { debugName: "errorStateMatcher" }] : []));
1630
1651
  // ============ OUTPUTS ============
1631
1652
  /** Emitted when the value changes. */
1632
1653
  valueChange = output();
@@ -1639,6 +1660,8 @@ class ComDropdown {
1639
1660
  // ============ INTERNAL STATE ============
1640
1661
  /** Whether the panel is open. */
1641
1662
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
1663
+ /** Whether the trigger button is focused. */
1664
+ _triggerFocused = signal(false, ...(ngDevMode ? [{ debugName: "_triggerFocused" }] : []));
1642
1665
  /** Current search query. */
1643
1666
  searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
1644
1667
  /** Currently active (keyboard focused) option ID. */
@@ -1647,6 +1670,10 @@ class ComDropdown {
1647
1670
  internalValue = linkedSignal(() => this.value() ?? null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
1648
1671
  /** Live announcements for screen readers. */
1649
1672
  liveAnnouncement = signal('', ...(ngDevMode ? [{ debugName: "liveAnnouncement" }] : []));
1673
+ /** IDs for aria-describedby (set by form-field). */
1674
+ _describedByIds = signal('', ...(ngDevMode ? [{ debugName: "_describedByIds" }] : []));
1675
+ /** Form field appearance (set by form-field). */
1676
+ _appearance = signal('outline', ...(ngDevMode ? [{ debugName: "_appearance" }] : []));
1650
1677
  // ============ COMPUTED STATE ============
1651
1678
  /** Trigger element ID. */
1652
1679
  triggerId = computed(() => `${this.dropdownId}-trigger`, ...(ngDevMode ? [{ debugName: "triggerId" }] : []));
@@ -1664,6 +1691,34 @@ class ComDropdown {
1664
1691
  }
1665
1692
  return val !== null && val !== undefined;
1666
1693
  }, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
1694
+ // ============ FormFieldControl SIGNALS ============
1695
+ /** Whether the dropdown is focused (trigger focused or panel open). Implements FormFieldControl. */
1696
+ focused = computed(() => this._triggerFocused() || this.isOpen(), ...(ngDevMode ? [{ debugName: "focused" }] : []));
1697
+ /** Whether the label should float. Label floats when focused or has a value. */
1698
+ shouldLabelFloat = computed(() => this.focused() || this.hasValue(), ...(ngDevMode ? [{ debugName: "shouldLabelFloat" }] : []));
1699
+ /** Whether the control is in an error state. Implements FormFieldControl. */
1700
+ errorState = computed(() => {
1701
+ // Read reactive dependencies to trigger re-evaluation
1702
+ this.isOpen();
1703
+ this.hasValue();
1704
+ const matcher = this.errorStateMatcher() ?? this.defaultErrorStateMatcher;
1705
+ const form = this.parentFormGroup ?? this.parentForm;
1706
+ return matcher.isErrorState(this.ngControl?.control ?? null, form);
1707
+ }, ...(ngDevMode ? [{ debugName: "errorState" }] : []));
1708
+ /** Unique ID for the control. Implements FormFieldControl. */
1709
+ id = this.triggerId;
1710
+ /**
1711
+ * Effective state combining manual state with automatic error detection.
1712
+ * Manual state takes precedence over auto-detected error state.
1713
+ */
1714
+ effectiveState = computed(() => {
1715
+ const manualState = this.state();
1716
+ if (manualState !== 'default')
1717
+ return manualState;
1718
+ return this.errorState() ? 'error' : 'default';
1719
+ }, ...(ngDevMode ? [{ debugName: "effectiveState" }] : []));
1720
+ /** Combined aria-describedby from form-field. */
1721
+ ariaDescribedBy = computed(() => this._describedByIds() || null, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
1667
1722
  /** Selected value (single mode). */
1668
1723
  selectedValue = computed(() => {
1669
1724
  const val = this.internalValue();
@@ -1764,10 +1819,15 @@ class ComDropdown {
1764
1819
  const baseClasses = dropdownTriggerVariants({
1765
1820
  variant: this.variant(),
1766
1821
  size: this.size(),
1767
- state: this.state(),
1822
+ state: this.effectiveState(),
1768
1823
  open: this.isOpen(),
1769
1824
  });
1770
- return mergeClasses(baseClasses, this.userClass());
1825
+ // For naked variant, add padding based on form-field appearance
1826
+ let paddingClasses = '';
1827
+ if (this.variant() === 'naked') {
1828
+ paddingClasses = this._appearance() === 'fill' ? 'pt-5 pb-1.5 px-3' : 'py-2.5 px-3';
1829
+ }
1830
+ return mergeClasses(baseClasses, paddingClasses, this.userClass());
1771
1831
  }, ...(ngDevMode ? [{ debugName: "triggerClasses" }] : []));
1772
1832
  /** Computed panel classes. */
1773
1833
  panelClasses = computed(() => {
@@ -1792,14 +1852,10 @@ class ComDropdown {
1792
1852
  onChange = () => { };
1793
1853
  onTouched = () => { };
1794
1854
  constructor() {
1795
- // Wire up NgControl if present
1796
1855
  if (this.ngControl) {
1797
1856
  this.ngControl.valueAccessor = this;
1798
1857
  }
1799
1858
  }
1800
- ngOnInit() {
1801
- // Nothing special needed here since we use effects
1802
- }
1803
1859
  // ============ CVA IMPLEMENTATION ============
1804
1860
  writeValue(value) {
1805
1861
  this.internalValue.set(value);
@@ -1814,6 +1870,32 @@ class ComDropdown {
1814
1870
  // Disabled state is handled via the disabled input
1815
1871
  // When using forms, the form control's disabled state takes precedence
1816
1872
  }
1873
+ // ============ FormFieldControl IMPLEMENTATION ============
1874
+ /**
1875
+ * Called when the form field container is clicked.
1876
+ * Implements FormFieldControl.
1877
+ */
1878
+ onContainerClick(event) {
1879
+ const target = event.target;
1880
+ // Only toggle if click was outside the trigger button (not on it or inside it)
1881
+ if (!this.disabled() && !this.triggerRef().nativeElement.contains(target)) {
1882
+ this.toggle();
1883
+ }
1884
+ }
1885
+ /**
1886
+ * Sets the describedBy IDs from the form field.
1887
+ * Called by the parent form field component.
1888
+ */
1889
+ setDescribedByIds(ids) {
1890
+ this._describedByIds.set(ids);
1891
+ }
1892
+ /**
1893
+ * Sets the appearance for styling.
1894
+ * Called by the parent form field component.
1895
+ */
1896
+ setAppearance(appearance) {
1897
+ this._appearance.set(appearance);
1898
+ }
1817
1899
  // ============ PUBLIC METHODS ============
1818
1900
  /** Opens the dropdown panel. */
1819
1901
  open() {
@@ -1990,6 +2072,12 @@ class ComDropdown {
1990
2072
  // Delegate to panel keydown handler
1991
2073
  this.onPanelKeydown(event);
1992
2074
  }
2075
+ onTriggerFocus() {
2076
+ this._triggerFocused.set(true);
2077
+ }
2078
+ onTriggerBlur() {
2079
+ this._triggerFocused.set(false);
2080
+ }
1993
2081
  onOptionHover(optionId) {
1994
2082
  this.activeOptionId.set(optionId);
1995
2083
  }
@@ -2185,7 +2273,7 @@ class ComDropdown {
2185
2273
  this.liveAnnouncer.announce(message, 'polite');
2186
2274
  }
2187
2275
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDropdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
2188
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComDropdown, isStandalone: true, selector: "com-dropdown", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, filterWith: { classPropertyName: "filterWith", publicName: "filterWith", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, panelWidth: { classPropertyName: "panelWidth", publicName: "panelWidth", isSignal: true, isRequired: false, transformFunction: null }, searchDebounceMs: { classPropertyName: "searchDebounceMs", publicName: "searchDebounceMs", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollThreshold: { classPropertyName: "virtualScrollThreshold", publicName: "virtualScrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, maxVisibleTags: { classPropertyName: "maxVisibleTags", publicName: "maxVisibleTags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", searchChange: "searchChange", opened: "opened", closed: "closed" }, host: { properties: { "class.com-dropdown-disabled": "disabled()", "class.com-dropdown-open": "isOpen()" }, classAttribute: "com-dropdown-host inline-block" }, queries: [{ propertyName: "optionTemplate", first: true, predicate: (ComDropdownOptionTpl), descendants: true, isSignal: true }, { propertyName: "selectedTemplate", first: true, predicate: (ComDropdownSelectedTpl), descendants: true, isSignal: true }, { propertyName: "emptyTemplate", first: true, predicate: ComDropdownEmptyTpl, descendants: true, isSignal: true }, { propertyName: "groupTemplate", first: true, predicate: ComDropdownGroupTpl, descendants: true, isSignal: true }, { propertyName: "tagTemplate", first: true, predicate: (ComDropdownTagTpl), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["triggerElement"], descendants: true, isSignal: true }, { propertyName: "panelTemplateRef", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }], exportAs: ["comDropdown"], ngImport: i0, template: `
2276
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComDropdown, isStandalone: true, selector: "com-dropdown", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, filterWith: { classPropertyName: "filterWith", publicName: "filterWith", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, panelWidth: { classPropertyName: "panelWidth", publicName: "panelWidth", isSignal: true, isRequired: false, transformFunction: null }, searchDebounceMs: { classPropertyName: "searchDebounceMs", publicName: "searchDebounceMs", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollThreshold: { classPropertyName: "virtualScrollThreshold", publicName: "virtualScrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, maxVisibleTags: { classPropertyName: "maxVisibleTags", publicName: "maxVisibleTags", isSignal: true, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", searchChange: "searchChange", opened: "opened", closed: "closed" }, host: { properties: { "class.com-dropdown-disabled": "disabled()", "class.com-dropdown-open": "isOpen()" }, classAttribute: "com-dropdown-host inline-block" }, providers: [{ provide: FormFieldControl, useExisting: forwardRef(() => ComDropdown) }], queries: [{ propertyName: "optionTemplate", first: true, predicate: (ComDropdownOptionTpl), descendants: true, isSignal: true }, { propertyName: "selectedTemplate", first: true, predicate: (ComDropdownSelectedTpl), descendants: true, isSignal: true }, { propertyName: "emptyTemplate", first: true, predicate: ComDropdownEmptyTpl, descendants: true, isSignal: true }, { propertyName: "groupTemplate", first: true, predicate: ComDropdownGroupTpl, descendants: true, isSignal: true }, { propertyName: "tagTemplate", first: true, predicate: (ComDropdownTagTpl), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["triggerElement"], descendants: true, isSignal: true }, { propertyName: "panelTemplateRef", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }], exportAs: ["comDropdown"], ngImport: i0, template: `
2189
2277
  <!-- Trigger button -->
2190
2278
  <button
2191
2279
  #triggerElement
@@ -2198,12 +2286,15 @@ class ComDropdown {
2198
2286
  [attr.aria-haspopup]="'listbox'"
2199
2287
  [attr.aria-activedescendant]="activeDescendant()"
2200
2288
  [attr.aria-required]="required() || null"
2201
- [attr.aria-invalid]="state() === 'error' || null"
2289
+ [attr.aria-invalid]="effectiveState() === 'error' || null"
2290
+ [attr.aria-describedby]="ariaDescribedBy()"
2202
2291
  [attr.aria-disabled]="disabled() || null"
2203
2292
  [attr.tabindex]="disabled() ? -1 : 0"
2204
2293
  [disabled]="disabled()"
2205
2294
  (click)="toggle()"
2206
2295
  (keydown)="onTriggerKeydown($event)"
2296
+ (focus)="onTriggerFocus()"
2297
+ (blur)="onTriggerBlur()"
2207
2298
  >
2208
2299
  <!-- Selected value display -->
2209
2300
  <span class="flex-1 truncate text-left">
@@ -2394,12 +2485,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
2394
2485
  [attr.aria-haspopup]="'listbox'"
2395
2486
  [attr.aria-activedescendant]="activeDescendant()"
2396
2487
  [attr.aria-required]="required() || null"
2397
- [attr.aria-invalid]="state() === 'error' || null"
2488
+ [attr.aria-invalid]="effectiveState() === 'error' || null"
2489
+ [attr.aria-describedby]="ariaDescribedBy()"
2398
2490
  [attr.aria-disabled]="disabled() || null"
2399
2491
  [attr.tabindex]="disabled() ? -1 : 0"
2400
2492
  [disabled]="disabled()"
2401
2493
  (click)="toggle()"
2402
2494
  (keydown)="onTriggerKeydown($event)"
2495
+ (focus)="onTriggerFocus()"
2496
+ (blur)="onTriggerBlur()"
2403
2497
  >
2404
2498
  <!-- Selected value display -->
2405
2499
  <span class="flex-1 truncate text-left">
@@ -2580,12 +2674,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
2580
2674
  ComDropdownSearch,
2581
2675
  ComDropdownTag,
2582
2676
  ComDropdownGroup,
2583
- ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
2677
+ ], providers: [{ provide: FormFieldControl, useExisting: forwardRef(() => ComDropdown) }], changeDetection: ChangeDetectionStrategy.OnPush, host: {
2584
2678
  class: 'com-dropdown-host inline-block',
2585
2679
  '[class.com-dropdown-disabled]': 'disabled()',
2586
2680
  '[class.com-dropdown-open]': 'isOpen()',
2587
2681
  }, styles: [".sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
2588
- }], ctorParameters: () => [], propDecorators: { triggerRef: [{ type: i0.ViewChild, args: ['triggerElement', { isSignal: true }] }], panelTemplateRef: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], optionTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownOptionTpl), { isSignal: true }] }], selectedTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownSelectedTpl), { isSignal: true }] }], emptyTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownEmptyTpl), { isSignal: true }] }], groupTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownGroupTpl), { isSignal: true }] }], tagTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownTagTpl), { isSignal: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], filterWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterWith", required: false }] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], panelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelWidth", required: false }] }], searchDebounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchDebounceMs", required: false }] }], virtualScrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollThreshold", required: false }] }], maxVisibleTags: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxVisibleTags", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
2682
+ }], ctorParameters: () => [], propDecorators: { triggerRef: [{ type: i0.ViewChild, args: ['triggerElement', { isSignal: true }] }], panelTemplateRef: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], optionTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownOptionTpl), { isSignal: true }] }], selectedTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownSelectedTpl), { isSignal: true }] }], emptyTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownEmptyTpl), { isSignal: true }] }], groupTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownGroupTpl), { isSignal: true }] }], tagTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComDropdownTagTpl), { isSignal: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], filterWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterWith", required: false }] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], panelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelWidth", required: false }] }], searchDebounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchDebounceMs", required: false }] }], virtualScrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollThreshold", required: false }] }], maxVisibleTags: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxVisibleTags", required: false }] }], errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
2589
2683
 
2590
2684
  // Public API for the dropdown component
2591
2685
  // Main component