@eagami/ui 2.9.0 → 2.10.0

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 { InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, signal, inject, effect, computed, Injectable, input, ChangeDetectionStrategy, Component, HostBinding, Directive, model, output, viewChild, forwardRef, HostListener, ElementRef, Renderer2, Injector, untracked, afterNextRender, ViewEncapsulation, DestroyRef, contentChild, viewChildren } from '@angular/core';
2
+ import { InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, signal, inject, effect, computed, Injectable, input, ChangeDetectionStrategy, Component, HostBinding, Directive, model, output, DestroyRef, viewChild, ElementRef, HostListener, forwardRef, Renderer2, Injector, untracked, afterNextRender, ViewEncapsulation, contentChild, viewChildren } from '@angular/core';
3
3
  import { NgClass, NgComponentOutlet, NgTemplateOutlet } from '@angular/common';
4
4
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
5
 
@@ -1270,11 +1270,11 @@ class AccordionComponent {
1270
1270
  return this.expandedItems().has(value);
1271
1271
  }
1272
1272
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1273
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.16", type: AccordionComponent, isStandalone: true, selector: "ea-accordion", inputs: { multi: { classPropertyName: "multi", publicName: "multi", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ` <div class="ea-accordion"><ng-content /></div> `, isInline: true, styles: [".ea-accordion{display:flex;flex-direction:column;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-lg);overflow:hidden}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1273
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.16", type: AccordionComponent, isStandalone: true, selector: "ea-accordion", inputs: { multi: { classPropertyName: "multi", publicName: "multi", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ` <div class="ea-accordion"><ng-content /></div> `, isInline: true, styles: [":host{display:block}.ea-accordion{display:flex;flex-direction:column;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-lg);overflow:hidden}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1274
1274
  }
1275
1275
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AccordionComponent, decorators: [{
1276
1276
  type: Component,
1277
- args: [{ selector: 'ea-accordion', template: ` <div class="ea-accordion"><ng-content /></div> `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".ea-accordion{display:flex;flex-direction:column;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-lg);overflow:hidden}\n"] }]
1277
+ args: [{ selector: 'ea-accordion', template: ` <div class="ea-accordion"><ng-content /></div> `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.ea-accordion{display:flex;flex-direction:column;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-lg);overflow:hidden}\n"] }]
1278
1278
  }], propDecorators: { multi: [{ type: i0.Input, args: [{ isSignal: true, alias: "multi", required: false }] }] } });
1279
1279
 
1280
1280
  /**
@@ -1742,226 +1742,653 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1742
1742
  args: [{ selector: 'ea-field-messages', imports: [AlertCircleIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (error()) {\n <p\n class=\"ea-field-messages__message ea-field-messages__message--error\"\n [id]=\"id() + '-error'\"\n role=\"alert\">\n <ea-icon-alert-circle class=\"ea-field-messages__icon\" />\n {{ error() }}\n </p>\n} @else if (hint()) {\n <p\n class=\"ea-field-messages__message ea-field-messages__message--hint\"\n [id]=\"id() + '-hint'\">\n {{ hint() }}\n </p>\n}\n", styles: [":host{display:contents}.ea-field-messages__message{display:flex;align-items:center;gap:var(--space-1);font-size:var(--text-helper-size);font-weight:var(--text-helper-weight);line-height:var(--text-helper-lh)}.ea-field-messages__message--hint{color:var(--color-text-secondary)}.ea-field-messages__message--error{color:var(--color-error-default)}.ea-field-messages__icon{flex-shrink:0;width:.875em;height:.875em}\n"] }]
1743
1743
  }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }] } });
1744
1744
 
1745
- /**
1746
- * Text input paired with a filtered suggestion list. Filters options by
1747
- * case-insensitive substring match, supports arrow-key navigation, and
1748
- * implements `ControlValueAccessor` for use with reactive and template-driven
1749
- * forms.
1750
- */
1751
- class AutocompleteComponent {
1752
- inputEl = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []));
1753
- hostEl = viewChild('hostEl', ...(ngDevMode ? [{ debugName: "hostEl" }] : /* istanbul ignore next */ []));
1754
- i18n = inject(EagamiI18nService);
1755
- label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1756
- placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1757
- options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1758
- size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
1759
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1760
- readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
1761
- required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
1762
- hint = input(undefined, ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
1763
- errorMsg = input(undefined, ...(ngDevMode ? [{ debugName: "errorMsg" }] : /* istanbul ignore next */ []));
1764
- minLength = input(0, ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
1765
- maxResults = input(10, ...(ngDevMode ? [{ debugName: "maxResults" }] : /* istanbul ignore next */ []));
1766
- emptyMessage = input(undefined, ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
1767
- id = input(uniqueId('ea-autocomplete'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
1768
- value = model('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1769
- /** Fires when the user picks an option from the suggestion list. */
1770
- selected = output();
1771
- /** Fires whenever the input text changes, including on free-text edits. */
1772
- changed = output();
1773
- /** Fires when the input receives focus. */
1774
- focused = output();
1775
- /** Fires when the input loses focus. */
1776
- blurred = output();
1777
- isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
1778
- isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1779
- focusedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "focusedIndex" }] : /* istanbul ignore next */ []));
1780
- _formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_formDisabled" }] : /* istanbul ignore next */ []));
1781
- justSelected = false;
1782
- onChange = () => { };
1783
- onTouched = () => { };
1784
- isDisabled = computed(() => this.disabled() || this._formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1785
- hasError = computed(() => !!this.errorMsg(), ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
1786
- showError = this.hasError;
1787
- showHint = computed(() => !!this.hint() && !this.hasError(), ...(ngDevMode ? [{ debugName: "showHint" }] : /* istanbul ignore next */ []));
1788
- filteredOptions = computed(() => {
1789
- const query = this.value().trim().toLowerCase();
1790
- const allOptions = this.options();
1791
- const max = this.maxResults();
1792
- if (query.length < this.minLength()) {
1793
- return [];
1794
- }
1795
- const matched = query
1796
- ? allOptions.filter(o => o.label.toLowerCase().includes(query))
1797
- : allOptions;
1798
- return matched.slice(0, max);
1799
- }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
1800
- showList = computed(() => this.isOpen() && this.value().length >= this.minLength(), ...(ngDevMode ? [{ debugName: "showList" }] : /* istanbul ignore next */ []));
1801
- showEmpty = computed(() => this.showList() && this.filteredOptions().length === 0, ...(ngDevMode ? [{ debugName: "showEmpty" }] : /* istanbul ignore next */ []));
1802
- /** Empty-list message, falling back to the active locale's translation. */
1803
- resolvedEmptyMessage = computed(() => this.emptyMessage() ?? this.i18n.messages().autocomplete.empty, ...(ngDevMode ? [{ debugName: "resolvedEmptyMessage" }] : /* istanbul ignore next */ []));
1804
- wrapperClasses = computed(() => ({
1805
- [`ea-autocomplete__wrapper--${this.size()}`]: true,
1806
- 'ea-autocomplete__wrapper--error': this.hasError(),
1807
- 'ea-autocomplete__wrapper--focused': this.isFocused(),
1808
- 'ea-autocomplete__wrapper--disabled': this.isDisabled(),
1809
- }), ...(ngDevMode ? [{ debugName: "wrapperClasses" }] : /* istanbul ignore next */ []));
1810
- listboxClasses = computed(() => ({
1811
- [`ea-autocomplete__listbox--${this.size()}`]: true,
1812
- }), ...(ngDevMode ? [{ debugName: "listboxClasses" }] : /* istanbul ignore next */ []));
1813
- writeValue(val) {
1814
- this.value.set(val ?? '');
1745
+ /** True for cardinal placements that centre the popover on the perpendicular axis. */
1746
+ function isCardinal(placement) {
1747
+ return (placement === 'top' ||
1748
+ placement === 'bottom' ||
1749
+ placement === 'left' ||
1750
+ placement === 'right');
1751
+ }
1752
+ /** The dominant side of a placement (`top-start` and `top` both give `top`, etc.). */
1753
+ function side(placement) {
1754
+ if (placement.startsWith('top')) {
1755
+ return 'top';
1815
1756
  }
1816
- registerOnChange(fn) {
1817
- this.onChange = fn;
1757
+ if (placement.startsWith('bottom')) {
1758
+ return 'bottom';
1818
1759
  }
1819
- registerOnTouched(fn) {
1820
- this.onTouched = fn;
1760
+ if (placement === 'left') {
1761
+ return 'left';
1821
1762
  }
1822
- setDisabledState(isDisabled) {
1823
- this._formDisabled.set(isDisabled);
1763
+ return 'right';
1764
+ }
1765
+ /** Maps `top` to `bottom`, `bottom-start` to `top-start`, etc. for flip logic. */
1766
+ function flipPlacement(placement) {
1767
+ if (placement === 'top') {
1768
+ return 'bottom';
1824
1769
  }
1825
- handleInput(event) {
1826
- const next = event.target.value;
1827
- this.value.set(next);
1828
- this.onChange(next);
1829
- this.changed.emit(next);
1830
- this.isOpen.set(true);
1831
- this.focusedIndex.set(-1);
1770
+ if (placement === 'bottom') {
1771
+ return 'top';
1832
1772
  }
1833
- handleFocus(event) {
1834
- this.isFocused.set(true);
1835
- this.focused.emit(event);
1836
- if (this.justSelected) {
1837
- this.justSelected = false;
1838
- return;
1839
- }
1840
- if (this.value().length >= this.minLength()) {
1841
- this.isOpen.set(true);
1842
- }
1773
+ if (placement === 'left') {
1774
+ return 'right';
1843
1775
  }
1844
- handleBlur(event) {
1845
- this.isFocused.set(false);
1846
- this.onTouched();
1847
- this.blurred.emit(event);
1776
+ if (placement === 'right') {
1777
+ return 'left';
1848
1778
  }
1849
- handleKeydown(event) {
1850
- if (this.isDisabled() || this.readonly()) {
1851
- return;
1852
- }
1853
- switch (event.key) {
1854
- case 'ArrowDown':
1855
- event.preventDefault();
1856
- if (!this.isOpen()) {
1857
- this.isOpen.set(true);
1858
- }
1859
- else {
1860
- this.moveFocus(1);
1861
- }
1862
- break;
1863
- case 'ArrowUp':
1864
- event.preventDefault();
1865
- if (this.isOpen()) {
1866
- this.moveFocus(-1);
1867
- }
1868
- break;
1869
- case 'Enter': {
1870
- const opts = this.filteredOptions();
1871
- const idx = this.focusedIndex();
1872
- if (this.isOpen() && idx >= 0 && idx < opts.length && !opts[idx].disabled) {
1873
- event.preventDefault();
1874
- this.selectOption(opts[idx]);
1875
- }
1876
- break;
1877
- }
1878
- case 'Escape':
1879
- if (this.isOpen()) {
1880
- event.preventDefault();
1881
- this.close();
1882
- }
1883
- break;
1884
- }
1779
+ if (placement === 'top-start') {
1780
+ return 'bottom-start';
1885
1781
  }
1886
- /** Programmatically selects the given option, updating the value and closing the list. */
1887
- selectOption(option) {
1888
- if (option.disabled || this.isDisabled()) {
1889
- return;
1890
- }
1891
- this.value.set(option.label);
1892
- this.onChange(option.label);
1893
- this.changed.emit(option.label);
1894
- this.selected.emit(option);
1895
- this.justSelected = true;
1896
- this.close();
1897
- this.inputEl()?.nativeElement.focus();
1782
+ if (placement === 'top-end') {
1783
+ return 'bottom-end';
1898
1784
  }
1899
- /** Closes the suggestion list without changing the current value. */
1900
- close() {
1901
- this.isOpen.set(false);
1902
- this.focusedIndex.set(-1);
1785
+ if (placement === 'bottom-start') {
1786
+ return 'top-start';
1903
1787
  }
1904
- /** Moves keyboard focus to the underlying text input. */
1905
- focus() {
1906
- this.inputEl()?.nativeElement.focus();
1788
+ return 'top-end';
1789
+ }
1790
+ /** Computes the top/left for a given placement without any flip or clamp logic. */
1791
+ function placeRaw(anchor, popover, placement, offset) {
1792
+ const s = side(placement);
1793
+ let top = 0;
1794
+ let left = 0;
1795
+ if (s === 'top') {
1796
+ top = anchor.top - popover.height - offset;
1907
1797
  }
1908
- moveFocus(delta) {
1909
- const opts = this.filteredOptions();
1910
- if (opts.length === 0) {
1911
- return;
1798
+ else if (s === 'bottom') {
1799
+ top = anchor.bottom + offset;
1800
+ }
1801
+ else if (s === 'left') {
1802
+ left = anchor.left - popover.width - offset;
1803
+ }
1804
+ else {
1805
+ left = anchor.right + offset;
1806
+ }
1807
+ if (s === 'top' || s === 'bottom') {
1808
+ if (isCardinal(placement)) {
1809
+ left = anchor.left + (anchor.width - popover.width) / 2;
1912
1810
  }
1913
- let idx = this.focusedIndex() + delta;
1914
- while (idx >= 0 && idx < opts.length && opts[idx].disabled) {
1915
- idx += delta;
1811
+ else if (placement === 'top-start' || placement === 'bottom-start') {
1812
+ left = anchor.left;
1916
1813
  }
1917
- if (idx < 0 || idx >= opts.length) {
1918
- return;
1814
+ else {
1815
+ left = anchor.right - popover.width;
1919
1816
  }
1920
- this.focusedIndex.set(idx);
1921
1817
  }
1922
- onDocumentClick(event) {
1923
- if (!this.isOpen()) {
1924
- return;
1925
- }
1926
- const host = this.hostEl()?.nativeElement;
1927
- if (host && !host.contains(event.target)) {
1928
- this.close();
1929
- }
1818
+ else {
1819
+ top = anchor.top + (anchor.height - popover.height) / 2;
1930
1820
  }
1931
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1932
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: AutocompleteComponent, isStandalone: true, selector: "ea-autocomplete", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, errorMsg: { classPropertyName: "errorMsg", publicName: "errorMsg", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxResults: { classPropertyName: "maxResults", publicName: "maxResults", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", selected: "selected", changed: "changed", focused: "focused", blurred: "blurred" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, providers: [
1933
- {
1934
- provide: NG_VALUE_ACCESSOR,
1935
- useExisting: forwardRef(() => AutocompleteComponent),
1936
- multi: true,
1937
- },
1938
- ], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }, { propertyName: "hostEl", first: true, predicate: ["hostEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n #hostEl\n class=\"ea-autocomplete\">\n @if (label(); as labelText) {\n <ea-field-label\n [text]=\"labelText\"\n [forId]=\"id()\"\n [required]=\"required()\" />\n }\n\n <div\n class=\"ea-autocomplete__wrapper\"\n [ngClass]=\"wrapperClasses()\">\n <span class=\"ea-autocomplete__prefix\">\n <ng-content select=\"[slot=prefix]\" />\n </span>\n\n <input\n #inputEl\n class=\"ea-autocomplete__input\"\n type=\"text\"\n autocomplete=\"off\"\n role=\"combobox\"\n [id]=\"id()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readOnly]=\"readonly()\"\n [required]=\"required()\"\n [value]=\"value()\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-controls]=\"id() + '-listbox'\"\n [attr.aria-activedescendant]=\"\n focusedIndex() >= 0 ? id() + '-option-' + focusedIndex() : null\n \"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"\n showError() ? id() + '-error' : showHint() ? id() + '-hint' : null\n \"\n (input)=\"handleInput($event)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeydown($event)\" />\n\n <span class=\"ea-autocomplete__suffix\">\n <ng-content select=\"[slot=suffix]\" />\n </span>\n </div>\n\n @if (showList()) {\n <ul\n class=\"ea-autocomplete__listbox\"\n [ngClass]=\"listboxClasses()\"\n role=\"listbox\"\n [id]=\"id() + '-listbox'\">\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <li\n class=\"ea-autocomplete__option\"\n role=\"option\"\n [id]=\"id() + '-option-' + i\"\n [class.ea-autocomplete__option--focused]=\"i === focusedIndex()\"\n [class.ea-autocomplete__option--disabled]=\"option.disabled\"\n [attr.aria-selected]=\"i === focusedIndex()\"\n [attr.aria-disabled]=\"option.disabled || null\"\n (mousedown)=\"selectOption(option)\"\n (mouseenter)=\"focusedIndex.set(i)\">\n {{ option.label }}\n </li>\n }\n @if (showEmpty()) {\n <li\n class=\"ea-autocomplete__empty\"\n role=\"presentation\">\n {{ resolvedEmptyMessage() }}\n </li>\n }\n </ul>\n }\n\n <ea-field-messages\n [id]=\"id()\"\n [error]=\"showError() ? errorMsg() : null\"\n [hint]=\"showHint() ? hint() : null\" />\n</div>\n", styles: [".ea-autocomplete{position:relative;display:flex;flex-direction:column;gap:var(--space-1-5)}.ea-autocomplete__wrapper{display:flex;align-items:center;gap:.5em;width:100%;min-height:2.5em;padding:.5em .75em;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);background-color:var(--color-bg-base);transition:var(--transition-colors),var(--transition-shadow)}.ea-autocomplete__wrapper--xs{font-size:var(--font-size-xs)}.ea-autocomplete__wrapper--sm{font-size:var(--font-size-sm)}.ea-autocomplete__wrapper--md{font-size:var(--font-size-md)}.ea-autocomplete__wrapper--lg{font-size:var(--font-size-lg)}.ea-autocomplete__wrapper--xl{font-size:var(--font-size-xl)}.ea-autocomplete__wrapper--focused{border-color:var(--color-border-focus);box-shadow:var(--shadow-focus-ring)}.ea-autocomplete__wrapper--error{border-color:var(--color-error-default)}.ea-autocomplete__wrapper--error.ea-autocomplete__wrapper--focused{box-shadow:var(--shadow-focus-ring-error)}.ea-autocomplete__wrapper--disabled{background-color:var(--color-bg-muted);opacity:.6;cursor:not-allowed}.ea-autocomplete__input{flex:1;min-width:0;padding:0;border:none;background:transparent;font-family:var(--font-family-sans);color:var(--color-text-primary);outline:none}.ea-autocomplete__input::placeholder{color:var(--color-text-tertiary)}.ea-autocomplete__input:disabled{cursor:not-allowed}.ea-autocomplete__prefix,.ea-autocomplete__suffix{display:flex;flex-shrink:0;align-items:center;color:var(--color-text-secondary)}.ea-autocomplete__prefix:empty,.ea-autocomplete__suffix:empty{display:none}.ea-autocomplete__listbox{position:absolute;z-index:var(--z-index-dropdown);top:100%;left:0;right:0;max-height:15rem;padding:var(--space-1) 0;margin:var(--space-1) 0 0;overflow-y:auto;list-style:none;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);background-color:var(--color-bg-elevated)}.ea-autocomplete__listbox--xs{font-size:var(--font-size-xs)}.ea-autocomplete__listbox--sm{font-size:var(--font-size-sm)}.ea-autocomplete__listbox--md{font-size:var(--font-size-md)}.ea-autocomplete__listbox--lg{font-size:var(--font-size-lg)}.ea-autocomplete__listbox--xl{font-size:var(--font-size-xl)}.ea-autocomplete__option{padding:var(--space-2) var(--space-3);font-size:inherit;font-family:var(--font-family-sans);color:var(--color-text-primary);cursor:pointer;transition:var(--transition-colors)}.ea-autocomplete__option--focused{background-color:var(--color-state-hover)}.ea-autocomplete__option--disabled{color:var(--color-text-disabled);cursor:not-allowed}.ea-autocomplete__empty{padding:var(--space-2) var(--space-3);font-size:inherit;font-style:italic;color:var(--color-text-tertiary)}\n"], dependencies: [{ kind: "component", type: FieldLabelComponent, selector: "ea-field-label", inputs: ["text", "forId", "required", "labelId"] }, { kind: "component", type: FieldMessagesComponent, selector: "ea-field-messages", inputs: ["id", "error", "hint"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1821
+ return { top, left };
1939
1822
  }
1940
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AutocompleteComponent, decorators: [{
1941
- type: Component,
1942
- args: [{ selector: 'ea-autocomplete', imports: [FieldLabelComponent, FieldMessagesComponent, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1943
- {
1944
- provide: NG_VALUE_ACCESSOR,
1945
- useExisting: forwardRef(() => AutocompleteComponent),
1946
- multi: true,
1947
- },
1948
- ], template: "<div\n #hostEl\n class=\"ea-autocomplete\">\n @if (label(); as labelText) {\n <ea-field-label\n [text]=\"labelText\"\n [forId]=\"id()\"\n [required]=\"required()\" />\n }\n\n <div\n class=\"ea-autocomplete__wrapper\"\n [ngClass]=\"wrapperClasses()\">\n <span class=\"ea-autocomplete__prefix\">\n <ng-content select=\"[slot=prefix]\" />\n </span>\n\n <input\n #inputEl\n class=\"ea-autocomplete__input\"\n type=\"text\"\n autocomplete=\"off\"\n role=\"combobox\"\n [id]=\"id()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readOnly]=\"readonly()\"\n [required]=\"required()\"\n [value]=\"value()\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-controls]=\"id() + '-listbox'\"\n [attr.aria-activedescendant]=\"\n focusedIndex() >= 0 ? id() + '-option-' + focusedIndex() : null\n \"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"\n showError() ? id() + '-error' : showHint() ? id() + '-hint' : null\n \"\n (input)=\"handleInput($event)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeydown($event)\" />\n\n <span class=\"ea-autocomplete__suffix\">\n <ng-content select=\"[slot=suffix]\" />\n </span>\n </div>\n\n @if (showList()) {\n <ul\n class=\"ea-autocomplete__listbox\"\n [ngClass]=\"listboxClasses()\"\n role=\"listbox\"\n [id]=\"id() + '-listbox'\">\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <li\n class=\"ea-autocomplete__option\"\n role=\"option\"\n [id]=\"id() + '-option-' + i\"\n [class.ea-autocomplete__option--focused]=\"i === focusedIndex()\"\n [class.ea-autocomplete__option--disabled]=\"option.disabled\"\n [attr.aria-selected]=\"i === focusedIndex()\"\n [attr.aria-disabled]=\"option.disabled || null\"\n (mousedown)=\"selectOption(option)\"\n (mouseenter)=\"focusedIndex.set(i)\">\n {{ option.label }}\n </li>\n }\n @if (showEmpty()) {\n <li\n class=\"ea-autocomplete__empty\"\n role=\"presentation\">\n {{ resolvedEmptyMessage() }}\n </li>\n }\n </ul>\n }\n\n <ea-field-messages\n [id]=\"id()\"\n [error]=\"showError() ? errorMsg() : null\"\n [hint]=\"showHint() ? hint() : null\" />\n</div>\n", styles: [".ea-autocomplete{position:relative;display:flex;flex-direction:column;gap:var(--space-1-5)}.ea-autocomplete__wrapper{display:flex;align-items:center;gap:.5em;width:100%;min-height:2.5em;padding:.5em .75em;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);background-color:var(--color-bg-base);transition:var(--transition-colors),var(--transition-shadow)}.ea-autocomplete__wrapper--xs{font-size:var(--font-size-xs)}.ea-autocomplete__wrapper--sm{font-size:var(--font-size-sm)}.ea-autocomplete__wrapper--md{font-size:var(--font-size-md)}.ea-autocomplete__wrapper--lg{font-size:var(--font-size-lg)}.ea-autocomplete__wrapper--xl{font-size:var(--font-size-xl)}.ea-autocomplete__wrapper--focused{border-color:var(--color-border-focus);box-shadow:var(--shadow-focus-ring)}.ea-autocomplete__wrapper--error{border-color:var(--color-error-default)}.ea-autocomplete__wrapper--error.ea-autocomplete__wrapper--focused{box-shadow:var(--shadow-focus-ring-error)}.ea-autocomplete__wrapper--disabled{background-color:var(--color-bg-muted);opacity:.6;cursor:not-allowed}.ea-autocomplete__input{flex:1;min-width:0;padding:0;border:none;background:transparent;font-family:var(--font-family-sans);color:var(--color-text-primary);outline:none}.ea-autocomplete__input::placeholder{color:var(--color-text-tertiary)}.ea-autocomplete__input:disabled{cursor:not-allowed}.ea-autocomplete__prefix,.ea-autocomplete__suffix{display:flex;flex-shrink:0;align-items:center;color:var(--color-text-secondary)}.ea-autocomplete__prefix:empty,.ea-autocomplete__suffix:empty{display:none}.ea-autocomplete__listbox{position:absolute;z-index:var(--z-index-dropdown);top:100%;left:0;right:0;max-height:15rem;padding:var(--space-1) 0;margin:var(--space-1) 0 0;overflow-y:auto;list-style:none;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);background-color:var(--color-bg-elevated)}.ea-autocomplete__listbox--xs{font-size:var(--font-size-xs)}.ea-autocomplete__listbox--sm{font-size:var(--font-size-sm)}.ea-autocomplete__listbox--md{font-size:var(--font-size-md)}.ea-autocomplete__listbox--lg{font-size:var(--font-size-lg)}.ea-autocomplete__listbox--xl{font-size:var(--font-size-xl)}.ea-autocomplete__option{padding:var(--space-2) var(--space-3);font-size:inherit;font-family:var(--font-family-sans);color:var(--color-text-primary);cursor:pointer;transition:var(--transition-colors)}.ea-autocomplete__option--focused{background-color:var(--color-state-hover)}.ea-autocomplete__option--disabled{color:var(--color-text-disabled);cursor:not-allowed}.ea-autocomplete__empty{padding:var(--space-2) var(--space-3);font-size:inherit;font-style:italic;color:var(--color-text-tertiary)}\n"] }]
1949
- }], propDecorators: { inputEl: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], hostEl: [{ type: i0.ViewChild, args: ['hostEl', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], errorMsg: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMsg", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], maxResults: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxResults", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], selected: [{ type: i0.Output, args: ["selected"] }], changed: [{ type: i0.Output, args: ["changed"] }], focused: [{ type: i0.Output, args: ["focused"] }], blurred: [{ type: i0.Output, args: ["blurred"] }], onDocumentClick: [{
1950
- type: HostListener,
1951
- args: ['document:click', ['$event']]
1952
- }] } });
1953
-
1954
- class CameraIconComponent extends IconComponentBase {
1955
- static slug = 'camera';
1956
- static category = 'feather';
1957
- static defaultStrokeWidth = 1.5;
1958
- static tags = [
1959
- 'camera',
1960
- 'photo',
1961
- 'photography',
1962
- 'picture',
1963
- 'snapshot',
1964
- 'appareil photo',
1823
+ /**
1824
+ * Computes the viewport-space top/left for a popover anchored to `anchorRect`,
1825
+ * applying optional flip-on-overflow and edge-clamp logic. Pure function, no
1826
+ * DOM access. Both `<ea-popover>` and `[eaTooltip]` consume this.
1827
+ *
1828
+ * @param anchorRect The anchor element's `getBoundingClientRect()`.
1829
+ * @param popoverRect Width and height of the popover (post-render measurement).
1830
+ * @param viewport Viewport dimensions (`window.innerWidth/Height`).
1831
+ * @param options Placement and behavior flags.
1832
+ */
1833
+ function computePopoverPosition(anchorRect, popoverRect, viewport, options) {
1834
+ const offset = options.offset ?? 4;
1835
+ const margin = options.margin ?? 8;
1836
+ const flip = options.flip ?? true;
1837
+ const clamp = options.clamp ?? true;
1838
+ let placement = options.placement;
1839
+ let pos = placeRaw(anchorRect, popoverRect, placement, offset);
1840
+ if (flip) {
1841
+ const overflowsTop = pos.top < margin;
1842
+ const overflowsBottom = pos.top + popoverRect.height > viewport.height - margin;
1843
+ const overflowsLeft = pos.left < margin;
1844
+ const overflowsRight = pos.left + popoverRect.width > viewport.width - margin;
1845
+ const s = side(placement);
1846
+ const shouldFlip = (s === 'top' && overflowsTop) ||
1847
+ (s === 'bottom' && overflowsBottom) ||
1848
+ (s === 'left' && overflowsLeft) ||
1849
+ (s === 'right' && overflowsRight);
1850
+ if (shouldFlip) {
1851
+ const flipped = flipPlacement(placement);
1852
+ const flippedPos = placeRaw(anchorRect, popoverRect, flipped, offset);
1853
+ const flippedFitsBetter = (s === 'top' &&
1854
+ flippedPos.top + popoverRect.height <= viewport.height - margin) ||
1855
+ (s === 'bottom' && flippedPos.top >= margin) ||
1856
+ (s === 'left' &&
1857
+ flippedPos.left + popoverRect.width <= viewport.width - margin) ||
1858
+ (s === 'right' && flippedPos.left >= margin);
1859
+ if (flippedFitsBetter) {
1860
+ placement = flipped;
1861
+ pos = flippedPos;
1862
+ }
1863
+ }
1864
+ }
1865
+ if (clamp) {
1866
+ // Only clamp the cross-axis (perpendicular to the placement). For
1867
+ // bottom-side placements we clamp `left` so the popover doesn't slip off
1868
+ // the left/right of the viewport, but we leave `top` alone; clamping it
1869
+ // when the popover is taller than the available space below the anchor
1870
+ // would yank it up over the anchor itself (the common Storybook-docs-
1871
+ // iframe failure mode where viewports are short). Better to let it
1872
+ // overflow and scroll than to overlap its own trigger.
1873
+ const s = side(placement);
1874
+ const isVertical = s === 'top' || s === 'bottom';
1875
+ if (isVertical) {
1876
+ const maxLeft = viewport.width - popoverRect.width - margin;
1877
+ pos = {
1878
+ top: pos.top,
1879
+ left: Math.max(margin, Math.min(pos.left, Math.max(margin, maxLeft))),
1880
+ };
1881
+ }
1882
+ else {
1883
+ const maxTop = viewport.height - popoverRect.height - margin;
1884
+ pos = {
1885
+ top: Math.max(margin, Math.min(pos.top, Math.max(margin, maxTop))),
1886
+ left: pos.left,
1887
+ };
1888
+ }
1889
+ }
1890
+ return {
1891
+ top: pos.top,
1892
+ left: pos.left,
1893
+ placement,
1894
+ ...(options.matchAnchorWidth ? { width: anchorRect.width } : {}),
1895
+ };
1896
+ }
1897
+
1898
+ /**
1899
+ * Floating-element primitive. Renders projected content as `position: fixed`
1900
+ * anchored to an external element, with flip-on-overflow, viewport clamping,
1901
+ * outside-click and Escape dismissal, and SSR-safe scroll / resize handling.
1902
+ *
1903
+ * The primitive is intentionally low-level: a parent component drives the
1904
+ * `[open]` state and listens for `(closeRequested)` to mirror it back. Internal
1905
+ * library components (`<ea-menu>`, `<ea-dropdown>`, `<ea-color-picker>`,
1906
+ * `<ea-date-picker>`, `[eaTooltip]`) compose on top of it; downstream apps can
1907
+ * use it directly to build their own popover-based UI.
1908
+ *
1909
+ * @example
1910
+ * ```html
1911
+ * <button #trigger (click)="open.set(!open())">Open</button>
1912
+ * <ea-popover [anchor]="trigger" [open]="open()" (closeRequested)="open.set(false)">
1913
+ * <div>Popover content</div>
1914
+ * </ea-popover>
1915
+ * ```
1916
+ */
1917
+ class PopoverComponent {
1918
+ destroyRef = inject(DestroyRef);
1919
+ surfaceEl = viewChild('surfaceEl', ...(ngDevMode ? [{ debugName: "surfaceEl" }] : /* istanbul ignore next */ []));
1920
+ /** Anchor element the popover positions itself against. */
1921
+ anchor = input.required(...(ngDevMode ? [{ debugName: "anchor" }] : /* istanbul ignore next */ []));
1922
+ /** Whether the popover is currently open. */
1923
+ open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
1924
+ /** Where the popover attaches relative to the anchor. */
1925
+ placement = input('bottom-start', ...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
1926
+ /** ARIA role applied to the popover surface. */
1927
+ role = input('dialog', ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
1928
+ /** Accessible label. Falls back to nothing; consumers should provide one when no visible heading is in the popover. */
1929
+ ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
1930
+ /** DOM id for the surface so trigger elements can reference it via aria-controls. */
1931
+ surfaceId = input(uniqueId('ea-popover'), ...(ngDevMode ? [{ debugName: "surfaceId" }] : /* istanbul ignore next */ []));
1932
+ /**
1933
+ * Gap in px between the anchor and the popover. Defaults to 2 so an open panel
1934
+ * clears the anchor's active focus ring instead of sitting flush over it.
1935
+ */
1936
+ offset = input(2, ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
1937
+ /** Flip to the opposite side when the requested side overflows the viewport. */
1938
+ flip = input(true, ...(ngDevMode ? [{ debugName: "flip" }] : /* istanbul ignore next */ []));
1939
+ /** Clamp the popover inside the viewport when it would otherwise overflow. */
1940
+ clamp = input(true, ...(ngDevMode ? [{ debugName: "clamp" }] : /* istanbul ignore next */ []));
1941
+ /** Set the popover's `min-width` to match the anchor's width (dropdown pattern). */
1942
+ matchAnchorWidth = input(false, ...(ngDevMode ? [{ debugName: "matchAnchorWidth" }] : /* istanbul ignore next */ []));
1943
+ /** Close on click outside the popover and the anchor. */
1944
+ closeOnOutsideClick = input(true, ...(ngDevMode ? [{ debugName: "closeOnOutsideClick" }] : /* istanbul ignore next */ []));
1945
+ /** Close on Escape. */
1946
+ closeOnEscape = input(true, ...(ngDevMode ? [{ debugName: "closeOnEscape" }] : /* istanbul ignore next */ []));
1947
+ /** What to do on scroll / resize while open. */
1948
+ scrollBehavior = input('reposition', ...(ngDevMode ? [{ debugName: "scrollBehavior" }] : /* istanbul ignore next */ []));
1949
+ /** Requested close. The parent should mirror this into `[open]`. */
1950
+ closeRequested = output();
1951
+ position = signal(null, ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
1952
+ /** True placement after flip, for class-based styling (e.g. arrow direction). */
1953
+ effectivePlacement = computed(() => this.position()?.placement ?? this.placement(), ...(ngDevMode ? [{ debugName: "effectivePlacement" }] : /* istanbul ignore next */ []));
1954
+ /** Latches true once the post-rAF reposition has run, so the surface is
1955
+ * only revealed after its dimensions are guaranteed stable. Reset on close. */
1956
+ stable = signal(false, ...(ngDevMode ? [{ debugName: "stable" }] : /* istanbul ignore next */ []));
1957
+ /** True once the first `reposition()` has resolved a placement on a
1958
+ * laid-out surface. Drives the `--positioned` class. */
1959
+ isPositioned = computed(() => this.open() && this.position() !== null && this.stable(), ...(ngDevMode ? [{ debugName: "isPositioned" }] : /* istanbul ignore next */ []));
1960
+ /** Class list for the surface. Computed in TS so the placement key (with
1961
+ * its interpolated suffix) and the positioned modifier compose cleanly. */
1962
+ surfaceClass = computed(() => `ea-popover__surface ea-popover__surface--${this.effectivePlacement()}${this.isPositioned() ? ' ea-popover__surface--positioned' : ''}`, ...(ngDevMode ? [{ debugName: "surfaceClass" }] : /* istanbul ignore next */ []));
1963
+ /** Inline style applied to the surface element. */
1964
+ surfaceStyle = computed(() => {
1965
+ if (!this.open()) {
1966
+ return { display: 'none' };
1967
+ }
1968
+ const p = this.position();
1969
+ if (!p) {
1970
+ return {};
1971
+ }
1972
+ const style = {
1973
+ top: `${p.top}px`,
1974
+ left: `${p.left}px`,
1975
+ };
1976
+ if (p.width !== undefined) {
1977
+ style['min-width'] = `${p.width}px`;
1978
+ }
1979
+ return style;
1980
+ }, ...(ngDevMode ? [{ debugName: "surfaceStyle" }] : /* istanbul ignore next */ []));
1981
+ constructor() {
1982
+ // Re-measure and reposition whenever the anchor, placement, surface, or
1983
+ // open state changes. Reading `surfaceEl()` here makes it a tracked signal
1984
+ // dependency, so the effect re-runs once Angular has rendered the `@if`
1985
+ // block and the viewChild signal has updated; at that point both the
1986
+ // anchor and the surface have a `getBoundingClientRect`, and the position
1987
+ // can be computed. This is more reliable than `afterNextRender` because it
1988
+ // doesn't depend on a single render cycle landing in the expected order
1989
+ // (some host environments, Storybook docs mode for example, defer that
1990
+ // callback in a way that leaves the surface stuck at `visibility: hidden`).
1991
+ // Naturally SSR-safe: the surface never renders on the server, so the
1992
+ // effect always early-returns during prerender.
1993
+ // Teleport the surface to `document.body` as soon as it exists so
1994
+ // `position: fixed` is always relative to the actual viewport (escaping
1995
+ // any transformed/contained ancestor that would otherwise create a new
1996
+ // containing block). Doing the move on init, not on open, also means
1997
+ // the first `getBoundingClientRect` call inside `reposition()` reads a
1998
+ // surface that's already in its final DOM home, so the browser's layout
1999
+ // is settled and dimensions are accurate. Skipped in SSR (no `document`).
2000
+ effect(() => {
2001
+ const surface = this.surfaceEl()?.nativeElement;
2002
+ if (surface &&
2003
+ typeof document !== 'undefined' &&
2004
+ surface.parentNode !== document.body) {
2005
+ document.body.appendChild(surface);
2006
+ }
2007
+ });
2008
+ effect(() => {
2009
+ const surface = this.surfaceEl()?.nativeElement;
2010
+ const anchor = this.resolveAnchor();
2011
+ const isOpen = this.open();
2012
+ if (!surface || !anchor || !isOpen) {
2013
+ this.position.set(null);
2014
+ this.stable.set(false);
2015
+ return;
2016
+ }
2017
+ // Re-read inputs so signal subscriptions stay current after a re-open.
2018
+ this.placement();
2019
+ this.offset();
2020
+ this.flip();
2021
+ this.clamp();
2022
+ this.matchAnchorWidth();
2023
+ // First reposition runs synchronously off the open effect (fast), but
2024
+ // the surface is still transitioning out of `display: none` and the
2025
+ // first `getBoundingClientRect` can report the surface's natural width
2026
+ // even when that overflows the viewport. We deliberately keep the
2027
+ // surface hidden (`visibility: hidden` from CSS) until the next rAF
2028
+ // when the browser has finished laying it out at its real dimensions;
2029
+ // the second reposition then uses accurate measurements and the
2030
+ // `stable` latch flips, revealing the surface at the final position
2031
+ // with no visible jump. SSR / non-browser hosts fall back to flipping
2032
+ // `stable` synchronously.
2033
+ this.reposition();
2034
+ if (typeof requestAnimationFrame !== 'undefined') {
2035
+ requestAnimationFrame(() => {
2036
+ if (!this.open()) {
2037
+ return;
2038
+ }
2039
+ this.reposition();
2040
+ this.stable.set(true);
2041
+ });
2042
+ }
2043
+ else {
2044
+ this.stable.set(true);
2045
+ }
2046
+ });
2047
+ // Watch the surface's own size and reposition whenever it changes. The
2048
+ // first `reposition()` after open fires synchronously inside the open
2049
+ // effect, while the surface is still transitioning out of `display: none`;
2050
+ // in some browsers the layout pass hasn't completed, so the
2051
+ // `getBoundingClientRect` width can read as the surface's natural width
2052
+ // even when that overflows the viewport, so the clamp can't kick in. The
2053
+ // ResizeObserver fires once the surface has been laid out with its real
2054
+ // dimensions, giving us a second, accurate measurement to clamp against.
2055
+ // Also catches projected-content size changes while the popover is open
2056
+ // (e.g. virtualised lists adding rows).
2057
+ if (typeof ResizeObserver !== 'undefined') {
2058
+ const surfaceResizeObserver = new ResizeObserver(() => {
2059
+ if (this.open()) {
2060
+ this.reposition();
2061
+ }
2062
+ });
2063
+ effect(() => {
2064
+ const surface = this.surfaceEl()?.nativeElement;
2065
+ surfaceResizeObserver.disconnect();
2066
+ if (surface) {
2067
+ surfaceResizeObserver.observe(surface);
2068
+ }
2069
+ });
2070
+ this.destroyRef.onDestroy(() => surfaceResizeObserver.disconnect());
2071
+ }
2072
+ // Listen for scroll / resize while open. The `scrollBehavior` input picks
2073
+ // the response. SSR guard is required because the website prerenders pages
2074
+ // that mount popovers.
2075
+ if (typeof window !== 'undefined') {
2076
+ const onViewportChange = (event) => {
2077
+ if (!this.open()) {
2078
+ return;
2079
+ }
2080
+ // Scrolling within the popover's own surface (e.g. a long dropdown list)
2081
+ // must not dismiss or re-track it; only outside/viewport scroll should.
2082
+ const target = event?.target;
2083
+ if (event?.type === 'scroll' &&
2084
+ target &&
2085
+ this.surfaceEl()?.nativeElement.contains(target)) {
2086
+ return;
2087
+ }
2088
+ const behavior = this.scrollBehavior();
2089
+ if (behavior === 'close') {
2090
+ this.closeRequested.emit();
2091
+ }
2092
+ else if (behavior === 'reposition') {
2093
+ this.reposition();
2094
+ }
2095
+ };
2096
+ window.addEventListener('scroll', onViewportChange, {
2097
+ capture: true,
2098
+ passive: true,
2099
+ });
2100
+ window.addEventListener('resize', onViewportChange);
2101
+ this.destroyRef.onDestroy(() => {
2102
+ window.removeEventListener('scroll', onViewportChange, { capture: true });
2103
+ window.removeEventListener('resize', onViewportChange);
2104
+ });
2105
+ }
2106
+ // Explicitly remove the portaled surface on destroy. Angular's view
2107
+ // destruction normally removes nodes the renderer created, but moving the
2108
+ // surface via raw `appendChild` (out of its original anchor slot) is
2109
+ // enough to break that tracking in some host environments: Storybook's
2110
+ // SPA navigation between docs pages, for one, leaves the surface stranded
2111
+ // in `document.body` after the parent component is gone. Removing it here
2112
+ // guarantees cleanup regardless of how Angular's view destruction handles
2113
+ // the relocated node.
2114
+ this.destroyRef.onDestroy(() => {
2115
+ const surface = this.surfaceEl()?.nativeElement;
2116
+ surface?.parentNode?.removeChild(surface);
2117
+ });
2118
+ }
2119
+ resolveAnchor() {
2120
+ const a = this.anchor();
2121
+ if (!a) {
2122
+ return null;
2123
+ }
2124
+ return a instanceof ElementRef ? a.nativeElement : a;
2125
+ }
2126
+ reposition() {
2127
+ if (typeof window === 'undefined') {
2128
+ return;
2129
+ }
2130
+ const anchor = this.resolveAnchor();
2131
+ const surface = this.surfaceEl()?.nativeElement;
2132
+ if (!anchor || !surface) {
2133
+ return;
2134
+ }
2135
+ const anchorRect = anchor.getBoundingClientRect();
2136
+ const surfaceRect = surface.getBoundingClientRect();
2137
+ this.position.set(computePopoverPosition(anchorRect, { width: surfaceRect.width, height: surfaceRect.height }, { width: window.innerWidth, height: window.innerHeight }, {
2138
+ placement: this.placement(),
2139
+ offset: this.offset(),
2140
+ flip: this.flip(),
2141
+ clamp: this.clamp(),
2142
+ matchAnchorWidth: this.matchAnchorWidth(),
2143
+ }));
2144
+ }
2145
+ onDocumentClick(event) {
2146
+ if (!this.open() || !this.closeOnOutsideClick()) {
2147
+ return;
2148
+ }
2149
+ const target = event.target;
2150
+ if (!target) {
2151
+ return;
2152
+ }
2153
+ const anchor = this.resolveAnchor();
2154
+ if (anchor?.contains(target)) {
2155
+ return;
2156
+ }
2157
+ if (this.surfaceEl()?.nativeElement.contains(target)) {
2158
+ return;
2159
+ }
2160
+ this.closeRequested.emit();
2161
+ }
2162
+ onEscape() {
2163
+ if (!this.open() || !this.closeOnEscape()) {
2164
+ return;
2165
+ }
2166
+ this.closeRequested.emit();
2167
+ }
2168
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2169
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.16", type: PopoverComponent, isStandalone: true, selector: "ea-popover", inputs: { anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: true, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, surfaceId: { classPropertyName: "surfaceId", publicName: "surfaceId", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, flip: { classPropertyName: "flip", publicName: "flip", isSignal: true, isRequired: false, transformFunction: null }, clamp: { classPropertyName: "clamp", publicName: "clamp", isSignal: true, isRequired: false, transformFunction: null }, matchAnchorWidth: { classPropertyName: "matchAnchorWidth", publicName: "matchAnchorWidth", isSignal: true, isRequired: false, transformFunction: null }, closeOnOutsideClick: { classPropertyName: "closeOnOutsideClick", publicName: "closeOnOutsideClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, scrollBehavior: { classPropertyName: "scrollBehavior", publicName: "scrollBehavior", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeRequested: "closeRequested" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" }, properties: { "attr.role": "null", "attr.aria-label": "null" } }, viewQueries: [{ propertyName: "surfaceEl", first: true, predicate: ["surfaceEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n The surface is rendered unconditionally so the `<ng-content/>` slot always\n exists. If we gated it on `@if (open())`, Angular would re-project the\n consumer's content at the popover host's position whenever the surface was\n absent, leaking menu items / picker controls into the document flow (made\n worse by `display: contents` on the host). Hiding via `display: none` keeps\n the projected DOM owned by the surface and out of the flow when closed.\n-->\n<div\n #surfaceEl\n [class]=\"surfaceClass()\"\n [id]=\"surfaceId()\"\n [attr.role]=\"open() ? role() : null\"\n [attr.aria-label]=\"open() ? ariaLabel() : null\"\n [attr.aria-hidden]=\"open() ? null : true\"\n [style]=\"surfaceStyle()\">\n <ng-content />\n</div>\n", styles: [":host{display:contents}.ea-popover__surface{z-index:var(--z-index-popover);position:fixed;visibility:hidden}.ea-popover__surface--positioned{visibility:visible}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2170
+ }
2171
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PopoverComponent, decorators: [{
2172
+ type: Component,
2173
+ args: [{ selector: 'ea-popover', changeDetection: ChangeDetectionStrategy.OnPush, host: {
2174
+ '[attr.role]': 'null',
2175
+ '[attr.aria-label]': 'null',
2176
+ }, template: "<!--\n The surface is rendered unconditionally so the `<ng-content/>` slot always\n exists. If we gated it on `@if (open())`, Angular would re-project the\n consumer's content at the popover host's position whenever the surface was\n absent, leaking menu items / picker controls into the document flow (made\n worse by `display: contents` on the host). Hiding via `display: none` keeps\n the projected DOM owned by the surface and out of the flow when closed.\n-->\n<div\n #surfaceEl\n [class]=\"surfaceClass()\"\n [id]=\"surfaceId()\"\n [attr.role]=\"open() ? role() : null\"\n [attr.aria-label]=\"open() ? ariaLabel() : null\"\n [attr.aria-hidden]=\"open() ? null : true\"\n [style]=\"surfaceStyle()\">\n <ng-content />\n</div>\n", styles: [":host{display:contents}.ea-popover__surface{z-index:var(--z-index-popover);position:fixed;visibility:hidden}.ea-popover__surface--positioned{visibility:visible}\n"] }]
2177
+ }], ctorParameters: () => [], propDecorators: { surfaceEl: [{ type: i0.ViewChild, args: ['surfaceEl', { isSignal: true }] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: true }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], surfaceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "surfaceId", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], flip: [{ type: i0.Input, args: [{ isSignal: true, alias: "flip", required: false }] }], clamp: [{ type: i0.Input, args: [{ isSignal: true, alias: "clamp", required: false }] }], matchAnchorWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "matchAnchorWidth", required: false }] }], closeOnOutsideClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnOutsideClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], scrollBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollBehavior", required: false }] }], closeRequested: [{ type: i0.Output, args: ["closeRequested"] }], onDocumentClick: [{
2178
+ type: HostListener,
2179
+ args: ['document:click', ['$event']]
2180
+ }], onEscape: [{
2181
+ type: HostListener,
2182
+ args: ['document:keydown.escape']
2183
+ }] } });
2184
+
2185
+ /**
2186
+ * Text input paired with a filtered suggestion list. Filters options by
2187
+ * case-insensitive substring match, supports arrow-key navigation, and
2188
+ * implements `ControlValueAccessor` for use with reactive and template-driven
2189
+ * forms.
2190
+ */
2191
+ class AutocompleteComponent {
2192
+ inputEl = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []));
2193
+ i18n = inject(EagamiI18nService);
2194
+ label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
2195
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
2196
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
2197
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2198
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
2199
+ readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
2200
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
2201
+ hint = input(undefined, ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
2202
+ errorMsg = input(undefined, ...(ngDevMode ? [{ debugName: "errorMsg" }] : /* istanbul ignore next */ []));
2203
+ minLength = input(0, ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
2204
+ maxResults = input(10, ...(ngDevMode ? [{ debugName: "maxResults" }] : /* istanbul ignore next */ []));
2205
+ emptyMessage = input(undefined, ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
2206
+ id = input(uniqueId('ea-autocomplete'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
2207
+ value = model('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2208
+ /** Fires when the user picks an option from the suggestion list. */
2209
+ selected = output();
2210
+ /** Fires whenever the input text changes, including on free-text edits. */
2211
+ changed = output();
2212
+ /** Fires when the input receives focus. */
2213
+ focused = output();
2214
+ /** Fires when the input loses focus. */
2215
+ blurred = output();
2216
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
2217
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
2218
+ focusedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "focusedIndex" }] : /* istanbul ignore next */ []));
2219
+ _formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_formDisabled" }] : /* istanbul ignore next */ []));
2220
+ justSelected = false;
2221
+ onChange = () => { };
2222
+ onTouched = () => { };
2223
+ isDisabled = computed(() => this.disabled() || this._formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
2224
+ hasError = computed(() => !!this.errorMsg(), ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
2225
+ showError = this.hasError;
2226
+ showHint = computed(() => !!this.hint() && !this.hasError(), ...(ngDevMode ? [{ debugName: "showHint" }] : /* istanbul ignore next */ []));
2227
+ filteredOptions = computed(() => {
2228
+ const query = this.value().trim().toLowerCase();
2229
+ const allOptions = this.options();
2230
+ const max = this.maxResults();
2231
+ if (query.length < this.minLength()) {
2232
+ return [];
2233
+ }
2234
+ const matched = query
2235
+ ? allOptions.filter(o => o.label.toLowerCase().includes(query))
2236
+ : allOptions;
2237
+ return matched.slice(0, max);
2238
+ }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
2239
+ showList = computed(() => this.isOpen() && this.value().length >= this.minLength(), ...(ngDevMode ? [{ debugName: "showList" }] : /* istanbul ignore next */ []));
2240
+ showEmpty = computed(() => this.showList() && this.filteredOptions().length === 0, ...(ngDevMode ? [{ debugName: "showEmpty" }] : /* istanbul ignore next */ []));
2241
+ /** Empty-list message, falling back to the active locale's translation. */
2242
+ resolvedEmptyMessage = computed(() => this.emptyMessage() ?? this.i18n.messages().autocomplete.empty, ...(ngDevMode ? [{ debugName: "resolvedEmptyMessage" }] : /* istanbul ignore next */ []));
2243
+ wrapperClasses = computed(() => ({
2244
+ [`ea-autocomplete__wrapper--${this.size()}`]: true,
2245
+ 'ea-autocomplete__wrapper--error': this.hasError(),
2246
+ 'ea-autocomplete__wrapper--focused': this.isFocused(),
2247
+ 'ea-autocomplete__wrapper--disabled': this.isDisabled(),
2248
+ }), ...(ngDevMode ? [{ debugName: "wrapperClasses" }] : /* istanbul ignore next */ []));
2249
+ listboxClasses = computed(() => ({
2250
+ [`ea-autocomplete__listbox--${this.size()}`]: true,
2251
+ }), ...(ngDevMode ? [{ debugName: "listboxClasses" }] : /* istanbul ignore next */ []));
2252
+ writeValue(val) {
2253
+ this.value.set(val ?? '');
2254
+ }
2255
+ registerOnChange(fn) {
2256
+ this.onChange = fn;
2257
+ }
2258
+ registerOnTouched(fn) {
2259
+ this.onTouched = fn;
2260
+ }
2261
+ setDisabledState(isDisabled) {
2262
+ this._formDisabled.set(isDisabled);
2263
+ }
2264
+ handleInput(event) {
2265
+ const next = event.target.value;
2266
+ this.value.set(next);
2267
+ this.onChange(next);
2268
+ this.changed.emit(next);
2269
+ this.isOpen.set(true);
2270
+ this.focusedIndex.set(-1);
2271
+ }
2272
+ handleFocus(event) {
2273
+ this.isFocused.set(true);
2274
+ this.focused.emit(event);
2275
+ if (this.justSelected) {
2276
+ this.justSelected = false;
2277
+ return;
2278
+ }
2279
+ if (this.value().length >= this.minLength()) {
2280
+ this.isOpen.set(true);
2281
+ }
2282
+ }
2283
+ handleBlur(event) {
2284
+ this.isFocused.set(false);
2285
+ this.onTouched();
2286
+ this.blurred.emit(event);
2287
+ }
2288
+ handleKeydown(event) {
2289
+ if (this.isDisabled() || this.readonly()) {
2290
+ return;
2291
+ }
2292
+ switch (event.key) {
2293
+ case 'ArrowDown':
2294
+ event.preventDefault();
2295
+ if (!this.isOpen()) {
2296
+ this.isOpen.set(true);
2297
+ }
2298
+ else {
2299
+ this.moveFocus(1);
2300
+ }
2301
+ break;
2302
+ case 'ArrowUp':
2303
+ event.preventDefault();
2304
+ if (this.isOpen()) {
2305
+ this.moveFocus(-1);
2306
+ }
2307
+ break;
2308
+ case 'Enter': {
2309
+ const opts = this.filteredOptions();
2310
+ const idx = this.focusedIndex();
2311
+ if (this.isOpen() && idx >= 0 && idx < opts.length && !opts[idx].disabled) {
2312
+ event.preventDefault();
2313
+ this.selectOption(opts[idx]);
2314
+ }
2315
+ break;
2316
+ }
2317
+ case 'Escape':
2318
+ if (this.isOpen()) {
2319
+ event.preventDefault();
2320
+ this.close();
2321
+ }
2322
+ break;
2323
+ }
2324
+ }
2325
+ /** Programmatically selects the given option, updating the value and closing the list. */
2326
+ selectOption(option) {
2327
+ if (option.disabled || this.isDisabled()) {
2328
+ return;
2329
+ }
2330
+ this.value.set(option.label);
2331
+ this.onChange(option.label);
2332
+ this.changed.emit(option.label);
2333
+ this.selected.emit(option);
2334
+ this.justSelected = true;
2335
+ this.close();
2336
+ this.inputEl()?.nativeElement.focus();
2337
+ }
2338
+ /** Closes the suggestion list without changing the current value. */
2339
+ close() {
2340
+ this.isOpen.set(false);
2341
+ this.focusedIndex.set(-1);
2342
+ }
2343
+ /** Moves keyboard focus to the underlying text input. */
2344
+ focus() {
2345
+ this.inputEl()?.nativeElement.focus();
2346
+ }
2347
+ moveFocus(delta) {
2348
+ const opts = this.filteredOptions();
2349
+ if (opts.length === 0) {
2350
+ return;
2351
+ }
2352
+ let idx = this.focusedIndex() + delta;
2353
+ while (idx >= 0 && idx < opts.length && opts[idx].disabled) {
2354
+ idx += delta;
2355
+ }
2356
+ if (idx < 0 || idx >= opts.length) {
2357
+ return;
2358
+ }
2359
+ this.focusedIndex.set(idx);
2360
+ }
2361
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2362
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: AutocompleteComponent, isStandalone: true, selector: "ea-autocomplete", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, errorMsg: { classPropertyName: "errorMsg", publicName: "errorMsg", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxResults: { classPropertyName: "maxResults", publicName: "maxResults", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", selected: "selected", changed: "changed", focused: "focused", blurred: "blurred" }, providers: [
2363
+ {
2364
+ provide: NG_VALUE_ACCESSOR,
2365
+ useExisting: forwardRef(() => AutocompleteComponent),
2366
+ multi: true,
2367
+ },
2368
+ ], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ea-autocomplete\">\n @if (label(); as labelText) {\n <ea-field-label\n [text]=\"labelText\"\n [forId]=\"id()\"\n [required]=\"required()\" />\n }\n\n <div\n #anchorEl\n class=\"ea-autocomplete__wrapper\"\n [ngClass]=\"wrapperClasses()\">\n <span class=\"ea-autocomplete__prefix\">\n <ng-content select=\"[slot=prefix]\" />\n </span>\n\n <input\n #inputEl\n class=\"ea-autocomplete__input\"\n type=\"text\"\n autocomplete=\"off\"\n role=\"combobox\"\n [id]=\"id()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readOnly]=\"readonly()\"\n [required]=\"required()\"\n [value]=\"value()\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-controls]=\"id() + '-listbox'\"\n [attr.aria-activedescendant]=\"\n focusedIndex() >= 0 ? id() + '-option-' + focusedIndex() : null\n \"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"\n showError() ? id() + '-error' : showHint() ? id() + '-hint' : null\n \"\n (input)=\"handleInput($event)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeydown($event)\" />\n\n <span class=\"ea-autocomplete__suffix\">\n <ng-content select=\"[slot=suffix]\" />\n </span>\n </div>\n\n <ea-popover\n [anchor]=\"anchorEl\"\n [open]=\"showList()\"\n placement=\"bottom-start\"\n role=\"listbox\"\n [surfaceId]=\"id() + '-listbox'\"\n [matchAnchorWidth]=\"true\"\n [closeOnEscape]=\"false\"\n scrollBehavior=\"close\"\n (closeRequested)=\"close()\">\n <div\n class=\"ea-autocomplete__listbox\"\n [ngClass]=\"listboxClasses()\">\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <div\n class=\"ea-autocomplete__option\"\n role=\"option\"\n [id]=\"id() + '-option-' + i\"\n [class.ea-autocomplete__option--focused]=\"i === focusedIndex()\"\n [class.ea-autocomplete__option--disabled]=\"option.disabled\"\n [attr.aria-selected]=\"i === focusedIndex()\"\n [attr.aria-disabled]=\"option.disabled || null\"\n (mousedown)=\"selectOption(option)\"\n (mouseenter)=\"focusedIndex.set(i)\">\n {{ option.label }}\n </div>\n }\n @if (showEmpty()) {\n <div\n class=\"ea-autocomplete__empty\"\n role=\"presentation\">\n {{ resolvedEmptyMessage() }}\n </div>\n }\n </div>\n </ea-popover>\n\n <ea-field-messages\n [id]=\"id()\"\n [error]=\"showError() ? errorMsg() : null\"\n [hint]=\"showHint() ? hint() : null\" />\n</div>\n", styles: [".ea-autocomplete{position:relative;display:flex;flex-direction:column;gap:var(--space-1-5)}.ea-autocomplete__wrapper{display:flex;align-items:center;gap:.5em;width:100%;min-height:2.5em;padding:.5em .75em;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);background-color:var(--color-bg-base);transition:var(--transition-colors),var(--transition-shadow)}.ea-autocomplete__wrapper--xs{font-size:var(--font-size-xs)}.ea-autocomplete__wrapper--sm{font-size:var(--font-size-sm)}.ea-autocomplete__wrapper--md{font-size:var(--font-size-md)}.ea-autocomplete__wrapper--lg{font-size:var(--font-size-lg)}.ea-autocomplete__wrapper--xl{font-size:var(--font-size-xl)}.ea-autocomplete__wrapper--focused{border-color:var(--color-border-focus);box-shadow:var(--shadow-focus-ring)}.ea-autocomplete__wrapper--error{border-color:var(--color-error-default)}.ea-autocomplete__wrapper--error.ea-autocomplete__wrapper--focused{box-shadow:var(--shadow-focus-ring-error)}.ea-autocomplete__wrapper--disabled{background-color:var(--color-bg-muted);opacity:.6;cursor:not-allowed}.ea-autocomplete__input{flex:1;min-width:0;padding:0;border:none;background:transparent;font-family:var(--font-family-sans);color:var(--color-text-primary);outline:none}.ea-autocomplete__input::placeholder{color:var(--color-text-tertiary)}.ea-autocomplete__input:disabled{cursor:not-allowed}.ea-autocomplete__prefix,.ea-autocomplete__suffix{display:flex;flex-shrink:0;align-items:center;color:var(--color-text-secondary)}.ea-autocomplete__prefix:empty,.ea-autocomplete__suffix:empty{display:none}.ea-autocomplete__listbox{overflow-y:auto;max-height:15rem;padding:var(--space-1) 0;list-style:none;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);background-color:var(--color-bg-elevated)}.ea-autocomplete__listbox--xs{font-size:var(--font-size-xs)}.ea-autocomplete__listbox--sm{font-size:var(--font-size-sm)}.ea-autocomplete__listbox--md{font-size:var(--font-size-md)}.ea-autocomplete__listbox--lg{font-size:var(--font-size-lg)}.ea-autocomplete__listbox--xl{font-size:var(--font-size-xl)}.ea-autocomplete__option{padding:var(--space-2) var(--space-3);font-size:inherit;font-family:var(--font-family-sans);color:var(--color-text-primary);cursor:pointer;transition:var(--transition-colors)}.ea-autocomplete__option--focused{background-color:var(--color-state-hover)}.ea-autocomplete__option--disabled{color:var(--color-text-disabled);cursor:not-allowed}.ea-autocomplete__empty{padding:var(--space-2) var(--space-3);font-size:inherit;font-style:italic;color:var(--color-text-tertiary)}\n"], dependencies: [{ kind: "component", type: FieldLabelComponent, selector: "ea-field-label", inputs: ["text", "forId", "required", "labelId"] }, { kind: "component", type: FieldMessagesComponent, selector: "ea-field-messages", inputs: ["id", "error", "hint"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: PopoverComponent, selector: "ea-popover", inputs: ["anchor", "open", "placement", "role", "aria-label", "surfaceId", "offset", "flip", "clamp", "matchAnchorWidth", "closeOnOutsideClick", "closeOnEscape", "scrollBehavior"], outputs: ["closeRequested"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2369
+ }
2370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AutocompleteComponent, decorators: [{
2371
+ type: Component,
2372
+ args: [{ selector: 'ea-autocomplete', imports: [FieldLabelComponent, FieldMessagesComponent, NgClass, PopoverComponent], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2373
+ {
2374
+ provide: NG_VALUE_ACCESSOR,
2375
+ useExisting: forwardRef(() => AutocompleteComponent),
2376
+ multi: true,
2377
+ },
2378
+ ], template: "<div class=\"ea-autocomplete\">\n @if (label(); as labelText) {\n <ea-field-label\n [text]=\"labelText\"\n [forId]=\"id()\"\n [required]=\"required()\" />\n }\n\n <div\n #anchorEl\n class=\"ea-autocomplete__wrapper\"\n [ngClass]=\"wrapperClasses()\">\n <span class=\"ea-autocomplete__prefix\">\n <ng-content select=\"[slot=prefix]\" />\n </span>\n\n <input\n #inputEl\n class=\"ea-autocomplete__input\"\n type=\"text\"\n autocomplete=\"off\"\n role=\"combobox\"\n [id]=\"id()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [readOnly]=\"readonly()\"\n [required]=\"required()\"\n [value]=\"value()\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-controls]=\"id() + '-listbox'\"\n [attr.aria-activedescendant]=\"\n focusedIndex() >= 0 ? id() + '-option-' + focusedIndex() : null\n \"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"\n showError() ? id() + '-error' : showHint() ? id() + '-hint' : null\n \"\n (input)=\"handleInput($event)\"\n (focus)=\"handleFocus($event)\"\n (blur)=\"handleBlur($event)\"\n (keydown)=\"handleKeydown($event)\" />\n\n <span class=\"ea-autocomplete__suffix\">\n <ng-content select=\"[slot=suffix]\" />\n </span>\n </div>\n\n <ea-popover\n [anchor]=\"anchorEl\"\n [open]=\"showList()\"\n placement=\"bottom-start\"\n role=\"listbox\"\n [surfaceId]=\"id() + '-listbox'\"\n [matchAnchorWidth]=\"true\"\n [closeOnEscape]=\"false\"\n scrollBehavior=\"close\"\n (closeRequested)=\"close()\">\n <div\n class=\"ea-autocomplete__listbox\"\n [ngClass]=\"listboxClasses()\">\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <div\n class=\"ea-autocomplete__option\"\n role=\"option\"\n [id]=\"id() + '-option-' + i\"\n [class.ea-autocomplete__option--focused]=\"i === focusedIndex()\"\n [class.ea-autocomplete__option--disabled]=\"option.disabled\"\n [attr.aria-selected]=\"i === focusedIndex()\"\n [attr.aria-disabled]=\"option.disabled || null\"\n (mousedown)=\"selectOption(option)\"\n (mouseenter)=\"focusedIndex.set(i)\">\n {{ option.label }}\n </div>\n }\n @if (showEmpty()) {\n <div\n class=\"ea-autocomplete__empty\"\n role=\"presentation\">\n {{ resolvedEmptyMessage() }}\n </div>\n }\n </div>\n </ea-popover>\n\n <ea-field-messages\n [id]=\"id()\"\n [error]=\"showError() ? errorMsg() : null\"\n [hint]=\"showHint() ? hint() : null\" />\n</div>\n", styles: [".ea-autocomplete{position:relative;display:flex;flex-direction:column;gap:var(--space-1-5)}.ea-autocomplete__wrapper{display:flex;align-items:center;gap:.5em;width:100%;min-height:2.5em;padding:.5em .75em;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);background-color:var(--color-bg-base);transition:var(--transition-colors),var(--transition-shadow)}.ea-autocomplete__wrapper--xs{font-size:var(--font-size-xs)}.ea-autocomplete__wrapper--sm{font-size:var(--font-size-sm)}.ea-autocomplete__wrapper--md{font-size:var(--font-size-md)}.ea-autocomplete__wrapper--lg{font-size:var(--font-size-lg)}.ea-autocomplete__wrapper--xl{font-size:var(--font-size-xl)}.ea-autocomplete__wrapper--focused{border-color:var(--color-border-focus);box-shadow:var(--shadow-focus-ring)}.ea-autocomplete__wrapper--error{border-color:var(--color-error-default)}.ea-autocomplete__wrapper--error.ea-autocomplete__wrapper--focused{box-shadow:var(--shadow-focus-ring-error)}.ea-autocomplete__wrapper--disabled{background-color:var(--color-bg-muted);opacity:.6;cursor:not-allowed}.ea-autocomplete__input{flex:1;min-width:0;padding:0;border:none;background:transparent;font-family:var(--font-family-sans);color:var(--color-text-primary);outline:none}.ea-autocomplete__input::placeholder{color:var(--color-text-tertiary)}.ea-autocomplete__input:disabled{cursor:not-allowed}.ea-autocomplete__prefix,.ea-autocomplete__suffix{display:flex;flex-shrink:0;align-items:center;color:var(--color-text-secondary)}.ea-autocomplete__prefix:empty,.ea-autocomplete__suffix:empty{display:none}.ea-autocomplete__listbox{overflow-y:auto;max-height:15rem;padding:var(--space-1) 0;list-style:none;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);background-color:var(--color-bg-elevated)}.ea-autocomplete__listbox--xs{font-size:var(--font-size-xs)}.ea-autocomplete__listbox--sm{font-size:var(--font-size-sm)}.ea-autocomplete__listbox--md{font-size:var(--font-size-md)}.ea-autocomplete__listbox--lg{font-size:var(--font-size-lg)}.ea-autocomplete__listbox--xl{font-size:var(--font-size-xl)}.ea-autocomplete__option{padding:var(--space-2) var(--space-3);font-size:inherit;font-family:var(--font-family-sans);color:var(--color-text-primary);cursor:pointer;transition:var(--transition-colors)}.ea-autocomplete__option--focused{background-color:var(--color-state-hover)}.ea-autocomplete__option--disabled{color:var(--color-text-disabled);cursor:not-allowed}.ea-autocomplete__empty{padding:var(--space-2) var(--space-3);font-size:inherit;font-style:italic;color:var(--color-text-tertiary)}\n"] }]
2379
+ }], propDecorators: { inputEl: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], errorMsg: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMsg", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], maxResults: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxResults", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], selected: [{ type: i0.Output, args: ["selected"] }], changed: [{ type: i0.Output, args: ["changed"] }], focused: [{ type: i0.Output, args: ["focused"] }], blurred: [{ type: i0.Output, args: ["blurred"] }] } });
2380
+
2381
+ class CameraIconComponent extends IconComponentBase {
2382
+ static slug = 'camera';
2383
+ static category = 'feather';
2384
+ static defaultStrokeWidth = 1.5;
2385
+ static tags = [
2386
+ 'camera',
2387
+ 'photo',
2388
+ 'photography',
2389
+ 'picture',
2390
+ 'snapshot',
2391
+ 'appareil photo',
1965
2392
  'cámara',
1966
2393
  'foto',
1967
2394
  'κάμερα',
@@ -2358,171 +2785,18 @@ class SkeletonComponent {
2358
2785
  styles['height'] = this.height();
2359
2786
  }
2360
2787
  return styles;
2361
- }, ...(ngDevMode ? [{ debugName: "hostStyles" }] : /* istanbul ignore next */ []));
2362
- hostClasses = computed(() => ({
2363
- [`ea-skeleton--${this.variant()}`]: true,
2364
- 'ea-skeleton--animated': this.animated(),
2365
- }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
2366
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2367
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.16", type: SkeletonComponent, isStandalone: true, selector: "ea-skeleton", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"ea-skeleton\"\n [class.ea-skeleton--text]=\"variant() === 'text'\"\n [class.ea-skeleton--circle]=\"variant() === 'circle'\"\n [class.ea-skeleton--rect]=\"variant() === 'rect'\"\n [class.ea-skeleton--animated]=\"animated()\"\n [style]=\"hostStyles()\"\n aria-hidden=\"true\">\n</div>\n", styles: [".ea-skeleton{display:block;background-color:var(--color-bg-muted);border-radius:var(--radius-md)}.ea-skeleton--text{width:100%;height:1rem;border-radius:var(--radius-sm)}.ea-skeleton--circle{width:2.5rem;height:2.5rem;border-radius:var(--radius-full)}.ea-skeleton--rect{width:100%;height:6rem}.ea-skeleton--animated{animation:ea-skeleton-pulse 1.5s ease-in-out infinite}@keyframes ea-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}@media(prefers-reduced-motion:reduce){.ea-skeleton--animated{animation:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2368
- }
2369
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SkeletonComponent, decorators: [{
2370
- type: Component,
2371
- args: [{ selector: 'ea-skeleton', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"ea-skeleton\"\n [class.ea-skeleton--text]=\"variant() === 'text'\"\n [class.ea-skeleton--circle]=\"variant() === 'circle'\"\n [class.ea-skeleton--rect]=\"variant() === 'rect'\"\n [class.ea-skeleton--animated]=\"animated()\"\n [style]=\"hostStyles()\"\n aria-hidden=\"true\">\n</div>\n", styles: [".ea-skeleton{display:block;background-color:var(--color-bg-muted);border-radius:var(--radius-md)}.ea-skeleton--text{width:100%;height:1rem;border-radius:var(--radius-sm)}.ea-skeleton--circle{width:2.5rem;height:2.5rem;border-radius:var(--radius-full)}.ea-skeleton--rect{width:100%;height:6rem}.ea-skeleton--animated{animation:ea-skeleton-pulse 1.5s ease-in-out infinite}@keyframes ea-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}@media(prefers-reduced-motion:reduce){.ea-skeleton--animated{animation:none}}\n"] }]
2372
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], animated: [{ type: i0.Input, args: [{ isSignal: true, alias: "animated", required: false }] }] } });
2373
-
2374
- /** True for cardinal placements that centre the popover on the perpendicular axis. */
2375
- function isCardinal(placement) {
2376
- return (placement === 'top' ||
2377
- placement === 'bottom' ||
2378
- placement === 'left' ||
2379
- placement === 'right');
2380
- }
2381
- /** The dominant side of a placement (`top-start` and `top` both give `top`, etc.). */
2382
- function side(placement) {
2383
- if (placement.startsWith('top')) {
2384
- return 'top';
2385
- }
2386
- if (placement.startsWith('bottom')) {
2387
- return 'bottom';
2388
- }
2389
- if (placement === 'left') {
2390
- return 'left';
2391
- }
2392
- return 'right';
2393
- }
2394
- /** Maps `top` to `bottom`, `bottom-start` to `top-start`, etc. for flip logic. */
2395
- function flipPlacement(placement) {
2396
- if (placement === 'top') {
2397
- return 'bottom';
2398
- }
2399
- if (placement === 'bottom') {
2400
- return 'top';
2401
- }
2402
- if (placement === 'left') {
2403
- return 'right';
2404
- }
2405
- if (placement === 'right') {
2406
- return 'left';
2407
- }
2408
- if (placement === 'top-start') {
2409
- return 'bottom-start';
2410
- }
2411
- if (placement === 'top-end') {
2412
- return 'bottom-end';
2413
- }
2414
- if (placement === 'bottom-start') {
2415
- return 'top-start';
2416
- }
2417
- return 'top-end';
2418
- }
2419
- /** Computes the top/left for a given placement without any flip or clamp logic. */
2420
- function placeRaw(anchor, popover, placement, offset) {
2421
- const s = side(placement);
2422
- let top = 0;
2423
- let left = 0;
2424
- if (s === 'top') {
2425
- top = anchor.top - popover.height - offset;
2426
- }
2427
- else if (s === 'bottom') {
2428
- top = anchor.bottom + offset;
2429
- }
2430
- else if (s === 'left') {
2431
- left = anchor.left - popover.width - offset;
2432
- }
2433
- else {
2434
- left = anchor.right + offset;
2435
- }
2436
- if (s === 'top' || s === 'bottom') {
2437
- if (isCardinal(placement)) {
2438
- left = anchor.left + (anchor.width - popover.width) / 2;
2439
- }
2440
- else if (placement === 'top-start' || placement === 'bottom-start') {
2441
- left = anchor.left;
2442
- }
2443
- else {
2444
- left = anchor.right - popover.width;
2445
- }
2446
- }
2447
- else {
2448
- top = anchor.top + (anchor.height - popover.height) / 2;
2449
- }
2450
- return { top, left };
2451
- }
2452
- /**
2453
- * Computes the viewport-space top/left for a popover anchored to `anchorRect`,
2454
- * applying optional flip-on-overflow and edge-clamp logic. Pure function, no
2455
- * DOM access. Both `<ea-popover>` and `[eaTooltip]` consume this.
2456
- *
2457
- * @param anchorRect The anchor element's `getBoundingClientRect()`.
2458
- * @param popoverRect Width and height of the popover (post-render measurement).
2459
- * @param viewport Viewport dimensions (`window.innerWidth/Height`).
2460
- * @param options Placement and behavior flags.
2461
- */
2462
- function computePopoverPosition(anchorRect, popoverRect, viewport, options) {
2463
- const offset = options.offset ?? 4;
2464
- const margin = options.margin ?? 8;
2465
- const flip = options.flip ?? true;
2466
- const clamp = options.clamp ?? true;
2467
- let placement = options.placement;
2468
- let pos = placeRaw(anchorRect, popoverRect, placement, offset);
2469
- if (flip) {
2470
- const overflowsTop = pos.top < margin;
2471
- const overflowsBottom = pos.top + popoverRect.height > viewport.height - margin;
2472
- const overflowsLeft = pos.left < margin;
2473
- const overflowsRight = pos.left + popoverRect.width > viewport.width - margin;
2474
- const s = side(placement);
2475
- const shouldFlip = (s === 'top' && overflowsTop) ||
2476
- (s === 'bottom' && overflowsBottom) ||
2477
- (s === 'left' && overflowsLeft) ||
2478
- (s === 'right' && overflowsRight);
2479
- if (shouldFlip) {
2480
- const flipped = flipPlacement(placement);
2481
- const flippedPos = placeRaw(anchorRect, popoverRect, flipped, offset);
2482
- const flippedFitsBetter = (s === 'top' &&
2483
- flippedPos.top + popoverRect.height <= viewport.height - margin) ||
2484
- (s === 'bottom' && flippedPos.top >= margin) ||
2485
- (s === 'left' &&
2486
- flippedPos.left + popoverRect.width <= viewport.width - margin) ||
2487
- (s === 'right' && flippedPos.left >= margin);
2488
- if (flippedFitsBetter) {
2489
- placement = flipped;
2490
- pos = flippedPos;
2491
- }
2492
- }
2493
- }
2494
- if (clamp) {
2495
- // Only clamp the cross-axis (perpendicular to the placement). For
2496
- // bottom-side placements we clamp `left` so the popover doesn't slip off
2497
- // the left/right of the viewport, but we leave `top` alone; clamping it
2498
- // when the popover is taller than the available space below the anchor
2499
- // would yank it up over the anchor itself (the common Storybook-docs-
2500
- // iframe failure mode where viewports are short). Better to let it
2501
- // overflow and scroll than to overlap its own trigger.
2502
- const s = side(placement);
2503
- const isVertical = s === 'top' || s === 'bottom';
2504
- if (isVertical) {
2505
- const maxLeft = viewport.width - popoverRect.width - margin;
2506
- pos = {
2507
- top: pos.top,
2508
- left: Math.max(margin, Math.min(pos.left, Math.max(margin, maxLeft))),
2509
- };
2510
- }
2511
- else {
2512
- const maxTop = viewport.height - popoverRect.height - margin;
2513
- pos = {
2514
- top: Math.max(margin, Math.min(pos.top, Math.max(margin, maxTop))),
2515
- left: pos.left,
2516
- };
2517
- }
2518
- }
2519
- return {
2520
- top: pos.top,
2521
- left: pos.left,
2522
- placement,
2523
- ...(options.matchAnchorWidth ? { width: anchorRect.width } : {}),
2524
- };
2788
+ }, ...(ngDevMode ? [{ debugName: "hostStyles" }] : /* istanbul ignore next */ []));
2789
+ hostClasses = computed(() => ({
2790
+ [`ea-skeleton--${this.variant()}`]: true,
2791
+ 'ea-skeleton--animated': this.animated(),
2792
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
2793
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2794
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.16", type: SkeletonComponent, isStandalone: true, selector: "ea-skeleton", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"ea-skeleton\"\n [class.ea-skeleton--text]=\"variant() === 'text'\"\n [class.ea-skeleton--circle]=\"variant() === 'circle'\"\n [class.ea-skeleton--rect]=\"variant() === 'rect'\"\n [class.ea-skeleton--animated]=\"animated()\"\n [style]=\"hostStyles()\"\n aria-hidden=\"true\">\n</div>\n", styles: [".ea-skeleton{display:block;background-color:var(--color-bg-muted);border-radius:var(--radius-md)}.ea-skeleton--text{width:100%;height:1rem;border-radius:var(--radius-sm)}.ea-skeleton--circle{width:2.5rem;height:2.5rem;border-radius:var(--radius-full)}.ea-skeleton--rect{width:100%;height:6rem}.ea-skeleton--animated{animation:ea-skeleton-pulse 1.5s ease-in-out infinite}@keyframes ea-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}@media(prefers-reduced-motion:reduce){.ea-skeleton--animated{animation:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2525
2795
  }
2796
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SkeletonComponent, decorators: [{
2797
+ type: Component,
2798
+ args: [{ selector: 'ea-skeleton', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"ea-skeleton\"\n [class.ea-skeleton--text]=\"variant() === 'text'\"\n [class.ea-skeleton--circle]=\"variant() === 'circle'\"\n [class.ea-skeleton--rect]=\"variant() === 'rect'\"\n [class.ea-skeleton--animated]=\"animated()\"\n [style]=\"hostStyles()\"\n aria-hidden=\"true\">\n</div>\n", styles: [".ea-skeleton{display:block;background-color:var(--color-bg-muted);border-radius:var(--radius-md)}.ea-skeleton--text{width:100%;height:1rem;border-radius:var(--radius-sm)}.ea-skeleton--circle{width:2.5rem;height:2.5rem;border-radius:var(--radius-full)}.ea-skeleton--rect{width:100%;height:6rem}.ea-skeleton--animated{animation:ea-skeleton-pulse 1.5s ease-in-out infinite}@keyframes ea-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}@media(prefers-reduced-motion:reduce){.ea-skeleton--animated{animation:none}}\n"] }]
2799
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], animated: [{ type: i0.Input, args: [{ isSignal: true, alias: "animated", required: false }] }] } });
2526
2800
 
2527
2801
  /**
2528
2802
  * Attaches a positioned tooltip to its host element. Shows on hover and
@@ -3341,401 +3615,141 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
3341
3615
  SkeletonComponent,
3342
3616
  TooltipDirective,
3343
3617
  TrashIconComponent,
3344
- UploadIconComponent,
3345
- ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div\n class=\"ea-avatar-editor\"\n [ngClass]=\"hostClasses()\">\n <input\n #fileInputEl\n type=\"file\"\n class=\"ea-avatar-editor__file-input\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.upload\"\n [accept]=\"accept()\"\n (change)=\"onFileSelected($event)\" />\n\n @if (!hasImage() && !isLoading()) {\n <button\n type=\"button\"\n class=\"ea-avatar-editor__dropzone\"\n [style.width.px]=\"canvasSize()\"\n [style.height.px]=\"canvasSize()\"\n (click)=\"openFilePicker()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <ea-icon-upload class=\"ea-avatar-editor__upload-icon\" />\n <span class=\"ea-avatar-editor__dropzone-text\">\n {{ i18n.messages().avatarEditor.dropzone }}\n </span>\n </button>\n }\n\n @if (hasImage() || isLoading()) {\n <div\n class=\"ea-avatar-editor__canvas-wrapper\"\n [style.width.px]=\"canvasSize()\"\n [style.height.px]=\"canvasSize()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n @if (isLoading()) {\n <ea-skeleton\n [variant]=\"shape() === 'circle' ? 'circle' : 'rect'\"\n [width]=\"canvasSize() + 'px'\"\n [height]=\"canvasSize() + 'px'\" />\n }\n\n @if (hasImage()) {\n <canvas\n #canvasEl\n class=\"ea-avatar-editor__canvas\"\n role=\"img\"\n tabindex=\"0\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.canvas\"\n [style.display]=\"isLoading() ? 'none' : 'block'\"\n [width]=\"canvasSize()\"\n [height]=\"canvasSize()\"\n (mousedown)=\"onMouseDown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (keydown)=\"onCanvasKeydown($event)\"></canvas>\n\n @if (!isLoading()) {\n <div\n class=\"ea-avatar-editor__canvas-overlay\"\n [class.ea-avatar-editor__canvas-overlay--on-light]=\"!isImageDark()\">\n <ea-icon-camera class=\"ea-avatar-editor__overlay-icon\" />\n <span>{{ i18n.messages().avatarEditor.change }}</span>\n </div>\n }\n }\n </div>\n }\n\n <div class=\"ea-avatar-editor__controls\">\n <button\n type=\"button\"\n class=\"ea-avatar-editor__icon-btn\"\n [eaTooltip]=\"i18n.messages().avatarEditor.revert\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.revert\"\n [disabled]=\"!canRevert() || isLoading()\"\n (click)=\"revertImage()\">\n <ea-icon-rotate-ccw />\n </button>\n\n <button\n type=\"button\"\n class=\"ea-avatar-editor__icon-btn\"\n [eaTooltip]=\"i18n.messages().avatarEditor.zoomOut\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.zoomOut\"\n [disabled]=\"!hasImage() || isLoading() || zoom() <= minZoom()\"\n (click)=\"setZoom(zoom() - 0.1)\">\n <ea-icon-minus />\n </button>\n\n <input\n type=\"range\"\n class=\"ea-avatar-editor__zoom-slider\"\n [min]=\"minZoom()\"\n [max]=\"maxZoom()\"\n step=\"0.01\"\n [value]=\"zoom()\"\n [disabled]=\"!hasImage() || isLoading()\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.zoom\"\n (input)=\"onZoomInput($event)\" />\n\n <button\n type=\"button\"\n class=\"ea-avatar-editor__icon-btn\"\n [eaTooltip]=\"i18n.messages().avatarEditor.zoomIn\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.zoomIn\"\n [disabled]=\"!hasImage() || isLoading() || zoom() >= maxZoom()\"\n (click)=\"setZoom(zoom() + 0.1)\">\n <ea-icon-plus />\n </button>\n\n <button\n type=\"button\"\n class=\"ea-avatar-editor__icon-btn ea-avatar-editor__icon-btn--danger\"\n [eaTooltip]=\"i18n.messages().avatarEditor.remove\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.remove\"\n [disabled]=\"!hasImage() || isLoading()\"\n (click)=\"removeImage()\">\n <ea-icon-trash />\n </button>\n </div>\n</div>\n", styles: [".ea-avatar-editor{display:inline-flex;flex-direction:column;align-items:center;gap:var(--space-3);font-family:var(--font-family-sans)}.ea-avatar-editor__file-input{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;margin:-1px;border:0;white-space:nowrap;clip:rect(0,0,0,0);clip-path:inset(50%)}.ea-avatar-editor__dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2);padding:var(--space-4);font-family:inherit;font-size:var(--font-size-sm);line-height:var(--line-height-normal);border:2px dashed var(--color-border-default);border-radius:var(--radius-lg);background-color:var(--color-bg-subtle);color:var(--color-text-tertiary);cursor:pointer;transition:var(--transition-colors)}.ea-avatar-editor__dropzone:hover{border-color:var(--color-border-focus);background-color:var(--color-state-hover);color:var(--color-text-secondary)}.ea-avatar-editor__dropzone:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-avatar-editor--drag-over .ea-avatar-editor__dropzone{border-color:var(--color-primary-500);background-color:var(--color-primary-50);color:var(--color-primary-600)}.ea-avatar-editor--circle .ea-avatar-editor__dropzone{border-radius:var(--radius-full)}.ea-avatar-editor__upload-icon{width:32px;height:32px;opacity:.6}.ea-avatar-editor__dropzone-text{text-align:center}.ea-avatar-editor--compact .ea-avatar-editor__dropzone{gap:var(--space-1-5);padding:var(--space-3);font-size:.75rem;line-height:1.35}.ea-avatar-editor--compact .ea-avatar-editor__upload-icon{width:24px;height:24px}.ea-avatar-editor--ultra-compact .ea-avatar-editor__dropzone{gap:var(--space-1);padding:var(--space-2);font-size:.625rem;line-height:1.2}.ea-avatar-editor--ultra-compact .ea-avatar-editor__upload-icon{width:16px;height:16px}.ea-avatar-editor__canvas-wrapper{position:relative;overflow:hidden;border-radius:var(--radius-lg)}.ea-avatar-editor--circle .ea-avatar-editor__canvas-wrapper{border-radius:var(--radius-full)}.ea-avatar-editor__canvas{display:block;cursor:grab}.ea-avatar-editor__canvas:active{cursor:grabbing}.ea-avatar-editor__canvas-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-1);font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-neutral-0);opacity:0;pointer-events:none;transition:opacity var(--duration-fast) var(--easing-default),color var(--duration-fast) var(--easing-default)}.ea-avatar-editor__canvas-overlay--on-light{color:var(--color-neutral-950)}.ea-avatar-editor__overlay-icon{width:24px;height:24px}.ea-avatar-editor__canvas-wrapper:hover .ea-avatar-editor__canvas-overlay{opacity:1}.ea-avatar-editor__controls{display:flex;align-items:center;gap:var(--space-2)}.ea-avatar-editor__icon-btn{display:flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0}.ea-avatar-editor__icon-btn ea-icon-rotate-ccw,.ea-avatar-editor__icon-btn ea-icon-minus,.ea-avatar-editor__icon-btn ea-icon-plus,.ea-avatar-editor__icon-btn ea-icon-trash{width:1rem;height:1rem}.ea-avatar-editor__icon-btn{border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);background-color:var(--color-bg-base);color:var(--color-text-primary);cursor:pointer;transition:var(--transition-colors)}.ea-avatar-editor__icon-btn:hover:not(:disabled){border-color:var(--color-border-focus)}.ea-avatar-editor__icon-btn:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-avatar-editor__icon-btn:disabled{opacity:.4;cursor:not-allowed}.ea-avatar-editor__icon-btn--danger{color:var(--color-error-default)}.ea-avatar-editor__icon-btn--danger:hover:not(:disabled){border-color:var(--color-error-default)}.ea-avatar-editor__zoom-slider{width:7rem;height:4px;border-radius:var(--radius-full);background:var(--color-neutral-200);appearance:none;cursor:pointer}.ea-avatar-editor__zoom-slider::-webkit-slider-thumb{width:1rem;height:1rem;border:2px solid var(--color-primary-500);border-radius:var(--radius-full);background:var(--color-bg-base);appearance:none;cursor:grab}.ea-avatar-editor__zoom-slider::-webkit-slider-thumb:active{cursor:grabbing}.ea-avatar-editor__zoom-slider::-moz-range-thumb{width:1rem;height:1rem;border:2px solid var(--color-primary-500);border-radius:var(--radius-full);background:var(--color-bg-base);cursor:grab}.ea-avatar-editor__zoom-slider::-moz-range-thumb:active{cursor:grabbing}.ea-avatar-editor__zoom-slider:focus-visible{outline:none}.ea-avatar-editor__zoom-slider:focus-visible::-webkit-slider-thumb{box-shadow:var(--shadow-focus-ring)}.ea-avatar-editor__zoom-slider:disabled{opacity:.4;cursor:not-allowed}\n"] }]
3346
- }], ctorParameters: () => [], propDecorators: { canvasEl: [{ type: i0.ViewChild, args: ['canvasEl', { isSignal: true }] }], fileInputEl: [{ type: i0.ViewChild, args: ['fileInputEl', { isSignal: true }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], canvasSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "canvasSize", required: false }] }], currentSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentSrc", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], minZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "minZoom", required: false }] }], maxZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxZoom", required: false }] }], exportQuality: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportQuality", required: false }] }], exportType: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportType", required: false }] }], cropState: [{ type: i0.Input, args: [{ isSignal: true, alias: "cropState", required: false }] }], cropped: [{ type: i0.Output, args: ["cropped"] }], fileSelected: [{ type: i0.Output, args: ["fileSelected"] }], removed: [{ type: i0.Output, args: ["removed"] }], errored: [{ type: i0.Output, args: ["errored"] }], cropStateChanged: [{ type: i0.Output, args: ["cropStateChanged"] }] } });
3347
-
3348
- class UserIconComponent extends IconComponentBase {
3349
- static slug = 'user';
3350
- static category = 'feather';
3351
- static tags = [
3352
- 'user',
3353
- 'person',
3354
- 'profile',
3355
- 'account',
3356
- 'avatar',
3357
- 'utilisateur',
3358
- 'personne',
3359
- 'usuario',
3360
- 'persona',
3361
- 'χρήστης',
3362
- 'άτομο',
3363
- 'użytkownik',
3364
- 'osoba',
3365
- ];
3366
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: UserIconComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3367
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: UserIconComponent, isStandalone: true, selector: "ea-icon-user", usesInheritance: true, ngImport: i0, template: `
3368
- <svg
3369
- viewBox="0 0 24 24"
3370
- fill="none"
3371
- stroke="currentColor"
3372
- [attr.stroke-width]="strokeWidth()"
3373
- stroke-linecap="round"
3374
- stroke-linejoin="round"
3375
- aria-hidden="true"
3376
- width="100%"
3377
- height="100%">
3378
- <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
3379
- <circle
3380
- cx="12"
3381
- cy="7"
3382
- r="4" />
3383
- </svg>
3384
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3385
- }
3386
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: UserIconComponent, decorators: [{
3387
- type: Component,
3388
- args: [{
3389
- selector: 'ea-icon-user',
3390
- changeDetection: ChangeDetectionStrategy.OnPush,
3391
- template: `
3392
- <svg
3393
- viewBox="0 0 24 24"
3394
- fill="none"
3395
- stroke="currentColor"
3396
- [attr.stroke-width]="strokeWidth()"
3397
- stroke-linecap="round"
3398
- stroke-linejoin="round"
3399
- aria-hidden="true"
3400
- width="100%"
3401
- height="100%">
3402
- <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
3403
- <circle
3404
- cx="12"
3405
- cy="7"
3406
- r="4" />
3407
- </svg>
3408
- `,
3409
- }]
3410
- }] });
3411
-
3412
- /**
3413
- * Compact image used to represent a user or entity. Falls back to initials
3414
- * when no `src` is provided, then to a generic user icon when neither image
3415
- * nor initials are available.
3416
- */
3417
- class AvatarComponent {
3418
- src = input(undefined, ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
3419
- alt = input('', ...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
3420
- initials = input(undefined, ...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
3421
- size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3422
- shape = input('circle', ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
3423
- hostClasses = computed(() => ({
3424
- [`ea-avatar--${this.size()}`]: true,
3425
- [`ea-avatar--${this.shape()}`]: true,
3426
- }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3427
- showImage = computed(() => !!this.src(), ...(ngDevMode ? [{ debugName: "showImage" }] : /* istanbul ignore next */ []));
3428
- showInitials = computed(() => !this.src() && !!this.initials(), ...(ngDevMode ? [{ debugName: "showInitials" }] : /* istanbul ignore next */ []));
3429
- showFallback = computed(() => !this.src() && !this.initials(), ...(ngDevMode ? [{ debugName: "showFallback" }] : /* istanbul ignore next */ []));
3430
- handleImageError(event) {
3431
- event.target.style.display = 'none';
3432
- }
3433
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3434
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: AvatarComponent, isStandalone: true, selector: "ea-avatar", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display: inline-block; line-height: 0;" }, ngImport: i0, template: "<div\n class=\"ea-avatar\"\n [ngClass]=\"hostClasses()\"\n [attr.aria-label]=\"alt() || initials() || null\"\n role=\"img\">\n @if (showImage()) {\n <img\n class=\"ea-avatar__image\"\n [src]=\"src()\"\n [alt]=\"alt()\"\n (error)=\"handleImageError($event)\" />\n }\n @if (showInitials()) {\n <span class=\"ea-avatar__initials\">{{ initials() }}</span>\n }\n @if (showFallback()) {\n <ea-icon-user class=\"ea-avatar__fallback\" />\n }\n</div>\n", styles: [".ea-avatar{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;width:var(--ea-avatar-size, 2.5rem);height:var(--ea-avatar-size, 2.5rem);background-color:var(--color-bg-emphasis);color:var(--color-text-secondary);font-family:var(--font-family-sans);font-weight:var(--font-weight-medium)}.ea-avatar--xs{--ea-avatar-size: 1.5rem}.ea-avatar--sm{--ea-avatar-size: 2rem}.ea-avatar--md{--ea-avatar-size: 2.5rem}.ea-avatar--lg{--ea-avatar-size: 3rem}.ea-avatar--xl{--ea-avatar-size: 4rem}.ea-avatar--circle{border-radius:var(--radius-full)}.ea-avatar--square{border-radius:var(--radius-md)}.ea-avatar__image{width:100%;height:100%;object-fit:cover}.ea-avatar__initials{font-size:calc(var(--ea-avatar-size, 2.5rem) * .4);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide);line-height:var(--line-height-none)}.ea-avatar__fallback{font-size:calc(var(--ea-avatar-size, 2.5rem) * .6)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: UserIconComponent, selector: "ea-icon-user" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3435
- }
3436
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AvatarComponent, decorators: [{
3437
- type: Component,
3438
- args: [{ selector: 'ea-avatar', imports: [NgClass, UserIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { style: 'display: inline-block; line-height: 0;' }, template: "<div\n class=\"ea-avatar\"\n [ngClass]=\"hostClasses()\"\n [attr.aria-label]=\"alt() || initials() || null\"\n role=\"img\">\n @if (showImage()) {\n <img\n class=\"ea-avatar__image\"\n [src]=\"src()\"\n [alt]=\"alt()\"\n (error)=\"handleImageError($event)\" />\n }\n @if (showInitials()) {\n <span class=\"ea-avatar__initials\">{{ initials() }}</span>\n }\n @if (showFallback()) {\n <ea-icon-user class=\"ea-avatar__fallback\" />\n }\n</div>\n", styles: [".ea-avatar{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;width:var(--ea-avatar-size, 2.5rem);height:var(--ea-avatar-size, 2.5rem);background-color:var(--color-bg-emphasis);color:var(--color-text-secondary);font-family:var(--font-family-sans);font-weight:var(--font-weight-medium)}.ea-avatar--xs{--ea-avatar-size: 1.5rem}.ea-avatar--sm{--ea-avatar-size: 2rem}.ea-avatar--md{--ea-avatar-size: 2.5rem}.ea-avatar--lg{--ea-avatar-size: 3rem}.ea-avatar--xl{--ea-avatar-size: 4rem}.ea-avatar--circle{border-radius:var(--radius-full)}.ea-avatar--square{border-radius:var(--radius-md)}.ea-avatar__image{width:100%;height:100%;object-fit:cover}.ea-avatar__initials{font-size:calc(var(--ea-avatar-size, 2.5rem) * .4);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide);line-height:var(--line-height-none)}.ea-avatar__fallback{font-size:calc(var(--ea-avatar-size, 2.5rem) * .6)}\n"] }]
3439
- }], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }] } });
3440
-
3441
- /**
3442
- * Compact indicator used to communicate status, counts, or labels inline with
3443
- * surrounding content.
3444
- */
3445
- class BadgeComponent {
3446
- variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3447
- size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3448
- shape = input('pill', ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
3449
- hostClasses = computed(() => ({
3450
- [`ea-badge--${this.variant()}`]: true,
3451
- [`ea-badge--${this.size()}`]: true,
3452
- [`ea-badge--${this.shape()}`]: true,
3453
- }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3454
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3455
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.16", type: BadgeComponent, isStandalone: true, selector: "ea-badge", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<span\n class=\"ea-badge\"\n [ngClass]=\"hostClasses()\">\n <ng-content />\n</span>\n", styles: [".ea-badge{display:inline-flex;align-items:center;gap:.25em;padding:.25em .625em;white-space:nowrap;font-family:var(--font-family-sans);font-weight:var(--font-weight-medium);line-height:var(--line-height-none);border-style:solid;border-width:var(--ea-badge-border-width, 0);border-color:var(--ea-badge-border-color, transparent);border-radius:var(--radius-full)}.ea-badge--xs{font-size:var(--ea-badge-font-size, var(--font-size-2xs))}.ea-badge--sm{font-size:var(--ea-badge-font-size, var(--font-size-xs))}.ea-badge--md{font-size:var(--ea-badge-font-size, var(--font-size-sm))}.ea-badge--lg{font-size:var(--ea-badge-font-size, var(--font-size-md))}.ea-badge--xl{font-size:var(--ea-badge-font-size, var(--font-size-lg))}.ea-badge--default{background-color:var(--ea-badge-background-color, var(--color-bg-muted));color:var(--ea-badge-color, var(--color-text-secondary))}.ea-badge--success{background-color:var(--ea-badge-background-color, var(--color-success-subtle));color:var(--ea-badge-color, var(--color-success-text))}.ea-badge--warning{background-color:var(--ea-badge-background-color, var(--color-warning-subtle));color:var(--ea-badge-color, var(--color-warning-text))}.ea-badge--error{background-color:var(--ea-badge-background-color, var(--color-error-subtle));color:var(--ea-badge-color, var(--color-error-text))}.ea-badge--info{background-color:var(--ea-badge-background-color, var(--color-info-subtle));color:var(--ea-badge-color, var(--color-info-text))}.ea-badge--pin{justify-content:center;box-sizing:border-box;min-width:var(--ea-badge-size, 1.8em);height:var(--ea-badge-size, 1.8em);padding:0 var(--space-1);font-variant-numeric:tabular-nums}.ea-badge--pin.ea-badge--sm{font-size:var(--ea-badge-font-size, 9px)}.ea-badge--pin.ea-badge--md{font-size:var(--ea-badge-font-size, 10px)}.ea-badge--pin.ea-badge--lg{font-size:var(--ea-badge-font-size, 12px)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3456
- }
3457
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BadgeComponent, decorators: [{
3458
- type: Component,
3459
- args: [{ selector: 'ea-badge', imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<span\n class=\"ea-badge\"\n [ngClass]=\"hostClasses()\">\n <ng-content />\n</span>\n", styles: [".ea-badge{display:inline-flex;align-items:center;gap:.25em;padding:.25em .625em;white-space:nowrap;font-family:var(--font-family-sans);font-weight:var(--font-weight-medium);line-height:var(--line-height-none);border-style:solid;border-width:var(--ea-badge-border-width, 0);border-color:var(--ea-badge-border-color, transparent);border-radius:var(--radius-full)}.ea-badge--xs{font-size:var(--ea-badge-font-size, var(--font-size-2xs))}.ea-badge--sm{font-size:var(--ea-badge-font-size, var(--font-size-xs))}.ea-badge--md{font-size:var(--ea-badge-font-size, var(--font-size-sm))}.ea-badge--lg{font-size:var(--ea-badge-font-size, var(--font-size-md))}.ea-badge--xl{font-size:var(--ea-badge-font-size, var(--font-size-lg))}.ea-badge--default{background-color:var(--ea-badge-background-color, var(--color-bg-muted));color:var(--ea-badge-color, var(--color-text-secondary))}.ea-badge--success{background-color:var(--ea-badge-background-color, var(--color-success-subtle));color:var(--ea-badge-color, var(--color-success-text))}.ea-badge--warning{background-color:var(--ea-badge-background-color, var(--color-warning-subtle));color:var(--ea-badge-color, var(--color-warning-text))}.ea-badge--error{background-color:var(--ea-badge-background-color, var(--color-error-subtle));color:var(--ea-badge-color, var(--color-error-text))}.ea-badge--info{background-color:var(--ea-badge-background-color, var(--color-info-subtle));color:var(--ea-badge-color, var(--color-info-text))}.ea-badge--pin{justify-content:center;box-sizing:border-box;min-width:var(--ea-badge-size, 1.8em);height:var(--ea-badge-size, 1.8em);padding:0 var(--space-1);font-variant-numeric:tabular-nums}.ea-badge--pin.ea-badge--sm{font-size:var(--ea-badge-font-size, 9px)}.ea-badge--pin.ea-badge--md{font-size:var(--ea-badge-font-size, 10px)}.ea-badge--pin.ea-badge--lg{font-size:var(--ea-badge-font-size, 12px)}\n"] }]
3460
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }] } });
3461
-
3462
- class ChevronRightIconComponent extends IconComponentBase {
3463
- static slug = 'chevron-right';
3464
- static category = 'feather';
3465
- static tags = [
3466
- 'chevron-right',
3467
- 'chevron',
3468
- 'right',
3469
- 'arrow',
3470
- 'forward',
3471
- 'next',
3472
- 'droite',
3473
- 'derecha',
3474
- 'δεξιά',
3475
- 'prawo',
3476
- ];
3477
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ChevronRightIconComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3478
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: ChevronRightIconComponent, isStandalone: true, selector: "ea-icon-chevron-right", usesInheritance: true, ngImport: i0, template: `
3479
- <svg
3480
- viewBox="0 0 24 24"
3481
- fill="none"
3482
- stroke="currentColor"
3483
- [attr.stroke-width]="strokeWidth()"
3484
- stroke-linecap="round"
3485
- stroke-linejoin="round"
3486
- aria-hidden="true"
3487
- width="100%"
3488
- height="100%">
3489
- <polyline points="9 18 15 12 9 6" />
3490
- </svg>
3491
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3492
- }
3493
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ChevronRightIconComponent, decorators: [{
3494
- type: Component,
3495
- args: [{
3496
- selector: 'ea-icon-chevron-right',
3497
- changeDetection: ChangeDetectionStrategy.OnPush,
3498
- template: `
3499
- <svg
3500
- viewBox="0 0 24 24"
3501
- fill="none"
3502
- stroke="currentColor"
3503
- [attr.stroke-width]="strokeWidth()"
3504
- stroke-linecap="round"
3505
- stroke-linejoin="round"
3506
- aria-hidden="true"
3507
- width="100%"
3508
- height="100%">
3509
- <polyline points="9 18 15 12 9 6" />
3510
- </svg>
3511
- `,
3512
- }]
3513
- }] });
3514
-
3515
- /**
3516
- * Navigation trail that shows the user's location within a hierarchy. Items
3517
- * with an `href` render as links, others render as buttons; the final item is
3518
- * always treated as the current page and is non-interactive.
3519
- */
3520
- class BreadcrumbsComponent {
3521
- i18n = inject(EagamiI18nService);
3522
- items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
3523
- separator = input('chevron', ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
3524
- ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
3525
- /** Fires when a non-disabled, non-final breadcrumb is activated. */
3526
- clicked = output();
3527
- /** Accessible label for the breadcrumb nav, falling back to the active locale. */
3528
- resolvedAriaLabel = computed(() => this.ariaLabel() ?? this.i18n.messages().breadcrumbs.label, ...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
3529
- isLast(index) {
3530
- return index === this.items().length - 1;
3531
- }
3532
- handleClick(item, index, event) {
3533
- if (item.disabled || this.isLast(index)) {
3534
- event.preventDefault();
3535
- return;
3536
- }
3537
- this.clicked.emit({ item, index, event });
3538
- }
3539
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BreadcrumbsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3540
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: BreadcrumbsComponent, isStandalone: true, selector: "ea-breadcrumbs", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<nav\n class=\"ea-breadcrumbs\"\n [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"ea-breadcrumbs__list\">\n @for (item of items(); track $index; let i = $index; let last = $last) {\n <li class=\"ea-breadcrumbs__item\">\n @if (last) {\n <span\n class=\"ea-breadcrumbs__current\"\n aria-current=\"page\">\n {{ item.label }}\n </span>\n } @else if (item.disabled) {\n <span class=\"ea-breadcrumbs__link ea-breadcrumbs__link--disabled\">\n {{ item.label }}\n </span>\n } @else if (item.href) {\n <a\n class=\"ea-breadcrumbs__link\"\n [href]=\"item.href\"\n (click)=\"handleClick(item, i, $event)\">\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"ea-breadcrumbs__link ea-breadcrumbs__link--button\"\n (click)=\"handleClick(item, i, $event)\">\n {{ item.label }}\n </button>\n }\n\n @if (!last) {\n @if (separator() === 'chevron') {\n <ea-icon-chevron-right\n class=\"ea-breadcrumbs__separator\"\n aria-hidden=\"true\" />\n } @else {\n <span\n class=\"ea-breadcrumbs__separator ea-breadcrumbs__separator--slash\"\n aria-hidden=\"true\">\n /\n </span>\n }\n }\n </li>\n }\n </ol>\n</nav>\n", styles: [".ea-breadcrumbs{font-family:var(--font-family-sans);font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary)}.ea-breadcrumbs__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-1);padding:0;margin:0;list-style:none}.ea-breadcrumbs__item{display:inline-flex;align-items:center;gap:var(--space-1);min-width:0}.ea-breadcrumbs__link{padding:var(--space-0-5) var(--space-1);border:none;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-secondary);font-family:inherit;font-size:inherit;text-decoration:none;cursor:pointer;transition:var(--transition-colors)}.ea-breadcrumbs__link:hover:not(.ea-breadcrumbs__link--disabled){color:var(--color-text-primary);text-decoration:underline}.ea-breadcrumbs__link:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-breadcrumbs__link--disabled{color:var(--color-text-disabled);cursor:not-allowed}.ea-breadcrumbs__current{padding:var(--space-0-5) var(--space-1);font-weight:var(--font-weight-medium);color:var(--color-text-primary)}.ea-breadcrumbs__separator{display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;color:var(--color-text-tertiary);flex-shrink:0;-webkit-user-select:none;user-select:none}.ea-breadcrumbs__separator--slash{width:auto;height:auto;font-size:var(--font-size-sm)}\n"], dependencies: [{ kind: "component", type: ChevronRightIconComponent, selector: "ea-icon-chevron-right" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3541
- }
3542
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BreadcrumbsComponent, decorators: [{
3543
- type: Component,
3544
- args: [{ selector: 'ea-breadcrumbs', imports: [ChevronRightIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<nav\n class=\"ea-breadcrumbs\"\n [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"ea-breadcrumbs__list\">\n @for (item of items(); track $index; let i = $index; let last = $last) {\n <li class=\"ea-breadcrumbs__item\">\n @if (last) {\n <span\n class=\"ea-breadcrumbs__current\"\n aria-current=\"page\">\n {{ item.label }}\n </span>\n } @else if (item.disabled) {\n <span class=\"ea-breadcrumbs__link ea-breadcrumbs__link--disabled\">\n {{ item.label }}\n </span>\n } @else if (item.href) {\n <a\n class=\"ea-breadcrumbs__link\"\n [href]=\"item.href\"\n (click)=\"handleClick(item, i, $event)\">\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"ea-breadcrumbs__link ea-breadcrumbs__link--button\"\n (click)=\"handleClick(item, i, $event)\">\n {{ item.label }}\n </button>\n }\n\n @if (!last) {\n @if (separator() === 'chevron') {\n <ea-icon-chevron-right\n class=\"ea-breadcrumbs__separator\"\n aria-hidden=\"true\" />\n } @else {\n <span\n class=\"ea-breadcrumbs__separator ea-breadcrumbs__separator--slash\"\n aria-hidden=\"true\">\n /\n </span>\n }\n }\n </li>\n }\n </ol>\n</nav>\n", styles: [".ea-breadcrumbs{font-family:var(--font-family-sans);font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary)}.ea-breadcrumbs__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-1);padding:0;margin:0;list-style:none}.ea-breadcrumbs__item{display:inline-flex;align-items:center;gap:var(--space-1);min-width:0}.ea-breadcrumbs__link{padding:var(--space-0-5) var(--space-1);border:none;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-secondary);font-family:inherit;font-size:inherit;text-decoration:none;cursor:pointer;transition:var(--transition-colors)}.ea-breadcrumbs__link:hover:not(.ea-breadcrumbs__link--disabled){color:var(--color-text-primary);text-decoration:underline}.ea-breadcrumbs__link:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-breadcrumbs__link--disabled{color:var(--color-text-disabled);cursor:not-allowed}.ea-breadcrumbs__current{padding:var(--space-0-5) var(--space-1);font-weight:var(--font-weight-medium);color:var(--color-text-primary)}.ea-breadcrumbs__separator{display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;color:var(--color-text-tertiary);flex-shrink:0;-webkit-user-select:none;user-select:none}.ea-breadcrumbs__separator--slash{width:auto;height:auto;font-size:var(--font-size-sm)}\n"] }]
3545
- }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
3546
-
3547
- /**
3548
- * Standard action button supporting primary, secondary, ghost, and danger
3549
- * variants. Includes a loading state that swaps the label for a spinner while
3550
- * preserving the rendered width.
3551
- */
3552
- class ButtonComponent {
3553
- variant = input('primary', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3554
- size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3555
- type = input('button', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
3556
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3557
- loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
3558
- fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : /* istanbul ignore next */ []));
3559
- /** Optional icon component rendered to the left of the label. */
3560
- icon = input(undefined, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
3561
- ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
3562
- ariaCurrent = input(undefined, { ...(ngDevMode ? { debugName: "ariaCurrent" } : /* istanbul ignore next */ {}), alias: 'aria-current' });
3563
- /** Fires when the button is activated; suppressed while disabled or loading. */
3564
- clicked = output();
3565
- isDisabled = computed(() => this.disabled() || this.loading(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
3566
- hostClasses = computed(() => ({
3567
- [`ea-button--${this.variant()}`]: true,
3568
- [`ea-button--${this.size()}`]: true,
3569
- 'ea-button--full-width': this.fullWidth(),
3570
- 'ea-button--loading': this.loading(),
3571
- 'ea-button--disabled': this.isDisabled(),
3572
- }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3573
- handleClick(event) {
3574
- if (this.isDisabled()) {
3575
- event.preventDefault();
3576
- return;
3577
- }
3578
- this.clicked.emit(event);
3579
- }
3580
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3581
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: ButtonComponent, isStandalone: true, selector: "ea-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaCurrent: { classPropertyName: "ariaCurrent", publicName: "aria-current", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, host: { properties: { "class.ea-button--full-width": "fullWidth()" } }, ngImport: i0, template: "<button\n class=\"ea-button\"\n [ngClass]=\"hostClasses()\"\n [type]=\"type()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-current]=\"ariaCurrent() || null\"\n [attr.aria-busy]=\"loading() || null\"\n (click)=\"handleClick($event)\">\n @if (loading()) {\n <span\n class=\"ea-button__spinner\"\n aria-hidden=\"true\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"31.4\"\n stroke-dashoffset=\"10\" />\n </svg>\n </span>\n }\n\n <span\n class=\"ea-button__content\"\n [class.ea-button__content--hidden]=\"loading()\">\n <ng-content select=\"[slot=prefix]\" />\n @if (icon(); as iconComponent) {\n <span\n class=\"ea-button__icon\"\n aria-hidden=\"true\">\n <ng-container *ngComponentOutlet=\"iconComponent\" />\n </span>\n }\n <span class=\"ea-button__label\">\n <ng-content />\n </span>\n <ng-content select=\"[slot=suffix]\" />\n </span>\n</button>\n", styles: [":host{display:inline-flex}.ea-button{display:inline-flex;align-items:center;justify-content:center;gap:.5em;position:relative;white-space:nowrap;-webkit-user-select:none;user-select:none;padding:.5em 1em;min-height:2.5em;font-family:var(--font-family-sans);font-weight:var(--ea-button-font-weight, var(--font-weight-medium));letter-spacing:var(--letter-spacing-wide);text-decoration:none;line-height:var(--line-height-none);border-width:var(--border-width-thin);border-style:solid;border-radius:var(--radius-md);transition:var(--transition-colors),var(--transition-shadow);cursor:pointer}.ea-button:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-button--xs{font-size:var(--ea-button-font-size, var(--font-size-xs))}.ea-button--sm{font-size:var(--ea-button-font-size, var(--font-size-sm))}.ea-button--md{font-size:var(--ea-button-font-size, var(--font-size-md))}.ea-button--lg{font-size:var(--ea-button-font-size, var(--font-size-lg))}.ea-button--xl{font-size:var(--ea-button-font-size, var(--font-size-xl))}.ea-button--primary{background-color:var(--ea-button-background-color, var(--color-brand-default));border-color:var(--ea-button-border-color, var(--color-brand-default));color:var(--ea-button-color, var(--color-neutral-0))}.ea-button--primary:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-brand-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-brand-hover)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--primary:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-brand-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-brand-active)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--secondary{background-color:var(--ea-button-background-color, transparent);border-color:var(--ea-button-border-color, var(--color-border-strong));color:var(--ea-button-color, var(--color-text-primary))}.ea-button--secondary:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-state-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-neutral-500)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-text-primary)))}.ea-button--secondary:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-state-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-neutral-500)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-text-primary)))}.ea-button--ghost{background-color:var(--ea-button-background-color, transparent);border-color:var(--ea-button-border-color, transparent);color:var(--ea-button-color, var(--color-text-primary))}.ea-button--ghost:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-state-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, transparent));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-text-primary)))}.ea-button--ghost:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-state-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, transparent));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-text-primary)))}.ea-button--danger{background-color:var(--ea-button-background-color, var(--color-error-default));border-color:var(--ea-button-border-color, var(--color-error-default));color:var(--ea-button-color, var(--color-neutral-0))}.ea-button--danger:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-error-700)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-error-700)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--danger:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-error-700)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-error-700)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-neutral-0)));filter:brightness(.9)}.ea-button--full-width{width:100%}.ea-button--disabled,.ea-button:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.ea-button--loading{cursor:wait;pointer-events:none}.ea-button__content{display:inline-flex;align-items:center;gap:.5em}.ea-button__content--hidden{visibility:hidden}.ea-button__icon{display:inline-flex;align-items:center;flex-shrink:0;font-size:var(--icon-inline-size)}.ea-button__label{display:inline-flex;align-items:center}.ea-button__spinner{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.ea-button__spinner svg{width:1.1em;height:1.1em;animation:ea-spin .75s linear infinite}:host(.ea-button--full-width){display:block;width:100%}@keyframes ea-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3582
- }
3583
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ButtonComponent, decorators: [{
3584
- type: Component,
3585
- args: [{ selector: 'ea-button', imports: [NgClass, NgComponentOutlet], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3586
- '[class.ea-button--full-width]': 'fullWidth()',
3587
- }, template: "<button\n class=\"ea-button\"\n [ngClass]=\"hostClasses()\"\n [type]=\"type()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-current]=\"ariaCurrent() || null\"\n [attr.aria-busy]=\"loading() || null\"\n (click)=\"handleClick($event)\">\n @if (loading()) {\n <span\n class=\"ea-button__spinner\"\n aria-hidden=\"true\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"31.4\"\n stroke-dashoffset=\"10\" />\n </svg>\n </span>\n }\n\n <span\n class=\"ea-button__content\"\n [class.ea-button__content--hidden]=\"loading()\">\n <ng-content select=\"[slot=prefix]\" />\n @if (icon(); as iconComponent) {\n <span\n class=\"ea-button__icon\"\n aria-hidden=\"true\">\n <ng-container *ngComponentOutlet=\"iconComponent\" />\n </span>\n }\n <span class=\"ea-button__label\">\n <ng-content />\n </span>\n <ng-content select=\"[slot=suffix]\" />\n </span>\n</button>\n", styles: [":host{display:inline-flex}.ea-button{display:inline-flex;align-items:center;justify-content:center;gap:.5em;position:relative;white-space:nowrap;-webkit-user-select:none;user-select:none;padding:.5em 1em;min-height:2.5em;font-family:var(--font-family-sans);font-weight:var(--ea-button-font-weight, var(--font-weight-medium));letter-spacing:var(--letter-spacing-wide);text-decoration:none;line-height:var(--line-height-none);border-width:var(--border-width-thin);border-style:solid;border-radius:var(--radius-md);transition:var(--transition-colors),var(--transition-shadow);cursor:pointer}.ea-button:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-button--xs{font-size:var(--ea-button-font-size, var(--font-size-xs))}.ea-button--sm{font-size:var(--ea-button-font-size, var(--font-size-sm))}.ea-button--md{font-size:var(--ea-button-font-size, var(--font-size-md))}.ea-button--lg{font-size:var(--ea-button-font-size, var(--font-size-lg))}.ea-button--xl{font-size:var(--ea-button-font-size, var(--font-size-xl))}.ea-button--primary{background-color:var(--ea-button-background-color, var(--color-brand-default));border-color:var(--ea-button-border-color, var(--color-brand-default));color:var(--ea-button-color, var(--color-neutral-0))}.ea-button--primary:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-brand-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-brand-hover)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--primary:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-brand-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-brand-active)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--secondary{background-color:var(--ea-button-background-color, transparent);border-color:var(--ea-button-border-color, var(--color-border-strong));color:var(--ea-button-color, var(--color-text-primary))}.ea-button--secondary:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-state-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-neutral-500)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-text-primary)))}.ea-button--secondary:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-state-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-neutral-500)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-text-primary)))}.ea-button--ghost{background-color:var(--ea-button-background-color, transparent);border-color:var(--ea-button-border-color, transparent);color:var(--ea-button-color, var(--color-text-primary))}.ea-button--ghost:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-state-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, transparent));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-text-primary)))}.ea-button--ghost:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-state-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, transparent));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-text-primary)))}.ea-button--danger{background-color:var(--ea-button-background-color, var(--color-error-default));border-color:var(--ea-button-border-color, var(--color-error-default));color:var(--ea-button-color, var(--color-neutral-0))}.ea-button--danger:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-error-700)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-error-700)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--danger:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-error-700)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-error-700)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-neutral-0)));filter:brightness(.9)}.ea-button--full-width{width:100%}.ea-button--disabled,.ea-button:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.ea-button--loading{cursor:wait;pointer-events:none}.ea-button__content{display:inline-flex;align-items:center;gap:.5em}.ea-button__content--hidden{visibility:hidden}.ea-button__icon{display:inline-flex;align-items:center;flex-shrink:0;font-size:var(--icon-inline-size)}.ea-button__label{display:inline-flex;align-items:center}.ea-button__spinner{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.ea-button__spinner svg{width:1.1em;height:1.1em;animation:ea-spin .75s linear infinite}:host(.ea-button--full-width){display:block;width:100%}@keyframes ea-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
3588
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-current", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
3618
+ UploadIconComponent,
3619
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div\n class=\"ea-avatar-editor\"\n [ngClass]=\"hostClasses()\">\n <input\n #fileInputEl\n type=\"file\"\n class=\"ea-avatar-editor__file-input\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.upload\"\n [accept]=\"accept()\"\n (change)=\"onFileSelected($event)\" />\n\n @if (!hasImage() && !isLoading()) {\n <button\n type=\"button\"\n class=\"ea-avatar-editor__dropzone\"\n [style.width.px]=\"canvasSize()\"\n [style.height.px]=\"canvasSize()\"\n (click)=\"openFilePicker()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <ea-icon-upload class=\"ea-avatar-editor__upload-icon\" />\n <span class=\"ea-avatar-editor__dropzone-text\">\n {{ i18n.messages().avatarEditor.dropzone }}\n </span>\n </button>\n }\n\n @if (hasImage() || isLoading()) {\n <div\n class=\"ea-avatar-editor__canvas-wrapper\"\n [style.width.px]=\"canvasSize()\"\n [style.height.px]=\"canvasSize()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n @if (isLoading()) {\n <ea-skeleton\n [variant]=\"shape() === 'circle' ? 'circle' : 'rect'\"\n [width]=\"canvasSize() + 'px'\"\n [height]=\"canvasSize() + 'px'\" />\n }\n\n @if (hasImage()) {\n <canvas\n #canvasEl\n class=\"ea-avatar-editor__canvas\"\n role=\"img\"\n tabindex=\"0\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.canvas\"\n [style.display]=\"isLoading() ? 'none' : 'block'\"\n [width]=\"canvasSize()\"\n [height]=\"canvasSize()\"\n (mousedown)=\"onMouseDown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (keydown)=\"onCanvasKeydown($event)\"></canvas>\n\n @if (!isLoading()) {\n <div\n class=\"ea-avatar-editor__canvas-overlay\"\n [class.ea-avatar-editor__canvas-overlay--on-light]=\"!isImageDark()\">\n <ea-icon-camera class=\"ea-avatar-editor__overlay-icon\" />\n <span>{{ i18n.messages().avatarEditor.change }}</span>\n </div>\n }\n }\n </div>\n }\n\n <div class=\"ea-avatar-editor__controls\">\n <button\n type=\"button\"\n class=\"ea-avatar-editor__icon-btn\"\n [eaTooltip]=\"i18n.messages().avatarEditor.revert\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.revert\"\n [disabled]=\"!canRevert() || isLoading()\"\n (click)=\"revertImage()\">\n <ea-icon-rotate-ccw />\n </button>\n\n <button\n type=\"button\"\n class=\"ea-avatar-editor__icon-btn\"\n [eaTooltip]=\"i18n.messages().avatarEditor.zoomOut\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.zoomOut\"\n [disabled]=\"!hasImage() || isLoading() || zoom() <= minZoom()\"\n (click)=\"setZoom(zoom() - 0.1)\">\n <ea-icon-minus />\n </button>\n\n <input\n type=\"range\"\n class=\"ea-avatar-editor__zoom-slider\"\n [min]=\"minZoom()\"\n [max]=\"maxZoom()\"\n step=\"0.01\"\n [value]=\"zoom()\"\n [disabled]=\"!hasImage() || isLoading()\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.zoom\"\n (input)=\"onZoomInput($event)\" />\n\n <button\n type=\"button\"\n class=\"ea-avatar-editor__icon-btn\"\n [eaTooltip]=\"i18n.messages().avatarEditor.zoomIn\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.zoomIn\"\n [disabled]=\"!hasImage() || isLoading() || zoom() >= maxZoom()\"\n (click)=\"setZoom(zoom() + 0.1)\">\n <ea-icon-plus />\n </button>\n\n <button\n type=\"button\"\n class=\"ea-avatar-editor__icon-btn ea-avatar-editor__icon-btn--danger\"\n [eaTooltip]=\"i18n.messages().avatarEditor.remove\"\n [attr.aria-label]=\"i18n.messages().avatarEditor.remove\"\n [disabled]=\"!hasImage() || isLoading()\"\n (click)=\"removeImage()\">\n <ea-icon-trash />\n </button>\n </div>\n</div>\n", styles: [".ea-avatar-editor{display:inline-flex;flex-direction:column;align-items:center;gap:var(--space-3);font-family:var(--font-family-sans)}.ea-avatar-editor__file-input{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;margin:-1px;border:0;white-space:nowrap;clip:rect(0,0,0,0);clip-path:inset(50%)}.ea-avatar-editor__dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2);padding:var(--space-4);font-family:inherit;font-size:var(--font-size-sm);line-height:var(--line-height-normal);border:2px dashed var(--color-border-default);border-radius:var(--radius-lg);background-color:var(--color-bg-subtle);color:var(--color-text-tertiary);cursor:pointer;transition:var(--transition-colors)}.ea-avatar-editor__dropzone:hover{border-color:var(--color-border-focus);background-color:var(--color-state-hover);color:var(--color-text-secondary)}.ea-avatar-editor__dropzone:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-avatar-editor--drag-over .ea-avatar-editor__dropzone{border-color:var(--color-primary-500);background-color:var(--color-primary-50);color:var(--color-primary-600)}.ea-avatar-editor--circle .ea-avatar-editor__dropzone{border-radius:var(--radius-full)}.ea-avatar-editor__upload-icon{width:32px;height:32px;opacity:.6}.ea-avatar-editor__dropzone-text{text-align:center}.ea-avatar-editor--compact .ea-avatar-editor__dropzone{gap:var(--space-1-5);padding:var(--space-3);font-size:.75rem;line-height:1.35}.ea-avatar-editor--compact .ea-avatar-editor__upload-icon{width:24px;height:24px}.ea-avatar-editor--ultra-compact .ea-avatar-editor__dropzone{gap:var(--space-1);padding:var(--space-2);font-size:.625rem;line-height:1.2}.ea-avatar-editor--ultra-compact .ea-avatar-editor__upload-icon{width:16px;height:16px}.ea-avatar-editor__canvas-wrapper{position:relative;overflow:hidden;border-radius:var(--radius-lg)}.ea-avatar-editor--circle .ea-avatar-editor__canvas-wrapper{border-radius:var(--radius-full)}.ea-avatar-editor__canvas{display:block;cursor:grab}.ea-avatar-editor__canvas:active{cursor:grabbing}.ea-avatar-editor__canvas-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-1);font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--color-neutral-0);opacity:0;pointer-events:none;transition:opacity var(--duration-fast) var(--easing-default),color var(--duration-fast) var(--easing-default)}.ea-avatar-editor__canvas-overlay--on-light{color:var(--color-neutral-950)}.ea-avatar-editor__overlay-icon{width:24px;height:24px}.ea-avatar-editor__canvas-wrapper:hover .ea-avatar-editor__canvas-overlay{opacity:1}.ea-avatar-editor__controls{display:flex;align-items:center;gap:var(--space-2)}.ea-avatar-editor__icon-btn{display:flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0}.ea-avatar-editor__icon-btn ea-icon-rotate-ccw,.ea-avatar-editor__icon-btn ea-icon-minus,.ea-avatar-editor__icon-btn ea-icon-plus,.ea-avatar-editor__icon-btn ea-icon-trash{width:1rem;height:1rem}.ea-avatar-editor__icon-btn{border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-md);background-color:var(--color-bg-base);color:var(--color-text-primary);cursor:pointer;transition:var(--transition-colors)}.ea-avatar-editor__icon-btn:hover:not(:disabled){border-color:var(--color-border-focus)}.ea-avatar-editor__icon-btn:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-avatar-editor__icon-btn:disabled{opacity:.4;cursor:not-allowed}.ea-avatar-editor__icon-btn--danger{color:var(--color-error-default)}.ea-avatar-editor__icon-btn--danger:hover:not(:disabled){border-color:var(--color-error-default)}.ea-avatar-editor__zoom-slider{width:7rem;height:4px;border-radius:var(--radius-full);background:var(--color-neutral-200);appearance:none;cursor:pointer}.ea-avatar-editor__zoom-slider::-webkit-slider-thumb{width:1rem;height:1rem;border:2px solid var(--color-primary-500);border-radius:var(--radius-full);background:var(--color-bg-base);appearance:none;cursor:grab}.ea-avatar-editor__zoom-slider::-webkit-slider-thumb:active{cursor:grabbing}.ea-avatar-editor__zoom-slider::-moz-range-thumb{width:1rem;height:1rem;border:2px solid var(--color-primary-500);border-radius:var(--radius-full);background:var(--color-bg-base);cursor:grab}.ea-avatar-editor__zoom-slider::-moz-range-thumb:active{cursor:grabbing}.ea-avatar-editor__zoom-slider:focus-visible{outline:none}.ea-avatar-editor__zoom-slider:focus-visible::-webkit-slider-thumb{box-shadow:var(--shadow-focus-ring)}.ea-avatar-editor__zoom-slider:disabled{opacity:.4;cursor:not-allowed}\n"] }]
3620
+ }], ctorParameters: () => [], propDecorators: { canvasEl: [{ type: i0.ViewChild, args: ['canvasEl', { isSignal: true }] }], fileInputEl: [{ type: i0.ViewChild, args: ['fileInputEl', { isSignal: true }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], canvasSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "canvasSize", required: false }] }], currentSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentSrc", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], minZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "minZoom", required: false }] }], maxZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxZoom", required: false }] }], exportQuality: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportQuality", required: false }] }], exportType: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportType", required: false }] }], cropState: [{ type: i0.Input, args: [{ isSignal: true, alias: "cropState", required: false }] }], cropped: [{ type: i0.Output, args: ["cropped"] }], fileSelected: [{ type: i0.Output, args: ["fileSelected"] }], removed: [{ type: i0.Output, args: ["removed"] }], errored: [{ type: i0.Output, args: ["errored"] }], cropStateChanged: [{ type: i0.Output, args: ["cropStateChanged"] }] } });
3589
3621
 
3590
- /**
3591
- * Thin separator used to visually divide content. Renders horizontally by
3592
- * default and may include an optional centred label (e.g. "or").
3593
- */
3594
- class DividerComponent {
3595
- orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
3596
- label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
3597
- /** Renders a heavier rule. */
3598
- thick = input(false, ...(ngDevMode ? [{ debugName: "thick" }] : /* istanbul ignore next */ []));
3599
- hostClasses = computed(() => ({
3600
- [`ea-divider--${this.orientation()}`]: true,
3601
- 'ea-divider--with-label': !!this.label(),
3602
- 'ea-divider--thick': this.thick(),
3603
- }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3604
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: DividerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3605
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: DividerComponent, isStandalone: true, selector: "ea-divider", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, thick: { classPropertyName: "thick", publicName: "thick", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"ea-divider\"\n [ngClass]=\"hostClasses()\"\n [attr.role]=\"'separator'\"\n [attr.aria-orientation]=\"orientation()\">\n @if (label()) {\n <span class=\"ea-divider__label\">{{ label() }}</span>\n }\n</div>\n", styles: [".ea-divider{--ea-divider-thickness: 1px;flex-shrink:0;border:0;font-family:var(--font-family-sans)}.ea-divider--thick{--ea-divider-thickness: 2px}.ea-divider--horizontal{display:flex;align-items:center;width:100%;height:var(--ea-divider-thickness);background-color:var(--color-divider)}.ea-divider--vertical{display:inline-block;width:var(--ea-divider-thickness);height:1.5em;min-height:1rem;background-color:var(--color-divider)}.ea-divider--vertical.ea-divider--with-label{display:inline-flex;flex-direction:column;align-items:center;width:auto;height:auto;background-color:transparent;gap:var(--space-3)}.ea-divider--vertical.ea-divider--with-label:before,.ea-divider--vertical.ea-divider--with-label:after{content:\"\";flex:1;width:var(--ea-divider-thickness);height:auto;min-height:2em;background-color:var(--color-divider)}.ea-divider--with-label{height:auto;background-color:transparent;gap:var(--space-3)}.ea-divider--with-label:before,.ea-divider--with-label:after{content:\"\";flex:1;height:var(--ea-divider-thickness);background-color:var(--color-divider)}.ea-divider__label{flex-shrink:0;font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary);white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3622
+ class UserIconComponent extends IconComponentBase {
3623
+ static slug = 'user';
3624
+ static category = 'feather';
3625
+ static tags = [
3626
+ 'user',
3627
+ 'person',
3628
+ 'profile',
3629
+ 'account',
3630
+ 'avatar',
3631
+ 'utilisateur',
3632
+ 'personne',
3633
+ 'usuario',
3634
+ 'persona',
3635
+ 'χρήστης',
3636
+ 'άτομο',
3637
+ 'użytkownik',
3638
+ 'osoba',
3639
+ ];
3640
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: UserIconComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3641
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: UserIconComponent, isStandalone: true, selector: "ea-icon-user", usesInheritance: true, ngImport: i0, template: `
3642
+ <svg
3643
+ viewBox="0 0 24 24"
3644
+ fill="none"
3645
+ stroke="currentColor"
3646
+ [attr.stroke-width]="strokeWidth()"
3647
+ stroke-linecap="round"
3648
+ stroke-linejoin="round"
3649
+ aria-hidden="true"
3650
+ width="100%"
3651
+ height="100%">
3652
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
3653
+ <circle
3654
+ cx="12"
3655
+ cy="7"
3656
+ r="4" />
3657
+ </svg>
3658
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3606
3659
  }
3607
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: DividerComponent, decorators: [{
3660
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: UserIconComponent, decorators: [{
3608
3661
  type: Component,
3609
- args: [{ selector: 'ea-divider', imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div\n class=\"ea-divider\"\n [ngClass]=\"hostClasses()\"\n [attr.role]=\"'separator'\"\n [attr.aria-orientation]=\"orientation()\">\n @if (label()) {\n <span class=\"ea-divider__label\">{{ label() }}</span>\n }\n</div>\n", styles: [".ea-divider{--ea-divider-thickness: 1px;flex-shrink:0;border:0;font-family:var(--font-family-sans)}.ea-divider--thick{--ea-divider-thickness: 2px}.ea-divider--horizontal{display:flex;align-items:center;width:100%;height:var(--ea-divider-thickness);background-color:var(--color-divider)}.ea-divider--vertical{display:inline-block;width:var(--ea-divider-thickness);height:1.5em;min-height:1rem;background-color:var(--color-divider)}.ea-divider--vertical.ea-divider--with-label{display:inline-flex;flex-direction:column;align-items:center;width:auto;height:auto;background-color:transparent;gap:var(--space-3)}.ea-divider--vertical.ea-divider--with-label:before,.ea-divider--vertical.ea-divider--with-label:after{content:\"\";flex:1;width:var(--ea-divider-thickness);height:auto;min-height:2em;background-color:var(--color-divider)}.ea-divider--with-label{height:auto;background-color:transparent;gap:var(--space-3)}.ea-divider--with-label:before,.ea-divider--with-label:after{content:\"\";flex:1;height:var(--ea-divider-thickness);background-color:var(--color-divider)}.ea-divider__label{flex-shrink:0;font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary);white-space:nowrap}\n"] }]
3610
- }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], thick: [{ type: i0.Input, args: [{ isSignal: true, alias: "thick", required: false }] }] } });
3662
+ args: [{
3663
+ selector: 'ea-icon-user',
3664
+ changeDetection: ChangeDetectionStrategy.OnPush,
3665
+ template: `
3666
+ <svg
3667
+ viewBox="0 0 24 24"
3668
+ fill="none"
3669
+ stroke="currentColor"
3670
+ [attr.stroke-width]="strokeWidth()"
3671
+ stroke-linecap="round"
3672
+ stroke-linejoin="round"
3673
+ aria-hidden="true"
3674
+ width="100%"
3675
+ height="100%">
3676
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
3677
+ <circle
3678
+ cx="12"
3679
+ cy="7"
3680
+ r="4" />
3681
+ </svg>
3682
+ `,
3683
+ }]
3684
+ }] });
3611
3685
 
3612
3686
  /**
3613
- * Surface for grouping related content. Provides optional `header` and
3614
- * `footer` content slots and supports elevated, outlined, and filled
3615
- * variants. The card shadow can be customised per instance via the
3616
- * `--ea-card-shadow` CSS custom property.
3687
+ * Compact image used to represent a user or entity. Falls back to initials
3688
+ * when no `src` is provided, then to a generic user icon when neither image
3689
+ * nor initials are available.
3617
3690
  */
3618
- class CardComponent {
3619
- variant = input('elevated', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3620
- padding = input('md', ...(ngDevMode ? [{ debugName: "padding" }] : /* istanbul ignore next */ []));
3621
- fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : /* istanbul ignore next */ []));
3622
- headerAlign = input('center', ...(ngDevMode ? [{ debugName: "headerAlign" }] : /* istanbul ignore next */ []));
3623
- headerDivider = input(false, ...(ngDevMode ? [{ debugName: "headerDivider" }] : /* istanbul ignore next */ []));
3691
+ class AvatarComponent {
3692
+ src = input(undefined, ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
3693
+ alt = input('', ...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
3694
+ initials = input(undefined, ...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
3695
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3696
+ shape = input('circle', ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
3624
3697
  hostClasses = computed(() => ({
3625
- [`ea-card--${this.variant()}`]: true,
3626
- [`ea-card--padding-${this.padding()}`]: true,
3627
- 'ea-card--full-width': this.fullWidth(),
3698
+ [`ea-avatar--${this.size()}`]: true,
3699
+ [`ea-avatar--${this.shape()}`]: true,
3628
3700
  }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3629
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3630
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: CardComponent, isStandalone: true, selector: "ea-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, headerAlign: { classPropertyName: "headerAlign", publicName: "headerAlign", isSignal: true, isRequired: false, transformFunction: null }, headerDivider: { classPropertyName: "headerDivider", publicName: "headerDivider", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"ea-card\"\n [ngClass]=\"hostClasses()\">\n <div\n class=\"ea-card__header\"\n [style.text-align]=\"headerAlign()\">\n <ng-content select=\"[slot=header]\" />\n </div>\n\n @if (headerDivider()) {\n <ea-divider class=\"ea-card__divider\" />\n }\n\n <div class=\"ea-card__body\">\n <ng-content />\n </div>\n\n <div class=\"ea-card__footer\">\n <ng-content select=\"[slot=footer]\" />\n </div>\n</div>\n", styles: ["ea-card{display:block;min-width:0;max-width:100%;min-height:0}.ea-card{display:flex;flex-direction:column;min-width:0;max-width:100%;min-height:0;border-radius:var(--radius-lg);font-family:var(--font-family-sans);color:var(--color-text-primary);overflow:hidden}.ea-card--elevated{background-color:var(--color-bg-elevated);border:var(--border-width-thin) solid var(--color-border-default);box-shadow:var(--ea-card-shadow, var(--shadow-md))}.ea-card--outlined{background-color:var(--color-bg-base);border:var(--border-width-thin) solid var(--color-border-default)}.ea-card--filled{background-color:var(--color-bg-subtle)}.ea-card--padding-none .ea-card__header{padding:var(--ea-card-header-padding, 0)}.ea-card--padding-none .ea-card__body{padding:var(--ea-card-body-padding, 0)}.ea-card--padding-none .ea-card__footer{padding:var(--ea-card-footer-padding, 0)}.ea-card--padding-sm .ea-card__header{padding:var(--ea-card-header-padding, var(--space-3) var(--space-3) 0)}.ea-card--padding-sm .ea-card__body{padding:var(--ea-card-body-padding, var(--space-3))}.ea-card--padding-sm .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-3) var(--space-3))}.ea-card--padding-md .ea-card__header{padding:var(--ea-card-header-padding, var(--space-4) var(--space-4) 0)}.ea-card--padding-md .ea-card__body{padding:var(--ea-card-body-padding, var(--space-4))}.ea-card--padding-md .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-4) var(--space-4))}.ea-card--padding-lg .ea-card__header{padding:var(--ea-card-header-padding, var(--space-6) var(--space-6) 0)}.ea-card--padding-lg .ea-card__body{padding:var(--ea-card-body-padding, var(--space-6))}.ea-card--padding-lg .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-6) var(--space-6))}.ea-card--padding-xl .ea-card__header{padding:var(--ea-card-header-padding, var(--space-8) var(--space-8) 0)}.ea-card--padding-xl .ea-card__body{padding:var(--ea-card-body-padding, var(--space-8))}.ea-card--padding-xl .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-8) var(--space-8))}.ea-card--full-width{width:100%}.ea-card__divider{margin:var(--space-2) var(--space-4) 0}.ea-card__header:empty,.ea-card__footer:empty{display:none}.ea-card__header{font-size:var(--text-label-lg-size);font-weight:var(--text-label-lg-weight);line-height:var(--text-label-lg-lh)}.ea-card__body{flex:1;font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary)}.ea-card__footer{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}.ea-card__footer>*{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}\n"], dependencies: [{ kind: "component", type: DividerComponent, selector: "ea-divider", inputs: ["orientation", "label", "thick"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3701
+ showImage = computed(() => !!this.src(), ...(ngDevMode ? [{ debugName: "showImage" }] : /* istanbul ignore next */ []));
3702
+ showInitials = computed(() => !this.src() && !!this.initials(), ...(ngDevMode ? [{ debugName: "showInitials" }] : /* istanbul ignore next */ []));
3703
+ showFallback = computed(() => !this.src() && !this.initials(), ...(ngDevMode ? [{ debugName: "showFallback" }] : /* istanbul ignore next */ []));
3704
+ handleImageError(event) {
3705
+ event.target.style.display = 'none';
3706
+ }
3707
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3708
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: AvatarComponent, isStandalone: true, selector: "ea-avatar", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display: inline-block; line-height: 0;" }, ngImport: i0, template: "<div\n class=\"ea-avatar\"\n [ngClass]=\"hostClasses()\"\n [attr.aria-label]=\"alt() || initials() || null\"\n role=\"img\">\n @if (showImage()) {\n <img\n class=\"ea-avatar__image\"\n [src]=\"src()\"\n [alt]=\"alt()\"\n (error)=\"handleImageError($event)\" />\n }\n @if (showInitials()) {\n <span class=\"ea-avatar__initials\">{{ initials() }}</span>\n }\n @if (showFallback()) {\n <ea-icon-user class=\"ea-avatar__fallback\" />\n }\n</div>\n", styles: [".ea-avatar{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;width:var(--ea-avatar-size, 2.5rem);height:var(--ea-avatar-size, 2.5rem);background-color:var(--color-bg-emphasis);color:var(--color-text-secondary);font-family:var(--font-family-sans);font-weight:var(--font-weight-medium)}.ea-avatar--xs{--ea-avatar-size: 1.5rem}.ea-avatar--sm{--ea-avatar-size: 2rem}.ea-avatar--md{--ea-avatar-size: 2.5rem}.ea-avatar--lg{--ea-avatar-size: 3rem}.ea-avatar--xl{--ea-avatar-size: 4rem}.ea-avatar--circle{border-radius:var(--radius-full)}.ea-avatar--square{border-radius:var(--radius-md)}.ea-avatar__image{width:100%;height:100%;object-fit:cover}.ea-avatar__initials{font-size:calc(var(--ea-avatar-size, 2.5rem) * .4);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide);line-height:var(--line-height-none)}.ea-avatar__fallback{font-size:calc(var(--ea-avatar-size, 2.5rem) * .6)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: UserIconComponent, selector: "ea-icon-user" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3631
3709
  }
3632
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CardComponent, decorators: [{
3710
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AvatarComponent, decorators: [{
3633
3711
  type: Component,
3634
- args: [{ selector: 'ea-card', imports: [DividerComponent, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div\n class=\"ea-card\"\n [ngClass]=\"hostClasses()\">\n <div\n class=\"ea-card__header\"\n [style.text-align]=\"headerAlign()\">\n <ng-content select=\"[slot=header]\" />\n </div>\n\n @if (headerDivider()) {\n <ea-divider class=\"ea-card__divider\" />\n }\n\n <div class=\"ea-card__body\">\n <ng-content />\n </div>\n\n <div class=\"ea-card__footer\">\n <ng-content select=\"[slot=footer]\" />\n </div>\n</div>\n", styles: ["ea-card{display:block;min-width:0;max-width:100%;min-height:0}.ea-card{display:flex;flex-direction:column;min-width:0;max-width:100%;min-height:0;border-radius:var(--radius-lg);font-family:var(--font-family-sans);color:var(--color-text-primary);overflow:hidden}.ea-card--elevated{background-color:var(--color-bg-elevated);border:var(--border-width-thin) solid var(--color-border-default);box-shadow:var(--ea-card-shadow, var(--shadow-md))}.ea-card--outlined{background-color:var(--color-bg-base);border:var(--border-width-thin) solid var(--color-border-default)}.ea-card--filled{background-color:var(--color-bg-subtle)}.ea-card--padding-none .ea-card__header{padding:var(--ea-card-header-padding, 0)}.ea-card--padding-none .ea-card__body{padding:var(--ea-card-body-padding, 0)}.ea-card--padding-none .ea-card__footer{padding:var(--ea-card-footer-padding, 0)}.ea-card--padding-sm .ea-card__header{padding:var(--ea-card-header-padding, var(--space-3) var(--space-3) 0)}.ea-card--padding-sm .ea-card__body{padding:var(--ea-card-body-padding, var(--space-3))}.ea-card--padding-sm .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-3) var(--space-3))}.ea-card--padding-md .ea-card__header{padding:var(--ea-card-header-padding, var(--space-4) var(--space-4) 0)}.ea-card--padding-md .ea-card__body{padding:var(--ea-card-body-padding, var(--space-4))}.ea-card--padding-md .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-4) var(--space-4))}.ea-card--padding-lg .ea-card__header{padding:var(--ea-card-header-padding, var(--space-6) var(--space-6) 0)}.ea-card--padding-lg .ea-card__body{padding:var(--ea-card-body-padding, var(--space-6))}.ea-card--padding-lg .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-6) var(--space-6))}.ea-card--padding-xl .ea-card__header{padding:var(--ea-card-header-padding, var(--space-8) var(--space-8) 0)}.ea-card--padding-xl .ea-card__body{padding:var(--ea-card-body-padding, var(--space-8))}.ea-card--padding-xl .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-8) var(--space-8))}.ea-card--full-width{width:100%}.ea-card__divider{margin:var(--space-2) var(--space-4) 0}.ea-card__header:empty,.ea-card__footer:empty{display:none}.ea-card__header{font-size:var(--text-label-lg-size);font-weight:var(--text-label-lg-weight);line-height:var(--text-label-lg-lh)}.ea-card__body{flex:1;font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary)}.ea-card__footer{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}.ea-card__footer>*{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}\n"] }]
3635
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], headerAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerAlign", required: false }] }], headerDivider: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerDivider", required: false }] }] } });
3712
+ args: [{ selector: 'ea-avatar', imports: [NgClass, UserIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { style: 'display: inline-block; line-height: 0;' }, template: "<div\n class=\"ea-avatar\"\n [ngClass]=\"hostClasses()\"\n [attr.aria-label]=\"alt() || initials() || null\"\n role=\"img\">\n @if (showImage()) {\n <img\n class=\"ea-avatar__image\"\n [src]=\"src()\"\n [alt]=\"alt()\"\n (error)=\"handleImageError($event)\" />\n }\n @if (showInitials()) {\n <span class=\"ea-avatar__initials\">{{ initials() }}</span>\n }\n @if (showFallback()) {\n <ea-icon-user class=\"ea-avatar__fallback\" />\n }\n</div>\n", styles: [".ea-avatar{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;width:var(--ea-avatar-size, 2.5rem);height:var(--ea-avatar-size, 2.5rem);background-color:var(--color-bg-emphasis);color:var(--color-text-secondary);font-family:var(--font-family-sans);font-weight:var(--font-weight-medium)}.ea-avatar--xs{--ea-avatar-size: 1.5rem}.ea-avatar--sm{--ea-avatar-size: 2rem}.ea-avatar--md{--ea-avatar-size: 2.5rem}.ea-avatar--lg{--ea-avatar-size: 3rem}.ea-avatar--xl{--ea-avatar-size: 4rem}.ea-avatar--circle{border-radius:var(--radius-full)}.ea-avatar--square{border-radius:var(--radius-md)}.ea-avatar__image{width:100%;height:100%;object-fit:cover}.ea-avatar__initials{font-size:calc(var(--ea-avatar-size, 2.5rem) * .4);text-transform:uppercase;letter-spacing:var(--letter-spacing-wide);line-height:var(--line-height-none)}.ea-avatar__fallback{font-size:calc(var(--ea-avatar-size, 2.5rem) * .6)}\n"] }]
3713
+ }], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }] } });
3636
3714
 
3637
3715
  /**
3638
- * Boolean form control with support for an indeterminate visual state. Pairs
3639
- * a visually hidden native input with a custom checkmark and integrates with
3640
- * Angular forms via `ControlValueAccessor`.
3716
+ * Compact indicator used to communicate status, counts, or labels inline with
3717
+ * surrounding content.
3641
3718
  */
3642
- class CheckboxComponent {
3643
- label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
3644
- /**
3645
- * Optional supplementary value shown immediately after the label, dimmed
3646
- * to the tertiary text token. Renders inside the same `<span>` as the
3647
- * label so it shares the label's exact baseline and font metrics, keeping
3648
- * "Inbox 42" / "Brand (30)" patterns aligned without a sibling element
3649
- * fighting flex / inline-flow centring at the consumer's call site.
3650
- */
3651
- count = input(undefined, ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
3652
- hint = input(undefined, ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
3653
- errorMsg = input(undefined, ...(ngDevMode ? [{ debugName: "errorMsg" }] : /* istanbul ignore next */ []));
3719
+ class BadgeComponent {
3720
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3654
3721
  size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3655
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3656
- required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
3657
- indeterminate = input(false, ...(ngDevMode ? [{ debugName: "indeterminate" }] : /* istanbul ignore next */ []));
3658
- ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
3659
- id = input(uniqueId('ea-checkbox'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
3660
- checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
3661
- /** Fires with the new checked state whenever the user toggles the checkbox. */
3662
- changed = output();
3663
- _formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_formDisabled" }] : /* istanbul ignore next */ []));
3664
- isDisabled = computed(() => this.disabled() || this._formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
3665
- hasError = computed(() => !!this.errorMsg(), ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
3666
- showError = this.hasError;
3667
- showHint = computed(() => !!this.hint() && !this.hasError(), ...(ngDevMode ? [{ debugName: "showHint" }] : /* istanbul ignore next */ []));
3722
+ shape = input('pill', ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
3668
3723
  hostClasses = computed(() => ({
3669
- [`ea-checkbox--${this.size()}`]: true,
3670
- 'ea-checkbox--disabled': this.isDisabled(),
3671
- 'ea-checkbox--checked': this.checked(),
3672
- 'ea-checkbox--indeterminate': this.indeterminate(),
3673
- 'ea-checkbox--error': this.hasError(),
3724
+ [`ea-badge--${this.variant()}`]: true,
3725
+ [`ea-badge--${this.size()}`]: true,
3726
+ [`ea-badge--${this.shape()}`]: true,
3674
3727
  }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3675
- onChange = () => { };
3676
- onTouched = () => { };
3677
- writeValue(val) {
3678
- this.checked.set(!!val);
3679
- }
3680
- registerOnChange(fn) {
3681
- this.onChange = fn;
3682
- }
3683
- registerOnTouched(fn) {
3684
- this.onTouched = fn;
3685
- }
3686
- setDisabledState(isDisabled) {
3687
- this._formDisabled.set(isDisabled);
3688
- }
3689
- handleChange() {
3690
- if (this.isDisabled()) {
3691
- return;
3692
- }
3693
- const newValue = !this.checked();
3694
- this.checked.set(newValue);
3695
- this.onChange(newValue);
3696
- this.onTouched();
3697
- this.changed.emit(newValue);
3698
- }
3699
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3700
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: CheckboxComponent, isStandalone: true, selector: "ea-checkbox", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, errorMsg: { classPropertyName: "errorMsg", publicName: "errorMsg", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", changed: "changed" }, providers: [
3701
- {
3702
- provide: NG_VALUE_ACCESSOR,
3703
- useExisting: forwardRef(() => CheckboxComponent),
3704
- multi: true,
3705
- },
3706
- ], ngImport: i0, template: "<div class=\"ea-checkbox-field\">\n <label\n class=\"ea-checkbox\"\n [ngClass]=\"hostClasses()\"\n [for]=\"id()\">\n <input\n #inputEl\n type=\"checkbox\"\n class=\"ea-checkbox__input\"\n [id]=\"id()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [indeterminate]=\"indeterminate()\"\n [attr.aria-checked]=\"indeterminate() ? 'mixed' : checked()\"\n [attr.aria-label]=\"!label() ? (ariaLabel() ?? null) : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"\n showError() ? id() + '-error' : showHint() ? id() + '-hint' : null\n \"\n (change)=\"handleChange()\" />\n\n <span\n class=\"ea-checkbox__box\"\n aria-hidden=\"true\">\n @if (indeterminate()) {\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M4 8h8\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\" />\n </svg>\n } @else if (checked()) {\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M3.5 8.5L6.5 11.5L12.5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n }\n </span>\n\n @if (label()) {\n <span\n class=\"ea-checkbox__label\"\n [class.ea-checkbox__label--required]=\"required()\">\n {{ label() }}\n @if (count() !== undefined && count() !== null) {\n <span class=\"ea-checkbox__count\">{{ count() }}</span>\n }\n </span>\n }\n </label>\n\n <ea-field-messages\n [id]=\"id()\"\n [error]=\"showError() ? errorMsg() : null\"\n [hint]=\"showHint() ? hint() : null\" />\n</div>\n", styles: [":host{display:inline-flex;vertical-align:middle;line-height:1}.ea-checkbox-field{display:inline-flex;flex-direction:column;gap:var(--space-1-5)}.ea-checkbox{display:inline-flex;align-items:center;gap:.5em;cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--font-family-sans);color:var(--color-text-primary)}.ea-checkbox--xs{font-size:var(--font-size-xs)}.ea-checkbox--sm{font-size:var(--font-size-sm)}.ea-checkbox--md{font-size:var(--font-size-md)}.ea-checkbox--lg{font-size:var(--font-size-lg)}.ea-checkbox--xl{font-size:var(--font-size-xl)}.ea-checkbox--disabled{opacity:.45;cursor:not-allowed}.ea-checkbox--checked .ea-checkbox__box,.ea-checkbox--indeterminate .ea-checkbox__box{background-color:var(--color-brand-default);border-color:var(--color-brand-default);color:var(--color-text-inverse)}.ea-checkbox:hover:not(.ea-checkbox--disabled) .ea-checkbox__box{border-color:var(--color-brand-default)}.ea-checkbox__input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.ea-checkbox__input:focus-visible+.ea-checkbox__box{box-shadow:var(--shadow-focus-ring)}.ea-checkbox__box{position:relative;display:flex;align-items:center;justify-content:center;flex-shrink:0;box-sizing:border-box;width:1.25em;height:1.25em;border:var(--border-width-thin) solid var(--color-border-strong);border-radius:var(--radius-sm);background-color:var(--color-bg-base);transition:var(--transition-colors),var(--transition-shadow)}.ea-checkbox__box svg{position:absolute;width:65%;height:65%}.ea-checkbox__label{font-weight:var(--font-weight-regular)}.ea-checkbox__label--required:after{content:\" *\";color:var(--color-error-default)}.ea-checkbox__count{margin-left:.25em;color:var(--color-text-tertiary)}.ea-checkbox--error .ea-checkbox__box{border-color:var(--color-error-default)}\n"], dependencies: [{ kind: "component", type: FieldMessagesComponent, selector: "ea-field-messages", inputs: ["id", "error", "hint"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3728
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3729
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.16", type: BadgeComponent, isStandalone: true, selector: "ea-badge", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<span\n class=\"ea-badge\"\n [ngClass]=\"hostClasses()\">\n <ng-content />\n</span>\n", styles: [".ea-badge{display:inline-flex;align-items:center;gap:.25em;padding:.25em .625em;white-space:nowrap;font-family:var(--font-family-sans);font-weight:var(--font-weight-medium);line-height:var(--line-height-none);border-style:solid;border-width:var(--ea-badge-border-width, 0);border-color:var(--ea-badge-border-color, transparent);border-radius:var(--radius-full)}.ea-badge--xs{font-size:var(--ea-badge-font-size, var(--font-size-2xs))}.ea-badge--sm{font-size:var(--ea-badge-font-size, var(--font-size-xs))}.ea-badge--md{font-size:var(--ea-badge-font-size, var(--font-size-sm))}.ea-badge--lg{font-size:var(--ea-badge-font-size, var(--font-size-md))}.ea-badge--xl{font-size:var(--ea-badge-font-size, var(--font-size-lg))}.ea-badge--default{background-color:var(--ea-badge-background-color, var(--color-bg-muted));color:var(--ea-badge-color, var(--color-text-secondary))}.ea-badge--success{background-color:var(--ea-badge-background-color, var(--color-success-subtle));color:var(--ea-badge-color, var(--color-success-text))}.ea-badge--warning{background-color:var(--ea-badge-background-color, var(--color-warning-subtle));color:var(--ea-badge-color, var(--color-warning-text))}.ea-badge--error{background-color:var(--ea-badge-background-color, var(--color-error-subtle));color:var(--ea-badge-color, var(--color-error-text))}.ea-badge--info{background-color:var(--ea-badge-background-color, var(--color-info-subtle));color:var(--ea-badge-color, var(--color-info-text))}.ea-badge--pin{justify-content:center;box-sizing:border-box;min-width:var(--ea-badge-size, 1.8em);height:var(--ea-badge-size, 1.8em);padding:0 var(--space-1);font-variant-numeric:tabular-nums}.ea-badge--pin.ea-badge--sm{font-size:var(--ea-badge-font-size, 9px)}.ea-badge--pin.ea-badge--md{font-size:var(--ea-badge-font-size, 10px)}.ea-badge--pin.ea-badge--lg{font-size:var(--ea-badge-font-size, 12px)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3707
3730
  }
3708
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CheckboxComponent, decorators: [{
3731
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BadgeComponent, decorators: [{
3709
3732
  type: Component,
3710
- args: [{ selector: 'ea-checkbox', imports: [FieldMessagesComponent, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
3711
- {
3712
- provide: NG_VALUE_ACCESSOR,
3713
- useExisting: forwardRef(() => CheckboxComponent),
3714
- multi: true,
3715
- },
3716
- ], template: "<div class=\"ea-checkbox-field\">\n <label\n class=\"ea-checkbox\"\n [ngClass]=\"hostClasses()\"\n [for]=\"id()\">\n <input\n #inputEl\n type=\"checkbox\"\n class=\"ea-checkbox__input\"\n [id]=\"id()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [indeterminate]=\"indeterminate()\"\n [attr.aria-checked]=\"indeterminate() ? 'mixed' : checked()\"\n [attr.aria-label]=\"!label() ? (ariaLabel() ?? null) : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"\n showError() ? id() + '-error' : showHint() ? id() + '-hint' : null\n \"\n (change)=\"handleChange()\" />\n\n <span\n class=\"ea-checkbox__box\"\n aria-hidden=\"true\">\n @if (indeterminate()) {\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M4 8h8\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\" />\n </svg>\n } @else if (checked()) {\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M3.5 8.5L6.5 11.5L12.5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n }\n </span>\n\n @if (label()) {\n <span\n class=\"ea-checkbox__label\"\n [class.ea-checkbox__label--required]=\"required()\">\n {{ label() }}\n @if (count() !== undefined && count() !== null) {\n <span class=\"ea-checkbox__count\">{{ count() }}</span>\n }\n </span>\n }\n </label>\n\n <ea-field-messages\n [id]=\"id()\"\n [error]=\"showError() ? errorMsg() : null\"\n [hint]=\"showHint() ? hint() : null\" />\n</div>\n", styles: [":host{display:inline-flex;vertical-align:middle;line-height:1}.ea-checkbox-field{display:inline-flex;flex-direction:column;gap:var(--space-1-5)}.ea-checkbox{display:inline-flex;align-items:center;gap:.5em;cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--font-family-sans);color:var(--color-text-primary)}.ea-checkbox--xs{font-size:var(--font-size-xs)}.ea-checkbox--sm{font-size:var(--font-size-sm)}.ea-checkbox--md{font-size:var(--font-size-md)}.ea-checkbox--lg{font-size:var(--font-size-lg)}.ea-checkbox--xl{font-size:var(--font-size-xl)}.ea-checkbox--disabled{opacity:.45;cursor:not-allowed}.ea-checkbox--checked .ea-checkbox__box,.ea-checkbox--indeterminate .ea-checkbox__box{background-color:var(--color-brand-default);border-color:var(--color-brand-default);color:var(--color-text-inverse)}.ea-checkbox:hover:not(.ea-checkbox--disabled) .ea-checkbox__box{border-color:var(--color-brand-default)}.ea-checkbox__input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.ea-checkbox__input:focus-visible+.ea-checkbox__box{box-shadow:var(--shadow-focus-ring)}.ea-checkbox__box{position:relative;display:flex;align-items:center;justify-content:center;flex-shrink:0;box-sizing:border-box;width:1.25em;height:1.25em;border:var(--border-width-thin) solid var(--color-border-strong);border-radius:var(--radius-sm);background-color:var(--color-bg-base);transition:var(--transition-colors),var(--transition-shadow)}.ea-checkbox__box svg{position:absolute;width:65%;height:65%}.ea-checkbox__label{font-weight:var(--font-weight-regular)}.ea-checkbox__label--required:after{content:\" *\";color:var(--color-error-default)}.ea-checkbox__count{margin-left:.25em;color:var(--color-text-tertiary)}.ea-checkbox--error .ea-checkbox__box{border-color:var(--color-error-default)}\n"] }]
3717
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], errorMsg: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMsg", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
3733
+ args: [{ selector: 'ea-badge', imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<span\n class=\"ea-badge\"\n [ngClass]=\"hostClasses()\">\n <ng-content />\n</span>\n", styles: [".ea-badge{display:inline-flex;align-items:center;gap:.25em;padding:.25em .625em;white-space:nowrap;font-family:var(--font-family-sans);font-weight:var(--font-weight-medium);line-height:var(--line-height-none);border-style:solid;border-width:var(--ea-badge-border-width, 0);border-color:var(--ea-badge-border-color, transparent);border-radius:var(--radius-full)}.ea-badge--xs{font-size:var(--ea-badge-font-size, var(--font-size-2xs))}.ea-badge--sm{font-size:var(--ea-badge-font-size, var(--font-size-xs))}.ea-badge--md{font-size:var(--ea-badge-font-size, var(--font-size-sm))}.ea-badge--lg{font-size:var(--ea-badge-font-size, var(--font-size-md))}.ea-badge--xl{font-size:var(--ea-badge-font-size, var(--font-size-lg))}.ea-badge--default{background-color:var(--ea-badge-background-color, var(--color-bg-muted));color:var(--ea-badge-color, var(--color-text-secondary))}.ea-badge--success{background-color:var(--ea-badge-background-color, var(--color-success-subtle));color:var(--ea-badge-color, var(--color-success-text))}.ea-badge--warning{background-color:var(--ea-badge-background-color, var(--color-warning-subtle));color:var(--ea-badge-color, var(--color-warning-text))}.ea-badge--error{background-color:var(--ea-badge-background-color, var(--color-error-subtle));color:var(--ea-badge-color, var(--color-error-text))}.ea-badge--info{background-color:var(--ea-badge-background-color, var(--color-info-subtle));color:var(--ea-badge-color, var(--color-info-text))}.ea-badge--pin{justify-content:center;box-sizing:border-box;min-width:var(--ea-badge-size, 1.8em);height:var(--ea-badge-size, 1.8em);padding:0 var(--space-1);font-variant-numeric:tabular-nums}.ea-badge--pin.ea-badge--sm{font-size:var(--ea-badge-font-size, 9px)}.ea-badge--pin.ea-badge--md{font-size:var(--ea-badge-font-size, 10px)}.ea-badge--pin.ea-badge--lg{font-size:var(--ea-badge-font-size, 12px)}\n"] }]
3734
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }] } });
3718
3735
 
3719
- class SearchIconComponent extends IconComponentBase {
3720
- static slug = 'search';
3736
+ class ChevronRightIconComponent extends IconComponentBase {
3737
+ static slug = 'chevron-right';
3721
3738
  static category = 'feather';
3722
3739
  static tags = [
3723
- 'search',
3724
- 'find',
3725
- 'magnify',
3726
- 'lookup',
3727
- 'query',
3728
- 'recherche',
3729
- 'chercher',
3730
- 'buscar',
3731
- 'búsqueda',
3732
- 'αναζήτηση',
3733
- 'βρες',
3734
- 'szukaj',
3735
- 'wyszukaj',
3740
+ 'chevron-right',
3741
+ 'chevron',
3742
+ 'right',
3743
+ 'arrow',
3744
+ 'forward',
3745
+ 'next',
3746
+ 'droite',
3747
+ 'derecha',
3748
+ 'δεξιά',
3749
+ 'prawo',
3736
3750
  ];
3737
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SearchIconComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3738
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: SearchIconComponent, isStandalone: true, selector: "ea-icon-search", usesInheritance: true, ngImport: i0, template: `
3751
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ChevronRightIconComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3752
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: ChevronRightIconComponent, isStandalone: true, selector: "ea-icon-chevron-right", usesInheritance: true, ngImport: i0, template: `
3739
3753
  <svg
3740
3754
  viewBox="0 0 24 24"
3741
3755
  fill="none"
@@ -3746,18 +3760,14 @@ class SearchIconComponent extends IconComponentBase {
3746
3760
  aria-hidden="true"
3747
3761
  width="100%"
3748
3762
  height="100%">
3749
- <circle
3750
- cx="11"
3751
- cy="11"
3752
- r="8" />
3753
- <path d="m21 21-4.35-4.35" />
3763
+ <polyline points="9 18 15 12 9 6" />
3754
3764
  </svg>
3755
3765
  `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3756
3766
  }
3757
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SearchIconComponent, decorators: [{
3767
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ChevronRightIconComponent, decorators: [{
3758
3768
  type: Component,
3759
3769
  args: [{
3760
- selector: 'ea-icon-search',
3770
+ selector: 'ea-icon-chevron-right',
3761
3771
  changeDetection: ChangeDetectionStrategy.OnPush,
3762
3772
  template: `
3763
3773
  <svg
@@ -3770,298 +3780,236 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
3770
3780
  aria-hidden="true"
3771
3781
  width="100%"
3772
3782
  height="100%">
3773
- <circle
3774
- cx="11"
3775
- cy="11"
3776
- r="8" />
3777
- <path d="m21 21-4.35-4.35" />
3783
+ <polyline points="9 18 15 12 9 6" />
3778
3784
  </svg>
3779
3785
  `,
3780
3786
  }]
3781
3787
  }] });
3782
3788
 
3783
3789
  /**
3784
- * `<ea-command-palette>` is a search-driven action launcher: a modal dialog
3785
- * containing a search input and a filtered list of commands. Designed for
3786
- * `Cmd/Ctrl + K`-style global menus.
3787
- *
3788
- * The component does NOT bind global shortcuts; the consumer wires up
3789
- * whatever trigger they want and toggles `[(open)]`. Each selected command
3790
- * is emitted via `(execute)`; the palette closes automatically afterwards.
3791
- *
3792
- * Items can be grouped via the `group` field; ungrouped items render first.
3793
- * The active row is tracked via roving `aria-activedescendant` on the search
3794
- * input, which is the canonical ARIA combobox-with-listbox pattern.
3790
+ * Navigation trail that shows the user's location within a hierarchy. Items
3791
+ * with an `href` render as links, others render as buttons; the final item is
3792
+ * always treated as the current page and is non-interactive.
3795
3793
  */
3796
- class CommandPaletteComponent {
3794
+ class BreadcrumbsComponent {
3797
3795
  i18n = inject(EagamiI18nService);
3798
- hostEl = inject(ElementRef);
3799
- items = input.required(...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
3800
- open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
3801
- placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
3802
- emptyMessage = input('', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
3803
- execute = output();
3804
- messages = this.i18n.messages;
3805
- dialogEl = viewChild('dialogEl', ...(ngDevMode ? [{ debugName: "dialogEl" }] : /* istanbul ignore next */ []));
3806
- searchEl = viewChild('searchEl', ...(ngDevMode ? [{ debugName: "searchEl" }] : /* istanbul ignore next */ []));
3807
- query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
3808
- listboxId = uniqueId('ea-command-palette-listbox');
3809
- /**
3810
- * Items bucketed by `group`, then flattened back into display order
3811
- * (ungrouped items first, then each named group). The matching
3812
- * `filteredItems` array follows this same order so `flatIndex` lines up
3813
- * one-to-one with what the user sees.
3814
- */
3815
- groupedItems = computed(() => {
3816
- const q = this.query().trim().toLowerCase();
3817
- const candidates = this.items().filter(item => {
3818
- if (item.disabled) {
3819
- return false;
3820
- }
3821
- if (!q) {
3822
- return true;
3823
- }
3824
- const haystack = [item.label, item.description ?? '', ...(item.keywords ?? [])]
3825
- .join(' ')
3826
- .toLowerCase();
3827
- // Match when the query is a prefix of any word in the haystack.
3828
- // Substring-anywhere matching would surface confusing results (typing
3829
- // "c" matching "Repla*c*e" via a mid-word character); word-boundary
3830
- // matching keeps results predictable.
3831
- return haystack.split(/[\s\-_]+/).some(word => word.startsWith(q));
3832
- });
3833
- const buckets = new Map();
3834
- for (const item of candidates) {
3835
- const key = item.group ?? '';
3836
- if (!buckets.has(key)) {
3837
- buckets.set(key, []);
3838
- }
3839
- buckets.get(key).push(item);
3840
- }
3841
- const groups = [];
3842
- let flatIndex = 0;
3843
- const pushGroup = (group, items) => {
3844
- groups.push({
3845
- group,
3846
- items: items.map(item => ({ item, flatIndex: flatIndex++ })),
3847
- });
3848
- };
3849
- const ungrouped = buckets.get('');
3850
- if (ungrouped && ungrouped.length > 0) {
3851
- pushGroup('', ungrouped);
3852
- }
3853
- for (const [group, items] of buckets) {
3854
- if (group !== '') {
3855
- pushGroup(group, items);
3856
- }
3857
- }
3858
- return groups;
3859
- }, ...(ngDevMode ? [{ debugName: "groupedItems" }] : /* istanbul ignore next */ []));
3860
- /**
3861
- * Flat list of matches in display order. Drives keyboard navigation and
3862
- * `aria-activedescendant`.
3863
- */
3864
- filteredItems = computed(() => this.groupedItems().flatMap(group => group.items.map(entry => entry.item)), ...(ngDevMode ? [{ debugName: "filteredItems" }] : /* istanbul ignore next */ []));
3865
- _activeIndex = signal(0, ...(ngDevMode ? [{ debugName: "_activeIndex" }] : /* istanbul ignore next */ []));
3866
- /**
3867
- * Tracks what the user last did so the visual highlight only renders when
3868
- * it actually reflects what a click/Enter would select right now:
3869
- * - `keyboard`: keyboard nav (or just-opened / just-typed): show the
3870
- * active item's background so keyboard users see what Enter will pick.
3871
- * - `mouse`: pointer is moving inside the list: rely on `:hover` for the
3872
- * visual; skip the active-row background to avoid two highlights.
3873
- * - `none`: pointer is outside the list and no keyboard nav has happened
3874
- * since: nothing is highlighted, because nothing on screen is a
3875
- * next-click target.
3876
- */
3877
- interaction = signal('keyboard', ...(ngDevMode ? [{ debugName: "interaction" }] : /* istanbul ignore next */ []));
3878
- activeIndex = computed(() => {
3879
- const max = this.filteredItems().length - 1;
3880
- if (max < 0) {
3881
- return -1;
3882
- }
3883
- return Math.min(this._activeIndex(), max);
3884
- }, ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
3885
- activeId = computed(() => {
3886
- const idx = this.activeIndex();
3887
- if (idx < 0) {
3888
- return null;
3889
- }
3890
- return `ea-command-palette-item-${this.filteredItems()[idx].id}`;
3891
- }, ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
3892
- constructor() {
3893
- /* When the palette opens, focus the search input and reset state. The
3894
- dialog needs an extra tick to call `showModal()` before the input is
3895
- focusable, so queue the focus into a microtask. */
3896
- effect(() => {
3897
- const isOpen = this.open();
3898
- const dialog = this.dialogEl()?.nativeElement;
3899
- if (!dialog) {
3900
- return;
3901
- }
3902
- if (isOpen) {
3903
- if (!dialog.open) {
3904
- dialog.showModal?.();
3905
- }
3906
- this.query.set('');
3907
- this._activeIndex.set(0);
3908
- this.interaction.set('keyboard');
3909
- queueMicrotask(() => this.searchEl()?.nativeElement.focus());
3910
- }
3911
- else if (dialog.open) {
3912
- dialog.close();
3913
- }
3914
- });
3915
- }
3916
- itemDomId(item) {
3917
- return `ea-command-palette-item-${item.id}`;
3918
- }
3919
- isActive(flatIndex) {
3920
- return this.activeIndex() === flatIndex;
3921
- }
3922
- /**
3923
- * Whether the active row should render its highlighted background right
3924
- * now. False when the pointer is hovering the list (`:hover` handles the
3925
- * visual) or when the pointer is out of the list entirely (nothing is a
3926
- * next-click target).
3927
- */
3928
- showActiveHighlight(flatIndex) {
3929
- return this.interaction() === 'keyboard' && this.isActive(flatIndex);
3930
- }
3931
- onQueryInput(event) {
3932
- this.query.set(event.target.value);
3933
- this._activeIndex.set(0);
3934
- // Typing implies keyboard intent; surface the first match so the user
3935
- // knows what Enter would pick without having to mouse over.
3936
- this.interaction.set('keyboard');
3937
- }
3938
- clearQuery() {
3939
- this.query.set('');
3940
- this._activeIndex.set(0);
3941
- this.interaction.set('keyboard');
3942
- this.searchEl()?.nativeElement.focus();
3796
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
3797
+ separator = input('chevron', ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
3798
+ ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
3799
+ /** Fires when a non-disabled, non-final breadcrumb is activated. */
3800
+ clicked = output();
3801
+ /** Accessible label for the breadcrumb nav, falling back to the active locale. */
3802
+ resolvedAriaLabel = computed(() => this.ariaLabel() ?? this.i18n.messages().breadcrumbs.label, ...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
3803
+ isLast(index) {
3804
+ return index === this.items().length - 1;
3943
3805
  }
3944
- onSearchKeydown(event) {
3945
- const max = this.filteredItems().length - 1;
3946
- if (max < 0) {
3806
+ handleClick(item, index, event) {
3807
+ if (item.disabled || this.isLast(index)) {
3808
+ event.preventDefault();
3947
3809
  return;
3948
3810
  }
3949
- switch (event.key) {
3950
- case 'ArrowDown': {
3951
- event.preventDefault();
3952
- this._activeIndex.set(this.activeIndex() < max ? this.activeIndex() + 1 : 0);
3953
- this.interaction.set('keyboard');
3954
- this.scrollActiveIntoView();
3955
- break;
3956
- }
3957
- case 'ArrowUp': {
3958
- event.preventDefault();
3959
- this._activeIndex.set(this.activeIndex() > 0 ? this.activeIndex() - 1 : max);
3960
- this.interaction.set('keyboard');
3961
- this.scrollActiveIntoView();
3962
- break;
3963
- }
3964
- case 'Home': {
3965
- event.preventDefault();
3966
- this._activeIndex.set(0);
3967
- this.interaction.set('keyboard');
3968
- this.scrollActiveIntoView();
3969
- break;
3970
- }
3971
- case 'End': {
3972
- event.preventDefault();
3973
- this._activeIndex.set(max);
3974
- this.interaction.set('keyboard');
3975
- this.scrollActiveIntoView();
3976
- break;
3977
- }
3978
- case 'Enter': {
3979
- event.preventDefault();
3980
- const item = this.filteredItems()[this.activeIndex()];
3981
- if (item) {
3982
- this.executeItem(item);
3983
- }
3984
- break;
3985
- }
3986
- }
3811
+ this.clicked.emit({ item, index, event });
3987
3812
  }
3988
- onItemClick(item) {
3989
- if (item.disabled) {
3813
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BreadcrumbsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3814
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: BreadcrumbsComponent, isStandalone: true, selector: "ea-breadcrumbs", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<nav\n class=\"ea-breadcrumbs\"\n [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"ea-breadcrumbs__list\">\n @for (item of items(); track $index; let i = $index; let last = $last) {\n <li class=\"ea-breadcrumbs__item\">\n @if (last) {\n <span\n class=\"ea-breadcrumbs__current\"\n aria-current=\"page\">\n {{ item.label }}\n </span>\n } @else if (item.disabled) {\n <span class=\"ea-breadcrumbs__link ea-breadcrumbs__link--disabled\">\n {{ item.label }}\n </span>\n } @else if (item.href) {\n <a\n class=\"ea-breadcrumbs__link\"\n [href]=\"item.href\"\n (click)=\"handleClick(item, i, $event)\">\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"ea-breadcrumbs__link ea-breadcrumbs__link--button\"\n (click)=\"handleClick(item, i, $event)\">\n {{ item.label }}\n </button>\n }\n\n @if (!last) {\n @if (separator() === 'chevron') {\n <ea-icon-chevron-right\n class=\"ea-breadcrumbs__separator\"\n aria-hidden=\"true\" />\n } @else {\n <span\n class=\"ea-breadcrumbs__separator ea-breadcrumbs__separator--slash\"\n aria-hidden=\"true\">\n /\n </span>\n }\n }\n </li>\n }\n </ol>\n</nav>\n", styles: [".ea-breadcrumbs{font-family:var(--font-family-sans);font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary)}.ea-breadcrumbs__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-1);padding:0;margin:0;list-style:none}.ea-breadcrumbs__item{display:inline-flex;align-items:center;gap:var(--space-1);min-width:0}.ea-breadcrumbs__link{padding:var(--space-0-5) var(--space-1);border:none;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-secondary);font-family:inherit;font-size:inherit;text-decoration:none;cursor:pointer;transition:var(--transition-colors)}.ea-breadcrumbs__link:hover:not(.ea-breadcrumbs__link--disabled){color:var(--color-text-primary);text-decoration:underline}.ea-breadcrumbs__link:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-breadcrumbs__link--disabled{color:var(--color-text-disabled);cursor:not-allowed}.ea-breadcrumbs__current{padding:var(--space-0-5) var(--space-1);font-weight:var(--font-weight-medium);color:var(--color-text-primary)}.ea-breadcrumbs__separator{display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;color:var(--color-text-tertiary);flex-shrink:0;-webkit-user-select:none;user-select:none}.ea-breadcrumbs__separator--slash{width:auto;height:auto;font-size:var(--font-size-sm)}\n"], dependencies: [{ kind: "component", type: ChevronRightIconComponent, selector: "ea-icon-chevron-right" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3815
+ }
3816
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: BreadcrumbsComponent, decorators: [{
3817
+ type: Component,
3818
+ args: [{ selector: 'ea-breadcrumbs', imports: [ChevronRightIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<nav\n class=\"ea-breadcrumbs\"\n [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"ea-breadcrumbs__list\">\n @for (item of items(); track $index; let i = $index; let last = $last) {\n <li class=\"ea-breadcrumbs__item\">\n @if (last) {\n <span\n class=\"ea-breadcrumbs__current\"\n aria-current=\"page\">\n {{ item.label }}\n </span>\n } @else if (item.disabled) {\n <span class=\"ea-breadcrumbs__link ea-breadcrumbs__link--disabled\">\n {{ item.label }}\n </span>\n } @else if (item.href) {\n <a\n class=\"ea-breadcrumbs__link\"\n [href]=\"item.href\"\n (click)=\"handleClick(item, i, $event)\">\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"ea-breadcrumbs__link ea-breadcrumbs__link--button\"\n (click)=\"handleClick(item, i, $event)\">\n {{ item.label }}\n </button>\n }\n\n @if (!last) {\n @if (separator() === 'chevron') {\n <ea-icon-chevron-right\n class=\"ea-breadcrumbs__separator\"\n aria-hidden=\"true\" />\n } @else {\n <span\n class=\"ea-breadcrumbs__separator ea-breadcrumbs__separator--slash\"\n aria-hidden=\"true\">\n /\n </span>\n }\n }\n </li>\n }\n </ol>\n</nav>\n", styles: [".ea-breadcrumbs{font-family:var(--font-family-sans);font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary)}.ea-breadcrumbs__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-1);padding:0;margin:0;list-style:none}.ea-breadcrumbs__item{display:inline-flex;align-items:center;gap:var(--space-1);min-width:0}.ea-breadcrumbs__link{padding:var(--space-0-5) var(--space-1);border:none;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-secondary);font-family:inherit;font-size:inherit;text-decoration:none;cursor:pointer;transition:var(--transition-colors)}.ea-breadcrumbs__link:hover:not(.ea-breadcrumbs__link--disabled){color:var(--color-text-primary);text-decoration:underline}.ea-breadcrumbs__link:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-breadcrumbs__link--disabled{color:var(--color-text-disabled);cursor:not-allowed}.ea-breadcrumbs__current{padding:var(--space-0-5) var(--space-1);font-weight:var(--font-weight-medium);color:var(--color-text-primary)}.ea-breadcrumbs__separator{display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;color:var(--color-text-tertiary);flex-shrink:0;-webkit-user-select:none;user-select:none}.ea-breadcrumbs__separator--slash{width:auto;height:auto;font-size:var(--font-size-sm)}\n"] }]
3819
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
3820
+
3821
+ /**
3822
+ * Standard action button supporting primary, secondary, ghost, and danger
3823
+ * variants. Includes a loading state that swaps the label for a spinner while
3824
+ * preserving the rendered width.
3825
+ */
3826
+ class ButtonComponent {
3827
+ variant = input('primary', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3828
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3829
+ type = input('button', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
3830
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3831
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
3832
+ fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : /* istanbul ignore next */ []));
3833
+ /** Optional icon component rendered to the left of the label. */
3834
+ icon = input(undefined, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
3835
+ ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
3836
+ ariaCurrent = input(undefined, { ...(ngDevMode ? { debugName: "ariaCurrent" } : /* istanbul ignore next */ {}), alias: 'aria-current' });
3837
+ /** Fires when the button is activated; suppressed while disabled or loading. */
3838
+ clicked = output();
3839
+ isDisabled = computed(() => this.disabled() || this.loading(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
3840
+ hostClasses = computed(() => ({
3841
+ [`ea-button--${this.variant()}`]: true,
3842
+ [`ea-button--${this.size()}`]: true,
3843
+ 'ea-button--full-width': this.fullWidth(),
3844
+ 'ea-button--loading': this.loading(),
3845
+ 'ea-button--disabled': this.isDisabled(),
3846
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3847
+ handleClick(event) {
3848
+ if (this.isDisabled()) {
3849
+ event.preventDefault();
3990
3850
  return;
3991
3851
  }
3992
- this.executeItem(item);
3852
+ this.clicked.emit(event);
3993
3853
  }
3994
- onItemMouseEnter(flatIndex) {
3995
- this._activeIndex.set(flatIndex);
3996
- this.interaction.set('mouse');
3854
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3855
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: ButtonComponent, isStandalone: true, selector: "ea-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaCurrent: { classPropertyName: "ariaCurrent", publicName: "aria-current", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, host: { properties: { "class.ea-button--full-width": "fullWidth()" } }, ngImport: i0, template: "<button\n class=\"ea-button\"\n [ngClass]=\"hostClasses()\"\n [type]=\"type()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-current]=\"ariaCurrent() || null\"\n [attr.aria-busy]=\"loading() || null\"\n (click)=\"handleClick($event)\">\n @if (loading()) {\n <span\n class=\"ea-button__spinner\"\n aria-hidden=\"true\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"31.4\"\n stroke-dashoffset=\"10\" />\n </svg>\n </span>\n }\n\n <span\n class=\"ea-button__content\"\n [class.ea-button__content--hidden]=\"loading()\">\n <ng-content select=\"[slot=prefix]\" />\n @if (icon(); as iconComponent) {\n <span\n class=\"ea-button__icon\"\n aria-hidden=\"true\">\n <ng-container *ngComponentOutlet=\"iconComponent\" />\n </span>\n }\n <span class=\"ea-button__label\">\n <ng-content />\n </span>\n <ng-content select=\"[slot=suffix]\" />\n </span>\n</button>\n", styles: [":host{display:inline-flex}.ea-button{display:inline-flex;align-items:center;justify-content:center;gap:.5em;position:relative;white-space:nowrap;-webkit-user-select:none;user-select:none;padding:.5em 1em;min-height:2.5em;font-family:var(--font-family-sans);font-weight:var(--ea-button-font-weight, var(--font-weight-medium));letter-spacing:var(--letter-spacing-wide);text-decoration:none;line-height:var(--line-height-none);border-width:var(--border-width-thin);border-style:solid;border-radius:var(--radius-md);transition:var(--transition-colors),var(--transition-shadow);cursor:pointer}.ea-button:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-button--xs{font-size:var(--ea-button-font-size, var(--font-size-xs))}.ea-button--sm{font-size:var(--ea-button-font-size, var(--font-size-sm))}.ea-button--md{font-size:var(--ea-button-font-size, var(--font-size-md))}.ea-button--lg{font-size:var(--ea-button-font-size, var(--font-size-lg))}.ea-button--xl{font-size:var(--ea-button-font-size, var(--font-size-xl))}.ea-button--primary{background-color:var(--ea-button-background-color, var(--color-brand-default));border-color:var(--ea-button-border-color, var(--color-brand-default));color:var(--ea-button-color, var(--color-neutral-0))}.ea-button--primary:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-brand-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-brand-hover)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--primary:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-brand-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-brand-active)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--secondary{background-color:var(--ea-button-background-color, transparent);border-color:var(--ea-button-border-color, var(--color-border-strong));color:var(--ea-button-color, var(--color-text-primary))}.ea-button--secondary:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-state-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-neutral-500)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-text-primary)))}.ea-button--secondary:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-state-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-neutral-500)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-text-primary)))}.ea-button--ghost{background-color:var(--ea-button-background-color, transparent);border-color:var(--ea-button-border-color, transparent);color:var(--ea-button-color, var(--color-text-primary))}.ea-button--ghost:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-state-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, transparent));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-text-primary)))}.ea-button--ghost:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-state-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, transparent));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-text-primary)))}.ea-button--danger{background-color:var(--ea-button-background-color, var(--color-error-default));border-color:var(--ea-button-border-color, var(--color-error-default));color:var(--ea-button-color, var(--color-neutral-0))}.ea-button--danger:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-error-700)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-error-700)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--danger:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-error-700)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-error-700)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-neutral-0)));filter:brightness(.9)}.ea-button--full-width{width:100%}.ea-button--disabled,.ea-button:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.ea-button--loading{cursor:wait;pointer-events:none}.ea-button__content{display:inline-flex;align-items:center;gap:.5em}.ea-button__content--hidden{visibility:hidden}.ea-button__icon{display:inline-flex;align-items:center;flex-shrink:0;font-size:var(--icon-inline-size)}.ea-button__label{display:inline-flex;align-items:center}.ea-button__spinner{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.ea-button__spinner svg{width:1.1em;height:1.1em;animation:ea-spin .75s linear infinite}:host(.ea-button--full-width){display:block;width:100%}@keyframes ea-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3856
+ }
3857
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ButtonComponent, decorators: [{
3858
+ type: Component,
3859
+ args: [{ selector: 'ea-button', imports: [NgClass, NgComponentOutlet], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3860
+ '[class.ea-button--full-width]': 'fullWidth()',
3861
+ }, template: "<button\n class=\"ea-button\"\n [ngClass]=\"hostClasses()\"\n [type]=\"type()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-current]=\"ariaCurrent() || null\"\n [attr.aria-busy]=\"loading() || null\"\n (click)=\"handleClick($event)\">\n @if (loading()) {\n <span\n class=\"ea-button__spinner\"\n aria-hidden=\"true\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"31.4\"\n stroke-dashoffset=\"10\" />\n </svg>\n </span>\n }\n\n <span\n class=\"ea-button__content\"\n [class.ea-button__content--hidden]=\"loading()\">\n <ng-content select=\"[slot=prefix]\" />\n @if (icon(); as iconComponent) {\n <span\n class=\"ea-button__icon\"\n aria-hidden=\"true\">\n <ng-container *ngComponentOutlet=\"iconComponent\" />\n </span>\n }\n <span class=\"ea-button__label\">\n <ng-content />\n </span>\n <ng-content select=\"[slot=suffix]\" />\n </span>\n</button>\n", styles: [":host{display:inline-flex}.ea-button{display:inline-flex;align-items:center;justify-content:center;gap:.5em;position:relative;white-space:nowrap;-webkit-user-select:none;user-select:none;padding:.5em 1em;min-height:2.5em;font-family:var(--font-family-sans);font-weight:var(--ea-button-font-weight, var(--font-weight-medium));letter-spacing:var(--letter-spacing-wide);text-decoration:none;line-height:var(--line-height-none);border-width:var(--border-width-thin);border-style:solid;border-radius:var(--radius-md);transition:var(--transition-colors),var(--transition-shadow);cursor:pointer}.ea-button:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-button--xs{font-size:var(--ea-button-font-size, var(--font-size-xs))}.ea-button--sm{font-size:var(--ea-button-font-size, var(--font-size-sm))}.ea-button--md{font-size:var(--ea-button-font-size, var(--font-size-md))}.ea-button--lg{font-size:var(--ea-button-font-size, var(--font-size-lg))}.ea-button--xl{font-size:var(--ea-button-font-size, var(--font-size-xl))}.ea-button--primary{background-color:var(--ea-button-background-color, var(--color-brand-default));border-color:var(--ea-button-border-color, var(--color-brand-default));color:var(--ea-button-color, var(--color-neutral-0))}.ea-button--primary:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-brand-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-brand-hover)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--primary:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-brand-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-brand-active)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--secondary{background-color:var(--ea-button-background-color, transparent);border-color:var(--ea-button-border-color, var(--color-border-strong));color:var(--ea-button-color, var(--color-text-primary))}.ea-button--secondary:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-state-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-neutral-500)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-text-primary)))}.ea-button--secondary:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-state-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-neutral-500)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-text-primary)))}.ea-button--ghost{background-color:var(--ea-button-background-color, transparent);border-color:var(--ea-button-border-color, transparent);color:var(--ea-button-color, var(--color-text-primary))}.ea-button--ghost:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-state-hover)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, transparent));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-text-primary)))}.ea-button--ghost:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-state-active)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, transparent));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-text-primary)))}.ea-button--danger{background-color:var(--ea-button-background-color, var(--color-error-default));border-color:var(--ea-button-border-color, var(--color-error-default));color:var(--ea-button-color, var(--color-neutral-0))}.ea-button--danger:hover:not(.ea-button--disabled){background-color:var(--ea-button-background-color-hover, var(--ea-button-background-color, var(--color-error-700)));border-color:var(--ea-button-border-color-hover, var(--ea-button-border-color, var(--color-error-700)));color:var(--ea-button-color-hover, var(--ea-button-color, var(--color-neutral-0)))}.ea-button--danger:active:not(.ea-button--disabled){background-color:var(--ea-button-background-color-active, var(--ea-button-background-color, var(--color-error-700)));border-color:var(--ea-button-border-color-active, var(--ea-button-border-color, var(--color-error-700)));color:var(--ea-button-color-active, var(--ea-button-color, var(--color-neutral-0)));filter:brightness(.9)}.ea-button--full-width{width:100%}.ea-button--disabled,.ea-button:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.ea-button--loading{cursor:wait;pointer-events:none}.ea-button__content{display:inline-flex;align-items:center;gap:.5em}.ea-button__content--hidden{visibility:hidden}.ea-button__icon{display:inline-flex;align-items:center;flex-shrink:0;font-size:var(--icon-inline-size)}.ea-button__label{display:inline-flex;align-items:center}.ea-button__spinner{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.ea-button__spinner svg{width:1.1em;height:1.1em;animation:ea-spin .75s linear infinite}:host(.ea-button--full-width){display:block;width:100%}@keyframes ea-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
3862
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-current", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }] } });
3863
+
3864
+ /**
3865
+ * Thin separator used to visually divide content. Renders horizontally by
3866
+ * default and may include an optional centred label (e.g. "or").
3867
+ */
3868
+ class DividerComponent {
3869
+ orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
3870
+ label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
3871
+ /** Renders a heavier rule. */
3872
+ thick = input(false, ...(ngDevMode ? [{ debugName: "thick" }] : /* istanbul ignore next */ []));
3873
+ hostClasses = computed(() => ({
3874
+ [`ea-divider--${this.orientation()}`]: true,
3875
+ 'ea-divider--with-label': !!this.label(),
3876
+ 'ea-divider--thick': this.thick(),
3877
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3878
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: DividerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3879
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: DividerComponent, isStandalone: true, selector: "ea-divider", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, thick: { classPropertyName: "thick", publicName: "thick", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"ea-divider\"\n [ngClass]=\"hostClasses()\"\n [attr.role]=\"'separator'\"\n [attr.aria-orientation]=\"orientation()\">\n @if (label()) {\n <span class=\"ea-divider__label\">{{ label() }}</span>\n }\n</div>\n", styles: [".ea-divider{--ea-divider-thickness: 1px;flex-shrink:0;border:0;font-family:var(--font-family-sans)}.ea-divider--thick{--ea-divider-thickness: 2px}.ea-divider--horizontal{display:flex;align-items:center;width:100%;height:var(--ea-divider-thickness);background-color:var(--color-divider)}.ea-divider--vertical{display:inline-block;width:var(--ea-divider-thickness);height:1.5em;min-height:1rem;background-color:var(--color-divider)}.ea-divider--vertical.ea-divider--with-label{display:inline-flex;flex-direction:column;align-items:center;width:auto;height:auto;background-color:transparent;gap:var(--space-3)}.ea-divider--vertical.ea-divider--with-label:before,.ea-divider--vertical.ea-divider--with-label:after{content:\"\";flex:1;width:var(--ea-divider-thickness);height:auto;min-height:2em;background-color:var(--color-divider)}.ea-divider--with-label{height:auto;background-color:transparent;gap:var(--space-3)}.ea-divider--with-label:before,.ea-divider--with-label:after{content:\"\";flex:1;height:var(--ea-divider-thickness);background-color:var(--color-divider)}.ea-divider__label{flex-shrink:0;font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary);white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3880
+ }
3881
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: DividerComponent, decorators: [{
3882
+ type: Component,
3883
+ args: [{ selector: 'ea-divider', imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div\n class=\"ea-divider\"\n [ngClass]=\"hostClasses()\"\n [attr.role]=\"'separator'\"\n [attr.aria-orientation]=\"orientation()\">\n @if (label()) {\n <span class=\"ea-divider__label\">{{ label() }}</span>\n }\n</div>\n", styles: [".ea-divider{--ea-divider-thickness: 1px;flex-shrink:0;border:0;font-family:var(--font-family-sans)}.ea-divider--thick{--ea-divider-thickness: 2px}.ea-divider--horizontal{display:flex;align-items:center;width:100%;height:var(--ea-divider-thickness);background-color:var(--color-divider)}.ea-divider--vertical{display:inline-block;width:var(--ea-divider-thickness);height:1.5em;min-height:1rem;background-color:var(--color-divider)}.ea-divider--vertical.ea-divider--with-label{display:inline-flex;flex-direction:column;align-items:center;width:auto;height:auto;background-color:transparent;gap:var(--space-3)}.ea-divider--vertical.ea-divider--with-label:before,.ea-divider--vertical.ea-divider--with-label:after{content:\"\";flex:1;width:var(--ea-divider-thickness);height:auto;min-height:2em;background-color:var(--color-divider)}.ea-divider--with-label{height:auto;background-color:transparent;gap:var(--space-3)}.ea-divider--with-label:before,.ea-divider--with-label:after{content:\"\";flex:1;height:var(--ea-divider-thickness);background-color:var(--color-divider)}.ea-divider__label{flex-shrink:0;font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary);white-space:nowrap}\n"] }]
3884
+ }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], thick: [{ type: i0.Input, args: [{ isSignal: true, alias: "thick", required: false }] }] } });
3885
+
3886
+ /**
3887
+ * Surface for grouping related content. Provides optional `header` and
3888
+ * `footer` content slots and supports elevated, outlined, and filled
3889
+ * variants. The card shadow can be customised per instance via the
3890
+ * `--ea-card-shadow` CSS custom property.
3891
+ */
3892
+ class CardComponent {
3893
+ variant = input('elevated', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3894
+ padding = input('md', ...(ngDevMode ? [{ debugName: "padding" }] : /* istanbul ignore next */ []));
3895
+ fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : /* istanbul ignore next */ []));
3896
+ headerAlign = input('center', ...(ngDevMode ? [{ debugName: "headerAlign" }] : /* istanbul ignore next */ []));
3897
+ headerDivider = input(false, ...(ngDevMode ? [{ debugName: "headerDivider" }] : /* istanbul ignore next */ []));
3898
+ hostClasses = computed(() => ({
3899
+ [`ea-card--${this.variant()}`]: true,
3900
+ [`ea-card--padding-${this.padding()}`]: true,
3901
+ 'ea-card--full-width': this.fullWidth(),
3902
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3903
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3904
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: CardComponent, isStandalone: true, selector: "ea-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, headerAlign: { classPropertyName: "headerAlign", publicName: "headerAlign", isSignal: true, isRequired: false, transformFunction: null }, headerDivider: { classPropertyName: "headerDivider", publicName: "headerDivider", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"ea-card\"\n [ngClass]=\"hostClasses()\">\n <div\n class=\"ea-card__header\"\n [style.text-align]=\"headerAlign()\">\n <ng-content select=\"[slot=header]\" />\n </div>\n\n @if (headerDivider()) {\n <ea-divider class=\"ea-card__divider\" />\n }\n\n <div class=\"ea-card__body\">\n <ng-content />\n </div>\n\n <div class=\"ea-card__footer\">\n <ng-content select=\"[slot=footer]\" />\n </div>\n</div>\n", styles: ["ea-card{display:block;min-width:0;max-width:100%;min-height:0}.ea-card{display:flex;flex-direction:column;min-width:0;max-width:100%;min-height:0;border-radius:var(--radius-lg);font-family:var(--font-family-sans);color:var(--color-text-primary);overflow:hidden}.ea-card--elevated{background-color:var(--color-bg-elevated);border:var(--border-width-thin) solid var(--color-border-default);box-shadow:var(--ea-card-shadow, var(--shadow-md))}.ea-card--outlined{background-color:var(--color-bg-base);border:var(--border-width-thin) solid var(--color-border-default)}.ea-card--filled{background-color:var(--color-bg-subtle)}.ea-card--padding-none .ea-card__header{padding:var(--ea-card-header-padding, 0)}.ea-card--padding-none .ea-card__body{padding:var(--ea-card-body-padding, 0)}.ea-card--padding-none .ea-card__footer{padding:var(--ea-card-footer-padding, 0)}.ea-card--padding-sm .ea-card__header{padding:var(--ea-card-header-padding, var(--space-3) var(--space-3) 0)}.ea-card--padding-sm .ea-card__body{padding:var(--ea-card-body-padding, var(--space-3))}.ea-card--padding-sm .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-3) var(--space-3))}.ea-card--padding-md .ea-card__header{padding:var(--ea-card-header-padding, var(--space-4) var(--space-4) 0)}.ea-card--padding-md .ea-card__body{padding:var(--ea-card-body-padding, var(--space-4))}.ea-card--padding-md .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-4) var(--space-4))}.ea-card--padding-lg .ea-card__header{padding:var(--ea-card-header-padding, var(--space-6) var(--space-6) 0)}.ea-card--padding-lg .ea-card__body{padding:var(--ea-card-body-padding, var(--space-6))}.ea-card--padding-lg .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-6) var(--space-6))}.ea-card--padding-xl .ea-card__header{padding:var(--ea-card-header-padding, var(--space-8) var(--space-8) 0)}.ea-card--padding-xl .ea-card__body{padding:var(--ea-card-body-padding, var(--space-8))}.ea-card--padding-xl .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-8) var(--space-8))}.ea-card--full-width{width:100%}.ea-card__divider{margin:var(--space-2) var(--space-4) 0}.ea-card__header:empty,.ea-card__footer:empty{display:none}.ea-card__header{font-size:var(--text-label-lg-size);font-weight:var(--text-label-lg-weight);line-height:var(--text-label-lg-lh)}.ea-card__body{flex:1;font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary)}.ea-card__footer{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}.ea-card__footer>*{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}\n"], dependencies: [{ kind: "component", type: DividerComponent, selector: "ea-divider", inputs: ["orientation", "label", "thick"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3905
+ }
3906
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CardComponent, decorators: [{
3907
+ type: Component,
3908
+ args: [{ selector: 'ea-card', imports: [DividerComponent, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div\n class=\"ea-card\"\n [ngClass]=\"hostClasses()\">\n <div\n class=\"ea-card__header\"\n [style.text-align]=\"headerAlign()\">\n <ng-content select=\"[slot=header]\" />\n </div>\n\n @if (headerDivider()) {\n <ea-divider class=\"ea-card__divider\" />\n }\n\n <div class=\"ea-card__body\">\n <ng-content />\n </div>\n\n <div class=\"ea-card__footer\">\n <ng-content select=\"[slot=footer]\" />\n </div>\n</div>\n", styles: ["ea-card{display:block;min-width:0;max-width:100%;min-height:0}.ea-card{display:flex;flex-direction:column;min-width:0;max-width:100%;min-height:0;border-radius:var(--radius-lg);font-family:var(--font-family-sans);color:var(--color-text-primary);overflow:hidden}.ea-card--elevated{background-color:var(--color-bg-elevated);border:var(--border-width-thin) solid var(--color-border-default);box-shadow:var(--ea-card-shadow, var(--shadow-md))}.ea-card--outlined{background-color:var(--color-bg-base);border:var(--border-width-thin) solid var(--color-border-default)}.ea-card--filled{background-color:var(--color-bg-subtle)}.ea-card--padding-none .ea-card__header{padding:var(--ea-card-header-padding, 0)}.ea-card--padding-none .ea-card__body{padding:var(--ea-card-body-padding, 0)}.ea-card--padding-none .ea-card__footer{padding:var(--ea-card-footer-padding, 0)}.ea-card--padding-sm .ea-card__header{padding:var(--ea-card-header-padding, var(--space-3) var(--space-3) 0)}.ea-card--padding-sm .ea-card__body{padding:var(--ea-card-body-padding, var(--space-3))}.ea-card--padding-sm .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-3) var(--space-3))}.ea-card--padding-md .ea-card__header{padding:var(--ea-card-header-padding, var(--space-4) var(--space-4) 0)}.ea-card--padding-md .ea-card__body{padding:var(--ea-card-body-padding, var(--space-4))}.ea-card--padding-md .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-4) var(--space-4))}.ea-card--padding-lg .ea-card__header{padding:var(--ea-card-header-padding, var(--space-6) var(--space-6) 0)}.ea-card--padding-lg .ea-card__body{padding:var(--ea-card-body-padding, var(--space-6))}.ea-card--padding-lg .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-6) var(--space-6))}.ea-card--padding-xl .ea-card__header{padding:var(--ea-card-header-padding, var(--space-8) var(--space-8) 0)}.ea-card--padding-xl .ea-card__body{padding:var(--ea-card-body-padding, var(--space-8))}.ea-card--padding-xl .ea-card__footer{padding:var(--ea-card-footer-padding, 0 var(--space-8) var(--space-8))}.ea-card--full-width{width:100%}.ea-card__divider{margin:var(--space-2) var(--space-4) 0}.ea-card__header:empty,.ea-card__footer:empty{display:none}.ea-card__header{font-size:var(--text-label-lg-size);font-weight:var(--text-label-lg-weight);line-height:var(--text-label-lg-lh)}.ea-card__body{flex:1;font-size:var(--font-size-sm);line-height:var(--line-height-normal);color:var(--color-text-secondary)}.ea-card__footer{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}.ea-card__footer>*{display:flex;align-items:center;justify-content:center;gap:var(--space-3)}\n"] }]
3909
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], headerAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerAlign", required: false }] }], headerDivider: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerDivider", required: false }] }] } });
3910
+
3911
+ /**
3912
+ * Boolean form control with support for an indeterminate visual state. Pairs
3913
+ * a visually hidden native input with a custom checkmark and integrates with
3914
+ * Angular forms via `ControlValueAccessor`.
3915
+ */
3916
+ class CheckboxComponent {
3917
+ label = input(undefined, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
3918
+ /**
3919
+ * Optional supplementary value shown immediately after the label, dimmed
3920
+ * to the tertiary text token. Renders inside the same `<span>` as the
3921
+ * label so it shares the label's exact baseline and font metrics, keeping
3922
+ * "Inbox 42" / "Brand (30)" patterns aligned without a sibling element
3923
+ * fighting flex / inline-flow centring at the consumer's call site.
3924
+ */
3925
+ count = input(undefined, ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
3926
+ hint = input(undefined, ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
3927
+ errorMsg = input(undefined, ...(ngDevMode ? [{ debugName: "errorMsg" }] : /* istanbul ignore next */ []));
3928
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3929
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3930
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
3931
+ indeterminate = input(false, ...(ngDevMode ? [{ debugName: "indeterminate" }] : /* istanbul ignore next */ []));
3932
+ ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
3933
+ id = input(uniqueId('ea-checkbox'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
3934
+ checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
3935
+ /** Fires with the new checked state whenever the user toggles the checkbox. */
3936
+ changed = output();
3937
+ _formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_formDisabled" }] : /* istanbul ignore next */ []));
3938
+ isDisabled = computed(() => this.disabled() || this._formDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
3939
+ hasError = computed(() => !!this.errorMsg(), ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
3940
+ showError = this.hasError;
3941
+ showHint = computed(() => !!this.hint() && !this.hasError(), ...(ngDevMode ? [{ debugName: "showHint" }] : /* istanbul ignore next */ []));
3942
+ hostClasses = computed(() => ({
3943
+ [`ea-checkbox--${this.size()}`]: true,
3944
+ 'ea-checkbox--disabled': this.isDisabled(),
3945
+ 'ea-checkbox--checked': this.checked(),
3946
+ 'ea-checkbox--indeterminate': this.indeterminate(),
3947
+ 'ea-checkbox--error': this.hasError(),
3948
+ }), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
3949
+ onChange = () => { };
3950
+ onTouched = () => { };
3951
+ writeValue(val) {
3952
+ this.checked.set(!!val);
3997
3953
  }
3998
- onListMouseLeave() {
3999
- /* Once the pointer leaves the list, no item is a candidate for the next
4000
- click, so drop the keyboard-highlight too. A subsequent keyboard nav
4001
- will restore it. */
4002
- this.interaction.set('none');
3954
+ registerOnChange(fn) {
3955
+ this.onChange = fn;
4003
3956
  }
4004
- onBackdropClick(event) {
4005
- /* Native `<dialog>` clicks land on the dialog itself when the user clicks
4006
- the backdrop; clicks inside content bubble up via the panel. So a click
4007
- whose target IS the dialog element means the user clicked the backdrop. */
4008
- if (event.target === this.dialogEl()?.nativeElement) {
4009
- this.open.set(false);
4010
- }
3957
+ registerOnTouched(fn) {
3958
+ this.onTouched = fn;
4011
3959
  }
4012
- onDialogClose() {
4013
- /* The dialog's native `close` event fires when the user hits Esc or the
4014
- backdrop dispatches `close`. Mirror that back into the `open` model
4015
- so consumers stay in sync. */
4016
- if (this.open()) {
4017
- this.open.set(false);
4018
- }
3960
+ setDisabledState(isDisabled) {
3961
+ this._formDisabled.set(isDisabled);
4019
3962
  }
4020
- executeItem(item) {
4021
- if (item.disabled) {
3963
+ handleChange() {
3964
+ if (this.isDisabled()) {
4022
3965
  return;
4023
3966
  }
4024
- this.execute.emit(item);
4025
- this.open.set(false);
4026
- }
4027
- scrollActiveIntoView() {
4028
- queueMicrotask(() => {
4029
- const id = this.activeId();
4030
- if (!id) {
4031
- return;
4032
- }
4033
- const el = this.hostEl.nativeElement.querySelector(`#${id}`);
4034
- el?.scrollIntoView({ block: 'nearest' });
4035
- });
3967
+ const newValue = !this.checked();
3968
+ this.checked.set(newValue);
3969
+ this.onChange(newValue);
3970
+ this.onTouched();
3971
+ this.changed.emit(newValue);
4036
3972
  }
4037
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CommandPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4038
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: CommandPaletteComponent, isStandalone: true, selector: "ea-command-palette", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", execute: "execute" }, viewQueries: [{ propertyName: "dialogEl", first: true, predicate: ["dialogEl"], descendants: true, isSignal: true }, { propertyName: "searchEl", first: true, predicate: ["searchEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<dialog\n #dialogEl\n class=\"ea-command-palette\"\n [attr.aria-label]=\"messages().commandPalette.dialogLabel\"\n (close)=\"onDialogClose()\"\n (click)=\"onBackdropClick($event)\">\n <div class=\"ea-command-palette__panel\">\n <div class=\"ea-command-palette__search\">\n <ea-icon-search\n class=\"ea-command-palette__search-icon\"\n aria-hidden=\"true\" />\n <input\n #searchEl\n class=\"ea-command-palette__input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-activedescendant]=\"activeId()\"\n [placeholder]=\"placeholder() || messages().commandPalette.searchPlaceholder\"\n [value]=\"query()\"\n (input)=\"onQueryInput($event)\"\n (keydown)=\"onSearchKeydown($event)\" />\n @if (query()) {\n <button\n type=\"button\"\n class=\"ea-command-palette__clear\"\n [attr.aria-label]=\"messages().commandPalette.clear\"\n (click)=\"clearQuery()\">\n <ea-icon-x />\n </button>\n }\n </div>\n\n @if (filteredItems().length === 0) {\n <p class=\"ea-command-palette__empty\">\n {{ emptyMessage() || messages().commandPalette.empty }}\n </p>\n } @else {\n <ul\n class=\"ea-command-palette__list\"\n role=\"listbox\"\n [id]=\"listboxId\"\n (mouseleave)=\"onListMouseLeave()\">\n @for (group of groupedItems(); track group.group) {\n @if (group.group) {\n <li\n class=\"ea-command-palette__group\"\n role=\"presentation\">\n {{ group.group }}\n </li>\n }\n @for (entry of group.items; track entry.item.id) {\n <li\n class=\"ea-command-palette__item\"\n role=\"option\"\n [id]=\"itemDomId(entry.item)\"\n [attr.aria-selected]=\"isActive(entry.flatIndex)\"\n [class.ea-command-palette__item--active]=\"\n showActiveHighlight(entry.flatIndex)\n \"\n [class.ea-command-palette__item--disabled]=\"entry.item.disabled\"\n (click)=\"onItemClick(entry.item)\"\n (mouseenter)=\"onItemMouseEnter(entry.flatIndex)\">\n @if (entry.item.icon; as iconClass) {\n <span\n class=\"ea-command-palette__item-icon\"\n aria-hidden=\"true\">\n <ng-container *ngComponentOutlet=\"iconClass\" />\n </span>\n }\n <span class=\"ea-command-palette__item-text\">\n <span class=\"ea-command-palette__item-label\">\n {{ entry.item.label }}\n </span>\n @if (entry.item.description) {\n <span class=\"ea-command-palette__item-description\">\n {{ entry.item.description }}\n </span>\n }\n </span>\n @if (entry.item.shortcut) {\n <kbd class=\"ea-command-palette__item-shortcut\">\n {{ entry.item.shortcut }}\n </kbd>\n }\n </li>\n }\n }\n </ul>\n }\n </div>\n</dialog>\n", styles: [".ea-command-palette{position:fixed;top:15vh;left:50%;transform:translate(-50%);width:min(640px,100vw - var(--space-8));max-height:70vh;padding:0;border:none;border-radius:var(--radius-lg);background:transparent;color:var(--color-text-primary)}.ea-command-palette::backdrop{background-color:var(--color-bg-overlay)}.ea-command-palette__panel{display:flex;flex-direction:column;max-height:70vh;overflow:hidden;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);background-color:var(--color-bg-elevated)}.ea-command-palette__search{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-3) var(--space-4);border-bottom:var(--border-width-thin) solid var(--color-border-default)}.ea-command-palette__search-icon{display:inline-flex;align-items:center;width:1.125em;height:1.125em;color:var(--color-text-secondary)}.ea-command-palette__input{flex:1;min-width:0;font-size:var(--font-size-md);border:none;background:transparent;color:inherit;outline:none}.ea-command-palette__input::placeholder{color:var(--color-text-tertiary)}.ea-command-palette__clear{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ea-icon-button-size, 1.75em);height:var(--ea-icon-button-size, 1.75em);padding:0;border:none;border-radius:var(--radius-sm);background:none;color:var(--color-text-secondary);cursor:pointer;transition:var(--transition-colors)}.ea-command-palette__clear>*{font-size:1.25em}.ea-command-palette__clear:hover{background-color:var(--color-state-hover);color:var(--color-text-primary)}.ea-command-palette__clear:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-command-palette__clear:disabled{cursor:not-allowed;opacity:.5}.ea-command-palette__list{flex:1;padding:var(--space-1);margin:0;overflow-y:auto;list-style:none}.ea-command-palette__group{position:relative;margin-top:var(--space-3);padding:var(--space-2) var(--space-3) var(--space-1);font-size:var(--font-size-xs);font-weight:var(--font-weight-semibold);letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none}.ea-command-palette__group:before{content:\"\";position:absolute;top:0;right:0;left:0;height:var(--border-width-thin);background-color:var(--color-border-default)}.ea-command-palette__group:first-child{margin-top:0}.ea-command-palette__group:first-child:before{display:none}.ea-command-palette__item{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);cursor:pointer;transition:var(--transition-colors);-webkit-user-select:none;user-select:none}.ea-command-palette__item:hover:not(.ea-command-palette__item--disabled){background-color:var(--color-state-hover)}.ea-command-palette__item--active{background-color:var(--color-state-active)}.ea-command-palette__item--disabled{opacity:.5;cursor:not-allowed}.ea-command-palette__item-icon{display:inline-flex;align-items:center;justify-content:center;width:1em;height:1em;flex-shrink:0;color:var(--color-text-secondary)}.ea-command-palette__item-text{display:flex;flex:1;flex-direction:column;min-width:0}.ea-command-palette__item-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ea-command-palette__item-description{font-size:var(--font-size-xs);color:var(--color-text-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ea-command-palette__item-shortcut{flex-shrink:0;padding:var(--space-1) var(--space-2);font-size:var(--font-size-xs);font-family:var(--font-family-mono);border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-sm);background-color:var(--color-bg-elevated);color:var(--color-text-secondary)}.ea-command-palette__empty{padding:var(--space-6) var(--space-4);margin:0;font-size:var(--font-size-sm);text-align:center;color:var(--color-text-tertiary)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: SearchIconComponent, selector: "ea-icon-search" }, { kind: "component", type: XIconComponent, selector: "ea-icon-x" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3973
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3974
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: CheckboxComponent, isStandalone: true, selector: "ea-checkbox", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, errorMsg: { classPropertyName: "errorMsg", publicName: "errorMsg", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", changed: "changed" }, providers: [
3975
+ {
3976
+ provide: NG_VALUE_ACCESSOR,
3977
+ useExisting: forwardRef(() => CheckboxComponent),
3978
+ multi: true,
3979
+ },
3980
+ ], ngImport: i0, template: "<div class=\"ea-checkbox-field\">\n <label\n class=\"ea-checkbox\"\n [ngClass]=\"hostClasses()\"\n [for]=\"id()\">\n <input\n #inputEl\n type=\"checkbox\"\n class=\"ea-checkbox__input\"\n [id]=\"id()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [indeterminate]=\"indeterminate()\"\n [attr.aria-checked]=\"indeterminate() ? 'mixed' : checked()\"\n [attr.aria-label]=\"!label() ? (ariaLabel() ?? null) : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"\n showError() ? id() + '-error' : showHint() ? id() + '-hint' : null\n \"\n (change)=\"handleChange()\" />\n\n <span\n class=\"ea-checkbox__box\"\n aria-hidden=\"true\">\n @if (indeterminate()) {\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M4 8h8\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\" />\n </svg>\n } @else if (checked()) {\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M3.5 8.5L6.5 11.5L12.5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n }\n </span>\n\n @if (label()) {\n <span\n class=\"ea-checkbox__label\"\n [class.ea-checkbox__label--required]=\"required()\">\n {{ label() }}\n @if (count() !== undefined && count() !== null) {\n <span class=\"ea-checkbox__count\">{{ count() }}</span>\n }\n </span>\n }\n </label>\n\n <ea-field-messages\n [id]=\"id()\"\n [error]=\"showError() ? errorMsg() : null\"\n [hint]=\"showHint() ? hint() : null\" />\n</div>\n", styles: [":host{display:inline-flex;vertical-align:middle;line-height:1}.ea-checkbox-field{display:inline-flex;flex-direction:column;gap:var(--space-1-5)}.ea-checkbox{display:inline-flex;align-items:center;gap:.5em;cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--font-family-sans);color:var(--color-text-primary)}.ea-checkbox--xs{font-size:var(--font-size-xs)}.ea-checkbox--sm{font-size:var(--font-size-sm)}.ea-checkbox--md{font-size:var(--font-size-md)}.ea-checkbox--lg{font-size:var(--font-size-lg)}.ea-checkbox--xl{font-size:var(--font-size-xl)}.ea-checkbox--disabled{opacity:.45;cursor:not-allowed}.ea-checkbox--checked .ea-checkbox__box,.ea-checkbox--indeterminate .ea-checkbox__box{background-color:var(--color-brand-default);border-color:var(--color-brand-default);color:var(--color-text-inverse)}.ea-checkbox:hover:not(.ea-checkbox--disabled) .ea-checkbox__box{border-color:var(--color-brand-default)}.ea-checkbox__input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.ea-checkbox__input:focus-visible+.ea-checkbox__box{box-shadow:var(--shadow-focus-ring)}.ea-checkbox__box{position:relative;display:flex;align-items:center;justify-content:center;flex-shrink:0;box-sizing:border-box;width:1.25em;height:1.25em;border:var(--border-width-thin) solid var(--color-border-strong);border-radius:var(--radius-sm);background-color:var(--color-bg-base);transition:var(--transition-colors),var(--transition-shadow)}.ea-checkbox__box svg{position:absolute;width:65%;height:65%}.ea-checkbox__label{font-weight:var(--font-weight-regular)}.ea-checkbox__label--required:after{content:\" *\";color:var(--color-error-default)}.ea-checkbox__count{margin-left:.25em;color:var(--color-text-tertiary)}.ea-checkbox--error .ea-checkbox__box{border-color:var(--color-error-default)}\n"], dependencies: [{ kind: "component", type: FieldMessagesComponent, selector: "ea-field-messages", inputs: ["id", "error", "hint"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4039
3981
  }
4040
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CommandPaletteComponent, decorators: [{
3982
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CheckboxComponent, decorators: [{
4041
3983
  type: Component,
4042
- args: [{ selector: 'ea-command-palette', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [NgComponentOutlet, SearchIconComponent, XIconComponent], template: "<dialog\n #dialogEl\n class=\"ea-command-palette\"\n [attr.aria-label]=\"messages().commandPalette.dialogLabel\"\n (close)=\"onDialogClose()\"\n (click)=\"onBackdropClick($event)\">\n <div class=\"ea-command-palette__panel\">\n <div class=\"ea-command-palette__search\">\n <ea-icon-search\n class=\"ea-command-palette__search-icon\"\n aria-hidden=\"true\" />\n <input\n #searchEl\n class=\"ea-command-palette__input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-activedescendant]=\"activeId()\"\n [placeholder]=\"placeholder() || messages().commandPalette.searchPlaceholder\"\n [value]=\"query()\"\n (input)=\"onQueryInput($event)\"\n (keydown)=\"onSearchKeydown($event)\" />\n @if (query()) {\n <button\n type=\"button\"\n class=\"ea-command-palette__clear\"\n [attr.aria-label]=\"messages().commandPalette.clear\"\n (click)=\"clearQuery()\">\n <ea-icon-x />\n </button>\n }\n </div>\n\n @if (filteredItems().length === 0) {\n <p class=\"ea-command-palette__empty\">\n {{ emptyMessage() || messages().commandPalette.empty }}\n </p>\n } @else {\n <ul\n class=\"ea-command-palette__list\"\n role=\"listbox\"\n [id]=\"listboxId\"\n (mouseleave)=\"onListMouseLeave()\">\n @for (group of groupedItems(); track group.group) {\n @if (group.group) {\n <li\n class=\"ea-command-palette__group\"\n role=\"presentation\">\n {{ group.group }}\n </li>\n }\n @for (entry of group.items; track entry.item.id) {\n <li\n class=\"ea-command-palette__item\"\n role=\"option\"\n [id]=\"itemDomId(entry.item)\"\n [attr.aria-selected]=\"isActive(entry.flatIndex)\"\n [class.ea-command-palette__item--active]=\"\n showActiveHighlight(entry.flatIndex)\n \"\n [class.ea-command-palette__item--disabled]=\"entry.item.disabled\"\n (click)=\"onItemClick(entry.item)\"\n (mouseenter)=\"onItemMouseEnter(entry.flatIndex)\">\n @if (entry.item.icon; as iconClass) {\n <span\n class=\"ea-command-palette__item-icon\"\n aria-hidden=\"true\">\n <ng-container *ngComponentOutlet=\"iconClass\" />\n </span>\n }\n <span class=\"ea-command-palette__item-text\">\n <span class=\"ea-command-palette__item-label\">\n {{ entry.item.label }}\n </span>\n @if (entry.item.description) {\n <span class=\"ea-command-palette__item-description\">\n {{ entry.item.description }}\n </span>\n }\n </span>\n @if (entry.item.shortcut) {\n <kbd class=\"ea-command-palette__item-shortcut\">\n {{ entry.item.shortcut }}\n </kbd>\n }\n </li>\n }\n }\n </ul>\n }\n </div>\n</dialog>\n", styles: [".ea-command-palette{position:fixed;top:15vh;left:50%;transform:translate(-50%);width:min(640px,100vw - var(--space-8));max-height:70vh;padding:0;border:none;border-radius:var(--radius-lg);background:transparent;color:var(--color-text-primary)}.ea-command-palette::backdrop{background-color:var(--color-bg-overlay)}.ea-command-palette__panel{display:flex;flex-direction:column;max-height:70vh;overflow:hidden;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);background-color:var(--color-bg-elevated)}.ea-command-palette__search{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-3) var(--space-4);border-bottom:var(--border-width-thin) solid var(--color-border-default)}.ea-command-palette__search-icon{display:inline-flex;align-items:center;width:1.125em;height:1.125em;color:var(--color-text-secondary)}.ea-command-palette__input{flex:1;min-width:0;font-size:var(--font-size-md);border:none;background:transparent;color:inherit;outline:none}.ea-command-palette__input::placeholder{color:var(--color-text-tertiary)}.ea-command-palette__clear{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ea-icon-button-size, 1.75em);height:var(--ea-icon-button-size, 1.75em);padding:0;border:none;border-radius:var(--radius-sm);background:none;color:var(--color-text-secondary);cursor:pointer;transition:var(--transition-colors)}.ea-command-palette__clear>*{font-size:1.25em}.ea-command-palette__clear:hover{background-color:var(--color-state-hover);color:var(--color-text-primary)}.ea-command-palette__clear:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-command-palette__clear:disabled{cursor:not-allowed;opacity:.5}.ea-command-palette__list{flex:1;padding:var(--space-1);margin:0;overflow-y:auto;list-style:none}.ea-command-palette__group{position:relative;margin-top:var(--space-3);padding:var(--space-2) var(--space-3) var(--space-1);font-size:var(--font-size-xs);font-weight:var(--font-weight-semibold);letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none}.ea-command-palette__group:before{content:\"\";position:absolute;top:0;right:0;left:0;height:var(--border-width-thin);background-color:var(--color-border-default)}.ea-command-palette__group:first-child{margin-top:0}.ea-command-palette__group:first-child:before{display:none}.ea-command-palette__item{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);cursor:pointer;transition:var(--transition-colors);-webkit-user-select:none;user-select:none}.ea-command-palette__item:hover:not(.ea-command-palette__item--disabled){background-color:var(--color-state-hover)}.ea-command-palette__item--active{background-color:var(--color-state-active)}.ea-command-palette__item--disabled{opacity:.5;cursor:not-allowed}.ea-command-palette__item-icon{display:inline-flex;align-items:center;justify-content:center;width:1em;height:1em;flex-shrink:0;color:var(--color-text-secondary)}.ea-command-palette__item-text{display:flex;flex:1;flex-direction:column;min-width:0}.ea-command-palette__item-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ea-command-palette__item-description{font-size:var(--font-size-xs);color:var(--color-text-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ea-command-palette__item-shortcut{flex-shrink:0;padding:var(--space-1) var(--space-2);font-size:var(--font-size-xs);font-family:var(--font-family-mono);border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-sm);background-color:var(--color-bg-elevated);color:var(--color-text-secondary)}.ea-command-palette__empty{padding:var(--space-6) var(--space-4);margin:0;font-size:var(--font-size-sm);text-align:center;color:var(--color-text-tertiary)}\n"] }]
4043
- }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], execute: [{ type: i0.Output, args: ["execute"] }], dialogEl: [{ type: i0.ViewChild, args: ['dialogEl', { isSignal: true }] }], searchEl: [{ type: i0.ViewChild, args: ['searchEl', { isSignal: true }] }] } });
3984
+ args: [{ selector: 'ea-checkbox', imports: [FieldMessagesComponent, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
3985
+ {
3986
+ provide: NG_VALUE_ACCESSOR,
3987
+ useExisting: forwardRef(() => CheckboxComponent),
3988
+ multi: true,
3989
+ },
3990
+ ], template: "<div class=\"ea-checkbox-field\">\n <label\n class=\"ea-checkbox\"\n [ngClass]=\"hostClasses()\"\n [for]=\"id()\">\n <input\n #inputEl\n type=\"checkbox\"\n class=\"ea-checkbox__input\"\n [id]=\"id()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [indeterminate]=\"indeterminate()\"\n [attr.aria-checked]=\"indeterminate() ? 'mixed' : checked()\"\n [attr.aria-label]=\"!label() ? (ariaLabel() ?? null) : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"\n showError() ? id() + '-error' : showHint() ? id() + '-hint' : null\n \"\n (change)=\"handleChange()\" />\n\n <span\n class=\"ea-checkbox__box\"\n aria-hidden=\"true\">\n @if (indeterminate()) {\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M4 8h8\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\" />\n </svg>\n } @else if (checked()) {\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M3.5 8.5L6.5 11.5L12.5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n }\n </span>\n\n @if (label()) {\n <span\n class=\"ea-checkbox__label\"\n [class.ea-checkbox__label--required]=\"required()\">\n {{ label() }}\n @if (count() !== undefined && count() !== null) {\n <span class=\"ea-checkbox__count\">{{ count() }}</span>\n }\n </span>\n }\n </label>\n\n <ea-field-messages\n [id]=\"id()\"\n [error]=\"showError() ? errorMsg() : null\"\n [hint]=\"showHint() ? hint() : null\" />\n</div>\n", styles: [":host{display:inline-flex;vertical-align:middle;line-height:1}.ea-checkbox-field{display:inline-flex;flex-direction:column;gap:var(--space-1-5)}.ea-checkbox{display:inline-flex;align-items:center;gap:.5em;cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--font-family-sans);color:var(--color-text-primary)}.ea-checkbox--xs{font-size:var(--font-size-xs)}.ea-checkbox--sm{font-size:var(--font-size-sm)}.ea-checkbox--md{font-size:var(--font-size-md)}.ea-checkbox--lg{font-size:var(--font-size-lg)}.ea-checkbox--xl{font-size:var(--font-size-xl)}.ea-checkbox--disabled{opacity:.45;cursor:not-allowed}.ea-checkbox--checked .ea-checkbox__box,.ea-checkbox--indeterminate .ea-checkbox__box{background-color:var(--color-brand-default);border-color:var(--color-brand-default);color:var(--color-text-inverse)}.ea-checkbox:hover:not(.ea-checkbox--disabled) .ea-checkbox__box{border-color:var(--color-brand-default)}.ea-checkbox__input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.ea-checkbox__input:focus-visible+.ea-checkbox__box{box-shadow:var(--shadow-focus-ring)}.ea-checkbox__box{position:relative;display:flex;align-items:center;justify-content:center;flex-shrink:0;box-sizing:border-box;width:1.25em;height:1.25em;border:var(--border-width-thin) solid var(--color-border-strong);border-radius:var(--radius-sm);background-color:var(--color-bg-base);transition:var(--transition-colors),var(--transition-shadow)}.ea-checkbox__box svg{position:absolute;width:65%;height:65%}.ea-checkbox__label{font-weight:var(--font-weight-regular)}.ea-checkbox__label--required:after{content:\" *\";color:var(--color-error-default)}.ea-checkbox__count{margin-left:.25em;color:var(--color-text-tertiary)}.ea-checkbox--error .ea-checkbox__box{border-color:var(--color-error-default)}\n"] }]
3991
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], errorMsg: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMsg", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
4044
3992
 
4045
- class DropletIconComponent extends IconComponentBase {
4046
- static slug = 'droplet';
3993
+ class SearchIconComponent extends IconComponentBase {
3994
+ static slug = 'search';
4047
3995
  static category = 'feather';
4048
3996
  static tags = [
4049
- 'droplet',
4050
- 'water',
4051
- 'drop',
4052
- 'liquid',
4053
- 'rain',
4054
- 'goutte',
4055
- 'eau',
4056
- 'gota',
4057
- 'agua',
4058
- 'σταγόνα',
4059
- 'νερό',
4060
- 'kropla',
4061
- 'woda',
3997
+ 'search',
3998
+ 'find',
3999
+ 'magnify',
4000
+ 'lookup',
4001
+ 'query',
4002
+ 'recherche',
4003
+ 'chercher',
4004
+ 'buscar',
4005
+ 'búsqueda',
4006
+ 'αναζήτηση',
4007
+ 'βρες',
4008
+ 'szukaj',
4009
+ 'wyszukaj',
4062
4010
  ];
4063
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: DropletIconComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4064
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: DropletIconComponent, isStandalone: true, selector: "ea-icon-droplet", usesInheritance: true, ngImport: i0, template: `
4011
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SearchIconComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4012
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: SearchIconComponent, isStandalone: true, selector: "ea-icon-search", usesInheritance: true, ngImport: i0, template: `
4065
4013
  <svg
4066
4014
  viewBox="0 0 24 24"
4067
4015
  fill="none"
@@ -4072,14 +4020,18 @@ class DropletIconComponent extends IconComponentBase {
4072
4020
  aria-hidden="true"
4073
4021
  width="100%"
4074
4022
  height="100%">
4075
- <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
4023
+ <circle
4024
+ cx="11"
4025
+ cy="11"
4026
+ r="8" />
4027
+ <path d="m21 21-4.35-4.35" />
4076
4028
  </svg>
4077
4029
  `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4078
4030
  }
4079
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: DropletIconComponent, decorators: [{
4031
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: SearchIconComponent, decorators: [{
4080
4032
  type: Component,
4081
4033
  args: [{
4082
- selector: 'ea-icon-droplet',
4034
+ selector: 'ea-icon-search',
4083
4035
  changeDetection: ChangeDetectionStrategy.OnPush,
4084
4036
  template: `
4085
4037
  <svg
@@ -4092,298 +4044,333 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
4092
4044
  aria-hidden="true"
4093
4045
  width="100%"
4094
4046
  height="100%">
4095
- <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
4047
+ <circle
4048
+ cx="11"
4049
+ cy="11"
4050
+ r="8" />
4051
+ <path d="m21 21-4.35-4.35" />
4096
4052
  </svg>
4097
4053
  `,
4098
4054
  }]
4099
4055
  }] });
4100
4056
 
4101
4057
  /**
4102
- * Floating-element primitive. Renders projected content as `position: fixed`
4103
- * anchored to an external element, with flip-on-overflow, viewport clamping,
4104
- * outside-click and Escape dismissal, and SSR-safe scroll / resize handling.
4105
- *
4106
- * The primitive is intentionally low-level: a parent component drives the
4107
- * `[open]` state and listens for `(closeRequested)` to mirror it back. Internal
4108
- * library components (`<ea-menu>`, `<ea-dropdown>`, `<ea-color-picker>`,
4109
- * `<ea-date-picker>`, `[eaTooltip]`) compose on top of it; downstream apps can
4110
- * use it directly to build their own popover-based UI.
4058
+ * `<ea-command-palette>` is a search-driven action launcher: a modal dialog
4059
+ * containing a search input and a filtered list of commands. Designed for
4060
+ * `Cmd/Ctrl + K`-style global menus.
4111
4061
  *
4112
- * @example
4113
- * ```html
4114
- * <button #trigger (click)="open.set(!open())">Open</button>
4115
- * <ea-popover [anchor]="trigger" [open]="open()" (closeRequested)="open.set(false)">
4116
- * <div>Popover content</div>
4117
- * </ea-popover>
4118
- * ```
4119
- */
4120
- class PopoverComponent {
4121
- destroyRef = inject(DestroyRef);
4122
- surfaceEl = viewChild('surfaceEl', ...(ngDevMode ? [{ debugName: "surfaceEl" }] : /* istanbul ignore next */ []));
4123
- /** Anchor element the popover positions itself against. */
4124
- anchor = input.required(...(ngDevMode ? [{ debugName: "anchor" }] : /* istanbul ignore next */ []));
4125
- /** Whether the popover is currently open. */
4126
- open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
4127
- /** Where the popover attaches relative to the anchor. */
4128
- placement = input('bottom-start', ...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
4129
- /** ARIA role applied to the popover surface. */
4130
- role = input('dialog', ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
4131
- /** Accessible label. Falls back to nothing; consumers should provide one when no visible heading is in the popover. */
4132
- ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
4133
- /** DOM id for the surface so trigger elements can reference it via aria-controls. */
4134
- surfaceId = input(uniqueId('ea-popover'), ...(ngDevMode ? [{ debugName: "surfaceId" }] : /* istanbul ignore next */ []));
4062
+ * The component does NOT bind global shortcuts; the consumer wires up
4063
+ * whatever trigger they want and toggles `[(open)]`. Each selected command
4064
+ * is emitted via `(execute)`; the palette closes automatically afterwards.
4065
+ *
4066
+ * Items can be grouped via the `group` field; ungrouped items render first.
4067
+ * The active row is tracked via roving `aria-activedescendant` on the search
4068
+ * input, which is the canonical ARIA combobox-with-listbox pattern.
4069
+ */
4070
+ class CommandPaletteComponent {
4071
+ i18n = inject(EagamiI18nService);
4072
+ hostEl = inject(ElementRef);
4073
+ items = input.required(...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
4074
+ open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
4075
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
4076
+ emptyMessage = input('', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
4077
+ execute = output();
4078
+ messages = this.i18n.messages;
4079
+ dialogEl = viewChild('dialogEl', ...(ngDevMode ? [{ debugName: "dialogEl" }] : /* istanbul ignore next */ []));
4080
+ searchEl = viewChild('searchEl', ...(ngDevMode ? [{ debugName: "searchEl" }] : /* istanbul ignore next */ []));
4081
+ query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
4082
+ listboxId = uniqueId('ea-command-palette-listbox');
4135
4083
  /**
4136
- * Gap in px between the anchor and the popover. Defaults to 2 so an open panel
4137
- * clears the anchor's active focus ring instead of sitting flush over it.
4084
+ * Items bucketed by `group`, then flattened back into display order
4085
+ * (ungrouped items first, then each named group). The matching
4086
+ * `filteredItems` array follows this same order so `flatIndex` lines up
4087
+ * one-to-one with what the user sees.
4138
4088
  */
4139
- offset = input(2, ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
4140
- /** Flip to the opposite side when the requested side overflows the viewport. */
4141
- flip = input(true, ...(ngDevMode ? [{ debugName: "flip" }] : /* istanbul ignore next */ []));
4142
- /** Clamp the popover inside the viewport when it would otherwise overflow. */
4143
- clamp = input(true, ...(ngDevMode ? [{ debugName: "clamp" }] : /* istanbul ignore next */ []));
4144
- /** Set the popover's `min-width` to match the anchor's width (dropdown pattern). */
4145
- matchAnchorWidth = input(false, ...(ngDevMode ? [{ debugName: "matchAnchorWidth" }] : /* istanbul ignore next */ []));
4146
- /** Close on click outside the popover and the anchor. */
4147
- closeOnOutsideClick = input(true, ...(ngDevMode ? [{ debugName: "closeOnOutsideClick" }] : /* istanbul ignore next */ []));
4148
- /** Close on Escape. */
4149
- closeOnEscape = input(true, ...(ngDevMode ? [{ debugName: "closeOnEscape" }] : /* istanbul ignore next */ []));
4150
- /** What to do on scroll / resize while open. */
4151
- scrollBehavior = input('reposition', ...(ngDevMode ? [{ debugName: "scrollBehavior" }] : /* istanbul ignore next */ []));
4152
- /** Requested close. The parent should mirror this into `[open]`. */
4153
- closeRequested = output();
4154
- position = signal(null, ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
4155
- /** True placement after flip, for class-based styling (e.g. arrow direction). */
4156
- effectivePlacement = computed(() => this.position()?.placement ?? this.placement(), ...(ngDevMode ? [{ debugName: "effectivePlacement" }] : /* istanbul ignore next */ []));
4157
- /** Latches true once the post-rAF reposition has run, so the surface is
4158
- * only revealed after its dimensions are guaranteed stable. Reset on close. */
4159
- stable = signal(false, ...(ngDevMode ? [{ debugName: "stable" }] : /* istanbul ignore next */ []));
4160
- /** True once the first `reposition()` has resolved a placement on a
4161
- * laid-out surface. Drives the `--positioned` class. */
4162
- isPositioned = computed(() => this.open() && this.position() !== null && this.stable(), ...(ngDevMode ? [{ debugName: "isPositioned" }] : /* istanbul ignore next */ []));
4163
- /** Class list for the surface. Computed in TS so the placement key (with
4164
- * its interpolated suffix) and the positioned modifier compose cleanly. */
4165
- surfaceClass = computed(() => `ea-popover__surface ea-popover__surface--${this.effectivePlacement()}${this.isPositioned() ? ' ea-popover__surface--positioned' : ''}`, ...(ngDevMode ? [{ debugName: "surfaceClass" }] : /* istanbul ignore next */ []));
4166
- /** Inline style applied to the surface element. */
4167
- surfaceStyle = computed(() => {
4168
- if (!this.open()) {
4169
- return { display: 'none' };
4170
- }
4171
- const p = this.position();
4172
- if (!p) {
4173
- return {};
4174
- }
4175
- const style = {
4176
- top: `${p.top}px`,
4177
- left: `${p.left}px`,
4178
- };
4179
- if (p.width !== undefined) {
4180
- style['min-width'] = `${p.width}px`;
4181
- }
4182
- return style;
4183
- }, ...(ngDevMode ? [{ debugName: "surfaceStyle" }] : /* istanbul ignore next */ []));
4184
- constructor() {
4185
- // Re-measure and reposition whenever the anchor, placement, surface, or
4186
- // open state changes. Reading `surfaceEl()` here makes it a tracked signal
4187
- // dependency, so the effect re-runs once Angular has rendered the `@if`
4188
- // block and the viewChild signal has updated; at that point both the
4189
- // anchor and the surface have a `getBoundingClientRect`, and the position
4190
- // can be computed. This is more reliable than `afterNextRender` because it
4191
- // doesn't depend on a single render cycle landing in the expected order
4192
- // (some host environments, Storybook docs mode for example, defer that
4193
- // callback in a way that leaves the surface stuck at `visibility: hidden`).
4194
- // Naturally SSR-safe: the surface never renders on the server, so the
4195
- // effect always early-returns during prerender.
4196
- // Teleport the surface to `document.body` as soon as it exists so
4197
- // `position: fixed` is always relative to the actual viewport (escaping
4198
- // any transformed/contained ancestor that would otherwise create a new
4199
- // containing block). Doing the move on init, not on open, also means
4200
- // the first `getBoundingClientRect` call inside `reposition()` reads a
4201
- // surface that's already in its final DOM home, so the browser's layout
4202
- // is settled and dimensions are accurate. Skipped in SSR (no `document`).
4203
- effect(() => {
4204
- const surface = this.surfaceEl()?.nativeElement;
4205
- if (surface &&
4206
- typeof document !== 'undefined' &&
4207
- surface.parentNode !== document.body) {
4208
- document.body.appendChild(surface);
4209
- }
4210
- });
4211
- effect(() => {
4212
- const surface = this.surfaceEl()?.nativeElement;
4213
- const anchor = this.resolveAnchor();
4214
- const isOpen = this.open();
4215
- if (!surface || !anchor || !isOpen) {
4216
- this.position.set(null);
4217
- this.stable.set(false);
4218
- return;
4219
- }
4220
- // Re-read inputs so signal subscriptions stay current after a re-open.
4221
- this.placement();
4222
- this.offset();
4223
- this.flip();
4224
- this.clamp();
4225
- this.matchAnchorWidth();
4226
- // First reposition runs synchronously off the open effect (fast), but
4227
- // the surface is still transitioning out of `display: none` and the
4228
- // first `getBoundingClientRect` can report the surface's natural width
4229
- // even when that overflows the viewport. We deliberately keep the
4230
- // surface hidden (`visibility: hidden` from CSS) until the next rAF
4231
- // when the browser has finished laying it out at its real dimensions;
4232
- // the second reposition then uses accurate measurements and the
4233
- // `stable` latch flips, revealing the surface at the final position
4234
- // with no visible jump. SSR / non-browser hosts fall back to flipping
4235
- // `stable` synchronously.
4236
- this.reposition();
4237
- if (typeof requestAnimationFrame !== 'undefined') {
4238
- requestAnimationFrame(() => {
4239
- if (!this.open()) {
4240
- return;
4241
- }
4242
- this.reposition();
4243
- this.stable.set(true);
4244
- });
4089
+ groupedItems = computed(() => {
4090
+ const q = this.query().trim().toLowerCase();
4091
+ const candidates = this.items().filter(item => {
4092
+ if (item.disabled) {
4093
+ return false;
4245
4094
  }
4246
- else {
4247
- this.stable.set(true);
4095
+ if (!q) {
4096
+ return true;
4248
4097
  }
4098
+ const haystack = [item.label, item.description ?? '', ...(item.keywords ?? [])]
4099
+ .join(' ')
4100
+ .toLowerCase();
4101
+ // Match when the query is a prefix of any word in the haystack.
4102
+ // Substring-anywhere matching would surface confusing results (typing
4103
+ // "c" matching "Repla*c*e" via a mid-word character); word-boundary
4104
+ // matching keeps results predictable.
4105
+ return haystack.split(/[\s\-_]+/).some(word => word.startsWith(q));
4249
4106
  });
4250
- // Watch the surface's own size and reposition whenever it changes. The
4251
- // first `reposition()` after open fires synchronously inside the open
4252
- // effect, while the surface is still transitioning out of `display: none`;
4253
- // in some browsers the layout pass hasn't completed, so the
4254
- // `getBoundingClientRect` width can read as the surface's natural width
4255
- // even when that overflows the viewport, so the clamp can't kick in. The
4256
- // ResizeObserver fires once the surface has been laid out with its real
4257
- // dimensions, giving us a second, accurate measurement to clamp against.
4258
- // Also catches projected-content size changes while the popover is open
4259
- // (e.g. virtualised lists adding rows).
4260
- if (typeof ResizeObserver !== 'undefined') {
4261
- const surfaceResizeObserver = new ResizeObserver(() => {
4262
- if (this.open()) {
4263
- this.reposition();
4264
- }
4265
- });
4266
- effect(() => {
4267
- const surface = this.surfaceEl()?.nativeElement;
4268
- surfaceResizeObserver.disconnect();
4269
- if (surface) {
4270
- surfaceResizeObserver.observe(surface);
4271
- }
4272
- });
4273
- this.destroyRef.onDestroy(() => surfaceResizeObserver.disconnect());
4107
+ const buckets = new Map();
4108
+ for (const item of candidates) {
4109
+ const key = item.group ?? '';
4110
+ if (!buckets.has(key)) {
4111
+ buckets.set(key, []);
4112
+ }
4113
+ buckets.get(key).push(item);
4274
4114
  }
4275
- // Listen for scroll / resize while open. The `scrollBehavior` input picks
4276
- // the response. SSR guard is required because the website prerenders pages
4277
- // that mount popovers.
4278
- if (typeof window !== 'undefined') {
4279
- const onViewportChange = (event) => {
4280
- if (!this.open()) {
4281
- return;
4282
- }
4283
- // Scrolling within the popover's own surface (e.g. a long dropdown list)
4284
- // must not dismiss or re-track it; only outside/viewport scroll should.
4285
- const target = event?.target;
4286
- if (event?.type === 'scroll' &&
4287
- target &&
4288
- this.surfaceEl()?.nativeElement.contains(target)) {
4289
- return;
4290
- }
4291
- const behavior = this.scrollBehavior();
4292
- if (behavior === 'close') {
4293
- this.closeRequested.emit();
4294
- }
4295
- else if (behavior === 'reposition') {
4296
- this.reposition();
4297
- }
4298
- };
4299
- window.addEventListener('scroll', onViewportChange, {
4300
- capture: true,
4301
- passive: true,
4302
- });
4303
- window.addEventListener('resize', onViewportChange);
4304
- this.destroyRef.onDestroy(() => {
4305
- window.removeEventListener('scroll', onViewportChange, { capture: true });
4306
- window.removeEventListener('resize', onViewportChange);
4115
+ const groups = [];
4116
+ let flatIndex = 0;
4117
+ const pushGroup = (group, items) => {
4118
+ groups.push({
4119
+ group,
4120
+ items: items.map(item => ({ item, flatIndex: flatIndex++ })),
4307
4121
  });
4122
+ };
4123
+ const ungrouped = buckets.get('');
4124
+ if (ungrouped && ungrouped.length > 0) {
4125
+ pushGroup('', ungrouped);
4126
+ }
4127
+ for (const [group, items] of buckets) {
4128
+ if (group !== '') {
4129
+ pushGroup(group, items);
4130
+ }
4131
+ }
4132
+ return groups;
4133
+ }, ...(ngDevMode ? [{ debugName: "groupedItems" }] : /* istanbul ignore next */ []));
4134
+ /**
4135
+ * Flat list of matches in display order. Drives keyboard navigation and
4136
+ * `aria-activedescendant`.
4137
+ */
4138
+ filteredItems = computed(() => this.groupedItems().flatMap(group => group.items.map(entry => entry.item)), ...(ngDevMode ? [{ debugName: "filteredItems" }] : /* istanbul ignore next */ []));
4139
+ _activeIndex = signal(0, ...(ngDevMode ? [{ debugName: "_activeIndex" }] : /* istanbul ignore next */ []));
4140
+ /**
4141
+ * Tracks what the user last did so the visual highlight only renders when
4142
+ * it actually reflects what a click/Enter would select right now:
4143
+ * - `keyboard`: keyboard nav (or just-opened / just-typed): show the
4144
+ * active item's background so keyboard users see what Enter will pick.
4145
+ * - `mouse`: pointer is moving inside the list: rely on `:hover` for the
4146
+ * visual; skip the active-row background to avoid two highlights.
4147
+ * - `none`: pointer is outside the list and no keyboard nav has happened
4148
+ * since: nothing is highlighted, because nothing on screen is a
4149
+ * next-click target.
4150
+ */
4151
+ interaction = signal('keyboard', ...(ngDevMode ? [{ debugName: "interaction" }] : /* istanbul ignore next */ []));
4152
+ activeIndex = computed(() => {
4153
+ const max = this.filteredItems().length - 1;
4154
+ if (max < 0) {
4155
+ return -1;
4156
+ }
4157
+ return Math.min(this._activeIndex(), max);
4158
+ }, ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
4159
+ activeId = computed(() => {
4160
+ const idx = this.activeIndex();
4161
+ if (idx < 0) {
4162
+ return null;
4308
4163
  }
4309
- // Explicitly remove the portaled surface on destroy. Angular's view
4310
- // destruction normally removes nodes the renderer created, but moving the
4311
- // surface via raw `appendChild` (out of its original anchor slot) is
4312
- // enough to break that tracking in some host environments: Storybook's
4313
- // SPA navigation between docs pages, for one, leaves the surface stranded
4314
- // in `document.body` after the parent component is gone. Removing it here
4315
- // guarantees cleanup regardless of how Angular's view destruction handles
4316
- // the relocated node.
4317
- this.destroyRef.onDestroy(() => {
4318
- const surface = this.surfaceEl()?.nativeElement;
4319
- surface?.parentNode?.removeChild(surface);
4164
+ return `ea-command-palette-item-${this.filteredItems()[idx].id}`;
4165
+ }, ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
4166
+ constructor() {
4167
+ /* When the palette opens, focus the search input and reset state. The
4168
+ dialog needs an extra tick to call `showModal()` before the input is
4169
+ focusable, so queue the focus into a microtask. */
4170
+ effect(() => {
4171
+ const isOpen = this.open();
4172
+ const dialog = this.dialogEl()?.nativeElement;
4173
+ if (!dialog) {
4174
+ return;
4175
+ }
4176
+ if (isOpen) {
4177
+ if (!dialog.open) {
4178
+ dialog.showModal?.();
4179
+ }
4180
+ this.query.set('');
4181
+ this._activeIndex.set(0);
4182
+ this.interaction.set('keyboard');
4183
+ queueMicrotask(() => this.searchEl()?.nativeElement.focus());
4184
+ }
4185
+ else if (dialog.open) {
4186
+ dialog.close();
4187
+ }
4320
4188
  });
4321
4189
  }
4322
- resolveAnchor() {
4323
- const a = this.anchor();
4324
- if (!a) {
4325
- return null;
4326
- }
4327
- return a instanceof ElementRef ? a.nativeElement : a;
4190
+ itemDomId(item) {
4191
+ return `ea-command-palette-item-${item.id}`;
4328
4192
  }
4329
- reposition() {
4330
- if (typeof window === 'undefined') {
4193
+ isActive(flatIndex) {
4194
+ return this.activeIndex() === flatIndex;
4195
+ }
4196
+ /**
4197
+ * Whether the active row should render its highlighted background right
4198
+ * now. False when the pointer is hovering the list (`:hover` handles the
4199
+ * visual) or when the pointer is out of the list entirely (nothing is a
4200
+ * next-click target).
4201
+ */
4202
+ showActiveHighlight(flatIndex) {
4203
+ return this.interaction() === 'keyboard' && this.isActive(flatIndex);
4204
+ }
4205
+ onQueryInput(event) {
4206
+ this.query.set(event.target.value);
4207
+ this._activeIndex.set(0);
4208
+ // Typing implies keyboard intent; surface the first match so the user
4209
+ // knows what Enter would pick without having to mouse over.
4210
+ this.interaction.set('keyboard');
4211
+ }
4212
+ clearQuery() {
4213
+ this.query.set('');
4214
+ this._activeIndex.set(0);
4215
+ this.interaction.set('keyboard');
4216
+ this.searchEl()?.nativeElement.focus();
4217
+ }
4218
+ onSearchKeydown(event) {
4219
+ const max = this.filteredItems().length - 1;
4220
+ if (max < 0) {
4331
4221
  return;
4332
4222
  }
4333
- const anchor = this.resolveAnchor();
4334
- const surface = this.surfaceEl()?.nativeElement;
4335
- if (!anchor || !surface) {
4336
- return;
4223
+ switch (event.key) {
4224
+ case 'ArrowDown': {
4225
+ event.preventDefault();
4226
+ this._activeIndex.set(this.activeIndex() < max ? this.activeIndex() + 1 : 0);
4227
+ this.interaction.set('keyboard');
4228
+ this.scrollActiveIntoView();
4229
+ break;
4230
+ }
4231
+ case 'ArrowUp': {
4232
+ event.preventDefault();
4233
+ this._activeIndex.set(this.activeIndex() > 0 ? this.activeIndex() - 1 : max);
4234
+ this.interaction.set('keyboard');
4235
+ this.scrollActiveIntoView();
4236
+ break;
4237
+ }
4238
+ case 'Home': {
4239
+ event.preventDefault();
4240
+ this._activeIndex.set(0);
4241
+ this.interaction.set('keyboard');
4242
+ this.scrollActiveIntoView();
4243
+ break;
4244
+ }
4245
+ case 'End': {
4246
+ event.preventDefault();
4247
+ this._activeIndex.set(max);
4248
+ this.interaction.set('keyboard');
4249
+ this.scrollActiveIntoView();
4250
+ break;
4251
+ }
4252
+ case 'Enter': {
4253
+ event.preventDefault();
4254
+ const item = this.filteredItems()[this.activeIndex()];
4255
+ if (item) {
4256
+ this.executeItem(item);
4257
+ }
4258
+ break;
4259
+ }
4337
4260
  }
4338
- const anchorRect = anchor.getBoundingClientRect();
4339
- const surfaceRect = surface.getBoundingClientRect();
4340
- this.position.set(computePopoverPosition(anchorRect, { width: surfaceRect.width, height: surfaceRect.height }, { width: window.innerWidth, height: window.innerHeight }, {
4341
- placement: this.placement(),
4342
- offset: this.offset(),
4343
- flip: this.flip(),
4344
- clamp: this.clamp(),
4345
- matchAnchorWidth: this.matchAnchorWidth(),
4346
- }));
4347
4261
  }
4348
- onDocumentClick(event) {
4349
- if (!this.open() || !this.closeOnOutsideClick()) {
4350
- return;
4351
- }
4352
- const target = event.target;
4353
- if (!target) {
4262
+ onItemClick(item) {
4263
+ if (item.disabled) {
4354
4264
  return;
4355
4265
  }
4356
- const anchor = this.resolveAnchor();
4357
- if (anchor?.contains(target)) {
4358
- return;
4266
+ this.executeItem(item);
4267
+ }
4268
+ onItemMouseEnter(flatIndex) {
4269
+ this._activeIndex.set(flatIndex);
4270
+ this.interaction.set('mouse');
4271
+ }
4272
+ onListMouseLeave() {
4273
+ /* Once the pointer leaves the list, no item is a candidate for the next
4274
+ click, so drop the keyboard-highlight too. A subsequent keyboard nav
4275
+ will restore it. */
4276
+ this.interaction.set('none');
4277
+ }
4278
+ onBackdropClick(event) {
4279
+ /* Native `<dialog>` clicks land on the dialog itself when the user clicks
4280
+ the backdrop; clicks inside content bubble up via the panel. So a click
4281
+ whose target IS the dialog element means the user clicked the backdrop. */
4282
+ if (event.target === this.dialogEl()?.nativeElement) {
4283
+ this.open.set(false);
4359
4284
  }
4360
- if (this.surfaceEl()?.nativeElement.contains(target)) {
4361
- return;
4285
+ }
4286
+ onDialogClose() {
4287
+ /* The dialog's native `close` event fires when the user hits Esc or the
4288
+ backdrop dispatches `close`. Mirror that back into the `open` model
4289
+ so consumers stay in sync. */
4290
+ if (this.open()) {
4291
+ this.open.set(false);
4362
4292
  }
4363
- this.closeRequested.emit();
4364
4293
  }
4365
- onEscape() {
4366
- if (!this.open() || !this.closeOnEscape()) {
4294
+ executeItem(item) {
4295
+ if (item.disabled) {
4367
4296
  return;
4368
4297
  }
4369
- this.closeRequested.emit();
4298
+ this.execute.emit(item);
4299
+ this.open.set(false);
4370
4300
  }
4371
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4372
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.16", type: PopoverComponent, isStandalone: true, selector: "ea-popover", inputs: { anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: true, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, surfaceId: { classPropertyName: "surfaceId", publicName: "surfaceId", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, flip: { classPropertyName: "flip", publicName: "flip", isSignal: true, isRequired: false, transformFunction: null }, clamp: { classPropertyName: "clamp", publicName: "clamp", isSignal: true, isRequired: false, transformFunction: null }, matchAnchorWidth: { classPropertyName: "matchAnchorWidth", publicName: "matchAnchorWidth", isSignal: true, isRequired: false, transformFunction: null }, closeOnOutsideClick: { classPropertyName: "closeOnOutsideClick", publicName: "closeOnOutsideClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, scrollBehavior: { classPropertyName: "scrollBehavior", publicName: "scrollBehavior", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeRequested: "closeRequested" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" }, properties: { "attr.role": "null", "attr.aria-label": "null" } }, viewQueries: [{ propertyName: "surfaceEl", first: true, predicate: ["surfaceEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n The surface is rendered unconditionally so the `<ng-content/>` slot always\n exists. If we gated it on `@if (open())`, Angular would re-project the\n consumer's content at the popover host's position whenever the surface was\n absent, leaking menu items / picker controls into the document flow (made\n worse by `display: contents` on the host). Hiding via `display: none` keeps\n the projected DOM owned by the surface and out of the flow when closed.\n-->\n<div\n #surfaceEl\n [class]=\"surfaceClass()\"\n [id]=\"surfaceId()\"\n [attr.role]=\"open() ? role() : null\"\n [attr.aria-label]=\"open() ? ariaLabel() : null\"\n [attr.aria-hidden]=\"open() ? null : true\"\n [style]=\"surfaceStyle()\">\n <ng-content />\n</div>\n", styles: [":host{display:contents}.ea-popover__surface{z-index:var(--z-index-dropdown);position:fixed;visibility:hidden}.ea-popover__surface--positioned{visibility:visible}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4301
+ scrollActiveIntoView() {
4302
+ queueMicrotask(() => {
4303
+ const id = this.activeId();
4304
+ if (!id) {
4305
+ return;
4306
+ }
4307
+ const el = this.hostEl.nativeElement.querySelector(`#${id}`);
4308
+ el?.scrollIntoView({ block: 'nearest' });
4309
+ });
4310
+ }
4311
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CommandPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4312
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: CommandPaletteComponent, isStandalone: true, selector: "ea-command-palette", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", execute: "execute" }, viewQueries: [{ propertyName: "dialogEl", first: true, predicate: ["dialogEl"], descendants: true, isSignal: true }, { propertyName: "searchEl", first: true, predicate: ["searchEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<dialog\n #dialogEl\n class=\"ea-command-palette\"\n [attr.aria-label]=\"messages().commandPalette.dialogLabel\"\n (close)=\"onDialogClose()\"\n (click)=\"onBackdropClick($event)\">\n <div class=\"ea-command-palette__panel\">\n <div class=\"ea-command-palette__search\">\n <ea-icon-search\n class=\"ea-command-palette__search-icon\"\n aria-hidden=\"true\" />\n <input\n #searchEl\n class=\"ea-command-palette__input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-activedescendant]=\"activeId()\"\n [placeholder]=\"placeholder() || messages().commandPalette.searchPlaceholder\"\n [value]=\"query()\"\n (input)=\"onQueryInput($event)\"\n (keydown)=\"onSearchKeydown($event)\" />\n @if (query()) {\n <button\n type=\"button\"\n class=\"ea-command-palette__clear\"\n [attr.aria-label]=\"messages().commandPalette.clear\"\n (click)=\"clearQuery()\">\n <ea-icon-x />\n </button>\n }\n </div>\n\n @if (filteredItems().length === 0) {\n <p class=\"ea-command-palette__empty\">\n {{ emptyMessage() || messages().commandPalette.empty }}\n </p>\n } @else {\n <ul\n class=\"ea-command-palette__list\"\n role=\"listbox\"\n [id]=\"listboxId\"\n (mouseleave)=\"onListMouseLeave()\">\n @for (group of groupedItems(); track group.group) {\n @if (group.group) {\n <li\n class=\"ea-command-palette__group\"\n role=\"presentation\">\n {{ group.group }}\n </li>\n }\n @for (entry of group.items; track entry.item.id) {\n <li\n class=\"ea-command-palette__item\"\n role=\"option\"\n [id]=\"itemDomId(entry.item)\"\n [attr.aria-selected]=\"isActive(entry.flatIndex)\"\n [class.ea-command-palette__item--active]=\"\n showActiveHighlight(entry.flatIndex)\n \"\n [class.ea-command-palette__item--disabled]=\"entry.item.disabled\"\n (click)=\"onItemClick(entry.item)\"\n (mouseenter)=\"onItemMouseEnter(entry.flatIndex)\">\n @if (entry.item.icon; as iconClass) {\n <span\n class=\"ea-command-palette__item-icon\"\n aria-hidden=\"true\">\n <ng-container *ngComponentOutlet=\"iconClass\" />\n </span>\n }\n <span class=\"ea-command-palette__item-text\">\n <span class=\"ea-command-palette__item-label\">\n {{ entry.item.label }}\n </span>\n @if (entry.item.description) {\n <span class=\"ea-command-palette__item-description\">\n {{ entry.item.description }}\n </span>\n }\n </span>\n @if (entry.item.shortcut) {\n <kbd class=\"ea-command-palette__item-shortcut\">\n {{ entry.item.shortcut }}\n </kbd>\n }\n </li>\n }\n }\n </ul>\n }\n </div>\n</dialog>\n", styles: [".ea-command-palette{position:fixed;top:15vh;left:50%;transform:translate(-50%);width:min(640px,100vw - var(--space-8));max-height:70vh;padding:0;border:none;border-radius:var(--radius-lg);background:transparent;color:var(--color-text-primary)}.ea-command-palette::backdrop{background-color:var(--color-bg-overlay)}.ea-command-palette__panel{display:flex;flex-direction:column;max-height:70vh;overflow:hidden;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);background-color:var(--color-bg-elevated)}.ea-command-palette__search{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-3) var(--space-4);border-bottom:var(--border-width-thin) solid var(--color-border-default)}.ea-command-palette__search-icon{display:inline-flex;align-items:center;width:1.125em;height:1.125em;color:var(--color-text-secondary)}.ea-command-palette__input{flex:1;min-width:0;font-size:var(--font-size-md);border:none;background:transparent;color:inherit;outline:none}.ea-command-palette__input::placeholder{color:var(--color-text-tertiary)}.ea-command-palette__clear{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ea-icon-button-size, 1.75em);height:var(--ea-icon-button-size, 1.75em);padding:0;border:none;border-radius:var(--radius-sm);background:none;color:var(--color-text-secondary);cursor:pointer;transition:var(--transition-colors)}.ea-command-palette__clear>*{font-size:1.25em}.ea-command-palette__clear:hover{background-color:var(--color-state-hover);color:var(--color-text-primary)}.ea-command-palette__clear:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-command-palette__clear:disabled{cursor:not-allowed;opacity:.5}.ea-command-palette__list{flex:1;padding:var(--space-1);margin:0;overflow-y:auto;list-style:none}.ea-command-palette__group{position:relative;margin-top:var(--space-3);padding:var(--space-2) var(--space-3) var(--space-1);font-size:var(--font-size-xs);font-weight:var(--font-weight-semibold);letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none}.ea-command-palette__group:before{content:\"\";position:absolute;top:0;right:0;left:0;height:var(--border-width-thin);background-color:var(--color-border-default)}.ea-command-palette__group:first-child{margin-top:0}.ea-command-palette__group:first-child:before{display:none}.ea-command-palette__item{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);cursor:pointer;transition:var(--transition-colors);-webkit-user-select:none;user-select:none}.ea-command-palette__item:hover:not(.ea-command-palette__item--disabled){background-color:var(--color-state-hover)}.ea-command-palette__item--active{background-color:var(--color-state-active)}.ea-command-palette__item--disabled{opacity:.5;cursor:not-allowed}.ea-command-palette__item-icon{display:inline-flex;align-items:center;justify-content:center;width:1em;height:1em;flex-shrink:0;color:var(--color-text-secondary)}.ea-command-palette__item-text{display:flex;flex:1;flex-direction:column;min-width:0}.ea-command-palette__item-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ea-command-palette__item-description{font-size:var(--font-size-xs);color:var(--color-text-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ea-command-palette__item-shortcut{flex-shrink:0;padding:var(--space-1) var(--space-2);font-size:var(--font-size-xs);font-family:var(--font-family-mono);border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-sm);background-color:var(--color-bg-elevated);color:var(--color-text-secondary)}.ea-command-palette__empty{padding:var(--space-6) var(--space-4);margin:0;font-size:var(--font-size-sm);text-align:center;color:var(--color-text-tertiary)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: SearchIconComponent, selector: "ea-icon-search" }, { kind: "component", type: XIconComponent, selector: "ea-icon-x" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
4373
4313
  }
4374
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PopoverComponent, decorators: [{
4314
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CommandPaletteComponent, decorators: [{
4375
4315
  type: Component,
4376
- args: [{ selector: 'ea-popover', changeDetection: ChangeDetectionStrategy.OnPush, host: {
4377
- '[attr.role]': 'null',
4378
- '[attr.aria-label]': 'null',
4379
- }, template: "<!--\n The surface is rendered unconditionally so the `<ng-content/>` slot always\n exists. If we gated it on `@if (open())`, Angular would re-project the\n consumer's content at the popover host's position whenever the surface was\n absent, leaking menu items / picker controls into the document flow (made\n worse by `display: contents` on the host). Hiding via `display: none` keeps\n the projected DOM owned by the surface and out of the flow when closed.\n-->\n<div\n #surfaceEl\n [class]=\"surfaceClass()\"\n [id]=\"surfaceId()\"\n [attr.role]=\"open() ? role() : null\"\n [attr.aria-label]=\"open() ? ariaLabel() : null\"\n [attr.aria-hidden]=\"open() ? null : true\"\n [style]=\"surfaceStyle()\">\n <ng-content />\n</div>\n", styles: [":host{display:contents}.ea-popover__surface{z-index:var(--z-index-dropdown);position:fixed;visibility:hidden}.ea-popover__surface--positioned{visibility:visible}\n"] }]
4380
- }], ctorParameters: () => [], propDecorators: { surfaceEl: [{ type: i0.ViewChild, args: ['surfaceEl', { isSignal: true }] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: true }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], surfaceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "surfaceId", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], flip: [{ type: i0.Input, args: [{ isSignal: true, alias: "flip", required: false }] }], clamp: [{ type: i0.Input, args: [{ isSignal: true, alias: "clamp", required: false }] }], matchAnchorWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "matchAnchorWidth", required: false }] }], closeOnOutsideClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnOutsideClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], scrollBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollBehavior", required: false }] }], closeRequested: [{ type: i0.Output, args: ["closeRequested"] }], onDocumentClick: [{
4381
- type: HostListener,
4382
- args: ['document:click', ['$event']]
4383
- }], onEscape: [{
4384
- type: HostListener,
4385
- args: ['document:keydown.escape']
4386
- }] } });
4316
+ args: [{ selector: 'ea-command-palette', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [NgComponentOutlet, SearchIconComponent, XIconComponent], template: "<dialog\n #dialogEl\n class=\"ea-command-palette\"\n [attr.aria-label]=\"messages().commandPalette.dialogLabel\"\n (close)=\"onDialogClose()\"\n (click)=\"onBackdropClick($event)\">\n <div class=\"ea-command-palette__panel\">\n <div class=\"ea-command-palette__search\">\n <ea-icon-search\n class=\"ea-command-palette__search-icon\"\n aria-hidden=\"true\" />\n <input\n #searchEl\n class=\"ea-command-palette__input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-activedescendant]=\"activeId()\"\n [placeholder]=\"placeholder() || messages().commandPalette.searchPlaceholder\"\n [value]=\"query()\"\n (input)=\"onQueryInput($event)\"\n (keydown)=\"onSearchKeydown($event)\" />\n @if (query()) {\n <button\n type=\"button\"\n class=\"ea-command-palette__clear\"\n [attr.aria-label]=\"messages().commandPalette.clear\"\n (click)=\"clearQuery()\">\n <ea-icon-x />\n </button>\n }\n </div>\n\n @if (filteredItems().length === 0) {\n <p class=\"ea-command-palette__empty\">\n {{ emptyMessage() || messages().commandPalette.empty }}\n </p>\n } @else {\n <ul\n class=\"ea-command-palette__list\"\n role=\"listbox\"\n [id]=\"listboxId\"\n (mouseleave)=\"onListMouseLeave()\">\n @for (group of groupedItems(); track group.group) {\n @if (group.group) {\n <li\n class=\"ea-command-palette__group\"\n role=\"presentation\">\n {{ group.group }}\n </li>\n }\n @for (entry of group.items; track entry.item.id) {\n <li\n class=\"ea-command-palette__item\"\n role=\"option\"\n [id]=\"itemDomId(entry.item)\"\n [attr.aria-selected]=\"isActive(entry.flatIndex)\"\n [class.ea-command-palette__item--active]=\"\n showActiveHighlight(entry.flatIndex)\n \"\n [class.ea-command-palette__item--disabled]=\"entry.item.disabled\"\n (click)=\"onItemClick(entry.item)\"\n (mouseenter)=\"onItemMouseEnter(entry.flatIndex)\">\n @if (entry.item.icon; as iconClass) {\n <span\n class=\"ea-command-palette__item-icon\"\n aria-hidden=\"true\">\n <ng-container *ngComponentOutlet=\"iconClass\" />\n </span>\n }\n <span class=\"ea-command-palette__item-text\">\n <span class=\"ea-command-palette__item-label\">\n {{ entry.item.label }}\n </span>\n @if (entry.item.description) {\n <span class=\"ea-command-palette__item-description\">\n {{ entry.item.description }}\n </span>\n }\n </span>\n @if (entry.item.shortcut) {\n <kbd class=\"ea-command-palette__item-shortcut\">\n {{ entry.item.shortcut }}\n </kbd>\n }\n </li>\n }\n }\n </ul>\n }\n </div>\n</dialog>\n", styles: [".ea-command-palette{position:fixed;top:15vh;left:50%;transform:translate(-50%);width:min(640px,100vw - var(--space-8));max-height:70vh;padding:0;border:none;border-radius:var(--radius-lg);background:transparent;color:var(--color-text-primary)}.ea-command-palette::backdrop{background-color:var(--color-bg-overlay)}.ea-command-palette__panel{display:flex;flex-direction:column;max-height:70vh;overflow:hidden;border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);background-color:var(--color-bg-elevated)}.ea-command-palette__search{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-3) var(--space-4);border-bottom:var(--border-width-thin) solid var(--color-border-default)}.ea-command-palette__search-icon{display:inline-flex;align-items:center;width:1.125em;height:1.125em;color:var(--color-text-secondary)}.ea-command-palette__input{flex:1;min-width:0;font-size:var(--font-size-md);border:none;background:transparent;color:inherit;outline:none}.ea-command-palette__input::placeholder{color:var(--color-text-tertiary)}.ea-command-palette__clear{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--ea-icon-button-size, 1.75em);height:var(--ea-icon-button-size, 1.75em);padding:0;border:none;border-radius:var(--radius-sm);background:none;color:var(--color-text-secondary);cursor:pointer;transition:var(--transition-colors)}.ea-command-palette__clear>*{font-size:1.25em}.ea-command-palette__clear:hover{background-color:var(--color-state-hover);color:var(--color-text-primary)}.ea-command-palette__clear:focus-visible{outline:none;box-shadow:var(--shadow-focus-ring)}.ea-command-palette__clear:disabled{cursor:not-allowed;opacity:.5}.ea-command-palette__list{flex:1;padding:var(--space-1);margin:0;overflow-y:auto;list-style:none}.ea-command-palette__group{position:relative;margin-top:var(--space-3);padding:var(--space-2) var(--space-3) var(--space-1);font-size:var(--font-size-xs);font-weight:var(--font-weight-semibold);letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none}.ea-command-palette__group:before{content:\"\";position:absolute;top:0;right:0;left:0;height:var(--border-width-thin);background-color:var(--color-border-default)}.ea-command-palette__group:first-child{margin-top:0}.ea-command-palette__group:first-child:before{display:none}.ea-command-palette__item{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);cursor:pointer;transition:var(--transition-colors);-webkit-user-select:none;user-select:none}.ea-command-palette__item:hover:not(.ea-command-palette__item--disabled){background-color:var(--color-state-hover)}.ea-command-palette__item--active{background-color:var(--color-state-active)}.ea-command-palette__item--disabled{opacity:.5;cursor:not-allowed}.ea-command-palette__item-icon{display:inline-flex;align-items:center;justify-content:center;width:1em;height:1em;flex-shrink:0;color:var(--color-text-secondary)}.ea-command-palette__item-text{display:flex;flex:1;flex-direction:column;min-width:0}.ea-command-palette__item-label{font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ea-command-palette__item-description{font-size:var(--font-size-xs);color:var(--color-text-secondary);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ea-command-palette__item-shortcut{flex-shrink:0;padding:var(--space-1) var(--space-2);font-size:var(--font-size-xs);font-family:var(--font-family-mono);border:var(--border-width-thin) solid var(--color-border-default);border-radius:var(--radius-sm);background-color:var(--color-bg-elevated);color:var(--color-text-secondary)}.ea-command-palette__empty{padding:var(--space-6) var(--space-4);margin:0;font-size:var(--font-size-sm);text-align:center;color:var(--color-text-tertiary)}\n"] }]
4317
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], execute: [{ type: i0.Output, args: ["execute"] }], dialogEl: [{ type: i0.ViewChild, args: ['dialogEl', { isSignal: true }] }], searchEl: [{ type: i0.ViewChild, args: ['searchEl', { isSignal: true }] }] } });
4318
+
4319
+ class DropletIconComponent extends IconComponentBase {
4320
+ static slug = 'droplet';
4321
+ static category = 'feather';
4322
+ static tags = [
4323
+ 'droplet',
4324
+ 'water',
4325
+ 'drop',
4326
+ 'liquid',
4327
+ 'rain',
4328
+ 'goutte',
4329
+ 'eau',
4330
+ 'gota',
4331
+ 'agua',
4332
+ 'σταγόνα',
4333
+ 'νερό',
4334
+ 'kropla',
4335
+ 'woda',
4336
+ ];
4337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: DropletIconComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4338
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: DropletIconComponent, isStandalone: true, selector: "ea-icon-droplet", usesInheritance: true, ngImport: i0, template: `
4339
+ <svg
4340
+ viewBox="0 0 24 24"
4341
+ fill="none"
4342
+ stroke="currentColor"
4343
+ [attr.stroke-width]="strokeWidth()"
4344
+ stroke-linecap="round"
4345
+ stroke-linejoin="round"
4346
+ aria-hidden="true"
4347
+ width="100%"
4348
+ height="100%">
4349
+ <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
4350
+ </svg>
4351
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4352
+ }
4353
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: DropletIconComponent, decorators: [{
4354
+ type: Component,
4355
+ args: [{
4356
+ selector: 'ea-icon-droplet',
4357
+ changeDetection: ChangeDetectionStrategy.OnPush,
4358
+ template: `
4359
+ <svg
4360
+ viewBox="0 0 24 24"
4361
+ fill="none"
4362
+ stroke="currentColor"
4363
+ [attr.stroke-width]="strokeWidth()"
4364
+ stroke-linecap="round"
4365
+ stroke-linejoin="round"
4366
+ aria-hidden="true"
4367
+ width="100%"
4368
+ height="100%">
4369
+ <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
4370
+ </svg>
4371
+ `,
4372
+ }]
4373
+ }] });
4387
4374
 
4388
4375
  const DEFAULT_PRESETS = [
4389
4376
  '#ef4444',