ps-helix 6.0.0 → 6.0.2

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, output, computed, ChangeDetectionStrategy, Component, model, inject, ElementRef, signal, PLATFORM_ID, ViewEncapsulation, InjectionToken, viewChild, effect, Injectable, DestroyRef, Directive, Injector, afterNextRender, ChangeDetectorRef, ViewChild, HostListener, Renderer2, contentChild, linkedSignal, EventEmitter, Output, Input, contentChildren } from '@angular/core';
2
+ import { input, output, computed, ChangeDetectionStrategy, Component, model, inject, ElementRef, signal, PLATFORM_ID, ViewEncapsulation, InjectionToken, viewChild, effect, Injectable, DestroyRef, Directive, ViewContainerRef, ChangeDetectorRef, ViewChild, Injector, afterNextRender, HostListener, Renderer2, contentChild, linkedSignal, EventEmitter, Output, Input, contentChildren } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule, DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
5
5
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
@@ -1516,6 +1516,112 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
1516
1516
  }]
1517
1517
  }], ctorParameters: () => [], propDecorators: { pshClickOutside: [{ type: i0.Output, args: ["pshClickOutside"] }] } });
1518
1518
 
1519
+ /**
1520
+ * Teleports a `TemplateRef` into a single body-level overlay layer so popover
1521
+ * panels (select / dropdown / menu…) escape any ancestor `overflow`, `transform`
1522
+ * or stacking context and can layer above modals.
1523
+ *
1524
+ * A lightweight, CDK-free "manual portal": the embedded view is created from the
1525
+ * consumer's `ViewContainerRef` (so it stays in that component's change-detection
1526
+ * tree and keeps its bindings reactive), then its root nodes are moved into the
1527
+ * shared `.psh-overlay-layer`. The layer is `position: fixed; inset: 0;
1528
+ * pointer-events: none` at `z-index: var(--z-index-overlay)`; panels re-enable
1529
+ * pointer events. The layer is created lazily and removed once empty.
1530
+ */
1531
+ class PshPortalService {
1532
+ constructor() {
1533
+ this.document = inject(DOCUMENT);
1534
+ this.layer = null;
1535
+ this.count = 0;
1536
+ }
1537
+ /** Teleports `tpl` into the overlay layer and returns a handle to control it. */
1538
+ attach(tpl, vcr) {
1539
+ const layer = this.ensureLayer();
1540
+ const viewRef = vcr.createEmbeddedView(tpl);
1541
+ viewRef.detectChanges();
1542
+ const nodes = viewRef.rootNodes;
1543
+ const panel = (nodes.find((n) => n instanceof HTMLElement) ??
1544
+ nodes[0]);
1545
+ nodes.forEach(node => layer.appendChild(node));
1546
+ this.count++;
1547
+ return {
1548
+ panel,
1549
+ position: (anchor, side, gap) => this.position(panel, anchor, side, gap),
1550
+ positionByPlacement: (anchor, placement, gap) => this.positionByPlacement(panel, anchor, placement, gap),
1551
+ detach: () => this.detach(viewRef),
1552
+ };
1553
+ }
1554
+ position(panel, anchor, side, gap) {
1555
+ const view = this.document.defaultView;
1556
+ if (!view)
1557
+ return;
1558
+ const rect = anchor.getBoundingClientRect();
1559
+ const style = panel.style;
1560
+ style.position = 'fixed';
1561
+ style.left = `${rect.left}px`;
1562
+ style.width = `${rect.width}px`;
1563
+ if (side === 'top') {
1564
+ const height = panel.offsetHeight;
1565
+ style.top = `${Math.max(gap, rect.top - height - gap)}px`;
1566
+ style.maxHeight = `${Math.max(0, rect.top - gap * 2)}px`;
1567
+ }
1568
+ else {
1569
+ style.top = `${rect.bottom + gap}px`;
1570
+ style.maxHeight = `${Math.max(0, view.innerHeight - rect.bottom - gap * 2)}px`;
1571
+ }
1572
+ }
1573
+ positionByPlacement(panel, anchor, placement, gap) {
1574
+ const view = this.document.defaultView;
1575
+ if (!view)
1576
+ return;
1577
+ const rect = anchor.getBoundingClientRect();
1578
+ const [side, align] = placement.split('-');
1579
+ const style = panel.style;
1580
+ style.position = 'fixed';
1581
+ if (side === 'top') {
1582
+ const height = panel.offsetHeight;
1583
+ style.top = `${Math.max(gap, rect.top - height - gap)}px`;
1584
+ style.maxHeight = `${Math.max(0, rect.top - gap * 2)}px`;
1585
+ }
1586
+ else {
1587
+ style.top = `${rect.bottom + gap}px`;
1588
+ style.maxHeight = `${Math.max(0, view.innerHeight - rect.bottom - gap * 2)}px`;
1589
+ }
1590
+ // Content-sized menu: anchor the start/end edge to the trigger.
1591
+ if (align === 'end') {
1592
+ style.left = `${Math.max(0, rect.right - panel.offsetWidth)}px`;
1593
+ }
1594
+ else {
1595
+ style.left = `${rect.left}px`;
1596
+ }
1597
+ }
1598
+ detach(viewRef) {
1599
+ viewRef.destroy();
1600
+ this.count = Math.max(0, this.count - 1);
1601
+ if (this.count === 0 && this.layer) {
1602
+ this.layer.remove();
1603
+ this.layer = null;
1604
+ }
1605
+ }
1606
+ ensureLayer() {
1607
+ if (this.layer)
1608
+ return this.layer;
1609
+ const layer = this.document.createElement('div');
1610
+ layer.className = 'psh-overlay-layer';
1611
+ layer.style.cssText =
1612
+ 'position:fixed;inset:0;z-index:var(--z-index-overlay);pointer-events:none;';
1613
+ this.document.body.appendChild(layer);
1614
+ this.layer = layer;
1615
+ return layer;
1616
+ }
1617
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshPortalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1618
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshPortalService, providedIn: 'root' }); }
1619
+ }
1620
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshPortalService, decorators: [{
1621
+ type: Injectable,
1622
+ args: [{ providedIn: 'root' }]
1623
+ }] });
1624
+
1519
1625
  class PshDropdownComponent {
1520
1626
  getState() {
1521
1627
  if (this.disabled())
@@ -1528,7 +1634,14 @@ class PshDropdownComponent {
1528
1634
  this.elementRef = inject(ElementRef);
1529
1635
  this.isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
1530
1636
  this.overlayPosition = inject(PshOverlayPositionService);
1531
- this.injector = inject(Injector);
1637
+ this.portal = inject(PshPortalService);
1638
+ this.viewContainer = inject(ViewContainerRef);
1639
+ // The menu is teleported to a body-level overlay layer on open so it escapes
1640
+ // any ancestor overflow / stacking context (a modal body, a scrollable card…).
1641
+ this.menuTpl = viewChild('menuTpl', /* @ts-ignore */
1642
+ ...(ngDevMode ? [{ debugName: "menuTpl" }] : /* istanbul ignore next */ []));
1643
+ this.portalRef = null;
1644
+ this.repositionHandler = () => this.reposition();
1532
1645
  // Regular inputs
1533
1646
  this.appearance = input('filled', /* @ts-ignore */
1534
1647
  ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
@@ -1584,33 +1697,54 @@ class PshDropdownComponent {
1584
1697
  ...(ngDevMode ? [{ debugName: "computedAriaLabel" }] : /* istanbul ignore next */ []));
1585
1698
  this.state = computed(() => this.getState(), /* @ts-ignore */
1586
1699
  ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
1587
- // Close on outside click via the shared click-outside primitive.
1700
+ // Close on outside click. Clicks inside the menu are stopped at the menu
1701
+ // (stopPropagation); as a safety net for the teleported panel we also ignore
1702
+ // clicks whose target is inside it here.
1588
1703
  const clickOutside = inject(PshClickOutsideDirective);
1589
- const sub = clickOutside.pshClickOutside.subscribe(() => this.close());
1590
- inject(DestroyRef).onDestroy(() => sub.unsubscribe());
1591
- // Keep resolvedPlacement mirroring the input while closed; flip against the
1592
- // viewport (after the menu has rendered) when opened.
1593
- effect(() => {
1594
- if (this.isOpen()) {
1595
- afterNextRender(() => this.reposition(), { injector: this.injector });
1596
- }
1597
- else {
1598
- this.resolvedPlacement.set(this.placement());
1599
- }
1704
+ const sub = clickOutside.pshClickOutside.subscribe(event => {
1705
+ const target = event.target;
1706
+ if (target && this.portalRef?.panel.contains(target))
1707
+ return;
1708
+ this.close();
1709
+ });
1710
+ inject(DestroyRef).onDestroy(() => {
1711
+ sub.unsubscribe();
1712
+ this.closePanel();
1600
1713
  });
1601
1714
  }
1715
+ openPanel() {
1716
+ if (!this.isBrowser || this.portalRef)
1717
+ return;
1718
+ const tpl = this.menuTpl();
1719
+ if (!tpl)
1720
+ return;
1721
+ this.portalRef = this.portal.attach(tpl, this.viewContainer);
1722
+ this.reposition();
1723
+ const view = this.elementRef.nativeElement.ownerDocument.defaultView;
1724
+ view?.addEventListener('scroll', this.repositionHandler, true);
1725
+ view?.addEventListener('resize', this.repositionHandler);
1726
+ }
1727
+ closePanel() {
1728
+ const view = this.elementRef.nativeElement.ownerDocument.defaultView;
1729
+ view?.removeEventListener('scroll', this.repositionHandler, true);
1730
+ view?.removeEventListener('resize', this.repositionHandler);
1731
+ this.portalRef?.detach();
1732
+ this.portalRef = null;
1733
+ this.resolvedPlacement.set(this.placement());
1734
+ }
1602
1735
  reposition() {
1603
- if (!this.isBrowser || !this.isOpen())
1736
+ if (!this.portalRef)
1604
1737
  return;
1605
1738
  const host = this.elementRef.nativeElement;
1606
1739
  const trigger = host.querySelector('.dropdown-trigger');
1607
- const menu = host.querySelector('.dropdown-menu');
1608
1740
  if (!trigger)
1609
1741
  return;
1610
- this.resolvedPlacement.set(this.overlayPosition.flipPlacement(trigger, this.placement(), {
1611
- overlayHeight: menu?.offsetHeight ?? 0,
1612
- overlayWidth: menu?.offsetWidth ?? 0,
1613
- }));
1742
+ const placement = this.overlayPosition.flipPlacement(trigger, this.placement(), {
1743
+ overlayHeight: this.portalRef.panel.offsetHeight,
1744
+ overlayWidth: this.portalRef.panel.offsetWidth,
1745
+ });
1746
+ this.resolvedPlacement.set(placement);
1747
+ this.portalRef.positionByPlacement(trigger, placement, 4);
1614
1748
  }
1615
1749
  toggleDropdown() {
1616
1750
  if (!this.disabled()) {
@@ -1619,9 +1753,11 @@ class PshDropdownComponent {
1619
1753
  if (this.isOpen()) {
1620
1754
  this.focusedItemIndex.set(0);
1621
1755
  this.opened.emit();
1756
+ this.openPanel();
1622
1757
  }
1623
1758
  else {
1624
1759
  this.focusedItemIndex.set(-1);
1760
+ this.closePanel();
1625
1761
  this.closed.emit();
1626
1762
  }
1627
1763
  }
@@ -1637,6 +1773,7 @@ class PshDropdownComponent {
1637
1773
  if (this.isOpen()) {
1638
1774
  this.isOpenSignal.set(false);
1639
1775
  this.focusedItemIndex.set(-1);
1776
+ this.closePanel();
1640
1777
  this.closed.emit();
1641
1778
  }
1642
1779
  }
@@ -1771,17 +1908,17 @@ class PshDropdownComponent {
1771
1908
  }
1772
1909
  focusItemAtIndex(index) {
1773
1910
  setTimeout(() => {
1774
- const item = this.elementRef.nativeElement.querySelector(`[data-dropdown-item-index="${index}"]`);
1911
+ const item = this.portalRef?.panel.querySelector(`[data-dropdown-item-index="${index}"]`);
1775
1912
  item?.focus();
1776
1913
  });
1777
1914
  }
1778
1915
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1779
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: PshDropdownComponent, isStandalone: true, selector: "psh-dropdown", inputs: { appearance: { classPropertyName: "appearance", publicName: "appearance", 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 }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, iconOnlyText: { classPropertyName: "iconOnlyText", publicName: "iconOnlyText", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", selected: "selected", opened: "opened", closed: "closed" }, hostDirectives: [{ directive: PshClickOutsideDirective }], ngImport: i0, template: "<div\r\n class=\"dropdown-container\"\r\n [class]=\"size()\"\r\n [class.open]=\"isOpen()\"\r\n [class.disabled]=\"disabled()\"\r\n [class.icon-only]=\"isIconOnly()\"\r\n [attr.data-state]=\"state()\"\r\n>\r\n <button\r\n class=\"dropdown-trigger\"\r\n [class]=\"appearance()\"\r\n [class.primary]=\"variant() === 'primary'\"\r\n [class.secondary]=\"variant() === 'secondary'\"\r\n [class.success]=\"variant() === 'success'\"\r\n [class.warning]=\"variant() === 'warning'\"\r\n [class.danger]=\"variant() === 'danger'\"\r\n (click)=\"toggleDropdown()\"\r\n (keydown)=\"handleTriggerKeyDown($event)\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-disabled]=\"disabled()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-haspopup]=\"'menu'\"\r\n type=\"button\"\r\n [disabled]=\"disabled()\"\r\n >\r\n <span class=\"trigger-content\">\r\n @if (isIconOnly()) {\r\n <i class=\"ph ph-{{ icon() }}\" aria-hidden=\"true\"></i>\r\n } @else {\r\n @if (icon()) {\r\n <i class=\"ph ph-{{ icon() }}\" aria-hidden=\"true\"></i>\r\n }\r\n <ng-content select=\"[dropdown-trigger]\">\r\n @if (label()) {\r\n {{ label() }}\r\n }\r\n </ng-content>\r\n <i\r\n class=\"ph ph-caret-down trigger-arrow\"\r\n [class.open]=\"isOpen()\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n }\r\n </span>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <!-- stopPropagation only (keeps clicks inside the menu from closing it); not a user interaction. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <div\r\n class=\"dropdown-menu\"\r\n [class]=\"resolvedPlacement()\"\r\n role=\"menu\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n <ng-content select=\"[dropdown-menu]\">\r\n @for (item of items(); track item.value; let i = $index) {\r\n <button\r\n type=\"button\"\r\n class=\"dropdown-item\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.active]=\"selectedItem() === item\"\r\n [attr.data-dropdown-item-index]=\"i\"\r\n (click)=\"selectItem(item)\"\r\n (keydown)=\"handleItemKeyDown($event, item, i)\"\r\n role=\"menuitem\"\r\n [attr.aria-disabled]=\"item.disabled\"\r\n [disabled]=\"item.disabled\"\r\n [attr.tabindex]=\"item.disabled ? -1 : 0\"\r\n >\r\n @if (item.icon) {\r\n <i class=\"ph ph-{{ item.icon }}\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"item-label\">{{ item.content }}</span>\r\n </button>\r\n }\r\n </ng-content>\r\n </div>\r\n }\r\n</div>", styles: [".dropdown-container{position:relative;display:inline-block}.dropdown-trigger{display:inline-flex;align-items:center;justify-content:center;height:var(--height-input-md);padding:0 var(--spacing-lg);border:none;border-radius:var(--border-radius);font-weight:var(--font-weight-medium);font-size:var(--font-size-base);font-family:var(--font-family);line-height:var(--line-height-normal);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth);min-width:7.5rem;background:transparent;position:relative;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.trigger-content{display:flex;align-items:center;gap:var(--spacing-sm);position:relative;z-index:1;line-height:1}.trigger-arrow{transition:transform var(--animation-duration-fast) var(--animation-easing-smooth);margin-left:var(--spacing-sm)}.trigger-arrow.open{transform:rotate(180deg)}.dropdown-trigger.primary{color:var(--primary-color)}.dropdown-trigger.secondary{color:var(--secondary-color)}.dropdown-trigger.success{color:var(--success-color)}.dropdown-trigger.warning{color:var(--warning-color)}.dropdown-trigger.danger{color:var(--danger-color)}.dropdown-trigger.filled{background-color:currentColor}.dropdown-trigger.filled.primary{background:var(--primary-gradient);color:var(--text-on-primary)}.dropdown-trigger.filled.primary .trigger-content{color:var(--text-on-primary)}.dropdown-trigger.filled.secondary .trigger-content{color:var(--text-on-secondary)}.dropdown-trigger.filled.success .trigger-content{color:var(--text-on-success)}.dropdown-trigger.filled.warning .trigger-content{color:var(--text-on-warning)}.dropdown-trigger.filled.danger .trigger-content{color:var(--text-on-danger)}.dropdown-trigger.filled.primary:hover:not(.disabled){background:var(--primary-gradient-hover)}.dropdown-trigger.filled.secondary:hover:not(.disabled){background-color:var(--secondary-color-light)}.dropdown-trigger.filled.success:hover:not(.disabled){background-color:var(--green-400)}.dropdown-trigger.filled.warning:hover:not(.disabled){background-color:var(--orange-400)}.dropdown-trigger.filled.danger:hover:not(.disabled){background-color:var(--red-400)}.dropdown-trigger.outline{background:transparent;border:var(--border-width-2) solid currentColor}.dropdown-trigger.outline .trigger-content{color:inherit}.dropdown-trigger.outline:hover:not(.disabled){background:currentColor}.dropdown-trigger.outline.primary:hover:not(.disabled) .trigger-content{color:var(--text-on-primary)}.dropdown-trigger.outline.secondary:hover:not(.disabled) .trigger-content{color:var(--text-on-secondary)}.dropdown-trigger.outline.success:hover:not(.disabled) .trigger-content{color:var(--text-on-success)}.dropdown-trigger.outline.warning:hover:not(.disabled) .trigger-content{color:var(--text-on-warning)}.dropdown-trigger.outline.danger:hover:not(.disabled) .trigger-content{color:var(--text-on-danger)}.dropdown-trigger.text{background:transparent}.dropdown-trigger.text:hover:not(.disabled){background:var(--surface-hover)}.dropdown-container.small .dropdown-trigger{height:var(--height-input-sm);padding:0 var(--spacing-md);font-size:var(--font-size-sm);min-width:6rem}.dropdown-container.large .dropdown-trigger{height:var(--height-input-lg);padding:0 var(--spacing-lg);font-size:var(--font-size-lg);min-width:8.75rem}.dropdown-container.icon-only .dropdown-trigger{min-width:0;width:var(--height-input-md);padding:0}.dropdown-container.icon-only.small .dropdown-trigger{width:var(--height-input-sm);padding:0;min-width:0}.dropdown-container.icon-only.large .dropdown-trigger{width:var(--height-input-lg);padding:0;min-width:0}.dropdown-container.icon-only .trigger-content{gap:0;justify-content:center}.dropdown-container.icon-only .trigger-content i{margin:0}.dropdown-menu{position:absolute;min-width:12.5rem;background:var(--surface-card);border:var(--border-width-1) solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg);z-index:var(--z-index-dropdown);margin-top:var(--spacing-xs);animation:fadeIn var(--animation-duration-fast) var(--animation-easing-smooth);overflow:hidden}.dropdown-menu ::ng-deep [dropdown-menu]{background:transparent;padding:0;margin:0}.dropdown-menu ::ng-deep .dropdown-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border:none;background:transparent;color:var(--text-color);font-family:var(--font-family);font-size:var(--font-size-base);text-align:left;cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth)}.dropdown-menu ::ng-deep .dropdown-item:hover:not(.disabled){background:var(--surface-hover)}.dropdown-menu ::ng-deep .dropdown-item.active{color:var(--primary-color);background:rgba(var(--primary-color-rgb),.1)}.dropdown-menu ::ng-deep .dropdown-item.disabled{opacity:.6;cursor:not-allowed}.dropdown-menu ::ng-deep .dropdown-item i{font-size:var(--icon-size-md);color:inherit}.dropdown-menu.bottom-start{top:100%;left:0}.dropdown-menu.bottom-end{top:100%;right:0}.dropdown-menu.top-start{bottom:100%;left:0}.dropdown-menu.top-end{bottom:100%;right:0}.dropdown-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border:none;background:transparent;color:var(--text-color);font-family:var(--font-family);font-size:var(--font-size-base);text-align:left;cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth)}.dropdown-item:hover:not(.disabled){background:var(--surface-hover)}.dropdown-item.active{color:var(--primary-color);background:rgba(var(--primary-color-rgb),.1)}.dropdown-item.disabled{opacity:.6;cursor:not-allowed}.item-check{margin-left:auto;color:var(--primary-color)}.dropdown-trigger:focus-visible{outline:none;box-shadow:0 0 0 var(--focus-ring-width) var(--focus-ring-color)}.dropdown-trigger:active:not(.disabled){transform:translateY(.0625rem)}.dropdown-container.disabled{opacity:.6;cursor:not-allowed}.dropdown-container.disabled .dropdown-trigger{pointer-events:none}@keyframes fadeIn{0%{opacity:0;transform:translateY(calc(-1 * var(--animation-distance-sm)))}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1916
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: PshDropdownComponent, isStandalone: true, selector: "psh-dropdown", inputs: { appearance: { classPropertyName: "appearance", publicName: "appearance", 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 }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, iconOnlyText: { classPropertyName: "iconOnlyText", publicName: "iconOnlyText", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", selected: "selected", opened: "opened", closed: "closed" }, viewQueries: [{ propertyName: "menuTpl", first: true, predicate: ["menuTpl"], descendants: true, isSignal: true }], hostDirectives: [{ directive: PshClickOutsideDirective }], ngImport: i0, template: "<div\r\n class=\"dropdown-container\"\r\n [class]=\"size()\"\r\n [class.open]=\"isOpen()\"\r\n [class.disabled]=\"disabled()\"\r\n [class.icon-only]=\"isIconOnly()\"\r\n [attr.data-state]=\"state()\"\r\n>\r\n <button\r\n class=\"dropdown-trigger\"\r\n [class]=\"appearance()\"\r\n [class.primary]=\"variant() === 'primary'\"\r\n [class.secondary]=\"variant() === 'secondary'\"\r\n [class.success]=\"variant() === 'success'\"\r\n [class.warning]=\"variant() === 'warning'\"\r\n [class.danger]=\"variant() === 'danger'\"\r\n (click)=\"toggleDropdown()\"\r\n (keydown)=\"handleTriggerKeyDown($event)\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-disabled]=\"disabled()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-haspopup]=\"'menu'\"\r\n type=\"button\"\r\n [disabled]=\"disabled()\"\r\n >\r\n <span class=\"trigger-content\">\r\n @if (isIconOnly()) {\r\n <i class=\"ph ph-{{ icon() }}\" aria-hidden=\"true\"></i>\r\n } @else {\r\n @if (icon()) {\r\n <i class=\"ph ph-{{ icon() }}\" aria-hidden=\"true\"></i>\r\n }\r\n <ng-content select=\"[dropdown-trigger]\">\r\n @if (label()) {\r\n {{ label() }}\r\n }\r\n </ng-content>\r\n <i\r\n class=\"ph ph-caret-down trigger-arrow\"\r\n [class.open]=\"isOpen()\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n }\r\n </span>\r\n </button>\r\n\r\n <ng-template #menuTpl>\r\n <!-- stopPropagation only (keeps clicks inside the menu from closing it); not a user interaction. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <div\r\n class=\"dropdown-menu\"\r\n [class]=\"resolvedPlacement()\"\r\n role=\"menu\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n <ng-content select=\"[dropdown-menu]\">\r\n @for (item of items(); track item.value; let i = $index) {\r\n <button\r\n type=\"button\"\r\n class=\"dropdown-item\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.active]=\"selectedItem() === item\"\r\n [attr.data-dropdown-item-index]=\"i\"\r\n (click)=\"selectItem(item)\"\r\n (keydown)=\"handleItemKeyDown($event, item, i)\"\r\n role=\"menuitem\"\r\n [attr.aria-disabled]=\"item.disabled\"\r\n [disabled]=\"item.disabled\"\r\n [attr.tabindex]=\"item.disabled ? -1 : 0\"\r\n >\r\n @if (item.icon) {\r\n <i class=\"ph ph-{{ item.icon }}\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"item-label\">{{ item.content }}</span>\r\n </button>\r\n }\r\n </ng-content>\r\n </div>\r\n </ng-template>\r\n</div>", styles: [".dropdown-container{position:relative;display:inline-block}.dropdown-trigger{display:inline-flex;align-items:center;justify-content:center;height:var(--height-input-md);padding:0 var(--spacing-lg);border:none;border-radius:var(--border-radius);font-weight:var(--font-weight-medium);font-size:var(--font-size-base);font-family:var(--font-family);line-height:var(--line-height-normal);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth);min-width:7.5rem;background:transparent;position:relative;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.trigger-content{display:flex;align-items:center;gap:var(--spacing-sm);position:relative;z-index:1;line-height:1}.trigger-arrow{transition:transform var(--animation-duration-fast) var(--animation-easing-smooth);margin-left:var(--spacing-sm)}.trigger-arrow.open{transform:rotate(180deg)}.dropdown-trigger.primary{color:var(--primary-color)}.dropdown-trigger.secondary{color:var(--secondary-color)}.dropdown-trigger.success{color:var(--success-color)}.dropdown-trigger.warning{color:var(--warning-color)}.dropdown-trigger.danger{color:var(--danger-color)}.dropdown-trigger.filled{background-color:currentColor}.dropdown-trigger.filled.primary{background:var(--primary-gradient);color:var(--text-on-primary)}.dropdown-trigger.filled.primary .trigger-content{color:var(--text-on-primary)}.dropdown-trigger.filled.secondary .trigger-content{color:var(--text-on-secondary)}.dropdown-trigger.filled.success .trigger-content{color:var(--text-on-success)}.dropdown-trigger.filled.warning .trigger-content{color:var(--text-on-warning)}.dropdown-trigger.filled.danger .trigger-content{color:var(--text-on-danger)}.dropdown-trigger.filled.primary:hover:not(.disabled){background:var(--primary-gradient-hover)}.dropdown-trigger.filled.secondary:hover:not(.disabled){background-color:var(--secondary-color-light)}.dropdown-trigger.filled.success:hover:not(.disabled){background-color:var(--green-400)}.dropdown-trigger.filled.warning:hover:not(.disabled){background-color:var(--orange-400)}.dropdown-trigger.filled.danger:hover:not(.disabled){background-color:var(--red-400)}.dropdown-trigger.outline{background:transparent;border:var(--border-width-2) solid currentColor}.dropdown-trigger.outline .trigger-content{color:inherit}.dropdown-trigger.outline:hover:not(.disabled){background:currentColor}.dropdown-trigger.outline.primary:hover:not(.disabled) .trigger-content{color:var(--text-on-primary)}.dropdown-trigger.outline.secondary:hover:not(.disabled) .trigger-content{color:var(--text-on-secondary)}.dropdown-trigger.outline.success:hover:not(.disabled) .trigger-content{color:var(--text-on-success)}.dropdown-trigger.outline.warning:hover:not(.disabled) .trigger-content{color:var(--text-on-warning)}.dropdown-trigger.outline.danger:hover:not(.disabled) .trigger-content{color:var(--text-on-danger)}.dropdown-trigger.text{background:transparent}.dropdown-trigger.text:hover:not(.disabled){background:var(--surface-hover)}.dropdown-container.small .dropdown-trigger{height:var(--height-input-sm);padding:0 var(--spacing-md);font-size:var(--font-size-sm);min-width:6rem}.dropdown-container.large .dropdown-trigger{height:var(--height-input-lg);padding:0 var(--spacing-lg);font-size:var(--font-size-lg);min-width:8.75rem}.dropdown-container.icon-only .dropdown-trigger{min-width:0;width:var(--height-input-md);padding:0}.dropdown-container.icon-only.small .dropdown-trigger{width:var(--height-input-sm);padding:0;min-width:0}.dropdown-container.icon-only.large .dropdown-trigger{width:var(--height-input-lg);padding:0;min-width:0}.dropdown-container.icon-only .trigger-content{gap:0;justify-content:center}.dropdown-container.icon-only .trigger-content i{margin:0}.dropdown-menu{min-width:12.5rem;background:var(--surface-card);border:var(--border-width-1) solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg);pointer-events:auto;animation:fadeIn var(--animation-duration-fast) var(--animation-easing-smooth);overflow:hidden}.dropdown-menu ::ng-deep [dropdown-menu]{background:transparent;padding:0;margin:0}.dropdown-menu ::ng-deep .dropdown-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border:none;background:transparent;color:var(--text-color);font-family:var(--font-family);font-size:var(--font-size-base);text-align:left;cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth)}.dropdown-menu ::ng-deep .dropdown-item:hover:not(.disabled){background:var(--surface-hover)}.dropdown-menu ::ng-deep .dropdown-item.active{color:var(--primary-color);background:rgba(var(--primary-color-rgb),.1)}.dropdown-menu ::ng-deep .dropdown-item.disabled{opacity:.6;cursor:not-allowed}.dropdown-menu ::ng-deep .dropdown-item i{font-size:var(--icon-size-md);color:inherit}.dropdown-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border:none;background:transparent;color:var(--text-color);font-family:var(--font-family);font-size:var(--font-size-base);text-align:left;cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth)}.dropdown-item:hover:not(.disabled){background:var(--surface-hover)}.dropdown-item.active{color:var(--primary-color);background:rgba(var(--primary-color-rgb),.1)}.dropdown-item.disabled{opacity:.6;cursor:not-allowed}.item-check{margin-left:auto;color:var(--primary-color)}.dropdown-trigger:focus-visible{outline:none;box-shadow:0 0 0 var(--focus-ring-width) var(--focus-ring-color)}.dropdown-trigger:active:not(.disabled){transform:translateY(.0625rem)}.dropdown-container.disabled{opacity:.6;cursor:not-allowed}.dropdown-container.disabled .dropdown-trigger{pointer-events:none}@keyframes fadeIn{0%{opacity:0;transform:translateY(calc(-1 * var(--animation-distance-sm)))}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1780
1917
  }
1781
1918
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshDropdownComponent, decorators: [{
1782
1919
  type: Component,
1783
- args: [{ selector: 'psh-dropdown', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, hostDirectives: [PshClickOutsideDirective], template: "<div\r\n class=\"dropdown-container\"\r\n [class]=\"size()\"\r\n [class.open]=\"isOpen()\"\r\n [class.disabled]=\"disabled()\"\r\n [class.icon-only]=\"isIconOnly()\"\r\n [attr.data-state]=\"state()\"\r\n>\r\n <button\r\n class=\"dropdown-trigger\"\r\n [class]=\"appearance()\"\r\n [class.primary]=\"variant() === 'primary'\"\r\n [class.secondary]=\"variant() === 'secondary'\"\r\n [class.success]=\"variant() === 'success'\"\r\n [class.warning]=\"variant() === 'warning'\"\r\n [class.danger]=\"variant() === 'danger'\"\r\n (click)=\"toggleDropdown()\"\r\n (keydown)=\"handleTriggerKeyDown($event)\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-disabled]=\"disabled()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-haspopup]=\"'menu'\"\r\n type=\"button\"\r\n [disabled]=\"disabled()\"\r\n >\r\n <span class=\"trigger-content\">\r\n @if (isIconOnly()) {\r\n <i class=\"ph ph-{{ icon() }}\" aria-hidden=\"true\"></i>\r\n } @else {\r\n @if (icon()) {\r\n <i class=\"ph ph-{{ icon() }}\" aria-hidden=\"true\"></i>\r\n }\r\n <ng-content select=\"[dropdown-trigger]\">\r\n @if (label()) {\r\n {{ label() }}\r\n }\r\n </ng-content>\r\n <i\r\n class=\"ph ph-caret-down trigger-arrow\"\r\n [class.open]=\"isOpen()\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n }\r\n </span>\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <!-- stopPropagation only (keeps clicks inside the menu from closing it); not a user interaction. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <div\r\n class=\"dropdown-menu\"\r\n [class]=\"resolvedPlacement()\"\r\n role=\"menu\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n <ng-content select=\"[dropdown-menu]\">\r\n @for (item of items(); track item.value; let i = $index) {\r\n <button\r\n type=\"button\"\r\n class=\"dropdown-item\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.active]=\"selectedItem() === item\"\r\n [attr.data-dropdown-item-index]=\"i\"\r\n (click)=\"selectItem(item)\"\r\n (keydown)=\"handleItemKeyDown($event, item, i)\"\r\n role=\"menuitem\"\r\n [attr.aria-disabled]=\"item.disabled\"\r\n [disabled]=\"item.disabled\"\r\n [attr.tabindex]=\"item.disabled ? -1 : 0\"\r\n >\r\n @if (item.icon) {\r\n <i class=\"ph ph-{{ item.icon }}\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"item-label\">{{ item.content }}</span>\r\n </button>\r\n }\r\n </ng-content>\r\n </div>\r\n }\r\n</div>", styles: [".dropdown-container{position:relative;display:inline-block}.dropdown-trigger{display:inline-flex;align-items:center;justify-content:center;height:var(--height-input-md);padding:0 var(--spacing-lg);border:none;border-radius:var(--border-radius);font-weight:var(--font-weight-medium);font-size:var(--font-size-base);font-family:var(--font-family);line-height:var(--line-height-normal);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth);min-width:7.5rem;background:transparent;position:relative;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.trigger-content{display:flex;align-items:center;gap:var(--spacing-sm);position:relative;z-index:1;line-height:1}.trigger-arrow{transition:transform var(--animation-duration-fast) var(--animation-easing-smooth);margin-left:var(--spacing-sm)}.trigger-arrow.open{transform:rotate(180deg)}.dropdown-trigger.primary{color:var(--primary-color)}.dropdown-trigger.secondary{color:var(--secondary-color)}.dropdown-trigger.success{color:var(--success-color)}.dropdown-trigger.warning{color:var(--warning-color)}.dropdown-trigger.danger{color:var(--danger-color)}.dropdown-trigger.filled{background-color:currentColor}.dropdown-trigger.filled.primary{background:var(--primary-gradient);color:var(--text-on-primary)}.dropdown-trigger.filled.primary .trigger-content{color:var(--text-on-primary)}.dropdown-trigger.filled.secondary .trigger-content{color:var(--text-on-secondary)}.dropdown-trigger.filled.success .trigger-content{color:var(--text-on-success)}.dropdown-trigger.filled.warning .trigger-content{color:var(--text-on-warning)}.dropdown-trigger.filled.danger .trigger-content{color:var(--text-on-danger)}.dropdown-trigger.filled.primary:hover:not(.disabled){background:var(--primary-gradient-hover)}.dropdown-trigger.filled.secondary:hover:not(.disabled){background-color:var(--secondary-color-light)}.dropdown-trigger.filled.success:hover:not(.disabled){background-color:var(--green-400)}.dropdown-trigger.filled.warning:hover:not(.disabled){background-color:var(--orange-400)}.dropdown-trigger.filled.danger:hover:not(.disabled){background-color:var(--red-400)}.dropdown-trigger.outline{background:transparent;border:var(--border-width-2) solid currentColor}.dropdown-trigger.outline .trigger-content{color:inherit}.dropdown-trigger.outline:hover:not(.disabled){background:currentColor}.dropdown-trigger.outline.primary:hover:not(.disabled) .trigger-content{color:var(--text-on-primary)}.dropdown-trigger.outline.secondary:hover:not(.disabled) .trigger-content{color:var(--text-on-secondary)}.dropdown-trigger.outline.success:hover:not(.disabled) .trigger-content{color:var(--text-on-success)}.dropdown-trigger.outline.warning:hover:not(.disabled) .trigger-content{color:var(--text-on-warning)}.dropdown-trigger.outline.danger:hover:not(.disabled) .trigger-content{color:var(--text-on-danger)}.dropdown-trigger.text{background:transparent}.dropdown-trigger.text:hover:not(.disabled){background:var(--surface-hover)}.dropdown-container.small .dropdown-trigger{height:var(--height-input-sm);padding:0 var(--spacing-md);font-size:var(--font-size-sm);min-width:6rem}.dropdown-container.large .dropdown-trigger{height:var(--height-input-lg);padding:0 var(--spacing-lg);font-size:var(--font-size-lg);min-width:8.75rem}.dropdown-container.icon-only .dropdown-trigger{min-width:0;width:var(--height-input-md);padding:0}.dropdown-container.icon-only.small .dropdown-trigger{width:var(--height-input-sm);padding:0;min-width:0}.dropdown-container.icon-only.large .dropdown-trigger{width:var(--height-input-lg);padding:0;min-width:0}.dropdown-container.icon-only .trigger-content{gap:0;justify-content:center}.dropdown-container.icon-only .trigger-content i{margin:0}.dropdown-menu{position:absolute;min-width:12.5rem;background:var(--surface-card);border:var(--border-width-1) solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg);z-index:var(--z-index-dropdown);margin-top:var(--spacing-xs);animation:fadeIn var(--animation-duration-fast) var(--animation-easing-smooth);overflow:hidden}.dropdown-menu ::ng-deep [dropdown-menu]{background:transparent;padding:0;margin:0}.dropdown-menu ::ng-deep .dropdown-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border:none;background:transparent;color:var(--text-color);font-family:var(--font-family);font-size:var(--font-size-base);text-align:left;cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth)}.dropdown-menu ::ng-deep .dropdown-item:hover:not(.disabled){background:var(--surface-hover)}.dropdown-menu ::ng-deep .dropdown-item.active{color:var(--primary-color);background:rgba(var(--primary-color-rgb),.1)}.dropdown-menu ::ng-deep .dropdown-item.disabled{opacity:.6;cursor:not-allowed}.dropdown-menu ::ng-deep .dropdown-item i{font-size:var(--icon-size-md);color:inherit}.dropdown-menu.bottom-start{top:100%;left:0}.dropdown-menu.bottom-end{top:100%;right:0}.dropdown-menu.top-start{bottom:100%;left:0}.dropdown-menu.top-end{bottom:100%;right:0}.dropdown-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border:none;background:transparent;color:var(--text-color);font-family:var(--font-family);font-size:var(--font-size-base);text-align:left;cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth)}.dropdown-item:hover:not(.disabled){background:var(--surface-hover)}.dropdown-item.active{color:var(--primary-color);background:rgba(var(--primary-color-rgb),.1)}.dropdown-item.disabled{opacity:.6;cursor:not-allowed}.item-check{margin-left:auto;color:var(--primary-color)}.dropdown-trigger:focus-visible{outline:none;box-shadow:0 0 0 var(--focus-ring-width) var(--focus-ring-color)}.dropdown-trigger:active:not(.disabled){transform:translateY(.0625rem)}.dropdown-container.disabled{opacity:.6;cursor:not-allowed}.dropdown-container.disabled .dropdown-trigger{pointer-events:none}@keyframes fadeIn{0%{opacity:0;transform:translateY(calc(-1 * var(--animation-distance-sm)))}to{opacity:1;transform:translateY(0)}}\n"] }]
1784
- }], ctorParameters: () => [], propDecorators: { appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], iconOnlyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnlyText", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], selected: [{ type: i0.Output, args: ["selected"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
1920
+ args: [{ selector: 'psh-dropdown', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, hostDirectives: [PshClickOutsideDirective], template: "<div\r\n class=\"dropdown-container\"\r\n [class]=\"size()\"\r\n [class.open]=\"isOpen()\"\r\n [class.disabled]=\"disabled()\"\r\n [class.icon-only]=\"isIconOnly()\"\r\n [attr.data-state]=\"state()\"\r\n>\r\n <button\r\n class=\"dropdown-trigger\"\r\n [class]=\"appearance()\"\r\n [class.primary]=\"variant() === 'primary'\"\r\n [class.secondary]=\"variant() === 'secondary'\"\r\n [class.success]=\"variant() === 'success'\"\r\n [class.warning]=\"variant() === 'warning'\"\r\n [class.danger]=\"variant() === 'danger'\"\r\n (click)=\"toggleDropdown()\"\r\n (keydown)=\"handleTriggerKeyDown($event)\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-disabled]=\"disabled()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-haspopup]=\"'menu'\"\r\n type=\"button\"\r\n [disabled]=\"disabled()\"\r\n >\r\n <span class=\"trigger-content\">\r\n @if (isIconOnly()) {\r\n <i class=\"ph ph-{{ icon() }}\" aria-hidden=\"true\"></i>\r\n } @else {\r\n @if (icon()) {\r\n <i class=\"ph ph-{{ icon() }}\" aria-hidden=\"true\"></i>\r\n }\r\n <ng-content select=\"[dropdown-trigger]\">\r\n @if (label()) {\r\n {{ label() }}\r\n }\r\n </ng-content>\r\n <i\r\n class=\"ph ph-caret-down trigger-arrow\"\r\n [class.open]=\"isOpen()\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n }\r\n </span>\r\n </button>\r\n\r\n <ng-template #menuTpl>\r\n <!-- stopPropagation only (keeps clicks inside the menu from closing it); not a user interaction. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <div\r\n class=\"dropdown-menu\"\r\n [class]=\"resolvedPlacement()\"\r\n role=\"menu\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n <ng-content select=\"[dropdown-menu]\">\r\n @for (item of items(); track item.value; let i = $index) {\r\n <button\r\n type=\"button\"\r\n class=\"dropdown-item\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.active]=\"selectedItem() === item\"\r\n [attr.data-dropdown-item-index]=\"i\"\r\n (click)=\"selectItem(item)\"\r\n (keydown)=\"handleItemKeyDown($event, item, i)\"\r\n role=\"menuitem\"\r\n [attr.aria-disabled]=\"item.disabled\"\r\n [disabled]=\"item.disabled\"\r\n [attr.tabindex]=\"item.disabled ? -1 : 0\"\r\n >\r\n @if (item.icon) {\r\n <i class=\"ph ph-{{ item.icon }}\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"item-label\">{{ item.content }}</span>\r\n </button>\r\n }\r\n </ng-content>\r\n </div>\r\n </ng-template>\r\n</div>", styles: [".dropdown-container{position:relative;display:inline-block}.dropdown-trigger{display:inline-flex;align-items:center;justify-content:center;height:var(--height-input-md);padding:0 var(--spacing-lg);border:none;border-radius:var(--border-radius);font-weight:var(--font-weight-medium);font-size:var(--font-size-base);font-family:var(--font-family);line-height:var(--line-height-normal);cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth);min-width:7.5rem;background:transparent;position:relative;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.trigger-content{display:flex;align-items:center;gap:var(--spacing-sm);position:relative;z-index:1;line-height:1}.trigger-arrow{transition:transform var(--animation-duration-fast) var(--animation-easing-smooth);margin-left:var(--spacing-sm)}.trigger-arrow.open{transform:rotate(180deg)}.dropdown-trigger.primary{color:var(--primary-color)}.dropdown-trigger.secondary{color:var(--secondary-color)}.dropdown-trigger.success{color:var(--success-color)}.dropdown-trigger.warning{color:var(--warning-color)}.dropdown-trigger.danger{color:var(--danger-color)}.dropdown-trigger.filled{background-color:currentColor}.dropdown-trigger.filled.primary{background:var(--primary-gradient);color:var(--text-on-primary)}.dropdown-trigger.filled.primary .trigger-content{color:var(--text-on-primary)}.dropdown-trigger.filled.secondary .trigger-content{color:var(--text-on-secondary)}.dropdown-trigger.filled.success .trigger-content{color:var(--text-on-success)}.dropdown-trigger.filled.warning .trigger-content{color:var(--text-on-warning)}.dropdown-trigger.filled.danger .trigger-content{color:var(--text-on-danger)}.dropdown-trigger.filled.primary:hover:not(.disabled){background:var(--primary-gradient-hover)}.dropdown-trigger.filled.secondary:hover:not(.disabled){background-color:var(--secondary-color-light)}.dropdown-trigger.filled.success:hover:not(.disabled){background-color:var(--green-400)}.dropdown-trigger.filled.warning:hover:not(.disabled){background-color:var(--orange-400)}.dropdown-trigger.filled.danger:hover:not(.disabled){background-color:var(--red-400)}.dropdown-trigger.outline{background:transparent;border:var(--border-width-2) solid currentColor}.dropdown-trigger.outline .trigger-content{color:inherit}.dropdown-trigger.outline:hover:not(.disabled){background:currentColor}.dropdown-trigger.outline.primary:hover:not(.disabled) .trigger-content{color:var(--text-on-primary)}.dropdown-trigger.outline.secondary:hover:not(.disabled) .trigger-content{color:var(--text-on-secondary)}.dropdown-trigger.outline.success:hover:not(.disabled) .trigger-content{color:var(--text-on-success)}.dropdown-trigger.outline.warning:hover:not(.disabled) .trigger-content{color:var(--text-on-warning)}.dropdown-trigger.outline.danger:hover:not(.disabled) .trigger-content{color:var(--text-on-danger)}.dropdown-trigger.text{background:transparent}.dropdown-trigger.text:hover:not(.disabled){background:var(--surface-hover)}.dropdown-container.small .dropdown-trigger{height:var(--height-input-sm);padding:0 var(--spacing-md);font-size:var(--font-size-sm);min-width:6rem}.dropdown-container.large .dropdown-trigger{height:var(--height-input-lg);padding:0 var(--spacing-lg);font-size:var(--font-size-lg);min-width:8.75rem}.dropdown-container.icon-only .dropdown-trigger{min-width:0;width:var(--height-input-md);padding:0}.dropdown-container.icon-only.small .dropdown-trigger{width:var(--height-input-sm);padding:0;min-width:0}.dropdown-container.icon-only.large .dropdown-trigger{width:var(--height-input-lg);padding:0;min-width:0}.dropdown-container.icon-only .trigger-content{gap:0;justify-content:center}.dropdown-container.icon-only .trigger-content i{margin:0}.dropdown-menu{min-width:12.5rem;background:var(--surface-card);border:var(--border-width-1) solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg);pointer-events:auto;animation:fadeIn var(--animation-duration-fast) var(--animation-easing-smooth);overflow:hidden}.dropdown-menu ::ng-deep [dropdown-menu]{background:transparent;padding:0;margin:0}.dropdown-menu ::ng-deep .dropdown-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border:none;background:transparent;color:var(--text-color);font-family:var(--font-family);font-size:var(--font-size-base);text-align:left;cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth)}.dropdown-menu ::ng-deep .dropdown-item:hover:not(.disabled){background:var(--surface-hover)}.dropdown-menu ::ng-deep .dropdown-item.active{color:var(--primary-color);background:rgba(var(--primary-color-rgb),.1)}.dropdown-menu ::ng-deep .dropdown-item.disabled{opacity:.6;cursor:not-allowed}.dropdown-menu ::ng-deep .dropdown-item i{font-size:var(--icon-size-md);color:inherit}.dropdown-item{display:flex;align-items:center;gap:var(--spacing-sm);width:100%;padding:var(--spacing-sm) var(--spacing-md);border:none;background:transparent;color:var(--text-color);font-family:var(--font-family);font-size:var(--font-size-base);text-align:left;cursor:pointer;transition:all var(--animation-duration-fast) var(--animation-easing-smooth)}.dropdown-item:hover:not(.disabled){background:var(--surface-hover)}.dropdown-item.active{color:var(--primary-color);background:rgba(var(--primary-color-rgb),.1)}.dropdown-item.disabled{opacity:.6;cursor:not-allowed}.item-check{margin-left:auto;color:var(--primary-color)}.dropdown-trigger:focus-visible{outline:none;box-shadow:0 0 0 var(--focus-ring-width) var(--focus-ring-color)}.dropdown-trigger:active:not(.disabled){transform:translateY(.0625rem)}.dropdown-container.disabled{opacity:.6;cursor:not-allowed}.dropdown-container.disabled .dropdown-trigger{pointer-events:none}@keyframes fadeIn{0%{opacity:0;transform:translateY(calc(-1 * var(--animation-distance-sm)))}to{opacity:1;transform:translateY(0)}}\n"] }]
1921
+ }], ctorParameters: () => [], propDecorators: { menuTpl: [{ type: i0.ViewChild, args: ['menuTpl', { isSignal: true }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], iconOnlyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnlyText", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], selected: [{ type: i0.Output, args: ["selected"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
1785
1922
 
1786
1923
  const INPUT_LABELS = {
1787
1924
  showPassword: 'Afficher le mot de passe',
@@ -1809,6 +1946,16 @@ class PshInputComponent {
1809
1946
  this.elementRef = inject(ElementRef);
1810
1947
  this.cdr = inject(ChangeDetectorRef);
1811
1948
  this.destroyRef = inject(DestroyRef);
1949
+ this.portal = inject(PshPortalService);
1950
+ this.viewContainer = inject(ViewContainerRef);
1951
+ this.overlayPosition = inject(PshOverlayPositionService);
1952
+ // The autocomplete suggestions are teleported to a body-level overlay layer so
1953
+ // they escape any ancestor overflow / stacking context (a modal body, a
1954
+ // scrollable card…) instead of being clipped.
1955
+ this.suggestionsTpl = viewChild('suggestionsTpl', /* @ts-ignore */
1956
+ ...(ngDevMode ? [{ debugName: "suggestionsTpl" }] : /* istanbul ignore next */ []));
1957
+ this.portalRef = null;
1958
+ this.repositionHandler = () => this.reposition();
1812
1959
  this.inputId = `psh-input-${PshInputComponent.nextId++}`;
1813
1960
  this.value = model('', /* @ts-ignore */
1814
1961
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
@@ -1898,7 +2045,49 @@ class PshInputComponent {
1898
2045
  clearTimeout(this.blurTimeoutId);
1899
2046
  if (this.debounceTimeoutId)
1900
2047
  clearTimeout(this.debounceTimeoutId);
2048
+ this.closePanel();
2049
+ });
2050
+ }
2051
+ /** Attaches / detaches / repositions the teleported suggestions panel to match visibility. */
2052
+ syncPanel() {
2053
+ if (this.showSuggestions()) {
2054
+ if (this.portalRef)
2055
+ this.reposition();
2056
+ else
2057
+ this.openPanel();
2058
+ }
2059
+ else if (this.portalRef) {
2060
+ this.closePanel();
2061
+ }
2062
+ }
2063
+ openPanel() {
2064
+ const tpl = this.suggestionsTpl();
2065
+ if (!tpl || this.portalRef)
2066
+ return;
2067
+ this.portalRef = this.portal.attach(tpl, this.viewContainer);
2068
+ this.reposition();
2069
+ const view = this.elementRef.nativeElement.ownerDocument.defaultView;
2070
+ // Capture phase so inner (e.g. modal body) scrolls keep the panel aligned.
2071
+ view?.addEventListener('scroll', this.repositionHandler, true);
2072
+ view?.addEventListener('resize', this.repositionHandler);
2073
+ }
2074
+ closePanel() {
2075
+ const view = this.elementRef.nativeElement.ownerDocument.defaultView;
2076
+ view?.removeEventListener('scroll', this.repositionHandler, true);
2077
+ view?.removeEventListener('resize', this.repositionHandler);
2078
+ this.portalRef?.detach();
2079
+ this.portalRef = null;
2080
+ }
2081
+ reposition() {
2082
+ if (!this.portalRef)
2083
+ return;
2084
+ const anchor = this.elementRef.nativeElement.querySelector('.input-wrapper');
2085
+ if (!anchor)
2086
+ return;
2087
+ const side = this.overlayPosition.flipSide(anchor, 'bottom', {
2088
+ overlayHeight: this.portalRef.panel.offsetHeight,
1901
2089
  });
2090
+ this.portalRef.position(anchor, side, 4);
1902
2091
  }
1903
2092
  writeValue(value) {
1904
2093
  const safeValue = typeof value === 'string' ? value : '';
@@ -1936,6 +2125,7 @@ class PshInputComponent {
1936
2125
  this.blurTimeoutId = setTimeout(() => {
1937
2126
  this.suggestionsVisible.set(false);
1938
2127
  this.focusedSuggestionIndex.set(-1);
2128
+ this.syncPanel();
1939
2129
  this.blurTimeoutId = null;
1940
2130
  }, 200);
1941
2131
  }
@@ -1966,6 +2156,7 @@ class PshInputComponent {
1966
2156
  event.preventDefault();
1967
2157
  this.suggestionsVisible.set(false);
1968
2158
  this.focusedSuggestionIndex.set(-1);
2159
+ this.syncPanel();
1969
2160
  break;
1970
2161
  }
1971
2162
  }
@@ -1975,6 +2166,7 @@ class PshInputComponent {
1975
2166
  this.suggestionSelect.emit(suggestion);
1976
2167
  this.suggestionsVisible.set(false);
1977
2168
  this.focusedSuggestionIndex.set(-1);
2169
+ this.syncPanel();
1978
2170
  }
1979
2171
  togglePasswordVisibility() {
1980
2172
  this.passwordVisibleSignal.update(v => !v);
@@ -2009,12 +2201,14 @@ class PshInputComponent {
2009
2201
  }
2010
2202
  this.filteredSuggestionsSignal.set(results);
2011
2203
  this.suggestionsVisible.set(results.length > 0);
2204
+ this.syncPanel();
2012
2205
  }
2013
2206
  catch (error) {
2014
2207
  // Fail gracefully (hide stale suggestions) but surface the error so a
2015
2208
  // failing provider is not silently swallowed during development.
2016
2209
  this.filteredSuggestionsSignal.set([]);
2017
2210
  this.suggestionsVisible.set(false);
2211
+ this.syncPanel();
2018
2212
  console.error('[psh-input] Suggestion provider failed:', error);
2019
2213
  }
2020
2214
  }
@@ -2025,7 +2219,7 @@ class PshInputComponent {
2025
2219
  useExisting: PshInputComponent,
2026
2220
  multi: true
2027
2221
  }
2028
- ], ngImport: i0, template: "<div class=\"input-container\">\r\n @if (showLabel()) {\r\n <!-- label[for] delegates focus to its control natively; the click handler is a convenience. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <label\r\n class=\"input-label\"\r\n [for]=\"inputId\"\r\n (click)=\"focusSelect()\"\r\n [class.required]=\"required()\"\r\n >\r\n <ng-content select=\"[input-label]\"></ng-content>\r\n @if (!hasLabelContent()) {\r\n {{ label() }}\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"input-wrapper\">\r\n @if (iconStart()) {\r\n <i class=\"ph ph-{{ iconStart() }}\" aria-hidden=\"true\"></i>\r\n }\r\n\r\n <input\r\n [id]=\"inputId\"\r\n [type]=\"effectiveType()\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-invalid]=\"!!error()\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-describedby]=\"error() ? 'error-message' : success() ? 'success-message' : hint() ? 'hint-message' : null\"\r\n [disabled]=\"disabled()\"\r\n [readonly]=\"readonly()\"\r\n [required]=\"required()\"\r\n [value]=\"value()\"\r\n (input)=\"handleInput($event)\"\r\n (keydown)=\"handleKeydown($event)\"\r\n (focus)=\"handleFocus()\"\r\n (blur)=\"handleBlur()\"\r\n [attr.data-state]=\"state()\"\r\n />\r\n\r\n @if (iconEnd()) {\r\n <i class=\"ph ph-{{ iconEnd() }}\" aria-hidden=\"true\"></i>\r\n }\r\n\r\n @if (type() === 'password') {\r\n <button\r\n type=\"button\"\r\n class=\"password-toggle\"\r\n (click)=\"togglePasswordVisibility()\"\r\n [attr.aria-label]=\"passwordToggleLabel()\"\r\n [disabled]=\"disabled() || readonly()\"\r\n >\r\n <i \r\n [class]=\"'ph ph-' + (passwordVisible() ? 'eye-slash' : 'eye')\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n }\r\n\r\n @if (loading()) {\r\n <div class=\"input-loader\" aria-hidden=\"true\">\r\n <div class=\"loader\"></div>\r\n </div>\r\n }\r\n\r\n @if (showSuggestions()) {\r\n <div class=\"suggestions-list\" role=\"listbox\">\r\n @for (suggestion of filteredSuggestions(); track suggestion; let i = $index) {\r\n <!-- option in a listbox: keyboard is handled by the input (arrow keys + focusedSuggestionIndex). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <div\r\n class=\"suggestion-item\"\r\n [class.focused]=\"i === focusedSuggestionIndex()\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"i === focusedSuggestionIndex()\"\r\n (click)=\"handleSuggestionClick(suggestion)\"\r\n (mouseenter)=\"focusedSuggestionIndex.set(i)\"\r\n >\r\n {{ suggestion }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (error()) {\r\n <div id=\"error-message\" class=\"input-error\" role=\"alert\">\r\n <ng-content select=\"[input-error]\">\r\n {{ error() }}\r\n </ng-content>\r\n </div>\r\n } @else if (success()) {\r\n <div id=\"success-message\" class=\"input-success\" role=\"status\">\r\n <ng-content select=\"[input-success]\">\r\n {{ success() }}\r\n </ng-content>\r\n </div>\r\n } @else if (hint()) {\r\n <div id=\"hint-message\" class=\"input-hint\">\r\n <ng-content select=\"[input-hint]\">\r\n {{ hint() }}\r\n </ng-content>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:block;width:100%;max-width:18.75rem;transition:all var(--animation-duration-default) var(--animation-easing-default)}:host.full-width{max-width:none}.input-container{position:relative;width:100%;display:flex;flex-direction:column;gap:var(--form-field-gap)}.input-label{display:block;color:var(--text-color);font-size:.875rem;font-weight:500;width:100%;cursor:pointer}.input-label.required:after{content:\"*\";color:var(--danger-color);margin-left:.125rem}.input-wrapper{position:relative;display:flex;align-items:center;width:100%}input{width:100%;height:2.5rem;padding:0 1rem;font-family:inherit;font-size:1rem;color:var(--text-color);background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);transition:all var(--animation-duration-default) var(--animation-easing-default)}input:hover:not(:disabled):not(:focus){border-color:var(--primary-color)}input::placeholder{color:var(--text-color-tertiary);opacity:1}input:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 .125rem var(--focus-ring-color)}:host.outlined input{background:transparent}:host.filled input{background:var(--surface-ground);border:.125rem solid var(--surface-border)}:host.filled input:hover:not(:disabled):not(:focus){background:var(--surface-hover)}:host.filled input:focus{background:var(--surface-0)}:host.error input{border-color:var(--danger-color)}:host.error input:focus{box-shadow:0 0 0 .125rem var(--focus-ring-error)}:host.success input{border-color:var(--success-color)}:host.success input:focus{box-shadow:0 0 0 .125rem var(--focus-ring-success)}:host.disabled input{background:var(--surface-ground);cursor:not-allowed;opacity:var(--opacity-disabled)}:host.readonly input{background:var(--surface-ground);cursor:default}:host.small input{height:2rem;font-size:.875rem}:host.large input{height:3rem;font-size:1.125rem}:host.has-start-icon input{padding-left:2.75rem}:host.has-end-icon input{padding-right:2.75rem}.input-wrapper i{position:absolute;top:50%;transform:translateY(-50%);font-size:1.25rem;color:var(--text-color-secondary);pointer-events:none;z-index:2}.input-wrapper i:first-of-type{padding-left:1rem}.input-wrapper i:last-of-type{padding-right:1rem}.password-toggle{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);width:2rem;height:2rem;background:transparent;border:none;color:var(--text-color-secondary);cursor:pointer;padding:0;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;transition:all .2s ease;z-index:3}.password-toggle:hover:not(:disabled){background:var(--surface-hover);color:var(--text-color)}.password-toggle i{position:static;transform:none;pointer-events:auto}.input-loader{position:absolute;right:1rem;top:50%;transform:translateY(-50%);z-index:3}.loader{width:1rem;height:1rem;border:.125rem solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}.suggestions-list{position:absolute;top:100%;left:0;right:0;background:var(--surface-0);border:.0625rem solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg);z-index:var(--z-index-dropdown);max-height:12.5rem;overflow-y:auto;margin-top:.25rem}.suggestion-item{padding:.5rem 1rem;cursor:pointer;color:var(--text-color)}.suggestion-item:hover,.suggestion-item.focused{background:var(--surface-hover);color:var(--primary-color)}.input-error,.input-success,.input-hint{font-size:.875rem}.input-error{color:var(--danger-color)}.input-success{color:var(--success-color)}.input-hint{color:var(--text-color-secondary)}@keyframes spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2222
+ ], viewQueries: [{ propertyName: "suggestionsTpl", first: true, predicate: ["suggestionsTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"input-container\">\r\n @if (showLabel()) {\r\n <!-- label[for] delegates focus to its control natively; the click handler is a convenience. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <label\r\n class=\"input-label\"\r\n [for]=\"inputId\"\r\n (click)=\"focusSelect()\"\r\n [class.required]=\"required()\"\r\n >\r\n <ng-content select=\"[input-label]\"></ng-content>\r\n @if (!hasLabelContent()) {\r\n {{ label() }}\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"input-wrapper\">\r\n @if (iconStart()) {\r\n <i class=\"ph ph-{{ iconStart() }}\" aria-hidden=\"true\"></i>\r\n }\r\n\r\n <input\r\n [id]=\"inputId\"\r\n [type]=\"effectiveType()\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-invalid]=\"!!error()\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-describedby]=\"error() ? 'error-message' : success() ? 'success-message' : hint() ? 'hint-message' : null\"\r\n [disabled]=\"disabled()\"\r\n [readonly]=\"readonly()\"\r\n [required]=\"required()\"\r\n [value]=\"value()\"\r\n (input)=\"handleInput($event)\"\r\n (keydown)=\"handleKeydown($event)\"\r\n (focus)=\"handleFocus()\"\r\n (blur)=\"handleBlur()\"\r\n [attr.data-state]=\"state()\"\r\n />\r\n\r\n @if (iconEnd()) {\r\n <i class=\"ph ph-{{ iconEnd() }}\" aria-hidden=\"true\"></i>\r\n }\r\n\r\n @if (type() === 'password') {\r\n <button\r\n type=\"button\"\r\n class=\"password-toggle\"\r\n (click)=\"togglePasswordVisibility()\"\r\n [attr.aria-label]=\"passwordToggleLabel()\"\r\n [disabled]=\"disabled() || readonly()\"\r\n >\r\n <i \r\n [class]=\"'ph ph-' + (passwordVisible() ? 'eye-slash' : 'eye')\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n }\r\n\r\n @if (loading()) {\r\n <div class=\"input-loader\" aria-hidden=\"true\">\r\n <div class=\"loader\"></div>\r\n </div>\r\n }\r\n\r\n <ng-template #suggestionsTpl>\r\n <div class=\"suggestions-list\" role=\"listbox\">\r\n @for (suggestion of filteredSuggestions(); track suggestion; let i = $index) {\r\n <!-- option in a listbox: keyboard is handled by the input (arrow keys + focusedSuggestionIndex). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <div\r\n class=\"suggestion-item\"\r\n [class.focused]=\"i === focusedSuggestionIndex()\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"i === focusedSuggestionIndex()\"\r\n (click)=\"handleSuggestionClick(suggestion)\"\r\n (mouseenter)=\"focusedSuggestionIndex.set(i)\"\r\n >\r\n {{ suggestion }}\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n @if (error()) {\r\n <div id=\"error-message\" class=\"input-error\" role=\"alert\">\r\n <ng-content select=\"[input-error]\">\r\n {{ error() }}\r\n </ng-content>\r\n </div>\r\n } @else if (success()) {\r\n <div id=\"success-message\" class=\"input-success\" role=\"status\">\r\n <ng-content select=\"[input-success]\">\r\n {{ success() }}\r\n </ng-content>\r\n </div>\r\n } @else if (hint()) {\r\n <div id=\"hint-message\" class=\"input-hint\">\r\n <ng-content select=\"[input-hint]\">\r\n {{ hint() }}\r\n </ng-content>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:block;width:100%;max-width:18.75rem;transition:all var(--animation-duration-default) var(--animation-easing-default)}:host.full-width{max-width:none}.input-container{position:relative;width:100%;display:flex;flex-direction:column;gap:var(--form-field-gap)}.input-label{display:block;color:var(--text-color);font-size:.875rem;font-weight:500;width:100%;cursor:pointer}.input-label.required:after{content:\"*\";color:var(--danger-color);margin-left:.125rem}.input-wrapper{position:relative;display:flex;align-items:center;width:100%}input{width:100%;height:2.5rem;padding:0 1rem;font-family:inherit;font-size:1rem;color:var(--text-color);background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);transition:all var(--animation-duration-default) var(--animation-easing-default)}input:hover:not(:disabled):not(:focus){border-color:var(--primary-color)}input::placeholder{color:var(--text-color-tertiary);opacity:1}input:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 .125rem var(--focus-ring-color)}:host.outlined input{background:transparent}:host.filled input{background:var(--surface-ground);border:.125rem solid var(--surface-border)}:host.filled input:hover:not(:disabled):not(:focus){background:var(--surface-hover)}:host.filled input:focus{background:var(--surface-0)}:host.error input{border-color:var(--danger-color)}:host.error input:focus{box-shadow:0 0 0 .125rem var(--focus-ring-error)}:host.success input{border-color:var(--success-color)}:host.success input:focus{box-shadow:0 0 0 .125rem var(--focus-ring-success)}:host.disabled input{background:var(--surface-ground);cursor:not-allowed;opacity:var(--opacity-disabled)}:host.readonly input{background:var(--surface-ground);cursor:default}:host.small input{height:2rem;font-size:.875rem}:host.large input{height:3rem;font-size:1.125rem}:host.has-start-icon input{padding-left:2.75rem}:host.has-end-icon input{padding-right:2.75rem}.input-wrapper i{position:absolute;top:50%;transform:translateY(-50%);font-size:1.25rem;color:var(--text-color-secondary);pointer-events:none;z-index:2}.input-wrapper i:first-of-type{padding-left:1rem}.input-wrapper i:last-of-type{padding-right:1rem}.password-toggle{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);width:2rem;height:2rem;background:transparent;border:none;color:var(--text-color-secondary);cursor:pointer;padding:0;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;transition:all .2s ease;z-index:3}.password-toggle:hover:not(:disabled){background:var(--surface-hover);color:var(--text-color)}.password-toggle i{position:static;transform:none;pointer-events:auto}.input-loader{position:absolute;right:1rem;top:50%;transform:translateY(-50%);z-index:3}.loader{width:1rem;height:1rem;border:.125rem solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}.suggestions-list{pointer-events:auto;background:var(--surface-0);border:.0625rem solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg);overflow-y:auto}.suggestion-item{padding:.5rem 1rem;cursor:pointer;color:var(--text-color)}.suggestion-item:hover,.suggestion-item.focused{background:var(--surface-hover);color:var(--primary-color)}.input-error,.input-success,.input-hint{font-size:.875rem}.input-error{color:var(--danger-color)}.input-success{color:var(--success-color)}.input-hint{color:var(--text-color-secondary)}@keyframes spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2029
2223
  }
2030
2224
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshInputComponent, decorators: [{
2031
2225
  type: Component,
@@ -2049,8 +2243,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
2049
2243
  '[class.has-end-icon]': '!!iconEnd() || type() === "password"',
2050
2244
  '[class.outlined]': 'variant() === "outlined"',
2051
2245
  '[class.filled]': 'variant() === "filled"',
2052
- }, template: "<div class=\"input-container\">\r\n @if (showLabel()) {\r\n <!-- label[for] delegates focus to its control natively; the click handler is a convenience. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <label\r\n class=\"input-label\"\r\n [for]=\"inputId\"\r\n (click)=\"focusSelect()\"\r\n [class.required]=\"required()\"\r\n >\r\n <ng-content select=\"[input-label]\"></ng-content>\r\n @if (!hasLabelContent()) {\r\n {{ label() }}\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"input-wrapper\">\r\n @if (iconStart()) {\r\n <i class=\"ph ph-{{ iconStart() }}\" aria-hidden=\"true\"></i>\r\n }\r\n\r\n <input\r\n [id]=\"inputId\"\r\n [type]=\"effectiveType()\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-invalid]=\"!!error()\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-describedby]=\"error() ? 'error-message' : success() ? 'success-message' : hint() ? 'hint-message' : null\"\r\n [disabled]=\"disabled()\"\r\n [readonly]=\"readonly()\"\r\n [required]=\"required()\"\r\n [value]=\"value()\"\r\n (input)=\"handleInput($event)\"\r\n (keydown)=\"handleKeydown($event)\"\r\n (focus)=\"handleFocus()\"\r\n (blur)=\"handleBlur()\"\r\n [attr.data-state]=\"state()\"\r\n />\r\n\r\n @if (iconEnd()) {\r\n <i class=\"ph ph-{{ iconEnd() }}\" aria-hidden=\"true\"></i>\r\n }\r\n\r\n @if (type() === 'password') {\r\n <button\r\n type=\"button\"\r\n class=\"password-toggle\"\r\n (click)=\"togglePasswordVisibility()\"\r\n [attr.aria-label]=\"passwordToggleLabel()\"\r\n [disabled]=\"disabled() || readonly()\"\r\n >\r\n <i \r\n [class]=\"'ph ph-' + (passwordVisible() ? 'eye-slash' : 'eye')\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n }\r\n\r\n @if (loading()) {\r\n <div class=\"input-loader\" aria-hidden=\"true\">\r\n <div class=\"loader\"></div>\r\n </div>\r\n }\r\n\r\n @if (showSuggestions()) {\r\n <div class=\"suggestions-list\" role=\"listbox\">\r\n @for (suggestion of filteredSuggestions(); track suggestion; let i = $index) {\r\n <!-- option in a listbox: keyboard is handled by the input (arrow keys + focusedSuggestionIndex). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <div\r\n class=\"suggestion-item\"\r\n [class.focused]=\"i === focusedSuggestionIndex()\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"i === focusedSuggestionIndex()\"\r\n (click)=\"handleSuggestionClick(suggestion)\"\r\n (mouseenter)=\"focusedSuggestionIndex.set(i)\"\r\n >\r\n {{ suggestion }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (error()) {\r\n <div id=\"error-message\" class=\"input-error\" role=\"alert\">\r\n <ng-content select=\"[input-error]\">\r\n {{ error() }}\r\n </ng-content>\r\n </div>\r\n } @else if (success()) {\r\n <div id=\"success-message\" class=\"input-success\" role=\"status\">\r\n <ng-content select=\"[input-success]\">\r\n {{ success() }}\r\n </ng-content>\r\n </div>\r\n } @else if (hint()) {\r\n <div id=\"hint-message\" class=\"input-hint\">\r\n <ng-content select=\"[input-hint]\">\r\n {{ hint() }}\r\n </ng-content>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:block;width:100%;max-width:18.75rem;transition:all var(--animation-duration-default) var(--animation-easing-default)}:host.full-width{max-width:none}.input-container{position:relative;width:100%;display:flex;flex-direction:column;gap:var(--form-field-gap)}.input-label{display:block;color:var(--text-color);font-size:.875rem;font-weight:500;width:100%;cursor:pointer}.input-label.required:after{content:\"*\";color:var(--danger-color);margin-left:.125rem}.input-wrapper{position:relative;display:flex;align-items:center;width:100%}input{width:100%;height:2.5rem;padding:0 1rem;font-family:inherit;font-size:1rem;color:var(--text-color);background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);transition:all var(--animation-duration-default) var(--animation-easing-default)}input:hover:not(:disabled):not(:focus){border-color:var(--primary-color)}input::placeholder{color:var(--text-color-tertiary);opacity:1}input:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 .125rem var(--focus-ring-color)}:host.outlined input{background:transparent}:host.filled input{background:var(--surface-ground);border:.125rem solid var(--surface-border)}:host.filled input:hover:not(:disabled):not(:focus){background:var(--surface-hover)}:host.filled input:focus{background:var(--surface-0)}:host.error input{border-color:var(--danger-color)}:host.error input:focus{box-shadow:0 0 0 .125rem var(--focus-ring-error)}:host.success input{border-color:var(--success-color)}:host.success input:focus{box-shadow:0 0 0 .125rem var(--focus-ring-success)}:host.disabled input{background:var(--surface-ground);cursor:not-allowed;opacity:var(--opacity-disabled)}:host.readonly input{background:var(--surface-ground);cursor:default}:host.small input{height:2rem;font-size:.875rem}:host.large input{height:3rem;font-size:1.125rem}:host.has-start-icon input{padding-left:2.75rem}:host.has-end-icon input{padding-right:2.75rem}.input-wrapper i{position:absolute;top:50%;transform:translateY(-50%);font-size:1.25rem;color:var(--text-color-secondary);pointer-events:none;z-index:2}.input-wrapper i:first-of-type{padding-left:1rem}.input-wrapper i:last-of-type{padding-right:1rem}.password-toggle{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);width:2rem;height:2rem;background:transparent;border:none;color:var(--text-color-secondary);cursor:pointer;padding:0;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;transition:all .2s ease;z-index:3}.password-toggle:hover:not(:disabled){background:var(--surface-hover);color:var(--text-color)}.password-toggle i{position:static;transform:none;pointer-events:auto}.input-loader{position:absolute;right:1rem;top:50%;transform:translateY(-50%);z-index:3}.loader{width:1rem;height:1rem;border:.125rem solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}.suggestions-list{position:absolute;top:100%;left:0;right:0;background:var(--surface-0);border:.0625rem solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg);z-index:var(--z-index-dropdown);max-height:12.5rem;overflow-y:auto;margin-top:.25rem}.suggestion-item{padding:.5rem 1rem;cursor:pointer;color:var(--text-color)}.suggestion-item:hover,.suggestion-item.focused{background:var(--surface-hover);color:var(--primary-color)}.input-error,.input-success,.input-hint{font-size:.875rem}.input-error{color:var(--danger-color)}.input-success{color:var(--success-color)}.input-hint{color:var(--text-color-secondary)}@keyframes spin{to{transform:rotate(360deg)}}\n"] }]
2053
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }, { type: i0.Output, args: ["readonlyChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }, { type: i0.Output, args: ["loadingChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], iconStart: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconStart", required: false }] }], iconEnd: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconEnd", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], success: [{ type: i0.Input, args: [{ isSignal: true, alias: "success", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], suggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "suggestions", required: false }] }], autocompleteConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocompleteConfig", required: false }] }], inputFocus: [{ type: i0.Output, args: ["inputFocus"] }], inputBlur: [{ type: i0.Output, args: ["inputBlur"] }], suggestionSelect: [{ type: i0.Output, args: ["suggestionSelect"] }] } });
2246
+ }, template: "<div class=\"input-container\">\r\n @if (showLabel()) {\r\n <!-- label[for] delegates focus to its control natively; the click handler is a convenience. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <label\r\n class=\"input-label\"\r\n [for]=\"inputId\"\r\n (click)=\"focusSelect()\"\r\n [class.required]=\"required()\"\r\n >\r\n <ng-content select=\"[input-label]\"></ng-content>\r\n @if (!hasLabelContent()) {\r\n {{ label() }}\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"input-wrapper\">\r\n @if (iconStart()) {\r\n <i class=\"ph ph-{{ iconStart() }}\" aria-hidden=\"true\"></i>\r\n }\r\n\r\n <input\r\n [id]=\"inputId\"\r\n [type]=\"effectiveType()\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-invalid]=\"!!error()\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-describedby]=\"error() ? 'error-message' : success() ? 'success-message' : hint() ? 'hint-message' : null\"\r\n [disabled]=\"disabled()\"\r\n [readonly]=\"readonly()\"\r\n [required]=\"required()\"\r\n [value]=\"value()\"\r\n (input)=\"handleInput($event)\"\r\n (keydown)=\"handleKeydown($event)\"\r\n (focus)=\"handleFocus()\"\r\n (blur)=\"handleBlur()\"\r\n [attr.data-state]=\"state()\"\r\n />\r\n\r\n @if (iconEnd()) {\r\n <i class=\"ph ph-{{ iconEnd() }}\" aria-hidden=\"true\"></i>\r\n }\r\n\r\n @if (type() === 'password') {\r\n <button\r\n type=\"button\"\r\n class=\"password-toggle\"\r\n (click)=\"togglePasswordVisibility()\"\r\n [attr.aria-label]=\"passwordToggleLabel()\"\r\n [disabled]=\"disabled() || readonly()\"\r\n >\r\n <i \r\n [class]=\"'ph ph-' + (passwordVisible() ? 'eye-slash' : 'eye')\"\r\n aria-hidden=\"true\"\r\n ></i>\r\n </button>\r\n }\r\n\r\n @if (loading()) {\r\n <div class=\"input-loader\" aria-hidden=\"true\">\r\n <div class=\"loader\"></div>\r\n </div>\r\n }\r\n\r\n <ng-template #suggestionsTpl>\r\n <div class=\"suggestions-list\" role=\"listbox\">\r\n @for (suggestion of filteredSuggestions(); track suggestion; let i = $index) {\r\n <!-- option in a listbox: keyboard is handled by the input (arrow keys + focusedSuggestionIndex). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <div\r\n class=\"suggestion-item\"\r\n [class.focused]=\"i === focusedSuggestionIndex()\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"i === focusedSuggestionIndex()\"\r\n (click)=\"handleSuggestionClick(suggestion)\"\r\n (mouseenter)=\"focusedSuggestionIndex.set(i)\"\r\n >\r\n {{ suggestion }}\r\n </div>\r\n }\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n @if (error()) {\r\n <div id=\"error-message\" class=\"input-error\" role=\"alert\">\r\n <ng-content select=\"[input-error]\">\r\n {{ error() }}\r\n </ng-content>\r\n </div>\r\n } @else if (success()) {\r\n <div id=\"success-message\" class=\"input-success\" role=\"status\">\r\n <ng-content select=\"[input-success]\">\r\n {{ success() }}\r\n </ng-content>\r\n </div>\r\n } @else if (hint()) {\r\n <div id=\"hint-message\" class=\"input-hint\">\r\n <ng-content select=\"[input-hint]\">\r\n {{ hint() }}\r\n </ng-content>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:block;width:100%;max-width:18.75rem;transition:all var(--animation-duration-default) var(--animation-easing-default)}:host.full-width{max-width:none}.input-container{position:relative;width:100%;display:flex;flex-direction:column;gap:var(--form-field-gap)}.input-label{display:block;color:var(--text-color);font-size:.875rem;font-weight:500;width:100%;cursor:pointer}.input-label.required:after{content:\"*\";color:var(--danger-color);margin-left:.125rem}.input-wrapper{position:relative;display:flex;align-items:center;width:100%}input{width:100%;height:2.5rem;padding:0 1rem;font-family:inherit;font-size:1rem;color:var(--text-color);background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);transition:all var(--animation-duration-default) var(--animation-easing-default)}input:hover:not(:disabled):not(:focus){border-color:var(--primary-color)}input::placeholder{color:var(--text-color-tertiary);opacity:1}input:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 .125rem var(--focus-ring-color)}:host.outlined input{background:transparent}:host.filled input{background:var(--surface-ground);border:.125rem solid var(--surface-border)}:host.filled input:hover:not(:disabled):not(:focus){background:var(--surface-hover)}:host.filled input:focus{background:var(--surface-0)}:host.error input{border-color:var(--danger-color)}:host.error input:focus{box-shadow:0 0 0 .125rem var(--focus-ring-error)}:host.success input{border-color:var(--success-color)}:host.success input:focus{box-shadow:0 0 0 .125rem var(--focus-ring-success)}:host.disabled input{background:var(--surface-ground);cursor:not-allowed;opacity:var(--opacity-disabled)}:host.readonly input{background:var(--surface-ground);cursor:default}:host.small input{height:2rem;font-size:.875rem}:host.large input{height:3rem;font-size:1.125rem}:host.has-start-icon input{padding-left:2.75rem}:host.has-end-icon input{padding-right:2.75rem}.input-wrapper i{position:absolute;top:50%;transform:translateY(-50%);font-size:1.25rem;color:var(--text-color-secondary);pointer-events:none;z-index:2}.input-wrapper i:first-of-type{padding-left:1rem}.input-wrapper i:last-of-type{padding-right:1rem}.password-toggle{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);width:2rem;height:2rem;background:transparent;border:none;color:var(--text-color-secondary);cursor:pointer;padding:0;border-radius:var(--border-radius);display:flex;align-items:center;justify-content:center;transition:all .2s ease;z-index:3}.password-toggle:hover:not(:disabled){background:var(--surface-hover);color:var(--text-color)}.password-toggle i{position:static;transform:none;pointer-events:auto}.input-loader{position:absolute;right:1rem;top:50%;transform:translateY(-50%);z-index:3}.loader{width:1rem;height:1rem;border:.125rem solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}.suggestions-list{pointer-events:auto;background:var(--surface-0);border:.0625rem solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg);overflow-y:auto}.suggestion-item{padding:.5rem 1rem;cursor:pointer;color:var(--text-color)}.suggestion-item:hover,.suggestion-item.focused{background:var(--surface-hover);color:var(--primary-color)}.input-error,.input-success,.input-hint{font-size:.875rem}.input-error{color:var(--danger-color)}.input-success{color:var(--success-color)}.input-hint{color:var(--text-color-secondary)}@keyframes spin{to{transform:rotate(360deg)}}\n"] }]
2247
+ }], ctorParameters: () => [], propDecorators: { suggestionsTpl: [{ type: i0.ViewChild, args: ['suggestionsTpl', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }, { type: i0.Output, args: ["readonlyChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }, { type: i0.Output, args: ["loadingChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], iconStart: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconStart", required: false }] }], iconEnd: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconEnd", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], success: [{ type: i0.Input, args: [{ isSignal: true, alias: "success", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], suggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "suggestions", required: false }] }], autocompleteConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocompleteConfig", required: false }] }], inputFocus: [{ type: i0.Output, args: ["inputFocus"] }], inputBlur: [{ type: i0.Output, args: ["inputBlur"] }], suggestionSelect: [{ type: i0.Output, args: ["suggestionSelect"] }] } });
2054
2248
 
2055
2249
  const TEXTAREA_LABELS = {
2056
2250
  characterCountSuffix: 'caractères',
@@ -3734,7 +3928,14 @@ class PshSelectComponent {
3734
3928
  this.elementRef = inject(ElementRef);
3735
3929
  this.destroyRef = inject(DestroyRef);
3736
3930
  this.overlayPosition = inject(PshOverlayPositionService);
3737
- this.injector = inject(Injector);
3931
+ this.portal = inject(PshPortalService);
3932
+ this.viewContainer = inject(ViewContainerRef);
3933
+ // The options panel, teleported to a body-level overlay layer on open so it
3934
+ // escapes any ancestor overflow / stacking context (e.g. a modal body).
3935
+ this.panelTpl = viewChild('panelTpl', /* @ts-ignore */
3936
+ ...(ngDevMode ? [{ debugName: "panelTpl" }] : /* istanbul ignore next */ []));
3937
+ this.portalRef = null;
3938
+ this.repositionHandler = () => this.reposition();
3738
3939
  // Side the panel actually opens on, after viewport collision/flip.
3739
3940
  this.resolvedSide = signal('bottom', /* @ts-ignore */
3740
3941
  ...(ngDevMode ? [{ debugName: "resolvedSide" }] : /* istanbul ignore next */ []));
@@ -3888,34 +4089,58 @@ class PshSelectComponent {
3888
4089
  ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
3889
4090
  this.onChange = () => { };
3890
4091
  this.onTouched = () => { };
3891
- // Close on outside click via the shared click-outside primitive.
4092
+ // Close on outside click via the shared click-outside primitive. The options
4093
+ // panel is teleported to the body (outside the host), so a click inside it
4094
+ // must NOT count as "outside" — otherwise selecting an option would close the
4095
+ // panel before select() runs.
3892
4096
  const clickOutside = inject(PshClickOutsideDirective);
3893
- const sub = clickOutside.pshClickOutside.subscribe(() => {
3894
- if (this.isOpen())
3895
- this.close();
4097
+ const sub = clickOutside.pshClickOutside.subscribe(event => {
4098
+ if (!this.isOpen())
4099
+ return;
4100
+ const target = event.target;
4101
+ if (target && this.portalRef?.panel.contains(target))
4102
+ return;
4103
+ this.close();
3896
4104
  });
3897
- this.destroyRef.onDestroy(() => sub.unsubscribe());
3898
- // Flip the options panel against the viewport on open; reset while closed.
3899
- effect(() => {
3900
- if (this.isOpen()) {
3901
- afterNextRender(() => this.reposition(), { injector: this.injector });
3902
- }
3903
- else {
3904
- this.resolvedSide.set('bottom');
3905
- }
4105
+ this.destroyRef.onDestroy(() => {
4106
+ sub.unsubscribe();
4107
+ this.closePanel();
3906
4108
  });
3907
4109
  }
4110
+ /** Teleports the options panel to the body-level overlay layer and positions it. */
4111
+ openPanel() {
4112
+ if (this.portalRef)
4113
+ return;
4114
+ const tpl = this.panelTpl();
4115
+ if (!tpl)
4116
+ return;
4117
+ this.portalRef = this.portal.attach(tpl, this.viewContainer);
4118
+ this.reposition();
4119
+ const view = this.elementRef.nativeElement.ownerDocument.defaultView;
4120
+ // Capture phase so inner (e.g. modal body) scrolls keep the panel aligned.
4121
+ view?.addEventListener('scroll', this.repositionHandler, true);
4122
+ view?.addEventListener('resize', this.repositionHandler);
4123
+ }
4124
+ closePanel() {
4125
+ const view = this.elementRef.nativeElement.ownerDocument.defaultView;
4126
+ view?.removeEventListener('scroll', this.repositionHandler, true);
4127
+ view?.removeEventListener('resize', this.repositionHandler);
4128
+ this.portalRef?.detach();
4129
+ this.portalRef = null;
4130
+ this.resolvedSide.set('bottom');
4131
+ }
3908
4132
  reposition() {
3909
- if (!this.isOpen())
4133
+ if (!this.portalRef)
3910
4134
  return;
3911
4135
  const host = this.elementRef.nativeElement;
3912
4136
  const trigger = host.querySelector('.select-trigger');
3913
- const panel = host.querySelector('.select-dropdown');
3914
4137
  if (!trigger)
3915
4138
  return;
3916
- this.resolvedSide.set(this.overlayPosition.flipSide(trigger, 'bottom', {
3917
- overlayHeight: panel?.offsetHeight ?? 0,
3918
- }));
4139
+ const side = this.overlayPosition.flipSide(trigger, 'bottom', {
4140
+ overlayHeight: this.portalRef.panel.offsetHeight,
4141
+ });
4142
+ this.resolvedSide.set(side);
4143
+ this.portalRef.position(trigger, side, 8);
3919
4144
  }
3920
4145
  writeValue(value) {
3921
4146
  this.value.set(value);
@@ -3932,6 +4157,7 @@ class PshSelectComponent {
3932
4157
  else {
3933
4158
  this.isOpenSignal.set(true);
3934
4159
  this.opened.emit();
4160
+ this.openPanel();
3935
4161
  }
3936
4162
  }
3937
4163
  close() {
@@ -3940,6 +4166,7 @@ class PshSelectComponent {
3940
4166
  this.isOpenSignal.set(false);
3941
4167
  this.searchTermSignal.set('');
3942
4168
  this.focusedIndex.set(-1);
4169
+ this.closePanel();
3943
4170
  this.closed.emit();
3944
4171
  }
3945
4172
  select(option, groupDisabled = false) {
@@ -4100,7 +4327,7 @@ class PshSelectComponent {
4100
4327
  useExisting: PshSelectComponent,
4101
4328
  multi: true
4102
4329
  }
4103
- ], hostDirectives: [{ directive: PshClickOutsideDirective }], ngImport: i0, template: "<div class=\"select-container\">\r\n @if (label()) {\r\n <!-- label[for] delegates focus to its control natively; the click handler is a convenience. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <label class=\"select-label\" [attr.for]=\"selectId\" (click)=\"focusSelect()\" [class.required]=\"required()\">\r\n {{ label() }}\r\n @if (required()) { <span class=\"required-mark\">*</span> }\r\n </label>\r\n }\r\n\r\n <div class=\"select-trigger-wrapper\">\r\n <div class=\"select-trigger\"\r\n role=\"combobox\"\r\n [id]=\"selectId\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n [attr.aria-controls]=\"selectId + '-listbox'\"\r\n [attr.aria-activedescendant]=\"activeDescendant()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-invalid]=\"!!error()\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-disabled]=\"disabled()\"\r\n [attr.aria-busy]=\"loading()\"\r\n [attr.aria-describedby]=\"describedBy()\"\r\n [attr.tabindex]=\"disabled() || loading() ? -1 : 0\"\r\n (keydown)=\"handleKeydown($event)\"\r\n (click)=\"toggle()\">\r\n <div class=\"select-value\">\r\n <span [class.placeholder]=\"!hasValue()\">\r\n {{ selectedLabel() }}\r\n </span>\r\n </div>\r\n <div class=\"select-actions\">\r\n @if (clearable() && hasValue() && !disabled()) {\r\n <button class=\"select-clear\" (click)=\"clear($event)\">\r\n <i class=\"ph ph-x\"></i>\r\n </button>\r\n }\r\n @if (loading()) {\r\n <div class=\"select-loader-trigger\"></div>\r\n } @else {\r\n <i class=\"ph ph-caret-down select-arrow\"></i>\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"select-dropdown\" [class.open-top]=\"resolvedSide() === 'top'\">\r\n @if (searchable()) {\r\n <div class=\"select-search\">\r\n <i class=\"ph ph-magnifying-glass select-search-icon\"></i>\r\n <input type=\"text\"\r\n [placeholder]=\"searchConfig().placeholder\"\r\n [value]=\"searchTerm()\"\r\n (input)=\"onSearch($event)\"\r\n (click)=\"$event.stopPropagation()\">\r\n </div>\r\n }\r\n <div class=\"select-options\"\r\n role=\"listbox\"\r\n [id]=\"selectId + '-listbox'\"\r\n [attr.aria-multiselectable]=\"multiple() || null\"\r\n (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions(); track getOptionKey(item)) {\r\n @if (isOptionGroup(item)) {\r\n <div class=\"select-group\" role=\"group\" [attr.aria-label]=\"item.label\">\r\n <div class=\"select-group-label\">{{ item.label }}</div>\r\n @for (opt of item.options; track opt.label) {\r\n <!-- option in a listbox: keyboard is handled at the widget level (arrows + aria-activedescendant). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\r\n <div class=\"select-option\"\r\n role=\"option\"\r\n [id]=\"selectId + '-' + opt.value\"\r\n [attr.aria-selected]=\"isSelected(opt)\"\r\n [attr.aria-disabled]=\"(item.disabled || opt.disabled) ? true : null\"\r\n [attr.tabindex]=\"(item.disabled || opt.disabled) ? -1 : null\"\r\n [class.selected]=\"isSelected(opt)\"\r\n [class.disabled]=\"item.disabled || opt.disabled\"\r\n [class.focused]=\"getFlatIndex(opt) === focusedIndex()\"\r\n (click)=\"select(opt, !!item.disabled)\">\r\n @if (opt.icon) { <i [class]=\"'ph ph-' + opt.icon\" aria-hidden=\"true\"></i> }\r\n <div class=\"select-option-content\">\r\n <span class=\"select-option-label\">{{ opt.label }}</span>\r\n @if (opt.description) { <span class=\"select-option-description\">{{ opt.description }}</span> }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <!-- option in a listbox: keyboard is handled at the widget level (arrows + aria-activedescendant). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\r\n <div class=\"select-option\"\r\n role=\"option\"\r\n [id]=\"selectId + '-' + item.value\"\r\n [attr.aria-selected]=\"isSelected(item)\"\r\n [attr.aria-disabled]=\"item.disabled ? true : null\"\r\n [attr.tabindex]=\"item.disabled ? -1 : null\"\r\n [class.selected]=\"isSelected(item)\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.focused]=\"getFlatIndex(item) === focusedIndex()\"\r\n (click)=\"select(item)\">\r\n @if (item.icon) { <i [class]=\"'ph ph-' + item.icon\" aria-hidden=\"true\"></i> }\r\n <div class=\"select-option-content\">\r\n <span class=\"select-option-label\">{{ item.label }}</span>\r\n @if (item.description) { <span class=\"select-option-description\">{{ item.description }}</span> }\r\n </div>\r\n </div>\r\n }\r\n } @empty {\r\n <div class=\"select-no-results\">Aucun r\u00E9sultat</div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (error()) {\r\n <div id=\"error-message\" class=\"select-error-message\" role=\"alert\">{{ error() }}</div>\r\n } @else if (success()) {\r\n <div id=\"success-message\" class=\"select-success-message\" role=\"status\">{{ success() }}</div>\r\n } @else if (hint()) {\r\n <div id=\"hint-message\" class=\"select-hint\">{{ hint() }}</div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;max-width:18.75rem}:host.full-width{max-width:none}.select-container{width:100%;display:flex;flex-direction:column;gap:var(--form-field-gap)}.select-trigger-wrapper{position:relative}.select-label{display:block;color:var(--text-color);font-size:.875rem;font-weight:500;cursor:pointer}.select-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;height:2.5rem;padding:0 1rem;background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);cursor:pointer;box-sizing:border-box}.select-trigger:focus{border-color:var(--primary-color);outline:none;box-shadow:0 0 0 .125rem var(--focus-ring-color)}:host.error .select-trigger{border-color:var(--danger-color)}:host.error .select-trigger:focus{box-shadow:0 0 0 .125rem var(--focus-ring-error)}:host.success .select-trigger{border-color:var(--success-color)}:host.success .select-trigger:focus{box-shadow:0 0 0 .125rem var(--focus-ring-success)}:host.disabled .select-trigger{background:var(--surface-ground);cursor:not-allowed;opacity:var(--opacity-disabled)}:host.filled .select-trigger{background:var(--surface-ground);border:.125rem solid var(--surface-border)}:host.filled .select-trigger:hover:not([disabled]):not(:focus){background:var(--surface-hover)}.select-value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.select-clear{background:none;border:none;cursor:pointer;padding:0;color:var(--text-color-secondary);display:flex}.select-clear:hover{color:var(--danger-color)}.select-loader-trigger{width:1rem;height:1rem;border:2px solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}.select-arrow{transition:transform .2s;font-size:1rem;color:var(--text-color-secondary)}:host[aria-expanded=true] .select-arrow{transform:rotate(180deg)}.select-dropdown.open-top{top:auto;bottom:calc(100% + .25rem)}.select-dropdown{position:absolute;top:calc(100% + .25rem);left:0;right:0;z-index:var(--z-index-dropdown);background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg)}.select-search{position:relative;padding:.5rem;border-bottom:.0625rem solid var(--surface-border)}.select-search-icon{position:absolute;left:1.25rem;top:50%;transform:translateY(-50%);color:var(--text-color-secondary);font-size:.875rem;pointer-events:none}.select-search input{width:100%;height:2rem;padding:0 .75rem 0 2rem;background:var(--surface-ground);border:.0625rem solid var(--surface-border);border-radius:var(--border-radius);color:var(--text-color);font-size:.875rem;outline:none;box-sizing:border-box;transition:border-color .15s}.select-search input::placeholder{color:var(--text-color-secondary)}.select-search input:focus{border-color:var(--primary-color)}.select-options{max-height:15rem;overflow-y:auto}.select-group-label{padding:.5rem 1rem;font-size:.75rem;font-weight:600;color:var(--text-color-secondary);background:var(--surface-ground)}.select-option{padding:.625rem 1rem;cursor:pointer;display:flex;align-items:flex-start;gap:.5rem}.select-option:hover{background:var(--surface-hover)}.select-option.selected{background:rgba(var(--primary-color-rgb),.1);color:var(--primary-color);font-weight:500}.select-option.focused{background:var(--surface-hover)}.select-option.disabled{opacity:var(--opacity-disabled);cursor:not-allowed}.select-option i{flex-shrink:0;line-height:1.4}.select-option-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.select-option-label{min-width:0}.select-option-description{font-size:.75rem;color:var(--text-color-secondary)}.select-error-message,.select-success-message,.select-hint{font-size:.875rem}.select-error-message{color:var(--danger-color)}.select-success-message{color:var(--success-color)}.select-hint{color:var(--text-color-secondary)}:host.small .select-trigger{text-overflow:ellipsis;height:2rem;font-size:.875rem}:host.large .select-trigger{text-overflow:ellipsis;height:3rem;font-size:1.125rem}@keyframes spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4330
+ ], viewQueries: [{ propertyName: "panelTpl", first: true, predicate: ["panelTpl"], descendants: true, isSignal: true }], hostDirectives: [{ directive: PshClickOutsideDirective }], ngImport: i0, template: "<div class=\"select-container\">\r\n @if (label()) {\r\n <!-- label[for] delegates focus to its control natively; the click handler is a convenience. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <label class=\"select-label\" [attr.for]=\"selectId\" (click)=\"focusSelect()\" [class.required]=\"required()\">\r\n {{ label() }}\r\n @if (required()) { <span class=\"required-mark\">*</span> }\r\n </label>\r\n }\r\n\r\n <div class=\"select-trigger-wrapper\">\r\n <div class=\"select-trigger\"\r\n role=\"combobox\"\r\n [id]=\"selectId\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n [attr.aria-controls]=\"selectId + '-listbox'\"\r\n [attr.aria-activedescendant]=\"activeDescendant()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-invalid]=\"!!error()\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-disabled]=\"disabled()\"\r\n [attr.aria-busy]=\"loading()\"\r\n [attr.aria-describedby]=\"describedBy()\"\r\n [attr.tabindex]=\"disabled() || loading() ? -1 : 0\"\r\n (keydown)=\"handleKeydown($event)\"\r\n (click)=\"toggle()\">\r\n <div class=\"select-value\">\r\n <span [class.placeholder]=\"!hasValue()\">\r\n {{ selectedLabel() }}\r\n </span>\r\n </div>\r\n <div class=\"select-actions\">\r\n @if (clearable() && hasValue() && !disabled()) {\r\n <button class=\"select-clear\" (click)=\"clear($event)\">\r\n <i class=\"ph ph-x\"></i>\r\n </button>\r\n }\r\n @if (loading()) {\r\n <div class=\"select-loader-trigger\"></div>\r\n } @else {\r\n <i class=\"ph ph-caret-down select-arrow\"></i>\r\n }\r\n </div>\r\n </div>\r\n\r\n <ng-template #panelTpl>\r\n <div class=\"select-dropdown\" [class.open-top]=\"resolvedSide() === 'top'\">\r\n @if (searchable()) {\r\n <div class=\"select-search\">\r\n <i class=\"ph ph-magnifying-glass select-search-icon\"></i>\r\n <input type=\"text\"\r\n [placeholder]=\"searchConfig().placeholder\"\r\n [value]=\"searchTerm()\"\r\n (input)=\"onSearch($event)\"\r\n (click)=\"$event.stopPropagation()\">\r\n </div>\r\n }\r\n <div class=\"select-options\"\r\n role=\"listbox\"\r\n [id]=\"selectId + '-listbox'\"\r\n [attr.aria-multiselectable]=\"multiple() || null\"\r\n (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions(); track getOptionKey(item)) {\r\n @if (isOptionGroup(item)) {\r\n <div class=\"select-group\" role=\"group\" [attr.aria-label]=\"item.label\">\r\n <div class=\"select-group-label\">{{ item.label }}</div>\r\n @for (opt of item.options; track opt.label) {\r\n <!-- option in a listbox: keyboard is handled at the widget level (arrows + aria-activedescendant). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\r\n <div class=\"select-option\"\r\n role=\"option\"\r\n [id]=\"selectId + '-' + opt.value\"\r\n [attr.aria-selected]=\"isSelected(opt)\"\r\n [attr.aria-disabled]=\"(item.disabled || opt.disabled) ? true : null\"\r\n [attr.tabindex]=\"(item.disabled || opt.disabled) ? -1 : null\"\r\n [class.selected]=\"isSelected(opt)\"\r\n [class.disabled]=\"item.disabled || opt.disabled\"\r\n [class.focused]=\"getFlatIndex(opt) === focusedIndex()\"\r\n (click)=\"select(opt, !!item.disabled)\">\r\n @if (opt.icon) { <i [class]=\"'ph ph-' + opt.icon\" aria-hidden=\"true\"></i> }\r\n <div class=\"select-option-content\">\r\n <span class=\"select-option-label\">{{ opt.label }}</span>\r\n @if (opt.description) { <span class=\"select-option-description\">{{ opt.description }}</span> }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <!-- option in a listbox: keyboard is handled at the widget level (arrows + aria-activedescendant). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\r\n <div class=\"select-option\"\r\n role=\"option\"\r\n [id]=\"selectId + '-' + item.value\"\r\n [attr.aria-selected]=\"isSelected(item)\"\r\n [attr.aria-disabled]=\"item.disabled ? true : null\"\r\n [attr.tabindex]=\"item.disabled ? -1 : null\"\r\n [class.selected]=\"isSelected(item)\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.focused]=\"getFlatIndex(item) === focusedIndex()\"\r\n (click)=\"select(item)\">\r\n @if (item.icon) { <i [class]=\"'ph ph-' + item.icon\" aria-hidden=\"true\"></i> }\r\n <div class=\"select-option-content\">\r\n <span class=\"select-option-label\">{{ item.label }}</span>\r\n @if (item.description) { <span class=\"select-option-description\">{{ item.description }}</span> }\r\n </div>\r\n </div>\r\n }\r\n } @empty {\r\n <div class=\"select-no-results\">Aucun r\u00E9sultat</div>\r\n }\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n @if (error()) {\r\n <div id=\"error-message\" class=\"select-error-message\" role=\"alert\">{{ error() }}</div>\r\n } @else if (success()) {\r\n <div id=\"success-message\" class=\"select-success-message\" role=\"status\">{{ success() }}</div>\r\n } @else if (hint()) {\r\n <div id=\"hint-message\" class=\"select-hint\">{{ hint() }}</div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;max-width:18.75rem}:host.full-width{max-width:none}.select-container{width:100%;display:flex;flex-direction:column;gap:var(--form-field-gap)}.select-trigger-wrapper{position:relative}.select-label{display:block;color:var(--text-color);font-size:.875rem;font-weight:500;cursor:pointer}.select-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;height:2.5rem;padding:0 1rem;background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);cursor:pointer;box-sizing:border-box}.select-trigger:focus{border-color:var(--primary-color);outline:none;box-shadow:0 0 0 .125rem var(--focus-ring-color)}:host.error .select-trigger{border-color:var(--danger-color)}:host.error .select-trigger:focus{box-shadow:0 0 0 .125rem var(--focus-ring-error)}:host.success .select-trigger{border-color:var(--success-color)}:host.success .select-trigger:focus{box-shadow:0 0 0 .125rem var(--focus-ring-success)}:host.disabled .select-trigger{background:var(--surface-ground);cursor:not-allowed;opacity:var(--opacity-disabled)}:host.filled .select-trigger{background:var(--surface-ground);border:.125rem solid var(--surface-border)}:host.filled .select-trigger:hover:not([disabled]):not(:focus){background:var(--surface-hover)}.select-value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.select-clear{background:none;border:none;cursor:pointer;padding:0;color:var(--text-color-secondary);display:flex}.select-clear:hover{color:var(--danger-color)}.select-loader-trigger{width:1rem;height:1rem;border:2px solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}.select-arrow{transition:transform .2s;font-size:1rem;color:var(--text-color-secondary)}:host[aria-expanded=true] .select-arrow{transform:rotate(180deg)}.select-dropdown{display:flex;flex-direction:column;overflow:hidden;pointer-events:auto;background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg)}.select-search{position:relative;flex-shrink:0;padding:.5rem;border-bottom:.0625rem solid var(--surface-border)}.select-search-icon{position:absolute;left:1.25rem;top:50%;transform:translateY(-50%);color:var(--text-color-secondary);font-size:.875rem;pointer-events:none}.select-search input{width:100%;height:2rem;padding:0 .75rem 0 2rem;background:var(--surface-ground);border:.0625rem solid var(--surface-border);border-radius:var(--border-radius);color:var(--text-color);font-size:.875rem;outline:none;box-sizing:border-box;transition:border-color .15s}.select-search input::placeholder{color:var(--text-color-secondary)}.select-search input:focus{border-color:var(--primary-color)}.select-options{flex:0 1 auto;max-height:15rem;overflow-y:auto}.select-group-label{padding:.5rem 1rem;font-size:.75rem;font-weight:600;color:var(--text-color-secondary);background:var(--surface-ground)}.select-option{padding:.625rem 1rem;cursor:pointer;display:flex;align-items:flex-start;gap:.5rem}.select-option:hover{background:var(--surface-hover)}.select-option.selected{background:rgba(var(--primary-color-rgb),.1);color:var(--primary-color);font-weight:500}.select-option.focused{background:var(--surface-hover)}.select-option.disabled{opacity:var(--opacity-disabled);cursor:not-allowed}.select-option i{flex-shrink:0;line-height:1.4}.select-option-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.select-option-label{min-width:0}.select-option-description{font-size:.75rem;color:var(--text-color-secondary)}.select-error-message,.select-success-message,.select-hint{font-size:.875rem}.select-error-message{color:var(--danger-color)}.select-success-message{color:var(--success-color)}.select-hint{color:var(--text-color-secondary)}:host.small .select-trigger{text-overflow:ellipsis;height:2rem;font-size:.875rem}:host.large .select-trigger{text-overflow:ellipsis;height:3rem;font-size:1.125rem}@keyframes spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4104
4331
  }
4105
4332
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshSelectComponent, decorators: [{
4106
4333
  type: Component,
@@ -4122,8 +4349,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
4122
4349
  '[class.loading]': 'loading()',
4123
4350
  '[attr.aria-expanded]': 'isOpen().toString()',
4124
4351
  '[attr.data-state]': 'state()'
4125
- }, template: "<div class=\"select-container\">\r\n @if (label()) {\r\n <!-- label[for] delegates focus to its control natively; the click handler is a convenience. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <label class=\"select-label\" [attr.for]=\"selectId\" (click)=\"focusSelect()\" [class.required]=\"required()\">\r\n {{ label() }}\r\n @if (required()) { <span class=\"required-mark\">*</span> }\r\n </label>\r\n }\r\n\r\n <div class=\"select-trigger-wrapper\">\r\n <div class=\"select-trigger\"\r\n role=\"combobox\"\r\n [id]=\"selectId\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n [attr.aria-controls]=\"selectId + '-listbox'\"\r\n [attr.aria-activedescendant]=\"activeDescendant()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-invalid]=\"!!error()\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-disabled]=\"disabled()\"\r\n [attr.aria-busy]=\"loading()\"\r\n [attr.aria-describedby]=\"describedBy()\"\r\n [attr.tabindex]=\"disabled() || loading() ? -1 : 0\"\r\n (keydown)=\"handleKeydown($event)\"\r\n (click)=\"toggle()\">\r\n <div class=\"select-value\">\r\n <span [class.placeholder]=\"!hasValue()\">\r\n {{ selectedLabel() }}\r\n </span>\r\n </div>\r\n <div class=\"select-actions\">\r\n @if (clearable() && hasValue() && !disabled()) {\r\n <button class=\"select-clear\" (click)=\"clear($event)\">\r\n <i class=\"ph ph-x\"></i>\r\n </button>\r\n }\r\n @if (loading()) {\r\n <div class=\"select-loader-trigger\"></div>\r\n } @else {\r\n <i class=\"ph ph-caret-down select-arrow\"></i>\r\n }\r\n </div>\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"select-dropdown\" [class.open-top]=\"resolvedSide() === 'top'\">\r\n @if (searchable()) {\r\n <div class=\"select-search\">\r\n <i class=\"ph ph-magnifying-glass select-search-icon\"></i>\r\n <input type=\"text\"\r\n [placeholder]=\"searchConfig().placeholder\"\r\n [value]=\"searchTerm()\"\r\n (input)=\"onSearch($event)\"\r\n (click)=\"$event.stopPropagation()\">\r\n </div>\r\n }\r\n <div class=\"select-options\"\r\n role=\"listbox\"\r\n [id]=\"selectId + '-listbox'\"\r\n [attr.aria-multiselectable]=\"multiple() || null\"\r\n (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions(); track getOptionKey(item)) {\r\n @if (isOptionGroup(item)) {\r\n <div class=\"select-group\" role=\"group\" [attr.aria-label]=\"item.label\">\r\n <div class=\"select-group-label\">{{ item.label }}</div>\r\n @for (opt of item.options; track opt.label) {\r\n <!-- option in a listbox: keyboard is handled at the widget level (arrows + aria-activedescendant). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\r\n <div class=\"select-option\"\r\n role=\"option\"\r\n [id]=\"selectId + '-' + opt.value\"\r\n [attr.aria-selected]=\"isSelected(opt)\"\r\n [attr.aria-disabled]=\"(item.disabled || opt.disabled) ? true : null\"\r\n [attr.tabindex]=\"(item.disabled || opt.disabled) ? -1 : null\"\r\n [class.selected]=\"isSelected(opt)\"\r\n [class.disabled]=\"item.disabled || opt.disabled\"\r\n [class.focused]=\"getFlatIndex(opt) === focusedIndex()\"\r\n (click)=\"select(opt, !!item.disabled)\">\r\n @if (opt.icon) { <i [class]=\"'ph ph-' + opt.icon\" aria-hidden=\"true\"></i> }\r\n <div class=\"select-option-content\">\r\n <span class=\"select-option-label\">{{ opt.label }}</span>\r\n @if (opt.description) { <span class=\"select-option-description\">{{ opt.description }}</span> }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <!-- option in a listbox: keyboard is handled at the widget level (arrows + aria-activedescendant). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\r\n <div class=\"select-option\"\r\n role=\"option\"\r\n [id]=\"selectId + '-' + item.value\"\r\n [attr.aria-selected]=\"isSelected(item)\"\r\n [attr.aria-disabled]=\"item.disabled ? true : null\"\r\n [attr.tabindex]=\"item.disabled ? -1 : null\"\r\n [class.selected]=\"isSelected(item)\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.focused]=\"getFlatIndex(item) === focusedIndex()\"\r\n (click)=\"select(item)\">\r\n @if (item.icon) { <i [class]=\"'ph ph-' + item.icon\" aria-hidden=\"true\"></i> }\r\n <div class=\"select-option-content\">\r\n <span class=\"select-option-label\">{{ item.label }}</span>\r\n @if (item.description) { <span class=\"select-option-description\">{{ item.description }}</span> }\r\n </div>\r\n </div>\r\n }\r\n } @empty {\r\n <div class=\"select-no-results\">Aucun r\u00E9sultat</div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (error()) {\r\n <div id=\"error-message\" class=\"select-error-message\" role=\"alert\">{{ error() }}</div>\r\n } @else if (success()) {\r\n <div id=\"success-message\" class=\"select-success-message\" role=\"status\">{{ success() }}</div>\r\n } @else if (hint()) {\r\n <div id=\"hint-message\" class=\"select-hint\">{{ hint() }}</div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;max-width:18.75rem}:host.full-width{max-width:none}.select-container{width:100%;display:flex;flex-direction:column;gap:var(--form-field-gap)}.select-trigger-wrapper{position:relative}.select-label{display:block;color:var(--text-color);font-size:.875rem;font-weight:500;cursor:pointer}.select-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;height:2.5rem;padding:0 1rem;background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);cursor:pointer;box-sizing:border-box}.select-trigger:focus{border-color:var(--primary-color);outline:none;box-shadow:0 0 0 .125rem var(--focus-ring-color)}:host.error .select-trigger{border-color:var(--danger-color)}:host.error .select-trigger:focus{box-shadow:0 0 0 .125rem var(--focus-ring-error)}:host.success .select-trigger{border-color:var(--success-color)}:host.success .select-trigger:focus{box-shadow:0 0 0 .125rem var(--focus-ring-success)}:host.disabled .select-trigger{background:var(--surface-ground);cursor:not-allowed;opacity:var(--opacity-disabled)}:host.filled .select-trigger{background:var(--surface-ground);border:.125rem solid var(--surface-border)}:host.filled .select-trigger:hover:not([disabled]):not(:focus){background:var(--surface-hover)}.select-value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.select-clear{background:none;border:none;cursor:pointer;padding:0;color:var(--text-color-secondary);display:flex}.select-clear:hover{color:var(--danger-color)}.select-loader-trigger{width:1rem;height:1rem;border:2px solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}.select-arrow{transition:transform .2s;font-size:1rem;color:var(--text-color-secondary)}:host[aria-expanded=true] .select-arrow{transform:rotate(180deg)}.select-dropdown.open-top{top:auto;bottom:calc(100% + .25rem)}.select-dropdown{position:absolute;top:calc(100% + .25rem);left:0;right:0;z-index:var(--z-index-dropdown);background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg)}.select-search{position:relative;padding:.5rem;border-bottom:.0625rem solid var(--surface-border)}.select-search-icon{position:absolute;left:1.25rem;top:50%;transform:translateY(-50%);color:var(--text-color-secondary);font-size:.875rem;pointer-events:none}.select-search input{width:100%;height:2rem;padding:0 .75rem 0 2rem;background:var(--surface-ground);border:.0625rem solid var(--surface-border);border-radius:var(--border-radius);color:var(--text-color);font-size:.875rem;outline:none;box-sizing:border-box;transition:border-color .15s}.select-search input::placeholder{color:var(--text-color-secondary)}.select-search input:focus{border-color:var(--primary-color)}.select-options{max-height:15rem;overflow-y:auto}.select-group-label{padding:.5rem 1rem;font-size:.75rem;font-weight:600;color:var(--text-color-secondary);background:var(--surface-ground)}.select-option{padding:.625rem 1rem;cursor:pointer;display:flex;align-items:flex-start;gap:.5rem}.select-option:hover{background:var(--surface-hover)}.select-option.selected{background:rgba(var(--primary-color-rgb),.1);color:var(--primary-color);font-weight:500}.select-option.focused{background:var(--surface-hover)}.select-option.disabled{opacity:var(--opacity-disabled);cursor:not-allowed}.select-option i{flex-shrink:0;line-height:1.4}.select-option-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.select-option-label{min-width:0}.select-option-description{font-size:.75rem;color:var(--text-color-secondary)}.select-error-message,.select-success-message,.select-hint{font-size:.875rem}.select-error-message{color:var(--danger-color)}.select-success-message{color:var(--success-color)}.select-hint{color:var(--text-color-secondary)}:host.small .select-trigger{text-overflow:ellipsis;height:2rem;font-size:.875rem}:host.large .select-trigger{text-overflow:ellipsis;height:3rem;font-size:1.125rem}@keyframes spin{to{transform:rotate(360deg)}}\n"] }]
4126
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], multiplePlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiplePlaceholder", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], success: [{ type: i0.Input, args: [{ isSignal: true, alias: "success", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], maxSelections: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelections", required: false }] }], minSelections: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSelections", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], searchConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchConfig", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], searched: [{ type: i0.Output, args: ["searched"] }], scrollEnd: [{ type: i0.Output, args: ["scrollEnd"] }] } });
4352
+ }, template: "<div class=\"select-container\">\r\n @if (label()) {\r\n <!-- label[for] delegates focus to its control natively; the click handler is a convenience. -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\r\n <label class=\"select-label\" [attr.for]=\"selectId\" (click)=\"focusSelect()\" [class.required]=\"required()\">\r\n {{ label() }}\r\n @if (required()) { <span class=\"required-mark\">*</span> }\r\n </label>\r\n }\r\n\r\n <div class=\"select-trigger-wrapper\">\r\n <div class=\"select-trigger\"\r\n role=\"combobox\"\r\n [id]=\"selectId\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n [attr.aria-controls]=\"selectId + '-listbox'\"\r\n [attr.aria-activedescendant]=\"activeDescendant()\"\r\n [attr.aria-label]=\"computedAriaLabel()\"\r\n [attr.aria-invalid]=\"!!error()\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-disabled]=\"disabled()\"\r\n [attr.aria-busy]=\"loading()\"\r\n [attr.aria-describedby]=\"describedBy()\"\r\n [attr.tabindex]=\"disabled() || loading() ? -1 : 0\"\r\n (keydown)=\"handleKeydown($event)\"\r\n (click)=\"toggle()\">\r\n <div class=\"select-value\">\r\n <span [class.placeholder]=\"!hasValue()\">\r\n {{ selectedLabel() }}\r\n </span>\r\n </div>\r\n <div class=\"select-actions\">\r\n @if (clearable() && hasValue() && !disabled()) {\r\n <button class=\"select-clear\" (click)=\"clear($event)\">\r\n <i class=\"ph ph-x\"></i>\r\n </button>\r\n }\r\n @if (loading()) {\r\n <div class=\"select-loader-trigger\"></div>\r\n } @else {\r\n <i class=\"ph ph-caret-down select-arrow\"></i>\r\n }\r\n </div>\r\n </div>\r\n\r\n <ng-template #panelTpl>\r\n <div class=\"select-dropdown\" [class.open-top]=\"resolvedSide() === 'top'\">\r\n @if (searchable()) {\r\n <div class=\"select-search\">\r\n <i class=\"ph ph-magnifying-glass select-search-icon\"></i>\r\n <input type=\"text\"\r\n [placeholder]=\"searchConfig().placeholder\"\r\n [value]=\"searchTerm()\"\r\n (input)=\"onSearch($event)\"\r\n (click)=\"$event.stopPropagation()\">\r\n </div>\r\n }\r\n <div class=\"select-options\"\r\n role=\"listbox\"\r\n [id]=\"selectId + '-listbox'\"\r\n [attr.aria-multiselectable]=\"multiple() || null\"\r\n (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions(); track getOptionKey(item)) {\r\n @if (isOptionGroup(item)) {\r\n <div class=\"select-group\" role=\"group\" [attr.aria-label]=\"item.label\">\r\n <div class=\"select-group-label\">{{ item.label }}</div>\r\n @for (opt of item.options; track opt.label) {\r\n <!-- option in a listbox: keyboard is handled at the widget level (arrows + aria-activedescendant). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\r\n <div class=\"select-option\"\r\n role=\"option\"\r\n [id]=\"selectId + '-' + opt.value\"\r\n [attr.aria-selected]=\"isSelected(opt)\"\r\n [attr.aria-disabled]=\"(item.disabled || opt.disabled) ? true : null\"\r\n [attr.tabindex]=\"(item.disabled || opt.disabled) ? -1 : null\"\r\n [class.selected]=\"isSelected(opt)\"\r\n [class.disabled]=\"item.disabled || opt.disabled\"\r\n [class.focused]=\"getFlatIndex(opt) === focusedIndex()\"\r\n (click)=\"select(opt, !!item.disabled)\">\r\n @if (opt.icon) { <i [class]=\"'ph ph-' + opt.icon\" aria-hidden=\"true\"></i> }\r\n <div class=\"select-option-content\">\r\n <span class=\"select-option-label\">{{ opt.label }}</span>\r\n @if (opt.description) { <span class=\"select-option-description\">{{ opt.description }}</span> }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <!-- option in a listbox: keyboard is handled at the widget level (arrows + aria-activedescendant). -->\r\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -->\r\n <div class=\"select-option\"\r\n role=\"option\"\r\n [id]=\"selectId + '-' + item.value\"\r\n [attr.aria-selected]=\"isSelected(item)\"\r\n [attr.aria-disabled]=\"item.disabled ? true : null\"\r\n [attr.tabindex]=\"item.disabled ? -1 : null\"\r\n [class.selected]=\"isSelected(item)\"\r\n [class.disabled]=\"item.disabled\"\r\n [class.focused]=\"getFlatIndex(item) === focusedIndex()\"\r\n (click)=\"select(item)\">\r\n @if (item.icon) { <i [class]=\"'ph ph-' + item.icon\" aria-hidden=\"true\"></i> }\r\n <div class=\"select-option-content\">\r\n <span class=\"select-option-label\">{{ item.label }}</span>\r\n @if (item.description) { <span class=\"select-option-description\">{{ item.description }}</span> }\r\n </div>\r\n </div>\r\n }\r\n } @empty {\r\n <div class=\"select-no-results\">Aucun r\u00E9sultat</div>\r\n }\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n @if (error()) {\r\n <div id=\"error-message\" class=\"select-error-message\" role=\"alert\">{{ error() }}</div>\r\n } @else if (success()) {\r\n <div id=\"success-message\" class=\"select-success-message\" role=\"status\">{{ success() }}</div>\r\n } @else if (hint()) {\r\n <div id=\"hint-message\" class=\"select-hint\">{{ hint() }}</div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;max-width:18.75rem}:host.full-width{max-width:none}.select-container{width:100%;display:flex;flex-direction:column;gap:var(--form-field-gap)}.select-trigger-wrapper{position:relative}.select-label{display:block;color:var(--text-color);font-size:.875rem;font-weight:500;cursor:pointer}.select-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;height:2.5rem;padding:0 1rem;background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);cursor:pointer;box-sizing:border-box}.select-trigger:focus{border-color:var(--primary-color);outline:none;box-shadow:0 0 0 .125rem var(--focus-ring-color)}:host.error .select-trigger{border-color:var(--danger-color)}:host.error .select-trigger:focus{box-shadow:0 0 0 .125rem var(--focus-ring-error)}:host.success .select-trigger{border-color:var(--success-color)}:host.success .select-trigger:focus{box-shadow:0 0 0 .125rem var(--focus-ring-success)}:host.disabled .select-trigger{background:var(--surface-ground);cursor:not-allowed;opacity:var(--opacity-disabled)}:host.filled .select-trigger{background:var(--surface-ground);border:.125rem solid var(--surface-border)}:host.filled .select-trigger:hover:not([disabled]):not(:focus){background:var(--surface-hover)}.select-value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.select-clear{background:none;border:none;cursor:pointer;padding:0;color:var(--text-color-secondary);display:flex}.select-clear:hover{color:var(--danger-color)}.select-loader-trigger{width:1rem;height:1rem;border:2px solid var(--surface-border);border-top-color:var(--primary-color);border-radius:50%;animation:spin .8s linear infinite}.select-arrow{transition:transform .2s;font-size:1rem;color:var(--text-color-secondary)}:host[aria-expanded=true] .select-arrow{transform:rotate(180deg)}.select-dropdown{display:flex;flex-direction:column;overflow:hidden;pointer-events:auto;background:var(--surface-0);border:.125rem solid var(--surface-border);border-radius:var(--border-radius);box-shadow:var(--shadow-lg)}.select-search{position:relative;flex-shrink:0;padding:.5rem;border-bottom:.0625rem solid var(--surface-border)}.select-search-icon{position:absolute;left:1.25rem;top:50%;transform:translateY(-50%);color:var(--text-color-secondary);font-size:.875rem;pointer-events:none}.select-search input{width:100%;height:2rem;padding:0 .75rem 0 2rem;background:var(--surface-ground);border:.0625rem solid var(--surface-border);border-radius:var(--border-radius);color:var(--text-color);font-size:.875rem;outline:none;box-sizing:border-box;transition:border-color .15s}.select-search input::placeholder{color:var(--text-color-secondary)}.select-search input:focus{border-color:var(--primary-color)}.select-options{flex:0 1 auto;max-height:15rem;overflow-y:auto}.select-group-label{padding:.5rem 1rem;font-size:.75rem;font-weight:600;color:var(--text-color-secondary);background:var(--surface-ground)}.select-option{padding:.625rem 1rem;cursor:pointer;display:flex;align-items:flex-start;gap:.5rem}.select-option:hover{background:var(--surface-hover)}.select-option.selected{background:rgba(var(--primary-color-rgb),.1);color:var(--primary-color);font-weight:500}.select-option.focused{background:var(--surface-hover)}.select-option.disabled{opacity:var(--opacity-disabled);cursor:not-allowed}.select-option i{flex-shrink:0;line-height:1.4}.select-option-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.select-option-label{min-width:0}.select-option-description{font-size:.75rem;color:var(--text-color-secondary)}.select-error-message,.select-success-message,.select-hint{font-size:.875rem}.select-error-message{color:var(--danger-color)}.select-success-message{color:var(--success-color)}.select-hint{color:var(--text-color-secondary)}:host.small .select-trigger{text-overflow:ellipsis;height:2rem;font-size:.875rem}:host.large .select-trigger{text-overflow:ellipsis;height:3rem;font-size:1.125rem}@keyframes spin{to{transform:rotate(360deg)}}\n"] }]
4353
+ }], ctorParameters: () => [], propDecorators: { panelTpl: [{ type: i0.ViewChild, args: ['panelTpl', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], multiplePlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiplePlaceholder", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], success: [{ type: i0.Input, args: [{ isSignal: true, alias: "success", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], maxSelections: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelections", required: false }] }], minSelections: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSelections", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], searchConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchConfig", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], searched: [{ type: i0.Output, args: ["searched"] }], scrollEnd: [{ type: i0.Output, args: ["scrollEnd"] }] } });
4127
4354
 
4128
4355
  const SIDEBAR_CONFIG = new InjectionToken('SIDEBAR_CONFIG', {
4129
4356
  factory: () => ({
@@ -6153,5 +6380,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
6153
6380
  * Generated bundle index. Do not edit.
6154
6381
  */
6155
6382
 
6156
- export { CHECKBOX_CONFIG, CUSTOMER_CONTEXT_SERVICE, INPUT_LABELS, MODAL_CONFIG, ModalService, NgxTranslateProvider, PAGINATION_CONFIG, PROGRESSBAR_CONFIG, PSH_THEME_OPTIONS, PshAlertComponent, PshAvatarComponent, PshBadgeComponent, PshButtonComponent, PshCardComponent, PshCheckboxComponent, PshClickOutsideDirective, PshCollapseComponent, PshDropdownComponent, PshFlowStepComponent, PshFocusTrapDirective, PshHorizontalCardComponent, PshInfoCardComponent, PshInputComponent, PshLiveAnnouncerService, PshMenuComponent, PshModalComponent, PshOverlayPositionService, PshOverlayService, PshPaginationComponent, PshProgressbarComponent, PshRadioComponent, PshSelectComponent, PshSidebarComponent, PshSpinLoaderComponent, PshStatCardComponent, PshStateFlowIndicatorComponent, PshStepComponent, PshStepperComponent, PshSwitchComponent, PshTabBarComponent, PshTabComponent, PshTableComponent, PshTabsComponent, PshTagComponent, PshTextareaComponent, PshToastComponent, PshToastService, PshTooltipComponent, RADIO_CONFIG, RADIO_STYLES, SIDEBAR_CONFIG, SPINLOADER_CONFIG, STATE_FLOW_INDICATOR_CONFIG, STEPPER_CONFIG, SWITCH_CONFIG, ScrollService, TABLE_CONFIG, TABS_CONFIG, TAB_BAR_CONFIG, TAG_CONFIG, TEXTAREA_LABELS, TOAST_CONFIG, TOOLTIP_CONFIG, TRANSLATION_PROVIDER, ThemeService, ToastComponent, ToastService, provideTranslation };
6383
+ export { CHECKBOX_CONFIG, CUSTOMER_CONTEXT_SERVICE, INPUT_LABELS, MODAL_CONFIG, ModalService, NgxTranslateProvider, PAGINATION_CONFIG, PROGRESSBAR_CONFIG, PSH_THEME_OPTIONS, PshAlertComponent, PshAvatarComponent, PshBadgeComponent, PshButtonComponent, PshCardComponent, PshCheckboxComponent, PshClickOutsideDirective, PshCollapseComponent, PshDropdownComponent, PshFlowStepComponent, PshFocusTrapDirective, PshHorizontalCardComponent, PshInfoCardComponent, PshInputComponent, PshLiveAnnouncerService, PshMenuComponent, PshModalComponent, PshOverlayPositionService, PshOverlayService, PshPaginationComponent, PshPortalService, PshProgressbarComponent, PshRadioComponent, PshSelectComponent, PshSidebarComponent, PshSpinLoaderComponent, PshStatCardComponent, PshStateFlowIndicatorComponent, PshStepComponent, PshStepperComponent, PshSwitchComponent, PshTabBarComponent, PshTabComponent, PshTableComponent, PshTabsComponent, PshTagComponent, PshTextareaComponent, PshToastComponent, PshToastService, PshTooltipComponent, RADIO_CONFIG, RADIO_STYLES, SIDEBAR_CONFIG, SPINLOADER_CONFIG, STATE_FLOW_INDICATOR_CONFIG, STEPPER_CONFIG, SWITCH_CONFIG, ScrollService, TABLE_CONFIG, TABS_CONFIG, TAB_BAR_CONFIG, TAG_CONFIG, TEXTAREA_LABELS, TOAST_CONFIG, TOOLTIP_CONFIG, TRANSLATION_PROVIDER, ThemeService, ToastComponent, ToastService, provideTranslation };
6157
6384
  //# sourceMappingURL=ps-helix.mjs.map