ps-helix 6.0.1 → 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, ViewContainerRef, 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,8 +2045,50 @@ class PshInputComponent {
1898
2045
  clearTimeout(this.blurTimeoutId);
1899
2046
  if (this.debounceTimeoutId)
1900
2047
  clearTimeout(this.debounceTimeoutId);
2048
+ this.closePanel();
1901
2049
  });
1902
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,
2089
+ });
2090
+ this.portalRef.position(anchor, side, 4);
2091
+ }
1903
2092
  writeValue(value) {
1904
2093
  const safeValue = typeof value === 'string' ? value : '';
1905
2094
  this.value.set(safeValue);
@@ -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',
@@ -3729,86 +3923,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
3729
3923
  type: Output
3730
3924
  }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3731
3925
 
3732
- /**
3733
- * Teleports a `TemplateRef` into a single body-level overlay layer so popover
3734
- * panels (select / dropdown / menu…) escape any ancestor `overflow`, `transform`
3735
- * or stacking context and can layer above modals.
3736
- *
3737
- * A lightweight, CDK-free "manual portal": the embedded view is created from the
3738
- * consumer's `ViewContainerRef` (so it stays in that component's change-detection
3739
- * tree and keeps its bindings reactive), then its root nodes are moved into the
3740
- * shared `.psh-overlay-layer`. The layer is `position: fixed; inset: 0;
3741
- * pointer-events: none` at `z-index: var(--z-index-overlay)`; panels re-enable
3742
- * pointer events. The layer is created lazily and removed once empty.
3743
- */
3744
- class PshPortalService {
3745
- constructor() {
3746
- this.document = inject(DOCUMENT);
3747
- this.layer = null;
3748
- this.count = 0;
3749
- }
3750
- /** Teleports `tpl` into the overlay layer and returns a handle to control it. */
3751
- attach(tpl, vcr) {
3752
- const layer = this.ensureLayer();
3753
- const viewRef = vcr.createEmbeddedView(tpl);
3754
- viewRef.detectChanges();
3755
- const nodes = viewRef.rootNodes;
3756
- const panel = (nodes.find((n) => n instanceof HTMLElement) ??
3757
- nodes[0]);
3758
- nodes.forEach(node => layer.appendChild(node));
3759
- this.count++;
3760
- return {
3761
- panel,
3762
- position: (anchor, side, gap) => this.position(panel, anchor, side, gap),
3763
- detach: () => this.detach(viewRef),
3764
- };
3765
- }
3766
- position(panel, anchor, side, gap) {
3767
- const view = this.document.defaultView;
3768
- if (!view)
3769
- return;
3770
- const rect = anchor.getBoundingClientRect();
3771
- const style = panel.style;
3772
- style.position = 'fixed';
3773
- style.left = `${rect.left}px`;
3774
- style.width = `${rect.width}px`;
3775
- if (side === 'top') {
3776
- const height = panel.offsetHeight;
3777
- style.top = `${Math.max(gap, rect.top - height - gap)}px`;
3778
- style.maxHeight = `${Math.max(0, rect.top - gap * 2)}px`;
3779
- }
3780
- else {
3781
- style.top = `${rect.bottom + gap}px`;
3782
- style.maxHeight = `${Math.max(0, view.innerHeight - rect.bottom - gap * 2)}px`;
3783
- }
3784
- }
3785
- detach(viewRef) {
3786
- viewRef.destroy();
3787
- this.count = Math.max(0, this.count - 1);
3788
- if (this.count === 0 && this.layer) {
3789
- this.layer.remove();
3790
- this.layer = null;
3791
- }
3792
- }
3793
- ensureLayer() {
3794
- if (this.layer)
3795
- return this.layer;
3796
- const layer = this.document.createElement('div');
3797
- layer.className = 'psh-overlay-layer';
3798
- layer.style.cssText =
3799
- 'position:fixed;inset:0;z-index:var(--z-index-overlay);pointer-events:none;';
3800
- this.document.body.appendChild(layer);
3801
- this.layer = layer;
3802
- return layer;
3803
- }
3804
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshPortalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3805
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshPortalService, providedIn: 'root' }); }
3806
- }
3807
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PshPortalService, decorators: [{
3808
- type: Injectable,
3809
- args: [{ providedIn: 'root' }]
3810
- }] });
3811
-
3812
3926
  class PshSelectComponent {
3813
3927
  constructor() {
3814
3928
  this.elementRef = inject(ElementRef);